Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

LibraryBuilderInternals.cpp

Go to the documentation of this file.
00001 //
00002 // LibraryBuilderInternals.cpp
00003 //
00004 // Copyright (c) Shareaza Development Team, 2002-2005.
00005 // This file is part of SHAREAZA (www.shareaza.com)
00006 //
00007 // Shareaza is free software; you can redistribute it
00008 // and/or modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2 of
00010 // the License, or (at your option) any later version.
00011 //
00012 // Shareaza is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 // GNU General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with Shareaza; if not, write to the Free Software
00019 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 //
00021 
00022 #include "StdAfx.h"
00023 #include "Shareaza.h"
00024 #include "LibraryFolders.h"
00025 #include "LibraryBuilder.h"
00026 #include "LibraryBuilderInternals.h"
00027 
00028 #define _ID3_DEFINE_GENRES
00029 #include "Buffer.h"
00030 #include "Schema.h"
00031 #include "XML.h"
00032 #include "ID3.h"
00033 #include "Packet.h"
00034 #include "CollectionFile.h"
00035 
00036 #ifdef _DEBUG
00037 #undef THIS_FILE
00038 static char THIS_FILE[]=__FILE__;
00039 #define new DEBUG_NEW
00040 #endif
00041 
00042 
00044 // CLibraryBuilderInternals construction
00045 
00046 CLibraryBuilderInternals::CLibraryBuilderInternals(CLibraryBuilder* pBuilder)
00047 {
00048         m_pBuilder = pBuilder;
00049 }
00050 
00051 CLibraryBuilderInternals::~CLibraryBuilderInternals()
00052 {
00053 }
00054 
00056 // CLibraryBuilderInternals load settings
00057 
00058 void CLibraryBuilderInternals::LoadSettings()
00059 {
00060         m_bEnableMP3    = theApp.GetProfileInt( _T("Library"), _T("ScanMP3"), TRUE );
00061         m_bEnableEXE    = theApp.GetProfileInt( _T("Library"), _T("ScanEXE"), TRUE );
00062         m_bEnableImage  = theApp.GetProfileInt( _T("Library"), _T("ScanImage"), TRUE );
00063         m_bEnableASF    = theApp.GetProfileInt( _T("Library"), _T("ScanASF"), TRUE );
00064         m_bEnableOGG    = theApp.GetProfileInt( _T("Library"), _T("ScanOGG"), TRUE );
00065         m_bEnableAPE    = theApp.GetProfileInt( _T("Library"), _T("ScanAPE"), TRUE );
00066         m_bEnableAVI    = theApp.GetProfileInt( _T("Library"), _T("ScanAVI"), TRUE );
00067         m_bEnablePDF    = theApp.GetProfileInt( _T("Library"), _T("ScanPDF"), TRUE );
00068         m_bEnableCHM    = theApp.GetProfileInt( _T("Library"), _T("ScanCHM"), TRUE );
00069 }
00070 
00072 // CLibraryBuilderInternals extract metadata (threaded)
00073 
00074 BOOL CLibraryBuilderInternals::ExtractMetadata( CString& strPath, HANDLE hFile, SHA1* pSHA1)
00075 {
00076         CString strType;
00077         
00078         int nExtPos = strPath.ReverseFind( '.' );
00079         if ( nExtPos > 0 ) strType = strPath.Mid( nExtPos );
00080         
00081         CharLower( strType.GetBuffer() );
00082         strType.ReleaseBuffer();
00083         
00084         if ( strType == _T(".mp3") )
00085         {
00086                 if ( ! m_bEnableMP3 ) return FALSE;
00087                 if ( ReadID3v2( hFile ) ) return TRUE;
00088                 if ( ReadID3v1( hFile ) ) return TRUE;
00089                 if ( ReadMP3Frames( hFile ) ) return TRUE;
00090                 return SubmitCorrupted();
00091         }
00092         else if ( strType == _T(".exe") || strType == _T(".dll") )
00093         {
00094                 if ( ! m_bEnableEXE ) return FALSE;
00095                 return ReadVersion( strPath );
00096         }
00097         else if ( strType == _T(".asf") || strType == _T(".wma") || strType == _T(".wmv") )
00098         {
00099                 if ( ! m_bEnableASF ) return FALSE;
00100                 return ReadASF( hFile );
00101         }
00102         else if ( strType == _T(".avi") )
00103         {
00104                 if ( ! m_bEnableAVI ) return FALSE;
00105                 return ReadAVI( hFile );
00106         }
00107         else if ( strType == _T(".mpg") || strType == _T(".mpeg") )
00108         {
00109                 if ( ! m_bEnableASF ) return FALSE;
00110                 return ReadMPEG( hFile );
00111         }
00112         else if ( strType == _T(".ogg") )
00113         {
00114                 if ( ! m_bEnableOGG ) return FALSE;
00115         return ReadOGG( hFile );
00116         }
00117         else if ( strType == _T(".ape") || strType == _T(".mac") || strType == _T(".apl") )
00118         {
00119                 if ( ! m_bEnableAPE ) return FALSE;
00120                 return ReadAPE( hFile );
00121         }
00122         else if ( strType == _T(".jpg") || strType == _T(".jpeg") )
00123         {
00124                 if ( ! m_bEnableImage ) return FALSE;
00125                 return ReadJPEG( hFile );
00126         }
00127         else if ( strType == _T(".gif") )
00128         {
00129                 if ( ! m_bEnableImage ) return FALSE;
00130                 return ReadGIF( hFile );
00131         }
00132         else if ( strType == _T(".png") )
00133         {
00134                 if ( ! m_bEnableImage ) return FALSE;
00135                 return ReadPNG( hFile );
00136         }
00137         else if ( strType == _T(".bmp") )
00138         {
00139                 if ( ! m_bEnableImage ) return FALSE;
00140                 return ReadBMP( hFile );
00141         }
00142         else if ( strType == _T(".pdf") )
00143         {
00144                 if ( ! m_bEnablePDF ) return FALSE;
00145                 return ReadPDF( hFile, strPath );
00146         }
00147         else if ( strType == _T(".co") || strType == _T(".collection") )
00148         {
00149                 return ReadCollection( hFile, pSHA1 );
00150         }
00151         else if ( strType == _T(".chm") )
00152         {
00153                 if ( ! m_bEnableCHM ) return FALSE;
00154                 return ReadCHM( hFile, strPath );
00155         }
00156         return FALSE;
00157 }
00158 
00160 // CLibraryBuilderInternals submit metadata (threaded)
00161 
00162 BOOL CLibraryBuilderInternals::SubmitMetadata( LPCTSTR pszSchemaURI, CXMLElement* pXML)
00163 {
00164         // Ignoring return value from submission
00165         m_pBuilder->SubmitMetadata( pszSchemaURI, pXML );
00166         return TRUE;
00167 }
00168 
00169 BOOL CLibraryBuilderInternals::SubmitCorrupted()
00170 {
00171         return m_pBuilder->SubmitCorrupted();
00172 }
00173 
00175 // CLibraryBuilderInternals ID3v1 (threaded)
00176 
00177 BOOL CLibraryBuilderInternals::ReadID3v1( HANDLE hFile, CXMLElement* pXML)
00178 {
00179         if ( GetFileSize( hFile, NULL ) < 128 ) return FALSE;
00180         
00181         ID3V1 pInfo;
00182         DWORD nRead;
00183 
00184         SetFilePointer( hFile, -128, NULL, FILE_END );
00185         ReadFile( hFile, &pInfo, sizeof(pInfo), &nRead, NULL );
00186         
00187         if ( nRead != sizeof(pInfo) ) return FALSE;
00188         if ( strncmp( pInfo.szTag, ID3V1_TAG, 3 ) ) return FALSE;
00189         
00190         BOOL bIsMP3 = ( pXML == NULL );
00191         if ( bIsMP3 ) pXML = new CXMLElement( NULL, _T("audio") );
00192         
00193         CopyID3v1Field( pXML, _T("title"), pInfo.szSongname, 30 );
00194         CopyID3v1Field( pXML, _T("artist"), pInfo.szArtist, 30 );
00195         CopyID3v1Field( pXML, _T("album"), pInfo.szAlbum, 30 );
00196         CopyID3v1Field( pXML, _T("year"), pInfo.szYear, 4 );
00197         
00198         if ( pInfo.nGenre < ID3_GENRES )
00199         {
00200                 pXML->AddAttribute( _T("genre"), pszID3Genre[ pInfo.nGenre ] );
00201         }
00202         
00203         if ( pInfo.szComment[28] == 0 && pInfo.szComment[29] > 0 )
00204         {
00205                 CString strTrack;
00206                 strTrack.Format( _T("%i"), (int)pInfo.szComment[29] );
00207                 pXML->AddAttribute( _T("track"), strTrack );
00208                 CopyID3v1Field( pXML, _T("description"), pInfo.szComment, 28 );
00209         }
00210         else
00211         {
00212                 CopyID3v1Field( pXML, _T("description"), pInfo.szComment, 30 );
00213         }
00214         
00215         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
00216         
00217         if ( bIsMP3 )
00218         {
00219                 ScanMP3Frame( pXML, hFile, sizeof(pInfo) );
00220                 return SubmitMetadata( CSchema::uriAudio, pXML );
00221         }
00222         
00223         return TRUE;
00224 }
00225 
00226 BOOL CLibraryBuilderInternals::CopyID3v1Field(CXMLElement* pXML, LPCTSTR pszAttribute, LPCSTR pszValue, int nLength)
00227 {
00228         CString strValue;
00229         int nWide = MultiByteToWideChar( CP_ACP, 0, pszValue, nLength, NULL, 0 );
00230     LPWSTR pszOutput = strValue.GetBuffer( nWide + 1 );
00231         MultiByteToWideChar( CP_ACP, 0, pszValue, nLength, pszOutput, nWide );
00232         pszOutput[ nWide ] = 0;
00233         strValue.ReleaseBuffer();
00234         
00235         strValue.TrimLeft();
00236         strValue.TrimRight();
00237         if ( strValue.IsEmpty() ) return FALSE;
00238         
00239         pXML->AddAttribute( pszAttribute, strValue );
00240         
00241         return TRUE;
00242 }
00243 
00245 // CLibraryBuilderInternals ID3v2 (threaded)
00246 
00247 BOOL CLibraryBuilderInternals::ReadID3v2( HANDLE hFile)
00248 {
00249         ID3V2_HEADER pHeader;
00250         DWORD nRead;
00251         
00252         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
00253         ReadFile( hFile, &pHeader, sizeof(pHeader), &nRead, NULL );
00254         if ( nRead != sizeof(pHeader) ) return FALSE;
00255         
00256         if ( strncmp( pHeader.szTag, ID3V2_TAG, 3 ) ) return FALSE;
00257         if ( pHeader.nMajorVersion < 2 || pHeader.nMajorVersion > 4 ) return FALSE;
00258         if ( pHeader.nFlags & ~ID3V2_KNOWNMASK ) return FALSE;
00259         if ( pHeader.nFlags & ID3V2_UNSYNCHRONISED ) return FALSE;
00260         
00261         DWORD nBuffer = SWAP_LONG( pHeader.nSize );
00262         ID3_DESYNC_SIZE( nBuffer );
00263         
00264         if ( nBuffer > 1024 * 1024 * 2 ) return FALSE;
00265         
00266         BYTE* pBuffer   = new BYTE[ nBuffer ];
00267         BYTE* pRelease  = pBuffer;
00268         
00269         ReadFile( hFile, pBuffer, nBuffer, &nRead, NULL );
00270         if ( nRead != nBuffer )
00271         {
00272                 delete [] pRelease;
00273                 return FALSE;
00274         }
00275         
00276         if ( ( pHeader.nFlags & ID3V2_EXTENDEDHEADER ) && pHeader.nMajorVersion == 3 )
00277         {
00278                 if ( nBuffer < sizeof(ID3V2_EXTENDED_HEADER_1) )
00279                 {
00280                         delete [] pRelease;
00281                         return FALSE;
00282                 }
00283                 
00284                 ID3V2_EXTENDED_HEADER_1* pExtended = (ID3V2_EXTENDED_HEADER_1*)pBuffer;
00285                 pBuffer += sizeof(ID3V2_EXTENDED_HEADER_1);
00286                 nBuffer -= sizeof(ID3V2_EXTENDED_HEADER_1);
00287                 
00288                 pExtended->nSize = SWAP_LONG( pExtended->nSize );
00289                 
00290                 if ( nBuffer < pExtended->nSize )
00291                 {
00292                         delete [] pRelease;
00293                         return FALSE;
00294                 }
00295                 
00296                 pBuffer += pExtended->nSize;
00297                 nBuffer -= pExtended->nSize;
00298         }
00299         else if ( ( pHeader.nFlags & ID3V2_EXTENDEDHEADER ) && pHeader.nMajorVersion == 4 )
00300         {
00301                 if ( nBuffer < sizeof(ID3V2_EXTENDED_HEADER_2) )
00302                 {
00303                         delete [] pRelease;
00304                         return FALSE;
00305                 }
00306                 
00307                 ID3V2_EXTENDED_HEADER_2* pExtended = (ID3V2_EXTENDED_HEADER_2*)pBuffer;
00308                 pBuffer += sizeof(ID3V2_EXTENDED_HEADER_2);
00309                 nBuffer -= sizeof(ID3V2_EXTENDED_HEADER_2);
00310                 
00311                 pExtended->nSize = SWAP_LONG( pExtended->nSize );
00312                 ID3_DESYNC_SIZE( pExtended->nSize );
00313                 pExtended->nSize -= 6;
00314                 
00315                 if ( nBuffer < pExtended->nSize )
00316                 {
00317                         delete [] pRelease;
00318                         return FALSE;
00319                 }
00320                 
00321                 pBuffer += pExtended->nSize;
00322                 nBuffer -= pExtended->nSize;
00323         }
00324         
00325         CXMLElement* pXML = new CXMLElement( NULL, _T("audio") );
00326         
00327         while ( TRUE )
00328         {
00329                 DWORD nFrameSize = 0;
00330                 CHAR szFrameTag[5];
00331                 
00332                 if ( pHeader.nMajorVersion > 2 )
00333                 {
00334                         ID3V2_FRAME* pFrame = (ID3V2_FRAME*)pBuffer;
00335                         
00336                         if ( nBuffer < sizeof(*pFrame) ) break;
00337                         pBuffer += sizeof(*pFrame);
00338                         nBuffer -= sizeof(*pFrame);
00339                         
00340                         szFrameTag[0] = pFrame->szID[0];
00341                         szFrameTag[1] = pFrame->szID[1];
00342                         szFrameTag[2] = pFrame->szID[2];
00343                         szFrameTag[3] = pFrame->szID[3];
00344                         szFrameTag[4] = 0;
00345                         
00346                         nFrameSize = SWAP_LONG( pFrame->nSize );
00347                         if ( pHeader.nMajorVersion >= 4 ) ID3_DESYNC_SIZE( nFrameSize );
00348                         if ( pFrame->nFlags2 & ~ID3V2_KNOWNFRAME ) szFrameTag[0] = 0;
00349                 }
00350                 else
00351                 {
00352                         ID3V2_FRAME_2* pFrame = (ID3V2_FRAME_2*)pBuffer;
00353                         
00354                         if ( nBuffer < sizeof(*pFrame) ) break;
00355                         pBuffer += sizeof(*pFrame);
00356                         nBuffer -= sizeof(*pFrame);
00357                         
00358                         szFrameTag[0] = pFrame->szID[0];
00359                         szFrameTag[1] = pFrame->szID[1];
00360                         szFrameTag[2] = pFrame->szID[2];
00361                         szFrameTag[3] = szFrameTag[4] = 0;
00362                         nFrameSize = ( pFrame->nSize[0] << 16 ) | ( pFrame->nSize[1] << 8 ) | pFrame->nSize[2];
00363                 }
00364                 
00365                 if ( nBuffer < nFrameSize || ! szFrameTag[0] ) break;
00366                 
00367                 if ( strcmp( szFrameTag, "TIT2" ) == 0 || strcmp( szFrameTag, "TT2" ) == 0)
00368                 {
00369                         CopyID3v2Field( pXML, _T("title"), pBuffer, nFrameSize );
00370                 }
00371                 else if ( strcmp( szFrameTag, "TOPE" ) == 0 || strcmp( szFrameTag, "TOA" ) == 0 || strcmp( szFrameTag, "TPE1" ) == 0 || strcmp( szFrameTag, "TPE2" ) == 0 )
00372                 {
00373                         CopyID3v2Field( pXML, _T("artist"), pBuffer, nFrameSize );
00374                 }
00375                 else if ( strcmp( szFrameTag, "TALB" ) == 0 || strcmp( szFrameTag, "TOT" ) == 0 )
00376                 {
00377                         CopyID3v2Field( pXML, _T("album"), pBuffer, nFrameSize );
00378                 }
00379                 else if ( strcmp( szFrameTag, "TRCK" ) == 0 || strcmp( szFrameTag, "TRK" ) == 0 )
00380                 {
00381                         CopyID3v2Field( pXML, _T("track"), pBuffer, nFrameSize );
00382                 }
00383                 else if ( strcmp( szFrameTag, "TYER" ) == 0 || strcmp( szFrameTag, "TYE" ) == 0 )
00384                 {
00385                         CopyID3v2Field( pXML, _T("year"), pBuffer, nFrameSize );
00386                 }
00387                 else if ( strcmp( szFrameTag, "COMM" ) == 0 || strcmp( szFrameTag, "COM" ) == 0 )
00388                 {
00389                         CopyID3v2Field( pXML, _T("description"), pBuffer, nFrameSize, TRUE );
00390                 }
00391                 else if ( strcmp( szFrameTag, "TLEN" ) == 0 || strcmp( szFrameTag, "TLE" ) == 0 )
00392                 {
00393                         if ( CopyID3v2Field( pXML, _T("seconds"), pBuffer, nFrameSize ) )
00394                         {
00395                                 CString strMS = pXML->GetAttributeValue( _T("seconds"), _T("0") );
00396                                 int nMS;
00397                                 _stscanf( strMS, _T("%lu"), &nMS );
00398                                 strMS.Format( _T("%lu"), nMS / 1000 );
00399                                 pXML->AddAttribute( _T("seconds"), strMS );
00400                         }
00401                 }
00402                 else if ( strcmp( szFrameTag, "TCOP" ) == 0 || strcmp( szFrameTag, "TCR" ) == 0 )
00403                 {
00404                         CopyID3v2Field( pXML, _T("copyright"), pBuffer, nFrameSize );
00405                 }
00406                 else if ( strcmp( szFrameTag, "TCON" ) == 0 || strcmp( szFrameTag, "TCO" ) == 0 )
00407                 {
00408                         if ( CopyID3v2Field( pXML, _T("genre"), pBuffer, nFrameSize ) )
00409                         {
00410                                 CString strGenre = pXML->GetAttributeValue( _T("genre"), _T("") );
00411                                 
00412                                 while ( TRUE )
00413                                 {
00414                                         int nPos1 = strGenre.Find( '(' );
00415                                         if ( nPos1 < 0 ) break;
00416                                         int nPos2 = strGenre.Find( ')' );
00417                                         if ( nPos2 <= nPos1 ) break;
00418                                         
00419                                         CString strValue = strGenre.Mid( nPos1 + 1, nPos2 - nPos1 - 1 );
00420                                         int nGenre = 0;
00421                                         
00422                                         if ( strValue.CompareNoCase( _T("RX") ) == 0 )
00423                                         {
00424                                                 strValue = _T("Remix");
00425                                         }
00426                                         else if ( strValue.CompareNoCase( _T("CR") ) == 0 )
00427                                         {
00428                                                 strValue = _T("Cover");
00429                                         }
00430                                         else if ( _stscanf( strValue, _T("%i"), &nGenre ) == 1 && nGenre < ID3_GENRES )
00431                                         {
00432                                                 if ( _tcsistr( strGenre, pszID3Genre[ nGenre ] ) == NULL )
00433                                                 {
00434                                                         strValue = pszID3Genre[ nGenre ];
00435                                                 }
00436                                                 else
00437                                                 {
00438                                                         strValue.Empty();
00439                                                 }
00440                                         }
00441                                         else
00442                                         {
00443                                                 strValue = _T("[") + strValue + _T("]");
00444                                         }
00445                                         
00446                                         strGenre = strGenre.Left( nPos1 ) + strValue + strGenre.Mid( nPos2 + 1 );
00447                                 }
00448                                 
00449                                 Replace( strGenre, _T("["), _T("(") );
00450                                 Replace( strGenre, _T("]"), _T(")") );
00451                                 
00452                                 pXML->AddAttribute( _T("genre"), strGenre );
00453                         }
00454                 }
00455                 
00456                 pBuffer += nFrameSize;
00457                 nBuffer -= nFrameSize;
00458         }
00459         
00460         delete [] pRelease;
00461         
00462         ScanMP3Frame( pXML, hFile, 0 );
00463         
00464         return SubmitMetadata( CSchema::uriAudio, pXML );
00465 }
00466 
00467 BOOL CLibraryBuilderInternals::CopyID3v2Field(CXMLElement* pXML, LPCTSTR pszAttribute, BYTE* pBuffer, DWORD nLength, BOOL bSkipLanguage)
00468 {
00469         CString strValue;
00470         
00471         BYTE nEncoding = *pBuffer++;
00472         nLength--;
00473         
00474         if ( bSkipLanguage )
00475         {
00476                 if ( nLength < 3 ) return FALSE;
00477                 pBuffer += 3;
00478                 nLength -= 3;
00479                 if ( nLength > 0 && pBuffer[ 0 ] == 0 )
00480                 {
00481                         pBuffer += 1;
00482                         nLength -= 1;
00483                 }
00484         }
00485         
00486         if ( nEncoding == 0 )
00487         {
00488                 LPTSTR pszOutput = strValue.GetBuffer( nLength + 1 );
00489                 
00490         DWORD nOut = 0;
00491                 for ( DWORD nChar = 0 ; nChar < nLength ; nChar++, nOut++ )
00492                 {
00493                         pszOutput[ nOut ] = (TCHAR)pBuffer[ nChar ];
00494                         if ( pszOutput[ nOut ] == 0 ) break;
00495                 }
00496                 strValue.ReleaseBuffer( nOut );
00497                 
00498         }
00499         else if ( nEncoding == 1 && ( nLength & 1 ) == 0 && nLength >= 2 )
00500         {
00501                 nLength = ( nLength - 2 ) / 2;
00502                 LPTSTR pszOutput = strValue.GetBuffer( nLength + 1 );
00503                 
00504                 if ( pBuffer[0] == 0xFF && pBuffer[1] == 0xFE )
00505                 {
00506                         pBuffer += 2;
00507             DWORD nOut = 0;
00508                         for ( DWORD nChar = 0 ; nChar < nLength ; nChar++, nOut++ )
00509                         {
00510                                 pszOutput[ nOut ] = (TCHAR)pBuffer[ nChar*2+0 ] | ( (TCHAR)pBuffer[ nChar*2+1 ] << 8 );
00511                                 if ( pszOutput[ nOut ] == 0 ) break;
00512                         }
00513                         strValue.ReleaseBuffer( nOut );
00514                 }
00515                 else if ( pBuffer[0] == 0xFE && pBuffer[1] == 0xFF )
00516                 {
00517                         pBuffer += 2;
00518             DWORD nOut = 0;
00519                         for ( DWORD nChar = 0 ; nChar < nLength ; nChar++, nOut++ )
00520                         {
00521                                 pszOutput[ nOut ] = (TCHAR)pBuffer[ nChar*2+1 ] | ( (TCHAR)pBuffer[ nChar*2+0 ] << 8 );
00522                                 if ( pszOutput[ nOut ] == 0 ) break;
00523                         }
00524                         strValue.ReleaseBuffer( nOut );
00525                 }
00526                 else
00527                 {
00528                         strValue.ReleaseBuffer( 0 );
00529                         return FALSE;
00530                 }
00531         }
00532         else if ( nEncoding == 2 && ( nLength & 1 ) == 0 )
00533         {
00534                 nLength = nLength / 2;
00535                 LPTSTR pszOutput = strValue.GetBuffer( nLength + 1 );
00536                 
00537         DWORD nOut = 0;
00538                 for ( DWORD nChar = 0 ; nChar < nLength ; nChar++, nOut++ )
00539                 {
00540                         pszOutput[ nOut ] = (TCHAR)pBuffer[ nChar*2+1 ] | ( (TCHAR)pBuffer[ nChar*2+0 ] << 8 );
00541                         if ( pszOutput[ nOut ] == 0 ) break;
00542                 }
00543                 
00544                 strValue.ReleaseBuffer( nOut );
00545         }
00546         else if ( nEncoding == 3 )
00547         {
00548                 int nWide = MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pBuffer, nLength, NULL, 0 );
00549                 LPTSTR pszOutput = strValue.GetBuffer( nWide + 1 );
00550                 MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pBuffer, nLength, pszOutput, nWide );
00551                 pszOutput[ nWide ] = 0;
00552                 strValue.ReleaseBuffer();
00553         }
00554         
00555         strValue.TrimLeft();
00556         strValue.TrimRight();
00557         
00558         if ( strValue.GetLength() == 0 || _tcslen( strValue ) == 0 ) return FALSE;
00559         
00560         pXML->AddAttribute( pszAttribute, strValue );
00561         
00562         return TRUE;
00563 }
00564 
00566 // CLibraryBuilderInternals MP3 scan (threaded)
00567 
00568 BOOL CLibraryBuilderInternals::ReadMP3Frames( HANDLE hFile)
00569 {
00570         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
00571         
00572         CXMLElement* pXML = new CXMLElement( NULL, _T("audio") );
00573         
00574         if ( ScanMP3Frame( pXML, hFile, 0 ) )
00575         {
00576                 return SubmitMetadata( CSchema::uriAudio, pXML );
00577         }
00578         else
00579         {
00580                 delete pXML;
00581                 return FALSE;
00582         }
00583 }
00584 
00585 BOOL CLibraryBuilderInternals::ScanMP3Frame(CXMLElement* pXML, HANDLE hFile, DWORD nIgnore)
00586 {
00587         static DWORD nBitrateTable[16][5] =
00588         {
00589                 { 0, 0, 0, 0 },                                 { 32, 32, 32, 32, 8 },          { 64, 48, 40, 48, 16 },
00590                 { 96, 56, 48, 56, 24 },                 { 128, 64, 56, 64, 32 },        { 160, 80, 64, 80, 40 },
00591                 { 192, 96, 80, 96, 48 },                { 224, 112, 96, 112, 56 },      { 256, 128, 112, 128, 64 },
00592                 { 288, 160, 128, 144, 80 },             { 320, 192, 160, 160, 96 },     { 352, 224, 192, 176, 112 },
00593                 { 384, 256, 224, 192, 128 },    { 416, 320, 256, 224, 144 },{ 448, 384, 320, 256, 160 },
00594                 { 0, 0, 0, 0 }
00595         };
00596 
00597         static DWORD nFrequencyTable[4][4] =
00598         {
00599                 { 11025, 0, 22050, 44100 },
00600                 { 12000, 0,  24000, 48000 },
00601                 { 8000, 0, 16000, 32000 },
00602                 { 0, 0, 0, 0 }
00603         };
00604 
00605         BYTE nLayer                             = 0;
00606         BOOL bVariable                  = FALSE;
00607         __int64 nTotalBitrate   = 0;
00608         DWORD nBaseBitrate              = 0;
00609         DWORD nBaseFrequency    = 0;
00610         DWORD nFrameCount               = 0;
00611         DWORD nFrameSize                = 0;
00612         DWORD nHeader                   = 0;
00613 
00614         DWORD nRead;
00615         ReadFile( hFile, &nHeader, 4, &nRead, NULL );
00616         if ( nRead != 4 ) return FALSE;
00617         nHeader = SWAP_LONG( nHeader );
00618 
00619         for ( DWORD nSeek = 0 ; bVariable || ( nFrameCount < 16 && nSeek < 4096 ) ; nSeek++ )
00620         {
00621                 DWORD nTime = GetTickCount();
00622                 
00623                 if ( ( nHeader & 0xFFE00000 ) == 0xFFE00000 )
00624                 {
00625                         BYTE nVersion   = (BYTE)( ( nHeader & 0x00180000 ) >> 19 );
00626                         nLayer                  = (BYTE)( ( nHeader & 0x00060000 ) >> 17 );
00627                         BYTE nBitIndex  = (BYTE)( ( nHeader & 0x0000F000 ) >> 12 );
00628                         BYTE nFreqIndex = (BYTE)( ( nHeader & 0x00000C00 ) >> 10 );
00629                         BYTE nChannels  = (BYTE)( ( nHeader & 0x000000C0 ) >> 6 );
00630                         BOOL bPadding   = (BOOL)( nHeader & 0x0200 ) ? TRUE : FALSE;
00631                         
00632                         int nBitColumn = 0;
00633                         
00634                         if ( nVersion == 3 )
00635                         {
00636                                 if ( nLayer == 3 ) nBitColumn = 0;
00637                                 else if ( nLayer == 2 ) nBitColumn = 1;
00638                                 else if ( nLayer == 1 ) nBitColumn = 2;
00639                         }
00640                         else
00641                         {
00642                                 if ( nLayer == 3 ) nBitColumn = 3;
00643                                 else nBitColumn = 4;
00644                         }
00645                         
00646                         DWORD nBitrate          = nBitrateTable[ nBitIndex ][ nBitColumn ] * 1000;
00647                         DWORD nFrequency        = nFrequencyTable[ nFreqIndex ][ nVersion ];
00648                         
00649                         if ( ! nFrequency ) return FALSE;
00650                         
00651                         if ( nBaseBitrate )
00652                         {
00653                                 if ( nBaseBitrate != nBitrate ) bVariable = TRUE;
00654                         }
00655                         else
00656                         {
00657                                 nBaseBitrate    = nBitrate;
00658                                 nBaseFrequency  = nFrequency;
00659                         }
00660                         
00661                         nFrameSize = ( nLayer == 3 ) ? ( 12 * nBitrate / nFrequency + bPadding ) * 4
00662                                 : ( 144 * nBitrate / nFrequency + bPadding );
00663                         
00664                         if ( ! nFrameSize ) return FALSE;
00665                         
00666                         nTotalBitrate += nBitrate / 1000;
00667                         nFrameCount++;
00668                         
00669                         SetFilePointer( hFile, nFrameSize - 4, NULL, FILE_CURRENT );
00670                         ReadFile( hFile, &nHeader, 4, &nRead, NULL );
00671                         if ( nRead != 4 ) break;
00672                         nHeader = SWAP_LONG( nHeader );
00673                 }
00674                 else
00675                 {
00676                         nHeader <<= 8;
00677                         ReadFile( hFile, &nHeader, 1, &nRead, NULL );
00678                         if ( nRead != 1 ) break;
00679                 }
00680                 
00681                 if ( ! m_pBuilder->m_bPriority )
00682                 {
00683                         m_nSleep = ( GetTickCount() - nTime ) * 3;
00684                         if ( m_nSleep > 0 ) Sleep( m_nSleep );
00685                 }
00686                 
00687                 if ( ! m_pBuilder->m_bThread ) return FALSE;
00688         }
00689         
00690         if ( nFrameCount < 16 || ! nFrameSize ) return FALSE;
00691         
00692         if ( bVariable )
00693         {
00694                 nBaseBitrate = (DWORD)( nTotalBitrate / nFrameCount ) * 1000;
00695         }
00696         else
00697         {
00698                 DWORD dwFilePosition    = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );
00699                 DWORD dwFileSize                = GetFileSize( hFile, NULL );
00700                 DWORD dwMusicSize               = dwFileSize - dwFilePosition - nIgnore + 4;
00701                 nFrameCount += ( dwMusicSize / nFrameSize ) - 1;
00702         }
00703         
00704         DWORD nFrameTime        = ( nLayer == 3 ? 384 : 1152 ) * 100000 / nBaseFrequency;
00705         DWORD nTotalTime        = (DWORD)( (__int64)nFrameCount * (__int64)nFrameTime / 100000 );
00706         
00707         CString strValue;
00708         
00709         strValue.Format( bVariable ? _T("%lu~") : _T("%lu"), nBaseBitrate / 1000 );
00710         pXML->AddAttribute( _T("bitrate"), strValue );
00711         
00712         strValue.Format( _T("%lu"), nTotalTime );
00713         pXML->AddAttribute( _T("seconds"), strValue );
00714         
00715         strValue.Format( _T("%lu"), nBaseFrequency );
00716         pXML->AddAttribute( _T("sampleRate"), strValue );
00717         
00718         return TRUE;
00719 }
00720 
00722 // CLibraryBuilderInternals version information (threaded)
00723 
00724 BOOL CLibraryBuilderInternals::ReadVersion( LPCTSTR pszPath)
00725 {
00726         DWORD dwSize = GetFileVersionInfoSize( (LPTSTR)pszPath, &dwSize );
00727         if ( dwSize <= 152 ) return FALSE;
00728         
00729         BYTE* pBuffer = new BYTE[ dwSize ];
00730         
00731         if ( ! GetFileVersionInfo( (LPTSTR)pszPath, NULL, dwSize, pBuffer ) )
00732         {
00733                 delete [] pBuffer;
00734                 return FALSE;
00735         }
00736         
00737         WCHAR* pLanguage = (WCHAR*)pBuffer + 20 + 26 + 18 + 3;
00738         
00739         if ( wcslen( pLanguage ) != 8 )
00740         {
00741                 delete [] pBuffer;
00742                 return FALSE;
00743         }
00744         
00745         CXMLElement* pXML = new CXMLElement( NULL, _T("application") );
00746         
00747         pXML->AddAttribute( _T("os"), _T("Windows") );
00748         CopyVersionField( pXML, _T("title"), pBuffer, _T("ProductName") );
00749         CopyVersionField( pXML, _T("version"), pBuffer, _T("ProductVersion"), TRUE );
00750         CopyVersionField( pXML, _T("fileDescription"), pBuffer, _T("FileDescription") );
00751         CopyVersionField( pXML, _T("fileVersion"), pBuffer, _T("FileVersion"), TRUE );
00752         CopyVersionField( pXML, _T("originalFileName"), pBuffer, _T("OriginalFilename") );
00753         CopyVersionField( pXML, _T("company"), pBuffer, _T("CompanyName") );
00754         CopyVersionField( pXML, _T("copyright"), pBuffer, _T("LegalCopyright") );
00755         CopyVersionField( pXML, _T("comments"), pBuffer, _T("comments") );
00756         
00757         delete [] pBuffer;
00758 
00759         return SubmitMetadata( CSchema::uriApplication, pXML );
00760 }
00761 
00762 BOOL CLibraryBuilderInternals::CopyVersionField(CXMLElement* pXML, LPCTSTR pszAttribute, BYTE* pBuffer, LPCTSTR pszKey, BOOL bCommaToDot)
00763 {
00764         CString strValue = GetVersionKey( pBuffer, pszKey );
00765 
00766         if ( strValue.IsEmpty() ) return FALSE;
00767         
00768         if ( bCommaToDot )
00769         {
00770                 for ( int nPos = -1 ; ( nPos = strValue.Find( _T(", ") ) ) >= 0 ; )
00771                 {
00772                         strValue = strValue.Left( nPos ) + '.' + strValue.Mid( nPos + 2 );
00773                 }
00774         }
00775 
00776         pXML->AddAttribute( pszAttribute, strValue );
00777 
00778         return TRUE;
00779 }
00780 
00781 CString CLibraryBuilderInternals::GetVersionKey(BYTE* pBuffer, LPCTSTR pszKey)
00782 {
00783         CString strKey, strValue;
00784 
00785         WCHAR* pLanguage = (WCHAR*)pBuffer + 20 + 26 + 18 + 3;
00786 
00787         strKey = _T("\\StringFileInfo\\");
00788         strKey += pLanguage;
00789         strKey += _T("\\");
00790         strKey += pszKey;
00791 
00792         BYTE* pValue = NULL;
00793         DWORD dwSize = 0;
00794 
00795         if ( ! VerQueryValue( pBuffer, (LPTSTR)(LPCTSTR)strKey, (void**)&pValue, (UINT*)&dwSize ) )
00796                 return strValue;
00797         
00798         if ( pValue[1] )
00799                 strValue = (LPCSTR)pValue;
00800         else
00801                 strValue = (LPCTSTR)pValue;
00802 
00803         return strValue;
00804 }
00805 
00807 // CLibraryBuilderInternals JPEG (threaded)
00808 
00809 BOOL CLibraryBuilderInternals::ReadJPEG( HANDLE hFile)
00810 {
00811         DWORD nRead     = 0;
00812         WORD wMagic     = 0;
00813         BYTE nByte      = 0;
00814         
00815         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
00816         ReadFile( hFile, &wMagic, 2, &nRead, NULL );
00817         if ( nRead != 2 || wMagic != 0xD8FF ) return SubmitCorrupted();
00818         
00819         BYTE nBits = 0, nComponents = 0;
00820         WORD nWidth = 0, nHeight = 0;
00821         CString strComment;
00822         
00823         for ( DWORD nSeek = 512 ; nSeek > 0 ; nSeek-- )
00824         {
00825                 ReadFile( hFile, &nByte, 1, &nRead, NULL );
00826                 if ( nRead != 1 ) return FALSE;
00827                 if ( nByte != 0xFF ) continue;
00828 
00829                 while ( nByte == 0xFF )
00830                 {
00831                         ReadFile( hFile, &nByte, 1, &nRead, NULL );
00832                         if ( nRead != 1 ) return FALSE;
00833                 }
00834                 
00835                 ReadFile( hFile, &wMagic, 2, &nRead, NULL );
00836                 wMagic = ( wMagic >> 8 ) | ( wMagic << 8 );
00837                 if ( nRead != 2 || wMagic < 2 ) return FALSE;
00838 
00839                 switch ( nByte )
00840                 {
00841                 case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC5: case 0xC6: case 0xC7:
00842                 case 0xC9: case 0xCA: case 0xCB: case 0xCD: case 0xCE: case 0xCF:
00843                         ReadFile( hFile, &nBits, 1, &nRead, NULL );
00844                         if ( nRead != 1 ) return FALSE;
00845                         ReadFile( hFile, &nHeight, 2, &nRead, NULL );
00846                         if ( nRead != 2 ) return FALSE;
00847                         nHeight = ( nHeight >> 8 ) | ( nHeight << 8 );
00848                         ReadFile( hFile, &nWidth, 2, &nRead, NULL );
00849                         if ( nRead != 2 ) return FALSE;
00850                         nWidth = ( nWidth >> 8 ) | ( nWidth << 8 );
00851                         ReadFile( hFile, &nComponents, 1, &nRead, NULL );
00852                         if ( nRead != 1 ) return FALSE;
00853                         if ( wMagic < 8 ) return FALSE;
00854                         SetFilePointer( hFile, wMagic - 8, NULL, FILE_CURRENT );
00855                         break;
00856                 case 0xFE: case 0xEC:
00857                         if ( wMagic > 2 )
00858                         {
00859                                 CBuffer pComment;
00860                                 pComment.EnsureBuffer( wMagic - 2 );
00861                                 pComment.m_nLength = (DWORD)wMagic - 2;
00862                                 ReadFile( hFile, pComment.m_pBuffer, wMagic - 2, &nRead, NULL );
00863                                 strComment = pComment.ReadString( nRead );
00864                         }
00865                         break;
00866                 case 0xD9: case 0xDA:
00867                         nSeek = 1;
00868                         break;
00869                 default:
00870                         SetFilePointer( hFile, wMagic - 2, NULL, FILE_CURRENT );
00871                         break;
00872                 }
00873         }
00874 
00875         if ( nWidth == 0 || nHeight == 0 ) return FALSE;
00876 
00877         strComment.TrimLeft();
00878         strComment.TrimRight();
00879 
00880         for ( int nChar = 0 ; nChar < strComment.GetLength() ; nChar++ )
00881         {
00882                 if ( strComment[ nChar ] < 32 ) strComment.SetAt( nChar, '?' );
00883         }
00884 
00885         CXMLElement* pXML = new CXMLElement( NULL, _T("image") );
00886         CString strItem;
00887         
00888         strItem.Format( _T("%lu"), nWidth );
00889         pXML->AddAttribute( _T("width"), strItem );
00890         strItem.Format( _T("%lu"), nHeight );
00891         pXML->AddAttribute( _T("height"), strItem );
00892         
00893         if ( nComponents == 3 ) pXML->AddAttribute( _T("colors"), _T("16.7M") );
00894         else if ( nComponents == 1 ) pXML->AddAttribute( _T("colors"), _T("Greyscale") );
00895         
00896         if ( strComment.GetLength() ) pXML->AddAttribute( _T("description"), strComment );
00897         
00898         return SubmitMetadata( CSchema::uriImage, pXML );
00899 }
00900 
00902 // CLibraryBuilderInternals GIF (threaded)
00903 
00904 BOOL CLibraryBuilderInternals::ReadGIF( HANDLE hFile)
00905 {
00906         CHAR szMagic[6];
00907         DWORD nRead;
00908         
00909         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
00910         ReadFile( hFile, szMagic, 6, &nRead, NULL );
00911         
00912         if ( nRead != 6 || ( strncmp( szMagic, "GIF87a", 6 ) && strncmp( szMagic, "GIF89a", 6 ) ) )
00913                 return SubmitCorrupted();
00914         
00915         WORD nWidth, nHeight;
00916         
00917         ReadFile( hFile, &nWidth, 2, &nRead, NULL );
00918         if ( nRead != 2 || nWidth == 0 ) return FALSE;
00919         ReadFile( hFile, &nHeight, 2, &nRead, NULL );
00920         if ( nRead != 2 || nHeight == 0 ) return FALSE;
00921         
00922         CXMLElement* pXML = new CXMLElement( NULL, _T("image") );
00923         CString strItem;
00924         
00925         strItem.Format( _T("%lu"), nWidth );
00926         pXML->AddAttribute( _T("width"), strItem );
00927         strItem.Format( _T("%lu"), nHeight );
00928         pXML->AddAttribute( _T("height"), strItem );
00929         
00930         pXML->AddAttribute( _T("colors"), _T("256") );
00931         
00932         return SubmitMetadata( CSchema::uriImage, pXML );
00933 }
00934 
00936 // CLibraryBuilderInternals PNG (threaded)
00937 
00938 BOOL CLibraryBuilderInternals::ReadPNG( HANDLE hFile)
00939 {
00940         BYTE nMagic[8];
00941         DWORD nRead;
00942         
00943         if ( GetFileSize( hFile, NULL ) < 33 ) return SubmitCorrupted();
00944         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
00945         
00946         ReadFile( hFile, nMagic, 8, &nRead, NULL );
00947         if ( nRead != 8 ) return SubmitCorrupted();
00948         if ( nMagic[0] != 137 || nMagic[1] != 80 || nMagic[2] != 78 ) return SubmitCorrupted();
00949         if ( nMagic[3] != 71 || nMagic[4] != 13 || nMagic[5] != 10 ) return SubmitCorrupted();
00950         if ( nMagic[6] != 26 || nMagic[7] != 10 ) return SubmitCorrupted();
00951         
00952         DWORD nLength, nIHDR;
00953         
00954         ReadFile( hFile, &nLength, 4, &nRead, NULL ); nLength = SWAP_LONG( nLength );
00955         if ( nRead != 4 || nLength < 10 ) return FALSE;
00956         ReadFile( hFile, &nIHDR, 4, &nRead, NULL );
00957         if ( nRead != 4 || nIHDR != 'RDHI' ) return FALSE;
00958 
00959         DWORD nWidth, nHeight;
00960         BYTE nBits, nColors;
00961 
00962         ReadFile( hFile, &nWidth, 4, &nRead, NULL );  nWidth = SWAP_LONG( nWidth );
00963         if ( nRead != 4 || nWidth <= 0 || nWidth > 0xFFFF ) return FALSE;
00964         ReadFile( hFile, &nHeight, 4, &nRead, NULL ); nHeight = SWAP_LONG( nHeight );
00965         if ( nRead != 4 || nHeight <= 0 || nHeight > 0xFFFF ) return FALSE;
00966 
00967         ReadFile( hFile, &nBits, 1, &nRead, NULL );
00968         if ( nRead != 1 ) return FALSE;
00969         ReadFile( hFile, &nColors, 1, &nRead, NULL );
00970         if ( nRead != 1 ) return FALSE;
00971 
00972         CXMLElement* pXML = new CXMLElement( NULL, _T("image") );
00973         CString strItem;
00974         
00975         strItem.Format( _T("%lu"), nWidth );
00976         pXML->AddAttribute( _T("width"), strItem );
00977         strItem.Format( _T("%lu"), nHeight );
00978         pXML->AddAttribute( _T("height"), strItem );
00979 
00980         /*
00981         if ( nColors == 2 || nColors == 4 )
00982         {
00983                 pXML->AddAttribute( _T("colors"), _T("Greyscale") );
00984         }
00985         else
00986         */
00987         {
00988                 switch ( nBits )
00989                 {
00990                 case 1:
00991                         pXML->AddAttribute( _T("colors"), _T("2") );
00992                         break;
00993                 case 2:
00994                         pXML->AddAttribute( _T("colors"), _T("4") );
00995                         break;
00996                 case 4:
00997                         pXML->AddAttribute( _T("colors"), _T("16") );
00998                         break;
00999                 case 8:
01000                         pXML->AddAttribute( _T("colors"), _T("256") );
01001                         break;
01002                 case 16:
01003                         pXML->AddAttribute( _T("colors"), _T("64K") );
01004                         break;
01005                 }
01006         }
01007         
01008         return SubmitMetadata( CSchema::uriImage, pXML );
01009 }
01010 
01012 // CLibraryBuilderInternals BMP (threaded)
01013 
01014 BOOL CLibraryBuilderInternals::ReadBMP( HANDLE hFile)
01015 {
01016         BITMAPFILEHEADER pBFH;
01017         BITMAPINFOHEADER pBIH;
01018         DWORD nRead;
01019         
01020         if ( GetFileSize( hFile, NULL ) < sizeof(pBFH) + sizeof(pBIH) ) return SubmitCorrupted();
01021         
01022         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
01023         ReadFile( hFile, &pBFH, sizeof(pBFH), &nRead, NULL );
01024         if ( nRead != sizeof(pBFH) || pBFH.bfType != 'MB' ) return SubmitCorrupted();
01025         
01026         ReadFile( hFile, &pBIH, sizeof(pBIH), &nRead, NULL );
01027         if ( nRead != sizeof(pBIH) || pBIH.biSize != sizeof(pBIH) ) return FALSE;
01028         
01029         CXMLElement* pXML = new CXMLElement( NULL, _T("image") );
01030         CString strItem;
01031         
01032         strItem.Format( _T("%lu"), pBIH.biWidth );
01033         pXML->AddAttribute( _T("width"), strItem );
01034         strItem.Format( _T("%lu"), pBIH.biHeight );
01035         pXML->AddAttribute( _T("height"), strItem );
01036         
01037         switch ( pBIH.biBitCount )
01038         {
01039         case 4:
01040                 pXML->AddAttribute( _T("colors"), _T("16") );
01041                 break;
01042         case 8:
01043                 pXML->AddAttribute( _T("colors"), _T("256") );
01044                 break;
01045         case 24:
01046                 pXML->AddAttribute( _T("colors"), _T("16.7M") );
01047                 break;
01048         }
01049 
01050         return SubmitMetadata( CSchema::uriImage, pXML );
01051 }
01052 
01054 // CLibraryBuilderInternals ASF (threaded)
01055 
01056 static const CLSID asfHeader1 =
01057 { 0x75B22630, 0x668E, 0x11CF, { 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C } };
01058 
01059 static const CLSID asfContent1 =
01060 { 0x75B22633, 0x668E, 0x11CF, { 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C } };
01061 
01062 static const CLSID asfProperties1 =     // ???
01063 { 0x8CABDCA1, 0xA947, 0x11CF, { 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 } };
01064 
01065 static const CLSID asfStream1 =
01066 { 0xB7DC0791, 0xA9B7, 0x11CF, { 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 } };
01067 
01068 static const CLSID asfVideo1 =
01069 { 0xBC19EFC0, 0x5B4D, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B } };
01070 
01071 static const CLSID asfData1 =
01072 { 0x75b22636, 0x668e, 0x11cf, { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c } };
01073 
01074 // {D6E229D1-35DA-11d1-9034-00A0C90349BE}
01075 static const CLSID asfHeader2 =
01076 { 0xD6E229D1, 0x35DA, 0x11d1, { 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE } };
01077 
01078 // {D6E229D2-35DA-11d1-9034-00A0C90349BE}
01079 static const CLSID asfData2 =
01080 { 0xD6E229D2, 0x35DA, 0x11d1, { 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE } };
01081 
01082 // {D6E229D0-35DA-11d1-9034-00A0C90349BE}
01083 static const CLSID asfProperties2 =
01084 { 0xD6E229D0, 0x35DA, 0x11d1, { 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE } };
01085 
01086 // {D6E229D4-35DA-11d1-9034-00A0C90349BE}
01087 static const CLSID asfStream2 =
01088 { 0xD6E229D4, 0x35DA, 0x11d1, { 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE } };
01089 
01090 // {D6E229D5-35DA-11d1-9034-00A0C90349BE}
01091 static const CLSID asfContent2 =
01092 { 0xD6E229D5, 0x35DA, 0x11d1, { 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE } };
01093 
01094 // {D6E229E2-35DA-11d1-9034-00A0C90349BE}
01095 static const CLSID asfAudio2 =
01096 { 0xD6E229E2, 0x35DA, 0x11d1, { 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE } };
01097 
01098 // {D6E229E3-35DA-11d1-9034-00A0C90349BE}
01099 static const CLSID asfVideo2 =
01100 { 0xD6E229E3, 0x35DA, 0x11d1, { 0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE } };
01101 
01102 // {2211B3FB-BD23-11D2-B4B7-00A0C955FC6E}
01103 static const CLSID asfDRM1 =
01104 { 0x2211B3FB, 0xBD23, 0x11D2, { 0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E } };
01105 
01106 // {1EFB1A30-0B62-11D0-A39B-00A0C90348F6}
01107 static const CLSID asfDRM2 =
01108 { 0x1EFB1A30, 0x0B62, 0x11D0, { 0xA3, 0x9B, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 } };
01109 
01110 BOOL CLibraryBuilderInternals::ReadASF( HANDLE hFile)
01111 {
01112         QWORD nSize;
01113         DWORD nRead;
01114         GUID pGUID;
01115         
01116         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
01117         ReadFile( hFile, &pGUID, sizeof(pGUID), &nRead, NULL );
01118         if ( nRead != sizeof(pGUID) || ( pGUID != asfHeader1 && pGUID != asfHeader2 ) )
01119                 return SubmitCorrupted();
01120         ReadFile( hFile, &nSize, sizeof(nSize), &nRead, NULL );
01121         if ( nRead != sizeof(nSize) ) return SubmitCorrupted();
01122         
01123         if ( pGUID == asfHeader1 ) SetFilePointer( hFile, 6, NULL, FILE_CURRENT );
01124         
01125         CString strTitle, strAuthor, strCopyright, strDescription, strRating;
01126         DWORD nBitrate = 0, nVideoWidth = 0, nVideoHeight = 0;
01127         QWORD nContentLength = 0;
01128         BOOL bVideo = FALSE;
01129         BOOL bDRM = FALSE;
01130         
01131         while ( TRUE )
01132         {
01133                 DWORD dwPosition = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );
01134                 
01135                 ReadFile( hFile, &pGUID, sizeof(pGUID), &nRead, NULL );
01136                 if ( nRead != sizeof(pGUID) ) break;
01137                 ReadFile( hFile, &nSize, sizeof(nSize), &nRead, NULL );
01138                 if ( nRead != sizeof(nSize) || nSize >= 0x80000000 ) break;
01139                 
01140                 if ( pGUID == asfProperties1 )
01141                 {
01142                         SetFilePointer( hFile, 48, NULL, FILE_CURRENT );
01143                         ReadFile( hFile, &nContentLength, sizeof(nContentLength), &nRead, NULL );
01144                         if ( nRead != sizeof(nContentLength) ) return FALSE;
01145                 }
01146                 else if ( pGUID == asfProperties2 )
01147                 {
01148                         SetFilePointer( hFile, 40, NULL, FILE_CURRENT );
01149                         ReadFile( hFile, &nContentLength, sizeof(nContentLength), &nRead, NULL );
01150                         if ( nRead != sizeof(nContentLength) ) return FALSE;
01151                         SetFilePointer( hFile, 8, NULL, FILE_CURRENT );
01152                         ReadFile( hFile, &nBitrate, sizeof(nBitrate), &nRead, NULL );
01153                         if ( nRead != sizeof(nBitrate) ) return FALSE;
01154                 }
01155                 else if ( pGUID == asfStream1 )
01156                 {
01157                         ReadFile( hFile, &pGUID, sizeof(pGUID), &nRead, NULL );
01158                         if ( nRead != sizeof(pGUID) ) return FALSE;
01159                         
01160                         if ( pGUID == asfVideo1 )
01161                         {
01162                                 bVideo = TRUE;
01163                                 SetFilePointer( hFile, 38, NULL, FILE_CURRENT );
01164                                 ReadFile( hFile, &nVideoWidth, sizeof(nVideoWidth), &nRead, NULL );
01165                                 if ( nRead != sizeof(nVideoWidth) ) return FALSE;
01166                                 ReadFile( hFile, &nVideoHeight, sizeof(nVideoHeight), &nRead, NULL );
01167                                 if ( nRead != sizeof(nVideoHeight) ) return FALSE;
01168                         }
01169                 }
01170                 else if ( pGUID == asfStream2 )
01171                 {
01172                         ReadFile( hFile, &pGUID, sizeof(pGUID), &nRead, NULL );
01173                         if ( nRead != sizeof(pGUID) ) return FALSE;
01174 
01175                         if ( pGUID == asfVideo2 )
01176                         {
01177                                 bVideo = TRUE;
01178                                 /*
01179                                 SetFilePointer( hFile, 68, NULL, FILE_CURRENT );
01180                                 ReadFile( hFile, &nVideoWidth, sizeof(nVideoWidth), &nRead, NULL );
01181                                 if ( nRead != sizeof(nVideoWidth) ) return FALSE;
01182                                 nVideoHeight = nVideoWidth >> 16;
01183                                 nVideoWidth &= 0xFFFF;
01184                                 */
01185                         }
01186                 }
01187                 else if ( pGUID == asfContent1 )
01188                 {
01189                         WORD nStrLen[5];
01190                         ReadFile( hFile, nStrLen, sizeof(nStrLen), &nRead, NULL );
01191                         if ( nRead != sizeof(nStrLen) ) break;
01192                         
01193                         for ( int nStr = 0 ; nStr < 5 ; nStr++ )
01194                         {
01195                                 if ( ! nStrLen[ nStr ] || nStrLen[ nStr ] & 1 ) continue;
01196                                 WCHAR* pStr = new WCHAR[ nStrLen[ nStr ] / 2 ];
01197                                 ReadFile( hFile, pStr, nStrLen[ nStr ], &nRead, NULL );
01198                                 if ( nRead != nStrLen[ nStr ] ) return FALSE;
01199                                 pStr[ nStrLen[ nStr ] / 2 - 1 ] = 0;
01200                                 
01201                                 switch ( nStr )
01202                                 {
01203                                 case 0:
01204                                         strTitle = pStr;
01205                                         break;
01206                                 case 1:
01207                                         strAuthor = pStr;
01208                                         break;
01209                                 case 2:
01210                                         strCopyright = pStr;
01211                                         break;
01212                                 case 3:
01213                                         strDescription = pStr;
01214                                         break;
01215                                 case 4:
01216                                         strRating = pStr;
01217                                         break;
01218                                 }
01219                                 
01220                                 delete [] pStr;
01221                         }
01222                 }
01223                 else if ( pGUID == asfContent2 )
01224                 {
01225                         WORD nCount;
01226                         ReadFile( hFile, &nCount, sizeof(nCount), &nRead, NULL );
01227                         if ( nRead != sizeof(nCount) ) break;
01228                         
01229                         while ( nCount-- )
01230                         {
01231                                 WORD nLanguageID, nStreamID, nNameLen, nValueLen;
01232                                 BYTE nFieldType;
01233                                 WCHAR* pStr;
01234 
01235                                 ReadFile( hFile, &nFieldType, sizeof(nFieldType), &nRead, NULL );
01236                                 if ( nRead != sizeof(nFieldType) ) return FALSE;
01237                                 ReadFile( hFile, &nLanguageID, sizeof(nLanguageID), &nRead, NULL );
01238                                 if ( nRead != sizeof(nLanguageID) ) return FALSE;
01239                                 ReadFile( hFile, &nStreamID, sizeof(nStreamID), &nRead, NULL );
01240                                 if ( nRead != sizeof(nStreamID) ) return FALSE;
01241                                 ReadFile( hFile, &nNameLen, sizeof(nNameLen), &nRead, NULL );
01242                                 if ( nRead != sizeof(nNameLen) ) return FALSE;
01243                                 ReadFile( hFile, &nValueLen, sizeof(nValueLen), &nRead, NULL );
01244                                 if ( nRead != sizeof(nValueLen) ) return FALSE;
01245                                 
01246                                 pStr = new WCHAR[ nNameLen + 1 ];
01247                                 ReadFile( hFile, pStr, nNameLen * 2, &nRead, NULL );
01248                                 if ( nRead != (DWORD)nNameLen * 2 ) return FALSE;
01249                                 pStr[ nNameLen ] = 0;
01250                                 delete [] pStr;
01251 
01252                                 pStr = new WCHAR[ nValueLen + 1 ];
01253                                 ReadFile( hFile, pStr, nValueLen * 2, &nRead, NULL );
01254                                 if ( nRead != (DWORD)nValueLen * 2 ) return FALSE;
01255                                 pStr[ nValueLen ] = 0;
01256 
01257                                 switch ( nFieldType )
01258                                 {
01259                                 case 1:
01260                                         strAuthor = pStr;
01261                                         break;
01262                                 case 2: case 20:
01263                                         strTitle = pStr;
01264                                         break;
01265                                 case 3:
01266                                         strCopyright = pStr;
01267                                         break;
01268                                 case 4:
01269                                         strDescription = pStr;
01270                                         break;
01271                                 }
01272 
01273                                 delete [] pStr;
01274                         }
01275                 }
01276                 else if ( pGUID == asfDRM1 || pGUID == asfDRM2 )
01277                 {
01278                         bDRM = TRUE;
01279                 }
01280                 else if ( pGUID == asfData1 || pGUID == asfData2 )
01281                 {
01282                         break;
01283                 }
01284                 
01285                 SetFilePointer( hFile, dwPosition + (DWORD)nSize, NULL, FILE_BEGIN );
01286         }
01287         
01288         CXMLElement* pXML = new CXMLElement( NULL, bVideo ? _T("video") : _T("audio") );
01289         CString strItem;
01290         
01291         if ( strTitle.GetLength() ) pXML->AddAttribute( _T("title"), strTitle );
01292         
01293         if ( strDescription.GetLength() ) pXML->AddAttribute( _T("description"), strDescription );
01294         
01295         if ( bDRM )
01296         {
01297                 pXML->AddAttribute( _T("drm"), _T("true") );
01298         }
01299         
01300         if ( bVideo )
01301         {
01302                 if ( strAuthor.GetLength() ) pXML->AddAttribute( _T("producer"), strAuthor );
01303 
01304                 if ( strRating.GetLength() ) pXML->AddAttribute( _T("rating"), strRating );
01305 
01306                 if ( nContentLength > 0 )
01307                 {
01308                         DWORD nSeconds = (DWORD)( nContentLength / 10000000 );
01309                         strItem.Format( _T("%lu.%lu"), nSeconds / 60, ( ( nSeconds % 60 ) * 10 / 60 ) );
01310                         pXML->AddAttribute( _T("minutes"), strItem );
01311                 }
01312 
01313                 if ( nVideoWidth > 0 && nVideoHeight > 0 )
01314                 {
01315                         strItem.Format( _T("%lu"), nVideoWidth );
01316                         pXML->AddAttribute( _T("width"), strItem );
01317                         strItem.Format( _T("%lu"), nVideoHeight );
01318                         pXML->AddAttribute( _T("height"), strItem );
01319                 }
01320         }
01321         else
01322         {
01323                 if ( strAuthor.GetLength() ) pXML->AddAttribute( _T("artist"), strAuthor );
01324 
01325                 if ( nContentLength > 0 )
01326                 {
01327                         strItem.Format( _T("%lu"), (DWORD)( nContentLength / 10000000 ) );
01328                         pXML->AddAttribute( _T("seconds"), strItem );
01329                 }
01330 
01331                 if ( nBitrate > 0 )
01332                 {
01333                         strItem.Format( _T("%lu"), nBitrate / 1000 );
01334                         pXML->AddAttribute( _T("bitrate"), strItem );
01335                 }
01336         }
01337         
01338         pXML->AddAttribute( _T("codec"), _T("WM") );
01339         
01340         return SubmitMetadata( bVideo ? CSchema::uriVideo : CSchema::uriAudio, pXML );
01341 }
01342 
01344 // CLibraryBuilderInternals MPEG (threaded)
01345 
01346 BOOL CLibraryBuilderInternals::ReadMPEG( HANDLE hFile)
01347 {
01348         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
01349         
01350         DWORD nHeader = 0;
01351         
01352     DWORD nSeek = 8192;
01353         for ( ; nSeek > 0 ; nSeek--, nHeader <<= 8 )
01354         {
01355                 DWORD nRead = 0;
01356                 ReadFile( hFile, &nHeader, 1, &nRead, NULL );
01357                 if ( nRead != 1 ) break;
01358                 
01359                 if ( nHeader == 0x000001B3 ) break;
01360         }
01361         
01362         if ( ! nSeek ) return FALSE;
01363         
01364         BYTE nBuffer[7];
01365 
01366         ReadFile( hFile, nBuffer, 7, &nHeader, NULL );
01367         if ( nHeader != 7 ) return FALSE;
01368         
01369         CXMLElement* pXML = new CXMLElement( NULL, _T("video") );
01370         CString strItem;
01371         
01372         DWORD nWidth, nHeight;
01373         nWidth = ( (DWORD)nBuffer[0] << 4 ) | (DWORD)nBuffer[1] >> 4;
01374         nHeight = ( ( (DWORD)nBuffer[1] & 0x0F ) << 8 ) | (DWORD)nBuffer[2];
01375         
01376         strItem.Format( _T("%lu"), nWidth );
01377         pXML->AddAttribute( _T("width"), strItem );
01378         strItem.Format( _T("%lu"), nHeight );
01379         pXML->AddAttribute( _T("height"), strItem );
01380         pXML->AddAttribute( _T("codec"), _T("MPEG") );
01381         
01382         LPCTSTR pszFPS[] = { _T("23.976"), _T("24"), _T("25"), _T("29.97"), _T("30"), _T("50"), _T("59.94"), _T("60") };
01383         int nFrameIndex = ( nBuffer[3] & 0x0F );
01384         
01385         if ( nFrameIndex >= 1 && nFrameIndex < 9 )
01386         {
01387                 pXML->AddAttribute( _T("frameRate"), pszFPS[ nFrameIndex - 1 ] );
01388         }
01389         
01390         return SubmitMetadata( CSchema::uriVideo, pXML );
01391 }
01392 
01394 // CLibraryBuilderInternals OGG VORBIS (threaded)
01395 
01396 BOOL CLibraryBuilderInternals::ReadOGG( HANDLE hFile)
01397 {
01398         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
01399         
01400         DWORD nDummy, nHeader = 0;
01401         ReadFile( hFile, &nHeader, 4, &nDummy, NULL );
01402         
01403         for ( DWORD nSeek = 0 ; nSeek < 16384 ; nSeek++ )
01404         {
01405                 if ( nHeader == 'SggO' ) break;
01406                 nHeader >>= 8;
01407                 ReadFile( hFile, (BYTE*)&nHeader + 3, 1, &nDummy, NULL );
01408         }
01409         
01410         if ( nHeader != 'SggO' ) return SubmitCorrupted();
01411         SetFilePointer( hFile, -4, NULL, FILE_CURRENT );
01412         
01413         DWORD nOGG = 0;
01414         BYTE* pOGG = ReadOGGPage( hFile, nOGG, 0x02, 0, 0x1E );
01415         
01416         if ( ! pOGG ) return FALSE;
01417         
01418         BYTE nChannels          = pOGG[ 11 ];
01419         DWORD nFrequency        = *(DWORD*)&pOGG[12];
01420         DWORD nBitrate          = *(DWORD*)&pOGG[20];
01421         
01422         delete [] pOGG;
01423         BYTE* prOGG = pOGG = ReadOGGPage( hFile, nOGG, 0x00, 1, 1+6+4+4 );
01424         
01425         if ( ! pOGG ) return FALSE;
01426         pOGG += 1 + 6;
01427         nOGG -= 1 + 6;
01428         
01429         CString strComment;
01430         
01431         if ( ! ReadOGGString( pOGG, nOGG, strComment ) || nOGG < 4 )
01432         {
01433                 free( pOGG );
01434                 return FALSE;
01435         }
01436         
01437         DWORD nComments = *(DWORD*)pOGG;
01438         pOGG += 4; nOGG -= 4;
01439         
01440         CXMLElement* pXML = new CXMLElement( NULL, _T("audio") );
01441         
01442         for ( ; nComments && nOGG > 4 ; nComments-- )
01443         {
01444                 if ( ! ReadOGGString( pOGG, nOGG, strComment ) ) break;
01445                 
01446                 int nEquals = strComment.Find( '=' );
01447                 if ( nEquals <= 0 ) continue;
01448                 
01449                 CString strKey          = strComment.Left( nEquals );
01450                 CString strValue        = strComment.Mid( nEquals + 1 );
01451                 
01452                 strKey.TrimLeft(); strKey.TrimRight(); 
01453                 CharUpper( strKey.GetBuffer() );
01454                 strKey.ReleaseBuffer();
01455 
01456                 // decode UTF-8 string
01457                 int nLength = strValue.GetLength();
01458 
01459                 LPTSTR pszSource = new TCHAR[ nLength + 1 ]; 
01460                 CHAR* pszDest = new CHAR[ nLength + 1 ];
01461 
01462                 _tcscpy( pszSource, strValue.GetBuffer() );
01463                 for ( unsigned int nLen = 0; nLen < _tcslen( pszSource ); nLen++ )
01464                         pszDest[ nLen ] = (CHAR) pszSource[ nLen ];
01465                 delete pszSource;
01466 
01467                 int nWide = MultiByteToWideChar( CP_UTF8, 0, pszDest, nLength, NULL, 0 );
01468                 LPWSTR pszWide = new WCHAR[ nWide + 1 ];
01469                 MultiByteToWideChar( CP_UTF8, 0, pszDest, nLength, pszWide, nWide );
01470                 pszWide[ nWide ] = 0;
01471                 strValue = pszWide;
01472                 
01473                 delete [] pszWide;
01474                 delete pszDest;
01475 
01476                 strValue.TrimLeft(); strValue.TrimRight();
01477 
01478                 if ( strValue.IsEmpty() ) continue;
01479                 
01480                 if ( strKey == _T("TITLE") )
01481                 {
01482                         pXML->AddAttribute( _T("title"), strValue );
01483                 }
01484                 else if ( strKey == _T("ALBUM") )
01485                 {
01486                         pXML->AddAttribute( _T("album"), strValue );
01487                 }
01488                 else if ( strKey == _T("TRACKNUMBER") )
01489                 {
01490                         pXML->AddAttribute( _T("track"), strValue );
01491                 }
01492                 else if ( strKey == _T("ARTIST") )
01493                 {
01494                         pXML->AddAttribute( _T("artist"), strValue );
01495                 }
01496                 else if ( strKey == _T("DESCRIPTION") )
01497                 {
01498                         pXML->AddAttribute( _T("description"), strValue );
01499                 }
01500                 else if ( strKey == _T("GENRE") )
01501                 {
01502                         pXML->AddAttribute( _T("genre"), strValue );
01503                 }
01504                 else if ( strKey == _T("DATE") )
01505                 {
01506                         pXML->AddAttribute( _T("year"), strValue );
01507                 }
01508                 else if ( strKey == _T("LICENSE") )
01509                 {
01510                         pXML->AddAttribute( _T("copyright"), strValue );
01511                 }
01512         }
01513         
01514         delete [] prOGG;
01515         
01516         if ( nComments )
01517         {
01518                 if ( pXML ) delete pXML;
01519                 return FALSE;
01520         }
01521         
01522         DWORD nLength = 0;
01523         
01524         for ( nComments = 2 ; ; nComments++ )
01525         {
01526                 DWORD nTime = GetTickCount();
01527                 if ( ! ReadOGGPage( hFile, nOGG, 0xFF, nComments, 0xFFFFFFFF ) ) break;
01528                 nLength = max( nLength, nOGG );
01529                 m_nSleep = ( GetTickCount() - nTime ) * 3;
01530                 if ( m_nSleep > 0 ) Sleep( m_nSleep );
01531                 if ( ! m_pBuilder->m_bThread ) break;
01532         }
01533         
01534         if ( ! m_pBuilder->m_bThread )
01535         {
01536                 delete pXML;
01537                 return FALSE;
01538         }
01539         
01540         if ( nFrequency > 0 && nLength > 0 && ( nLength / nFrequency ) > 0 )
01541         {
01542                 strComment.Format( _T("%lu"), nLength / nFrequency );
01543                 pXML->AddAttribute( _T("seconds"), strComment );
01544 
01545                 nBitrate = GetFileSize( hFile, NULL ) / ( nLength / nFrequency ) * 8;
01546         }
01547         
01548         strComment.Format( _T("%lu"), nBitrate / 1000 );
01549         pXML->AddAttribute( _T("bitrate"), strComment );
01550         
01551         strComment.Format( _T("%lu"), nFrequency );
01552         pXML->AddAttribute( _T("sampleRate"), strComment );
01553         
01554         return SubmitMetadata( CSchema::uriAudio, pXML );
01555 }
01556 
01557 BYTE* CLibraryBuilderInternals::ReadOGGPage(HANDLE hFile, DWORD& nBuffer, BYTE nFlags, DWORD nSequence, DWORD nMinSize)
01558 {
01559         DWORD nMagic, nRead, nSample;
01560         BYTE nByte, nChunk;
01561         
01562         nBuffer = 0;
01563         
01564         ReadFile( hFile, &nMagic, 4, &nRead, NULL );
01565         if ( nRead != 4 || nMagic != 'SggO' ) return NULL;
01566         
01567         ReadFile( hFile, &nByte, 1, &nRead, NULL );
01568         if ( nRead != 1 || nByte != 0 ) return NULL;
01569         
01570         ReadFile( hFile, &nByte, 1, &nRead, NULL );
01571         if ( nRead != 1 ) return NULL;
01572         if ( nFlags < 0xFF && nByte != nFlags ) return NULL;
01573         
01574         ReadFile( hFile, &nSample, 4, &nRead, NULL );
01575         if ( nRead != 4 ) return NULL;
01576         
01577         SetFilePointer( hFile, 4 + 4, NULL, FILE_CURRENT );
01578         
01579         ReadFile( hFile, &nMagic, 4, &nRead, NULL );
01580         if ( nRead != 4 || nMagic != nSequence ) return NULL;
01581         
01582         ReadFile( hFile, &nMagic, 4, &nRead, NULL );
01583         if ( nRead != 4 ) return NULL;
01584         
01585         ReadFile( hFile, &nByte, 1, &nRead, NULL );
01586         if ( nRead != 1 ) return NULL;
01587         
01588         for ( ; nByte ; nByte-- )
01589         {
01590                 ReadFile( hFile, &nChunk, 1, &nRead, NULL );
01591                 if ( nRead != 1 ) break;
01592                 nBuffer += nChunk;
01593         }
01594         
01595         if ( nByte ) return NULL;
01596         
01597         if ( nMinSize < 0xFFFFFFFF )
01598         {
01599                 if ( nBuffer < nMinSize ) return NULL;
01600                 
01601                 BYTE* pBuffer = new BYTE[ nBuffer ];
01602                 
01603                 ReadFile( hFile, pBuffer, nBuffer, &nRead, NULL );
01604                 
01605                 if ( nRead == nBuffer ) return pBuffer;
01606                 
01607                 delete [] pBuffer;
01608         }
01609         else
01610         {
01611                 SetFilePointer( hFile, nBuffer, NULL, FILE_CURRENT );
01612                 nBuffer = nSample;
01613                 return (BYTE*)TRUE;
01614         }
01615         
01616         return NULL;
01617 }
01618 
01619 BOOL CLibraryBuilderInternals::ReadOGGString(BYTE*& pOGG, DWORD& nOGG, CString& str)
01620 {
01621         if ( nOGG < 4 ) return FALSE;
01622         
01623         DWORD nLen = *(DWORD*)pOGG;
01624         pOGG += 4; nOGG -= 4;
01625         
01626         if ( nOGG < nLen ) return FALSE;
01627         
01628         LPTSTR pszOut = str.GetBuffer( nLen + 1 );
01629         for ( ; nLen ; nLen--, nOGG-- ) *pszOut++ = (TCHAR)*pOGG++;
01630         *pszOut++ = 0;
01631         str.ReleaseBuffer();
01632         
01633         return TRUE;
01634 }
01635 
01637 // CLibraryBuilderInternals APE Monkey's Audio (threaded)
01638 
01639 BOOL CLibraryBuilderInternals::ReadAPE( HANDLE hFile)
01640 {
01641         if ( GetFileSize( hFile, NULL ) < sizeof(APE_HEADER) ) return SubmitCorrupted();
01642         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
01643         
01644         APE_HEADER pAPE;
01645         DWORD nRead;
01646         
01647         ReadFile( hFile, &pAPE, sizeof(pAPE), &nRead, NULL );
01648         if ( nRead != sizeof(pAPE) ) return SubmitCorrupted();
01649         if ( pAPE.cID[0] != 'M' || pAPE.cID[1] != 'A' || pAPE.cID[2] != 'C' ) return SubmitCorrupted();
01650         if ( pAPE.nSampleRate == 0 ) return SubmitCorrupted();
01651         
01652         DWORD nBlocksPerFrame = ( pAPE.nVersion >= 3900 || ( pAPE.nVersion >= 3800 &&
01653                 pAPE.nCompressionLevel == 4000 ) ) ? 73728 : 9216;
01654         
01655         DWORD nBlocks   = ( pAPE.nTotalFrames - 1 ) * nBlocksPerFrame + pAPE.nFinalFrameBlocks;
01656         DWORD nSamples  = nBlocks * pAPE.nChannels;
01657         
01658         if ( pAPE.nFormatFlags & 8 )
01659                 nSamples *= 3;
01660         else if ( ( pAPE.nFormatFlags & 1 ) == 0 )
01661                 nSamples *= 2;
01662         
01663         DWORD nDuration = nSamples / pAPE.nSampleRate;
01664         
01665         CXMLElement* pXML = new CXMLElement( NULL, _T("audio") );
01666         CString strItem;
01667         
01668         strItem.Format( _T("%lu"), nDuration );
01669         pXML->AddAttribute( _T("seconds"), strItem );
01670         
01671         strItem.Format( _T("%lu"), pAPE.nSampleRate );
01672         pXML->AddAttribute( _T("sampleRate"), strItem );
01673         
01674         if ( ReadID3v1( hFile, pXML ) )
01675         {
01676                 return SubmitMetadata( CSchema::uriAudio, pXML );
01677         }
01678         
01679         if ( GetFileSize( hFile, NULL ) < sizeof(APE_HEADER) + sizeof(APE_TAG_FOOTER) )
01680         {
01681                 return SubmitMetadata( CSchema::uriAudio, pXML );
01682         }
01683         
01684         APE_TAG_FOOTER pFooter;
01685         
01686         SetFilePointer( hFile, -(LONG)sizeof(pFooter), NULL, FILE_END );
01687         ReadFile( hFile, &pFooter, sizeof(pFooter), &nRead, NULL );
01688         
01689         if ( nRead != sizeof(pFooter) || strncmp( pFooter.cID, "APETAGEX", 8 ) ||
01690                  (DWORD)pFooter.nFields > 16 ||
01691                  ( pFooter.nVersion != 1000 && pFooter.nVersion != 2000 ) )
01692         {
01693                 return SubmitMetadata( CSchema::uriAudio, pXML );
01694         }
01695         
01696         SetFilePointer( hFile, -(LONG)pFooter.nSize, NULL, FILE_END );
01697         
01698         for ( int nTag = 0 ; nTag < pFooter.nFields ; nTag++ )
01699         {
01700                 DWORD nLength, nFlags;
01701                 
01702                 ReadFile( hFile, &nLength, 4, &nRead, NULL );
01703                 if ( nRead != 4 || nLength > 1024 ) break;
01704                 ReadFile( hFile, &nFlags, 4, &nRead, NULL );
01705                 if ( nRead != 4 ) break;
01706                 
01707                 CString strKey, strValue;
01708                 
01709                 while ( strKey.GetLength() < 64 )
01710                 {
01711                         BYTE nChar;
01712                         ReadFile( hFile, &nChar, 1, &nRead, NULL );
01713                         if ( nRead != 1 || nChar == 0 ) break;
01714                         strKey += (TCHAR)nChar;
01715                 }
01716                 
01717                 if ( nRead != 1 || strKey.GetLength() >= 64 ) break;
01718                 
01719                 LPSTR pszInput = new CHAR[ nLength ];
01720                 ReadFile( hFile, pszInput, nLength, &nRead, NULL );
01721                 if ( nLength != nRead ) break;
01722                 
01723                 int nWide = MultiByteToWideChar( CP_UTF8, 0, pszInput, nLength, NULL, 0 );
01724                 LPWSTR pszWide = new WCHAR[ nWide + 1 ];
01725                 MultiByteToWideChar( CP_UTF8, 0, pszInput, nLength, pszWide, nWide );
01726                 pszWide[ nWide ] = 0;
01727                 strValue = pszWide;
01728                 
01729                 delete [] pszWide;
01730                 delete [] pszInput;
01731                 
01732                 strKey.TrimLeft(); strKey.TrimRight();
01733                 strValue.TrimLeft(); strValue.TrimRight();
01734                 
01735                 if ( strKey.GetLength() && strValue.GetLength() )
01736                 {
01737                         CharLower( strKey.GetBuffer() );
01738                         strKey.ReleaseBuffer();
01739                         
01740                         if ( strKey == _T("title") )
01741                         {
01742                                 pXML->AddAttribute( _T("title"), strValue );
01743                         }
01744                         else if ( strKey == _T("artist") )
01745                         {
01746                                 pXML->AddAttribute( _T("artist"), strValue );
01747                         }
01748                         else if ( strKey == _T("album") )
01749                         {
01750                                 pXML->AddAttribute( _T("album"), strValue );
01751                         }
01752                         else if ( strKey == _T("comment") )
01753                         {
01754                                 pXML->AddAttribute( _T("description"), strValue );
01755                         }
01756                         else if ( strKey == _T("year") )
01757                         {
01758                                 pXML->AddAttribute( _T("year"), strValue );
01759                         }
01760                         else if ( strKey == _T("track") )
01761                         {
01762                                 pXML->AddAttribute( _T("track"), strValue );
01763                         }
01764                         else if ( strKey == _T("genre") )
01765                         {
01766                                 pXML->AddAttribute( _T("genre"), strValue );
01767                         }
01768                         else if ( strKey.Find( _T(" url") ) > 0 )
01769                         {
01770                                 pXML->AddAttribute( _T("link"), strValue );
01771                         }
01772                 }
01773         }
01774         
01775         return SubmitMetadata( CSchema::uriAudio, pXML );
01776 }
01777 
01779 // CLibraryBuilderInternals AVI (threaded)
01780 
01781 BOOL CLibraryBuilderInternals::ReadAVI( HANDLE hFile)
01782 {
01783         if ( GetFileSize( hFile, NULL ) < sizeof(AVI_HEADER) + 16 ) return SubmitCorrupted();
01784         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
01785         
01786         CHAR szID[5] = { 0, 0, 0, 0, 0 };
01787         DWORD nRead;
01788         
01789         ReadFile( hFile, szID, 4, &nRead, NULL );
01790         if ( nRead != 4 || strncmp( szID, "RIFF", 4 ) ) return SubmitCorrupted();
01791         ReadFile( hFile, szID, 4, &nRead, NULL );
01792         if ( nRead != 4 ) return FALSE;
01793         ReadFile( hFile, szID, 4, &nRead, NULL );
01794         if ( nRead != 4 || strncmp( szID, "AVI ", 4 ) ) return SubmitCorrupted();
01795         ReadFile( hFile, szID, 4, &nRead, NULL );
01796         if ( nRead != 4 || strncmp( szID, "LIST", 4 ) ) return FALSE;
01797         ReadFile( hFile, szID, 4, &nRead, NULL );
01798         if ( nRead != 4 ) return FALSE;
01799         ReadFile( hFile, szID, 4, &nRead, NULL );
01800         if ( nRead != 4 || strncmp( szID, "hdrl", 4 ) ) return FALSE;
01801         ReadFile( hFile, szID, 4, &nRead, NULL );
01802         if ( nRead != 4 || strncmp( szID, "avih", 4 ) ) return FALSE;
01803         ReadFile( hFile, szID, 4, &nRead, NULL );
01804         if ( nRead != 4 ) return FALSE;
01805         
01806         AVI_HEADER pHeader;
01807         ReadFile( hFile, &pHeader, sizeof(pHeader), &nRead, NULL );
01808         if ( nRead != sizeof(pHeader) ) return FALSE;
01809         
01810         ReadFile( hFile, szID, 4, &nRead, NULL );
01811         if ( nRead != 4 || strncmp( szID, "LIST", 4 ) ) return FALSE;
01812         ReadFile( hFile, szID, 4, &nRead, NULL );
01813         if ( nRead != 4 ) return FALSE;
01814         ReadFile( hFile, szID, 4, &nRead, NULL );
01815         if ( nRead != 4 || strncmp( szID, "strl", 4 ) ) return FALSE;
01816         ReadFile( hFile, szID, 4, &nRead, NULL );
01817         if ( nRead != 4 || strncmp( szID, "strh", 4 ) ) return FALSE;
01818         ReadFile( hFile, szID, 4, &nRead, NULL );
01819         if ( nRead != 4 ) return FALSE;
01820         ReadFile( hFile, szID, 4, &nRead, NULL );
01821         if ( nRead != 4 || strncmp( szID, "vids", 4 ) ) return FALSE;
01822         ReadFile( hFile, szID, 4, &nRead, NULL );
01823         if ( nRead != 4 ) return FALSE;
01824         
01825         CXMLElement* pXML = new CXMLElement( NULL, _T("video") );
01826         CString strItem;
01827         
01828         double nTime = (double)pHeader.dwMicroSecPerFrame / 1000000.0f;
01829         nTime *= (double)pHeader.dwTotalFrames;
01830         nTime /= 60.0f;
01831         
01832         double nRate = 1000000.0f / (double)pHeader.dwMicroSecPerFrame;
01833         
01834         strItem.Format( _T("%lu"), pHeader.dwWidth );
01835         pXML->AddAttribute( _T("width"), strItem );
01836         strItem.Format( _T("%lu"), pHeader.dwHeight );
01837         pXML->AddAttribute( _T("height"), strItem );
01838         strItem.Format( _T("%.3f"), nTime );
01839         pXML->AddAttribute( _T("minutes"), strItem );
01840         strItem.Format( _T("%.2f"), nRate );
01841         pXML->AddAttribute( _T("frameRate"), strItem );
01842         pXML->AddAttribute( _T("codec"), CString( szID ) );
01843         
01844         return SubmitMetadata( CSchema::uriVideo, pXML );
01845 }
01846 
01848 // CLibraryBuilderInternals PDF (threaded)
01849 
01850 BOOL CLibraryBuilderInternals::ReadPDF( HANDLE hFile, LPCTSTR pszPath)
01851 {
01852         DWORD nOffset, nCount, nPages, nInfo;
01853         CString strLine, strSeek;
01854         
01855         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
01856         if ( ReadLine( hFile ).Find( _T("%PDF") ) != 0 ) return FALSE;
01857         
01858         SetFilePointer( hFile, -1, NULL, FILE_END );
01859         strLine = ReadLineReverse( hFile );
01860         if ( strLine.IsEmpty() ) strLine = ReadLineReverse( hFile );
01861         if ( strLine != _T("%%EOF") ) return FALSE;
01862 
01863         strLine = ReadLineReverse( hFile );
01864         if ( ReadLineReverse( hFile ) != _T("startxref") ) return FALSE;
01865         
01866         if ( _stscanf( strLine, _T("%lu"), &nOffset ) != 1 ) return FALSE;
01867         if ( SetFilePointer( hFile, nOffset, NULL, FILE_BEGIN ) != nOffset ) return FALSE;
01868         
01869         if ( ReadLine( hFile ) != _T("xref") ) return FALSE;
01870         strLine = ReadLine( hFile );
01871         if ( _stscanf( strLine, _T("%lu %lu"), &nOffset, &nCount ) != 2 ) return FALSE;
01872         if ( nCount > 4096 ) return FALSE;
01873         
01874         DWORD* pOffset = new DWORD[ nCount ];
01875         ZeroMemory( pOffset, sizeof(DWORD) * nCount );
01876         
01877         for ( nOffset = 0 ; nOffset < nCount ; nOffset++ )
01878         {
01879                 strLine = ReadLine( hFile );
01880                 strLine.TrimLeft();
01881                 strLine.TrimRight();
01882                 
01883                 if ( strLine.GetLength() != 18 || strLine.GetAt( 10 ) != ' ' )
01884                 {
01885                         delete [] pOffset;
01886                         return FALSE;
01887                 }
01888                 
01889                 if ( strLine.GetAt( 17 ) == 'n' )
01890                 {
01891             LPCTSTR pszInt = strLine;
01892                         for ( ; *pszInt == '0' ; pszInt++ );
01893                         
01894                         if ( *pszInt != 0 )
01895                         {
01896                                 _stscanf( pszInt, _T("%lu"), &pOffset[ nOffset ] );
01897                         }
01898                 }
01899         }
01900         
01901         if ( ReadLine( hFile ) != _T("trailer") ) 
01902         {
01903                         delete [] pOffset;
01904                         return FALSE;
01905         }
01906         if ( ReadLine( hFile ) != _T("<<") ) 
01907         {
01908                         delete [] pOffset;
01909                         return FALSE;
01910         }
01911         
01912         for ( nOffset = 0 ; ; )
01913         {
01914                 strLine = ReadLine( hFile );
01915                 if ( strLine.IsEmpty() || strLine == _T(">>") ) break;
01916                 
01917                 if ( _tcsnicmp( strLine, _T("/Info "), 6 ) == 0 )
01918                 {
01919                         _stscanf( strLine.Mid( 6 ), _T("%lu"), &nOffset );
01920                         break;
01921                 }
01922         }
01923         
01924         if ( nOffset == 0 )
01925         {
01926                 delete [] pOffset;
01927                 return FALSE;
01928         }
01929         
01930         strSeek.Format( _T("%lu 0 obj"), nOffset );
01931         nPages = nInfo = 0;
01932         
01933         for ( nOffset = 0 ; nOffset < nCount ; nOffset++ )
01934         {
01935                 if ( pOffset[ nOffset ] == 0 ) continue;
01936                 SetFilePointer( hFile, pOffset[ nOffset ], NULL, FILE_BEGIN );
01937                 
01938                 strLine = ReadLine( hFile );
01939                 if ( strLine.Find( _T("obj") ) < 0 ) break;
01940                 
01941                 if ( strLine == strSeek )
01942                 {
01943                         nInfo = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );
01944                 }
01945                 else if ( ReadLine( hFile ) == _T("<<") )
01946                 {
01947                         if ( ReadLine( hFile ) == _T("/Type /Page") ) nPages++;
01948                 }
01949         }
01950         
01951         delete [] pOffset;
01952         
01953         if ( nInfo == 0 ) return FALSE;
01954         SetFilePointer( hFile, nInfo, NULL, FILE_BEGIN );
01955         
01956         if ( ReadLine( hFile ) != _T("<<") ) return FALSE;
01957         
01958         BOOL bBook = ( _tcsistr( pszPath, _T("book") ) != NULL );
01959         if ( ! bBook ) return FALSE;
01960         
01961         CXMLElement* pXML = new CXMLElement( NULL, bBook ? _T("book") : _T("document") );
01962         
01963         if ( LPCTSTR pszName = _tcsrchr( pszPath, '\\' ) )
01964         {
01965                 pszName++;
01966                 
01967                 if ( _tcsnicmp( pszName, _T("ebook - "), 8 ) == 0 )
01968                 {
01969                         strLine = pszName + 8;
01970                         strLine = strLine.SpanExcluding( _T(".") );
01971                         strLine.TrimLeft();
01972                         strLine.TrimRight();
01973                         pXML->AddAttribute( _T("title"), strLine );
01974                 }
01975                 else if ( _tcsnicmp( pszName, _T("(ebook"), 6 ) == 0 )
01976                 {
01977                         if ( pszName = _tcschr( pszName, ')' ) )
01978                         {
01979                                 if ( _tcsncmp( pszName, _T(") - "), 4 ) == 0 )
01980                                         strLine = pszName + 4;
01981                                 else
01982                                         strLine = pszName + 1;
01983                                 strLine = strLine.SpanExcluding( _T(".") );
01984                                 strLine.TrimLeft();
01985                                 strLine.TrimRight();
01986                                 pXML->AddAttribute( _T("title"), strLine );
01987                         }
01988                 }
01989         }
01990         
01991         if ( nPages > 0 )
01992         {
01993                 strLine.Format( _T("%lu"), nPages );
01994                 pXML->AddAttribute( _T("pages"), strLine );
01995         }
01996         
01997         while ( TRUE )
01998         {
01999                 strLine = ReadLine( hFile );
02000                 if ( strLine.IsEmpty() || strLine.GetAt( 0 ) != '/' ) break;
02001                 
02002                 CString strKey = strLine.SpanExcluding( _T(" \t") );
02003                 strLine = strLine.Mid( strKey.GetLength() );
02004                 strLine.TrimLeft();
02005                 CharLower( strKey.GetBuffer() );
02006                 strKey.ReleaseBuffer();
02007                 
02008                 if ( strLine.GetLength() >= 2 &&
02009                          strLine.GetAt( 0 ) == '(' &&
02010                          strLine.GetAt( strLine.GetLength() - 1 ) == ')' )
02011                 {
02012                         strLine = strLine.Mid( 1, strLine.GetLength() - 2 );
02013                 }
02014                 
02015                 if ( strLine.IsEmpty() ) continue;
02016                 
02017                 if ( ( strLine.GetLength() & 1 ) == 0 && strLine.GetAt( 0 ) == '<' )
02018                 {
02019                         CString strTemp;
02020                         
02021                         for ( int nHex = 0 ; nHex < strLine.GetLength() / 2 - 1 ; nHex++ )
02022                         {
02023                                 int nChar;
02024                                 if ( _stscanf( strLine.Mid( nHex + 1, 2 ), _T("%x"), &nChar ) == 1 )
02025                                 {
02026                                         strTemp += (TCHAR)nChar;
02027                                 }
02028                         }
02029                         
02030                         strLine = strTemp;
02031                         if ( strLine.IsEmpty() ) continue;
02032                 }
02033                 
02034                 if ( strKey == _T("/title") )
02035                 {
02036                         pXML->AddAttribute( _T("title"), strLine );
02037                 }
02038                 else if ( strKey == _T("/author") )
02039                 {
02040                         pXML->AddAttribute( _T("author"), strLine );
02041                 }
02042                 else if ( strKey == _T("/subject") )
02043                 {
02044                         pXML->AddAttribute( _T("subject"), strLine );
02045                 }
02046                 else if ( strKey == _T("/keywords") )
02047                 {
02048                         pXML->AddAttribute( _T("keywords"), strLine );
02049                 }
02050         }
02051         
02052         // TODO: Check bBook
02053         if ( bBook )
02054         {
02055                 pXML->AddAttribute( _T("format"), _T("PDF") );
02056                 pXML->AddAttribute( _T("back"), _T("Digital") );
02057                 return SubmitMetadata( CSchema::uriBook, pXML );
02058         }
02059         else
02060         {
02061                 pXML->AddAttribute( _T("format"), _T("Adobe Acrobat PDF") );
02062                 // CString strTemp;
02063                 // strTemp.Format( _T("1.%i"), nVersion );
02064                 // pXML->AddAttribute( _T("formatVersion"), strTemp );
02065                 return SubmitMetadata( CSchema::uriDocument, pXML );
02066         }
02067 }
02068 
02069 CString CLibraryBuilderInternals::ReadLine(HANDLE hFile)
02070 {
02071         DWORD nRead, nLength;
02072         TCHAR cChar;
02073         CString str;
02074         
02075         ZeroMemory( &cChar, sizeof(cChar) );
02076         for ( nLength = 0 ; ReadFile( hFile, &cChar, 1, &nRead, NULL ) && nRead == 1 && nLength++ < 4096 ; )
02077         {
02078                 if ( cChar == '\r' ) break;
02079                 if ( cChar != '\n' ) str += cChar;
02080         }
02081         
02082         str.TrimLeft();
02083         str.TrimRight();
02084         
02085         return str;
02086 }
02087 
02088 CString CLibraryBuilderInternals::ReadLineReverse(HANDLE hFile)
02089 {
02090         DWORD nRead, nLength;
02091         TCHAR cChar;
02092         CString str;
02093         
02094         ZeroMemory( &cChar, sizeof(cChar) );
02095         for ( nLength = 0 ; ReadFile( hFile, &cChar, 1, &nRead, NULL ) && nRead == 1 && nLength++ < 4096 ; )
02096         {
02097                 if ( SetFilePointer( hFile, -2, NULL, FILE_CURRENT ) == 0 ) break;
02098                 if ( cChar == '\r' ) break;
02099                 if ( cChar != '\n' ) str = cChar + str;
02100         }
02101         
02102         str.TrimLeft();
02103         str.TrimRight();
02104         
02105         return str;
02106 }
02107 
02109 // CLibraryBuilderInternals Collection (threaded)
02110 
02111 BOOL CLibraryBuilderInternals::ReadCollection( HANDLE hFile, SHA1* pSHA1)
02112 {
02113         CCollectionFile pCollection;
02114         if ( ! pCollection.Attach( hFile ) ) return FALSE;
02115         
02116         LibraryFolders.MountCollection( pSHA1, &pCollection );
02117         
02118         if ( CXMLElement* pMetadata = pCollection.GetMetadata() )
02119         {
02120                 pMetadata = pMetadata->GetFirstElement()->Clone();
02121                 return SubmitMetadata( pCollection.GetThisURI(), pMetadata );
02122         }
02123         
02124         return TRUE;
02125 }
02126 
02128 // CLibraryBuilderInternals CHM (threaded)
02129 
02130 BOOL CLibraryBuilderInternals::ReadCHM(HANDLE hFile, LPCTSTR pszPath)
02131 {
02132         CHAR szMagic[4];
02133         DWORD nVersion, nIHDRSize, nLCID, nRead, nPos, nComprVersion;
02134         QWORD nContentOffset;
02135         const DWORD MAX_LENGTH_ALLOWED = 8192;
02136         
02137         SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
02138         ReadFile( hFile, szMagic, 4, &nRead, NULL );
02139         
02140         if ( nRead != 4 || strncmp( szMagic, "ITSF", 4 ) )
02141                 return SubmitCorrupted();
02142         if ( GetFileSize( hFile, NULL ) < 510 ) return SubmitCorrupted();
02143 
02144         // Get CHM file version number
02145         ReadFile( hFile, &nVersion, sizeof(nVersion), &nRead, NULL );
02146         if ( nRead != sizeof(nVersion) || nVersion < 3 )
02147                 return FALSE; // In Version 2 files, content section data offset is not there
02148 
02149         // Get initial header size
02150         ReadFile( hFile, &nIHDRSize, sizeof(nIHDRSize), &nRead, NULL );
02151         if ( nRead != sizeof(nIHDRSize) || nIHDRSize == 0 ) return SubmitCorrupted();
02152         nPos = nIHDRSize - sizeof(nContentOffset);
02153 
02154         // Get Windows LCID of machine on which the file was compiled;
02155         // Always located at offset 20
02156         SetFilePointer( hFile, 20, NULL, FILE_BEGIN );
02157         ReadFile( hFile, &nLCID, sizeof(nLCID), &nRead, NULL );
02158         if ( nRead != sizeof(nLCID) ) return SubmitCorrupted();
02159         if ( !IsValidLocale( nLCID, LCID_SUPPORTED ) ) nLCID = 1033;
02160 
02161         // Read the last qword from the end of header; it contains content section data offset
02162         SetFilePointer( hFile, nPos, NULL, FILE_BEGIN );
02163         ReadFile( hFile, &nContentOffset, sizeof(nContentOffset), &nRead, NULL );
02164         if ( nRead != sizeof(nContentOffset) ) return SubmitCorrupted();
02165         if ( nContentOffset == 0 ) return FALSE;
02166 
02167         // Go to compressed control data and check version;
02168         // Content section data always takes 110 bytes (?)
02169         nContentOffset += 110;
02170         DWORD nError = NO_ERROR;
02171         DWORD nSizeLow  = (DWORD)( nContentOffset & 0xFFFFFFFF );
02172         DWORD nSizeHigh = (DWORD)( nContentOffset >> 32 );
02173 
02174         nSizeLow = SetFilePointer( hFile, nSizeLow, (long*)&nSizeHigh, FILE_BEGIN );
02175         if ( nSizeLow == INVALID_SET_FILE_POINTER && 
02176                  ( nError = GetLastError() ) != NO_ERROR ) return SubmitCorrupted();
02177         ReadFile( hFile, szMagic, 4, &nRead, NULL );
02178         if ( nRead != 4 || strncmp( szMagic, "LZXC", 4 ) ) // compression method
02179                 return FALSE;
02180         ReadFile( hFile, &nComprVersion, sizeof(nComprVersion), &nRead, NULL );
02181         if ( nRead != sizeof(nComprVersion) || nComprVersion != 2 ) // Note: MS Reader books has version 3
02182                 return FALSE;
02183 
02184         // Read no more than 8192 bytes to find "HHA Version" string
02185         CHAR szByte[1];
02186         CHAR* szFragment = new CHAR[10];
02187         BOOL bCorrupted = FALSE;
02188         BOOL bHFound = FALSE;
02189         int nFragmentPos = 0;
02190 
02191         ZeroMemory( szFragment, sizeof(CHAR) * 10 ); // "HA Version" string length
02192 
02193         for ( nPos = 0; ReadFile( hFile, &szByte, 1, &nRead, NULL ) && nPos++ < MAX_LENGTH_ALLOWED ; )
02194         {
02195                 if ( nRead != 1 ) 
02196                 {
02197                         bCorrupted = TRUE;
02198                         break;
02199                 }
02200                 if ( szByte[0] == 'H' )
02201                 {
02202                         nFragmentPos = 0;
02203                         szFragment[0] = 'H';
02204                         bHFound = TRUE;
02205                 }
02206                 else
02207                 {
02208                         nFragmentPos++;
02209                         if ( bHFound ) 
02210                         {
02211                                 if ( IsCharacter( szByte[0] ) ) 
02212                                         szFragment[ nFragmentPos ] = szByte[0];
02213                                 else
02214                                         szFragment[ nFragmentPos ] = ' ';
02215                         }
02216                 }
02217                 if ( nFragmentPos == 9 )
02218                 {
02219                         if ( !strncmp( szFragment, "HA Version", 10 ) ) 
02220                         {
02221                                 // Remember position two words before; 
02222                                 // the second word is data entry length
02223                                 nPos = SetFilePointer( hFile, 0, NULL, FILE_CURRENT ) - 15;
02224                                 break;
02225                         }
02226                         else
02227                         {
02228                                 nFragmentPos = 0;
02229                                 bHFound = FALSE;
02230                         }
02231                 }
02232         }
02233         if ( bCorrupted ) 
02234         {
02235                 delete [] szFragment;
02236                 return SubmitCorrupted();
02237         }
02238         if ( strncmp( szFragment, "HA Version", 10 ) && nPos == MAX_LENGTH_ALLOWED + 1 )
02239         {
02240                 delete [] szFragment;
02241                 return FALSE;
02242         }
02243         delete [] szFragment;
02244 
02245         // Collect author, title if file name contains "book" keyword
02246         CString strLine;
02247         BOOL bBook = ( _tcsistr( pszPath, _T("book") ) != NULL );
02248         
02249         CXMLElement* pXML = new CXMLElement( NULL, bBook ? _T("book") : _T("wordprocessing") );
02250         
02251         if ( LPCTSTR pszName = _tcsrchr( pszPath, '\\' ) )
02252         {
02253                 pszName++;
02254                 
02255                 if ( _tcsnicmp( pszName, _T("ebook - "), 8 ) == 0 )
02256                 {
02257                         strLine = pszName + 8;
02258                         strLine = strLine.SpanExcluding( _T(".") );
02259                         strLine.TrimLeft();
02260                         strLine.TrimRight();
02261                         pXML->AddAttribute( _T("title"), strLine );
02262                 }
02263                 else if ( _tcsnicmp( pszName, _T("(ebook"), 6 ) == 0 )
02264                 {
02265                         if ( pszName = _tcschr( pszName, ')' ) )
02266                         {
02267                                 if ( _tcsncmp( pszName, _T(") - "), 4 ) == 0 )
02268                                         strLine = pszName + 4;
02269                                 else
02270                                         strLine = pszName + 1;
02271                                 strLine = strLine.SpanExcluding( _T(".") );
02272                                 strLine.TrimLeft();
02273                                 strLine.TrimRight();
02274                                 pXML->AddAttribute( _T("title"), strLine );
02275                         }
02276                 }
02277         }
02278 
02279         // Meta data extraction
02280         WORD nData;
02281         CHARSETINFO csInfo;
02282         CString strTemp;
02283         TCHAR *pszBuffer = NULL;
02284         UINT nCodePage = CP_ACP;
02285         DWORD nCwc;
02286         UINT charSet = DEFAULT_CHARSET;
02287         BOOL bHasTitle = FALSE;
02288 
02289         // Find default ANSI codepage for given LCID
02290         DWORD nLength = GetLocaleInfo( nLCID, LOCALE_IDEFAULTANSICODEPAGE, NULL, 0 );
02291         pszBuffer = (TCHAR*)LocalAlloc( LPTR, ( nLength + 1 ) * sizeof(TCHAR) );
02292         nCwc = GetLocaleInfo( nLCID, LOCALE_IDEFAULTANSICODEPAGE, pszBuffer, nLength );
02293         if ( nCwc > 0 )
02294         {       
02295                 CString strTemp = pszBuffer;
02296                 strTemp = strTemp.Left( nCwc - 1 );
02297                 _stscanf( strTemp, _T("%lu"), &charSet );
02298                 if ( TranslateCharsetInfo( (LPDWORD)charSet, &csInfo, TCI_SRCCODEPAGE ) )
02299                         nCodePage = csInfo.ciACP;
02300         }
02301         SetFilePointer( hFile, nPos, NULL, FILE_BEGIN );
02302 
02303         for ( int nCount = 1 ; nCount < 5 && !bCorrupted ; nCount++ ) // nCount may be up to 6
02304         {
02305                 // Unknown data
02306                 ReadFile( hFile, &nData, sizeof(nData), &nRead, NULL );
02307                 if ( nRead != sizeof(nData) ) bCorrupted = TRUE;
02308 
02309                 // Entry length
02310                 ReadFile( hFile, &nData, sizeof(nData), &nRead, NULL );
02311                 if ( nRead != sizeof(nData) ) bCorrupted = TRUE;
02312                 if ( nData == 0 ) break;
02313                 if ( bCorrupted ) nData = 1;
02314 
02315                 CHAR* szMetadata = new CHAR[ nData ];
02316                 ReadFile( hFile, szMetadata, nData, &nRead, NULL );
02317                 if ( nRead != nData ) bCorrupted = TRUE;
02318 
02319                 if ( nCount == 2 ) 
02320                 {
02321                         delete [] szMetadata;
02322                         continue;
02323                 }
02324                 
02325                 // Convert meta data string from ANSI to unicode
02326                 int nWide = MultiByteToWideChar( nCodePage, 0, szMetadata, nData, NULL, 0 );
02327                 LPWSTR pszOutput = strLine.GetBuffer( nWide + 1 );
02328                 MultiByteToWideChar( nCodePage, 0, szMetadata, nData, pszOutput, nWide );
02329                 pszOutput[ nWide ] = 0;
02330                 strLine.ReleaseBuffer();
02331                 strLine.Trim();
02332 
02333                 int nPos;
02334 
02335                 switch ( nCount )
02336                 {
02337                         case 1: // version number
02338                                 nPos = strLine.ReverseFind( ' ' );
02339                                 strLine = strLine.Mid( nPos + 1 );
02340                                 if ( !bBook ) pXML->AddAttribute( _T("formatVersion"), strLine );
02341                         break;
02342                         case 2: // unknown data
02343                         break;
02344                         case 3: // redirection url
02345                                 CharLower( strLine.GetBuffer() );
02346                                 strLine.ReleaseBuffer();
02347                                 if ( strLine.Left( 7 ) == _T("ms-its:") )
02348                                 {
02349                                         nPos = strLine.Find( _T("::"), 7 );
02350                                         strTemp = _tcsrchr( pszPath, '\\' );
02351                                         strTemp = strTemp.Mid( 1 );
02352                                         CharLower( strTemp.GetBuffer() );
02353                                         strTemp.ReleaseBuffer();
02354                                         if ( strLine.Mid( 7, nPos - 7 ).Trim() != strTemp )
02355                                                 bCorrupted = TRUE; // it requires additional file
02356                                 }
02357                                 else if ( strLine.Left( 7 ) == _T("http://") )
02358                                         bCorrupted = TRUE; // redirects to external resource; may be dangerous
02359                         break;
02360                         case 4: // title
02361                                 if ( strLine.IsEmpty() ) break;
02362                                 nPos = strLine.Find( ',' );
02363                                 strTemp = strLine.Left( nPos );
02364                                 CharLower( strTemp.GetBuffer() );
02365                                 strTemp.ReleaseBuffer();
02366                                 if ( strLine.CompareNoCase( _T("htmlhelp") ) != 0 &&
02367                                          strTemp != _T("arial") && strTemp != _T("tahoma") &&
02368                                          strTemp != _T("times new roman") && strTemp != _T("verdana") &&
02369                                          strLine.CompareNoCase( _T("windows") ) != 0 )
02370                                 {
02371                                         bHasTitle = TRUE;
02372                                         nPos = strLine.ReverseFind( '\\' ); // remove paths in title
02373                                         strLine = strLine.Mid( nPos + 1 );
02374                                         pXML->AddAttribute( _T("title"), strLine );
02375                                 }
02376                         break;
02377                 }
02378                 delete [] szMetadata;
02379                 if ( bCorrupted ) 
02380                 {
02381                         delete pXML;
02382                         return SubmitCorrupted();
02383                 }
02384         }
02385 
02386         if ( !bHasTitle ) 
02387         {
02388                 delete pXML;
02389                 return FALSE;
02390         }
02391 
02392         pXML->AddAttribute( _T("format"), _T("Compiled HTML Help") );
02393         if ( bBook )
02394         {
02395                 pXML->AddAttribute( _T("back"), _T("Digital") );
02396                 strTemp = CSchema::uriBook;
02397         }
02398         else
02399                 strTemp = CSchema::uriDocument;
02400 
02401         return SubmitMetadata( strTemp, pXML );
02402 }

Generated on Thu Dec 15 10:39:44 2005 for Shareaza 2.2.1.0 by  doxygen 1.4.2