From 786868d191d786b0e2c603a050f6b50a00428de7 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sun, 12 Mar 2017 22:20:40 -0700 Subject: [PATCH] Updated VGMStream to r1050-352-g698269d. --- .../vgmstream.xcodeproj/project.pbxproj | 20 + .../vgmstream/vgmstream/ext_libs/clHCA.c | 87 +- .../vgmstream/vgmstream/src/coding/coding.h | 51 +- .../vgmstream/src/coding/ffmpeg_decoder.c | 234 +---- .../src/coding/ffmpeg_decoder_utils.c | 542 ++++++++++++ .../vgmstream/src/coding/ima_decoder.c | 837 +++++++++--------- .../vgmstream/src/coding/mpeg_decoder.c | 702 ++++++++++++--- .../vgmstream/src/coding/ngc_dsp_decoder.c | 11 +- .../vgmstream/src/coding/nwa_decoder.c | 36 +- .../vgmstream/src/coding/nwa_decoder.h | 14 +- Frameworks/vgmstream/vgmstream/src/formats.c | 25 +- Frameworks/vgmstream/vgmstream/src/header.c | 254 ++++++ Frameworks/vgmstream/vgmstream/src/header.h | 24 + Frameworks/vgmstream/vgmstream/src/meta/ads.c | 101 +-- .../vgmstream/vgmstream/src/meta/adx_header.c | 313 +++---- Frameworks/vgmstream/vgmstream/src/meta/ahx.c | 75 +- Frameworks/vgmstream/vgmstream/src/meta/akb.c | 171 ++++ Frameworks/vgmstream/vgmstream/src/meta/bik.c | 128 +++ .../vgmstream/vgmstream/src/meta/dsp_adx.c | 68 ++ .../vgmstream/vgmstream/src/meta/ffmpeg.c | 45 +- Frameworks/vgmstream/vgmstream/src/meta/fsb.c | 191 ++-- .../vgmstream/vgmstream/src/meta/fsb5.c | 611 +++++-------- .../vgmstream/vgmstream/src/meta/genh.c | 390 ++++---- Frameworks/vgmstream/vgmstream/src/meta/hca.c | 8 +- Frameworks/vgmstream/vgmstream/src/meta/mca.c | 59 +- .../vgmstream/vgmstream/src/meta/meta.h | 17 +- Frameworks/vgmstream/vgmstream/src/meta/mp4.c | 100 ++- .../vgmstream/src/meta/ngc_dsp_mpds.c | 117 +-- .../vgmstream/src/meta/ngc_dsp_std.c | 16 +- .../vgmstream/src/meta/ogg_vorbis_file.c | 7 + .../vgmstream/vgmstream/src/meta/pc_adp.c | 77 +- .../vgmstream/vgmstream/src/meta/ps2_msa.c | 48 +- .../vgmstream/vgmstream/src/meta/ps2_vag.c | 2 +- .../vgmstream/vgmstream/src/meta/ps3_msf.c | 269 +++--- .../vgmstream/src/meta/ps3_sgh_sgb.c | 341 +++---- .../vgmstream/vgmstream/src/meta/ps3_xvag.c | 348 ++++---- .../vgmstream/vgmstream/src/meta/sqex_scd.c | 49 +- Frameworks/vgmstream/vgmstream/src/meta/xma.c | 198 ++--- Frameworks/vgmstream/vgmstream/src/meta/xwb.c | 508 ++++++++--- .../vgmstream/vgmstream/src/streamfile.c | 21 +- .../vgmstream/vgmstream/src/streamfile.h | 2 +- Frameworks/vgmstream/vgmstream/src/util.h | 20 +- .../vgmstream/vgmstream/src/vgmstream.c | 111 ++- .../vgmstream/vgmstream/src/vgmstream.h | 49 +- 44 files changed, 4309 insertions(+), 2988 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c create mode 100644 Frameworks/vgmstream/vgmstream/src/header.c create mode 100644 Frameworks/vgmstream/vgmstream/src/header.h create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/bik.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/dsp_adx.c diff --git a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj index 007a8adee..00cd3d8fe 100644 --- a/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/vgmstream.xcodeproj/project.pbxproj @@ -16,6 +16,11 @@ 8323894B1D22419B00482226 /* clHCA.h in Headers */ = {isa = PBXBuildFile; fileRef = 832389491D22419B00482226 /* clHCA.h */; settings = {ATTRIBUTES = (Public, ); }; }; 832389501D2246C300482226 /* hca.c in Sources */ = {isa = PBXBuildFile; fileRef = 8323894F1D2246C300482226 /* hca.c */; }; 832389521D224C0800482226 /* hca_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 832389511D224C0800482226 /* hca_decoder.c */; }; + 83299FC61E76606B003A3242 /* ffmpeg_decoder_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FC51E76606B003A3242 /* ffmpeg_decoder_utils.c */; }; + 83299FCC1E76607A003A3242 /* header.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCA1E76607A003A3242 /* header.c */; }; + 83299FCD1E76607A003A3242 /* header.h in Headers */ = {isa = PBXBuildFile; fileRef = 83299FCB1E76607A003A3242 /* header.h */; }; + 83299FD01E7660C7003A3242 /* bik.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCE1E7660C7003A3242 /* bik.c */; }; + 83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83299FCF1E7660C7003A3242 /* dsp_adx.c */; }; 834D3A6E19F47C98001C54F6 /* g1l.c in Sources */ = {isa = PBXBuildFile; fileRef = 834D3A6D19F47C98001C54F6 /* g1l.c */; }; 8350C0551E071881009E0A93 /* xma.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0541E071881009E0A93 /* xma.c */; }; 8350C05A1E071990009E0A93 /* ps2_svag_snk.c in Sources */ = {isa = PBXBuildFile; fileRef = 8350C0591E071990009E0A93 /* ps2_svag_snk.c */; }; @@ -478,6 +483,11 @@ 832389491D22419B00482226 /* clHCA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clHCA.h; sourceTree = ""; }; 8323894F1D2246C300482226 /* hca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca.c; sourceTree = ""; }; 832389511D224C0800482226 /* hca_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca_decoder.c; sourceTree = ""; }; + 83299FC51E76606B003A3242 /* ffmpeg_decoder_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils.c; sourceTree = ""; }; + 83299FCA1E76607A003A3242 /* header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = header.c; sourceTree = ""; }; + 83299FCB1E76607A003A3242 /* header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = header.h; sourceTree = ""; }; + 83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = ""; }; + 83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = ""; }; 834D3A6D19F47C98001C54F6 /* g1l.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g1l.c; sourceTree = ""; }; 8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = ""; }; 8350C0591E071990009E0A93 /* ps2_svag_snk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag_snk.c; sourceTree = ""; }; @@ -957,6 +967,8 @@ 836F6E2718BDC2180095E648 /* meta */, 83A3F0711E3AD8B900D6A794 /* formats.c */, 83A3F0721E3AD8B900D6A794 /* formats.h */, + 83299FCA1E76607A003A3242 /* header.c */, + 83299FCB1E76607A003A3242 /* header.h */, 83A3F0731E3AD8B900D6A794 /* stack_alloc.h */, 836F6F1718BDC2190095E648 /* streamfile.c */, 836F6F1818BDC2190095E648 /* streamfile.h */, @@ -972,6 +984,7 @@ 836F6DDF18BDC2180095E648 /* coding */ = { isa = PBXGroup; children = ( + 83299FC51E76606B003A3242 /* ffmpeg_decoder_utils.c */, 838BDB6B1D3AFAB10022CA6F /* ffmpeg_decoder.c */, 832389511D224C0800482226 /* hca_decoder.c */, 83D7318B1A749EEE00CA1366 /* g719_decoder.c */, @@ -1054,6 +1067,8 @@ 836F6E2718BDC2180095E648 /* meta */ = { isa = PBXGroup; children = ( + 83299FCE1E7660C7003A3242 /* bik.c */, + 83299FCF1E7660C7003A3242 /* dsp_adx.c */, 83A3F07A1E3AD92400D6A794 /* ps2.c */, 83A3F07B1E3AD92400D6A794 /* x360.c */, 8350C0591E071990009E0A93 /* ps2_svag_snk.c */, @@ -1339,6 +1354,7 @@ 83A3F0761E3AD8B900D6A794 /* stack_alloc.h in Headers */, 8323894B1D22419B00482226 /* clHCA.h in Headers */, 83A3F0751E3AD8B900D6A794 /* formats.h in Headers */, + 83299FCD1E76607A003A3242 /* header.h in Headers */, 836F705918BDC2190095E648 /* vgmstream.h in Headers */, 48C2650F1A5D420800A0A3D6 /* vorbisfile.h in Headers */, 836F705718BDC2190095E648 /* util.h in Headers */, @@ -1530,6 +1546,7 @@ 836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */, 836F6F2818BDC2190095E648 /* ima_decoder.c in Sources */, 836F702318BDC2190095E648 /* rsf.c in Sources */, + 83299FD01E7660C7003A3242 /* bik.c in Sources */, 836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */, 836F6FBB18BDC2190095E648 /* ngca.c in Sources */, 836F6F5218BDC2190095E648 /* ps2_adm_blocked.c in Sources */, @@ -1567,6 +1584,7 @@ 836F6FF218BDC2190095E648 /* ps2_rnd.c in Sources */, 836F6FA818BDC2190095E648 /* nds_swav.c in Sources */, 836F6F3F18BDC2190095E648 /* ast_blocked.c in Sources */, + 83299FC61E76606B003A3242 /* ffmpeg_decoder_utils.c in Sources */, 836F6F5018BDC2190095E648 /* mxch_blocked.c in Sources */, 836F6FB518BDC2190095E648 /* ngc_pdt.c in Sources */, 836F6F4F18BDC2190095E648 /* mus_acm_layout.c in Sources */, @@ -1605,6 +1623,7 @@ 836F6F2918BDC2190095E648 /* l5_555_decoder.c in Sources */, 836F6FF318BDC2190095E648 /* ps2_rstm.c in Sources */, 836F6FCC18BDC2190095E648 /* ps2_adsc.c in Sources */, + 83299FCC1E76607A003A3242 /* header.c in Sources */, 836F6F7918BDC2190095E648 /* dc_asd.c in Sources */, 836F6FC118BDC2190095E648 /* pc_adp.c in Sources */, 836F6F8318BDC2190095E648 /* ea_old.c in Sources */, @@ -1614,6 +1633,7 @@ 836F6F8218BDC2190095E648 /* ea_header.c in Sources */, 836F700A18BDC2190095E648 /* ps2_vpk.c in Sources */, 836F6F7318BDC2190095E648 /* bcstm.c in Sources */, + 83299FD11E7660C7003A3242 /* dsp_adx.c in Sources */, 83A3F07D1E3AD92400D6A794 /* x360.c in Sources */, 836F704018BDC2190095E648 /* wii_sng.c in Sources */, 8350C0551E071881009E0A93 /* xma.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c index c81f6a2f1..c2935d60a 100644 --- a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c +++ b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c @@ -14,6 +14,7 @@ //-------------------------------------------------- // インライン関数 //-------------------------------------------------- +#if 0 static inline unsigned short get_le16(unsigned short v_){const unsigned char *v=(const unsigned char *)&v_;unsigned short r=v[1];r<<=8;r|=v[0];return r;} static inline unsigned short get_be16(unsigned short v_){const unsigned char *v=(const unsigned char *)&v_;unsigned short r=v[0];r<<=8;r|=v[1];return r;} static inline unsigned int get_be24(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[0];r<<=8;r|=v[1];r<<=8;r|=v[2];return r;}; @@ -21,7 +22,6 @@ static inline unsigned int get_le32(unsigned int v_){const unsigned char *v=(con static inline unsigned int get_be32(unsigned int v_){const unsigned char *v=(const unsigned char *)&v_;unsigned int r=v[0];r<<=8;r|=v[1];r<<=8;r|=v[2];r<<=8;r|=v[3];return r;} static inline float get_bef32(float v_){union{float f;unsigned int i;}v;v.f=v_;v.i=get_be32(v.i);return v.f;} -#if 0 static union { unsigned int i; unsigned char c[4]; } g_is_le = {1}; static inline unsigned short swap_u16(unsigned short v){unsigned short r=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;return r;} static inline unsigned short swap_u32(unsigned int v){unsigned int r=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;r<<=8;v>>=8;r|=v&0xFF;return r;} @@ -191,9 +191,11 @@ static void clHCA_destructor(clHCA *hca) //-------------------------------------------------- // HCAチェック //-------------------------------------------------- +#if 0 static int clHCA_CheckFile(void *data,unsigned int size){ - return (data&&size>=4&&(get_le32(*(unsigned int *)data)&0x7F7F7F7F)==0x00414348); + return (data&&size>=4&&(get_be32(*(unsigned int *)data)&0x7F7F7F7F)==0x48434100);/*'HCA\0'*/ } +#endif //-------------------------------------------------- // チェックサム @@ -366,7 +368,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // HCA - if(size>=sizeof(stHeader) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='HCA\0'){ + if(size>=sizeof(stHeader) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x48434100){/*'HCA\0'*/ clData_AddBit(&d,32); _version=clData_GetBit(&d,16); _dataOffset=clData_GetBit(&d,16); @@ -380,7 +382,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // fmt - if(size>=sizeof(stFormat) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)=='fmt\0'){ + if(size>=sizeof(stFormat) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x666D7400){/*'fmt\0'*/ clData_AddBit(&d,32); _channelCount=clData_GetBit(&d,8); _samplingRate=clData_GetBit(&d,24); @@ -408,7 +410,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // comp - if(size>=sizeof(stCompress) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)=='comp'){ + if(size>=sizeof(stCompress) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x636F6D70){/*'comp'*/ clData_AddBit(&d,32); _blockSize=clData_GetBit(&d,16); _comp_r01=clData_GetBit(&d,8); @@ -443,7 +445,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // dec - else if(size>=sizeof(stDecode) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)=='dec\0'){ + else if(size>=sizeof(stDecode) && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x64656300){/*'dec\0'*/ unsigned char count1,count2,enableCount2; clData_AddBit(&d, 32); _blockSize=clData_GetBit(&d,16); @@ -482,7 +484,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // vbr - if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='vbr\0'){ + if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x76627200){/*'vbr\0'*/ clData_AddBit(&d, 32); _vbr_r01=clData_GetBit(&d,16); _vbr_r02=clData_GetBit(&d,16); @@ -502,7 +504,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // ath - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='ath\0'){ + if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x61746800){/*'ath\0'*/ clData_AddBit(&d,32); _ath_type=clData_GetBit(&d,16); printf("ATHタイプ:%d ※v2.0から廃止されています。\n",_ath_type); @@ -514,7 +516,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // loop - if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='loop'){ + if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x6C6F6F70){/*'loop'*/ clData_AddBit(&d, 32); _loopStart=clData_GetBit(&d,32); _loopEnd=clData_GetBit(&d,32); @@ -531,7 +533,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // ciph - if(size>=6 && (clData_CheckBit(&d, 32)&0x7F7F7F7F)=='ciph'){ + if(size>=6 && (clData_CheckBit(&d, 32)&0x7F7F7F7F)==0x63697068){/*'ciph'*/ clData_AddBit(&d,32); _ciph_type=clData_GetBit(&d,16); switch(_ciph_type){ @@ -547,7 +549,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // rva - if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='rva\0'){ + if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x72766100){/*'rva\0'*/ union { unsigned int i; float f; } v; clData_AddBit(&d,32); v.i=clData_GetBit(&d,32); @@ -557,7 +559,7 @@ static int clHCA_PrintInfo(const char *filenameHCA){ } // comm - if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='comm'){ + if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D6D){/*'comm'*/ int i; clData_AddBit(&d,32); _comm_len=clData_GetBit(&d,8); @@ -758,7 +760,7 @@ int clHCA_DecodeToWavefile_Decode(clHCA *hca,void *fp1,void *fp2,unsigned int ad int clHCA_isOurFile0(const void *data){ clData d; clData_constructor(&d, data, 8); - if((clData_CheckBit(&d,32)&0x7F7F7F7F)=='HCA\0'){ + if((clData_CheckBit(&d,32)&0x7F7F7F7F)==0x48434100){/*'HCA\0'*/ clData_AddBit(&d,32+16); return clData_CheckBit(&d,16); } @@ -769,7 +771,7 @@ int clHCA_isOurFile1(const void *data, unsigned int size){ int minsize; if (size<8)return -1; minsize = clHCA_isOurFile0(data); - if (minsize < 0 || minsize > size)return -1; + if (minsize < 0 || (unsigned int)minsize > size)return -1; if (clHCA_CheckSum(data, minsize, 0))return -1; return 0; } @@ -793,10 +795,12 @@ void clHCA_DecodeSamples16(clHCA *hca,signed short *samples){ const float scale = 32768.0f; float f; signed int s; + int i, j; + unsigned int k, l; //const float _rva_volume=hca->_rva_volume; - for(int i=0;i<8;i++){ - for(int j=0;j<0x80;j++){ - for(unsigned int k=0,l=hca->_channelCount;k_channelCount;k_channel[k].wave[i][j]/**_rva_volume*/; if(f>1){f=1;}else if(f<-1){f=-1;} s=(signed int)(f*scale); @@ -981,7 +985,7 @@ void clCipher_Init56(clCipher *cipher,unsigned int key1,unsigned int key2){ unsigned char t2[0x10]; unsigned char t3[0x100],t31[0x10],t32[0x10],*t; - int i, v; + int i, j, v; if(!key1)key2--; key1--; @@ -1004,10 +1008,11 @@ void clCipher_Init56(clCipher *cipher,unsigned int key1,unsigned int key2){ // テーブル3 t=t3; clCipher_Init56_CreateTable(t31,t1[0]); - for(int i=0;i<0x10;i++){ + for(i=0;i<0x10;i++){ + unsigned char v; clCipher_Init56_CreateTable(t32,t2[i]); - unsigned char v=t31[i]<<4; - for(int j=0;j<0x10;j++){ + v=t31[i]<<4; + for(j=0;j<0x10;j++){ *(t++)=v|t32[j]; } } @@ -1028,8 +1033,9 @@ void clCipher_Init56(clCipher *cipher,unsigned int key1,unsigned int key2){ void clCipher_Init56_CreateTable(unsigned char *r,unsigned char key){ int mul=((key&1)<<3)|5; int add=(key&0xE)|1; + int i; key>>=4; - for(int i=0;i<0x10;i++){ + for(i=0;i<0x10;i++){ key=(key*mul+add)&0xF; *(r++)=key; } @@ -1063,7 +1069,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ if(size_version=clData_GetBit(&d,16); hca->_dataOffset=clData_GetBit(&d,16); @@ -1076,7 +1082,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // fmt - if(size>=sizeof(stFormat) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='fmt\0'){ + if(size>=sizeof(stFormat) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x666D7400){/*'fmt\0'*/ clData_AddBit(&d,32); hca->_channelCount=clData_GetBit(&d,8); hca->_samplingRate=clData_GetBit(&d,24); @@ -1091,7 +1097,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // comp - if(size>=sizeof(stCompress) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='comp'){ + if(size>=sizeof(stCompress) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D70){/*'comp'*/ clData_AddBit(&d,32); hca->_blockSize=clData_GetBit(&d,16); hca->_comp_r01=clData_GetBit(&d,8); @@ -1109,7 +1115,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // dec - else if(size>=sizeof(stDecode) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='dec\0'){ + else if(size>=sizeof(stDecode) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x64656300){/*'dec\0'*/ unsigned char count1,count2,enableCount2; clData_AddBit(&d,32); hca->_blockSize=clData_GetBit(&d,16); @@ -1133,7 +1139,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // vbr - if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='vbr\0'){ + if(size>=sizeof(stVBR) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x76627200){/*'vbr\0'*/ clData_AddBit(&d,32); hca->_vbr_r01=clData_GetBit(&d,16); hca->_vbr_r02=clData_GetBit(&d,16); @@ -1144,7 +1150,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // ath - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='ath\0'){ + if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x61746800){/*'ath\0'*/ clData_AddBit(&d,32); hca->_ath_type=clData_GetBit(&d,16); }else{ @@ -1152,7 +1158,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // loop - if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='loop'){ + if(size>=sizeof(stLoop) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x6C6F6F70){/*'loop'*/ clData_AddBit(&d,32); hca->_loopStart=clData_GetBit(&d,32); hca->_loopEnd=clData_GetBit(&d,32); @@ -1170,7 +1176,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // ciph - if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='ciph'){ + if(size>=6 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x63697068){/*'ciph'*/ clData_AddBit(&d,32); hca->_ciph_type=clData_GetBit(&d,16); if(!(hca->_ciph_type==0||hca->_ciph_type==1||hca->_ciph_type==0x38))return -1; @@ -1180,7 +1186,7 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // rva - if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='rva\0'){ + if(size>=sizeof(stRVA) && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x72766100){/*'rva\0'*/ union { unsigned int i; float f; } v; clData_AddBit(&d,32); v.i=clData_GetBit(&d,32); @@ -1191,9 +1197,9 @@ int clHCA_Decode(clHCA *hca,void *data,unsigned int size,unsigned int address){ } // comm - if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)=='comm'){ + if(size>=5 && (clData_CheckBit(&d,32)&0x7F7F7F7F)==0x636F6D6D){/*'comm'*/ void * newmem; - int i; + unsigned int i; clData_AddBit(&d,32); hca->_comm_len=clData_GetBit(&d,8); if(hca->_comm_len>size)return -1; @@ -1673,11 +1679,11 @@ void stChannel_Decode5(stChannel *ch,int index){ for(i=0,count1=0x40,count2=1;i<7;i++,count1>>=1,count2<<=1){ const float *list1Float=(const float *)stChannel_Decode5_list1Int[i]; const float *list2Float=(const float *)stChannel_Decode5_list2Int[i]; + float *d1, *d2, *w; s1=s; s2=&s1[count2]; - float *d1=d; - float *d2=&d1[count2*2-1]; - float *w; + d1=d; + d2=&d1[count2*2-1]; for(j=0;jwave[index]; s1=&ch->wav2[0x40];s2=ch->wav3; - for(int i=0;i<0x40;i++)*(d++)=*(s1++)**(s++)+*(s2++); - for(int i=0;i<0x40;i++)*(d++)=*(s++)**(--s1)-*(s2++); + for(i=0;i<0x40;i++)*(d++)=*(s1++)**(s++)+*(s2++); + for(i=0;i<0x40;i++)*(d++)=*(s++)**(--s1)-*(s2++); s1=&ch->wav2[0x40-1];d=ch->wav3; - for(int i=0;i<0x40;i++)*(d++)=*(s1--)**(--s); - for(int i=0;i<0x40;i++)*(d++)=*(--s)**(++s1); + for(i=0;i<0x40;i++)*(d++)=*(s1--)**(--s); + for(i=0;i<0x40;i++)*(d++)=*(--s)**(++s1); } - diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 8baa4dc34..b2e014975 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -25,12 +25,17 @@ void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); +void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); /* ngc_dsp_decoder */ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ngc_dsp_mem(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, uint8_t * mem); + int32_t dsp_nibbles_to_samples(int32_t nibbles); void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); +void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); +void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be); /* ngc_dtk_decoder */ void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); @@ -114,10 +119,18 @@ void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t sa /* mpeg_decoder */ #ifdef VGM_USE_MPEG -mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, long given_sample_rate, int given_channels, coding_t *coding_type, int * actual_sample_rate, int * actual_channels); +mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels); +mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int fixed_frame_size, int fsb_padding); +mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count); + +void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels); void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do); -void decode_mpeg(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); -long mpeg_bytes_to_samples(long bytes, const struct mpg123_frameinfo *mi); + +void free_mpeg(mpeg_codec_data *data); +void reset_mpeg(VGMSTREAM *vgmstream); +void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample); + +long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data); void mpeg_set_error_logging(mpeg_codec_data * data, int enable); #endif @@ -141,15 +154,45 @@ void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_ void decode_at3plus(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); #endif -/* ffmpeg_decoder */ + #ifdef VGM_USE_FFMPEG +/* ffmpeg_decoder */ void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels); void reset_ffmpeg(VGMSTREAM *vgmstream); void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample); +void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); + +/* ffmpeg_decoder_utils */ +int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec); int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay); +int ffmpeg_make_riff_atrac3plus(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay); +int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode); int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size); int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian); +int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t sample_count, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align); + +/* XMA sample parser info (struct to avoid passing so much stuff, separate for reusing) */ +typedef struct { + int xma_version; + int channels; + int stream_mode; + off_t data_offset; + size_t data_size; + int loop_flag; + /* frame offsets */ + uint32_t loop_start_b; + uint32_t loop_end_b; + uint32_t loop_start_subframe; + uint32_t loop_end_subframe; + + /* output */ + int32_t num_samples; + int32_t skip_samples; + int32_t loop_start_sample; + int32_t loop_end_sample; +} xma_sample_data; +void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 341022811..d6d6376d6 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -256,6 +256,16 @@ void reset_ffmpeg(VGMSTREAM *vgmstream) { data->endOfStream = 0; data->endOfAudio = 0; data->samplesToDiscard = 0; + + /* consider skip samples (encoder delay), if manually set (otherwise let FFmpeg handle it) */ + if (data->skipSamplesSet) { + AVStream *stream = data->formatCtx->streams[data->streamIndex]; + /* sometimes (ex. AAC) after seeking to the first packet skip_samples is restored, but we want our value */ + stream->skip_samples = 0; + stream->start_skip_samples = 0; + + data->samplesToDiscard += data->skipSamples; + } } @@ -263,8 +273,8 @@ void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; int64_t ts; - /* Start from 0 and discard samples until loop_start (slower but not too noticeable) */ - /* Due to various FFmpeg quirks seeking to a sample is erratic in many formats (would need extra steps) */ + /* Start from 0 and discard samples until loop_start (slower but not too noticeable). + * Due to various FFmpeg quirks seeking to a sample is erratic in many formats (would need extra steps). */ data->samplesToDiscard = num_sample; ts = 0; @@ -276,210 +286,42 @@ void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample) { data->endOfStream = 0; data->endOfAudio = 0; -} + /* consider skip samples (encoder delay), if manually set (otherwise let FFmpeg handle it) */ + if (data->skipSamplesSet) { + AVStream *stream = data->formatCtx->streams[data->streamIndex]; + /* sometimes (ex. AAC) after seeking to the first packet skip_samples is restored, but we want our value */ + stream->skip_samples = 0; + stream->start_skip_samples = 0; - - -/* ******************************************** */ -/* FAKE RIFF HELPERS */ -/* ******************************************** */ -static int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec); - -/** - * Copies a ATRAC3 riff to buf - * - * returns number of bytes in buf or -1 when buf is not big enough - */ -int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) { - uint16_t codec_ATRAC3 = 0x0270; - size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4; - - if (buf_size < riff_size) - return -1; - - memcpy(buf+0x00, "RIFF", 4); - put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ - memcpy(buf+0x08, "WAVE", 4); - - memcpy(buf+0x0c, "fmt ", 4); - put_32bitLE(buf+0x10, 0x20);/*fmt size*/ - put_16bitLE(buf+0x14, codec_ATRAC3); - put_16bitLE(buf+0x16, channels); - put_32bitLE(buf+0x18, sample_rate); - put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ - put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */ - - put_16bitLE(buf+0x24, 0x0e); /* extra data size */ - put_16bitLE(buf+0x26, 1); /* unknown, always 1 */ - put_16bitLE(buf+0x28, channels==1 ? 0x0800 : 0x1000); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */ - put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */ - put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000); - put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */ - put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */ - put_16bitLE(buf+0x32, 0); /* unknown, always 0 */ - - memcpy(buf+0x34, "fact", 4); - put_32bitLE(buf+0x38, 0x8); /* fact size */ - put_32bitLE(buf+0x3c, sample_count); - put_32bitLE(buf+0x40, encoder_delay); - - memcpy(buf+0x44, "data", 4); - put_32bitLE(buf+0x48, data_size); /* data size */ - - return riff_size; -} - -/** - * Copies a XMA2 riff to buf - * - * returns number of bytes in buf or -1 when buf is not big enough - */ -int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size) { - uint16_t codec_XMA2 = 0x0166; - size_t riff_size = 4+4+ 4 + 0x3c + 4+4; - size_t bytecount; - uint32_t streams = 0; - uint32_t speakers = 0; /* see audiodefs.h */ - - if (buf_size < riff_size) - return -1; - - bytecount = sample_count * channels * sizeof(sample); - - /* untested (no support for > 2ch xma for now) */ - switch (channels) { - case 1: - streams = 1; - speakers = 0x00000004; /* FC */ - break; - case 2: - streams = 1; - speakers = 0x00000001 | 0x00000002; /* FL FR */ - break; - case 3: - streams = 3; - speakers = 0x00000001 | 0x00000002 | 0x00000004; /* FL FC FR */ - break; - case 4: - streams = 2; - speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020; /* FL FR BL BR */ - break; - case 5: - streams = 3; - speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000004; /* FL C FR BL BR*/ - break; - case 6: - streams = 3; - speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000200 | 0x00000400; /* FL FR BL BR SL SR */ - break; - default: - streams = 1; - speakers = 0x80000000; - break; + data->samplesToDiscard += data->skipSamples; } - - /*memset(buf,0, sizeof(uint8_t) * fmt_size);*/ - - memcpy(buf+0x00, "RIFF", 4); - put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ - memcpy(buf+0x08, "WAVE", 4); - - memcpy(buf+0x0c, "fmt ", 4); - put_32bitLE(buf+0x10, 0x34);/*fmt size*/ - put_16bitLE(buf+0x14, codec_XMA2); - put_16bitLE(buf+0x16, channels); - put_32bitLE(buf+0x18, sample_rate); - put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ - put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */ - put_16bitLE(buf+0x22, sizeof(sample)*8); /* bits per sample */ - - put_16bitLE(buf+0x24, 0x22); /* extra data size */ - put_16bitLE(buf+0x26, streams); /* number of streams */ - put_32bitLE(buf+0x28, speakers); /* speaker position */ - put_32bitLE(buf+0x2c, bytecount); /* PCM samples */ - put_32bitLE(buf+0x30, block_size); /* XMA block size */ - /* (looping values not set, expected to be handled externally) */ - put_32bitLE(buf+0x34, 0); /* play begin */ - put_32bitLE(buf+0x38, 0); /* play length */ - put_32bitLE(buf+0x3c, 0); /* loop begin */ - put_32bitLE(buf+0x40, 0); /* loop length */ - put_8bit(buf+0x44, 0); /* loop count */ - put_8bit(buf+0x45, 4); /* encoder version */ - put_16bitLE(buf+0x46, block_count); /* blocks count = entried in seek table */ - - memcpy(buf+0x48, "data", 4); - put_32bitLE(buf+0x4c, data_size); /* data size */ - - return riff_size; -} - - -/** - * Copies a XMA2 riff to buf from a fmt chunk offset - * - * returns number of bytes in buf or -1 when buf is not big enough - */ -int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian) { - size_t riff_size = 4+4+ 4 + 4+4+fmt_size + 4+4; - uint8_t chunk[100]; - - if (buf_size < riff_size || fmt_size > 100) - goto fail; - if (read_streamfile(chunk,fmt_offset,fmt_size, streamFile) != fmt_size) - goto fail; - - if (big_endian) - ffmpeg_fmt_chunk_swap_endian(chunk, 0x166); - - memcpy(buf+0x00, "RIFF", 4); - put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ - memcpy(buf+0x08, "WAVE", 4); - - memcpy(buf+0x0c, "fmt ", 4); - put_32bitLE(buf+0x10, fmt_size);/*fmt size*/ - memcpy(buf+0x14, chunk, fmt_size); - - memcpy(buf+0x14+fmt_size, "data", 4); - put_32bitLE(buf+0x14+fmt_size+4, data_size); /* data size */ - - return riff_size; - -fail: - return -1; } /** - * Swaps endianness + * Sets the number of samples to skip at the beginning of the stream, needed by some "gapless" formats. + * (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc to "set up" the decoder). + * - should be used at the beginning of the stream + * - should check if there are data->skipSamples before using this, to avoid overwritting FFmpeg's value (ex. AAC). * - * returns 0 on error + * This could be added per format in FFmpeg directly, but it's here for flexibility and due to bugs + * (FFmpeg's stream->(start_)skip_samples causes glitches in XMA). */ -static int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) { - if (codec != 0x166) - goto fail; +void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) { + AVStream *stream = NULL; + if (!data->formatCtx) + return; - put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/ - put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/ - put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/ - put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/ - put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/ - put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/ - put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/ - put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/ - put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/ - put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/ - put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/ - put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/ - put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/ - put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/ - put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/ - /* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/ - /* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/ - put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/ + /* overwrite FFmpeg's skip samples */ + stream = data->formatCtx->streams[data->streamIndex]; + stream->start_skip_samples = 0; /* used for the first packet *if* pts=0 */ + stream->skip_samples = 0; /* skip_samples can be used for any packet */ - return 1; + /* set skip samples with our internal discard */ + data->skipSamplesSet = 1; + data->samplesToDiscard = skip_samples; -fail: - return 0; + /* expose (info only) */ + data->skipSamples = skip_samples; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c new file mode 100644 index 000000000..cb92f7753 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder_utils.c @@ -0,0 +1,542 @@ +#include "coding.h" +#include "../vgmstream.h" + +#ifdef VGM_USE_FFMPEG + +#define XMA_CHECK_SKIPS 0 +#define XMA_BYTES_PER_PACKET 2048 +#define XMA_SAMPLES_PER_FRAME 512 +#define XMA_SAMPLES_PER_SUBFRAME 128 + +/* ******************************************** */ +/* INTERNAL UTILS */ +/* ******************************************** */ + +/** + * read num_bits (up to 25) from a bit offset. + * 25 since we read a 32 bit int, and need to adjust up to 7 bits from the byte-rounded fseek (32-7=25) + */ +static uint32_t read_bitsBE_b(off_t bit_offset, int num_bits, STREAMFILE *streamFile) { + uint32_t num, mask; + if (num_bits > 25) return -1; //??? + + num = read_32bitBE(bit_offset / 8, streamFile); /* fseek rounded to 8 */ + num = num << (bit_offset % 8); /* offset adjust (up to 7) */ + num = num >> (32 - num_bits); + mask = 0xffffffff >> (32 - num_bits); + + return num & mask; +} + + +/* ******************************************** */ +/* FAKE RIFF HELPERS */ +/* ******************************************** */ +/* All helpers copy a RIFF header to buf and returns the number of bytes in buf or -1 when buf is not big enough */ + +int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) { + uint16_t codec_ATRAC3 = 0x0270; + size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4; + + if (buf_size < riff_size) + return -1; + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0x20);/*fmt size*/ + put_16bitLE(buf+0x14, codec_ATRAC3); + put_16bitLE(buf+0x16, channels); + put_32bitLE(buf+0x18, sample_rate); + put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ + put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */ + + put_16bitLE(buf+0x24, 0x0e); /* extra data size */ + put_16bitLE(buf+0x26, 1); /* unknown, always 1 */ + put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */ + put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */ + put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000); + put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */ + put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */ + put_16bitLE(buf+0x32, 0); /* unknown, always 0 */ + + memcpy(buf+0x34, "fact", 4); + put_32bitLE(buf+0x38, 0x8); /* fact size */ + put_32bitLE(buf+0x3c, sample_count); + put_32bitLE(buf+0x40, encoder_delay); + + memcpy(buf+0x44, "data", 4); + put_32bitLE(buf+0x48, data_size); /* data size */ + + return riff_size; +} + +int ffmpeg_make_riff_atrac3plus(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int encoder_delay) { + uint16_t codec_ATRAC3plus = 0xfffe; /* wave format extensible */ + size_t riff_size = 4+4+ 4 + 0x3c + 0x14 + 4+4; + + if (buf_size < riff_size) + return -1; + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0x34);/*fmt size*/ + put_16bitLE(buf+0x14, codec_ATRAC3plus); + put_16bitLE(buf+0x16, channels); + put_32bitLE(buf+0x18, sample_rate); + put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ + put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */ + + put_16bitLE(buf+0x24, 0x22); /* extra data size */ + put_16bitLE(buf+0x26, 0x0800); /* samples per block */ + put_32bitLE(buf+0x28, 0x0000003); /* unknown */ + put_32bitBE(buf+0x2c, 0xBFAA23E9); /* GUID1 */ + put_32bitBE(buf+0x30, 0x58CB7144); /* GUID2 */ + put_32bitBE(buf+0x34, 0xA119FFFA); /* GUID3 */ + put_32bitBE(buf+0x38, 0x01E4CE62); /* GUID4 */ + put_16bitBE(buf+0x3c, 0x0010); /* unknown */ + put_16bitBE(buf+0x3e, 0x0000); /* config */ //todo this varies with block size, but FFmpeg doesn't use it + put_32bitBE(buf+0x40, 0x00000000); /* empty */ + put_32bitBE(buf+0x44, 0x00000000); /* empty */ + + memcpy(buf+0x48, "fact", 4); + put_32bitLE(buf+0x4c, 0x0c); /* fact size */ + put_32bitLE(buf+0x50, sample_count); + put_32bitLE(buf+0x54, 0); /* unknown */ + put_32bitLE(buf+0x58, encoder_delay); + + memcpy(buf+0x5c, "data", 4); + put_32bitLE(buf+0x60, data_size); /* data size */ + + return riff_size; +} + +int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode) { + uint16_t codec_XMA1 = 0x0165; + size_t riff_size; + int streams, i; + + /* stream disposition: + * 0: default (ex. 5ch = 2ch + 2ch + 1ch = 3 streams) + * 1: lineal (ex. 5ch = 1ch + 1ch + 1ch + 1ch + 1ch = 5 streams), unusual but exists + * others: not seen (ex. maybe 5ch = 2ch + 1ch + 1ch + 1ch = 4 streams) */ + switch(stream_mode) { + case 0 : streams = (channels + 1) / 2; break; + case 1 : streams = channels; break; + default: return 0; + } + + riff_size = 4+4+ 4 + 0x14 + 0x14*streams + 4+4; + + if (buf_size < riff_size) + return -1; + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0xc + 0x14*streams);/*fmt size*/ + put_16bitLE(buf+0x14, codec_XMA1); + put_16bitLE(buf+0x16, 16); /* bits per sample */ + put_16bitLE(buf+0x18, 0x10D6); /* encoder options */ + put_16bitLE(buf+0x1a, 0); /* largest stream skip (wrong, unneeded) */ + put_16bitLE(buf+0x1c, streams); /* number of streams */ + put_8bit (buf+0x1e, 0); /* loop count */ + put_8bit (buf+0x1f, 2); /* version */ + + for (i = 0; i < streams; i++) { + int stream_channels; + uint32_t speakers; + off_t off = 0x20 + 0x14*i;/* stream riff offset */ + + if (stream_mode == 1) { + /* lineal */ + stream_channels = 1; + switch(i) { /* per stream, values observed */ + case 0: speakers = 0x0001; break;/* L */ + case 1: speakers = 0x0002; break;/* R */ + case 2: speakers = 0x0004; break;/* C */ + case 3: speakers = 0x0008; break;/* LFE */ + case 4: speakers = 0x0040; break;/* LB */ + case 5: speakers = 0x0080; break;/* RB */ + case 6: speakers = 0x0000; break;/* ? */ + case 7: speakers = 0x0000; break;/* ? */ + default: speakers = 0; + } + } + else { + /* with odd channels the last stream is mono */ + stream_channels = channels / streams + (channels%2 != 0 && i+1 != streams ? 1 : 0); + switch(i) { /* per stream, values from xmaencode */ + case 0: speakers = stream_channels == 1 ? 0x0001 : 0x0201; break;/* L R */ + case 1: speakers = stream_channels == 1 ? 0x0004 : 0x0804; break;/* C LFE */ + case 2: speakers = stream_channels == 1 ? 0x0040 : 0x8040; break;/* LB RB */ + case 3: speakers = stream_channels == 1 ? 0x0000 : 0x0000; break;/* somehow empty (maybe should use 0x2010 LS RS) */ + default: speakers = 0; + } + } + + put_32bitLE(buf+off+0x00, sample_rate*stream_channels / sizeof(sample)); /* average bytes per second (wrong, unneeded) */ + put_32bitLE(buf+off+0x04, sample_rate); + put_32bitLE(buf+off+0x08, 0); /* loop start */ + put_32bitLE(buf+off+0x0c, 0); /* loop end */ + put_8bit (buf+off+0x10, 0); /* loop subframe */ + put_8bit (buf+off+0x11, channels); + put_16bitLE(buf+off+0x12, speakers); + } + + memcpy(buf+riff_size-4-4, "data", 4); + put_32bitLE(buf+riff_size-4, data_size); /* data size */ + + return riff_size; +} + +int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size) { + uint16_t codec_XMA2 = 0x0166; + size_t riff_size = 4+4+ 4 + 0x3c + 4+4; + size_t bytecount; + int streams; + uint32_t speakers; + + /* info from xma2defs.h, xact3wb.h and audiodefs.h */ + streams = (channels + 1) / 2; + switch (channels) { + case 1: speakers = 0x04; break; /* 1.0: FC */ + case 2: speakers = 0x01 | 0x02; break; /* 2.0: FL FR */ + case 3: speakers = 0x01 | 0x02 | 0x08; break; /* 2.1: FL FR LF */ + case 4: speakers = 0x01 | 0x02 | 0x10 | 0x20; break; /* 4.0: FL FR BL BR */ + case 5: speakers = 0x01 | 0x02 | 0x08 | 0x10 | 0x20; break; /* 4.1: FL FR LF BL BR */ + case 6: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20; break; /* 5.1: FL FR FC LF BL BR */ + case 7: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x0100; break; /* 6.1: FL FR FC LF BL BR BC */ + case 8: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80; break; /* 7.1: FL FR FC LF BL BR FLC FRC */ + default: speakers = 0; break; + } + + if (buf_size < riff_size) + return -1; + + bytecount = sample_count * channels * sizeof(sample); + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0x34);/*fmt size*/ + put_16bitLE(buf+0x14, codec_XMA2); + put_16bitLE(buf+0x16, channels); + put_32bitLE(buf+0x18, sample_rate); + put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong unneeded) */ + put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */ + put_16bitLE(buf+0x22, 16); /* bits per sample */ + + put_16bitLE(buf+0x24, 0x22); /* extra data size */ + put_16bitLE(buf+0x26, streams); /* number of streams */ + put_32bitLE(buf+0x28, speakers); /* speaker position */ + put_32bitLE(buf+0x2c, bytecount); /* PCM samples */ + put_32bitLE(buf+0x30, block_size); /* XMA block size */ + /* (looping values not set, expected to be handled externally) */ + put_32bitLE(buf+0x34, 0); /* play begin */ + put_32bitLE(buf+0x38, 0); /* play length */ + put_32bitLE(buf+0x3c, 0); /* loop begin */ + put_32bitLE(buf+0x40, 0); /* loop length */ + put_8bit(buf+0x44, 0); /* loop count */ + put_8bit(buf+0x45, 4); /* encoder version */ + put_16bitLE(buf+0x46, block_count); /* blocks count = entries in seek table */ + + memcpy(buf+0x48, "data", 4); + put_32bitLE(buf+0x4c, data_size); /* data size */ + + return riff_size; +} + +int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian) { + size_t riff_size = 4+4+ 4 + 4+4+fmt_size + 4+4; + uint8_t chunk[100]; + + if (buf_size < riff_size || fmt_size > 100) + goto fail; + if (read_streamfile(chunk,fmt_offset,fmt_size, streamFile) != fmt_size) + goto fail; + + if (big_endian) + ffmpeg_fmt_chunk_swap_endian(chunk, 0x166); + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, fmt_size);/*fmt size*/ + memcpy(buf+0x14, chunk, fmt_size); + + memcpy(buf+0x14+fmt_size, "data", 4); + put_32bitLE(buf+0x14+fmt_size+4, data_size); /* data size */ + + return riff_size; + +fail: + return -1; +} + +int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t sample_count, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align) { + size_t riff_size = 4+4+ 4 + 0x1a + 4+4; + + if (buf_size < riff_size) + return -1; + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "XWMA", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0x12);/*fmt size*/ + put_16bitLE(buf+0x14, codec); + put_16bitLE(buf+0x16, channels); + put_32bitLE(buf+0x18, sample_rate); + put_32bitLE(buf+0x1c, avg_bps); /* average bits per second, somehow vital for XWMA */ + put_16bitLE(buf+0x20, block_align); /* block align */ + put_16bitLE(buf+0x22, 16); /* bits per sample */ + put_16bitLE(buf+0x24, 0); /* unk */ + /* here goes the "dpds" table, but it's not needed by FFmpeg */ + + memcpy(buf+0x26, "data", 4); + put_32bitLE(buf+0x2a, data_size); /* data size */ + + return riff_size; +} + + +int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) { + if (codec != 0x166)/* XMA2 */ + goto fail; + + put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/ + put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/ + put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/ + put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/ + put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/ + put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/ + put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/ + put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/ + put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/ + put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/ + put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/ + put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/ + put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/ + put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/ + put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/ + /* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/ + /* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/ + put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/ + + return 1; + +fail: + return 0; +} + + +/* ******************************************** */ +/* XMA PARSING */ +/* ******************************************** */ + +/** + * Find total and loop samples by reading XMA frame headers. + * + * A XMA stream is made of packets, each containing N small frames of X samples. + * Frames are further divided into subframes for looping purposes. + * XMA1 and XMA2 only differ in the packet headers. + */ +void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) { + int frames = 0, samples = 0, loop_start_frame = 0, loop_end_frame = 0, skip_packets; +#if XMA_CHECK_SKIPS + int start_skip = 0, end_skip = 0, first_start_skip = 0, last_end_skip = 0; +#endif + uint32_t first_frame_b, packet_skip_count = 0, frame_size_b, packet_size_b; + uint64_t offset_b, packet_offset_b, frame_offset_b; + size_t size; + + uint32_t packet_size = XMA_BYTES_PER_PACKET; + off_t offset = xma->data_offset; + uint32_t stream_offset_b = xma->data_offset * 8; + + size = offset + xma->data_size; + packet_size_b = packet_size * 8; + + /* if we knew the streams mode then we could read just the first one and adjust samples later + * not a big deal but maybe important for skip stuff */ + //streams = (xma->stream_mode==0 ? (xma->channels + 1) / 2 : xma->channels) + skip_packets = 0; + + /* read packets */ + while (offset < size) { + offset_b = offset * 8; /* global offset in bits */ + offset += packet_size; /* global offset in bytes */ + + /* skip packets not owned by the first stream, since we only need samples from it */ + if (skip_packets && packet_skip_count) { + packet_skip_count--; + continue; + } + + /* XMA1 or XMA2 packet header */ + if (xma->xma_version == 1) { + //packet_sequence = read_bitsBE_b(offset_b+0, 4, streamFile); /* numbered from 0 to N */ + //unknown = read_bitsBE_b(offset_b+4, 2, streamFile); /* packet_metadata? (always 2) */ + first_frame_b = read_bitsBE_b(offset_b+6, 15, streamFile); /* offset in bits inside the packet */ + packet_skip_count = read_bitsBE_b(offset_b+21, 11, streamFile); /* packets to skip for next packet of this stream */ + } else { + //frame_count = read_bitsBE_b(offset_b+0, 6, streamFile); /* frames that begin in this packet */ + first_frame_b = read_bitsBE_b(offset_b+6, 15, streamFile); /* offset in bits inside this packet */ + //packet_metadata = read_bitsBE_b(offset_b+21, 3, streamFile); /* packet_metadata (always 1) */ + packet_skip_count = read_bitsBE_b(offset_b+24, 8, streamFile); /* packets to skip for next packet of this stream */ + } + + /* full packet skip */ + if (packet_skip_count == 0x7FF) { + packet_skip_count = 0; + continue; + } + if (packet_skip_count > 255) { /* seen in some (converted?) XMA1 */ + packet_skip_count = 0; + } + VGM_ASSERT(packet_skip_count > 10, "XMA: found big packet skip %i\n", packet_skip_count);//a bit unusual... + //VGM_LOG("packet: off=%x, ff=%i, ps=%i\n", offset, first_frame_b, packet_skip_b); + + packet_offset_b = 4*8 + first_frame_b; /* packet offset in bits */ + + /* read packet frames */ + while (packet_offset_b < packet_size_b) { + frame_offset_b = offset_b + packet_offset_b; /* in bits for aligment stuff */ + + //todo not sure if frames or frames+1 (considering skip_samples) + if (xma->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == xma->loop_start_b) + loop_start_frame = frames; + if (xma->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == xma->loop_end_b) + loop_end_frame = frames; + + + /* XMA1/2 frame header */ + frame_size_b = read_bitsBE_b(frame_offset_b, 15, streamFile); + frame_offset_b += 15; + if (frame_size_b == 0) /* observed in some files with empty frames/packets */ + break; + packet_offset_b += frame_size_b; /* including header */ + +#if 0 + { + uint32_t frame_config + frame_config = read_bitsBE_b(frame_offset_b, 15, streamFile); + + //VGM_LOG(" frame %04i: off_b=%I64x (~0x%I64x), fs_b=%i (~0x%x), fs=%x\n",frames, frame_offset_b, frame_offset_b/8, frame_size_b,frame_size_b/8, frame_config); + + //if (frame_config != 0x7f00) /* "contains all subframes"? */ + // continue; // todo read packet end bit instead + } +#endif + frame_offset_b += 15; + + if (frame_size_b == 0x7FFF) { /* end packet frame marker */ + break; + } + +#if XMA_CHECK_SKIPS + // more header stuff (info from FFmpeg) + { + int flag; + + /* ignore "postproc transform" */ + if (xma->channels > 1) { + flag = read_bitsBE_b(frame_offset_b, 1, streamFile); + frame_offset_b += 1; + if (flag) { + flag = read_bitsBE_b(frame_offset_b, 1, streamFile); + frame_offset_b += 1; + if (flag) { + frame_offset_b += 1 + 4 * xma->channels*xma->channels; /* 4-something per double channel? */ + } + } + } + + /* get start/end skips to get the proper number of samples */ //todo check if first bit =1 means full 512 skip + flag = read_bitsBE_b(frame_offset_b, 1, streamFile); + frame_offset_b += 1; + if (flag) { + int new_skip; + + /* get start skip */ + flag = read_bitsBE_b(frame_offset_b, 1, streamFile); + frame_offset_b += 1; + if (flag) { + VGM_LOG("start_skip at 0x%I64x\n", frame_offset_b); + new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile); + frame_offset_b += 10; + VGM_ASSERT(start_skip, "XMA: more than one start_skip (%i)\n", new_skip); + + if (new_skip > XMA_SAMPLES_PER_FRAME) { /* from xmaencode */ + VGM_LOG("XMA: bad start_skip (%i)\n", new_skip); + new_skip = XMA_SAMPLES_PER_FRAME; + } + + if (frames==0) first_start_skip = new_skip; /* sometimes in the middle */ + start_skip += new_skip; + } + + /* get end skip */ + flag = read_bitsBE_b(frame_offset_b, 1, streamFile); + frame_offset_b += 1; + if (flag) { + VGM_LOG("end_skip at 0x%I64x\n", frame_offset_b); + new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile); + frame_offset_b += 10; + VGM_ASSERT(end_skip, "XMA: more than one end_skip (%i)\n", new_skip); + + if (new_skip > XMA_SAMPLES_PER_FRAME) { /* from xmaencode */ + VGM_LOG("XMA: bad end_skip (%i)\n", new_skip); + new_skip = XMA_SAMPLES_PER_FRAME; + } + + last_end_skip = new_skip; /* not seen */ + end_skip += new_skip; + } + + VGM_LOG(" skip: st=%i, ed=%i\n", start_skip, end_skip); + } + } +#endif + + samples += XMA_SAMPLES_PER_FRAME; + frames++; + } + } + +#if XMA_CHECK_SKIPS + //todo this seems to usually work, but not always + /* apply skips (not sure why 64, empty samples generated by the decoder not in the file?) */ + samples = samples + 64 - start_skip; + samples = samples + 64 - end_skip; + + xma->skip_samples = 64 + 512; //todo not always correct +#endif + + xma->num_samples = samples; + + if (xma->loop_flag && loop_end_frame > loop_start_frame) { + xma->loop_start_sample = loop_start_frame * XMA_SAMPLES_PER_FRAME + xma->loop_start_subframe * XMA_SAMPLES_PER_SUBFRAME; + xma->loop_end_sample = loop_end_frame * XMA_SAMPLES_PER_FRAME + xma->loop_end_subframe * XMA_SAMPLES_PER_SUBFRAME; +#if XMA_CHECK_SKIPS + /* maybe this is needed */ + //xma->loop_start_sample -= xma->skip_samples; + //xma->loop_end_sample -= xma->skip_samples; +#endif + } +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index 3700383cb..4978a71dc 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -1,10 +1,23 @@ #include "../util.h" #include "coding.h" -const int32_t ADPCMTable[89] = +/** + * IMA ADPCM algorithms (expand one nibble to one sample, based on prev sample/history and step table). + * Nibbles are usually grouped in blocks/chunks, with a header, containing 1 or N channels + * + * All IMAs are mostly the same with these variations: + * - interleave: blocks and channels are handled externally (layouts) or internally (mixed channels) + * - block header: none (external), normal (4 bytes of history 16b + step 16b) or others; per channel/global + * - expand type: ms-ima style or others; low or high nibble first + * + * todo: + * MS IMAs have the last sample of the prev block in the block header. In Microsoft implementation, the header sample + * is written first and last sample is skipped (since they match). vgmstream ignores the header sample and + * writes the last one instead. This means the very first sample in the first header in a stream is incorrectly skipped. + */ +static const int32_t ADPCMTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, @@ -17,47 +30,145 @@ const int32_t ADPCMTable[89] = 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; -const int IMA_IndexTable[16] = - +static const int IMA_IndexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; + +/* Original IMA */ +static void ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + //"original" ima nibble expansion + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_decoded = *hist1 << 3; + step = ADPCMTable[*step_index]; + delta = step * (sample_nibble & 7) * 2 + step; + if (sample_nibble & 8) + sample_decoded -= delta; + else + sample_decoded += delta; + + *hist1 = clamp16(sample_decoded >> 3); + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; +} + +/* Microsoft's IMA (most common) */ +static void ms_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_decoded = *hist1; + step = ADPCMTable[*step_index]; + delta = step >> 3; + if (sample_nibble & 1) delta += step >> 2; + if (sample_nibble & 2) delta += step >> 1; + if (sample_nibble & 4) delta += step; + if (sample_nibble & 8) + sample_decoded -= delta; + else + sample_decoded += delta; + + *hist1 = clamp16(sample_decoded); + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; +} + +/* Apple's MS IMA variation. Exactly the same except it uses 16b history (probably more sensitive to overflow/sign extend) */ +static void ms_ima_expand_nibble_16(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int16_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_decoded = *hist1; + step = ADPCMTable[*step_index]; + delta = step >> 3; + if (sample_nibble & 1) delta += step >> 2; + if (sample_nibble & 2) delta += step >> 1; + if (sample_nibble & 4) delta += step; + if (sample_nibble & 8) + sample_decoded -= delta; + else + sample_decoded += delta; + + *hist1 = clamp16(sample_decoded); //no need for this actually + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; +} + +/* update step_index before doing current sample */ +static void snds_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; + + step = ADPCMTable[*step_index]; + delta = (sample_nibble & 7) * step / 4 + step / 8; + if (sample_nibble & 8) delta = -delta; + sample_decoded = *hist1 + delta; + + *hist1 = clamp16(sample_decoded); +} + +/* algorithm by aluigi, unsure if it's a known IMA variation */ +static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample_nibble, sample_decoded, step, delta; + + sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample_decoded = *hist1; + step = ADPCMTable[*step_index]; + delta = 0; + if(sample_nibble & 4) delta = step << 2; + if(sample_nibble & 2) delta += step << 1; + if(sample_nibble & 1) delta += step; + delta >>= 2; + if(sample_nibble & 8) + sample_decoded -= delta; + else + sample_decoded += delta; + + *hist1 = clamp16(sample_decoded); + *step_index += IMA_IndexTable[sample_nibble]; + if (*step_index < 0) *step_index=0; + if (*step_index > 88) *step_index=88; +} + + void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int32_t sample_count; - int32_t hist1 = stream->adpcm_history1_16; + int i, sample_count; + + int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16? int step_index = stream->adpcm_step_index; - if (first_sample==0) { - hist1 = read_16bitLE(stream->offset,stream->streamfile); - step_index = read_16bitLE(stream->offset+2,stream->streamfile); + //external interleave + + //normal header + if (first_sample == 0) { + off_t header_offset = stream->offset; + + hist1 = read_16bitLE(header_offset,stream->streamfile); + step_index = read_16bitLE(header_offset+2,stream->streamfile); + + //todo clip step_index? } for (i=first_sample,sample_count=0; ioffset+4+i/2,stream->streamfile) >> (i&1?4:0))&0xf; - int delta; - int step = ADPCMTable[step_index]; + off_t byte_offset = stream->offset + 4 + i/2; + int nibble_shift = (i&1?4:0); //low nibble first - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - outbuf[sample_count] = clamp16(hist1 - delta); - else - outbuf[sample_count] = clamp16(hist1 + delta); - - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - hist1 = outbuf[sample_count]; + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } stream->adpcm_history1_16 = hist1; @@ -65,571 +176,431 @@ void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci } void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int32_t sample_count; - int32_t hist1 = stream->adpcm_history1_16; + int i, sample_count; + + int32_t hist1 = stream->adpcm_history1_16;//todo unneeded 16? int step_index = stream->adpcm_step_index; - if (first_sample==0) { - hist1 = read_16bitLE(stream->offset,stream->streamfile); - step_index = read_8bit(stream->offset+2,stream->streamfile); + //external interleave + + //normal header + if (first_sample == 0) { + off_t header_offset = stream->offset; + + hist1 = read_16bitLE(header_offset,stream->streamfile); + step_index = read_8bit(header_offset+2,stream->streamfile); //todo use 8bit in all MS IMA? + + //todo clip step_index? } for (i=first_sample,sample_count=0; ioffset+4+i/2,stream->streamfile) >> (i&1?0:4))&0xf; - int delta; - int step = ADPCMTable[step_index]; + off_t byte_offset = stream->offset + 4 + i/2; + int nibble_shift = (i&1?0:4); //high nibble first - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - outbuf[sample_count] = clamp16(hist1 - delta); - else - outbuf[sample_count] = clamp16(hist1 + delta); - - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - hist1 = outbuf[sample_count]; + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } stream->adpcm_history1_16 = hist1; stream->adpcm_step_index = step_index; } -/* Xbox IMA is MS IMA, but I'll leave it alone for now (esp as it has > 2 channel support) */ void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - int i=first_sample; - int sample_nibble; - int sample_decoded; - int delta; - int block_samples = (vgmstream->interleave_block_size - vgmstream->channels * 4) * 2 / vgmstream->channels; + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset=stream->offset; + //internal interleave (configurable size), mixed channels (4 byte per ch) + int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels; first_sample = first_sample % block_samples; + //normal header (per channel) if (first_sample == 0) { + off_t header_offset = stream->offset + 4*channel; - hist1 = read_16bitLE(offset+channel*4,stream->streamfile); - step_index = read_16bitLE(offset+channel*4+2,stream->streamfile); - + hist1 = read_16bitLE(header_offset,stream->streamfile); + step_index = read_8bit(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*vgmstream->channels + (i/8*4*vgmstream->channels) + (i%8)/2 + 4*channel; - - sample_nibble = (read_8bit(offset,stream->streamfile) >> (i&1?4:0))&0xf; - - sample_decoded=hist1; - - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + off_t byte_offset = stream->offset + 4*channel + 4*vgmstream->channels + i/8*4*vgmstream->channels + (i%8)/2; + int nibble_shift = (i&1?4:0); //low nibble first + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - // Only increment offset on complete frame + //internal interleave: increment offset on complete frame if (i == block_samples) stream->offset += vgmstream->interleave_block_size; - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; } -// "Radical ADPCM", essentially MS IMA void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { - int i=first_sample; - int sample_nibble; - int sample_decoded; - int delta; - int block_samples = (vgmstream->interleave_block_size - vgmstream->channels * 4) * 2 / vgmstream->channels; + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset=stream->offset; + //internal interleave (configurable size), mixed channels (4 byte per ch) + int block_samples = (vgmstream->interleave_block_size - 4*vgmstream->channels) * 2 / vgmstream->channels; first_sample = first_sample % block_samples; + //inverted header (per channel) if (first_sample == 0) { + off_t header_offset = stream->offset + 4*channel; - hist1 = read_16bitLE(offset+channel*4+2,stream->streamfile); - step_index = read_16bitLE(offset+channel*4,stream->streamfile); - + step_index = read_16bitLE(header_offset,stream->streamfile); + hist1 = 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*vgmstream->channels + (i/2*vgmstream->channels) + channel; - - sample_nibble = (read_8bit(offset,stream->streamfile) >> (i&1?4:0))&0xf; - - sample_decoded=hist1; - - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + off_t byte_offset = stream->offset + 4*vgmstream->channels + channel + i/2*vgmstream->channels; + int nibble_shift = (i&1?4:0); //low nibble first + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - // Only increment offset on complete frame + //internal interleave: increment offset on complete frame if (i == block_samples) stream->offset += vgmstream->interleave_block_size; - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; } -// "Radical ADPCM", mono form (for use with interleave layout) -// I think this is exactly equivalent to mono MS APDCM, but I need a -// channel-agnostic version to use within an interleave. void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i=first_sample; - int sample_nibble; - int sample_decoded; - int delta; - int block_samples = 0x14 * 2; + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset=stream->offset; + //semi-external interleave? + int block_samples = 0x14 * 2; first_sample = first_sample % block_samples; + //inverted header if (first_sample == 0) { + off_t header_offset = stream->offset; - hist1 = read_16bitLE(offset+2,stream->streamfile); - step_index = read_16bitLE(offset,stream->streamfile); - + step_index = read_16bitLE(header_offset,stream->streamfile); + hist1 = 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/2); - - sample_nibble = (read_8bit(offset,stream->streamfile) >> (i&1?4:0))&0xf; - - sample_decoded=hist1; - - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + off_t byte_offset = stream->offset + 4 + i/2; + int nibble_shift = (i&1?4:0); //low nibble first + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + stream->adpcm_history1_32 = hist1; + 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 */ 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=first_sample; - int sample_nibble; - int sample_decoded; - int delta; + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset=stream->offset; - if(vgmstream->channels==1) - first_sample = first_sample % 32; - else - first_sample = first_sample % (32*(vgmstream->channels&2)); + off_t offset = stream->offset; + //internal interleave (0x20+4 size), mixed channels (4 byte per ch, mixed stereo) + int block_samples = (vgmstream->channels==1) ? + 32 : + 32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using % below + first_sample = first_sample % block_samples; + + //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); + } - if(vgmstream->layout_type==layout_ea_blocked) { - hist1 = read_16bitLE(offset,stream->streamfile); - step_index = read_16bitLE(offset+2,stream->streamfile); - } else { - hist1 = read_16bitLE(offset+(channel%2)*4,stream->streamfile); - step_index = read_16bitLE(offset+(channel%2)*4+2,stream->streamfile); - } + hist1 = read_16bitLE(header_offset,stream->streamfile); + 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; ilayout_type==layout_ea_blocked) - offset = stream->offset + (i/8*4+(i%8)/2+4); - else { - if(channelspacing==1) - offset = stream->offset + 4 + (i/8*4+(i%8)/2+4*(channel%2)); - else - offset = stream->offset + 4*2 + (i/8*4*2+(i%8)/2+4*(channel%2)); - } + if(vgmstream->layout_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; + } + nibble_shift = (i&1?4:0); //low nibble first - sample_nibble = (read_8bit(offset,stream->streamfile) >> (i&1?4:0))&0xf; - - sample_decoded=hist1; - - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + ms_ima_expand_nibble(stream, offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - // Only increment offset on complete frame - if(vgmstream->layout_type==layout_ea_blocked) { - 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; - } - } - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + //internal interleave: increment offset on complete frame + if(vgmstream->layout_type==layout_ea_blocked) { + 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; + } + } + + 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=first_sample; - int sample_nibble; - int sample_decoded; - int delta; + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - off_t offset=stream->offset; - if(vgmstream->channels==1) - first_sample = first_sample % 32; - else - first_sample = first_sample % (32*(vgmstream->channels&2)); + 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 + first_sample = first_sample % block_samples; + + //normal header if (first_sample == 0) { + off_t header_offset = stream->offset; - hist1 = read_16bitLE(offset,stream->streamfile); - step_index = read_16bitLE(offset+2,stream->streamfile); - + hist1 = read_16bitLE(header_offset,stream->streamfile); + 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); + offset = stream->offset + 4 + i/8*4 + (i%8)/2; + nibble_shift = (i&1?4:0); //low nibble first - sample_nibble = (read_8bit(offset,stream->streamfile) >> (i&1?4:0))&0xf; - - sample_decoded=hist1; - - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + ms_ima_expand_nibble(stream, offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - // Only 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; - } - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + //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; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; } void decode_dvi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; + //external interleave + + //no header + for (i=first_sample,sample_count=0; ioffset + i/2; + int nibble_shift = (i&1?0:4); //high nibble first (old-style DVI) - sample_byte = read_8bit(stream->offset+i/2,stream->streamfile); - /* old-style DVI takes high nibble first */ - sample_nibble = (sample_byte >> (i&1?0:4))&0xf; - - sample_decoded = hist1; - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble&0x7]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; } - void decode_eacs_ima(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i; - VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]); + VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]);//todo pass externally for consistency + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; - vgmstream->get_high_nibble=!vgmstream->get_high_nibble; + //external interleave - if((first_sample) && (channelspacing==1)) - vgmstream->get_high_nibble=!vgmstream->get_high_nibble; + //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 - sample_byte = read_8bit(stream->offset+i,stream->streamfile); - sample_nibble = (sample_byte >> (vgmstream->get_high_nibble?0:4))&0xf; - - sample_decoded = hist1; - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble&0x7]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; } void decode_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; + int i, sample_count; - int32_t sample_count = 0; - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; - for (i = first_sample, sample_count = 0; ioffset + i / 2, stream->streamfile); - sample_nibble = (sample_byte >> (i & 1 ? 4 : 0)) & 0xf; + //no header - sample_decoded = hist1 << 3; + for (i=first_sample,sample_count=0; ioffset + i/2; + int nibble_shift = (i&1?4:0); //low nibble order - delta = step * (sample_nibble & 7) * 2 + step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; + ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } - hist1 = clamp16(sample_decoded >> 3); - - step_index += IMA_IndexTable[sample_nibble & 0x7]; - if (step_index < 0) step_index = 0; - if (step_index > 88) step_index = 88; - - outbuf[sample_count] = (short)(hist1); - } - - stream->adpcm_history1_32 = hist1; - stream->adpcm_step_index = step_index; + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; } void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { - int i; + int i, sample_count; - int32_t sample_count=0; - int16_t hist1=stream->adpcm_history1_16; + int16_t hist1 = stream->adpcm_history1_16;//todo unneeded 16? int step_index = stream->adpcm_step_index; off_t packet_offset = stream->offset + first_sample/64*34; - first_sample = first_sample % 64; + //semi-internal interleave //todo what + int block_samples = 64; + first_sample = first_sample % block_samples; - if (first_sample == 0) - { + //2-byte header + if (first_sample == 0) { hist1 = (int16_t)((uint16_t)read_16bitBE(packet_offset,stream->streamfile) & 0xff80); step_index = read_8bit(packet_offset+1,stream->streamfile) & 0x7f; + + //todo no clamp } for (i=first_sample,sample_count=0; istreamfile); - sample_nibble = (sample_byte >> (i&1?4:0))&0xf; - - sample_decoded = hist1; - delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) - sample_decoded -= delta; - else - sample_decoded += delta; - - hist1=clamp16(sample_decoded); - - step_index += IMA_IndexTable[sample_nibble&0x7]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - - outbuf[sample_count]=(short)(hist1); + ms_ima_expand_nibble_16(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - stream->adpcm_history1_16=hist1; - stream->adpcm_step_index=step_index; + stream->adpcm_history1_16 = hist1; + stream->adpcm_step_index = step_index; } void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i; + int i, sample_count; - int32_t sample_count=0; - int32_t hist1=stream->adpcm_history1_32; + int32_t hist1 = stream->adpcm_history1_32; int step_index = stream->adpcm_step_index; + //external interleave + + //no header + for (i=first_sample,sample_count=0; ioffset + i;//one nibble per channel + int nibble_shift = (channel==0?0:4); //high nibble first, based on channel - sample_byte = read_8bit(stream->offset+i,stream->streamfile); - sample_nibble = (sample_byte >> (channel==0?0:4))&0xf; - - // update step before doing current sample - step_index += IMA_IndexTable[sample_nibble]; - if (step_index < 0) step_index=0; - if (step_index > 88) step_index=88; - step = ADPCMTable[step_index]; - - delta = (sample_nibble & 7) * step / 4 + step / 8; - if (sample_nibble & 8) delta = -delta; - sample_decoded = hist1 + delta; - - hist1=clamp16(sample_decoded); - - outbuf[sample_count]=(short)(hist1); + snds_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); } - stream->adpcm_history1_32=hist1; - stream->adpcm_step_index=step_index; + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; } +void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { + int i, sample_count; + + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + //internal/byte interleave + + //no header + + for (i=first_sample,sample_count=0; ioffset + (vgmstream->channels==1 ? i/2 : i); //one nibble per channel if stereo + int nibble_shift = (vgmstream->channels==1) ? //todo simplify + (i&1?0:4) : //high nibble first(?) + (channel==0?4:0); //low=ch0, high=ch1 (this is correct compared to vids) + + otns_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} + +void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { + int i, sample_count; + + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + + //internal interleave + int block_samples = (36 - 4) * 2; /* block size - header, 2 samples per byte */ + first_sample = first_sample % block_samples; + + //interleaved header (all hist per channel + all step_index per channel) + if (first_sample == 0) { + off_t hist_offset = stream->offset + 2*channel; + off_t step_offset = stream->offset + 2*channel + 2*vgmstream->channels; + + hist1 = read_16bitLE(hist_offset,stream->streamfile); + step_index = read_8bit(step_offset,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*vgmstream->channels + 2*channel + i/4*2*vgmstream->channels + (i%4)/2;//2-byte per channel + int nibble_shift = (i&1?4:0); //low nibble first + + ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); + outbuf[sample_count] = (short)(hist1); + } + + //internal interleave: increment offset on complete frame + if (i == block_samples) stream->offset += 36*vgmstream->channels; + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c index f7b43c5b7..4fbd1b9fa 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_decoder.c @@ -1,142 +1,87 @@ +#include "coding.h" +#include "../util.h" #include "../vgmstream.h" #ifdef VGM_USE_MPEG -#include -#include -#include "coding.h" -#include "../util.h" +#define AHX_EXPECTED_FRAME_SIZE 0x414 +#define MPEG_DEFAULT_BUFFER_SIZE 0x1000 /* should be >= AHX_EXPECTED_FRAME_SIZE */ +#define MPEG_SYNC_BITS 0xFFE00000 +#define MPEG_PADDING_BIT 0x200 -/* mono, mpg123 expects frames of 0x414 (160kbps, 22050Hz) but they - * actually vary and are much shorter */ -void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream, - mpeg_codec_data * data, - sample * outbuf, int32_t samples_to_do) { - int samples_done = 0; +static mpeg_codec_data *init_mpeg_codec_data_internal(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int interleaved, int fixed_frame_size, int fsb_padding); +static mpg123_handle * init_mpg123_handle(); - while (samples_done < samples_to_do) { - size_t bytes_done; - int rc; +static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); +static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); +static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size); - if (!data->buffer_full) { - /* fill buffer up to next frame ending (or file ending) */ - int bytes_into_header = 0; - const uint8_t header[4] = {0xff,0xf5,0xe0,0xc0}; - off_t frame_offset = 0; +static void update_frame_sizes(mpeg_codec_data * data, VGMSTREAMCHANNEL *stream); +static void update_base_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamFile, off_t start_offset, int fixed_frame_size, int current_frame_size, int fsb_padding); - /* assume that we are starting at a header, skip it and look for the - * next one */ - read_streamfile(data->buffer, stream->offset+frame_offset, 4, - stream->streamfile); - frame_offset += 4; - do { - uint8_t byte; - byte = - read_8bit(stream->offset+frame_offset,stream->streamfile); - data->buffer[frame_offset] = byte; - frame_offset++; - - if (byte == header[bytes_into_header]) { - bytes_into_header++; - } else { - /* This might have been the first byte of the header, so - * we need to check again. - * No need to get more complicated than this, though, since - * there are no repeated characters in the search string. */ - if (bytes_into_header>0) { - frame_offset--; - } - bytes_into_header=0; - } - - if (bytes_into_header==4) { - break; - } - } while (frame_offset < AHX_EXPECTED_FRAME_SIZE); - - if (bytes_into_header==4) frame_offset-=4; - memset(data->buffer+frame_offset,0, - AHX_EXPECTED_FRAME_SIZE-frame_offset); - - data->buffer_full = 1; - data->buffer_used = 0; - - stream->offset += frame_offset; - } - - if (!data->buffer_used) { - rc = mpg123_decode(data->m, - data->buffer,AHX_EXPECTED_FRAME_SIZE, - (unsigned char *)(outbuf+samples_done), - (samples_to_do-samples_done)*sizeof(sample), - &bytes_done); - data->buffer_used = 1; - } else { - rc = mpg123_decode(data->m, - NULL,0, - (unsigned char *)(outbuf+samples_done), - (samples_to_do-samples_done)*sizeof(sample), - &bytes_done); - } - - if (rc == MPG123_NEED_MORE) data->buffer_full = 0; - - samples_done += bytes_done/sizeof(sample); - } +/** + * Inits regular MPEG. + */ +mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) { + return init_mpeg_codec_data_internal(streamfile, start_offset, coding_type, channels, 0, 0, 0); } -mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, long given_sample_rate, int given_channels, coding_t *coding_type, int * actual_sample_rate, int * actual_channels) { - int rc; - off_t read_offset; +/** + * Init interleaved MPEG. + */ +mpeg_codec_data *init_mpeg_codec_data_interleaved(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int fixed_frame_size, int fsb_padding) { + return init_mpeg_codec_data_internal(streamfile, start_offset, coding_type, channels, 1, fixed_frame_size, fsb_padding); +} + +static mpeg_codec_data *init_mpeg_codec_data_internal(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels, int interleaved, int fixed_frame_size, int fsb_padding) { mpeg_codec_data *data = NULL; + int current_frame_size = 0; + /* init codec */ data = calloc(1,sizeof(mpeg_codec_data)); - if (!data) goto mpeg_fail; + if (!data) goto fail; - data->m = mpg123_new(NULL,&rc); - if (rc==MPG123_NOT_INITIALIZED) { - if (mpg123_init()!=MPG123_OK) goto mpeg_fail; - data->m = mpg123_new(NULL,&rc); - if (rc!=MPG123_OK) goto mpeg_fail; - } else if (rc!=MPG123_OK) { - goto mpeg_fail; - } + data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE; + data->buffer = calloc(sizeof(uint8_t), data->buffer_size); + if (!data->buffer) goto fail; - mpg123_param(data->m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); - - if (mpg123_open_feed(data->m)!=MPG123_OK) { - goto mpeg_fail; - } + data->m = init_mpg123_handle(); + if (!data->m) goto fail; /* check format */ - read_offset=0; - do { - size_t bytes_done; - if (read_streamfile(data->buffer, start_offset+read_offset, - MPEG_BUFFER_SIZE,streamfile) != - MPEG_BUFFER_SIZE) goto mpeg_fail; - read_offset+=1; - rc = mpg123_decode(data->m,data->buffer,MPEG_BUFFER_SIZE, - NULL,0,&bytes_done); - if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && - rc != MPG123_NEED_MORE) goto mpeg_fail; - } while (rc != MPG123_NEW_FORMAT); - { - long rate; - int channels,encoding; - struct mpg123_frameinfo mi; - rc = mpg123_getformat(data->m,&rate,&channels,&encoding); - if (rc != MPG123_OK) goto mpeg_fail; - //fprintf(stderr,"getformat ok, sr=%ld (%ld) ch=%d (%d) enc=%d (%d)\n",rate,given_sample_rate,channels,vgmstream->channels,encoding,MPG123_ENC_SIGNED_16); - if ((given_sample_rate != -1 && rate != given_sample_rate) || - (given_channels != -1 && channels != given_channels) || - encoding != MPG123_ENC_SIGNED_16) goto mpeg_fail; - mpg123_info(data->m,&mi); - if (given_sample_rate != -1 && - mi.rate != given_sample_rate) goto mpeg_fail; + mpg123_handle *main_m = data->m; + off_t read_offset = 0; + int rc; - //fprintf(stderr,"mi.version=%d, mi.layer=%d\n",mi.version,mi.layer); + long sample_rate_per_frame; + int channels_per_frame, encoding; + size_t samples_per_frame; + struct mpg123_frameinfo mi; + + /* read first frame(s) */ + do { + size_t bytes_done; + if (read_streamfile(data->buffer, start_offset+read_offset, data->buffer_size, streamfile) != data->buffer_size) + goto fail; + read_offset+=1; + + rc = mpg123_decode(main_m, data->buffer,data->buffer_size, NULL,0, &bytes_done); + if (rc != MPG123_OK && rc != MPG123_NEW_FORMAT && rc != MPG123_NEED_MORE) goto fail; //todo handle MPG123_DONE + } while (rc != MPG123_NEW_FORMAT); + + /* check first frame header and validate */ + rc = mpg123_getformat(main_m,&sample_rate_per_frame,&channels_per_frame,&encoding); + if (rc != MPG123_OK) goto fail; + + mpg123_info(main_m,&mi); + + if (encoding != MPG123_ENC_SIGNED_16) + goto fail; + if (sample_rate_per_frame != mi.rate) + goto fail; + if ((channels != -1 && channels_per_frame != channels && !interleaved)) + goto fail; if (mi.version == MPG123_1_0 && mi.layer == 1) *coding_type = coding_MPEG1_L1; @@ -156,40 +101,157 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset *coding_type = coding_MPEG25_L2; else if (mi.version == MPG123_2_5 && mi.layer == 3) *coding_type = coding_MPEG25_L3; - else goto mpeg_fail; + else goto fail; - if ( actual_sample_rate ) *actual_sample_rate = rate; - if ( actual_channels ) *actual_channels = channels; + if (mi.layer == 1) + samples_per_frame = 384; + else if (mi.layer == 2) + samples_per_frame = 1152; + else if (mi.layer == 3 && mi.version == MPG123_1_0) //MP3 + samples_per_frame = 1152; + else if (mi.layer == 3) + samples_per_frame = 576; + else goto fail; + + data->sample_rate_per_frame = sample_rate_per_frame; + data->channels_per_frame = channels_per_frame; + data->samples_per_frame = samples_per_frame; + + /* unlikely (can fixed with bigger buffer or a feed loop) */ + if (mi.framesize > data->buffer_size) + goto fail; + current_frame_size = mi.framesize; + + /* reinit, to ignore the reading we've done so far */ + mpg123_open_feed(main_m); + } + + /* Init interleaved audio, which needs separate decoders per stream and frame size stuff. + * We still leave data->m as a "base" info/format to simplify some stuff (could be improved) */ + if (interleaved) { + int i; + + data->interleaved = interleaved; + + if (channels < 1 || channels > 32) goto fail; /* arbitrary max */ + if (channels < data->channels_per_frame) goto fail; + + update_base_frame_sizes(data, streamfile, start_offset, fixed_frame_size, current_frame_size, fsb_padding); + if (!data->base_frame_size) goto fail; + + data->ms_size = channels / data->channels_per_frame; + data->ms = calloc(sizeof(mpg123_handle *), data->ms_size); + for (i=0; i < data->ms_size; i++) { + data->ms[i] = init_mpg123_handle(); + if (!data->ms[i]) goto fail; + } + + data->frame_buffer_size = sizeof(sample) * data->samples_per_frame * data->channels_per_frame; + data->frame_buffer = calloc(sizeof(uint8_t), data->frame_buffer_size); + if (!data->frame_buffer) goto fail; + + data->interleave_buffer_size = sizeof(sample) * data->samples_per_frame * channels; + data->interleave_buffer = calloc(sizeof(uint8_t), data->interleave_buffer_size); + if (!data->interleave_buffer) goto fail; } - /* reinit, to ignore the reading we've done so far */ - mpg123_open_feed(data->m); return data; -mpeg_fail: - fprintf(stderr, "mpeg_fail start_offset=%x\n",(unsigned int)start_offset); - if (data) { - mpg123_delete(data->m); - free(data); - } +fail: + free_mpeg(data); return NULL; } -/* decode anything mpg123 can */ -void decode_mpeg(VGMSTREAMCHANNEL *stream, - mpeg_codec_data * data, - sample * outbuf, int32_t samples_to_do, int channels) { +/** + * Inits MPEG for AHX, which ignores frame headers. + */ +mpeg_codec_data *init_mpeg_codec_data_ahx(STREAMFILE *streamFile, off_t start_offset, int channel_count) { + mpeg_codec_data *data = NULL; + + /* init codec */ + data = calloc(1,sizeof(mpeg_codec_data)); + if (!data) goto fail; + + data->buffer_size = MPEG_DEFAULT_BUFFER_SIZE; + data->buffer = calloc(sizeof(uint8_t), data->buffer_size); + if (!data->buffer) goto fail; + + data->m = init_mpg123_handle(); + if (!data->m) goto fail; + + + return data; + +fail: + free_mpeg(data); + return NULL; +} + + +static mpg123_handle * init_mpg123_handle() { + mpg123_handle *m = NULL; + int rc; + + /* inits a new mpg123 handle */ + m = mpg123_new(NULL,&rc); + if (rc == MPG123_NOT_INITIALIZED) { + /* inits the library if needed */ + if (mpg123_init() != MPG123_OK) + goto fail; + m = mpg123_new(NULL,&rc); + if (rc != MPG123_OK) goto fail; + } else if (rc != MPG123_OK) { + goto fail; + } + + mpg123_param(m,MPG123_REMOVE_FLAGS,MPG123_GAPLESS,0.0); //todo fix gapless + mpg123_param(m,MPG123_RESYNC_LIMIT, -1, 0x10000); /* should be enough */ + + if (mpg123_open_feed(m) != MPG123_OK) { + goto fail; + } + + return m; + +fail: + mpg123_delete(m); + return NULL; +} + + +/************/ +/* DECODERS */ +/************/ + +void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels) { + mpeg_codec_data * data = (mpeg_codec_data *) vgmstream->codec_data; + + /* MPEGs streams contain one or two channels, so we may only need half VGMSTREAMCHANNELs for offsets */ + if (data->interleaved) { + decode_mpeg_interleave(vgmstream, data, outbuf, samples_to_do, channels); + } else { + decode_mpeg_default(&vgmstream->ch[0], data, outbuf, samples_to_do, channels); + } +} + +/** + * Decode anything mpg123 can. + * Feeds raw data and extracts decoded samples as needed. + */ +static void decode_mpeg_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { int samples_done = 0; + mpg123_handle *m = data->m; while (samples_done < samples_to_do) { size_t bytes_done; int rc; + /* read more raw data */ if (!data->buffer_full) { - data->bytes_in_buffer = read_streamfile(data->buffer, - stream->offset,MPEG_BUFFER_SIZE,stream->streamfile); + data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->buffer_size,stream->streamfile); + /* end of stream, fill rest with 0s */ if (!data->bytes_in_buffer) { memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample)); break; @@ -201,36 +263,392 @@ void decode_mpeg(VGMSTREAMCHANNEL *stream, stream->offset += data->bytes_in_buffer; } + /* feed new raw data to the decoder if needed, copy decoded results to output */ if (!data->buffer_used) { - rc = mpg123_decode(data->m, + rc = mpg123_decode(m, data->buffer,data->bytes_in_buffer, (unsigned char *)(outbuf+samples_done*channels), (samples_to_do-samples_done)*sizeof(sample)*channels, &bytes_done); data->buffer_used = 1; } else { - rc = mpg123_decode(data->m, + rc = mpg123_decode(m, NULL,0, (unsigned char *)(outbuf+samples_done*channels), (samples_to_do-samples_done)*sizeof(sample)*channels, &bytes_done); } - if (rc == MPG123_NEED_MORE) data->buffer_full = 0; + /* not enough raw data, request more */ + if (rc == MPG123_NEED_MORE) { + data->buffer_full = 0; + } + /* update copied samples */ samples_done += bytes_done/sizeof(sample)/channels; } } -long mpeg_bytes_to_samples(long bytes, const struct mpg123_frameinfo *mi) { - return (int64_t)bytes * mi->rate * 8 / (mi->bitrate * 1000); + +/** + * Decode interleaved (multichannel) MPEG. Works with mono/stereo too. + * Channels (1 or 2), samples and frame size per stream should be constant. //todo extra validations + * + * Reads frame 'streams' (ex. 4ch = 1+1+1+1 = 4 streams or 2+2 = 2 streams), decodes + * samples per stream and muxes them into a single internal buffer before copying to outbuf + * (to make sure channel samples are orderly copied between decode_mpeg calls). + * + * Interleave variations: + * - blocks of frames: fixed block_size per stream (unknown number of samples) [XVAG] + * (ex. b1 = N samples of ch1, b2 = N samples of ch2, b3 = M samples of ch1, etc) + * - partial frames: single frames per stream with padding (block_size is frame_size+padding) [FSB] + * (ex. f1+f3+f5 = 1152*2 samples of ch1+2, f2+f4 = 1152*2 samples of ch3+4, etc) + */ +static void decode_mpeg_interleave(VGMSTREAM * vgmstream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { + int samples_done = 0, bytes_max, bytes_to_copy; + + while (samples_done < samples_to_do) { + + if (data->bytes_used_in_interleave_buffer < data->bytes_in_interleave_buffer) { + /* copy remaining samples */ + bytes_to_copy = data->bytes_in_interleave_buffer - data->bytes_used_in_interleave_buffer; + bytes_max = (samples_to_do - samples_done) * sizeof(sample) * channels; + if (bytes_to_copy > bytes_max) + bytes_to_copy = bytes_max; + memcpy((uint8_t*)(outbuf+samples_done*channels), data->interleave_buffer + data->bytes_used_in_interleave_buffer, bytes_to_copy); + + /* update samples copied */ + data->bytes_used_in_interleave_buffer += bytes_to_copy; + samples_done += bytes_to_copy / sizeof(sample) / channels; + } + else { + /* fill the internal sample buffer */ + int i; + data->bytes_in_interleave_buffer = 0; + data->bytes_used_in_interleave_buffer = 0; + + for (i=0; i < data->ms_size; i++) { + decode_mpeg_interleave_samples(&vgmstream->ch[i], data, data->ms[i], channels, i, vgmstream->interleave_block_size); + } + } + } + } +/** + * Decodes frames from a stream and muxes the samples into a intermediate buffer. + * Skips to the next interleaved block once reaching the stream's block end. + */ +static void decode_mpeg_interleave_samples(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, mpg123_handle *m, int channels, int num_stream, size_t block_size) { + size_t bytes_done; + + /* decode samples from 1 full frame */ + do { + int rc; + + /* padded frame stuff */ + update_frame_sizes(data, stream); + + /* read more raw data (only 1 frame, to check interleave block end) */ + if (!data->buffer_full) { + data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,data->current_frame_size,stream->streamfile); + + /* end of stream, fill frame buffer with 0s but continue normally with other streams */ + if (!data->bytes_in_buffer) { + memset(data->frame_buffer, 0, data->frame_buffer_size); + bytes_done = data->frame_buffer_size; + break; + } + + data->buffer_full = 1; + data->buffer_used = 0; + + stream->offset += data->current_frame_size + data->current_padding; /* skip FSB frame+garbage */ + if (block_size && ((stream->offset - stream->channel_start_offset) % block_size==0)) { + stream->offset += block_size * (data->ms_size-1); /* skip a block per stream if block done */ + } + } + + /* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */ + if (!data->buffer_used) { + rc = mpg123_decode(m, + data->buffer, data->bytes_in_buffer, + (unsigned char *)data->frame_buffer, data->frame_buffer_size, + &bytes_done); + data->buffer_used = 1; + } else { + rc = mpg123_decode(m, + NULL,0, + (unsigned char *)data->frame_buffer, data->frame_buffer_size, + &bytes_done); + } + + /* samples per frame should be constant... */ + if (bytes_done > 0 && bytes_done < data->frame_buffer_size) { + VGM_LOG("borked frame: %i bytes done, expected %i, rc=%i\n", bytes_done, data->frame_buffer_size, rc); + memset(data->frame_buffer + bytes_done, 0, data->frame_buffer_size - bytes_done); + bytes_done = data->frame_buffer_size; + } + + /* not enough raw data, request more */ + if (rc == MPG123_NEED_MORE) { + data->buffer_full = 0; + continue; + } + + break; + } while (1); + + + /* copy decoded frame to intermediate sample buffer, muxing channels + * (ex stream1: ch1s1 ch1s2, stream2: ch2s1 ch2s2 > ch1s1 ch2s1 ch1s2 ch2s2) */ + { + size_t samples_done; + size_t sz = sizeof(sample); + int channels_f = data->channels_per_frame; + int fch, i; + + samples_done = bytes_done / sz / channels_f; + for (fch = 0; fch < channels_f; fch++) { /* channels inside the frame */ + for (i = 0; i < samples_done; i++) { /* decoded samples */ + off_t in_offset = sz*i*channels_f + sz*fch; + off_t out_offset = sz*i*channels + sz*(num_stream*channels_f + fch); + memcpy(data->interleave_buffer + out_offset, data->frame_buffer + in_offset, sz); + } + } + + data->bytes_in_interleave_buffer += bytes_done; + } +} + +/** + * Very Clunky Stuff for FSBs of varying padding sizes per frame. + * Padding sometimes contains next frame header so we can't feed it to mpg123 or it gets confused. + * Expected to be called at the beginning of a new frame. + */ +static void update_frame_sizes(mpeg_codec_data * data, VGMSTREAMCHANNEL *stream) { + if (!data->fixed_frame_size) { + /* Manually fix frame size. Not ideal but mpg123_info.framesize is weird. */ + uint32_t header = (uint32_t)read_32bitBE(stream->offset, stream->streamfile); + if (header & MPEG_SYNC_BITS) + data->current_frame_size = data->base_frame_size + (header & MPEG_PADDING_BIT ? 1 : 0); + else + data->current_frame_size = 0; /* todo skip invalid frame? */ + + if (data->fsb_padding) //todo not always ok + data->current_padding = (data->current_frame_size % data->fsb_padding) ? + data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0; + } +} +static void update_base_frame_sizes(mpeg_codec_data * data, STREAMFILE *streamFile, off_t start_offset, int fixed_frame_size, int current_frame_size, int fsb_padding) { + if (fixed_frame_size) { + data->fixed_frame_size = fixed_frame_size; + data->base_frame_size = data->fixed_frame_size; + data->current_frame_size = data->fixed_frame_size; + } else { + /* adjust sizes in the first frame */ + //todo: sometimes mpg123_info.framesize is not correct, manually calculate? (Xing headers?) + uint32_t header = (uint32_t)read_32bitBE(start_offset, streamFile); + if (header & MPEG_SYNC_BITS) + data->base_frame_size = current_frame_size - (header & MPEG_PADDING_BIT ? 1 : 0); + else + data->base_frame_size = 0; /* todo skip invalid frame? */ + + data->current_frame_size = current_frame_size; + data->fsb_padding = fsb_padding; + if (data->fsb_padding) //todo not always ok + data->current_padding = (data->current_frame_size % data->fsb_padding) ? + data->fsb_padding - (data->current_frame_size % data->fsb_padding) : 0; + } +} + +/** + * Decode AHX mono frames. + * mpg123 expects frames of 0x414 (160kbps, 22050Hz) but they actually vary and are much shorter + */ +void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do) { + int samples_done = 0; + const int frame_size = AHX_EXPECTED_FRAME_SIZE; + mpg123_handle *m = data->m; + + while (samples_done < samples_to_do) { + size_t bytes_done; + int rc; + + /* read more raw data */ + if (!data->buffer_full) { + /* fill buffer up to next frame ending (or file ending) */ + int bytes_into_header = 0; + const uint8_t header[4] = {0xff,0xf5,0xe0,0xc0}; + off_t frame_offset = 0; + + /* assume that we are starting at a header, skip it and look for the next one */ + read_streamfile(data->buffer, stream->offset+frame_offset, 4, stream->streamfile); + frame_offset += 4; + + do { + uint8_t byte; + byte = read_8bit(stream->offset+frame_offset,stream->streamfile); + data->buffer[frame_offset] = byte; + frame_offset++; + + if (byte == header[bytes_into_header]) { + bytes_into_header++; + } else { + /* This might have been the first byte of the header, so + * we need to check again. + * No need to get more complicated than this, though, since + * there are no repeated characters in the search string. */ + if (bytes_into_header>0) { + frame_offset--; + } + bytes_into_header=0; + } + + if (bytes_into_header==4) { + break; + } + } while (frame_offset < frame_size); + + if (bytes_into_header==4) + frame_offset-=4; + memset(data->buffer+frame_offset,0,frame_size-frame_offset); + + data->buffer_full = 1; + data->buffer_used = 0; + + stream->offset += frame_offset; + } + + /* feed new raw data to the decoder if needed, copy decodec results to output */ + if (!data->buffer_used) { + rc = mpg123_decode(m, + data->buffer,frame_size, + (unsigned char *)(outbuf+samples_done), + (samples_to_do-samples_done)*sizeof(sample), + &bytes_done); + data->buffer_used = 1; + } else { + rc = mpg123_decode(m, + NULL,0, + (unsigned char *)(outbuf+samples_done), + (samples_to_do-samples_done)*sizeof(sample), + &bytes_done); + } + + /* not enough raw data, request more */ + if (rc == MPG123_NEED_MORE) { + data->buffer_full = 0; + } + + /* update copied samples */ + samples_done += bytes_done/sizeof(sample);/* mono */ + } +} + +/*********/ +/* UTILS */ +/*********/ + +void free_mpeg(mpeg_codec_data *data) { + if (!data) + return; + + mpg123_delete(data->m); + if (data->interleaved) { + int i; + for (i=0; i < data->ms_size; i++) { + mpg123_delete(data->ms[i]); + } + free(data->ms); + + free(data->interleave_buffer); + free(data->frame_buffer); + } + + free(data->buffer); + free(data); + + /* The astute reader will note that a call to mpg123_exit is never + * made. While is is evilly breaking our contract with mpg123, it + * doesn't actually do anything except set the "initialized" flag + * to 0. And if we exit we run the risk of turning it off when + * someone else in another thread is using it. */ +} + +void reset_mpeg(VGMSTREAM *vgmstream) { + off_t input_offset; + mpeg_codec_data *data = vgmstream->codec_data; + + /* input_offset is ignored as we can assume it will be 0 for a seek to sample 0 */ + mpg123_feedseek(data->m,0,SEEK_SET,&input_offset); + + /* reset multistream */ //todo check if stream offsets are properly reset + if (data->interleaved) { + int i; + for (i=0; i < data->ms_size; i++) { + mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset); + vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset + input_offset; + } + + data->bytes_in_interleave_buffer = 0; + data->bytes_used_in_interleave_buffer = 0; + } +} + +void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) { + /* won't work for fake or multistream MPEG */ + off_t input_offset; + mpeg_codec_data *data = vgmstream->codec_data; + + /* seek multistream */ + if (!data->interleaved) { + mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset); + vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset; + } else { + int i; + /* re-start from 0 */ + for (i=0; i < data->ms_size; i++) { + mpg123_feedseek(data->ms[i],0,SEEK_SET,&input_offset); + vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset; + } + /* manually add skip samples, since we don't really know the correct offset */ + //todo call decode with samples_to_do and fake header + + data->bytes_in_interleave_buffer = 0; + data->bytes_used_in_interleave_buffer = 0; + } + + data->buffer_full = 0; + data->buffer_used = 0; +} + + +long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) { + struct mpg123_frameinfo mi; + mpg123_handle *m = data->m; + + if (MPG123_OK != mpg123_info(m, &mi)) + return 0; + + /* In this case just return 0 and expect to fail (if used for num_samples) + * We would need to read the number of frames in some frame header or count them to get samples */ + if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used + return 0; + + return (int64_t)bytes * mi.rate * 8 / (mi.bitrate * 1000); +} /** * disables/enables stderr output, useful for MPEG known to contain recoverable errors */ void mpeg_set_error_logging(mpeg_codec_data * data, int enable) { mpg123_param(data->m, MPG123_ADD_FLAGS, MPG123_QUIET, !enable); + if (data->interleaved) { + int i; + for (i=0; i < data->ms_size; i++) { + mpg123_param(data->ms[i], MPG123_ADD_FLAGS, MPG123_QUIET, !enable); + } + } } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c index 5b87c45b8..7584e00f7 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ngc_dsp_decoder.c @@ -100,12 +100,19 @@ int32_t dsp_nibbles_to_samples(int32_t nibbles) { * reads DSP coefs built in the streamfile */ void dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) { + dsp_read_coefs(vgmstream, streamFile, offset, spacing, 1); +} +void dsp_read_coefs_le(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) { + dsp_read_coefs(vgmstream, streamFile, offset, spacing, 0); +} +void dsp_read_coefs(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing, int be) { int ch, i; /* get ADPCM coefs */ for (ch=0; ch < vgmstream->channels; ch++) { for (i=0; i < 16; i++) { - vgmstream->ch[ch].adpcm_coef[i] = - read_16bitBE(offset + ch*spacing + i*2, streamFile); + vgmstream->ch[ch].adpcm_coef[i] = be ? + read_16bitBE(offset + ch*spacing + i*2, streamFile) : + read_16bitLE(offset + ch*spacing + i*2, streamFile); } } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c index 62a34058b..62fb87866 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.c @@ -6,14 +6,14 @@ * Redistribution and use in source and binary forms, with or without * modification, are permitted. * - * ΥץκԤ jagarl Ǥ + * このプログラムの作者は jagarl です。 * - * ΥץࡢڤӥѥˤäХʥ - * ץѹ롢ʤˤ餺۲ǽǤ - * κݡ嵭 Copyright ɽݻʤɤξϲݤ - * бݤʤΤǥХ᡼Ϣ򤹤 - * ʤɤɬפ⤢ޤ󡣥ΰήѤ뤳Ȥޤᡢ - * ͳˤȤ + * このプログラム、及びコンパイルによって生成したバイナリは + * プログラムを変更する、しないにかかわらず再配布可能です。 + * その際、上記 Copyright 表示を保持するなどの条件は課しま + * せん。対応が面倒なのでバグ報告を除き、メールで連絡をする + * などの必要もありません。ソースの一部を流用することを含め、 + * ご自由にお使いください。 * * THIS SOFTWARE IS PROVIDED BY KAZUNORI 'jagarl' UENO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -168,7 +168,7 @@ static int use_runlength(NWAData *nwa) static void nwa_decode_block(NWAData *nwa) { - /* ɤ߹ࡿǥɤǡ礭 */ + /* 今回読み込む/デコードするデータの大きさを得る */ int curblocksize; if (nwa->curblock != nwa->blocks - 1) { @@ -190,7 +190,7 @@ nwa_decode_block(NWAData *nwa) int shift = 0; off_t offset = nwa->offsets[nwa->curblock]; int dsize = curblocksize / (nwa->bps / 8); - int flip_flag = 0; /* stereo */ + int flip_flag = 0; /* stereo 用 */ int runlength = 0; /* read initial sample value */ @@ -207,16 +207,16 @@ nwa_decode_block(NWAData *nwa) for (i = 0; i < dsize; i++) { if (runlength == 0) - { /* ԡ롼Ǥʤʤǡɤ߹ */ + { /* コピーループ中でないならデータ読み込み */ int type = getbits(nwa->file, &offset, &shift, 3); - /* type ˤʬ0, 1-6, 7 */ + /* type により分岐:0, 1-6, 7 */ if (type == 7) { - /* 7 : 礭ʺʬ */ - /* RunLength() ͭCompLevel==5, ե) Ǥ̵ */ + /* 7 : 大きな差分 */ + /* RunLength() 有効時(CompLevel==5, 音声ファイル) では無効 */ if (getbits(nwa->file, &offset, &shift, 1) == 1) { - d[flip_flag] = 0; /* ̤ */ + d[flip_flag] = 0; /* 未使用 */ } else { @@ -244,7 +244,7 @@ nwa_decode_block(NWAData *nwa) } else if (type != 0) { - /* 1-6 : ̾κʬ */ + /* 1-6 : 通常の差分 */ int BITS, SHIFT; if (nwa->complevel >= 3) { @@ -268,10 +268,10 @@ nwa_decode_block(NWAData *nwa) } else { /* type == 0 */ - /* 󥰥̤ʤξϤʤˤ⤷ʤ */ + /* ランレングス圧縮なしの場合はなにもしない */ if (use_runlength(nwa)) { - /* 󥰥̤ξ */ + /* ランレングス圧縮ありの場合 */ runlength = getbits(nwa->file, &offset, &shift, 1); if (runlength == 1) { @@ -298,7 +298,7 @@ nwa_decode_block(NWAData *nwa) } nwa->samples_in_buffer++; if (nwa->channels == 2) - flip_flag ^= 1; /* channel ڤؤ */ + flip_flag ^= 1; /* channel 切り替え */ } } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h index 2dbd9f1ef..361736918 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/nwa_decoder.h @@ -6,14 +6,14 @@ * Redistribution and use in source and binary forms, with or without * modification, are permitted. * - * ΥץκԤ jagarl Ǥ + * このプログラムの作者は jagarl です。 * - * ΥץࡢڤӥѥˤäХʥ - * ץѹ롢ʤˤ餺۲ǽǤ - * κݡ嵭 Copyright ɽݻʤɤξϲݤ - * бݤʤΤǥХ᡼Ϣ򤹤 - * ʤɤɬפ⤢ޤ󡣥ΰήѤ뤳Ȥޤᡢ - * ͳˤȤ + * このプログラム、及びコンパイルによって生成したバイナリは + * プログラムを変更する、しないにかかわらず再配布可能です。 + * その際、上記 Copyright 表示を保持するなどの条件は課しま + * せん。対応が面倒なのでバグ報告を除き、メールで連絡をする + * などの必要もありません。ソースの一部を流用することを含め、 + * ご自由にお使いください。 * * THIS SOFTWARE IS PROVIDED BY KAZUNORI 'jagarl' UENO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 5b388a19c..88a50e750 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -28,7 +28,7 @@ static const char* extension_list[] = { "aifcl", //"aiff", //common "aix", - "akb", //AAC + "akb", "amts", "as4", "asd", @@ -53,6 +53,12 @@ static const char* extension_list[] = { "bg00", "bgw", "bh2pcm", + "bik", + "bika", + "bik2", + "bik2a", + "bk2", + "bk2a", "bmdx", "bms", "bnk", @@ -135,20 +141,23 @@ static const char* extension_list[] = { "kraw", "leg", - "logg", + "lmp4", //fake extension, for looping + "logg", //fake extension, for looping "lpcm", "lps", "lsf", - "lwav", + "lwav", //fake extension, for looping "matx", "mca", "mcg", + "mds", "mi4", "mib", "mic", "mihb", "mnstr", + //"mp4", //common "mpdsp", "mpds", "msa", @@ -167,9 +176,10 @@ static const char* extension_list[] = { "ngca", "nps", "npsf", - "nus3bank", //todo not existing? + "nus3bank", "nwa", + //"ogg", //common "oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA) "omu", "otm", @@ -285,6 +295,7 @@ static const char* extension_list[] = { "wad", "wam", "was", + //"wav", //common "wavm", "wb", "wii", @@ -405,6 +416,8 @@ static const coding_info coding_info_list[] = { {coding_RAD_IMA_mono, "'Radical' 4-bit IMA ADPCM (mono)"}, {coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"}, {coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"}, + {coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"}, + {coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"}, {coding_WS, "Westwood Studios ADPCM"}, {coding_ACM, "InterPlay ACM"}, {coding_NWA0, "NWA DPCM Level 0"}, @@ -590,7 +603,7 @@ static const meta_info meta_info_list[] = { {meta_FSB4, "FMOD Sample Bank (FSB4) Header"}, {meta_FSB5, "FMOD Sample Bank (FSB5) Header"}, {meta_RWX, "RWX Header"}, - {meta_XWB, "XWB WBND Header"}, + {meta_XWB, "Microsoft XWB Header"}, {meta_XA30, "XA30 Header"}, {meta_MUSC, "MUSC Header"}, {meta_MUSX_V004, "MUSX / Version 004 Header"}, @@ -787,6 +800,7 @@ static const meta_info meta_info_list[] = { {meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"}, {meta_IOS_PSND, "PSND Header"}, {meta_BOS_ADP, "ADP! header"}, + {meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"}, {meta_EB_SFX, "Excitebots .sfx header"}, {meta_EB_SF0, "assumed Excitebots .sf0 by extension"}, {meta_PS3_KLBS, "klBS Header"}, @@ -815,6 +829,7 @@ static const meta_info meta_info_list[] = { {meta_PS2_SVAG_SNK, "SNK SVAG header"}, {meta_PS2_VDS_VDM, "Graffiti Kingdom VDS/VDM Header"}, {meta_X360_CXS, "CXS Header"}, + {meta_AKB, "Square Enix AKB Header"}, #ifdef VGM_USE_VORBIS {meta_OGG_VORBIS, "Ogg Vorbis"}, {meta_OGG_SLI, "Ogg Vorbis with .sli (start,length) for looping"}, diff --git a/Frameworks/vgmstream/vgmstream/src/header.c b/Frameworks/vgmstream/vgmstream/src/header.c new file mode 100644 index 000000000..70fb0c63a --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/header.c @@ -0,0 +1,254 @@ +#include +#include "header.h" +#include "vgmstream.h" +#include "streamfile.h" +#include "streamtypes.h" +#include "util.h" + +/** + * checks if the stream filename is one of the extensions (comma-separated, ex. "adx" or "adx,aix") + * + * returns 0 on failure + */ +int header_check_extensions(STREAMFILE *streamFile, const char * cmpexts) { + char filename[PATH_LIMIT]; + const char * ext = NULL; + const char * cmpext = NULL; + size_t ext_len; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + ext = filename_extension(filename); + ext_len = strlen(ext); + + cmpext = cmpexts; + do { + if (strncasecmp(ext,cmpext, ext_len)==0 ) + return 1; + cmpext = strstr(cmpext, ","); + if (cmpext != NULL) + cmpext = cmpext + 1; /* skip comma */ + } while (cmpext != NULL); + + return 0; +} + + +/** + * opens a stream at offset + * + * returns 0 on failure + */ +int header_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) { + STREAMFILE * file; + char filename[PATH_LIMIT]; + int ch; + int buffer_size = STREAMFILE_DEFAULT_BUFFER_SIZE; + +#ifdef VGM_USE_FFMPEG + if (vgmstream->coding_type == coding_FFmpeg) /* not needed */ + return 1; +#endif + + /* minor optimizations */ + if (vgmstream->layout_type == layout_interleave + &&vgmstream->interleave_block_size > 0 + && vgmstream->interleave_block_size > buffer_size) { + buffer_size = vgmstream->interleave_block_size; + } + + if (buffer_size > 0x8000) { + buffer_size = 0x8000; + /* todo if interleave is big enough open one streamfile per channel so each uses it's own buffer */ + } + + + streamFile->get_name(streamFile,filename,sizeof(filename)); + /* open the file for reading by each channel */ + { + file = streamFile->open(streamFile,filename,buffer_size); + if (!file) return 0; + + for (ch=0; ch < vgmstream->channels; ch++) { + + vgmstream->ch[ch].streamfile = file; + + if (vgmstream->layout_type == layout_none +#ifdef VGM_USE_MPEG + || vgmstream->layout_type == layout_mpeg //todo simplify using flag "start offset" +#endif + ) { /* no appreciable difference for mpeg */ + /* for some codecs like IMA where channels work with the same bytes */ + vgmstream->ch[ch].channel_start_offset = + vgmstream->ch[ch].offset = start_offset; + } + else { + vgmstream->ch[ch].channel_start_offset = + vgmstream->ch[ch].offset = start_offset + + vgmstream->interleave_block_size*ch; + } + } + } + + + return 1; +} + + +/** + * Copies a XMA2 riff to buf + * + * returns number of bytes in buf or -1 when buf is not big enough + */ +int header_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size) { + uint16_t codec_XMA2 = 0x0166; + size_t riff_size = 4+4+ 4 + 0x3c + 4+4; + size_t bytecount; + uint32_t streams = 0; + uint32_t speakers = 0; /* see audiodefs.h */ + + if (buf_size < riff_size) + return -1; + + bytecount = sample_count * channels * sizeof(sample); + + /* untested (no support for > 2ch xma for now) */ + switch (channels) { + case 1: + streams = 1; + speakers = 0x00000004; /* FC */ + break; + case 2: + streams = 1; + speakers = 0x00000001 | 0x00000002; /* FL FR */ + break; + case 3: + streams = 3; + speakers = 0x00000001 | 0x00000002 | 0x00000004; /* FL FC FR */ + break; + case 4: + streams = 2; + speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020; /* FL FR BL BR */ + break; + case 5: + streams = 3; + speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000004; /* FL C FR BL BR*/ + break; + case 6: + streams = 3; + speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000200 | 0x00000400; /* FL FR BL BR SL SR */ + break; + default: + streams = 1; + speakers = 0x80000000; + break; + } + + /*memset(buf,0, sizeof(uint8_t) * fmt_size);*/ + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0x34);/*fmt size*/ + put_16bitLE(buf+0x14, codec_XMA2); + put_16bitLE(buf+0x16, channels); + put_32bitLE(buf+0x18, sample_rate); + put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ + put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */ + put_16bitLE(buf+0x22, sizeof(sample)*8); /* bits per sample */ + + put_16bitLE(buf+0x24, 0x22); /* extra data size */ + put_16bitLE(buf+0x26, streams); /* number of streams */ + put_32bitLE(buf+0x28, speakers); /* speaker position */ + put_32bitLE(buf+0x2c, bytecount); /* PCM samples */ + put_32bitLE(buf+0x30, block_size); /* XMA block size */ + /* (looping values not set, expected to be handled externally) */ + put_32bitLE(buf+0x34, 0); /* play begin */ + put_32bitLE(buf+0x38, 0); /* play length */ + put_32bitLE(buf+0x3c, 0); /* loop begin */ + put_32bitLE(buf+0x40, 0); /* loop length */ + put_8bit(buf+0x44, 0); /* loop count */ + put_8bit(buf+0x45, 4); /* encoder version */ + put_16bitLE(buf+0x46, block_count); /* blocks count = entried in seek table */ + + memcpy(buf+0x48, "data", 4); + put_32bitLE(buf+0x4c, data_size); /* data size */ + + return riff_size; +} + +/** + * Copies a ATRAC3 riff to buf + * + * returns number of bytes in buf or -1 when buf is not big enough + */ +int header_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) { + uint16_t codec_ATRAC3 = 0x0270; + size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4; + + if (buf_size < riff_size) + return -1; + + memcpy(buf+0x00, "RIFF", 4); + put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */ + memcpy(buf+0x08, "WAVE", 4); + + memcpy(buf+0x0c, "fmt ", 4); + put_32bitLE(buf+0x10, 0x20);/*fmt size*/ + put_16bitLE(buf+0x14, codec_ATRAC3); + put_16bitLE(buf+0x16, channels); + put_32bitLE(buf+0x18, sample_rate); + put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */ + put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */ + + put_16bitLE(buf+0x24, 0x0e); /* extra data size */ + put_16bitLE(buf+0x26, 1); /* unknown, always 1 */ + put_16bitLE(buf+0x28, channels==1 ? 0x0800 : 0x1000); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */ + put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */ + put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000); + put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */ + put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */ + put_16bitLE(buf+0x32, 0); /* unknown, always 0 */ + + memcpy(buf+0x34, "fact", 4); + put_32bitLE(buf+0x38, 0x8); /* fact size */ + put_32bitLE(buf+0x3c, sample_count); + put_32bitLE(buf+0x40, encoder_delay); + + memcpy(buf+0x44, "data", 4); + put_32bitLE(buf+0x48, data_size); /* data size */ + + return riff_size; +} + + +/** + * reads DSP coefs built in the streamfile + */ +void header_dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing) { + int ch, i; + /* get ADPCM coefs */ + for (ch=0; ch < vgmstream->channels; ch++) { + for (i=0; i < 16; i++) { + vgmstream->ch[ch].adpcm_coef[i] = + read_16bitBE(offset + ch*spacing + i*2, streamFile); + } + } +} + + +#if 0 +/** + * Converts a data offset (without headers) to sample, so full datasize would be num_samples + * + * return -1 on failure + */ +int data_offset_to_samples(layout_t layout, int channels, size_t interleave, size_t data_offset) { + // todo get samples per block + // VAG: datasize * 28 / 16 / channels; + // IMA: (datasize / 0x24 / channels) * ((0x24-4)*2);//0x24 = interleave? + // DSP: datasize / 8 / channel_count * 14; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/header.h b/Frameworks/vgmstream/vgmstream/src/header.h new file mode 100644 index 000000000..d6e144246 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/header.h @@ -0,0 +1,24 @@ +/* + * header.h - utilities for common/repetitive actions in stream headers (more complex than those in util.h) + */ + +#ifndef _HEADER_H_ +#define _HEADER_H_ + +#include "util.h" +#include "streamfile.h" +#include "vgmstream.h" + + +int header_check_extensions(STREAMFILE *streamFile, const char * cmpexts); + +int header_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset); + +int header_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size);; + +int header_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay); + +void header_dsp_read_coefs_be(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t offset, off_t spacing); + + +#endif /* _HEADER_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ads.c b/Frameworks/vgmstream/vgmstream/src/meta/ads.c index 0ec46e7b9..f555b87d3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ads.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ads.c @@ -1,18 +1,17 @@ #include "meta.h" #include "../util.h" +#include "../coding/coding.h" -/* ADS (from Gauntlet Dark Legends (GC)) */ +/* ADS - from Gauntlet Dark Legacy (GC/Xbox) */ VGMSTREAM * init_vgmstream_ads(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; int loop_flag; int channel_count; int identifer_byte; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ads",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"ads")) goto fail; /* check dhSS Header */ if (read_32bitBE(0x00,streamFile) != 0x64685353) @@ -35,86 +34,52 @@ VGMSTREAM * init_vgmstream_ads(STREAMFILE *streamFile) { /* fill in the vital statistics */ identifer_byte = read_32bitBE(0x08,streamFile); switch (identifer_byte) { - case 0x00000020: - start_offset = 0xE8; + case 0x00000020: /* GC */ + start_offset = 0x28 + 0x60 * channel_count; vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x0c,streamFile); vgmstream->coding_type = coding_NGC_DSP; vgmstream->num_samples = read_32bitBE(0x28,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } + if (loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } - if (channel_count == 1){ - vgmstream->layout_type = layout_none; - } else if (channel_count == 2){ - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x14,streamFile); - } - break; - case 0x00000021: + if (channel_count == 1){ + vgmstream->layout_type = layout_none; + } else if (channel_count == 2){ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = read_32bitBE(0x14,streamFile); + } + + dsp_read_coefs_be(vgmstream, streamFile, 0x44,0x60); + break; + + case 0x00000021: /* Xbox */ start_offset = 0x28; vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitBE(0x0c,streamFile); vgmstream->coding_type = coding_INT_XBOX; vgmstream->num_samples = (read_32bitBE(0x24,streamFile) / 36 *64 / vgmstream->channels)-64; // to avoid the "pop" at the loop point - vgmstream->layout_type = layout_interleave; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->interleave_block_size = 0x24; - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = vgmstream->num_samples; - } - break; - default: -goto fail; -} + if (loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = vgmstream->num_samples; + } + break; + + default: + goto fail; + } vgmstream->meta_type = meta_ADS; - { - int i; - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x44+i*2,streamFile); - if (channel_count == 2) { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0xA4+i*2,streamFile); - } - } - - - /* open the file for reading */ - if (vgmstream->coding_type == coding_NGC_DSP) { - int i,c; - for (c=0;cch[c].adpcm_coef[i] = - read_16bitBE(0x44+c*0x60 +i*2,streamFile); - } - } - } - - /* 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+vgmstream->interleave_block_size*i; - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; - - } - } - + if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + goto fail; return vgmstream; fail: - /* clean up anything we may have opened */ - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c b/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c index 900485aae..65f4922bf 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_header.c @@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { off_t stream_offset; uint16_t version_signature; int loop_flag=0; - int channel_count, i, j, channel_header_spacing; + int channel_count; int32_t loop_start_sample=0; int32_t loop_end_sample=0; meta_t header_type; @@ -25,207 +25,170 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { char filename[PATH_LIMIT]; int coding_type = coding_CRI_ADX; uint16_t xor_start=0,xor_mult=0,xor_add=0; - /* Xenoblade Chronicles 3D uses an adx extension as with - the Wii version, but it's actually DSP ADPCM. Adding - this flag to account for it. */ - int xb3d_flag = 0; - + /* check extension, case insensitive */ streamFile->get_name(streamFile,filename,sizeof(filename)); if (strcasecmp("adx",filename_extension(filename))) goto fail; /* check first 2 bytes */ - if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) { - if (read_8bit(0,streamFile)!=2)goto fail; - else {xb3d_flag = 1; coding_type = coding_NGC_DSP;} - } - - if (xb3d_flag) { - channel_count = read_32bitLE(0, streamFile); - loop_flag = read_16bitLE(0x6e, streamFile); - channel_header_spacing = 0x34; - } - else { - /* get stream offset, check for CRI signature just before */ - stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; - if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */ - (uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */ - ) goto fail; + if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail; - /* check for encoding type */ - /* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is - * ADX with exponential scale, 0x11 is AHX */ - if (read_8bit(4,streamFile) != 3) goto fail; + /* get stream offset, check for CRI signature just before */ + stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; + if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */ + (uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */ + ) goto fail; - /* check for frame size (only 18 is supported at the moment) */ - if (read_8bit(5,streamFile) != 18) goto fail; + /* check for encoding type */ + /* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is + * ADX with exponential scale, 0x11 is AHX */ + if (read_8bit(4,streamFile) != 3) goto fail; - /* check for bits per sample? (only 4 makes sense for ADX) */ - if (read_8bit(6,streamFile) != 4) goto fail; + /* check for frame size (only 18 is supported at the moment) */ + if (read_8bit(5,streamFile) != 18) goto fail; - /* check version signature, read loop info */ - version_signature = read_16bitBE(0x12,streamFile); - /* encryption */ - if (version_signature == 0x0408) { - if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) - { - coding_type = coding_CRI_ADX_enc_8; - version_signature = 0x0400; - } - } - else if (version_signature == 0x0409) { - if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) - { - coding_type = coding_CRI_ADX_enc_9; - version_signature = 0x0400; - } - } + /* check for bits per sample? (only 4 makes sense for ADX) */ + if (read_8bit(6,streamFile) != 4) goto fail; - if (version_signature == 0x0300) { /* type 03 */ - header_type = meta_ADX_03; - if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */ - loop_flag = (read_32bitBE(0x18,streamFile) != 0); - loop_start_sample = read_32bitBE(0x1c,streamFile); - //loop_start_offset = read_32bitBE(0x20,streamFile); - loop_end_sample = read_32bitBE(0x24,streamFile); - //loop_end_offset = read_32bitBE(0x28,streamFile); - } - } else if (version_signature == 0x0400) { + /* check version signature, read loop info */ + version_signature = read_16bitBE(0x12,streamFile); + /* encryption */ + if (version_signature == 0x0408) { + if (find_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) + { + coding_type = coding_CRI_ADX_enc_8; + version_signature = 0x0400; + } + } + else if (version_signature == 0x0409) { + if (find_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) + { + coding_type = coding_CRI_ADX_enc_9; + version_signature = 0x0400; + } + } - off_t ainf_info_length=0; + if (version_signature == 0x0300) { /* type 03 */ + header_type = meta_ADX_03; + if (stream_offset-6 >= 0x2c) { /* enough space for loop info? */ + loop_flag = (read_32bitBE(0x18,streamFile) != 0); + loop_start_sample = read_32bitBE(0x1c,streamFile); + //loop_start_offset = read_32bitBE(0x20,streamFile); + loop_end_sample = read_32bitBE(0x24,streamFile); + //loop_end_offset = read_32bitBE(0x28,streamFile); + } + } else if (version_signature == 0x0400) { - if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */ - ainf_info_length = (off_t)read_32bitBE(0x28,streamFile); + off_t ainf_info_length=0; - header_type = meta_ADX_04; - if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */ - if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE) - loop_flag = 0; - else - loop_flag = (read_32bitBE(0x24,streamFile) != 0); + if((uint32_t)read_32bitBE(0x24,streamFile)==0x41494E46) /* AINF Header */ + ainf_info_length = (off_t)read_32bitBE(0x28,streamFile); - loop_start_sample = read_32bitBE(0x28,streamFile); - //loop_start_offset = read_32bitBE(0x2c,streamFile); - loop_end_sample = read_32bitBE(0x30,streamFile); - //loop_end_offset = read_32bitBE(0x34,streamFile); - } + header_type = meta_ADX_04; + if (stream_offset-ainf_info_length-6 >= 0x38) { /* enough space for loop info? */ + if (read_32bitBE(0x24,streamFile) == 0xFFFEFFFE) + loop_flag = 0; + else + loop_flag = (read_32bitBE(0x24,streamFile) != 0); - /* AINF header can also start after the loop points - * (may be inserted by CRI's tools but is rarely used) */ - /* ainf_magic = read_32bitBE(0x38,streamFile); */ /* 0x41494E46 */ - /* ainf_length = read_32bitBE(0x3c,streamFile); */ - /* ainf_str_id = read_string(0x40,streamFile); */ /* max size 0x10 */ - /* ainf_volume = read_16bitBE(0x50,streamFile); */ /* 0=base/max?, negative=reduce */ - /* ainf_pan_l = read_16bitBE(0x54,streamFile); */ /* 0=base, max +-128 */ - /* ainf_pan_r = read_16bitBE(0x56,streamFile); */ + loop_start_sample = read_32bitBE(0x28,streamFile); + //loop_start_offset = read_32bitBE(0x2c,streamFile); + loop_end_sample = read_32bitBE(0x30,streamFile); + //loop_end_offset = read_32bitBE(0x34,streamFile); + } - } else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */ - header_type = meta_ADX_05; - } else goto fail; /* not a known/supported version signature */ + /* AINF header can also start after the loop points + * (may be inserted by CRI's tools but is rarely used) */ + /* ainf_magic = read_32bitBE(0x38,streamFile); */ /* 0x41494E46 */ + /* ainf_length = read_32bitBE(0x3c,streamFile); */ + /* ainf_str_id = read_string(0x40,streamFile); */ /* max size 0x10 */ + /* ainf_volume = read_16bitBE(0x50,streamFile); */ /* 0=base/max?, negative=reduce */ + /* ainf_pan_l = read_16bitBE(0x54,streamFile); */ /* 0=base, max +-128 */ + /* ainf_pan_r = read_16bitBE(0x56,streamFile); */ - /* At this point we almost certainly have an ADX file, - * so let's build the VGMSTREAM. */ + } else if (version_signature == 0x0500) { /* found in some SFD : Buggy Heat, appears to have no loop */ + header_type = meta_ADX_05; + } else goto fail; /* not a known/supported version signature */ - /* high-pass cutoff frequency, always 500 that I've seen */ - cutoff = (uint16_t)read_16bitBE(0x10,streamFile); + /* At this point we almost certainly have an ADX file, + * so let's build the VGMSTREAM. */ - if (loop_start_sample == 0 && loop_end_sample == 0) { - loop_flag = 0; - } + /* high-pass cutoff frequency, always 500 that I've seen */ + cutoff = (uint16_t)read_16bitBE(0x10,streamFile); - channel_count = read_8bit(7,streamFile); - } + if (loop_start_sample == 0 && loop_end_sample == 0) { + loop_flag = 0; + } + + channel_count = read_8bit(7,streamFile); vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; /* fill in the vital statistics */ - - if (xb3d_flag) { - for (j=0;jchannels;j++) { - for (i=0;i<16;i++) { - vgmstream->ch[j].adpcm_coef[i]=read_16bitLE(4+j*channel_header_spacing+i*2,streamFile); + vgmstream->num_samples = read_32bitBE(0xc,streamFile); + vgmstream->sample_rate = read_32bitBE(8,streamFile); + /* channels and loop flag are set by allocate_vgmstream */ + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + + vgmstream->coding_type = coding_type; + if (channel_count==1) + vgmstream->layout_type = layout_none; + else + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = header_type; + + vgmstream->interleave_block_size=18; + + /* calculate filter coefficients */ + { + double x,y,z,a,b,c; + + x = cutoff; + y = vgmstream->sample_rate; + z = cos(2.0*M_PI*x/y); + + a = M_SQRT2-z; + b = M_SQRT2-1.0; + c = (a-sqrt((a+b)*(a-b)))/b; + + coef1 = floor(c*8192); + coef2 = floor(c*c*-4096); + } + + { + int i; + STREAMFILE * chstreamfile; + + /* ADX is so tightly interleaved that having two buffers is silly */ + chstreamfile = streamFile->open(streamFile,filename,18*0x400); + if (!chstreamfile) goto fail; + + for (i=0;ich[i].streamfile = chstreamfile; + + vgmstream->ch[i].channel_start_offset= + vgmstream->ch[i].offset= + stream_offset+18*i; + + vgmstream->ch[i].adpcm_coef[0] = coef1; + vgmstream->ch[i].adpcm_coef[1] = coef2; + + if (coding_type == coding_CRI_ADX_enc_8 || + coding_type == coding_CRI_ADX_enc_9) + { + int j; + vgmstream->ch[i].adx_channels = channel_count; + vgmstream->ch[i].adx_xor = xor_start; + vgmstream->ch[i].adx_mult = xor_mult; + vgmstream->ch[i].adx_add = xor_add; + + for (j=0;jch[i]); } } - vgmstream->layout_type = layout_none; - vgmstream->coding_type = coding_type; - vgmstream->meta_type = meta_XB3D_ADX; - vgmstream->sample_rate = read_32bitLE(0x70,streamFile); - vgmstream->num_samples = read_32bitLE(0x74, streamFile); - vgmstream->loop_start_sample = read_32bitLE(0x78, streamFile); - vgmstream->loop_end_sample = read_32bitLE(0x7c, streamFile); - - for (i = 0; ich[i].streamfile = streamFile->open(streamFile, filename, 0x1000); - vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset - = read_32bitLE(0x34+i*channel_header_spacing, streamFile); - if (!vgmstream->ch[i].streamfile) goto fail; - } - - } - else { - vgmstream->num_samples = read_32bitBE(0xc,streamFile); - vgmstream->sample_rate = read_32bitBE(8,streamFile); - /* channels and loop flag are set by allocate_vgmstream */ - vgmstream->loop_start_sample = loop_start_sample; - vgmstream->loop_end_sample = loop_end_sample; + } - vgmstream->coding_type = coding_type; - if (channel_count==1) - vgmstream->layout_type = layout_none; - else - vgmstream->layout_type = layout_interleave; - vgmstream->meta_type = header_type; - - vgmstream->interleave_block_size=18; - /* calculate filter coefficients */ - { - double x,y,z,a,b,c; - - x = cutoff; - y = vgmstream->sample_rate; - z = cos(2.0*M_PI*x/y); - - a = M_SQRT2-z; - b = M_SQRT2-1.0; - c = (a-sqrt((a+b)*(a-b)))/b; - - coef1 = floor(c*8192); - coef2 = floor(c*c*-4096); - } - - { - int i; - STREAMFILE * chstreamfile; - - /* ADX is so tightly interleaved that having two buffers is silly */ - chstreamfile = streamFile->open(streamFile,filename,18*0x400); - if (!chstreamfile) goto fail; - - for (i=0;ich[i].streamfile = chstreamfile; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - stream_offset+18*i; - - vgmstream->ch[i].adpcm_coef[0] = coef1; - vgmstream->ch[i].adpcm_coef[1] = coef2; - - if (coding_type == coding_CRI_ADX_enc_8 || - coding_type == coding_CRI_ADX_enc_9) - { - int j; - vgmstream->ch[i].adx_channels = channel_count; - vgmstream->ch[i].adx_xor = xor_start; - vgmstream->ch[i].adx_mult = xor_mult; - vgmstream->ch[i].adx_add = xor_add; - - for (j=0;jch[i]); - } - } - } - } return vgmstream; /* clean up anything we may have opened */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c index 9e14c3edf..01503ed88 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ahx.c @@ -1,32 +1,27 @@ -#include "../vgmstream.h" +#include "meta.h" +#include "../coding/coding.h" +#include "../util.h" #ifdef VGM_USE_MPEG -#include "meta.h" -#include "../util.h" - /* AHX is a CRI format which contains an MPEG-2 Layer 2 audio stream. * Although the MPEG frame headers are incorrect... */ - VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - off_t stream_offset; - char filename[PATH_LIMIT]; + off_t start_offset; int channel_count = 1; int loop_flag = 0; - mpeg_codec_data *data = NULL; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("ahx",filename_extension(filename))) goto fail; + if ( !check_extensions(streamFile, "ahx") ) goto fail; /* check first 2 bytes */ if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail; /* get stream offset, check for CRI signature just before */ - stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; - if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */ - (uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */ + start_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; + if ((uint16_t)read_16bitBE(start_offset-6,streamFile)!=0x2863 ||/* "(c" */ + (uint32_t)read_32bitBE(start_offset-4,streamFile)!=0x29435249 /* ")CRI" */ ) goto fail; /* check for encoding type */ @@ -45,7 +40,7 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { /* check channel count (only mono AHXs are known) */ if (read_8bit(7,streamFile) != 1) goto fail; - /* At this point we almost certainly have an ADX file, + /* At this point we almost certainly have an AHX file, * so let's build the VGMSTREAM. */ vgmstream = allocate_vgmstream(channel_count,loop_flag); @@ -59,59 +54,15 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { vgmstream->coding_type = coding_fake_MPEG2_L2; vgmstream->layout_type = layout_fake_mpeg; vgmstream->meta_type = meta_AHX; + vgmstream->codec_data = init_mpeg_codec_data_ahx(streamFile, start_offset, channel_count); - { - int i; - STREAMFILE * chstreamfile; - - chstreamfile = streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!chstreamfile) goto fail; - - for (i=0;ich[i].streamfile = chstreamfile; - - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset= - stream_offset; - } - } - - /* ooh, fun part, set up mpg123 */ - { - int rc; - data = calloc(1,sizeof(mpeg_codec_data)); - if (!data) goto fail; - - data->m = mpg123_new(NULL,&rc); - if (rc==MPG123_NOT_INITIALIZED) { - if (mpg123_init()!=MPG123_OK) goto fail; - data->m = mpg123_new(NULL,&rc); - if (rc!=MPG123_OK) goto fail; - } else if (rc!=MPG123_OK) { - goto fail; - } - - if (mpg123_open_feed(data->m)!=MPG123_OK) { - goto fail; - } - - vgmstream->codec_data = data; - } + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (data) { - if (data->m) { - mpg123_delete(data->m); - data->m = NULL; - } - free(data); - data = NULL; - } - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index d16e814d8..7dcf6f717 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -2,6 +2,7 @@ #include "meta.h" #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) +/* AKB (AAC only) - found in SQEX iOS games */ VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; @@ -30,3 +31,173 @@ fail: return NULL; } #endif + + +/* AKB - found in SQEX iOS games */ +VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset; + size_t filesize; + int loop_flag = 0, channel_count, codec; + + /* check extensions */ + if ( !check_extensions(streamFile, "akb") ) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x414B4220) /* "AKB " */ + goto fail; + + channel_count = read_8bit(0x0d,streamFile); + loop_flag = read_32bitLE(0x18,streamFile) > 0; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* 0x04: version? (iPad/IPhone?) 0x24: file_id? */ + filesize = read_32bitLE(0x08,streamFile); + codec = read_8bit(0x0c,streamFile); + vgmstream->sample_rate = (uint16_t)read_16bitLE(0x0e,streamFile); + vgmstream->num_samples = read_32bitLE(0x10,streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x18,streamFile); + /* 0x0c: some size based on codec 0x10+: unk stuff? 0xc0+: data stuff? */ + vgmstream->meta_type = meta_AKB; + + switch (codec) { +#if 0 + case 0x02: { /* some kind of ADPCM or PCM [various SFX] */ + start_offset = 0xC4; + vgmstream->coding_type = coding_APPLE_IMA4; + vgmstream->layout_type = channel_count==1 ? layout_none : layout_interleave; + vgmstream->interleave_block_size = 0x100; + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x05: { /* ogg vorbis [Final Fantasy VI, Dragon Quest II-VI] */ + /* Starting from an offset in the current libvorbis code is a bit hard so just use FFmpeg. + * Decoding seems to produce the same output with (inaudible) +-1 lower byte differences here and there. */ + ffmpeg_codec_data *ffmpeg_data; + + start_offset = 0xCC; + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,filesize-start_offset); + if ( !ffmpeg_data ) goto fail; + + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + /* These oggs have loop info in the comments, too */ + + break; + } + + case 0x06: { /* aac [The World Ends with You (iPad)] */ + /* init_vgmstream_akb above has priority, but this works fine too */ + ffmpeg_codec_data *ffmpeg_data; + + start_offset = 0x20; + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,filesize-start_offset); + if ( !ffmpeg_data ) goto fail; + + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* remove encoder delay from the "global" sample values */ + vgmstream->num_samples -= ffmpeg_data->skipSamples; + vgmstream->loop_start_sample -= ffmpeg_data->skipSamples; + vgmstream->loop_end_sample -= ffmpeg_data->skipSamples; + + break; + } +#endif + + /* AAC @20 in some cases? (see above) */ + default: + goto fail; + } + + /* open the file for reading */ + if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + goto fail; + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} + + +/* AKB2 - found in later SQEX iOS games */ +VGMSTREAM * init_vgmstream_akb2_multi(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, header_offset; + size_t datasize; + int loop_flag = 0, channel_count, codec; + + /* check extensions */ + if ( !check_extensions(streamFile, "akb") ) + goto fail; + + /* check header */ + if (read_32bitBE(0x00,streamFile) != 0x414B4232) /* "AKB2" */ + goto fail; + + /* this is weird (BE?) but seems to work */ + start_offset = (uint16_t)read_16bitBE(0x21,streamFile); + header_offset = start_offset - 0x50; + if (header_offset < 0x30) goto fail; + + channel_count = read_8bit(header_offset+0x02,streamFile); + loop_flag = read_32bitLE(header_offset+0x14,streamFile) > 0; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* 0x04: version? 0x08: filesize, 0x28: file_id?, others: no idea */ + codec = read_8bit(header_offset+0x01,streamFile); + datasize = read_32bitLE(header_offset+0x08,streamFile); + vgmstream->sample_rate = (uint16_t)read_16bitLE(header_offset+0x06,streamFile); + /* When loop_flag num_samples may be much larger than real num_samples (it's fine when looping is off) + * Actual num_samples would be loop_end_sample+1, but more testing is needed */ + vgmstream->num_samples = read_32bitLE(header_offset+0x0c,streamFile); + vgmstream->loop_start_sample = read_32bitLE(header_offset+0x10,streamFile); + vgmstream->loop_end_sample = read_32bitLE(header_offset+0x14,streamFile); + + vgmstream->meta_type = meta_AKB; + + switch (codec) { +#ifdef VGM_USE_FFMPEG + case 0x05: { /* ogg vorbis [The World Ends with You (iPhone / latest update)] */ + ffmpeg_codec_data *ffmpeg_data; + + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,datasize); + if ( !ffmpeg_data ) goto fail; + + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + break; + } +#endif + + default: + goto fail; + } + + /* open the file for reading */ + 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/bik.c b/Frameworks/vgmstream/vgmstream/src/meta/bik.c new file mode 100644 index 000000000..be396b16f --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bik.c @@ -0,0 +1,128 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util.h" + +#ifdef VGM_USE_FFMPEG + +static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample); + +/* BIK 1/2 - RAD Game Tools movies (audio/video format) */ +VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + ffmpeg_codec_data *data = NULL; + + /* check extension, case insensitive (bika = manually demuxed audio) */ + if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail; + + /* check header "BIK" (bik1) or "KB2" (bik2) (followed by version-char) */ + if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 && + (read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail; + + /* FFmpeg can parse BIK audio, but can't get the number of samples, which vgmstream needs. + * The only way to get them is to read all frame headers */ + data = init_ffmpeg_offset(streamFile, 0x0, get_streamfile_size(streamFile)); + if (!data) goto fail; + + vgmstream = allocate_vgmstream(data->channels, 0); /* alloc FFmpeg first to get channel count */ + if (!vgmstream) goto fail; + vgmstream->codec_data = data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_FFmpeg; + vgmstream->sample_rate = data->sampleRate; + + /* manually get num_samples since data->totalSamples is always 0 */ + vgmstream->num_samples = bik_get_num_samples(streamFile, data->bitsPerSample); + if (vgmstream->num_samples == 0) + goto fail; + + return vgmstream; + +fail: + free_ffmpeg(data); + if (vgmstream) { + vgmstream->codec_data = NULL; + close_vgmstream(vgmstream); + } + return NULL; +} + +/** + * Gets the number of samples in a BIK file by reading all frames' headers, + * as they are not in the main header. The header for BIK1 and 2 is the same. + * (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough) + * + * Needs bits per sample to calculate PCM samples, since most bink audio seems to use 32, actually. + */ +static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample) { + uint32_t *offsets = NULL; + uint32_t num_samples_b = 0; + off_t cur_offset; + size_t filesize; + int i, j, num_frames, num_tracks; + int target_stream = 0; + + filesize = get_streamfile_size(streamFile); + + num_frames = read_32bitLE(0x08,streamFile); + if (num_frames <= 0) goto fail; + if (num_frames > 0x100000) goto fail; /* something must be off (avoids big allocs below) */ + + /* multichannel audio is usually N tracks of stereo/mono, no way to know channel layout */ + num_tracks = read_32bitLE(0x28,streamFile); + if (num_tracks<=0 || num_tracks > 255) goto fail; + + /* find the frame index table, which is after 3 audio headers of size 4 for each track */ + cur_offset = 0x2c + num_tracks*4 * 3; + + /* read offsets in a buffer, to avoid fseeking to the table back and forth + * the number of frames can be highly variable so we'll alloc */ + offsets = malloc(sizeof(uint32_t) * num_frames); + if (!offsets) goto fail; + + for (i=0; i < num_frames; i++) { + offsets[i] = read_32bitLE(cur_offset,streamFile) & 0xFFFFFFFE; /* mask first bit (= keyframe) */ + cur_offset += 0x4; + + if (offsets[i] > filesize) goto fail; + } + /* after the last index is the file size, validate just in case */ + if (read_32bitLE(cur_offset,streamFile)!=filesize) goto fail; + + /* multistream support just for fun (FFmpeg should select the same target stream) + * (num_samples for other streams seem erratic though) */ + if (target_stream > num_tracks) goto fail; + if (target_stream == 0) target_stream = 1; + //VGM_ASSERT(num_tracks > 1, "BIK: multiple streams found (%i entries)\n", num_tracks);//FFmpeg data has this + + /* read each frame header and sum all samples + * a frame has N audio packets with header (one per track) + video packet */ + for (i=0; i < num_frames; i++) { + cur_offset = offsets[i]; + + /* read audio packet headers */ + for (j=0; j < num_tracks; j++) { + uint32_t ap_size, samples_b; + ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */ + samples_b = read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */ + if (ap_size==0) break; /* no audio in this frame */ + + if (j == target_stream-1) { /* target samples found, read next frame */ + num_samples_b += samples_b; + break; + } else { /* check next audio packet */ + cur_offset += 4 + ap_size; /* todo sometimes ap_size doesn't include itself (+4), others it does? */ + } + } + } + + + free(offsets); + return num_samples_b / (bits_per_sample / 8); + +fail: + free(offsets); + return 0; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dsp_adx.c b/Frameworks/vgmstream/vgmstream/src/meta/dsp_adx.c new file mode 100644 index 000000000..addf8dd74 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/dsp_adx.c @@ -0,0 +1,68 @@ +#include "meta.h" +#include "../coding/coding.h" +#include "../util.h" + +/* .ADX - from Xenoblade 3D */ +/* Xenoblade Chronicles 3D uses an adx extension as with + * the Wii version, but it's actually DSP ADPCM. */ +VGMSTREAM * init_vgmstream_dsp_adx(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + int loop_flag, channel_count; + int channel_header_spacing = 0x34; + + /* check extension, case insensitive */ + if (!check_extensions(streamFile,"adx")) goto fail; + + /* check header */ + if (read_32bitBE(0,streamFile)!=0x02000000) goto fail; + + channel_count = read_32bitLE(0, streamFile); + loop_flag = read_16bitLE(0x6e, streamFile); + + if (channel_count > 2 || channel_count < 0) goto fail; + + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_XB3D_ADX; + vgmstream->sample_rate = read_32bitLE(0x70,streamFile); + vgmstream->num_samples = read_32bitLE(0x74, streamFile); + vgmstream->loop_start_sample = read_32bitLE(0x78, streamFile); + vgmstream->loop_end_sample = read_32bitLE(0x7c, streamFile); + + dsp_read_coefs_le(vgmstream,streamFile, 0x4, channel_header_spacing); + + + /* semi-interleave: manually open streams at offset */ + { + char filename[PATH_LIMIT]; + int i; + + streamFile->get_name(streamFile,filename,sizeof(filename)); + for (i = 0; ich[i].streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); + vgmstream->ch[i].channel_start_offset = + vgmstream->ch[i].offset = read_32bitLE(0x34+i*channel_header_spacing, streamFile); + if (!vgmstream->ch[i].streamfile) goto fail; + } + } + +#if 0 + /* this should be equivalent to the above, but more testing is needed */ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = + read_32bitLE(0x34+1*channel_header_spacing, streamFile) + - read_32bitLE(0x34+0*channel_header_spacing, streamFile); + + if (!vgmstream_open_stream(vgmstream,streamFile, read_32bitLE(0x34+0*channel_header_spacing, streamFile))) + goto fail; +#endif + + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 32082f62e..022f8a9eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -115,6 +115,7 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) uint64_t offset = data->offset; int max_to_copy = 0; int ret; + if (data->header_insert_block) { if (offset < data->header_size) { max_to_copy = (int)(data->header_size - offset); @@ -132,6 +133,13 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) } offset -= data->header_size; } + + /* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped; + * it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */ + if (offset + buf_size > data->size + data->header_size) { + buf_size = data->size - offset; /* header "read" is manually inserted later */ + } + ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile); if (ret > 0) { offset += ret; @@ -139,9 +147,11 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) ret += max_to_copy; } } + if (data->header_insert_block) { offset += data->header_size; } + data->offset = offset; return ret; } @@ -160,6 +170,8 @@ static int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size) static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { ffmpeg_codec_data *data = (ffmpeg_codec_data *) opaque; + int ret = 0; + if (whence & AVSEEK_SIZE) { return data->size + data->header_size; } @@ -179,9 +191,14 @@ static int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) offset += data->header_size; break; } - if (offset > data->size + data->header_size) + + /* clamp offset; fseek returns 0 when offset > size, too */ + if (offset > data->size + data->header_size) { offset = data->size + data->header_size; - return data->offset = offset; + } + + data->offset = offset; + return ret; } @@ -208,7 +225,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * int errcode, i; - int streamIndex; + int streamIndex, streamCount; AVStream *stream; AVCodecParameters *codecPar; @@ -256,6 +273,7 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * /* find valid audio stream inside */ streamIndex = -1; + streamCount = 0; /* audio streams only */ for (i = 0; i < data->formatCtx->nb_streams; ++i) { stream = data->formatCtx->streams[i]; @@ -265,12 +283,15 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * } else { stream->discard = AVDISCARD_ALL; /* disable demuxing unneded streams */ } + if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO) + streamCount++; } if (streamIndex < 0) goto fail; - + data->streamIndex = streamIndex; stream = data->formatCtx->streams[streamIndex]; + data->streamCount = streamCount; /* prepare codec and frame/packet buffers */ @@ -361,6 +382,12 @@ ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * errcode = init_seek(data); if (errcode < 0) goto fail; + /* expose start samples to be skipped (encoder delay, usually added by MDCT-based encoders like AAC/MP3/ATRAC3/XMA/etc) + * get after init_seek because some demuxers like AAC only fill skip_samples for the first packet */ + if (stream->start_skip_samples) /* samples to skip in the first packet */ + data->skipSamples = stream->start_skip_samples; + else if (stream->skip_samples) /* samples to skip in any packet (first in this case), used sometimes instead (ex. AAC) */ + data->skipSamples = stream->skip_samples; return data; @@ -435,10 +462,11 @@ static int init_seek(ffmpeg_codec_data * data) { if (ts == INT64_MIN) ts = 0; - /* apparently some (non-audio?) streams start with a DTS before 0, but some read_seeks expect 0, which would disrupt the index - * we may need to keep start_ts around, since avstream/codec/format isn't always set */ + /* Some streams start with negative DTS (observed in Ogg). For Ogg seeking to negative or 0 doesn't alter the output. + * It does seem seeking before decoding alters a bunch of (inaudible) +-1 lower bytes though. */ + VGM_ASSERT(ts != 0, "FFMPEG: negative start_ts (%li)\n", (long)ts); if (ts != 0) - goto fail; + ts = 0; /* add index 0 */ ret = av_add_index_entry(stream, pos, ts, size, distance, AVINDEX_KEYFRAME); @@ -463,6 +491,9 @@ fail: void free_ffmpeg(ffmpeg_codec_data *data) { + if (data == NULL) + return; + if (data->lastReadPacket) { av_packet_unref(data->lastReadPacket); free(data->lastReadPacket); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c index 29e3a89e3..710a5b77e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb.c @@ -113,9 +113,10 @@ VGMSTREAM * init_vgmstream_fsb4_wav(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { VGMSTREAM * vgmstream = NULL; - off_t start_offset, h_off, s_off; + off_t start_offset; size_t custom_data_offset; int loop_flag = 0; + int target_stream = 0; FSB_HEADER fsbh; @@ -123,8 +124,6 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { if ( !check_extensions(streamFile, "fsb,wii") ) goto fail; - h_off = offset; - /* check header */ fsbh.id = read_32bitBE(offset+0x00,streamFile); if (fsbh.id == 0x46534231) { /* "FSB1" (somewhat different from other fsbs) */ @@ -133,26 +132,34 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { fsbh.shdrsize_min = 0x40; /* main header */ - fsbh.numsamples = read_32bitLE(h_off+0x04,streamFile); - fsbh.datasize = read_32bitLE(h_off+0x08,streamFile); + fsbh.numsamples = read_32bitLE(offset+0x04,streamFile); + fsbh.datasize = read_32bitLE(offset+0x08,streamFile); fsbh.shdrsize = 0x40; fsbh.version = 0; fsbh.flags = 0; - s_off = offset+fsbh.hdrsize; - /* sample header */ - /* 0x00:name(len=0x20) */ - fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); - fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); - fsbh.deffreq = read_32bitLE(s_off+0x28,streamFile); - /* 0x2c:? 0x2e:? 0x30:? 0x32:? */ - fsbh.mode = read_32bitLE(s_off+0x34,streamFile); - fsbh.loopstart = read_32bitLE(s_off+0x38,streamFile); - fsbh.loopend = read_32bitLE(s_off+0x3c,streamFile); + if (fsbh.numsamples > 1) goto fail; - fsbh.numchannels = (fsbh.mode & FSOUND_STEREO) ? 2 : 1; - if (fsbh.loopend > fsbh.lengthsamples) /* this seems common... */ - fsbh.lengthsamples = fsbh.loopend; + /* sample header (first stream only, not sure if there are multi-FSB1) */ + { + off_t s_off = offset+fsbh.hdrsize; + + /* 0x00:name(len=0x20) */ + fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); + fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); + fsbh.deffreq = read_32bitLE(s_off+0x28,streamFile); + /* 0x2c:? 0x2e:? 0x30:? 0x32:? */ + fsbh.mode = read_32bitLE(s_off+0x34,streamFile); + fsbh.loopstart = read_32bitLE(s_off+0x38,streamFile); + fsbh.loopend = read_32bitLE(s_off+0x3c,streamFile); + + fsbh.numchannels = (fsbh.mode & FSOUND_STEREO) ? 2 : 1; + if (fsbh.loopend > fsbh.lengthsamples) /* this seems common... */ + fsbh.lengthsamples = fsbh.loopend; + + start_offset = offset + fsbh.hdrsize + fsbh.shdrsize; + custom_data_offset = offset + fsbh.hdrsize + fsbh.shdrsize_min; /* DSP coefs, seek tables, etc */ + } } else { /* other FSBs (common/extended format) */ if (fsbh.id == 0x46534232) { /* "FSB2" */ @@ -172,12 +179,12 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { } /* main header */ - fsbh.numsamples = read_32bitLE(h_off+0x04,streamFile); - fsbh.shdrsize = read_32bitLE(h_off+0x08,streamFile); - fsbh.datasize = read_32bitLE(h_off+0x0c,streamFile); + fsbh.numsamples = read_32bitLE(offset+0x04,streamFile); + fsbh.shdrsize = read_32bitLE(offset+0x08,streamFile); + fsbh.datasize = read_32bitLE(offset+0x0c,streamFile); if (fsbh.hdrsize > 0x10) { - fsbh.version = read_32bitLE(h_off+0x10,streamFile); - fsbh.flags = read_32bitLE(h_off+0x14,streamFile); + fsbh.version = read_32bitLE(offset+0x10,streamFile); + fsbh.flags = read_32bitLE(offset+0x14,streamFile); /* FSB4: 0x18:hash 0x20:guid */ } else { fsbh.version = 0; @@ -193,43 +200,62 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { } if (fsbh.shdrsize < fsbh.shdrsize_min) goto fail; + if (target_stream > fsbh.numsamples || target_stream < 0) goto fail; + if (target_stream == 0) target_stream = 1; - s_off = offset+fsbh.hdrsize; - /* sample header */ - /* 0x00:size 0x02:name(len=size) */ - fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); - fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); - fsbh.loopstart = read_32bitLE(s_off+0x28,streamFile); - fsbh.loopend = read_32bitLE(s_off+0x2c,streamFile); - fsbh.mode = read_32bitLE(s_off+0x30,streamFile); - fsbh.deffreq = read_32bitLE(s_off+0x34,streamFile); - /* 0x38:defvol 0x3a:defpan 0x3c:defpri */ - fsbh.numchannels = read_16bitLE(s_off+0x3e,streamFile); - /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsbh.varpan */ - /* 0x50:extended_data (of size_32bits, when fsbh.shdrsize > 0x50) */ + /* sample header (N-stream) */ + { + int i; + off_t s_off = offset + fsbh.hdrsize; + off_t d_off = offset + fsbh.hdrsize + fsbh.shdrsize; + + /* find target_stream data offset, reading each header */ + for(i=1; i <= fsbh.numsamples; i++) { + /* 0x00:size 0x02:name(len=size) */ + fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile); + fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile); + fsbh.loopstart = read_32bitLE(s_off+0x28,streamFile); + fsbh.loopend = read_32bitLE(s_off+0x2c,streamFile); + fsbh.mode = read_32bitLE(s_off+0x30,streamFile); + fsbh.deffreq = read_32bitLE(s_off+0x34,streamFile); + /* 0x38:defvol 0x3a:defpan 0x3c:defpri */ + fsbh.numchannels = read_16bitLE(s_off+0x3e,streamFile); + /* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsbh.varpan */ + + if (target_stream == i) /* d_off found */ + break; + + s_off += fsbh.shdrsize_min; /* default size */ + if (fsbh.version == FMOD_FSB_VERSION_4_0) { + uint32_t extended_data = read_32bitLE(s_off+0x48,streamFile); /* +0x50:extended_data of size_32bits */ + if (extended_data > fsbh.shdrsize_min) + s_off += extended_data; + } + + + d_off += fsbh.lengthcompressedbytes; /* there is no offset so manually count */ + //d_off += d_off % 0x30; /*todo some formats need padding, not sure when/how */ + } + if (i > fsbh.numsamples) goto fail; /* not found */ + + start_offset = d_off; + custom_data_offset = s_off + fsbh.shdrsize_min; /* DSP coefs, seek tables, etc */ + } } - /* FSB header ok, check other stuff */ - if (fsbh.numsamples != 1) { /* multistream */ - VGM_LOG("FSB numsamples > 1 found\n"); - goto fail; - } /* XOR encryption for some FSB4 */ if (fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED) { VGM_LOG("FSB ENCRYPTED found\n"); goto fail; } #if 0 - /* sometimes there is garbage at the end or missing bytes? (Guitar Hero Wii) */ + /* sometimes there is garbage at the end or missing bytes due to improper demuxing */ if (fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset) { VGM_LOG("FSB wrong head/datasize found\n"); goto fail; } #endif - start_offset = offset + fsbh.hdrsize + fsbh.shdrsize; - custom_data_offset = offset + fsbh.hdrsize + fsbh.shdrsize_min; /* DSP coefs, seek tables, etc */ - /* Loops by default unless disabled (sometimes may add FSOUND_LOOP_NORMAL). Often streams * repeat over and over (some tracks that shouldn't do this based on the flags, no real way to identify them). */ loop_flag = !(fsbh.mode & FSOUND_LOOP_OFF); /* (fsbh.mode & FSOUND_LOOP_NORMAL) */ @@ -244,6 +270,7 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { vgmstream->num_samples = fsbh.lengthsamples; vgmstream->loop_start_sample = fsbh.loopstart; vgmstream->loop_end_sample = fsbh.loopend; + vgmstream->num_streams = fsbh.numsamples; vgmstream->meta_type = fsbh.meta_type; /* parse format */ @@ -253,61 +280,52 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { mpeg_codec_data *mpeg_data = NULL; coding_t mpeg_coding_type; - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &mpeg_coding_type, NULL, NULL); +#if 0 + int fsb_padding = 0; + if (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED) + fsb_padding = fsbh.numchannels > 2 ? 16 : 2; + else if (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4) + fsb_padding = fsbh.numchannels > 2 ? 16 : 4; + else /* seems to be needed with no flag */ + fsb_padding = fsbh.numchannels > 2 ? 16 : 0; + + mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, 0, fsb_padding); + if (!mpeg_data) goto fail; + + vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding; + if (vgmstream->channels > 2) vgmstream->loop_flag = 0;//todo not implemented yet +#endif + + VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n"); + VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); + + mpeg_data = init_mpeg_codec_data(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; vgmstream->coding_type = mpeg_coding_type; vgmstream->layout_type = layout_mpeg; - /* struct mpg123_frameinfo mpeg_info; */ - /* if (MPG123_OK != mpg123_info(mpeg_data->m, &mpeg_info)) goto fail; */ - VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n"); - VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n"); - - /* when these flags are set each MPEG frame is 0-padded at the end, and mpg123 will complain to stderr (but ignore them) - * no way to easily skip the padding so for now we'll just disable stderr output */ - if ((fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED) || (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) { - mpeg_set_error_logging(mpeg_data, 0); - } + mpeg_set_error_logging(mpeg_data, 0); #elif defined(VGM_USE_FFMPEG) - /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset, this won't work */ - ffmpeg_codec_data *ffmpeg_data = NULL; - - ffmpeg_data = init_ffmpeg_offset(streamFile, 0, streamFile->get_size(streamFile)); - if ( !ffmpeg_data ) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - + /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */ + goto fail; #else - VGM_LOG("FSB4 MPEG found\n"); goto fail; #endif } else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */ - if (fsbh.mode & FSOUND_IMAADPCMSTEREO) { /* noninterleaved, true stereo IMA */ - /* FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3) */ - vgmstream->coding_type = coding_MS_IMA; /* todo not always working in Hard Corps, interleave problem? */ - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x24*vgmstream->channels; - //VGM_LOG("FSB FSOUND_IMAADPCMSTEREO found\n"); - } else { - /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ - vgmstream->coding_type = coding_MS_IMA; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x24*vgmstream->channels; - //VGM_LOG("FSB FSOUND_IMAADPCM found\n"); -#if 0 - if (fsbh.numchannels > 2) { /* Blade Kitten 5.1 */ - vgmstream->coding_type = coding_XBOX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x12 * vgmstream->channels; - } -#endif + //VGM_ASSERT(fsbh.mode & FSOUND_IMAADPCMSTEREO, "FSB FSOUND_IMAADPCMSTEREO found\n"); + /* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different + * (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */ - } + /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */ + vgmstream->coding_type = coding_XBOX; + vgmstream->layout_type = layout_none; + /* "interleaved header" IMA, which seems only used with >2ch (ex. Blade Kitten 5.1) */ + if (vgmstream->channels > 2) + vgmstream->coding_type = coding_FSB_IMA; } else if (fsbh.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis @@ -339,7 +357,6 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) { vgmstream->layout_type = layout_none; #else - VGM_LOG("FSB XMA found\n"); goto fail; #endif } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 240bd55c9..dfc6e617f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -5,448 +5,261 @@ /* FSB5 header */ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; + off_t StartOffset = 0; + off_t SampleHeaderStart = 0, DSPInfoStart = 0; + size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength; + + uint32_t BaseSamples = 0, LoopStart = 0, LoopEnd = 0, NumSamples = 0; + int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID; + int TotalStreams, TargetStream = 0; + int i; - off_t StartOffset; - - int LoopFlag = 0; - int32_t LoopStart, LoopEnd; - - int NumSamples; - int ChannelCount; - int SampleRate; - int DSPInfoStart = 0; - int SampleHeaderStart, SampleHeaderLength, NameTableLength, SampleDataLength, CodingID, SampleMode; - int ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("fsb",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"fsb")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x46534235) goto fail; /* "FSB5" */ - if (read_32bitLE(0x04,streamFile) != 0x01) goto fail; /* Version ID */ - if (read_32bitLE(0x08,streamFile) != 0x01) goto fail; /* Number of Sample Files */ - SampleHeaderStart = 0x3C; + //v0 has extra flags at 0x1c and SampleHeaderStart = 0x40? + if (read_32bitLE(0x04,streamFile) != 0x01) goto fail; /* Version ID */ + + TotalStreams = read_32bitLE(0x08,streamFile); SampleHeaderLength = read_32bitLE(0x0C,streamFile); - NameTableLength = read_32bitLE(0x10,streamFile); - SampleDataLength = read_32bitLE(0x14,streamFile); + NameTableLength = read_32bitLE(0x10,streamFile); + SampleDataLength = read_32bitLE(0x14,streamFile); CodingID = read_32bitLE(0x18,streamFile); + /* 0x1c (8): zero, 0x24 (16): hash, 0x34 (8): unk */ + BaseHeaderLength = 0x3C; + + SampleHeaderStart = BaseHeaderLength; if ((SampleHeaderLength + NameTableLength + SampleDataLength + 0x3C) != get_streamfile_size(streamFile)) goto fail; + if (TargetStream == 0) TargetStream = 1; /* default to 1 */ + if (TargetStream > TotalStreams || TotalStreams < 0) goto fail; - StartOffset = SampleHeaderLength + NameTableLength + 0x3C; - SampleMode = read_32bitLE(SampleHeaderStart+0x00,streamFile); - - if (SampleMode&0x02) - { - SampleRate = 48000; - } else { - SampleRate = 44100; - } + /* find target stream header and data offset and read all needed values for later use + * (reads one by one as the size of a single stream header is variable) */ + for (i = 0; i < TotalStreams; i++) { + off_t DataStart = 0; + size_t StreamHeaderLength = 0; + uint32_t SampleMode; - if (SampleMode&0x20) - { - ChannelCount = 2; - } else { - ChannelCount = 1; - } - - ExtraFlagStart = SampleHeaderStart+0x08; + SampleMode = read_32bitLE(SampleHeaderStart+0x00,streamFile); + BaseSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile); + StreamHeaderLength += 0x08; - if (SampleMode&0x01) - { - do - { - ExtraFlag = read_32bitLE(ExtraFlagStart,streamFile); - ExtraFlagType = (ExtraFlag>>25)&0x7F; - ExtraFlagSize = (ExtraFlag>>1)&0xFFFFFF; - ExtraFlagEnd = (ExtraFlag&0x01); - - switch(ExtraFlagType) - { - case 0x02: /* Sample Rate Info */ - { - SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile); - } - break; - - case 0x03: /* Loop Info */ - { - LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile); - if (LoopStart != 0x00) { - LoopFlag = 1; - LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); - } - } - break; - - case 0x07: /* DSP Info (Coeffs), only used if coding is DSP??? */ - { - DSPInfoStart = ExtraFlagStart+0x04; - } - break; + /* get global offset */ + DataStart = (SampleMode >> 7) * 0x20; + /* get sample rate */ + switch ((SampleMode >> 1) & 0x0f) { /* bits 5..1 */ + case 0: SampleRate = 4000; break; //??? + case 1: SampleRate = 8000; break; + case 2: SampleRate = 11000; break; + case 3: SampleRate = 11025; break; + case 4: SampleRate = 16000; break; + case 5: SampleRate = 22050; break; + case 6: SampleRate = 24000; break; + case 7: SampleRate = 32000; break; + case 8: SampleRate = 44100; break; + case 9: SampleRate = 48000; break; + case 10: SampleRate = 96000; break; //??? + default: + SampleRate = 44100; + break; /* probably specified in the extra flags */ } - ExtraFlagStart+=ExtraFlagSize+0x04; - } - while (ExtraFlagEnd != 0x00); + + /* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */ + switch ((SampleMode >> 5) & 0x03) { /* bits 7..6 */ + case 0: ChannelCount = 1; break; + case 1: ChannelCount = 2; break; + case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */ + case 3: ChannelCount = 8; break;/* some IMA ADPCM */ + default: /* other values (ex. 10ch) seem specified in the extra flags */ + goto fail; + } + + /* get extra flags */ + if (SampleMode&0x01) { /* bit 0 */ + uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd; + + ExtraFlagStart = SampleHeaderStart+0x08; + do { + ExtraFlag = read_32bitLE(ExtraFlagStart,streamFile); + ExtraFlagType = (ExtraFlag>>25)&0x7F; + ExtraFlagSize = (ExtraFlag>>1)&0xFFFFFF; + ExtraFlagEnd = (ExtraFlag&0x01); + + switch(ExtraFlagType) { + case 0x01: /* Channel Info */ + ChannelCount = read_8bit(ExtraFlagStart+0x04,streamFile); + break; + case 0x02: /* Sample Rate Info */ + SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile); + break; + case 0x03: /* Loop Info */ + LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile); + if (ExtraFlagSize > 0x04) /* probably no needed */ + LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile); + + /* when start is 0 seems the song reoeats with no real looping (ex. Sonic Boom Fire & Ice jingles) */ + LoopFlag = (LoopStart != 0x00); + break; + case 0x07: /* DSP Info (Coeffs), only used if coding is DSP??? */ + DSPInfoStart = ExtraFlagStart + 0x04; + break; + default: + VGM_LOG("FSB5: unknown extra flag %i at 0x%04x\n", ExtraFlagType, ExtraFlagStart); + break; + } + + ExtraFlagStart += 0x04 + ExtraFlagSize; + StreamHeaderLength += 0x04 + ExtraFlagSize; + } while (ExtraFlagEnd != 0x00); + } + + /* stream found */ + if (i == TotalStreams-1) { + StartOffset = BaseHeaderLength + SampleHeaderLength + NameTableLength + DataStart; + break; + } + + /* continue searching */ + SampleHeaderStart += StreamHeaderLength; } + /* target stream not found*/ + if (!StartOffset) goto fail; + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(ChannelCount,LoopFlag); if (!vgmstream) goto fail; /* fill in the vital statistics */ - vgmstream->channels = ChannelCount; - vgmstream->sample_rate = SampleRate; + vgmstream->channels = ChannelCount; + vgmstream->sample_rate = SampleRate; + vgmstream->num_streams = TotalStreams; + vgmstream->meta_type = meta_FSB5; + switch (CodingID) { + case 0x00: /* FMOD_SOUND_FORMAT_NONE */ + goto fail; - switch (CodingID) - { - case 0x00: /* FMOD_SOUND_FORMAT_NONE */ - { - goto fail; - } - break; - case 0x01: /* FMOD_SOUND_FORMAT_PCM8 */ - { - goto fail; - } - break; + case 0x01: /* FMOD_SOUND_FORMAT_PCM8 */ + goto fail; - case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */ - { - - NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/4; - - if (ChannelCount == 1) - { - vgmstream->layout_type = layout_none; - } else { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x02; - } - - - vgmstream->coding_type = coding_PCM16LE; - } - break; - - case 0x03:/* FMOD_SOUND_FORMAT_PCM24 */ - { - goto fail; - } - break; - - case 0x04: /* FMOD_SOUND_FORMAT_PCM32 */ - { - goto fail; - } - break; - - case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT */ - { - goto fail; - } - break; - - case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM */ - { - if (ChannelCount == 1) - { - NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/4; - vgmstream->layout_type = layout_none; - } else { - NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/(2*ChannelCount); - vgmstream->layout_type = layout_interleave_byte; - vgmstream->interleave_block_size = 0x02; - } - - vgmstream->coding_type = coding_NGC_DSP; - - /* DSP Coeffs */ - { - int c,i; - for (c=0;cch[c].adpcm_coef[i] = read_16bitBE(DSPInfoStart + c*0x2E + i*2,streamFile); + case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */ + NumSamples = BaseSamples / 4; + if (ChannelCount == 1) { + vgmstream->layout_type = layout_none; + } else { + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x02; } - } - } - } - break; - case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */ - { - - NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/4; - vgmstream->layout_type = layout_none; - vgmstream->coding_type = coding_XBOX; - - } - break; - - case 0x08: /* FMOD_SOUND_FORMAT_VAG */ - { - goto fail; - } - break; - - case 0x09: /* FMOD_SOUND_FORMAT_HEVAG */ - { - goto fail; - } - break; - - case 0x0A: /* FMOD_SOUND_FORMAT_XMA */ - { - goto fail; - } - break; - - case 0x0B: /* FMOD_SOUND_FORMAT_MPEG */ - { - NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/2/ChannelCount; - - #ifdef VGM_USE_MPEG - { - mpeg_codec_data *mpeg_data = NULL; - struct mpg123_frameinfo mi; - coding_t ct; - - mpeg_data = init_mpeg_codec_data(streamFile, StartOffset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; - - if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail; - - vgmstream->coding_type = ct; - vgmstream->layout_type = layout_mpeg; - if (mi.vbr != MPG123_CBR) goto fail; - vgmstream->interleave_block_size = 0; - } + vgmstream->coding_type = coding_PCM16LE; break; + + case 0x03: /* FMOD_SOUND_FORMAT_PCM24 */ + goto fail; + + case 0x04: /* FMOD_SOUND_FORMAT_PCM32 */ + goto fail; + + case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT */ + goto fail; + + case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM */ + if (ChannelCount == 1) { + NumSamples = BaseSamples / 4; + vgmstream->layout_type = layout_none; + } else { + NumSamples = BaseSamples / (2*ChannelCount); + vgmstream->layout_type = layout_interleave_byte; + vgmstream->interleave_block_size = 0x02; + } + + dsp_read_coefs_be(vgmstream,streamFile,DSPInfoStart,0x2E); + vgmstream->coding_type = coding_NGC_DSP; + break; + + case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */ + NumSamples = BaseSamples / 4; + vgmstream->layout_type = layout_none; + vgmstream->coding_type = coding_XBOX; + if (vgmstream->channels > 2) /* multichannel FSB IMA (interleaved header) */ + vgmstream->coding_type = coding_FSB_IMA; + break; + + case 0x08: /* FMOD_SOUND_FORMAT_VAG */ + goto fail; + + case 0x09: /* FMOD_SOUND_FORMAT_HEVAG */ + goto fail; + + case 0x0A: /* FMOD_SOUND_FORMAT_XMA */ + goto fail; + +#ifdef VGM_USE_MPEG + case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG */ + mpeg_codec_data *mpeg_data = NULL; + coding_t mpeg_coding_type; + + NumSamples = BaseSamples / 2 / ChannelCount; + +#if 0 + int fsb_padding = vgmstream->channels > 2 ? 16 : 0;//todo fix + + mpeg_data = init_mpeg_codec_data_interleaved(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels, 0, fsb_padding); + if (!mpeg_data) goto fail; + + vgmstream->interleave_block_size = mpeg_data->current_frame_size + mpeg_data->current_padding; + if (vgmstream->channels > 2) vgmstream->loop_flag = 0;//todo not implemented yet #endif - } - break; - case 0x0C: /* FMOD_SOUND_FORMAT_CELT */ - { - goto fail; - } - break; + if (vgmstream->channels > 2) + goto fail; /* no multichannel for now */ - case 0x0D: /* FMOD_SOUND_FORMAT_AT9 */ - { - goto fail; - } - break; + mpeg_data = init_mpeg_codec_data(streamFile, StartOffset, &mpeg_coding_type, vgmstream->channels); + if (!mpeg_data) goto fail; - case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */ - { - goto fail; - } - break; + vgmstream->codec_data = mpeg_data; + vgmstream->coding_type = mpeg_coding_type; + vgmstream->layout_type = layout_mpeg; + + mpeg_set_error_logging(mpeg_data, 0); + break; + } +#endif + case 0x0C: /* FMOD_SOUND_FORMAT_CELT */ + goto fail; + + case 0x0D: /* FMOD_SOUND_FORMAT_AT9 */ + goto fail; + + case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */ + goto fail; + + case 0x0F: /* FMOD_SOUND_FORMAT_VORBIS */ + goto fail; - case 0x0F: /* FMOD_SOUND_FORMAT_VORBIS */ - { - goto fail; - } - break; default: goto fail; } - + vgmstream->num_samples = NumSamples; - vgmstream->meta_type = meta_FSB5; - - if (LoopFlag) - { - vgmstream->loop_start_sample = LoopStart; - vgmstream->loop_end_sample = LoopEnd; + if (LoopFlag) { + vgmstream->loop_start_sample = LoopStart; + vgmstream->loop_end_sample = LoopEnd; } - /* 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; - - if (vgmstream->coding_type == coding_XBOX) { - /* xbox interleaving is a little odd */ - vgmstream->ch[i].channel_start_offset=StartOffset; - } else { - vgmstream->ch[i].channel_start_offset= - StartOffset+vgmstream->interleave_block_size*i; - } - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; - - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } - -#if 0 -// FSB5 MPEG -VGMSTREAM * init_vgmstream_fsb5_mpeg(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int channel_count, channels, loop_flag, fsb_mainheader_len, fsb_subheader_len, FSBFlag, rate; - long sample_rate = 0, num_samples = 0; - uint16_t mp3ID; - -#ifdef VGM_USE_MPEG - mpeg_codec_data *mpeg_data = NULL; - coding_t mpeg_coding_type = coding_MPEG1_L3; -#endif - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("fsb",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) == 0x46534235) /* "FSB5" */ - { - fsb_mainheader_len = 0x3C; - } - else - { - goto fail; - } - - //fsb_subheader_len = read_16bitLE(fsb_mainheader_len,streamFile); - - /* "Check if the FSB is used as conatiner or as single file" */ - if (read_32bitBE(0x04,streamFile) != 0x01000000) - goto fail; - -#if 0 - /* Check channel count, multi-channel not supported and will be refused */ - if ((read_16bitLE(0x6E,streamFile) != 0x2) && - (read_16bitLE(0x6E,streamFile) != 0x1)) - goto fail; -#endif - - start_offset = fsb_mainheader_len+fsb_subheader_len+0x10; - - /* Check the MPEG Sync Header */ - mp3ID = read_16bitBE(start_offset,streamFile); - if ((mp3ID&0xFFE0) != 0xFFE0) - goto fail; - - channel_count = read_16bitLE(fsb_mainheader_len+0x3E,streamFile); - if (channel_count != 1 && channel_count != 2) - goto fail; - - FSBFlag = read_32bitLE(fsb_mainheader_len+0x30,streamFile); - if (FSBFlag&0x2 || FSBFlag&0x4 || FSBFlag&0x6) - loop_flag = 1; - - num_samples = (read_32bitLE(fsb_mainheader_len+0x2C,streamFile)); - -#ifdef VGM_USE_MPEG - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, -1, -1, &mpeg_coding_type, &rate, &channels); // -1 to not check sample rate or channels - if (!mpeg_data) goto fail; - - //channel_count = channels; - sample_rate = rate; - -#else - // reject if no MPEG support - goto fail; -#endif - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = num_samples; - vgmstream->channels = channel_count; - - /* Still WIP */ - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(fsb_mainheader_len+0x28,streamFile); - vgmstream->loop_end_sample = read_32bitLE(fsb_mainheader_len+0x2C,streamFile); - } - vgmstream->meta_type = meta_FSB_MPEG; - -#ifdef VGM_USE_MPEG - /* NOTE: num_samples seems to be quite wrong for MPEG */ - vgmstream->codec_data = mpeg_data; - vgmstream->layout_type = layout_mpeg; - vgmstream->coding_type = mpeg_coding_type; -#else - // reject if no MPEG support - goto fail; -#endif - - -#if 0 - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0x18,streamFile)/960*1152; - vgmstream->loop_end_sample = read_32bitBE(0x1C,streamFile)/960*1152; - } -#endif - - /* open the file for reading */ - { - int i; - STREAMFILE * file; - if(vgmstream->layout_type == layout_interleave) - { - 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; - } - } - -#ifdef VGM_USE_MPEG - else if(vgmstream->layout_type == layout_mpeg) { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,MPEG_BUFFER_SIZE); - vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset=start_offset; - } - - } -#endif - else { goto fail; } - } - - return vgmstream; - - /* clean up anything we may have opened */ -fail: -#ifdef VGM_USE_MPEG - if (mpeg_data) { - mpg123_delete(mpeg_data->m); - free(mpeg_data); - - if (vgmstream) { - vgmstream->codec_data = NULL; - } - } -#endif - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} -#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/genh.c b/Frameworks/vgmstream/vgmstream/src/meta/genh.c index 3f7cefd8d..538491c2a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/genh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/genh.c @@ -1,123 +1,93 @@ -#include "../vgmstream.h" #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" #include "../util.h" -#ifdef VGM_USE_MPEG -#include -#endif + +/* known GENH 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 */ +} genh_type; /* GENH is an artificial "generic" header for headerless streams */ - VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - int32_t channel_count; - int32_t interleave; - int32_t sample_rate; - int32_t loop_start; - int32_t loop_end; - int32_t start_offset; - int32_t header_size; + int channel_count, loop_flag, sample_rate, interleave; + int32_t num_samples = 0, loop_start, loop_end, skip_samples = 0; + int32_t start_offset, header_size; + off_t datasize = 0; + int32_t coef[2]; int32_t coef_splitted[2]; int32_t dsp_interleave_type; int32_t coef_type; + int skip_samples_mode, atrac3_mode, xma_mode; + int i, j; - char filename[PATH_LIMIT]; - int coding; + coding_t coding; + genh_type type; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("genh",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"genh")) goto fail; /* check header magic */ if (read_32bitBE(0x0,streamFile) != 0x47454e48) goto fail; - /* check channel count (needed for ADP/DTK check) */ channel_count = read_32bitLE(0x4,streamFile); if (channel_count < 1) goto fail; - /* check format */ - /* 0 = PSX ADPCM */ - /* 1 = XBOX IMA ADPCM */ - /* 2 = NGC ADP/DTK ADPCM */ - /* 3 = 16bit big endian PCM */ - /* 4 = 16bit little endian PCM */ - /* 5 = 8bit PCM */ - /* 6 = SDX2 */ - /* 7 = DVI IMA */ - /* 8 = MPEG-1 Layer III, possibly also the MPEG-2 and 2.5 extensions */ - /* 9 = IMA */ - /* 10 = AICA ADPCM */ - /* 11 = MS ADPCM */ - /* 12 = NGC DSP */ - /* 13 = 8bit unsingned PCM */ - /* 14 = PSX ADPCM (bad flagged) */ - /* 15 = Microsoft IMA (MS ADPCM) */ - /* 16 = 8-bit PCM (unsigned) */ - /* 17 = Apple Quicktime 4-bit IMA ADPCM */ - - switch (read_32bitLE(0x18,streamFile)) { - case 0: - coding = coding_PSX; - break; - case 1: - coding = coding_XBOX; - break; - case 2: - coding = coding_NGC_DTK; - if (channel_count != 2) goto fail; - break; - case 3: - coding = coding_PCM16BE; - break; - case 4: - coding = coding_PCM16LE; - break; - case 5: - coding = coding_PCM8; - break; - case 6: - coding = coding_SDX2; - break; - case 7: - coding = coding_DVI_IMA; - break; + type = read_32bitLE(0x18,streamFile); + /* type to coding conversion */ + switch (type) { + 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 8: - /* we say MPEG-1 L3 here, but later find out exactly which */ - coding = coding_MPEG1_L3; - break; + 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 - case 9: - coding = coding_IMA; - break; - case 10: - coding = coding_AICA; - break; - case 11: - coding = coding_MSADPCM; - break; - case 12: - coding = coding_NGC_DSP; - break; - case 13: - coding = coding_PCM8_U_int; - break; - case 14: - coding = coding_PSX_badflags; - break; - case 15: - coding = coding_MS_IMA; - break; - case 16: - coding = coding_PCM8_U; - break; - case 17: - coding = coding_APPLE_IMA4; - break; default: goto fail; } @@ -147,26 +117,34 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { /* bit 0 - split coefs (2 arrays) */ /* bit 1 - little endian coefs */ coef_type = read_32bitLE(0x30,streamFile); - /* when using split coefficients, 2nd array is at: */ coef_splitted[0] = read_32bitLE(0x34,streamFile); coef_splitted[1] = read_32bitLE(0x38,streamFile); - //if (coding == coding_XBOX && channel_count != 2) goto fail; + /* other fields */ + num_samples = read_32bitLE(0x40,streamFile); + skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */ + skip_samples_mode = read_8bit(0x48,streamFile); /* 0=autodetect, 1=force manual value @ 0x44 */ + atrac3_mode = read_8bit(0x49,streamFile); /* 0=autodetect, 1=force joint stereo, 2=force full stereo */ + xma_mode = read_8bit(0x4a,streamFile); /* 0=default (4ch = 2ch + 2ch), 1=single (4ch = 1ch + 1ch + 1ch + 1ch) */ + datasize = read_32bitLE(0x50,streamFile); + if (!datasize) + datasize = get_streamfile_size(streamFile)-start_offset; + + num_samples = num_samples > 0 ? num_samples : loop_end; + loop_flag = loop_start != -1; + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,(loop_start!=-1)); + vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital information */ - - vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate; - vgmstream->num_samples = loop_end; + vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start; vgmstream->loop_end_sample = loop_end; - vgmstream->loop_flag = (loop_start != -1); + /* codec specific */ switch (coding) { case coding_PCM8_U_int: vgmstream->layout_type=layout_none; @@ -181,7 +159,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { case coding_DVI_IMA: case coding_IMA: case coding_AICA: - case coding_APPLE_IMA4: + case coding_APPLE_IMA4: vgmstream->interleave_block_size = interleave; if (channel_count > 1) { @@ -189,6 +167,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { coding = coding_SDX2_int; vgmstream->coding_type = coding_SDX2_int; } + //todo if 0 do this too (most codecs seem to enter an infinite loop otherwise) if(vgmstream->interleave_block_size==0xffffffff) vgmstream->layout_type=layout_none; else { @@ -201,6 +180,15 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { } else { vgmstream->layout_type = layout_none; } + + /* setup adpcm */ + if (coding == coding_AICA) { + int i; + for (i=0;ich[i].adpcm_step_index = 0x7f; + } + } + break; case coding_MS_IMA: vgmstream->interleave_block_size = interleave; @@ -215,6 +203,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; break; case coding_NGC_DTK: + if (channel_count != 2) goto fail; vgmstream->layout_type = layout_none; break; case coding_NGC_DSP: @@ -227,134 +216,115 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) { } else if (dsp_interleave_type == 2) { vgmstream->layout_type = layout_none; } + + /* get coefs */ + for (i=0;ich[i].adpcm_coef[j] = read_16bit(coef[i]+j*2,streamFile); + } + } else { + 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, start_offset, &coding, vgmstream->channels); + if (!vgmstream->codec_data) goto fail; + break; #endif - } - - vgmstream->coding_type = coding; - vgmstream->meta_type = meta_GENH; - - /* open the file for reading by each channel */ - { - int i; - int j; +#ifdef VGM_USE_FFMPEG + case coding_FFmpeg: { + ffmpeg_codec_data *ffmpeg_data = NULL; - STREAMFILE * chstreamfile = NULL; + if (type == FFMPEG) { + /* default FFmpeg */ + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,datasize); + if ( !ffmpeg_data ) goto fail; + } + else { + /* fake header FFmpeg */ + uint8_t buf[200]; + int32_t bytes; - for (i=0;ich[i].adpcm_step_index = 0x7f; + if (type == ATRAC3) { + int block_size = interleave; + int joint_stereo; + switch(atrac3_mode) { + case 0: joint_stereo = vgmstream->channels > 1 && 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; } - if (vgmstream->layout_type == layout_interleave) { - if (interleave >= 512) { - chstreamfile = - streamFile->open(streamFile,filename,interleave); - } else { - if (!chstreamfile) - chstreamfile = - streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - } - chstart_offset = - start_offset+vgmstream->interleave_block_size*i; - } else { - chstreamfile = - streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - } - break; - case coding_XBOX: - case coding_MSADPCM: - case coding_MS_IMA: - /* xbox's "interleave" is a lie, all channels start at same - * offset */ - chstreamfile = - streamFile->open(streamFile,filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - break; - case coding_NGC_DTK: - if (!chstreamfile) - chstreamfile = - streamFile->open(streamFile,filename,32*0x400); - break; - case coding_NGC_DSP: - if (!chstreamfile) - chstreamfile = - streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - { - int16_t (*read_16bit)(off_t , STREAMFILE*); - if ((coef_type & 2) == 0) { - read_16bit = read_16bitBE; - } else { - read_16bit = read_16bitLE; - } + bytes = ffmpeg_make_riff_atrac3(buf, 200, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, skip_samples); + } + else if (type == ATRAC3PLUS) { + int block_size = interleave; - if ((coef_type & 1) == 0) { - for (j=0;j<16;j++) { - vgmstream->ch[i].adpcm_coef[j] = read_16bit(coef[i]+j*2,streamFile); - } - } else { - 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); - } - } - } - chstart_offset =start_offset+vgmstream->interleave_block_size*i; - break; + bytes = ffmpeg_make_riff_atrac3plus(buf, 200, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, block_size, skip_samples); + } + else if (type == XMA1) { + int xma_stream_mode = xma_mode == 1 ? 1 : 0; -#ifdef VGM_USE_MPEG - case coding_MPEG1_L3: - if (!chstreamfile) - chstreamfile = - streamFile->open(streamFile,filename,MPEG_BUFFER_SIZE); - break; -#endif + bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, datasize, vgmstream->channels, vgmstream->sample_rate, xma_stream_mode); + } + else if (type == XMA2) { + int block_size = interleave ? interleave : 2048; + int block_count = datasize / block_size; + + bytes = ffmpeg_make_riff_xma2(buf, 200, vgmstream->num_samples, datasize, 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, start_offset,datasize); + if ( !ffmpeg_data ) goto fail; } - if (!chstreamfile) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->layout_type = layout_none; - vgmstream->ch[i].streamfile = chstreamfile; + /* force encoder delay */ + if (skip_samples_mode) { + ffmpeg_set_skip_samples(ffmpeg_data, skip_samples); + } - vgmstream->ch[i].channel_start_offset= - vgmstream->ch[i].offset=chstart_offset; + break; } +#endif + default: + break; } -#ifdef VGM_USE_MPEG - if (coding == coding_MPEG1_L3) { - vgmstream->codec_data = init_mpeg_codec_data(vgmstream->ch[0].streamfile, start_offset, vgmstream->sample_rate, vgmstream->channels, &(vgmstream->coding_type), NULL, NULL); - if (!vgmstream->codec_data) goto fail; - } -#endif + vgmstream->coding_type = coding; + vgmstream->meta_type = meta_GENH; + + + 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/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 93232b56a..8a16e7b41 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -2,13 +2,17 @@ #include "meta.h" #include "../util.h" -VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); +static VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { + + /* check extension, case insensitive */ + if ( !check_extensions(streamFile, "hca")) return NULL; + return init_vgmstream_hca_offset( streamFile, 0, streamFile->get_size(streamFile) ); } -VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { +static VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { unsigned int ciphKey1; unsigned int ciphKey2; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mca.c b/Frameworks/vgmstream/vgmstream/src/meta/mca.c index 42ec643cb..c74025d4d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mca.c @@ -1,27 +1,19 @@ -/* -Capcom MADP format found in Capcom 3DS games. -*/ - #include "meta.h" #include "../util.h" +#include "../coding/coding.h" +/* Capcom MADP - found in Capcom 3DS games */ VGMSTREAM * init_vgmstream_mca(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int channel_count; - int loop_flag; - int version; + int channel_count, loop_flag, version; size_t head_size, data_size, file_size; off_t start_offset, coef_offset, coef_start, coef_shift; - int i, j; int coef_spacing; /* check extension, case insensitive */ - streamFile->get_name(streamFile, filename, sizeof(filename)); - if (strcasecmp("mca", filename_extension(filename))) + if (!check_extensions(streamFile,"mca")) goto fail; - /* check header */ if ((uint32_t)read_32bitBE(0, streamFile) != 0x4D414450) /* "MADP" */ goto fail; @@ -41,10 +33,7 @@ VGMSTREAM * init_vgmstream_mca(STREAMFILE *streamFile) { vgmstream->loop_end_sample = read_32bitLE(0x18, streamFile); vgmstream->coding_type = coding_NGC_DSP; - if (channel_count == 1) - vgmstream->layout_type = layout_none; - else - vgmstream->layout_type = layout_interleave; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; vgmstream->meta_type = meta_MCA; @@ -78,7 +67,7 @@ VGMSTREAM * init_vgmstream_mca(STREAMFILE *streamFile) { coef_offset = coef_start + coef_shift * 0x14; } - /* sanity check */ + /* sanity check (for bad rips with the header manually truncated to in attempt to "fix" v5 headers) */ file_size = get_streamfile_size(streamFile); if (start_offset + data_size > file_size) { @@ -88,40 +77,16 @@ VGMSTREAM * init_vgmstream_mca(STREAMFILE *streamFile) { start_offset = file_size - data_size; } - - /* set up ADPCM coefs */ - for (j = 0; jchannels; j++) { - for (i = 0; i<16; i++) { - vgmstream->ch[j].adpcm_coef[i] = read_16bitLE(coef_offset + j*coef_spacing + i * 2, streamFile); - } - } - + /* set up ADPCM coefs */ + dsp_read_coefs_le(vgmstream, streamFile, coef_offset, coef_spacing); - /* open the file for reading by each channel */ - { - for (i = 0; ilayout_type == layout_interleave_shortblock) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - vgmstream->interleave_block_size); - else if (vgmstream->layout_type == layout_interleave) - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - STREAMFILE_DEFAULT_BUFFER_SIZE); - else - vgmstream->ch[i].streamfile = streamFile->open(streamFile, filename, - 0x1000); - - if (!vgmstream->ch[i].streamfile) goto fail; - - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + i*vgmstream->interleave_block_size; - } - } + /* 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/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index c9f25671c..7d5ef2a2b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -117,13 +117,11 @@ VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile); -VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); - #ifdef VGM_USE_FFMPEG ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size); -void free_ffmpeg(ffmpeg_codec_data *); +void free_ffmpeg(ffmpeg_codec_data *data); VGMSTREAM * init_vgmstream_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); @@ -194,8 +192,6 @@ VGMSTREAM * init_vgmstream_rwx(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xwb(STREAMFILE * streamFile); -VGMSTREAM * init_vgmstream_xwb2(STREAMFILE * streamFile); - VGMSTREAM * init_vgmstream_xa30(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_musc(STREAMFILE * streamFile); @@ -612,7 +608,8 @@ VGMSTREAM * init_vgmstream_hyperscan_kvag(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ios_psnd(STREAMFILE* streamFile); -VGMSTREAM * init_vgmstream_bos_adp(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_pc_adp_bos(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile); @@ -661,10 +658,18 @@ VGMSTREAM * init_vgmstream_ps2_svag_snk(STREAMFILE* streamFile); #ifdef VGM_USE_FFMPEG VGMSTREAM * init_vgmstream_xma(STREAMFILE* streamFile); + +VGMSTREAM * init_vgmstream_bik(STREAMFILE* streamFile); #endif VGMSTREAM * init_vgmstream_ps2_vds_vdm(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_x360_cxs(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_dsp_adx(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile); + +VGMSTREAM * init_vgmstream_akb2_multi(STREAMFILE *streamFile); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c index 3ea6b8c53..2bbf3ca2d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c @@ -165,69 +165,92 @@ fail: #ifdef VGM_USE_FFMPEG +static int find_atom_be(STREAMFILE *streamFile, uint32_t atom_id, off_t start_offset, off_t *out_atom_offset, size_t *out_atom_size); + + VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset = 0; int loop_flag = 0; int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0; + size_t filesize; + off_t atom_offset; + size_t atom_size; + int is_ffdl = 0; ffmpeg_codec_data *ffmpeg_data = NULL; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if ( strcasecmp("mp4",filename_extension(filename)) - && strcasecmp("m4a",filename_extension(filename)) - && strcasecmp("m4v",filename_extension(filename)) - && strcasecmp("bin",filename_extension(filename)) ) /* Final Fantasy Dimensions iOS */ + /* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS) */ + if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin")) goto fail; + filesize = streamFile->get_size(streamFile); /* check header for Final Fantasy Dimensions */ - if (read_32bitBE(0x00,streamFile) == 0x4646444C) { /* "FFDL" (any kind of FFD file) */ + if (read_32bitBE(0x00,streamFile) == 0x4646444C) { /* "FFDL" (any kind of file) */ + is_ffdl = 1; if (read_32bitBE(0x04,streamFile) == 0x6D747873) { /* "mtxs" (bgm file) */ + /* this value is erratic so we'll use FFmpeg's num_samples + * (can be bigger = silence-padded, or smaller = cut; doesn't matter for looping though)*/ num_samples = read_32bitLE(0x08,streamFile); + /* loop samples are within num_samples, and don't have encoder delay (loop_start=0 starts from encoder_delay) */ loop_start_sample = read_32bitLE(0x0c,streamFile); loop_end_sample = read_32bitLE(0x10,streamFile); loop_flag = !(loop_start_sample==0 && loop_end_sample==num_samples); start_offset = 0x14; + + /* some FFDL have muxed streams ("FFDL" + "mtxs" data1 + mp4 data1 + "mtxs" data2 + mp4 data2 + etc) + * check if there is anything after the first mp4 data */ + if (!find_atom_be(streamFile, 0x6D646174, start_offset, &atom_offset, &atom_size)) goto fail; /* "mdat" */ + if (atom_offset-8 + atom_size < filesize && read_32bitBE(atom_offset-8 + atom_size,streamFile) == 0x6D747873) { /*"mtxs"*/ + VGM_LOG("FFDL: multiple streams found\n"); + filesize = atom_offset-8 + atom_size; /* clamp size, though FFmpeg will ignore the extra data anyway */ + } } else { - start_offset = 0x4; /* some SEs */ + start_offset = 0x4; /* some SEs contain "ftyp" after "FFDL" */ } - /* todo some FFDL have multi streams ("FFLD" + mtxsdata1 + mp4data1 + mtxsdata2 + mp4data2 + etc) */ } - /* check header */ - if ( read_32bitBE(start_offset+0x04,streamFile) != 0x66747970) /* size 0x00 + "ftyp" 0x04 */ + if ( read_32bitBE(start_offset+0x04,streamFile) != 0x66747970) /* atom size @0x00 + "ftyp" @0x04 */ goto fail; - ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile)); + ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, filesize); if ( !ffmpeg_data ) goto fail; + /* Tales of Hearts iOS has loop info in the first "free" atom */ + if (!is_ffdl && find_atom_be(streamFile, 0x66726565, start_offset, &atom_offset, &atom_size)) { /* "free" */ + if (read_32bitBE(atom_offset,streamFile) == 0x4F700002 + && (atom_size == 0x38 || atom_size == 0x40)) { /* make sure it's ToHr "free" */ + /* 0x00: id? 0x04/8: s_rate; 0x10: num_samples (without padding, same as FFmpeg's) */ + /* 0x14/18/1c: 0x238/250/278? 0x20: ? 0x24: start_pad */ + loop_flag = read_32bitBE(atom_offset+0x28,streamFile); + if (loop_flag) { /* atom ends if no loop flag */ + loop_start_sample = read_32bitBE(atom_offset+0x2c,streamFile); + loop_end_sample = read_32bitBE(atom_offset+0x30,streamFile); + } + } + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(ffmpeg_data->channels,loop_flag); if (!vgmstream) goto fail; - - vgmstream->num_samples = ffmpeg_data->totalSamples; /* todo FFD num_samples is different from this */ - vgmstream->sample_rate = ffmpeg_data->sampleRate; - vgmstream->channels = ffmpeg_data->channels; - if (loop_flag) { - vgmstream->loop_start_sample = loop_start_sample; - vgmstream->loop_end_sample = loop_end_sample; - } - + vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_FFmpeg; - vgmstream->codec_data = ffmpeg_data; + vgmstream->num_samples = ffmpeg_data->totalSamples; + vgmstream->sample_rate = ffmpeg_data->sampleRate; + vgmstream->channels = ffmpeg_data->channels; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; return vgmstream; fail: - /* clean up anything we may have opened */ if (ffmpeg_data) { free_ffmpeg(ffmpeg_data); if (vgmstream) vgmstream->codec_data = NULL; @@ -236,4 +259,35 @@ fail: return NULL; } +/** + * Almost the same as streamfile.c's find_chunk but for "atom" chunks, which have chunk_size first because Apple. + * + * returns 0 on failure + */ +static int find_atom_be(STREAMFILE *streamFile, uint32_t atom_id, off_t start_offset, off_t *out_atom_offset, size_t *out_atom_size) { + size_t filesize; + off_t current_atom = start_offset; + int full_atom_size = 1; + int size_big_endian = 1; + + filesize = get_streamfile_size(streamFile); + /* read chunks */ + while (current_atom < filesize) { + off_t chunk_size = size_big_endian ? + read_32bitBE(current_atom+0,streamFile) : + read_32bitLE(current_atom+0,streamFile); + uint32_t chunk_type = read_32bitBE(current_atom+4,streamFile); + + if (chunk_type == atom_id) { + if (out_atom_size) *out_atom_size = chunk_size; + if (out_atom_offset) *out_atom_offset = current_atom+8; + return 1; + } + + current_atom += full_atom_size ? chunk_size : 4+4+chunk_size; + } + + return 0; +} + #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_mpds.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_mpds.c index 373088b43..109e0346b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_mpds.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_mpds.c @@ -1,99 +1,74 @@ #include "meta.h" #include "../util.h" +#include "../coding/coding.h" -/* MPDS - found in Big Air Freestyle, Terminator 3 (no coeffs), etc */ +/* MPDS - found in Paradigm Entertainment GC games */ VGMSTREAM * init_vgmstream_ngc_dsp_mpds(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - int loop_flag = 0; - int channel_count; - int ch1_start=-1, ch2_start=-1; + off_t start_offset; + int loop_flag = 0, channel_count, short_mpds; + /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("dsp",filename_extension(filename))) goto fail; + /* .adp: Big Air Freestyle */ + /* .mds: Terminator 3 The Redemption, Mission Impossible: Operation Surma */ + if (!check_extensions(streamFile, "dsp,mds")) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x4D504453) /* "MPDS" */ goto fail; - /* Version byte ??? */ - if (read_32bitBE(0x04,streamFile) != 0x00010000) /* "0x10000" */ - goto fail; - /* compare sample count with body size */ - if (((read_32bitBE(0x08,streamFile)/7*8)) != (read_32bitBE(0x0C,streamFile))) - goto fail; - channel_count = read_32bitBE(0x14,streamFile); + short_mpds = read_32bitBE(0x04,streamFile) != 0x00010000 && read_32bitBE(0x0c,streamFile) == 0x00000002; /* version byte? */ - if (channel_count > 2) - { - goto fail; - } + channel_count = short_mpds ? + read_16bitBE(0x0a, streamFile) : + read_32bitBE(0x14, streamFile); + if (channel_count > 2) goto fail; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - ch1_start = 0x80; - } - else if (channel_count == 2) - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = read_32bitBE(0x18,streamFile); - ch1_start = 0x80; - ch2_start = 0x80 + vgmstream->interleave_block_size; - } - else - { - goto fail; - } - - vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + /* fill in the vital statistics */ vgmstream->coding_type = coding_NGC_DSP; - vgmstream->num_samples = read_32bitBE(0x08,streamFile); - if (loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = read_32bitBE(0x08,streamFile); - } - vgmstream->meta_type = meta_NGC_DSP_MPDS; - - { - int i; - for (i=0;i<16;i++) - vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(0x24+i*2,streamFile); - - if (channel_count == 2) - { - for (i=0;i<16;i++) - vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(0x4C+i*2,streamFile); - } + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; + + if (!short_mpds) { /* Big Air Freestyle */ + start_offset = 0x80; + vgmstream->num_samples = read_32bitBE(0x08,streamFile); + vgmstream->sample_rate = read_32bitBE(0x10,streamFile); + vgmstream->interleave_block_size = channel_count==1 ? 0 : read_32bitBE(0x18,streamFile); + + /* compare sample count with body size */ + if ((vgmstream->num_samples / 7 * 8) != (read_32bitBE(0x0C,streamFile))) goto fail; + + dsp_read_coefs_be(vgmstream,streamFile,0x24, 0x28); + } + else { /* Terminator 3 The Redemption, Mission Impossible: Operation Surma */ + start_offset = 0x20; + vgmstream->num_samples = read_32bitBE(0x04,streamFile); + vgmstream->sample_rate = (uint16_t)read_16bitBE(0x08,streamFile); + vgmstream->interleave_block_size = channel_count==1 ? 0 : 0x200; + +#if 0 //todo unknown coeffs, maybe depends on stuff @ 0x10? (but looks like some kind of size) + { + int i,ch; + for (ch=0; ch < vgmstream->channels; ch++) { + for (i=0; i < 16; i++) + vgmstream->ch[ch].adpcm_coef[i] = mpds_coefs[i]; + } + } +#endif } - /* open the file for reading */ - vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[0].streamfile) goto fail; - vgmstream->ch[0].channel_start_offset = - vgmstream->ch[0].offset=ch1_start; - if (channel_count == 2) - { - vgmstream->ch[1].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!vgmstream->ch[1].streamfile) goto fail; - vgmstream->ch[1].channel_start_offset = - vgmstream->ch[1].offset=ch2_start; - } + 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/ngc_dsp_std.c b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c index f90501f07..91deb09db 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ngc_dsp_std.c @@ -112,8 +112,13 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) { off_t loop_off; /* check loop predictor/scale */ loop_off = header.loop_start_offset/16*8; - if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) - goto fail; + if (header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,streamFile)) { + /* rarely won't match (ex ESPN 2002), not sure if header or calc problem, but doesn't seem to matter + * (there may be a "click" when looping, or loop values may be too big and loop disabled anyway) */ + VGM_LOG("DSP (std): bad loop_predictor\n"); + //header.loop_flag = 0; + //goto fail; + } } /* compare num_samples with nibble count */ @@ -611,7 +616,7 @@ VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile) { int channel_count; /* check extension, case insensitive */ - //if (check_extensions(streamFile,"idsp")) goto fail; + //if (check_extensions(streamFile,"idsp,nus3bank")) goto fail; /* check header magic */ if( read_32bitBE(0x0,streamFile) != 0x49445350 ) /* "IDSP" */ @@ -666,7 +671,6 @@ VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile) { off_t loop_off; loop_off = ch_headers[ch].loop_start_offset / 8 / channel_count * 8; loop_off = (loop_off / interleave * interleave * channel_count) + (loop_off%interleave); - VGM_LOG("loop_ps=%lx, loop_off=%lx\n", ch_headers[ch].loop_ps, loop_off); if (ch_headers[ch].loop_ps != (uint8_t)read_8bit(start_offset + loop_off + interleave*ch, streamFile)) goto fail; } #endif @@ -685,6 +689,10 @@ VGMSTREAM * init_vgmstream_3ds_idsp(STREAMFILE *streamFile) { /* TODO: adjust for interleave? */ vgmstream->loop_start_sample = dsp_nibbles_to_samples(ch_headers[0].loop_start_offset); vgmstream->loop_end_sample = dsp_nibbles_to_samples(ch_headers[0].loop_end_offset) + 1; + /* games will ignore loop_end and use num_samples if going over it + * only needed for user-created IDSPs, but it's possible loop_end_sample shouldn't add +1 above */ + if (vgmstream->loop_end_sample > vgmstream->num_samples) + vgmstream->loop_end_sample = vgmstream->num_samples; vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = channel_count > 1 ? layout_interleave : layout_none; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c index 43648559f..fe3a9ba16 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis_file.c @@ -392,6 +392,13 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch loop_flag=1; loop_end_found=1; } + else if (strstr(comment->user_comments[i],"LOOPDEFS=")== + comment->user_comments[i]) { + sscanf(strrchr(comment->user_comments[i],'=')+1,"%d,%d", + &loop_start,&loop_end); + loop_flag=1; + loop_end_found=1; + } else if (strstr(comment->user_comments[i],"COMMENT=loop(")== comment->user_comments[i]) { sscanf(strrchr(comment->user_comments[i],'(')+1,"%d,%d", diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c index db6949d61..3d2db19c5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c @@ -1,17 +1,14 @@ #include "meta.h" #include "../util.h" -/* ADP (from Balls of Steel) */ -VGMSTREAM * init_vgmstream_bos_adp(STREAMFILE *streamFile) { +/* ADP - from Balls of Steel */ +VGMSTREAM * init_vgmstream_pc_adp_bos(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset; int loop_flag = 0; int channel_count; - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("adp",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"adp")) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x41445021) /* "ADP!" */ @@ -28,36 +25,68 @@ VGMSTREAM * init_vgmstream_bos_adp(STREAMFILE *streamFile) { start_offset = 0x18; vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bitLE(0x0C,streamFile); - vgmstream->coding_type = coding_DVI_IMA; vgmstream->num_samples = read_32bitLE(0x04,streamFile); if (loop_flag) { vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile); vgmstream->loop_end_sample = vgmstream->num_samples; } + vgmstream->coding_type = coding_DVI_IMA; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_BOS_ADP; - /* open the file for reading */ - { - STREAMFILE * file; - file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!file) goto fail; - vgmstream->ch[0].streamfile = file; - - vgmstream->ch[0].channel_start_offset= - vgmstream->ch[0].offset=start_offset; - - // 0x10, 0x12 - both initial history? - //vgmstream->ch[0].adpcm_history1_32 = read_16bitLE(0x10,streamFile); - // 0x14 - initial step index? - //vgmstream->ch[0].adpcm_step_index = read_32bitLE(0x14,streamFile); - } + // 0x10, 0x12 - both initial history? + //vgmstream->ch[0].adpcm_history1_32 = read_16bitLE(0x10,streamFile); + // 0x14 - initial step index? + //vgmstream->ch[0].adpcm_step_index = read_32bitLE(0x14,streamFile); + 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; +} + +/* ADP - from Omikron: The Nomad Soul (PC/DC) */ +VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, datasize; + int loop_flag = 0, channel_count, stereo_flag; + + if (!check_extensions(streamFile,"adp")) goto fail; + + /* 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) + goto fail; + + stereo_flag = read_8bit(0x03, streamFile); + if (stereo_flag > 1 || stereo_flag < 0) goto fail; + channel_count = stereo_flag ? 2 : 1; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + start_offset = 0x10; + vgmstream->channels = channel_count; + vgmstream->sample_rate = 22050; + vgmstream->num_samples = channel_count== 1 ? datasize*2 : datasize; + + vgmstream->coding_type = coding_OTNS_IMA; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_OTNS_ADP; + + 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/ps2_msa.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c index 6ad75cecf..197726aa8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_msa.c @@ -4,15 +4,11 @@ /* MSA (from Psyvariar -Complete Edition-) */ VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag; - int channel_count; + off_t start_offset, datasize, filesize; + int loop_flag, channel_count; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("msa",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile, "msa")) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x00000000) @@ -21,40 +17,36 @@ VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE *streamFile) { loop_flag = 0; channel_count = 2; - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ start_offset = 0x14; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; + datasize = read_32bitLE(0x4,streamFile); + filesize = get_streamfile_size(streamFile); + vgmstream->channels = channel_count; + vgmstream->sample_rate = read_32bitLE(0x10,streamFile); + vgmstream->num_samples = datasize*28/(16*channel_count); vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = read_32bitLE(0x4,streamFile)*28/32; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x4000; vgmstream->meta_type = meta_PS2_MSA; - /* 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; - - } + /* MSAs are strangely truncated, so manually calculate samples + * data after last usable block is always silence or garbage */ + if (datasize > filesize) { + off_t usable_size = filesize - start_offset; + usable_size -= usable_size % (vgmstream->interleave_block_size*channel_count);/* block-aligned */ + vgmstream->num_samples = usable_size * 28 / (16*channel_count); } + + /* 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/ps2_vag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c index 6f7b7eba1..94e633a63 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_vag.c @@ -20,7 +20,7 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) { int is_swag = 0; /* check extension, case insensitive */ - if ( !check_extensions(streamFile,"vag,swag") ) + if ( !check_extensions(streamFile,"vag,swag,str") ) goto fail; /* check VAG Header */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c index 169aab643..36882e0b6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_msf.c @@ -2,164 +2,149 @@ #include "../coding/coding.h" #include "../util.h" -/* MSF header */ +/* MSF - Sony's PS3 SDK format (MultiStream File) */ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; off_t start_offset, header_offset = 0; - int32_t data_size, loop_start, loop_end; - int loop_flag = 0; - int channel_count; - int codec_id; - -#ifdef VGM_USE_FFMPEG - ffmpeg_codec_data *ffmpeg_data = NULL; -#endif + uint32_t data_size, loop_start = 0, loop_end = 0; + uint32_t id, codec_id, flags; + int loop_flag = 0, channel_count; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("msf",filename_extension(filename))) goto fail; - + if (!check_extensions(streamFile,"msf,at3")) goto fail; /* .at3: Silent Hill HD Collection */ /* "WMSF" variation with a mini header over the MSFC header, same extension */ if (read_32bitBE(0x00,streamFile) == 0x574D5346) { header_offset = 0x10; } - start_offset = header_offset+0x40; + start_offset = header_offset+0x40; /* MSF header is always 0x40 */ - /* usually 0x4D534643 "MSFC" */ - if (read_8bit(header_offset+0x0,streamFile) != 0x4D) goto fail; /* M */ - if (read_8bit(header_offset+0x1,streamFile) != 0x53) goto fail; /* S */ - if (read_8bit(header_offset+0x2,streamFile) != 0x46) goto fail; /* F */ + /* check header "MSF" + version-char + * usually "MSF\0\1", "MSF\0\2", "MSF5"(\3\5), "MSFC"(\4\3) (latest/common version) */ + id = read_32bitBE(header_offset+0x00,streamFile); + if ((id & 0xffffff00) != 0x4D534600) goto fail; + codec_id = read_32bitBE(header_offset+0x04,streamFile); + channel_count = read_32bitBE(header_offset+0x08,streamFile); + data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header */ + if (data_size == 0xFFFFFFFF) /* unneeded? */ + data_size = get_streamfile_size(streamFile) - start_offset; - data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header*/ - if (data_size==0xFFFFFFFF) { - size_t fileLength = get_streamfile_size(streamFile); - data_size = fileLength - start_offset; - } + /* byte flags, not in MSFv1 or v2 + * 0x01/02/04/08: loop marker 0/1/2/3 (requires flag 0x10) + * 0x10: "resample" loop option (may be active with no 0x01 flag set) + * 0x20: VBR MP3 + * 0x40: joint stereo MP3 (apparently interleaved stereo for other formats) + * 0x80+: (none/reserved) */ + flags = read_32bitBE(header_offset+0x14,streamFile); + /* sometimes loop_start/end is set but not flag 0x01, but from tests it only loops with 0x01 */ + loop_flag = flags != 0xffffffff && (flags & 0x10) && (flags & 0x01); - /* block_align/loop_type? = read_32bitBE(header_offset+0x14,streamFile);*/ /* 00/40 when no loop, 11/50/51/71 */ - - loop_start = read_32bitBE(header_offset+0x18,streamFile); - loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */ - loop_flag = loop_start != 0xFFFFFFFF; + /* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */ if (loop_flag) { - if (loop_end==0xFFFFFFFF) {/* not seen */ + loop_start = read_32bitBE(header_offset+0x18,streamFile); + loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */ + loop_end = loop_start + loop_end; /* usually equals data_size but not always */ + if (loop_end > data_size)/* not seen */ loop_end = data_size; - } else { - loop_end = loop_start + loop_end; /* usually equals data_size but not always */ - if ( loop_end > data_size)/* not seen */ - loop_end = data_size; - } } - channel_count = read_32bitBE(header_offset+0x8,streamFile); - codec_id = read_32bitBE(header_offset+0x4,streamFile); - /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - /* fill in the vital statistics */ - vgmstream->channels = channel_count; - - /* Sample rate hack for strange files that don't have a specified frequency */ - if (read_32bitBE(header_offset+0x10,streamFile)==0x00000000) + /* Sample rate hack for strange MSFv1 files that don't have a specified frequency */ + vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile); + if (vgmstream->sample_rate == 0x00000000) /* PS ADPCM only? */ vgmstream->sample_rate = 48000; - else - vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile); - vgmstream->meta_type = meta_PS3_MSF; switch (codec_id) { - case 0x0: /* PCM (Big Endian) */ - { - vgmstream->coding_type = coding_PCM16BE; - vgmstream->num_samples = data_size/2/channel_count; - - if (loop_flag){ - vgmstream->loop_start_sample = loop_start/2/channel_count; - vgmstream->loop_end_sample = loop_end/2/channel_count; - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else if (channel_count > 1) - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 2; - } - } - break; - case 0x3: /* PSx ADPCM */ - { - vgmstream->coding_type = coding_PSX; - vgmstream->num_samples = data_size*28/16/channel_count; + case 0x0: /* PCM (Big Endian) */ + case 0x1: { /* PCM (Little Endian) */ + vgmstream->coding_type = codec_id==0 ? coding_PCM16BE : coding_PCM16LE; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; + vgmstream->interleave_block_size = 2; - if (loop_flag) - { - vgmstream->loop_start_sample = loop_start*28/16/channel_count; - vgmstream->loop_end_sample = loop_end*28/16/channel_count; - } - - if (channel_count == 1) - { - vgmstream->layout_type = layout_none; - } - else if (channel_count > 1) - { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - } + vgmstream->num_samples = data_size/2/channel_count; + if (loop_flag){ + vgmstream->loop_start_sample = loop_start/2/channel_count; + vgmstream->loop_end_sample = loop_end/2/channel_count; } + break; + } + + case 0x2: { /* PCM 32 (Float) */ + goto fail; //probably unused/spec only + } + + case 0x3: { /* PS ADPCM */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave; + vgmstream->interleave_block_size = 0x10; + + vgmstream->num_samples = data_size*28/16/channel_count; + if (loop_flag) { + vgmstream->loop_start_sample = loop_start*28/16/channel_count; + vgmstream->loop_end_sample = loop_end*28/16/channel_count; + } + + break; + } + #ifdef VGM_USE_FFMPEG - case 0x4: /* ATRAC3 (frame size 96) */ - case 0x5: /* ATRAC3 (frame size 152) */ - case 0x6: /* ATRAC3 (frame size 192) */ - /* delegate to FFMpeg, it can parse MSF files */ - ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); - if ( !ffmpeg_data ) goto fail; + case 0x4: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) */ + case 0x5: /* ATRAC3 mid (105 kbps, frame size 152) */ + case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */ + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[100]; + int32_t bytes, samples_size = 1024, block_size, encoder_delay, joint_stereo, max_samples; + block_size = (codec_id==4 ? 0x60 : (codec_id==5 ? 0x98 : 0xC0)) * vgmstream->channels; + encoder_delay = 0x0; //todo MSF encoder delay (around 440-450*2) + max_samples = (data_size / block_size) * samples_size; + joint_stereo = codec_id==4; /* interleaved joint stereo (ch must be even) */ + + if (vgmstream->sample_rate==0xFFFFFFFF) /* some MSFv1 (Digi World SP) */ + vgmstream->sample_rate = 44100;//voice tracks seems to use 44khz, not sure about other tracks + + /* make a fake riff so FFmpeg can parse the ATRAC3 */ + bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay); + if (bytes <= 0) goto fail; + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_FFmpeg; - vgmstream->codec_data = ffmpeg_data; - vgmstream->num_samples = ffmpeg_data->totalSamples; - - if (loop_flag && ffmpeg_data->blockAlign > 0) { - vgmstream->loop_start_sample = (loop_start / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize; - vgmstream->loop_end_sample = (loop_end / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize; + vgmstream->num_samples = max_samples; + if (loop_flag) { + vgmstream->loop_start_sample = (loop_start / block_size) * samples_size; + vgmstream->loop_end_sample = (loop_end / block_size) * samples_size; } break; + } #endif #ifdef VGM_USE_FFMPEG - case 0x7: /* MPEG */ + case 0x7: { /* MPEG (LAME MP3 of any quality) */ /* delegate to FFMpeg, it can parse MSF files */ - ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); + ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) ); if ( !ffmpeg_data ) goto fail; - + vgmstream->codec_data = ffmpeg_data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_FFmpeg; - vgmstream->codec_data = ffmpeg_data; - - /* TODO check CBR better (bitrate % X != 0?) */ - if (ffmpeg_data->bitrate == 0) - goto fail; /* vgmstream->num_samples = ffmpeg_data->totalSamples; */ /* duration may not be set/inaccurate */ vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; if (loop_flag) { + //todo properly apply encoder delay, which seems to vary between 1152 (1f), 528, 576 or 528+576 int frame_size = ffmpeg_data->frameSize; vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate; vgmstream->loop_start_sample -= vgmstream->loop_start_sample==frame_size ? frame_size @@ -170,67 +155,47 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) { } break; + } #endif #if defined(VGM_USE_MPEG) && !defined(VGM_USE_FFMPEG) - case 0x7: /* MPEG */ - { - int frame_size = 576; /* todo incorrect looping calcs, MP3 can have other sizes */ + case 0x7: { /* MPEG (LAME MP3 of any quality) */ + int frame_size = 576; /* todo incorrect looping calcs */ - mpeg_codec_data *mpeg_data = NULL; - struct mpg123_frameinfo mi; - coding_t ct; + mpeg_codec_data *mpeg_data = NULL; + coding_t ct; - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL); - if (!mpeg_data) goto fail; - vgmstream->codec_data = mpeg_data; + mpeg_data = init_mpeg_codec_data(streamFile, start_offset, &ct, vgmstream->channels); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; - if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail; - - vgmstream->coding_type = ct; - vgmstream->layout_type = layout_mpeg; - if (mi.vbr != MPG123_CBR) goto fail; - vgmstream->num_samples = mpeg_bytes_to_samples(data_size, &mi); - vgmstream->num_samples -= vgmstream->num_samples % frame_size; - if (loop_flag) { - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi); - vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size; - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi); - vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size; - } - vgmstream->interleave_block_size = 0; + vgmstream->coding_type = ct; + vgmstream->layout_type = layout_mpeg; + vgmstream->num_samples = mpeg_bytes_to_samples(data_size, mpeg_data); + vgmstream->num_samples -= vgmstream->num_samples % frame_size; + if (loop_flag) { + vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); + vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size; + vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); + vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size; } + vgmstream->interleave_block_size = 0; + break; + } #endif - default: + + default: /* 8+: not defined */ goto fail; } /* 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; - - } - } + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; return vgmstream; - /* clean up anything we may have opened */ fail: -#ifdef VGM_USE_FFMPEG - if (ffmpeg_data) { - free_ffmpeg(ffmpeg_data); - if (vgmstream) vgmstream->codec_data = NULL; - } -#endif - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_sgh_sgb.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_sgh_sgb.c index 164a77cb1..1204ca120 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_sgh_sgb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_sgh_sgb.c @@ -27,45 +27,34 @@ static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_ VGMSTREAM * init_vgmstream_ps3_sgdx(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamHeader = NULL; - char filename[PATH_LIMIT]; - off_t start_offset, data_offset; + off_t start_offset, data_offset, chunk_offset; + size_t data_size; - int i; - int is_sgx, is_sgd, is_sgb; + int is_sgx, is_sgb; + int loop_flag, channels, type; + int sample_rate, num_samples, loop_start_sample, loop_end_sample; - int chunk_offset; - int total_streams; - int target_stream = 0; /* usually only SE use substreams */ - -#ifdef VGM_USE_FFMPEG - ffmpeg_codec_data *ffmpeg_data = NULL; -#endif + int target_stream = 0, total_streams; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - is_sgx = strcasecmp("sgx",filename_extension(filename))==0; - is_sgd = strcasecmp("sgd",filename_extension(filename))==0; - is_sgb = strcasecmp("sgb",filename_extension(filename))==0; - if ( !(is_sgx || is_sgd || is_sgb) ) + if (!check_extensions(streamFile,"sgx,sgd,sgb")) goto fail; + is_sgx = check_extensions(streamFile,"sgx"); + is_sgb = check_extensions(streamFile,"sgb"); + //is_sgd = check_extensions(streamFile,"sgd"); /* SGB+SGH: use SGH as header; otherwise use the current file as header */ if (is_sgb) { - char fileheader[PATH_LIMIT]; - - strcpy(fileheader,filename); - strcpy(fileheader+strlen(fileheader)-3,"sgh"); - - streamHeader = streamFile->open(streamFile,fileheader,STREAMFILE_DEFAULT_BUFFER_SIZE); + streamHeader = open_stream_ext(streamFile, "sgh"); if (!streamHeader) goto fail; } else { streamHeader = streamFile; } - /* SGXD chunk (size 0x10) */ + /* SGXD base (size 0x10) */ if (read_32bitBE(0x00,streamHeader) != 0x53475844) /* "SGXD" */ goto fail; /* 0x04 SGX: full header_size; SGD/SGH: unknown header_size (counting from 0x0/0x8/0x10, varies) */ @@ -80,174 +69,132 @@ VGMSTREAM * init_vgmstream_ps3_sgdx(STREAMFILE *streamFile) { } - chunk_offset = 0x10; /* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */ - /* the format reads chunks until header_size, but we only want WAVE in the first position meaning BGM */ - if (read_32bitBE(chunk_offset+0x00,streamHeader) != 0x57415645) /* "WAVE" */ - goto fail; - /* 0x04 SGX: unknown; SGD/SGH: chunk length */ - /* 0x08 null */ + /* 0x04 SGX: unknown; SGD/SGH: chunk length, 0x08 null */ + if (is_sgx) { /* position after chunk+size */ + if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */ + chunk_offset = 0x18; + } else { + if (!find_chunk_le(streamHeader, 0x57415645,0x10,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */ + } - /* usually only SE containers have multiple streams but just in case... */ - total_streams = read_32bitLE(chunk_offset+0x0c,streamHeader); - if (target_stream+1 > total_streams) - goto fail; + /* check multi-streams (usually only SE containers; Puppeteer) */ + total_streams = read_32bitLE(chunk_offset+0x04,streamHeader); + if (target_stream == 0) target_stream = 1; + if (target_stream > total_streams) goto fail; - /* read stream (skip until target_stream) */ - chunk_offset += 0x10; + /* read stream header */ { - int stream_loop_flag; - int stream_type; - int stream_channels; - int32_t stream_sample_rate; - int32_t stream_num_samples, stream_loop_start_sample, stream_loop_end_sample; - off_t stream_start_offset; - int32_t stream_size; + off_t stream_offset; + chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/ - for (i=0; i < total_streams; i++) { - if (i != target_stream) { - chunk_offset += 0x38; /* next file */ - continue; + /* 0x00 ? (00/01/02) */ + /* 0x04 sometimes global offset to wave_name */ + type = read_8bit(chunk_offset+0x08,streamHeader); + channels = read_8bit(chunk_offset+0x09,streamHeader); + /* 0x0a null */ + sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); + + /* 0x10 info_type: meaning of the next value + * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ + /* 0x14 info_value (see above) */ + /* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ + /* 0x1c null */ + + num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); + loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); + loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); + data_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ + + if (is_sgx) { + stream_offset = 0x0; /* TODO unknown (not seen multi SGX) */ + } else{ + stream_offset = read_32bitLE(chunk_offset+0x30,streamHeader); + } + /* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */ + + loop_flag = loop_start_sample!=0xffffffff && loop_end_sample!=0xffffffff; + start_offset = data_offset + stream_offset; + } + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start_sample; + vgmstream->loop_end_sample = loop_end_sample; + vgmstream->num_streams = total_streams; + vgmstream->meta_type = meta_PS3_SGDX; + + switch (type) { + case 0x03: /* PSX ADPCM */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + if (is_sgx || is_sgb) { + vgmstream->interleave_block_size = 0x10; + } else { //todo this only seems to happen with SFX + vgmstream->interleave_block_size = data_size; } - /* 0x00 ? (00/01/02) */ - /* 0x04 sometimes global offset to wave_name */ - stream_type = read_8bit(chunk_offset+0x08,streamHeader); - stream_channels = read_8bit(chunk_offset+0x09,streamHeader); - /* 0x0a null */ - stream_sample_rate = read_32bitLE(chunk_offset+0x0c,streamHeader); + break; - /* 0x10 info_type: meaning of the next value - * (00=null, 30/40=data size without padding (ADPCM, ATRAC3plus), 80/A0=block size (AC3) */ - /* 0x14 info_value (see above) */ - /* 0x18 unknown (ex. 0x0008/0010/3307/CC02/etc)x2 */ - /* 0x1c null */ +#ifdef VGM_USE_FFMPEG + case 0x04: { /* ATRAC3plus */ + at3_riff_info info; + ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; - stream_num_samples = read_32bitLE(chunk_offset+0x20,streamHeader); - stream_loop_start_sample = read_32bitLE(chunk_offset+0x24,streamHeader); - stream_loop_end_sample = read_32bitLE(chunk_offset+0x28,streamHeader); - stream_size = read_32bitLE(chunk_offset+0x2c,streamHeader); /* stream size (without padding) / interleave (for type3) */ - - if (is_sgx) { - stream_start_offset = 0x0; /* TODO unknown (not seen multi SGX) */ - } else{ - stream_start_offset = read_32bitLE(chunk_offset+0x30,streamHeader); + /* manually fix looping due to FFmpeg bugs */ + if (loop_flag && get_at3_riff_info(&info, streamFile, start_offset)) { + if (vgmstream->num_samples == info.fact_samples) { /* use if looks normal */ + /* todo use "skip samples"; for now we just use absolute loop values */ + vgmstream->loop_start_sample = info.loop_start_sample; + vgmstream->loop_end_sample = info.loop_end_sample; + vgmstream->num_samples += info.skip_samples; /* to ensure it always reaches loop_end */ + } } - /* 0x34 SGX: unknown; SGD/SGH: stream size (with padding) / interleave */ - - stream_loop_flag = stream_loop_start_sample!=0xffffffff && stream_loop_end_sample!=0xffffffff; - chunk_offset += 0x38; /* next file */ break; } +#endif + case 0x05: /* Short VAG ADPCM */ + vgmstream->coding_type = coding_PSX_cfg; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x4; - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(stream_channels,stream_loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - vgmstream->num_samples = stream_num_samples; - vgmstream->sample_rate = stream_sample_rate; - vgmstream->channels = stream_channels; - if (stream_loop_flag) { - vgmstream->loop_start_sample = stream_loop_start_sample; - vgmstream->loop_end_sample = stream_loop_end_sample; - } - - vgmstream->meta_type = meta_PS3_SGDX; - - switch (stream_type) { - case 0x03: /* PSX ADPCM */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - if (is_sgx) { - vgmstream->interleave_block_size = 0x10; - } else { - vgmstream->interleave_block_size = stream_size; - } - - break; + break; #ifdef VGM_USE_FFMPEG - case 0x04: /* ATRAC3plus */ - { - at3_riff_info info; + case 0x06: { /* AC3 */ + ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, data_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; - ffmpeg_data = init_ffmpeg_offset(streamFile, data_offset, streamFile->get_size(streamFile)); - if ( !ffmpeg_data ) goto fail; - - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - /*vgmstream->meta_type = meta_FFmpeg;*/ - vgmstream->codec_data = ffmpeg_data; - - /* manually fix looping due to FFmpeg bugs */ - if (stream_loop_flag && get_at3_riff_info(&info, streamFile, data_offset)) { - if (vgmstream->num_samples == info.fact_samples) { /* use if looks normal */ - /* todo use "skip samples"; for now we just use absolute loop values */ - vgmstream->loop_start_sample = info.loop_start_sample; - vgmstream->loop_end_sample = info.loop_end_sample; - vgmstream->num_samples += info.skip_samples; /* to ensure it always reaches loop_end */ - } - } - - break; - } -#endif - case 0x05: /* Short VAG ADPCM */ - vgmstream->coding_type = coding_PSX_cfg; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x4; - - break; - -#ifdef VGM_USE_FFMPEG - case 0x06: /* AC3 */ - ffmpeg_data = init_ffmpeg_offset(streamFile, data_offset, streamFile->get_size(streamFile)); - if ( !ffmpeg_data ) goto fail; - - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - /*vgmstream->meta_type = meta_FFmpeg;*/ - vgmstream->codec_data = ffmpeg_data; - - break; + break; + } #endif - default: - goto fail; - } - - - start_offset = data_offset + stream_start_offset; - /* 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; i < vgmstream->channels; i++) { - vgmstream->ch[i].streamfile = file; - vgmstream->ch[i].channel_start_offset = - vgmstream->ch[i].offset = - start_offset + vgmstream->interleave_block_size * i; - } - } + default: + goto fail; } + /* open the file for reading */ + if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + goto fail; + if (is_sgb && streamHeader) close_streamfile(streamHeader); return vgmstream; fail: -#ifdef VGM_USE_FFMPEG - if (ffmpeg_data) { - free_ffmpeg(ffmpeg_data); - vgmstream->codec_data = NULL; - } -#endif if (is_sgb && streamHeader) close_streamfile(streamHeader); - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } @@ -260,8 +207,8 @@ fail: * FFmpeg doesn't support/export this, so we have to manually get the absolute values to fix looping. */ static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_t offset) { - off_t current_chunk, riff_size; - int data_found = 0; + off_t chunk_offset; + size_t chunk_size; memset(info, 0, sizeof(at3_riff_info)); @@ -269,51 +216,25 @@ static int get_at3_riff_info(at3_riff_info* info, STREAMFILE *streamFile, int32_ && read_32bitBE(offset+0x8,streamFile)!=0x57415645 ) /* "WAVE" */ goto fail; - - /* read chunks */ - riff_size = read_32bitLE(offset+0x4,streamFile); - current_chunk = offset + 0xc; - - while (!data_found && current_chunk < riff_size) { - uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); - off_t chunk_size = read_32bitLE(current_chunk+4,streamFile); - - switch(chunk_type) { - case 0x736D706C: /* smpl */ - if (read_32bitLE(current_chunk+0x24, streamFile)==0 - || read_32bitLE(current_chunk+0x2c+0x4, streamFile)!=0 ) - goto fail; - - info->loop_start_sample = read_32bitLE(current_chunk+0x2c+0x8, streamFile); - info->loop_end_sample = read_32bitLE(current_chunk+0x2c+0xc,streamFile); - break; - - case 0x66616374: /* fact */ - if (chunk_size == 0x8) { - info->fact_samples = read_32bitLE(current_chunk+0x8, streamFile); - info->skip_samples = read_32bitLE(current_chunk+0xc, streamFile); - } else if (chunk_size == 0xc) { - info->fact_samples = read_32bitLE(current_chunk+0x8, streamFile); - info->skip_samples = read_32bitLE(current_chunk+0x10, streamFile); - } else { - goto fail; - } - break; - - case 0x64617461: /* data */ - data_found = 1; - break; - - default: - break; - } - - current_chunk += 8+chunk_size; - } - - if (!data_found) + /*"smpl"*/ + if (!find_chunk_le(streamFile, 0x736D706C,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail; + if (read_32bitLE(chunk_offset+0x1C, streamFile)==0 + || read_32bitLE(chunk_offset+0x24+0x4, streamFile)!=0 ) goto fail; + info->loop_start_sample = read_32bitLE(chunk_offset+0x1C+0x8+0x8, streamFile); + info->loop_end_sample = read_32bitLE(chunk_offset+0x1C+0x8+0xc,streamFile); + /*"fact"*/ + if (!find_chunk_le(streamFile, 0x66616374,offset+0xc,0, &chunk_offset,&chunk_size)) goto fail; + if (chunk_size == 0x8) { + info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile); + info->skip_samples = read_32bitLE(chunk_offset+0x4, streamFile); + } else if (chunk_size == 0xc) { + info->fact_samples = read_32bitLE(chunk_offset+0x0, streamFile); + info->skip_samples = read_32bitLE(chunk_offset+0x8, streamFile); + } else { + goto fail; + } /* found */ return 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c b/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c index f4c0fc47a..da257c6f7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps3_xvag.c @@ -2,229 +2,199 @@ #include "../coding/coding.h" #include "../util.h" -/* XVAG (from Ratchet & Clank Future: Quest for Booty) */ -/* v0.1 : bxaimc : Initial release */ -/* v0.2 : Fastelbja : add support for loops points */ -/* + little endian header values */ +static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end); +/* XVAG - Sony's (second party?) format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */ VGMSTREAM * init_vgmstream_ps3_xvag(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - uint8_t testBuffer[0x10]; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int loop_flag = 0, channel_count, codec; - off_t start_offset; - off_t fileLength; - - int loop_flag = 0; - int channel_count; - off_t readOffset = 0; - int little_endian = 0; - long sample_rate = 0; - long num_samples = 0; - -#ifdef VGM_USE_MPEG - mpeg_codec_data *mpeg_data = NULL; - coding_t mpeg_coding_type = coding_MPEG1_L3; -#endif - - int loopStartPointsCount=0; - int loopEndPointsCount=0; - uint16_t mp3ID; - off_t loopStartPoints[0x10]; - off_t loopEndPoints[0x10]; - - off_t loopStart = 0; - off_t loopEnd = 0; + off_t start_offset, loop_start, loop_end, chunk_offset; + off_t first_offset = 0x20; + int little_endian; + int sample_rate, num_samples, multiplier; /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("xvag",filename_extension(filename))) goto fail; + if (!check_extensions(streamFile,"xvag")) goto fail; /* check header */ if (read_32bitBE(0x00,streamFile) != 0x58564147) /* "XVAG" */ goto fail; - fileLength = get_streamfile_size(streamFile); - - if (read_8bit(0x07,streamFile)==0) - little_endian=1; - - if(little_endian) - { - channel_count = read_32bitLE(0x28,streamFile); - start_offset = read_32bitLE(0x4,streamFile); - sample_rate = read_32bitLE(0x3c,streamFile); - num_samples = read_32bitLE(0x30,streamFile); - } - else - { - channel_count = read_32bitBE(0x28,streamFile); - start_offset = read_32bitBE(0x4,streamFile); - sample_rate = read_32bitBE(0x3c,streamFile); - num_samples = read_32bitBE(0x30,streamFile); - } - - readOffset=start_offset; - - // MP3s ? - mp3ID=(uint16_t)read_16bitBE(start_offset,streamFile); - if(mp3ID==0xFFFB) { -#ifdef VGM_USE_MPEG - int rate; - int channels; - - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, -1, -1, &mpeg_coding_type, &rate, &channels); // -1 to not check sample rate or channels - if (!mpeg_data) goto fail; - - channel_count = channels; - sample_rate = rate; - -#else - // reject if no MPEG support - goto fail; -#endif + little_endian = read_8bit(0x07,streamFile)==0; /* empty start_offset > little endian */ + if (little_endian) { + read_32bit = read_32bitLE; } else { - // get the loops the same way we get on .MIB - do { - readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); + read_32bit = read_32bitBE; + } - // Loop Start ... - if(testBuffer[0x01]==0x06) - { - if(loopStartPointsCount<0x10) - { - loopStartPoints[loopStartPointsCount] = readOffset-0x10; - loopStartPointsCount++; - } - } + start_offset = read_32bit(0x04,streamFile); + /* 0x08: flags? (&0x01=big endian?) 0x0a: version (chunk sizes vary) */ - // Loop End ... - if(((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) || - (testBuffer[0x01]==0x01)) - { - if(loopEndPointsCount<0x10) - { - loopEndPoints[loopEndPointsCount] = readOffset; //-0x10; - loopEndPointsCount++; - } + /* "fmat": base format */ + if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,NULL, !little_endian)) goto fail; /*"fmat"*/ + channel_count = read_32bit(chunk_offset+0x00,streamFile); + codec = read_32bit(chunk_offset+0x04,streamFile); + num_samples = read_32bit(chunk_offset+0x08,streamFile); + /* 0x0c: samples again? */ + multiplier = read_32bit(chunk_offset+0x10,streamFile); + sample_rate = read_32bit(chunk_offset+0x14,streamFile); + /* 0x18: datasize */ - } + /* other chunks: */ + /* "cpan": pan/volume per channel */ + /* "0000": end chunk before start_offset */ - } while (readOffset<((int32_t)fileLength)); + //if ((uint16_t)read_16bitBE(start_offset,streamFile)==0xFFFB) codec = 0x08; + if (codec == 0x06) { /* todo not sure if there are any looping XVAGs */ + loop_flag = ps_adpcm_find_loop_offsets(streamFile, channel_count, start_offset, &loop_start, &loop_end); + } - // Calc Loop Points & Interleave ... - if(loopStartPointsCount>=channel_count) - { - // can't get more then 0x10 loop point ! - if((loopStartPointsCount<=0x0F) && (loopStartPointsCount>=2)) - { - // Always took the first 2 loop points - loopStart=loopStartPoints[1]-start_offset; - loop_flag=1; - } else - loopStart=0; - } - - if(loopEndPointsCount>=channel_count) - { - // can't get more then 0x10 loop point ! - if((loopEndPointsCount<=0x0F) && (loopEndPointsCount>=2)) { - loop_flag=1; - loopEnd=loopEndPoints[loopEndPointsCount-1]-start_offset; - } else { - loopEnd=0; - } - } - - // as i can get on the header if a song is looped or not - // if try to take the loop marker on the file - // if the last byte of the file is = 00 is assume that the song is not looped - // i know that i can cover 95% of the file, but can't work on some of them - if(read_8bit((fileLength-1),streamFile)==0) - loop_flag=0; - } - - /* build the VGMSTREAM */ + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; - vgmstream->channels = channel_count; vgmstream->meta_type = meta_PS3_XVAG; - - if(mp3ID==0xFFFB) { + + switch (codec) { + case 0x06: /* PS ADPCM: God of War III, Uncharted 1/2, Ratchet and Clank Future */ + case 0x07: { /* Bizarro 6ch PS ADPCM: infamous 1 (todo won't play properly; algo tweak + bigger predictor table?) */ + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x10;//* multiplier? (doesn't seem necessary, always 1); + vgmstream->coding_type = coding_PSX; + + if (loop_flag) { + if (loop_start!=0) { + vgmstream->loop_start_sample = ((((loop_start/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count; + if(loop_start%vgmstream->interleave_block_size) + vgmstream->loop_start_sample += (((loop_start%vgmstream->interleave_block_size)-1)/16*14*channel_count); + } + vgmstream->loop_end_sample = ((((loop_end/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count; + if (loop_end%vgmstream->interleave_block_size) + vgmstream->loop_end_sample += (((loop_end%vgmstream->interleave_block_size)-1)/16*14*channel_count); + } + + break; + } + #ifdef VGM_USE_MPEG - /* NOTE: num_samples seems to be quite wrong for MPEG */ - vgmstream->codec_data = mpeg_data; - vgmstream->layout_type = layout_mpeg; - vgmstream->coding_type = mpeg_coding_type; -#else - // reject if no MPEG support - goto fail; -#endif - } - else { - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x10; - vgmstream->coding_type = coding_PSX; - } + case 0x08: { /* MPEG: The Last of Us, Uncharted 3, Medieval Moves */ + mpeg_codec_data *mpeg_data = NULL; + coding_t mpeg_coding_type; + int fixed_frame_size; - if (loop_flag) { - if(loopStart!=0) { - vgmstream->loop_start_sample = ((((loopStart/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count; - if(loopStart%vgmstream->interleave_block_size) { - vgmstream->loop_start_sample += (((loopStart%vgmstream->interleave_block_size)-1)/16*14*channel_count); - } - } - vgmstream->loop_end_sample = ((((loopEnd/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count; - if(loopEnd%vgmstream->interleave_block_size) { - vgmstream->loop_end_sample += (((loopEnd%vgmstream->interleave_block_size)-1)/16*14*channel_count); - } - } + /* "mpin": mpeg info */ + /* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */ + if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian)) goto fail; /*"mpin"*/ + fixed_frame_size = read_32bit(chunk_offset+0x1c,streamFile); - /* open the file for reading */ - { - int i; - STREAMFILE * file; - if(vgmstream->layout_type == layout_interleave) { - 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; - - } - } -#ifdef VGM_USE_MPEG - else if(vgmstream->layout_type == layout_mpeg) { - for (i=0;ich[i].streamfile = streamFile->open(streamFile,filename,MPEG_BUFFER_SIZE); - vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset=start_offset; - } + mpeg_data = init_mpeg_codec_data_interleaved(streamFile, start_offset, &mpeg_coding_type, vgmstream->channels, fixed_frame_size, 0); + if (!mpeg_data) goto fail; + vgmstream->codec_data = mpeg_data; + vgmstream->layout_type = layout_mpeg; + vgmstream->coding_type = mpeg_coding_type; + vgmstream->interleave_block_size = fixed_frame_size * multiplier; + break; } #endif - else { goto fail; } + + case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus */ + /* "a9in": ATRAC9 info */ + goto fail; + } + + default: + goto fail; } + + /* 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: -#ifdef VGM_USE_MPEG - if (mpeg_data) { - mpg123_delete(mpeg_data->m); - free(mpeg_data); - - if (vgmstream) { - vgmstream->codec_data = NULL; - } - } -#endif if (vgmstream) close_vgmstream(vgmstream); return NULL; } + + +static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end) { + uint8_t testBuffer[0x10]; + int loopStartPointsCount=0; + int loopEndPointsCount=0; + off_t readOffset = 0; + off_t loopStartPoints[0x10]; + off_t loopEndPoints[0x10]; + + off_t loopStart = 0; + off_t loopEnd = 0; + off_t fileLength; + int loop_flag = 0; + + readOffset=start_offset; + fileLength = get_streamfile_size(streamFile); + + // get the loops the same way we get on .MIB + do { + readOffset+=(off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile); + + // Loop Start ... + if(testBuffer[0x01]==0x06) { + if(loopStartPointsCount<0x10) { + loopStartPoints[loopStartPointsCount] = readOffset-0x10; + loopStartPointsCount++; + } + } + + // Loop End ... + if(((testBuffer[0x01]==0x03) && (testBuffer[0x03]!=0x77)) || (testBuffer[0x01]==0x01)) { + if(loopEndPointsCount<0x10) { + loopEndPoints[loopEndPointsCount] = readOffset; //-0x10; + loopEndPointsCount++; + } + } + + } while (readOffset<((int32_t)fileLength)); + + // Calc Loop Points & Interleave ... + if(loopStartPointsCount>=channel_count) { + // can't get more then 0x10 loop point ! + if((loopStartPointsCount<=0x0F) && (loopStartPointsCount>=2)) { + // Always took the first 2 loop points + loopStart=loopStartPoints[1]-start_offset; + loop_flag=1; + } else { + loopStart=0; + } + } + + if(loopEndPointsCount>=channel_count) { + // can't get more then 0x10 loop point ! + if((loopEndPointsCount<=0x0F) && (loopEndPointsCount>=2)) { + loop_flag=1; + loopEnd=loopEndPoints[loopEndPointsCount-1]-start_offset; + } else { + loopEnd=0; + } + } + + // as i can get on the header if a song is looped or not + // if try to take the loop marker on the file + // if the last byte of the file is = 00 is assume that the song is not looped + // i know that i can cover 95% of the file, but can't work on some of them + if(read_8bit((fileLength-1),streamFile)==0) + loop_flag=0; + + if (loop_flag) { + *loop_start = loopStart; + *loop_end = loopEnd; + } + + return loop_flag; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c index 08b89c3a0..a4c436e56 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sqex_scd.c @@ -54,17 +54,16 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { int32_t loop_start, loop_end; int target_stream = 1; /* N=Nth stream, 0=auto (first) */ - int loop_flag = 0; - int channel_count; - int codec_id; + int loop_flag = 0, channel_count, codec_id; int aux_chunk_count; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; /* check extension, case insensitive */ + if ( !check_extensions(streamFile, "scd") ) goto fail; + streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("scd",filename_extension(filename))) goto fail; /* SEDB */ if (read_32bitBE(0,streamFile) != 0x53454442) goto fail; @@ -109,9 +108,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* 0x18: unknown offset */ /* 0x1c: unknown (0x0) */ headers_entries = read_16bit(tables_offset+0x04,streamFile); - VGM_ASSERT(headers_entries > 1, "SCD: multiple streams found (%i entries)\n", headers_entries); if (target_stream == 0) target_stream = 1; /* auto: default to 1 */ - if (target_stream > headers_entries) goto fail; + if (headers_entries <= 0 || target_stream > headers_entries) goto fail; headers_offset = read_32bit(tables_offset+0x0c,streamFile); /** header table entries (each is an uint32_t offset to stream header) **/ @@ -220,6 +218,8 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* fill in the vital statistics */ vgmstream->channels = channel_count; vgmstream->sample_rate = read_32bit(meta_offset+8,streamFile); + vgmstream->num_streams = headers_entries; + vgmstream->meta_type = meta_SQEX_SCD; switch (codec_id) { case 0x1: @@ -238,7 +238,6 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { /* MPEG */ { mpeg_codec_data *mpeg_data = NULL; - struct mpg123_frameinfo mi; coding_t ct; /* Drakengard 3, some Kingdom Hearts */ @@ -247,21 +246,18 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { if (vgmstream->sample_rate == 44099) vgmstream->sample_rate = 44100; - mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL); + mpeg_data = init_mpeg_codec_data(streamFile, start_offset, &ct, vgmstream->channels); if (!mpeg_data) goto fail; vgmstream->codec_data = mpeg_data; - if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail; - vgmstream->coding_type = ct; vgmstream->layout_type = layout_mpeg; - if (mi.vbr != MPG123_CBR) goto fail; - vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, &mi); + vgmstream->num_samples = mpeg_bytes_to_samples(stream_size, mpeg_data); vgmstream->num_samples -= vgmstream->num_samples%576; if (loop_flag) { - vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi); + vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, mpeg_data); vgmstream->loop_start_sample -= vgmstream->loop_start_sample%576; - vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi); + vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, mpeg_data); vgmstream->loop_end_sample -= vgmstream->loop_end_sample%576; } vgmstream->interleave_block_size = 0; @@ -413,33 +409,14 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) { goto fail; } - vgmstream->meta_type = meta_SQEX_SCD; - /* open the file for reading */ - if (vgmstream->layout_type != layout_scd_int -#ifdef VGM_USE_FFMPEG - && vgmstream->coding_type != coding_FFmpeg -#endif - ) - { - 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; - - } - } + 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/xma.c b/Frameworks/vgmstream/vgmstream/src/meta/xma.c index d3974a928..f1ffe58e0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xma.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xma.c @@ -1,12 +1,9 @@ #include "meta.h" #include "../util.h" +#include "../coding/coding.h" #ifdef VGM_USE_FFMPEG -#define ADJUST_SAMPLE_RATE 0 -#define XMA_BYTES_PER_PACKET 2048 -#define XMA_SAMPLES_PER_FRAME 512 -#define XMA_SAMPLES_PER_SUBFRAME 128 #define FAKE_RIFF_BUFFER_SIZE 100 @@ -24,26 +21,23 @@ typedef struct { uint8_t xma2_version; int needs_header; int force_little_endian; /* FFmpeg can't parse big endian "fmt" chunks */ + int skip_samples; /* info */ + int channels; int loop_flag; int32_t num_samples; int32_t loop_start_sample; int32_t loop_end_sample; - int32_t xma1_loop_start_offset_b; - int32_t xma1_loop_end_offset_b; - int32_t xma1_subframe_data; + int32_t loop_start_b; + int32_t loop_end_b; + int32_t loop_subframe; } xma_header_data; - static int parse_header(xma_header_data * xma, STREAMFILE *streamFile); -static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile); +static void fix_samples(xma_header_data * xma, STREAMFILE *streamFile); static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * xma, STREAMFILE *streamFile); -static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec); -#if ADJUST_SAMPLE_RATE -static int get_xma_sample_rate(int32_t general_rate); -#endif /** * XMA 1/2 (Microsoft) @@ -51,7 +45,6 @@ static int get_xma_sample_rate(int32_t general_rate); * Usually in RIFF headers and minor variations. */ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { - char filename[PATH_LIMIT]; VGMSTREAM * vgmstream = NULL; ffmpeg_codec_data *data = NULL; @@ -61,12 +54,8 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("xma",filename_extension(filename)) - && strcasecmp("xma2",filename_extension(filename)) /* Skullgirls */ - && strcasecmp("nps",filename_extension(filename)) /* Beautiful Katamari */ - && strcasecmp("past",filename_extension(filename)) /* SoulCalibur II HD */ - ) + /* .xma2: Skullgirls, .nps: Beautiful Katamari, .past: SoulCalibur II HD */ + if ( !check_extensions(streamFile, "xma,xma2,nps,past") ) goto fail; /* check header */ @@ -91,24 +80,29 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { /* build VGMSTREAM */ vgmstream = allocate_vgmstream(data->channels, xma.loop_flag); if (!vgmstream) goto fail; - /*vgmstream->channels = data->channels;*/ - /*vgmstream->loop_flag = loop_flag;*/ - vgmstream->codec_data = data; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->meta_type = meta_FFmpeg; vgmstream->sample_rate = data->sampleRate; -#if ADJUST_SAMPLE_RATE - vgmstream->sample_rate = get_xma_sample_rate(vgmstream->sample_rate); -#endif - vgmstream->num_samples = xma.num_samples; /* data->totalSamples: XMA1 = not set; XMA2 = not reliable */ + /* fix samples for some formats (data->totalSamples: XMA1 = not set; XMA2 = not reliable) */ + xma.channels = data->channels; + fix_samples(&xma, streamFile); + + vgmstream->num_samples = xma.num_samples; if (vgmstream->loop_flag) { vgmstream->loop_start_sample = xma.loop_start_sample; vgmstream->loop_end_sample = xma.loop_end_sample; } +#if 0 + //not active due to a FFmpeg bug that misses some of the last packet samples and decodes + // garbage if asked for more samples (always happens but more apparent with skip_samples active) + /* fix encoder delay */ + if (data->skipSamples==0) + ffmpeg_set_skip_samples(data, xma.skip_samples); +#endif return vgmstream; @@ -178,7 +172,8 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { size_t riff_size = 0, fmt_size = 0, xma2_size = 0; riff_size = read_32bit(4,streamFile); - if (riff_size+8 > xma->file_size) goto fail; + if (riff_size != xma->file_size && /* some Beautiful Katamari, unsure if bad rip */ + riff_size+8 > xma->file_size) goto fail; while (current_chunk < xma->file_size && current_chunk < riff_size+8) { uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); @@ -229,7 +224,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { } } else if (id == id_NXMA) { /* Namco (Tekken 6, Galaga Legions DX) */ - /* custom header with a "XMA2" or "fmt " data chunk inside, most other values are unknown */ + /* custom header with a "XMA2" or "fmt " data chunk inside, most other values are unknown */ uint32_t chunk_type = read_32bit(0xC,streamFile); xma->data_offset = 0x100; xma->data_size = read_32bit(0x14,streamFile); @@ -248,7 +243,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { if (xma->data_size + xma->data_offset > xma->file_size) goto fail; } else if (id == id_PASX) { /* SoulCalibur II HD */ - /* custom header with a "fmt " data chunk inside */ + /* custom header with a "fmt " data chunk inside */ xma->chunk_size = read_32bit(0x08,streamFile); xma->data_size = read_32bit(0x0c,streamFile); xma->chunk_offset = read_32bit(0x10,streamFile); @@ -262,18 +257,21 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { goto fail; } - /* find sample data */ if (xma->xma2_version) { /* old XMA2 (internally always BE) */ xma->loop_start_sample = read_32bitBE(xma->chunk_offset+0x4,streamFile); xma->loop_end_sample = read_32bitBE(xma->chunk_offset+0x8,streamFile); - xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x3,streamFile) > 0 /* rarely not set */ + xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x3,streamFile) > 0 /* rarely not set, encoder default */ || xma->loop_end_sample; if (xma->xma2_version == 3) { xma->num_samples = read_32bitBE(xma->chunk_offset+0x14,streamFile); + /*xma->pcm_samples = read_32bitBE(xma->chunk_offset+0x18,streamFile)*/ } else { xma->num_samples = read_32bitBE(xma->chunk_offset+0x1C,streamFile); + /*xma->pcm_samples = read_32bitBE(xma->chunk_offset+0x20,streamFile)*/ } + /* num_samples is the max samples in the file (apparently not including encoder delay) */ + /* pcm_samples are original WAV's; not current since samples and sample rate may be adjusted for looping purposes */ } else if (xma->fmt_codec == 0x166) { /* pure XMA2 */ xma->num_samples = read_32bit(xma->chunk_offset+0x18,streamFile); @@ -281,17 +279,16 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) { xma->loop_end_sample = xma->loop_start_sample + read_32bit(xma->chunk_offset+0x2C,streamFile); xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x30,streamFile) > 0 /* never set in practice */ || xma->loop_end_sample; - /* not needed but may affect looping? (sometimes these don't match loop/total samples) */ + /* play_begin+end = probably pcm_samples (for original sample rate), don't seem to affect anything */ /* int32_t play_begin_sample = read_32bit(xma->chunk_offset+0x20,streamFile); */ /* int32_t play_end_sample = play_begin_sample + read_32bit(xma->chunk_offset+0x24,streamFile); */ } else if (xma->fmt_codec == 0x165) { /* pure XMA1 */ xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0xA,streamFile) > 0; - xma->xma1_loop_start_offset_b = read_32bit(xma->chunk_offset+0x14,streamFile); - xma->xma1_loop_end_offset_b = read_32bit(xma->chunk_offset+0x18,streamFile); - xma->xma1_subframe_data = (uint8_t)read_8bit(xma->chunk_offset+0x1C,streamFile); - /* find samples count + loop samples since they are not in the header */ - parse_xma1_sample_data(xma, streamFile); + xma->loop_start_b = read_32bit(xma->chunk_offset+0x14,streamFile); + xma->loop_end_b = read_32bit(xma->chunk_offset+0x18,streamFile); + xma->loop_subframe = (uint8_t)read_8bit(xma->chunk_offset+0x1C,streamFile); + /* num_samples are parsed later */ } else { /* unknown chunk */ goto fail; @@ -304,69 +301,42 @@ fail: } -/** - * XMA1: manually find total and loop samples - * - * A XMA1 stream is made of packets, each containing N frames of X samples, and frame is divided into subframes for looping purposes. - * FFmpeg can't get total samples without decoding, so we'll count frames+samples by reading packet headers. - */ -static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile) { - int frames = 0, loop_frame_start = 0, loop_frame_end = 0, loop_subframe_end, loop_subframe_skip; - uint32_t header, first_frame_b, packet_skip_b, frame_size_b, packet_size_b; - uint64_t packet_offset_b, frame_offset_b; - uint32_t size; +static void fix_samples(xma_header_data * xma, STREAMFILE *streamFile) { + xma_sample_data xma_sd; - uint32_t packet_size = XMA_BYTES_PER_PACKET; - uint32_t offset = xma->data_offset; - uint32_t offset_b = 0; - uint32_t stream_offset_b = xma->data_offset * 8; + /* for now only XMA1 is fixed, but xmaencode.exe doesn't seem to use + * XMA2 sample values in the headers, and the exact number of samples may not be exact. + * Also loop values don't seem to need skip_samples. */ - size = offset + xma->data_size; - packet_size_b = packet_size*8; - - while (offset < size) { /* stream global offset*/ - /* XMA1 packet header (32b) = packet_sequence:4, unk:2: frame_offset_in_bits:15, packet_stream_skip_count:11 */ - header = read_32bitBE(offset, streamFile); - first_frame_b = (header >> 11) & 0x7FFF; - packet_skip_b = (header) & 0x7FF; - - offset_b = offset * 8; - packet_offset_b = 4*8 + first_frame_b; - while (packet_offset_b < packet_size_b && packet_skip_b!=0x7FF) { /* internal packet offset + full packet skip (needed?) */ - frame_offset_b = offset_b + packet_offset_b; /* global offset to packet, in bits for aligment stuff */ - - /* XMA1 frame header (32b) = frame_length_in_bits:15, frame_data:17+ */ - header = read_32bitBE(frame_offset_b/8, streamFile); - frame_size_b = (header >> (17 - frame_offset_b % 8)) & 0x7FFF; - - if (frame_size_b == 0) /* observed in some files */ - break; - - packet_offset_b += frame_size_b;/* including header */ - - if (frame_size_b != 0x7FFF) /* end frame marker*/ - frames++; - - if (xma->loop_flag && frame_offset_b - stream_offset_b == xma->xma1_loop_start_offset_b) - loop_frame_start = frames; - if (xma->loop_flag && frame_offset_b - stream_offset_b == xma->xma1_loop_end_offset_b) - loop_frame_end = frames; - } - - offset += packet_size; + if (xma->fmt_codec != 0x165) { + return; } - loop_subframe_end = xma->xma1_subframe_data >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ - loop_subframe_skip = xma->xma1_subframe_data & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */ + memset(&xma_sd,0,sizeof(xma_sample_data)); - xma->num_samples = frames * XMA_SAMPLES_PER_FRAME; - if (xma->loop_flag) { - xma->loop_start_sample = loop_frame_start * XMA_SAMPLES_PER_FRAME + loop_subframe_skip * XMA_SAMPLES_PER_SUBFRAME; - xma->loop_end_sample = loop_frame_end * XMA_SAMPLES_PER_FRAME + loop_subframe_end * XMA_SAMPLES_PER_SUBFRAME; - } + /* call xma parser (copy to its own struct, a bit clunky but oh well...) */ + xma_sd.xma_version = xma->fmt_codec==0x165 ? 1 : 2; + xma_sd.channels = xma->channels; + xma_sd.data_offset = xma->data_offset; + xma_sd.data_size = xma->data_size; + xma_sd.loop_flag = xma->loop_flag; + xma_sd.loop_start_b = xma->loop_start_b; + xma_sd.loop_end_b = xma->loop_end_b; + xma_sd.loop_start_subframe = xma->loop_subframe & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */ + xma_sd.loop_end_subframe = xma->loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ + + xma_get_samples(&xma_sd, streamFile); + + /* and recieve results */ + xma->num_samples = xma_sd.num_samples; + xma->skip_samples = xma_sd.skip_samples; + xma->loop_start_sample = xma_sd.loop_start_sample; + xma->loop_end_sample = xma_sd.loop_end_sample; + /* XMA2 loop/num_samples don't seem to skip_samples */ } + /** * Recreates a RIFF header that FFmpeg can read since it lacks support for some variations. * @@ -406,7 +376,7 @@ static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * internal_size = 4+4+xma->chunk_size; if (xma->force_little_endian ) { - if ( !fmt_chunk_swap_endian(chunk, xma->fmt_codec) ) + if ( !ffmpeg_fmt_chunk_swap_endian(chunk, xma->fmt_codec) ) goto fail; } @@ -434,44 +404,10 @@ fail: } +#if 0 /** - * Swaps endianness - * - * returns 0 on error - */ -static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) { - if (codec != 0x166) - goto fail; - - put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/ - put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/ - put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/ - put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/ - put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/ - put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/ - put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/ - put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/ - put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/ - put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/ - put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/ - put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/ - put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/ - put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/ - put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/ - /* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/ - /* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/ - put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/ - - return 1; - -fail: - return 0; -} - - -#if ADJUST_SAMPLE_RATE -/** - * Get real XMA sample rate (from Microsoft docs, apparently info only and not correct for playback). + * Get real XMA sample rate (from Microsoft docs). + * Info only, not for playback as the encoder adjusts sample rate for looping purposes (sample<>data align). */ static int32_t get_xma_sample_rate(int32_t general_rate) { int32_t xma_rate = 48000; /* default XMA */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 78d00dd75..0da9807eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -1,169 +1,391 @@ #include "meta.h" #include "../util.h" +#include "../coding/coding.h" -/* XWB - XACT Wave Bank (Main format on the XBOX console) */ +/* most info from XWBtool, xactwb.h, xact2wb.h and xact3wb.h */ +#define WAVEBANK_FLAGS_COMPACT 0x00020000 // Bank uses compact format +#define WAVEBANKENTRY_FLAGS_IGNORELOOP 0x00000008 // Used internally when the loop region can't be used + +static const int32_t wma_avg_bps_index[] = { /*7*/ + 12000, 24000, 4000, 6000, 8000, 20000, 2500 +}; +static const int32_t wma_block_align_index[] = { /*17*/ + 929, 1487, 1280, 2230, 8917, 8192, 4459, 5945, 2304, 1536, 1485, 1008, 2731, 4096, 6827, 5462, 1280 +}; + + +typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA } xact_codec; +typedef struct { + int little_endian; + int xact; /* rough XACT version (1/2/3) */ + + int version; + + /* segments */ + off_t base_offset; + size_t base_size; + off_t entry_offset; + size_t entry_size; + off_t data_offset; + size_t data_size; + + off_t stream_offset; + size_t stream_size; + + uint32_t base_flags; + size_t entry_elem_size; + size_t entry_alignment; + int streams; + + uint32_t entry_flags; + uint32_t format; + int tag; + int channels; + int sample_rate; + int block_align; + int bits_per_sample; + xact_codec codec; + + int loop_flag; + uint32_t num_samples; + uint32_t loop_start; + uint32_t loop_end; + uint32_t loop_start_sample; + uint32_t loop_end_sample; +} xwb_header; + + +/* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - int loop_flag = 0; - int channel_count; - int coding; - int coding_flag; - int num_samples; - int header_start; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("xwb",filename_extension(filename))) goto fail; + off_t start_offset, off, suboff; + xwb_header xwb; + int target_stream = 0; + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - /* check header */ - if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* WBND */ - (read_32bitBE(0x00,streamFile) != 0x444E4257)) /* DNBW */ + /* basic checks */ + if (!check_extensions(streamFile,"xwb")) goto fail; + + if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */ + (read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */ goto fail; - /* check if the file is been used as container */ - if (read_32bitBE(0x2C,streamFile) != 0x01000000) - goto fail; - - header_start = read_32bitLE(0x10,streamFile); - loop_flag = (read_32bitLE(header_start+0x10,streamFile) != 0x0); - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - /* fill in the vital statistics */ - start_offset = read_32bitLE(0x20,streamFile); - vgmstream->channels = channel_count; - vgmstream->sample_rate = (((uint32_t)read_32bitLE(header_start+0x4, streamFile)) << 8 >> 12) / 2; - - coding_flag = (uint16_t)(read_16bitLE(header_start+0x2,streamFile)); - switch (coding_flag) { - case 0: - coding = coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - num_samples = read_32bitLE(header_start+0xC,streamFile)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(header_start+10,streamFile)/2/channel_count; - vgmstream->loop_end_sample = read_32bitLE(header_start+0xC,streamFile)/2/channel_count; - } - break; - case 1: - coding = coding_XBOX; - vgmstream->layout_type = layout_none; - num_samples = read_32bitLE(header_start+0xC,streamFile)/36/channel_count*64; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitLE(header_start+0x10,streamFile)/36/channel_count*64; - vgmstream->loop_end_sample = read_32bitLE(header_start+0xC,streamFile)/36/channel_count*64; - } - break; - default: - goto fail; - } + memset(&xwb,0,sizeof(xwb_header)); - vgmstream->coding_type = coding; - vgmstream->num_samples = num_samples; - vgmstream->meta_type = meta_XWB; + xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44;/* WBND */ + if (xwb.little_endian) { + read_32bit = read_32bitLE; + } else { + read_32bit = read_32bitBE; + } - /* 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; - - if (vgmstream->coding_type == coding_XBOX) { - /* xbox interleaving is a little odd */ - vgmstream->ch[i].channel_start_offset=start_offset; - } else { - vgmstream->ch[i].channel_start_offset= - start_offset+vgmstream->interleave_block_size*i; - } - vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; + /* read main header (WAVEBANKHEADER) */ + xwb.version = read_32bit(0x04, streamFile); + if (xwb.version <= 3) { + xwb.xact = 1; /* XACT1: XBOX [The King of Fighters 2003] */ + } else if (xwb.version < 42) { + xwb.xact = 2; /* XACT2: early XBOX360 [Kameo, Table Tennis, Blue Dragon], Windows */ + } else { /* highest seen: tool=v46, header=v44 */ + xwb.xact = 3; /* XACT3: late XBOX360, Windows */ + } + /* read segment offsets (SEGIDX) */ + off = xwb.xact <= 2 ? 0x08 : 0x0c; /* XACT3: 0x04=tool version, 0x08=header version */ + xwb.base_offset = read_32bit(off+0x00, streamFile);//BANKDATA + xwb.base_size = read_32bit(off+0x04, streamFile); + xwb.entry_offset= read_32bit(off+0x08, streamFile);//ENTRYMETADATA + xwb.entry_size = read_32bit(off+0x0c, streamFile); + /* go to last segment (XACT2/3 have 5 segments, XACT1 4) */ + //0x10: XACT1/2: ENTRYNAMES, XACT3: SEEKTABLES + //0x14: XACT1: none (ENTRYWAVEDATA), XACT2: EXTRA, XACT3: ENTRYNAMES + suboff = xwb.xact >= 2 ? 0x08+0x08 : 0x08; + xwb.data_offset = read_32bit(off+0x10+suboff, streamFile);//ENTRYWAVEDATA + xwb.data_size = read_32bit(off+0x10+suboff, streamFile); + + + /* read base entry (WAVEBANKDATA) */ + off = xwb.base_offset; + xwb.base_flags = (uint32_t)read_32bit(off+0x00, streamFile); + xwb.streams = read_32bit(off+0x04, streamFile); + /* 0x08 bank_name */ + suboff = 0x08 + (xwb.xact == 1 ? 0x10 : 0x40); + xwb.entry_elem_size = read_32bit(off+suboff+0x00, streamFile); + /* suboff+0x04: meta name entry size */ + xwb.entry_alignment = read_32bit(off+suboff+0x08, streamFile); /* usually 1 dvd sector */ + xwb.format = read_32bit(off+suboff+0x0c, streamFile); /* compact mode only */ + /* suboff+0x10: build time 64b (XACT2/3) */ + + if (target_stream == 0) target_stream = 1; /* auto: default to 1 */ + if (xwb.streams < 1 || target_stream > xwb.streams) goto fail; + + + /* read stream entry (WAVEBANKENTRY) */ + off = xwb.entry_offset + (target_stream-1) * xwb.entry_elem_size; + + if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { /* compact entry */ + /* offset_in_sectors:21 and sector_alignment_in_bytes:11 */ + uint32_t entry = (uint32_t)read_32bit(off+0x00, streamFile); + xwb.stream_offset = xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF); + + /* find size (up to next entry or data end) */ + if (xwb.streams > 1) { + entry = (uint32_t)read_32bit(off+xwb.entry_size, streamFile); + xwb.stream_size = xwb.stream_offset - + (xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF)); + } else { + xwb.stream_size = xwb.data_size; + } + } + else { + uint32_t entry_info = (uint32_t)read_32bit(off+0x00, streamFile); + if (xwb.xact == 1) { + xwb.entry_flags = entry_info; + } else { + xwb.entry_flags = (entry_info) & 0xF; /*4*/ + xwb.num_samples = (entry_info >> 4) & 0x0FFFFFFF; /*28*/ + } + xwb.format = (uint32_t)read_32bit(off+0x04, streamFile); + xwb.stream_offset = xwb.data_offset + (uint32_t)read_32bit(off+0x08, streamFile); + xwb.stream_size = (uint32_t)read_32bit(off+0x0c, streamFile); + + if (xwb.xact == 1) { //LoopRegion (bytes within data) + xwb.loop_start = (uint32_t)read_32bit(off+0x10, streamFile); + xwb.loop_end = (uint32_t)read_32bit(off+0x14, streamFile);//length + } else if (xwb.xact == 2 && xwb.version <= 38) {//LoopRegion (bytes within data) or XMALoopRegion (bits within data) + xwb.loop_start = (uint32_t)read_32bit(off+0x10, streamFile); + xwb.loop_end = (uint32_t)read_32bit(off+0x14, streamFile);//length (LoopRegion) or offset (XMALoopRegion) + } else {//LoopRegion (samples) + xwb.loop_start_sample = (uint32_t)read_32bit(off+0x10, streamFile); + xwb.loop_end_sample = xwb.loop_start_sample + (uint32_t)read_32bit(off+0x14, streamFile); + } + + xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start) + && !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP); + } + + + /* parse format */ + if (xwb.xact == 1) { + xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/ + xwb.sample_rate = (xwb.format >> 5) & 0x3FFFFFF; /*26*/ + xwb.channels = (xwb.format >> 2) & 0x7; /*3*/ + xwb.tag = (xwb.format) & 0x3; /*2*/ + } + else if (xwb.version <= 34) { /* early XACT2, not sure if includes v35/36 */ + xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/ + xwb.block_align = (xwb.format >> 24) & 0xFF; /*8*/ + xwb.sample_rate = (xwb.format >> 4) & 0x7FFFF; /*19*/ + xwb.channels = (xwb.format >> 1) & 0x7; /*3*/ + xwb.tag = (xwb.format) & 0x1; /*1*/ + } + else { + xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/ + xwb.block_align = (xwb.format >> 23) & 0xFF; /*8*/ + xwb.sample_rate = (xwb.format >> 5) & 0x3FFFF; /*18*/ + xwb.channels = (xwb.format >> 2) & 0x7; /*3*/ + xwb.tag = (xwb.format) & 0x3; /*2*/ + } + + /* standardize tag to codec */ + if (xwb.xact == 1) { + switch(xwb.tag){ + case 0: xwb.codec = PCM; break; + case 1: xwb.codec = XBOX_ADPCM; break; + case 2: xwb.codec = WMA; break; + default: goto fail; + } + } else if (xwb.xact == 2) { + switch(xwb.tag) { + case 0: xwb.codec = PCM; break; + /* Table Tennis (v34): XMA1, Prey (v38): XMA2, v35/36/37: ? */ + case 1: xwb.codec = xwb.version <= 34 ? XMA1 : XMA2; break; + case 2: xwb.codec = MS_ADPCM; break; + default: goto fail; + } + } else { + switch(xwb.tag) { + case 0: xwb.codec = PCM; break; + case 1: xwb.codec = XMA2; break; + case 2: xwb.codec = MS_ADPCM; break; + case 3: xwb.codec = XWMA; break; + default: goto fail; } } - return vgmstream; - - /* clean up anything we may have opened */ -fail: - if (vgmstream) close_vgmstream(vgmstream); - return NULL; -} - - -VGMSTREAM * init_vgmstream_xwb2(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - char filename[PATH_LIMIT]; - off_t start_offset; - - int loop_flag = 0; - int channel_count; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("xwb",filename_extension(filename))) goto fail; - - /* check extension, case insensitive */ - streamFile->get_name(streamFile,filename,sizeof(filename)); - if (strcasecmp("xwb",filename_extension(filename))) goto fail; - - /* check header */ - if (read_32bitBE(0x00,streamFile) != 0x444E4257) /* WBND */ - goto fail; - - loop_flag = (read_32bitBE(0xA4,streamFile)!=0); - channel_count = 2; - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - /* fill in the vital statistics */ - start_offset = 0x800; - vgmstream->channels = channel_count; - vgmstream->sample_rate = 44100; - vgmstream->coding_type = coding_PCM16BE; - vgmstream->num_samples = read_32bitBE(0xA0,streamFile)/2/channel_count; - if (loop_flag) { - vgmstream->loop_start_sample = read_32bitBE(0xA4,streamFile); - vgmstream->loop_end_sample = read_32bitBE(0xA0,streamFile)/2/channel_count; - } - - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x02; - vgmstream->meta_type = meta_XWB; - - /* 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; + /* fix samples */ + if ((xwb.xact == 1 || xwb.xact == 2) && xwb.codec == PCM) { + xwb.num_samples = xwb.stream_size / 2 / xwb.channels; + if (xwb.loop_flag) { + xwb.loop_start_sample = (xwb.loop_start) / 2 / xwb.channels; + xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / 2 / xwb.channels; } } + else if (xwb.xact == 1 && xwb.codec == XBOX_ADPCM) { + xwb.num_samples = xwb.stream_size / 36 / xwb.channels*64; + if (xwb.loop_flag) { + xwb.loop_start_sample = xwb.loop_start / 36 / xwb.channels*64; + xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / 36 / xwb.channels*64; + } + } + else if (xwb.xact == 2 && xwb.codec == MS_ADPCM && xwb.loop_flag) { + int block_size, samples_per_frame; + block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/ + samples_per_frame = (block_size-(7-1) * xwb.channels) * 2 / xwb.channels; + xwb.loop_start_sample = (xwb.loop_start) / block_size / samples_per_frame; + xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / block_size / samples_per_frame; + } + else if (xwb.xact == 2 && xwb.version <= 38 /* v38: byte offset, v40+: sample offset, v39: ? */ + && (xwb.codec == XMA1 || xwb.codec == XMA2) && xwb.loop_flag) { + /* need to manually find sample offsets, thanks to Microsoft dumb headers */ + xma_sample_data xma_sd; + memset(&xma_sd,0,sizeof(xma_sample_data)); + + xma_sd.xma_version = xwb.codec == XMA1 ? 1 : 2; + xma_sd.channels = xwb.channels; + xma_sd.data_offset = xwb.stream_offset; + xma_sd.data_size = xwb.stream_size; + xma_sd.loop_flag = xwb.loop_flag; + xma_sd.loop_start_b = xwb.loop_start; /* bit offset in the stream */ + xma_sd.loop_end_b = (xwb.loop_end >> 4); /*28b */ + /* XACT adds +1 to the subframe, but this means 0 can't be used? */ + xma_sd.loop_end_subframe = ((xwb.loop_end >> 2) & 0x3) + 1; /* 2b */ + xma_sd.loop_start_subframe = ((xwb.loop_end >> 0) & 0x3) + 1; /* 2b */ + + xma_get_samples(&xma_sd, streamFile); + xwb.loop_start_sample = xma_sd.loop_start_sample; + xwb.loop_end_sample = xma_sd.loop_end_sample; + + // todo fix properly (XWB loop_start/end seem to count padding samples while XMA1 RIFF doesn't) + //this doesn't seem ok because can fall within 0 to 512 (ie.- first frame) + //if (xwb.loop_start_sample) xwb.loop_start_sample -= 512; + //if (xwb.loop_end_sample) xwb.loop_end_sample -= 512; + + //add padding back until it's fixed (affects looping) + // (in rare cases this causes a glitch in FFmpeg since it has a bug where it's missing some samples) + xwb.num_samples += 64 + 512; + } + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(xwb.channels,xwb.loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = xwb.sample_rate; + vgmstream->num_samples = xwb.num_samples; + vgmstream->loop_start_sample = xwb.loop_start_sample; + vgmstream->loop_end_sample = xwb.loop_end_sample; + vgmstream->num_streams = xwb.streams; + vgmstream->meta_type = meta_XWB; + + switch(xwb.codec) { + case PCM: + vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 : + (xwb.little_endian ? coding_PCM16LE : coding_PCM16BE); + vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none; + vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02; + break; + + case XBOX_ADPCM: + vgmstream->coding_type = coding_XBOX; + vgmstream->layout_type = layout_none; + break; + + case MS_ADPCM: + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/ + break; + +#ifdef VGM_USE_FFMPEG + case XMA1: { + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[100]; + int bytes; + + bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0); + if (bytes <= 0) goto fail; + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } + + case XMA2: { + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[100]; + int bytes, block_size, block_count; + + block_size = 0x10000; /* XACT default */ + block_count = xwb.stream_size / block_size + (xwb.stream_size % block_size ? 1 : 0); + + bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + if (bytes <= 0) goto fail; + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } + + case WMA: { /* WMAudio1 (WMA v1) */ + ffmpeg_codec_data *ffmpeg_data = NULL; + + ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } + + case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */ + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[100]; + int bytes, bps_index, block_align, block_index, avg_bps, wma_codec; + + bps_index = (xwb.block_align >> 5); /* upper 3b bytes-per-second index */ //docs say 2b+6b but are wrong + block_index = (xwb.block_align) & 0x1F; /*lower 5b block alignment index */ + if (bps_index >= 7) goto fail; + if (block_index >= 17) goto fail; + + avg_bps = wma_avg_bps_index[bps_index]; + block_align = wma_block_align_index[block_index]; + wma_codec = xwb.bits_per_sample ? 0x162 : 0x161; /* 0=WMAudio2, 1=WMAudio3 */ + + bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align); + if (bytes <= 0) goto fail; + + ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size); + if ( !ffmpeg_data ) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + break; + } +#endif + + default: + goto fail; + } + + + start_offset = xwb.data_offset; + + 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/streamfile.c b/Frameworks/vgmstream/vgmstream/src/streamfile.c index 522515d60..c5e4b784e 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.c +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.c @@ -457,7 +457,7 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { char filename[PATH_LIMIT]; const char * ext = NULL; const char * cmp_ext = NULL; - size_t ext_len; + int ext_len, cmp_len; streamFile->get_name(streamFile,filename,sizeof(filename)); ext = filename_extension(filename); @@ -465,11 +465,17 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { cmp_ext = cmp_exts; do { - if (strncasecmp(ext,cmp_ext, ext_len)==0 ) + cmp_len = strstr(cmp_ext, ",") - cmp_ext; /* find next ext; becomes negative if not found */ + if (cmp_len < 0) + cmp_len = strlen(cmp_ext); /* total length if more not found */ + + if (strncasecmp(ext,cmp_ext, ext_len) == 0 && ext_len == cmp_len) return 1; + cmp_ext = strstr(cmp_ext, ","); if (cmp_ext != NULL) cmp_ext = cmp_ext + 1; /* skip comma */ + } while (cmp_ext != NULL); return 0; @@ -484,14 +490,13 @@ int check_extensions(STREAMFILE *streamFile, const char * cmp_exts) { * * returns 0 on failure */ -static int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, int size_big_endian, off_t *out_chunk_offset, size_t *out_chunk_size); 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) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, 1, out_chunk_offset, out_chunk_size); + return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 1); } 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) { - return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, 0, out_chunk_offset, out_chunk_size); + return find_chunk(streamFile, chunk_id, start_offset, full_chunk_size, out_chunk_offset, out_chunk_size, 0); } -int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, int full_chunk_size, int size_big_endian, 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) { size_t filesize; off_t current_chunk = start_offset; @@ -509,6 +514,10 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in return 1; } + /* end chunk with 0 size, seen in some custom formats */ + if (chunk_size == 0) + return 0; + current_chunk += full_chunk_size ? chunk_size : 4+4+chunk_size; } diff --git a/Frameworks/vgmstream/vgmstream/src/streamfile.h b/Frameworks/vgmstream/vgmstream/src/streamfile.h index bc2693319..5e804e610 100644 --- a/Frameworks/vgmstream/vgmstream/src/streamfile.h +++ b/Frameworks/vgmstream/vgmstream/src/streamfile.h @@ -158,5 +158,5 @@ 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); #endif diff --git a/Frameworks/vgmstream/vgmstream/src/util.h b/Frameworks/vgmstream/vgmstream/src/util.h index 6ae184920..6d12377c7 100644 --- a/Frameworks/vgmstream/vgmstream/src/util.h +++ b/Frameworks/vgmstream/vgmstream/src/util.h @@ -70,19 +70,31 @@ void concatn(int length, char * dst, const char * src); /* Simple stdout logging for debugging and regression testing purposes. * Needs C99 variadic macros. */ #ifdef VGM_DEBUG_OUTPUT - +/* equivalent to printf when condition is true */ #define VGM_ASSERT(condition, ...) \ do { if (condition) printf(__VA_ARGS__); } while (0) +/* equivalent to printf */ #define VGM_LOG(...) \ do { printf(__VA_ARGS__); } while (0) +/* prints file/line/func */ #define VGM_LOGF() \ do { printf("%s:%i '%s'\n", __FILE__, __LINE__, __func__); } while (0) - +/* prints a buffer/array */ +#define VGM_LOGB(buf, buf_size, bytes_per_line) \ + do { \ + int i; \ + for (i=0; i < buf_size; i++) { \ + printf("%02x",buf[i]); \ + if (bytes_per_line && (i+1) % bytes_per_line == 0) printf("\n"); \ + } \ + printf("\n"); \ + } while (0) #else -#define VGM_ASSERT(condition,fmt, ...) /* nothing */ +#define VGM_ASSERT(condition, ...) /* nothing */ #define VGM_LOG(...) /* nothing */ -#define VGM_LOGF(...) /* nothing */ +#define VGM_LOGF() /* nothing */ +#define VGM_LOGB(buf, buf_size, bytes_per_line) /* nothing */ #endif diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 3e2af74cd..21bf9ddae 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -98,7 +98,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_fsb5, init_vgmstream_rwx, init_vgmstream_xwb, - init_vgmstream_xwb2, init_vgmstream_xa30, init_vgmstream_musc, init_vgmstream_musx_v004, @@ -312,7 +311,8 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_wmus, init_vgmstream_hyperscan_kvag, init_vgmstream_ios_psnd, - init_vgmstream_bos_adp, + init_vgmstream_pc_adp_bos, + init_vgmstream_pc_adp_otns, init_vgmstream_eb_sfx, init_vgmstream_eb_sf0, init_vgmstream_ps3_klbs, @@ -337,9 +337,14 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_svag_snk, init_vgmstream_ps2_vds_vdm, init_vgmstream_x360_cxs, + init_vgmstream_dsp_adx, + init_vgmstream_akb_multi, + init_vgmstream_akb2_multi, + #ifdef VGM_USE_FFMPEG init_vgmstream_xma, init_vgmstream_mp4_aac_ffmpeg, + init_vgmstream_bik, init_vgmstream_ffmpeg, /* should go at the end */ #endif @@ -403,6 +408,16 @@ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) { try_dual_file_stereo(vgmstream, streamFile); } +#ifdef VGM_USE_FFMPEG + /* check FFmpeg streams here, for lack of a better place */ + if (vgmstream->coding_type == coding_FFmpeg) { + ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data; + if (data->streamCount && !vgmstream->num_streams) { + vgmstream->num_streams = data->streamCount; + } + } +#endif + /* save start things so we can restart for seeking */ /* copy the channels */ memcpy(vgmstream->start_ch,vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); @@ -472,13 +487,7 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #ifdef VGM_USE_MPEG if (vgmstream->layout_type==layout_mpeg || vgmstream->layout_type==layout_fake_mpeg) { - off_t input_offset; - mpeg_codec_data *data = vgmstream->codec_data; - - /* input_offset is ignored as we can assume it will be 0 for a seek - * to sample 0 */ - mpg123_feedseek(data->m,0,SEEK_SET,&input_offset); - data->buffer_full = data->buffer_used = 0; + reset_mpeg(vgmstream); } #endif #ifdef VGM_USE_G7221 @@ -696,18 +705,8 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #ifdef VGM_USE_MPEG if (vgmstream->layout_type==layout_fake_mpeg|| vgmstream->layout_type==layout_mpeg) { - mpeg_codec_data *data = (mpeg_codec_data *) vgmstream->codec_data; - - if (data) { - mpg123_delete(data->m); - free(vgmstream->codec_data); - vgmstream->codec_data = NULL; - /* The astute reader will note that a call to mpg123_exit is never - * made. While is is evilly breaking our contract with mpg123, it - * doesn't actually do anything except set the "initialized" flag - * to 0. And if we exit we run the risk of turning it off when - * someone else in another thread is using it. */ - } + free_mpeg((mpeg_codec_data *)vgmstream->codec_data); + vgmstream->codec_data = NULL; } #endif @@ -1022,6 +1021,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_EACS_IMA: case coding_SNDS_IMA: case coding_IMA: + case coding_OTNS_IMA: return 1; case coding_INT_IMA: case coding_INT_DVI_IMA: @@ -1039,6 +1039,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return (vgmstream->interleave_block_size - 1) * 2; /* decodes 1 byte into 2 bytes */ case coding_XBOX: case coding_INT_XBOX: + case coding_FSB_IMA: return 64; case coding_EA_XA: return 28; @@ -1155,6 +1156,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_IMA: case coding_G721: case coding_SNDS_IMA: + case coding_OTNS_IMA: return 0; case coding_NGC_AFC: return 9; @@ -1170,6 +1172,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 14*vgmstream->channels; case coding_XBOX: case coding_INT_XBOX: + case coding_FSB_IMA: return 36; case coding_MAXIS_ADPCM: return 15*vgmstream->channels; @@ -1547,6 +1550,21 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do,chan); } break; + case coding_OTNS_IMA: + for (chan=0;chanchannels;chan++) { + decode_otns_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do,chan); + } + break; + case coding_FSB_IMA: + for (chan=0;chanchannels;chan++) { + decode_fsb_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do,chan); + } + break; + case coding_WS: for (chan=0;chanchannels;chan++) { decode_ws(vgmstream,chan,buffer+samples_written*vgmstream->channels+chan, @@ -1572,9 +1590,9 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to case coding_MPEG25_L2: case coding_MPEG25_L3: decode_mpeg( - &vgmstream->ch[0], - vgmstream->codec_data, - buffer+samples_written*vgmstream->channels,samples_to_do, + vgmstream, + buffer+samples_written*vgmstream->channels, + samples_to_do, vgmstream->channels); break; #endif @@ -1788,14 +1806,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { #ifdef VGM_USE_MPEG /* won't work for fake MPEG */ if (vgmstream->layout_type==layout_mpeg) { - off_t input_offset; - mpeg_codec_data *data = vgmstream->codec_data; - - mpg123_feedseek(data->m,vgmstream->loop_sample, - SEEK_SET,&input_offset); - vgmstream->loop_ch[0].offset = - vgmstream->loop_ch[0].channel_start_offset + input_offset; - data->buffer_full = data->buffer_used = 0; + seek_mpeg(vgmstream, vgmstream->loop_sample); } #endif @@ -1941,6 +1952,12 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { break; } concatn(length,desc,temp); + + /* only interesting if more than one */ + if (vgmstream->num_streams > 1) { + snprintf(temp,TEMPSIZE,"\nnumber of streams: %d",vgmstream->num_streams); + concatn(length,desc,temp); + } } /* filename search pairs for dual file stereo */ @@ -2240,6 +2257,12 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) } +/** + * Inits vgmstreams' channels doing two things: + * - sets the starting offset per channel (depending on the layout) + * - opens its own streamfile from on a base one. One streamfile per channel may be open (to improve read/seeks). + * Should be called in metas before returning the VGMSTREAM.. + */ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t start_offset) { STREAMFILE * file; char filename[PATH_LIMIT]; @@ -2247,8 +2270,14 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s int use_streamfile_per_channel = 0; int use_same_offset_per_channel = 0; + + /* stream/offsets not needed, scd manages itself */ + if (vgmstream->layout_type == layout_scd_int) + return 1; + #ifdef VGM_USE_FFMPEG - if (vgmstream->coding_type == coding_FFmpeg) /* stream not needed, FFmpeg manages itself */ + /* stream/offsets not needed, FFmpeg manages itself */ + if (vgmstream->coding_type == coding_FFmpeg) return 1; #endif @@ -2257,10 +2286,8 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s use_streamfile_per_channel = 1; } - if (vgmstream->layout_type == layout_none - //#ifdef VGM_USE_MPEG || (vgmstream->layout_type == layout_mpeg) #endif //no appreciable difference - ) { - /* for some codecs like IMA where channels work with the same bytes *///todo which ones? + /* for mono or codecs like IMA (XBOX, MS IMA, MS ADPCM) where channels work with the same bytes */ + if (vgmstream->layout_type == layout_none) { use_same_offset_per_channel = 1; } @@ -2296,14 +2323,6 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s return 1; fail: - if (!use_streamfile_per_channel) { - streamFile->close(file); /* only one file was ever open */ - } else { - for (ch=0; ch < vgmstream->channels; ch++) { - if (vgmstream->ch[ch].streamfile) - streamFile->close(vgmstream->ch[ch].streamfile); /* close all open files */ - } - } - + /* open streams will be closed in close_vgmstream(), hopefully called by the meta */ return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 3aa31556b..439ec2c90 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -128,6 +128,8 @@ typedef enum { coding_APPLE_IMA4, /* Apple Quicktime IMA4 */ coding_DAT4_IMA, /* Eurocom 'DAT4' IMA ADPCM */ coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */ + coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */ + coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */ coding_WS, /* Westwood Studios VBR ADPCM */ coding_MSADPCM, /* Microsoft ADPCM */ @@ -573,6 +575,7 @@ typedef enum { meta_HYPERSCAN_KVAG, // Hyperscan KVAG/BVG meta_IOS_PSND, // Crash Bandicoot Nitro Kart 2 (iOS) meta_BOS_ADP, // ADP! (Balls of Steel, PC) + meta_OTNS_ADP, // Omikron: The Nomad Soul .adp (PC/DC) meta_EB_SFX, // Excitebots .sfx meta_EB_SF0, // Excitebots .sf0 meta_PS3_KLBS, // L@VE ONCE (PS3) @@ -598,6 +601,7 @@ typedef enum { meta_PS2_SVAG_SNK, /* SNK PS2 SVAG */ meta_PS2_VDS_VDM, /* Graffiti Kingdom */ meta_X360_CXS, /* Eternal Sonata (Xbox 360) */ + meta_AKB, /* SQEX iOS */ #ifdef VGM_USE_VORBIS meta_OGG_VORBIS, /* Ogg Vorbis */ @@ -683,6 +687,7 @@ typedef struct { coding_t coding_type; /* type of encoding */ layout_t layout_type; /* type of layout for data */ meta_t meta_type; /* how we know the metadata */ + int num_streams; /* info only, for a few multi-stream formats (0=not set/one, 1=one stream) */ /* looping */ int loop_flag; /* is this stream looped? */ @@ -766,16 +771,38 @@ typedef struct { #endif #ifdef VGM_USE_MPEG -#define AHX_EXPECTED_FRAME_SIZE 0x414 -/* MPEG_BUFFER_SIZE should be >= AHX_EXPECTED_FRAME_SIZE */ -#define MPEG_BUFFER_SIZE 0x1000 - typedef struct { - uint8_t buffer[MPEG_BUFFER_SIZE]; - int buffer_used; - int buffer_full; + uint8_t *buffer; /* raw (coded) data buffer */ + size_t buffer_size; size_t bytes_in_buffer; - mpg123_handle *m; + int buffer_full; /* raw buffer has been filled */ + int buffer_used; /* raw buffer has been fed to the decoder */ + + mpg123_handle *m; /* "base" MPEG stream */ + + /* base values, assumed to be constant in the file */ + int sample_rate_per_frame; + int channels_per_frame; + size_t samples_per_frame; + + /* interleaved MPEG internals */ + int interleaved; /* flag */ + mpg123_handle **ms; /* array of MPEG streams */ + size_t ms_size; + uint8_t *frame_buffer; /* temp buffer with samples from a single decoded frame */ + size_t frame_buffer_size; + uint8_t *interleave_buffer; /* intermediate buffer with samples from all channels */ + size_t interleave_buffer_size; + size_t bytes_in_interleave_buffer; + size_t bytes_used_in_interleave_buffer; + + /* messy stuff for padded FSB frames */ + size_t fixed_frame_size; /* when given a fixed size (XVAG) */ + size_t base_frame_size; /* without header padding byte */ + size_t current_frame_size; /* with padding byte applied if needed */ + int fsb_padding; /* for FSBs that have extra garbage between frames */ + size_t current_padding; /* padding needed for current frame size */ + } mpeg_codec_data; #endif @@ -865,6 +892,7 @@ typedef struct { #ifdef VGM_USE_FFMPEG typedef struct { + /*** init data ***/ STREAMFILE *streamfile; // offset and total size of raw stream data @@ -879,6 +907,7 @@ typedef struct { // header/fake RIFF over the real (parseable by FFmpeg) file start uint64_t header_size; + /*** "public" API (read-only) ***/ // stream info int channels; int bitsPerSample; @@ -889,7 +918,10 @@ typedef struct { int64_t totalSamples; // estimated count (may not be accurate for some demuxers) int64_t blockAlign; // coded block of bytes, counting channels (the block can be joint stereo) int64_t frameSize; // decoded samples per block + int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments + int streamCount; // number of FFmpeg audio streams + /*** internal state ***/ // Intermediate byte buffer uint8_t *sampleBuffer; // max samples we can held (can be less or more than frameSize) @@ -910,6 +942,7 @@ typedef struct { int readNextPacket; int endOfStream; int endOfAudio; + int skipSamplesSet; // flag to know skip samples were manually added from vgmstream // Seeking is not ideal, so rollback is necessary int samplesToDiscard;