Updated VGMStream to r1050-352-g698269d.

CQTexperiment
Christopher Snowhill 2017-03-12 22:20:40 -07:00
parent 01b91961ff
commit 786868d191
44 changed files with 4309 additions and 2988 deletions

View File

@ -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 */,

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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 切り替え */
}
}

View File

@ -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

View File

@ -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"},

View File

@ -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

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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*/

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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",

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;