/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
////																					 ////
////																					 ////
////									- HMp3.cpp -    								 ////
////																					 ////
////																					 ////
////				Implémentation des fonctions SCOL de la librairie sonore			 ////
////									 Version  1.0									 ////
////																					 ////
////								  Hilaire Verschuere								 ////
////																					 ////
////																					 ////
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////


#include "../Basic/ZooScene.h"


//-----------------------------------------------------------------------------
// Name: HMp3::HMp3()
// Desc: Constructs the class.  Call Open() to open a wave file for reading.  
//       Then call Read() as needed.  Calling the destructor or Close() 
//       will close the file.  
//-----------------------------------------------------------------------------
HMp3::HMp3( bool addToList )
	 :HSound( addToList )
{
	Mp3Type				= true ;
    m_dwSize			= 0 ;
	input_file			= NULL ;
	cur_mp3info.length	= 0 ;
	decodeSize			= 0 ;
	nbMP3Err			= 0 ;
	nbMP3OK				= 0 ;

	memset( &cur_mp3info, 0, sizeof(mp3info) ) ;
	m_mp3 = new struct mpstr ;
	InitMP3(m_mp3) ;
}





//-----------------------------------------------------------------------------
// Name: HMp3::HMp3()
// Desc: Copy Construct
//-----------------------------------------------------------------------------
HMp3::HMp3( HMp3 &h ) 
	 :HSound( h )
{
	Mp3Type				= true ;
    m_dwSize			= h.m_dwSize ;
	cur_mp3info.length	= h.GetSizeMs() ; 
}




//-----------------------------------------------------------------------------
// Name: Hmp3::~HMp3()
// Desc: Destructs the class
//-----------------------------------------------------------------------------
HMp3::~HMp3()
{
	Stop() ;
	SAFE_DELETE( pHSndNotify ) ;
	Close();
}







//-----------------------------------------------------------------------------
// Name: HMp3::Open()
// Desc: Opens a mp3 file for reading
//-----------------------------------------------------------------------------
HRESULT HMp3::Open( char* strFileName )
{
	if( strFileName == NULL )  return S_FALSE;

	m_fileName = (char*)new char[ strlen( strFileName ) + 1 ] ;
	strcpy( m_fileName, strFileName ) ;
	const int nBitsPerSample = 16 ;

	SAFE_DELETE_ARRAY( m_pwfx );
	Close() ;

	
	input_file = CreateFile( m_fileName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if( input_file == INVALID_HANDLE_VALUE ) return S_FALSE ;
	if( !GetMp3Info( input_file, &cur_mp3info, &current_id3, &current_vbrtag ) ) return S_FALSE ;

	if	   ( cur_mp3info.freq==16000 || cur_mp3info.freq==22050 || cur_mp3info.freq==24000 ) decodeSize = cur_mp3info.nch * OBSIZE / 4 ;
	else if( cur_mp3info.freq==32000 || cur_mp3info.freq==44100 || cur_mp3info.freq==48000 ) decodeSize = cur_mp3info.nch * OBSIZE / 2 ;
	else
	{
		MMechostr(1, "Mp3 file not supported, frequency must be 16000, 22050, 32000, 24000, 44100 or 48000 Hz.") ;
		return S_FALSE ;
	}
	
	//GetFileSize( input_file, NULL ) ;

	// setup format
	m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
	memset(m_pwfx, 0, sizeof(PCMWAVEFORMAT)) ;
	m_pwfx->wFormatTag		= WAVE_FORMAT_PCM ;
	m_pwfx->nChannels		= cur_mp3info.nch ;
	m_pwfx->wBitsPerSample	= nBitsPerSample ;
	m_pwfx->nSamplesPerSec	= cur_mp3info.freq ;
	m_pwfx->nBlockAlign		= ( nBitsPerSample / 8 ) * m_pwfx->nChannels ;
	m_pwfx->nAvgBytesPerSec	= m_pwfx->nSamplesPerSec * m_pwfx->nBlockAlign ;
	m_pwfx->cbSize			= 0 ;

	m_dwSize = MsToChar( m_pwfx, cur_mp3info.length ) ;

	SetFilePointer( input_file, cur_mp3info.hpos, NULL, FILE_BEGIN );

	if( !m_mp3 )
	{
		m_mp3 = new struct mpstr ;
		InitMP3(m_mp3) ;
	}

	// set the notification size
	m_dwNotifySize = decodeSize * m_pwfx->nBlockAlign * 4 ;

	return S_OK ;

}




//-----------------------------------------------------------------------------
// Name: HMp3::OpenNext()
// Desc: Opens next mp3 file for reading
//-----------------------------------------------------------------------------
HRESULT HMp3::OpenNext( char* strFileName )
{
	if( strFileName == NULL )  return S_FALSE;

	SAFE_DELETE_ARRAY( m_fileName ) ;
	m_fileName = (char*)new char[ strlen( strFileName ) + 1 ] ;
	strcpy( m_fileName, strFileName ) ;
	const int nBitsPerSample = 16 ;

	SAFE_DELETE_ARRAY( m_pwfx );
	Close() ;
	
	input_file = CreateFile( m_fileName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if( input_file == INVALID_HANDLE_VALUE ) return S_FALSE ;
	if( !GetMp3Info( input_file, &cur_mp3info, &current_id3, &current_vbrtag ) ) return S_FALSE ;

	// setup format
	m_pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
	memset(m_pwfx, 0, sizeof(PCMWAVEFORMAT)) ;
	m_pwfx->wFormatTag		= WAVE_FORMAT_PCM ;
	m_pwfx->nChannels		= cur_mp3info.nch ;
	m_pwfx->wBitsPerSample	= nBitsPerSample ;
	m_pwfx->nSamplesPerSec	= cur_mp3info.freq ;
	m_pwfx->nBlockAlign		= ( nBitsPerSample / 8 ) * m_pwfx->nChannels ;
	m_pwfx->nAvgBytesPerSec	= m_pwfx->nSamplesPerSec * m_pwfx->nBlockAlign ;
	m_pwfx->cbSize			= 0 ;

	m_nextSize = MsToChar( m_pwfx, cur_mp3info.length ) ;

	SetFilePointer( input_file, cur_mp3info.hpos, NULL, FILE_BEGIN );

	if( !m_mp3 )
	{
		m_mp3 = new struct mpstr ;
		InitMP3(m_mp3) ;
	}

	char* buffer = new char[ 2*decodeSize ] ;
	Read( buffer, 2*decodeSize ) ;
	SAFE_DELETE_ARRAY( buffer ) ;

	return S_OK ;
}



//-----------------------------------------------------------------------------
// Name: CWaveFile::Close()
// Desc: Closes the Mp3 file 
//-----------------------------------------------------------------------------
HRESULT HMp3::Close()
{
	SAFE_CLOSE_HANDLE( input_file ) ;
	if( m_mp3 ) ExitMP3( m_mp3 ) ;
	SAFE_DELETE( m_mp3 ) ;

	return S_OK ;
}





//-----------------------------------------------------------------------------
// Name: HMp3::Read()
// Desc: Opens a mp3 file for reading
//-----------------------------------------------------------------------------
DWORD HMp3::Read( char* pBuffer, DWORD sizeToRead )
{
	if( !pBuffer ) return S_FALSE;

	int nbFrameToRead = sizeToRead / decodeSize ;
	
	char* bufOut = pBuffer ;
	int nextFrame = 0 ;
	unsigned long sizeRead = 0 ;
	unsigned long i = 0 ;
	int sizeOut  = 0 ;
	unsigned long sizeIn = 1 ;
	int decodeState = MP3_NEED_MORE ;
	

	while( (i < nbFrameToRead) && ( sizeIn || m_mp3->bsize ) )
	{
		ReadFile( input_file, bufIn, (m_mp3->bsize > OBSIZE ? 0 : FBSIZE), &sizeIn, NULL ) ;
		
		decodeState = decodeMP3( m_mp3, bufIn, sizeIn, bufOut, OBSIZE, &sizeOut ) ;

		while( decodeState != MP3_OK ) 
		{
			if( decodeState == MP3_ERR )
			{
				if( ++nbMP3Err > MAX_MP3_ERROR )
				{
					nbMP3OK = 0 ;
					return -1 ;
				}
	
				nextFrame = GetNextFrame( SetFilePointer( input_file, (1-sizeIn), NULL, FILE_CURRENT ) ) ;
				if( nextFrame == -1 ) return sizeRead ;
				
				ExitMP3( m_mp3 ) ;
				InitMP3( m_mp3 ) ;

				SetFilePointer( input_file, nextFrame, NULL, FILE_BEGIN ) ;
			}
		
			ReadFile( input_file, bufIn, FBSIZE, &sizeIn, NULL );
			if( !sizeIn ) return sizeRead ;
						
			decodeState = decodeMP3( m_mp3, bufIn, sizeIn, bufOut, OBSIZE, &sizeOut ) ;
		}
		
		if( ++nbMP3OK > MIN_MP3_OK )
		{
			nbMP3Err = 0 ;
			nbMP3OK = 0 ;
		}

		if( sizeOut ) i++ ;
		sizeRead += sizeOut ;
		bufOut += sizeOut ;
	}

	if( sizeRead < sizeToRead ) {  sizeRead -= sizeOut ; }

	return sizeRead ;
}





//-----------------------------------------------------------------------------
// Name: HMp3::ResetFile()
// Desc: Reading starts from the beginning of the file again 
//-----------------------------------------------------------------------------
HRESULT HMp3::ResetFile()
{
	if( !input_file ) return S_FALSE ;

	ExitMP3( m_mp3 ) ;
	
	SetFilePointer(input_file,cur_mp3info.hpos,NULL,FILE_BEGIN);

	InitMP3( m_mp3 ) ;

	nbMP3Err = 0 ;
	nbMP3OK	 = 0 ;

	return S_OK ;
}





//-----------------------------------------------------------------------------
// Name: HMp3::SetFilePosition()
// Desc: Seeking into the mp3 file
// position in bytes
//-----------------------------------------------------------------------------
HRESULT	HMp3::SetFilePosition( long position )
{	
	int offs = cur_mp3info.hpos ;

	// Check for variable bitrate
	if (cur_mp3info.hasVbrtag)
	{
		position = (long)( ( (float)position / (float)m_dwSize ) * 100.0 ) ;
		offs += SeekPoint( current_vbrtag.toc, cur_mp3info.nbytes, position ) ;
	}
	else
	{
		position = (long)( ( (float)position  / (float)m_dwSize ) * cur_mp3info.nbytes ) ;
		offs += position ;
	}

	position = GetNextFrame( offs ) ;
	if( position == -1 ) return S_FALSE ;
	
	ExitMP3( m_mp3 ) ;
	InitMP3( m_mp3 ) ;

	nbMP3Err = 0 ;
	nbMP3OK	 = 0 ;
    
	SetFilePointer( input_file, position, NULL, FILE_BEGIN ) ;

	return S_OK ;
}


//-----------------------------------------------------------------------------
// Name: HMp3::GetNextFrame()
// Desc: Search the next frame in the mp3 file
//-----------------------------------------------------------------------------
long HMp3::GetNextFrame( long position )
{
	unsigned long head ;
	unsigned long len ;
	int hpos ;

	SetFilePointer( input_file, position, NULL, FILE_BEGIN );

	for( hpos=0; hpos<65536; hpos++ )
	{
		ReadFile( input_file, 3+(char*)&head, 1, &len, NULL );
		ReadFile( input_file, 2+(char*)&head, 1, &len, NULL );
		ReadFile( input_file, 1+(char*)&head, 1, &len, NULL );
		ReadFile( input_file, 0+(char*)&head, 1, &len, NULL );
		if ( len == 0 || head_check2(head,&cur_mp3info) ) break;
		SetFilePointer( input_file, -3, NULL, FILE_CURRENT );
	}

	if (hpos == 65536 || len == 0 ) return -1 ;

	return position+hpos ;
}




//-----------------------------------------------------------------------------
// Name: HMp3::GetNextFrame()
// Desc: Search the next frame in the buffer of data
//-----------------------------------------------------------------------------
long HMp3::GetFirstFrame( unsigned char* buffer, long size )
{
	unsigned char* endBuffer = buffer + size ;
	unsigned char* ptr = buffer ;

	while( ptr<endBuffer && !head_check( ExtractI4( ptr ) ) ) ptr++ ;

	if( ptr==endBuffer ) return -1 ;

	return (long)(ptr - buffer) ;
}


//-----------------------------------------------------------------------------
// Name: SameProperties( char* strFileName )	
// Desc: Tell if strFileName has same properties than current file 
//-----------------------------------------------------------------------------
bool HMp3::SameProperties( char* strFileName )
{
	if( strFileName == NULL )  return false;

	const int nBitsPerSample = 16 ;
	
	HANDLE file = CreateFile( strFileName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if( file == INVALID_HANDLE_VALUE ) return false ;

	mp3info			mp3_info ;
	VBRTAGDATA		vbrtag;
	id3tag			id3;

	if( !GetMp3Info( file, &mp3_info, &id3, &vbrtag ) ) return false ;

	// setup format
	WAVEFORMATEX* pwfx = (WAVEFORMATEX*)new CHAR[ sizeof(WAVEFORMATEX) ];
	memset(pwfx, 0, sizeof(PCMWAVEFORMAT)) ;
	pwfx->wFormatTag		= WAVE_FORMAT_PCM ;
	pwfx->nChannels			= mp3_info.nch ;
	pwfx->wBitsPerSample	= nBitsPerSample ;
	pwfx->nSamplesPerSec	= mp3_info.freq ;
	pwfx->nBlockAlign		= ( nBitsPerSample / 8 ) * pwfx->nChannels ;
	pwfx->nAvgBytesPerSec	= pwfx->nSamplesPerSec * pwfx->nBlockAlign ;
	pwfx->cbSize			= 0 ;

	if( *pwfx != *m_pwfx )
	{
		MMechostr( 0, "Wave file %s have different audio properties than %s.", strFileName, m_fileName ) ;
		return false ;
	}

	SAFE_DELETE_ARRAY( pwfx ) ;
	SAFE_CLOSE_HANDLE( file ) ;

	return true ;
}



//-----------------------------------------------------------------------------
// Name: IsAMp3File(char* fileName)
// Desc: Tell if the file is a mp3 file.
//-----------------------------------------------------------------------------
bool HMp3::IsAMp3File(char* fileName)
{
	VBRTAGDATA	vbrtag ;
	mp3info		mp3info ;
	id3tag		id3 ;
 
	HANDLE file = CreateFile( fileName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	bool res = GetMp3Info( file, &mp3info, &id3, &vbrtag ) ;
	SAFE_CLOSE_HANDLE( file ) ;

	return res ;
}



//-----------------------------------------------------------------------------
// Name: HMp3::ExtractI4()
// Desc: Get the header of a frame
//-----------------------------------------------------------------------------
int HMp3::ExtractI4(unsigned char *buf)
{
	int x;
	x = buf[0];
	x <<= 8;
	x |= buf[1];
	x <<= 8;
	x |= buf[2];
	x <<= 8;
	x |= buf[3];
	return x;
}



//-----------------------------------------------------------------------------
// Name: HMp3::head_check()
// Desc: Check the head of a frame and tell if it is a real mp3 frame
//-----------------------------------------------------------------------------
bool HMp3::head_check(unsigned long head)
{
    if ((head & 0xffe00000) != 0xffe00000) return false ;
    if (!((head>>17)&3)) return false ;
    if (((head>>12)&0xf) == 0xf) return false ;
    if (((head>>10)&0x3) == 0x3 ) return false ;
    //if ((head & 0xffff0000) == 0xfffe0000) return FALSE;
	if ((4-((head>>17)&3)) != 3) return false ;
    return true ;
}



//-----------------------------------------------------------------------------
// Name: HMp3::head_check2()
// Desc: Check the head of a frame and compare with the current mp3
//-----------------------------------------------------------------------------
bool HMp3::head_check2(unsigned long head, mp3info *info)
{
	int lsf,srate,nch,lay,mpeg25,freq,mode;

    if ((head & 0xffe00000) != 0xffe00000) return false;
    if (!((head>>17)&3)) return false ;
    if (((head>>12)&0xf) == 0xf) return false ;
    if (((head>>10)&0x3) == 0x3 ) return false ;
    //if ((head & 0xffff0000) == 0xfffe0000) return FALSE;

    if( head & (1<<20) ) 
	{
		lsf = (head & (1<<19)) ? 0x0 : 0x1;
		mpeg25 = 0;
    } 
	else 
	{
		lsf = 1;
		mpeg25 = 1;
    }

	if(mpeg25) srate = 6 + ((head>>10)&0x3);
    else srate = ((head>>10)&0x3) + (lsf*3);

    mode  = ((head>>6)&0x3);
	freq  = freqs[srate];
    nch   = (mode == MPG_MD_MONO) ? 1 : 2;
	lay   = 4-((head>>17)&3);

	if ( lay!=3 || nch!=info->nch || freq!=info->freq ) return false;

    return true ;
}


//-----------------------------------------------------------------------------
// Name: HMp3::getframeinfo()
// Desc: Get informations on a mp3 frame
//-----------------------------------------------------------------------------
int HMp3::GetFrameInfo( char* buffer, long size )
{
	unsigned long head ;
	int lsf, srate, framesize ;
	int bitrate_index,mode,nch,lay,extension,mpeg25,padding ;

	long framePos = GetFirstFrame( (unsigned char*)buffer, size ) ;

	head = ExtractI4( (unsigned char*)(buffer+framePos) ) ;

	if( !head ) return 0 ;

    if( head & (1<<20) ) 
	{
		lsf = (head & (1<<19)) ? 0x0 : 0x1 ;
		mpeg25 = 0 ;
    } 
	else 
	{
		lsf = 1 ;
		mpeg25 = 1 ;
    }

    bitrate_index = ((head>>12)&0xf) ;
	padding       = ((head>>9)&0x1) ;
    mode          = ((head>>6)&0x3) ;
    nch           = (mode == MPG_MD_MONO) ? 1 : 2 ;
	lay           = 4-((head>>17)&3) ;
	extension     = ((head>>8)&0x1) ;

	if(mpeg25)	srate = 6 + ((head>>10)&0x3) ;
    else		srate = ((head>>10)&0x3) + (lsf*3) ;

	if (lay != 3) return 0 ;

	cur_mp3info.lsf  = lsf;
	if( freqs[srate] == 0 ) return 0 ;
	cur_mp3info.freq = freqs[srate];
	cur_mp3info.nch  = nch;
	
	framesize = (int)((float)(tabsel_123[lsf][lay-1][bitrate_index]*144000)/(float)(freqs[srate]<<lsf)+padding) ;
	if( framesize == 0 ) return 0 ;
	cur_mp3info.length = (int)(((float)cur_mp3info.nbytes/(float)framesize*576.0*(float)(lsf?1:2)/(float)freqs[srate]) * 1000.0);
	cur_mp3info.hasVbrtag = 0;
	cur_mp3info.bitrate = tabsel_123[lsf][lay-1][bitrate_index]*1000;

	return framesize ; 
}





//-----------------------------------------------------------------------------
// Name: HMp3::GetMp3Info()
// Desc: Get informations on a mp3 file
//-----------------------------------------------------------------------------
bool HMp3::GetMp3Info(HANDLE file, mp3info *info, id3tag *id3, VBRTAGDATA *vbr)
{
	unsigned long head, len ;
	int lsf, srate ;
	int bitrate_index,mode,nch,lay,extension,mpeg25,padding ;
	int hpos, i ;

	info->nbytes = GetFileSize( file, NULL );
	hpos = 0;

	// skip ID3v2 tag
	{
		unsigned char c1,c2,c3,c4;

		ReadFile(file,&c1,1,&len,NULL);
		ReadFile(file,&c2,1,&len,NULL);
		ReadFile(file,&c3,1,&len,NULL);
		ReadFile(file,&c4,1,&len,NULL);

		if (c1 == 'I' && c2 == 'D' && c3 == '3' && c4 == 2) {
			SetFilePointer(file,6,NULL,FILE_BEGIN);
			ReadFile(file,&c1,1,&len,NULL);
			ReadFile(file,&c2,1,&len,NULL);
			ReadFile(file,&c3,1,&len,NULL);
			ReadFile(file,&c4,1,&len,NULL);
			hpos = c1*2097152+c2*16384+c3*128+c4;
		}
	}

	for(i=0;i<65536;i++)
	{
		SetFilePointer(file,hpos+i,NULL,FILE_BEGIN);
		ReadFile(file,3+(char *)&head,1,&len,NULL);
		ReadFile(file,2+(char *)&head,1,&len,NULL);
		ReadFile(file,1+(char *)&head,1,&len,NULL);
		ReadFile(file,0+(char *)&head,1,&len,NULL);
		if (len == 0 || head_check(head)) break;
	}

	if (len == 0 || i == 65536) return false ;

	hpos += i;

	info->hpos = hpos;
	info->nbytes -= hpos;

	// read ID3 tag

	SetFilePointer(file,-128,NULL,FILE_END);
	ReadFile(file,id3,128,&len,NULL);
	if (strncmp(id3->tag,"TAG",3) == 0) info->nbytes -= 128;
	
	//SetFilePointer(file,hpos,NULL,FILE_BEGIN);

    if( head & (1<<20) ) 
	{
		lsf = (head & (1<<19)) ? 0x0 : 0x1;
		mpeg25 = 0;
    } 
	else 
	{
		lsf = 1;
		mpeg25 = 1;
    }

    bitrate_index = ((head>>12)&0xf);
	padding       = ((head>>9)&0x1);
    mode          = ((head>>6)&0x3);
    nch           = (mode == MPG_MD_MONO) ? 1 : 2;
	lay           = 4-((head>>17)&3);
	extension     = ((head>>8)&0x1);

	if(mpeg25)	srate = 6 + ((head>>10)&0x3);
    else		srate = ((head>>10)&0x3) + (lsf*3);

	if (lay != 3) return false;

	// read VBR tag

	SetFilePointer(file,hpos,NULL,FILE_BEGIN);

	info->lsf  = lsf;
	if( freqs[srate] == 0 ) return false ;
	info->freq = freqs[srate];
	info->nch  = nch;
	
	unsigned char buf[VBRHEADERSIZE+36];

	ReadFile(file,&buf,VBRHEADERSIZE+36,&len,NULL);
	if (GetVbrTag(vbr,buf)) 
	{
		int cur_bitrate = (int)(vbr->bytes*8/(vbr->frames*576.0*(lsf?1:2)/freqs[srate]));
		info->length = (vbr->frames*576.0*(lsf?1:2)/freqs[srate]) * 1000 ;
		info->nbytes = vbr->bytes;
		info->hasVbrtag = 1;
		info->bitrate = cur_bitrate;
	} 
	else 
	{
		int framesize = (int)((float)(tabsel_123[lsf][lay-1][bitrate_index]*144000)/(float)(freqs[srate]<<lsf)+padding ) ;
		if( framesize == 0 ) return false ;
		info->length = (int)(((float)info->nbytes/(float)framesize*576.0*(float)(lsf?1:2)/(float)freqs[srate]) * 1000.0) ;
		info->hasVbrtag = 0;
		info->bitrate = tabsel_123[lsf][lay-1][bitrate_index]*1000;
	}

	return true ; 
}



//-----------------------------------------------------------------------------
// Name: HMp3::GetVbrTag()
// Desc: Get informations on a variable bitrate file
//-----------------------------------------------------------------------------
int HMp3::GetVbrTag( VBRTAGDATA *pTagData, unsigned char *buf)
{
	int			i, head_flags;
	int			h_id, h_mode, h_sr_index;
	static int	sr_table[4] = { 44100, 48000, 32000, 99999 };

	pTagData->flags = 0;     

	h_id       = (buf[1] >> 3) & 1;
	h_sr_index = (buf[2] >> 2) & 3;
	h_mode     = (buf[3] >> 6) & 3;

	if( h_id ) 
	{
		if( h_mode != 3 )	buf+=(32+4);
		else				buf+=(17+4);
	}
	else
	{
		if( h_mode != 3 ) buf+=(17+4);
		else              buf+=(9+4);
	}
	
	if( buf[0] != VBRTag[0] ) return 0;
	if( buf[1] != VBRTag[1] ) return 0;
	if( buf[2] != VBRTag[2] ) return 0;
	if( buf[3] != VBRTag[3] ) return 0;

	buf+=4;

	pTagData->h_id = h_id;

	pTagData->samprate = sr_table[h_sr_index];

	if( h_id == 0 )
		pTagData->samprate >>= 1;

	head_flags = pTagData->flags = ExtractI4(buf); buf+=4;

	if( head_flags & FRAMES_FLAG ) pTagData->frames   = ExtractI4(buf); buf+=4;

	if( head_flags & BYTES_FLAG )  pTagData->bytes = ExtractI4(buf); buf+=4;

	if( head_flags & TOC_FLAG )
	{
		if( pTagData->toc != NULL )
		{
			for( i=0; i<100; i++ )	pTagData->toc[i] = buf[i];
		}
		buf+=100;
	}

	pTagData->vbr_scale = -1;

	if( head_flags & VBR_SCALE_FLAG ) pTagData->vbr_scale = ExtractI4(buf); buf+=4;

	return 1;
}



//-----------------------------------------------------------------------------
// Name: HMp3::SeekPoint()
// Desc: Seek in a variable bitrate file
//-----------------------------------------------------------------------------
int HMp3::SeekPoint(unsigned char TOC[100], int file_bytes, float percent)
{
	/* interpolate in TOC to get file seek point in bytes */
	int a ;
	float fa, fb, fx;

	if( percent < (float)0.0 )   percent = (float)0.0;
	if( percent > (float)100.0 ) percent = (float)100.0;

	a = (int)percent;
	if( a > 99 ) a = 99;
	fa = TOC[a];
	if( a < 99 ) fb = TOC[a+1];
	else         fb = (float)256.0;

	fx = fa + (fb-fa)*(percent-a); 

	// return the seekpoint
	return (int)(((float)(1.0/256.0))*fx*file_bytes); 
}


