Updated VGMStream to r1050-352-g698269d.
parent
01b91961ff
commit
786868d191
|
@ -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 = "<group>"; };
|
||||
8323894F1D2246C300482226 /* hca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca.c; sourceTree = "<group>"; };
|
||||
832389511D224C0800482226 /* hca_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hca_decoder.c; sourceTree = "<group>"; };
|
||||
83299FC51E76606B003A3242 /* ffmpeg_decoder_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ffmpeg_decoder_utils.c; sourceTree = "<group>"; };
|
||||
83299FCA1E76607A003A3242 /* header.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = header.c; sourceTree = "<group>"; };
|
||||
83299FCB1E76607A003A3242 /* header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = header.h; sourceTree = "<group>"; };
|
||||
83299FCE1E7660C7003A3242 /* bik.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bik.c; sourceTree = "<group>"; };
|
||||
83299FCF1E7660C7003A3242 /* dsp_adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dsp_adx.c; sourceTree = "<group>"; };
|
||||
834D3A6D19F47C98001C54F6 /* g1l.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g1l.c; sourceTree = "<group>"; };
|
||||
8350C0541E071881009E0A93 /* xma.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xma.c; sourceTree = "<group>"; };
|
||||
8350C0591E071990009E0A93 /* ps2_svag_snk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_svag_snk.c; sourceTree = "<group>"; };
|
||||
|
@ -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 */,
|
||||
|
|
|
@ -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<l;k++){
|
||||
for(i=0;i<8;i++){
|
||||
for(j=0;j<0x80;j++){
|
||||
for(k=0,l=hca->_channelCount;k<l;k++){
|
||||
f=hca->_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<sizeof(stHeader))return -1;
|
||||
|
||||
// HCA
|
||||
if((clData_CheckBit(&d,32)&0x7F7F7F7F)=='HCA\0'){
|
||||
if((clData_CheckBit(&d,32)&0x7F7F7F7F)==0x48434100){/*'HCA\0'*/
|
||||
clData_AddBit(&d,32);
|
||||
hca->_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;j<count1;j++){
|
||||
for(k=0;k<count2;k++){
|
||||
float a=*(s1++);
|
||||
|
@ -1698,10 +1704,9 @@ void stChannel_Decode5(stChannel *ch,int index){
|
|||
for(i=0;i<0x80;i++)*(d++)=*(s++);
|
||||
s=(const float *)stChannel_Decode5_list3Int;d=ch->wave[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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -1,142 +1,87 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
#include <string.h>
|
||||
#include <mpg123/mpg123.h>
|
||||
#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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 切り替え */
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
#include <string.h>
|
||||
#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
|
|
@ -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_ */
|
|
@ -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;c<channel_count;c++) {
|
||||
for (i=0;i<16;i++) {
|
||||
vgmstream->ch[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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
|
|
@ -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;j<vgmstream->channels;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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;j<i;j++)
|
||||
adx_next_key(&vgmstream->ch[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; i<channel_count; i++) {
|
||||
vgmstream->ch[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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;j<i;j++)
|
||||
adx_next_key(&vgmstream->ch[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
|
|
|
@ -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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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; i<channel_count; i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;c<ChannelCount;c++) {
|
||||
for (i=0;i<16;i++)
|
||||
{
|
||||
vgmstream->ch[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;i<ChannelCount;i++) {
|
||||
vgmstream->ch[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;i<channel_count;i++)
|
||||
{
|
||||
vgmstream->ch[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;i<channel_count;i++) {
|
||||
vgmstream->ch[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
|
||||
|
|
|
@ -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 <mpg123/mpg123.h>
|
||||
#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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;i<channel_count;i++) {
|
||||
int16_t (*read_16bit)(off_t , STREAMFILE*);
|
||||
/* bit 1 - little endian coefs */
|
||||
if ((coef_type & 2) == 0) {
|
||||
read_16bit = read_16bitBE;
|
||||
} else {
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
/* bit 0 - split coefs (2 arrays) */
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;i<channel_count;i++) {
|
||||
off_t chstart_offset = start_offset;
|
||||
|
||||
switch (coding) {
|
||||
case coding_PSX:
|
||||
case coding_PSX_badflags:
|
||||
case coding_PCM16BE:
|
||||
case coding_PCM16LE:
|
||||
case coding_SDX2:
|
||||
case coding_SDX2_int:
|
||||
case coding_DVI_IMA:
|
||||
case coding_IMA:
|
||||
case coding_PCM8:
|
||||
case coding_PCM8_U:
|
||||
case coding_PCM8_U_int:
|
||||
case coding_AICA:
|
||||
case coding_INT_DVI_IMA:
|
||||
case coding_INT_IMA:
|
||||
case coding_APPLE_IMA4:
|
||||
if (coding == coding_AICA) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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; j<vgmstream->channels; 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; i<channel_count; i++) {
|
||||
if (vgmstream->layout_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;
|
||||
}
|
||||
|
|
|
@ -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*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
|
|
@ -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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;i<channel_count;i++) {
|
||||
vgmstream->ch[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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;chan<vgmstream->channels;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;chan<vgmstream->channels;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;chan<vgmstream->channels;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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue