/*
* Copyright (c) 2004 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  This class implements an ID3v1 and v1.1 parser as specified in
*                www.id3.org.
*
*/




// INCLUDE FILES
#include	"MetaDataParserWMA.h"
#ifdef _DEBUG
#include	<e32svr.h>
#endif

#include <s32mem.h>

// CONSTANTS
/***
 *  File Properties Objects:
 *  Object ID,          GUID,   128 [bits]
 *  Object Size,        QWORD,  64 
 *  File ID,            GUID,   128 
 *  File Size,          QWORD,  64
 *  Creation Date,      QWORD,  64
 *  Data Packets Count, QWORD,  64
 *  Play Duration,      QWORD,  64
 *  Send Duration,      QWORD,  64
 *  Preroll,            QWORD,  64
 *  Flags,              DWORD,  32 
 */
const TInt KDurationOffset  = 64;       // duration offset from File Property object
const TInt KPrerollOffset   = 80;       // preRoll offset from File Property object
const TInt KFlagsOffset     = 88;       // flags offset from File Property object

// ASF Header Object GUIDs 
  
_LIT8 (KASFContentDescriptionObject, "75B22633668E11CFA6D900AA0062CE6C");
_LIT8 (KASFExtendedContentDescriptionObject, "D2D0A440E30711D297F000A0C95EA850");
_LIT8 (KASFHeaderObject, "75B22630668E11CFA6D900AA0062CE6C");
_LIT8 (KASFFilePropertiesObject, "8CABDCA1A94711CF8EE400C00C205365");
_LIT8 (KASFHeaderExtensionObject, "5FBF03B5A92E11CF8EE300C00C205365");
_LIT8 (KASFMetadataLibraryObject, "44231C94949849D1A1411D134E457054");
_LIT(KWMAlbumTitle, "WM/AlbumTitle\0");
_LIT(KWMPicture,	"WM/Picture\0");
_LIT(KWMText,		"WM/Text\0");
_LIT(KWMComposer,	"WM/Composer\0");
_LIT(KWMGenre,		"WM/Genre\0");
_LIT(KWMYear,		"WM/Year\0");
_LIT(KWMYear1,		"WM/OriginalReleaseYear\0"); 
_LIT(KWMOriginalArtist,	"WM/OriginalArtist\0");
_LIT(KWMTrackNumber,	"WM/TrackNumber\0");
_LIT(KWMUniqueFileIdentifier,	"WM/UniqueFileIdentifier\0");
_LIT(KWMAudioFileURL,	"WM/AudioFileURL\0");
_LIT(KWMSharedUserRating, "WM/SharedUserRating\0");
_LIT(KWMDate, "WM/OriginalReleaseTime\0");
#ifdef __WINDOWS_MEDIA
_LIT(KWMProvider, "WM/Provider\0");
#endif


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::CMetaDataParserWMA
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CMetaDataParserWMA::CMetaDataParserWMA(
	CMetaDataSource& aSource )
	:	iSource(aSource),
		iCharacterSetId(0)
	{
    }

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::ConstructL()
    {
	if ( ValidateL() )
		{
		User::LeaveIfError(iFs.Connect());
		// Get list of charconv supported character sets
		iCharacterSet = CCnvCharacterSetConverter::CreateArrayOfCharacterSetsAvailableL(iFs);
		}
#ifdef _DEBUG
	RDebug::Print(_L("CMetaDataParserWMA::ConstructL - Done"));
#endif
    }

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CMetaDataParserWMA* CMetaDataParserWMA::NewL(
	CMetaDataSource& aSource )
    {
#ifdef _DEBUG
	RDebug::Print(_L("CMetaDataParserWMA::NewL"));
#endif
	CMetaDataParserWMA* self = new( ELeave ) CMetaDataParserWMA(aSource);
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

// Destructor
CMetaDataParserWMA::~CMetaDataParserWMA()
	{
		delete iHeaderData;
		delete iCharacterSet;
		iFs.Close();
	}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ParseL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::ParseL(
	const RArray<TMetaDataFieldId>& aWantedFields,
	CMetaDataFieldContainer& aContainer )
    {
#ifdef _DEBUG
	RDebug::Print(_L("CMetaDataParserWMA::ParseL"));
#endif
	iContainer = &aContainer;

	if(iContentDescriptionObjectExists)
		{
		ParseContentDescriptionObject();
		}
	if(iExtendedContentDescriptionObjectExists)
		{
		ParseExtendedContentDescriptionObjectL();
		}
	if(iHeaderExtensionObjectExists)
		{
		ParseHeaderExtensionObjectL();
		if(iMetadataLibraryObjectExists)
			{
			ParseMetadataLibraryObjectL();
			}
		}	
	TInt err = KErrNone; // ignore err, as some entry may be extracted without exception
	if ( aWantedFields.Count() == 0 )
		{
		TRAP(err, GetTitleL());
		TRAP(err, GetAuthorL());
		TRAP(err, GetCopyrightL());
		TRAP(err, GetCommentL());
		TRAP(err, GetJpegL());
		TRAP(err, GetExtContDesEntryL(EMetaDataRating, iSharedUserRatingOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataAlbum, iAlbumTitleOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataComment, iTextOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataComposer, iComposerOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataGenre, iGenreOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataYear, iYearOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataOriginalArtist, iOriginalArtistOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataAlbumTrack, iTrackNumberOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataUniqueFileIdentifier, iUniqueFileIdentifierOffset));
		TRAP(err, GetExtContDesEntryL(EMetaDataUrl, iAudioFileURLOffset));
		TRAP(err, GetDurationL());
		TRAP(err, GetExtContDesEntryL(EMetaDataDate, iDateOffset));
	#ifdef __WINDOWS_MEDIA
		TRAP(err, GetExtContDesEntryL(EMetaDataVendor, iProviderOffset));
	#endif
		}
	else
		{
		// Look for it in the wanted field array
		TInt count( aWantedFields.Count() );
		for ( TInt i = 0; i < count; i++ )
			{
			switch ( aWantedFields[ i ] )
				{
				case EMetaDataSongTitle:
					TRAP(err, GetTitleL());
					break;
				case EMetaDataArtist:
					TRAP(err, GetAuthorL());
					break;
				case EMetaDataCopyright:
					TRAP(err, GetCopyrightL());
					break;
				case EMetaDataRating:
					TRAP(err, GetExtContDesEntryL(EMetaDataRating, iSharedUserRatingOffset));
					break;
				case EMetaDataJpeg:
					TRAP(err, GetJpegL());
					break;
				case EMetaDataAlbum:
					TRAP(err, GetExtContDesEntryL(EMetaDataAlbum, iAlbumTitleOffset));
					break;
				case EMetaDataComment:
					TRAP(err, GetExtContDesEntryL(EMetaDataComment, iTextOffset));
					break;
				case EMetaDataComposer:
					TRAP(err, GetExtContDesEntryL(EMetaDataComposer, iComposerOffset));
					break;
				case EMetaDataGenre:
					TRAP(err, GetExtContDesEntryL(EMetaDataGenre, iGenreOffset));
					break;
				case EMetaDataYear:
					TRAP(err, GetExtContDesEntryL(EMetaDataYear, iYearOffset));
					break;
				case EMetaDataOriginalArtist:
					TRAP(err, GetExtContDesEntryL(EMetaDataOriginalArtist, iOriginalArtistOffset));
					break;
				case EMetaDataAlbumTrack:
					TRAP(err, GetExtContDesEntryL(EMetaDataAlbumTrack, iTrackNumberOffset));
					break;
				case EMetaDataUniqueFileIdentifier:
					TRAP(err, GetExtContDesEntryL(EMetaDataUniqueFileIdentifier, iUniqueFileIdentifierOffset));
					break;
				case EMetaDataUrl:
					TRAP(err, GetExtContDesEntryL(EMetaDataUrl, iAudioFileURLOffset));
					break;
				case EMetaDataDuration:
					TRAP(err, GetDurationL());
					break;
				case EMetaDataDate:
					TRAP(err, GetExtContDesEntryL(EMetaDataDate, iDateOffset));
					break;
			#ifdef __WINDOWS_MEDIA
				case EMetaDataVendor:
					TRAP(err, GetExtContDesEntryL(EMetaDataVendor, iProviderOffset));
			#endif
				default:
					break;
				}
			}
		}
    }

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ValidateL
// -----------------------------------------------------------------------------
//
TBool CMetaDataParserWMA::ValidateL()
	{
	TInt size;
	iExists = EFalse;
	User::LeaveIfError( iSource.Size( size ) );
	if ( size <= 0 )
		{
		return EFalse;
		}

	// ASF_Header_Object GUID 128 bits.
	TBuf8<32> header; 
	iSource.ReadL(header, 16);
	if(header.Length() < 16)
		{
		return EFalse;
		}
	FormatGUID(header);
	if(header !=  KASFHeaderObject)
		{
		return EFalse;
		}
	// read header object size.
	iSource.ReadL(header, 8);
	TInt headerSize = ConvertToInt64(header);
	if(headerSize <= 30)
		{
		return EFalse; // header object is empty.
		}
	if(headerSize >= size) //Header Size is too high
		{
		return EFalse; // header size is more than the file size.
		}
	// read header object
	// 2~31 = 2 GB, size of header would not be greater than this,
	// also, HBufC does not have a NewL with TInt64 as arguement.
	iHeaderData = HBufC8::NewL(headerSize); 
	TPtr8 headerPtr = iHeaderData->Des(); 
	iSource.ReadL(headerPtr, headerSize - 24);
	
	TBuf8<4> objects = iHeaderData->Mid(0, 4);
	TInt noOfObjects = ConvertToInt32(objects);
	if(noOfObjects <= 0)
		{
		iExists = EFalse;
		return EFalse; // no objects. 
		}
        
	TInt objOffset = 6;
	if(objOffset+16 > iHeaderData->Length()) //Header Size is too small
		{
		return EFalse;
		} 
	TBuf8<32> objGUID = iHeaderData->Mid(objOffset, 16);
	FormatGUID(objGUID);
	TBool loop = ETrue;
	TInt objectCount = 0;
	while (loop)
		{
		if(!iContentDescriptionObjectExists && objGUID == KASFContentDescriptionObject)
			{
			iContentDescriptionObjectExists = ETrue;
			iContentDescriptionOffset = objOffset;
			}
		if(!iFilePropertiesObjectExists && objGUID == KASFFilePropertiesObject)
			{
			iFilePropertiesObjectExists = ETrue; // must exist
			iFilePropertiesOffset = objOffset;
			}
		if(!iExtendedContentDescriptionObjectExists && objGUID == 
			KASFExtendedContentDescriptionObject)
			{
			iExtendedContentDescriptionObjectExists = ETrue;
			iExtendedContentDescriptionOffset = objOffset;
			}
		if(!iHeaderExtensionObjectExists && objGUID == 
			KASFHeaderExtensionObject)
			{
			iHeaderExtensionObjectExists = ETrue;
			iHeaderExtensionOffset = objOffset;
			}	
		TBuf8<8> size = iHeaderData->Mid(objOffset + 16, 8); 
		TInt objSize = ConvertToInt64(size); // upper 32 bits?
		if(0 > objSize)
			{
			return EFalse;
			}
		objOffset += objSize;
		if(objOffset >= headerSize - 30 || 
			(iContentDescriptionObjectExists && iFilePropertiesObjectExists 
			&& iExtendedContentDescriptionObjectExists && iHeaderExtensionObjectExists) )
			{
			loop = EFalse;
			}
		else
			{
			objGUID = iHeaderData->Mid(objOffset, 16);
			FormatGUID(objGUID);
			objectCount++;

			if (objectCount == noOfObjects)
				{
				iExists = EFalse;
				return EFalse;	// gone through all objects		
				}
			}
		}
	if(iFilePropertiesObjectExists) 
		{
		iExists = ETrue;
		}
	return iExists;
	}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::FormatGUID
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::FormatGUID(TDes8 &aGUID)
{
	TBuf8<16> copyGUID(aGUID);
	TInt i;
	for(i = 0; i < 4; i++)
		{
		copyGUID[i] = aGUID[3-i];
		}
	for(i = 4; i < 6; i++)
		{
		copyGUID[i] = aGUID[9 - i];
		}
	for (i = 6; i < 8; i++)
		{
		copyGUID[i] = aGUID[13 - i];
		}
	for(i = 8; i < 16 ; i++)
		{
		copyGUID[i] = aGUID[i];
		}
	aGUID.Delete(0, 32);
	for(i = 0; i <16; i++)
		{
			aGUID.AppendNumFixedWidthUC(copyGUID[i], EHex, 2);
		}
}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ConvertToInt64
// -----------------------------------------------------------------------------
//
TInt64 CMetaDataParserWMA::ConvertToInt64(TDesC8& aDes)
{
	TInt64 num = 0;
	TInt i;
	for(i = 7 ; i >= 0; i--)
		{
		num <<= 8;
		num |= aDes[i];
		}
	return num;
}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ConvertToInt32
// -----------------------------------------------------------------------------
//
TInt CMetaDataParserWMA::ConvertToInt32(TDesC8& aDes)
{
	TInt num = 0;
	for(TInt i = 3 ; i >= 0; i--)
		{	
		num <<= 8;
		num |= aDes[i];
		}
	return num;
}


// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ConvertToInt16
// -----------------------------------------------------------------------------
//
TInt CMetaDataParserWMA::ConvertToInt16(TDesC8& aDes)
{
	TInt num = 0;
	for(TInt i = 1 ; i >= 0; i--)
		{	
		num <<= 8;
		num |= aDes[i];
		}
	return num;
}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ConvertDes8toDes16
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::ConvertDes8toDes16(const TDesC8& aDes8,TDes16& aDes16)
	{
	for(TInt i = 0 ; i < aDes8.Length(); i+=2)
		{
		if(aDes16.MaxLength() <= i/2)
			{
#ifdef _DEBUG
	RDebug::Print(_L("CMetaDataParserWMA::ConvertDes8toDes16: i/2[%d] >= aDes16's MaxLength[%d]."),
			i/2, aDes16.MaxLength());
#endif
			return;
			}
		aDes16.Append(aDes8[i]);
		}
	}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::GetTitleL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::GetTitleL()
	{
	if (iTitleLength <= 0)
		{
		return;
		}
	TInt offset = iContentDescriptionOffset + 34;
	if(iTitleLength - 2 <= 0 || offset + iTitleLength > iHeaderData->Length())
		{
		return; // empty title or corrupt title length
		}
	TPtrC8 title = iHeaderData->Mid(offset, iTitleLength - 2);
	HBufC* data16 = HBufC::NewLC( iTitleLength - 2);
	TPtr titleUnicode( data16->Des() );
	if ( ConvertToUnicodeL(title, titleUnicode) == KErrNone )
		{
		iContainer->AppendL( EMetaDataSongTitle, titleUnicode );
		}
	CleanupStack::PopAndDestroy();  // data16
	}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::GetArtistL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::GetAuthorL()
	{
	if (iAuthorLength <= 0)
		{
		return;
		}
	TInt offset = iContentDescriptionOffset + 34 + iTitleLength;
	if(iAuthorLength - 2 <= 0 || offset + iAuthorLength > iHeaderData->Length())
		{
		return; // empty author or corrupt author length
		}
	TPtrC8 author = iHeaderData->Mid(offset, iAuthorLength - 2);
	HBufC* data16 = HBufC::NewLC( iAuthorLength - 2);
	TPtr authorUnicode( data16->Des() );
	if ( ConvertToUnicodeL(author, authorUnicode) == KErrNone )
		{
		iContainer->AppendL( EMetaDataArtist, authorUnicode );
		}
	CleanupStack::PopAndDestroy();  // data16
	}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::GetCopyrightL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::GetCopyrightL()
	{
	if (iCopyrightLength <= 0)
		{
		return;
		}
	TInt offset = iContentDescriptionOffset + 34 + iTitleLength + iAuthorLength;
	if(iCopyrightLength - 2 <= 0 || offset + iCopyrightLength > iHeaderData->Length())
		{
		return; // empty or corrupt length
		}
	TPtrC8 copyright = iHeaderData->Mid(offset, iCopyrightLength - 2);
	HBufC* data16 = HBufC::NewLC( iCopyrightLength - 2);
	TPtr copyrightUnicode( data16->Des() );
	if ( ConvertToUnicodeL(copyright, copyrightUnicode) == KErrNone )
		{
		iContainer->AppendL( EMetaDataCopyright, copyrightUnicode );
		}
	CleanupStack::PopAndDestroy();  // data16
	}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::GetCommentL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::GetCommentL()
	{
	if (iDescriptionLength <= 0)
		{
		return;
		}
	TInt offset = iContentDescriptionOffset + 34 + iTitleLength + iAuthorLength + iCopyrightLength;
	if(iDescriptionLength - 2 <= 0 || offset + iDescriptionLength > iHeaderData->Length())
		{
		return; // empty comment or corrupt comment length
		}
	TPtrC8 comments = iHeaderData->Mid(offset, iDescriptionLength - 2);
	HBufC* data16 = HBufC::NewLC( iDescriptionLength - 2);
	TPtr commentsUnicode( data16->Des() );
	if ( ConvertToUnicodeL(comments, commentsUnicode) == KErrNone )
		{
		iContainer->AppendL( EMetaDataComment, commentsUnicode );
		}
	CleanupStack::PopAndDestroy();  // data16
	}


// -----------------------------------------------------------------------------
// CMetaDataParserWMA::GetJpegL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::GetJpegL()
{
	if (iPictureOffset <= 0)
	{
		return;
	}
	
	TInt offset = iPictureOffset;
	if(!iMetadatLibraryObjectJpegExists)
		{
		TPtrC8 dataType = iHeaderData->Mid(offset, 2);
		offset += 2;
		TInt dataTypeInt = ConvertToInt16(dataType);
		if(dataTypeInt != 1)
			{
			return;
			}
		
		TPtrC8 lengthData = iHeaderData->Mid(offset, 2);
		offset += 2;
		TInt length	= ConvertToInt16(lengthData);
		if(length <= 0)
			{
			return;
			}		
		}
	TPtrC8 picData = iHeaderData->Mid(offset, 1);
	offset += 1;
	TInt picType = 0;
	picType |= picData[0];
	if(picType != 3)
	{
		return; // only Front Album Cover supported
	}
	
	TPtrC8 picLengthData = iHeaderData->Mid(offset, 4);
	offset += 4;
	TInt picLength = ConvertToInt32(picLengthData);
	if(picLength <= 0)
	{
		return;
	}
	
	_LIT8(KNULL, "\0\0");
	TPtrC8 data = iHeaderData->Mid(offset, picLength);
	TInt pos = data.Find(KNULL);
	if(pos != 0)
	{
		pos++; // for unicode coding for strings. 
	}
	// check mime type
	if(pos != KErrNotFound)
	{
		HBufC8 *mimeType = iHeaderData->Mid(offset, pos + 2).AllocLC();
		offset += pos + 2;
		HBufC* name16 = HBufC::NewLC( (pos+2)/2);
		TPtr mimeType16( name16->Des() );
		ConvertDes8toDes16(*mimeType, mimeType16);
		_LIT(KJPEG, "image/jpeg\0");
		_LIT(KJPG, "image/jpg\0");
		if(mimeType16.Compare(KJPEG) != 0 && mimeType16.Compare(KJPG) != 0)
		{
			CleanupStack::PopAndDestroy(2, mimeType);
			return; // only JPEG & JPG supported
		}
		CleanupStack::PopAndDestroy(2); // mimeType16, mimeType
	}
		
	// skip the picture description
	TPtrC8 picDesc = iHeaderData->Mid(offset, picLength);
	pos = picDesc.Find(KNULL);
	if(pos != 0)
	{
		pos++; // for unicode coding for strings. 
	}
	offset += pos + 2;
	
	// picture data 
	TPtrC8 pic8 = iHeaderData->Mid(offset, picLength);	
	iContainer->AppendL( EMetaDataJpeg, pic8 );
}


// -----------------------------------------------------------------------------
// CMetaDataParserWMA::GetExtContDesEntryL
// -----------------------------------------------------------------------------
//
TBool CMetaDataParserWMA::GetExtContDesEntryL(TMetaDataFieldId aFieldId, TInt aOffset)
{
	TBool ret = EFalse;
	if(iExtendedContentDescriptionCount == 0)
		{
		return ret;
		}
	TPtrC8 dataType = iHeaderData->Mid(aOffset, 2);
	TInt dataTypeInt = ConvertToInt16(dataType);
	if(dataTypeInt == 0x00)
		{
		TPtrC8 lengthData = iHeaderData->Mid(aOffset + 2, 2);
		TInt length	= ConvertToInt16(lengthData);
		if(length <= 0)
			{
			return ret;
			}
		TPtrC8 data = iHeaderData->Mid(aOffset + 4, length - 2);
		HBufC* data16 = HBufC::NewLC(length - 2);
		TPtr dataUnicode( data16->Des() );
		if ( ConvertToUnicodeL(data, dataUnicode) == KErrNone )
			{
			iContainer->AppendL( aFieldId, dataUnicode );
			ret = ETrue;
			}
		CleanupStack::PopAndDestroy();  // data16
		}
	else if(dataTypeInt == 0x03)
		{
		TPtrC8 lengthData = iHeaderData->Mid(aOffset + 2, 2);
		TInt length	= ConvertToInt16(lengthData);
		if(length != 4)
			{
			return ret;
			}
		TPtrC8 data = iHeaderData->Mid(aOffset + 4, length);
		TInt dword = ConvertToInt32(data);
		const TInt KMaxIntBufferSize = 10;
		HBufC* data16 = HBufC::NewLC(KMaxIntBufferSize);
		TPtr dataString( data16->Des() );
		dataString.Num(dword);
		iContainer->AppendL( aFieldId, dataString );
		CleanupStack::PopAndDestroy();  // data16
		ret = ETrue;
		}
	return ret;
}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::GetDurationL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::GetDurationL()
	{
#ifdef _DEBUG
    RDebug::Print(_L("CMetaDataParserWMA::GetDuration()"));
#endif    
    TInt offset = iFilePropertiesOffset + KFlagsOffset;
    TPtrC8 flags = iHeaderData->Mid(offset, 4);
    TInt broadcastBit = (TInt) (flags[0] & 0x01);
    if(broadcastBit == 1)
        {
        return; // duration not valid.
        }
    
    offset = iFilePropertiesOffset + KDurationOffset;
    TPtrC8 duration8 = iHeaderData->Mid(offset, 8);     // 100 nanosec units
    TReal durationSec = ((TReal)ConvertToInt64(duration8)) / 10000000; // seconds
    
    offset = iFilePropertiesOffset + KPrerollOffset;
    TPtrC8 preRoll8 = iHeaderData->Mid(offset, 8);      // millisec units
    TReal preRollSec = ((TReal)ConvertToInt64(preRoll8)) / 1000; // seconds
    TReal sec = durationSec - preRollSec;               // not include preroll

    TBuf16<10> des16;	
    TRealFormat format(9, 3);                           // width=9, decimal place=3
    // Use fixed format, and do not use Triads
    format.iType = KRealFormatFixed | KDoNotUseTriads; 
    des16.Num(sec, format);	                            // convert to string
	
	iContainer->AppendL( EMetaDataDuration, des16 );
#ifdef _DEBUG
    RDebug::Print(_L("CMetaDataParserWMA::GetDuration(), duration=%f"), sec);
#endif 	
	
	}



// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ConvertToUnicodeL
// -----------------------------------------------------------------------------
//
TInt CMetaDataParserWMA::ConvertToUnicodeL(
	const TDesC8& aDesc,
	TDes16& aUnicode )
	{
	TPtrC8 unicodeData;
  TUint characterSetId = 0;
	CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
	TInt state = CCnvCharacterSetConverter::KStateDefault;

	// ASF (WMA) file format has UTF-16 LittleEndian characters.
	characterSetId = KCharacterSetIdentifierUnicodeLittle;
	unicodeData.Set( aDesc );
	
	charSetConv->PrepareToConvertToOrFromL(characterSetId, *iCharacterSet, iFs);
	TInt err = charSetConv->ConvertToUnicode(aUnicode, unicodeData, state);

#ifdef _DEBUG
	RDebug::Print(_L("CMetaDataParserID3v24::ConvertToUnicode :-> Tag Size[%d] Unicode Tag Size[%d]Bytes Unconverted[%d]"),
		unicodeData.Length(), aUnicode.Length(), err);
#endif

	CleanupStack::PopAndDestroy();	// charSetConv
	return err;
	}


// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ParseContentDescriptionObject
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::ParseContentDescriptionObject()
	{
		TBuf8<2> data = iHeaderData->Mid(iContentDescriptionOffset + 24, 2);
		iTitleLength = ConvertToInt16(data); 
		data = iHeaderData->Mid(iContentDescriptionOffset + 26, 2);
		iAuthorLength = ConvertToInt16(data); 
		data = iHeaderData->Mid(iContentDescriptionOffset + 28, 2);
		iCopyrightLength = ConvertToInt16(data); 
		data = iHeaderData->Mid(iContentDescriptionOffset + 30, 2);
		iDescriptionLength = ConvertToInt16(data); 
		data = iHeaderData->Mid(iContentDescriptionOffset + 32, 2);
		iRatingLength = ConvertToInt16(data);
		return;
	}


// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ParseExtendedContentDescriptionObjectL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::ParseExtendedContentDescriptionObjectL()
	{
	TBuf8<2> data  =  iHeaderData->Mid(iExtendedContentDescriptionOffset + 24, 2);
	iExtendedContentDescriptionCount = ConvertToInt16(data);
	TInt offset = iExtendedContentDescriptionOffset + 26;
	for(TInt i = 0 ; i < iExtendedContentDescriptionCount; i ++)
		{
		TBuf8<2> data1  =  iHeaderData->Mid(offset, 2);
		offset += 2;
		TInt nameLength = ConvertToInt16(data1);
		if(nameLength > 0 && iHeaderData->Length() >= (offset+nameLength))
			{
			HBufC8 *name = iHeaderData->Mid(offset, nameLength).AllocLC();
			offset += nameLength;
			HBufC* data16 = HBufC::NewLC(nameLength/2 == 0 ? 1 : nameLength/2);
			TPtr name16( data16->Des() );
			ConvertDes8toDes16(*name, name16);
			
			if(!name16.Compare(KWMAlbumTitle))
				{
				iAlbumTitleOffset = offset; 
				}
			else if(!name16.Compare(KWMPicture))
				{
				iPictureOffset = offset; 
				}
			else if(!name16.Compare(KWMText))
				{
				iTextOffset = offset; 
				}
			else if(!name16.Compare(KWMComposer))
				{
				iComposerOffset = offset; 
				}
			else if(!name16.Compare(KWMGenre))
				{
				iGenreOffset = offset; 
				}
			else if(!name16.Compare(KWMYear) || !name16.Compare(KWMYear1))
				{
				iYearOffset = offset; 
				}
			else if(!name16.Compare(KWMOriginalArtist))
				{
				iOriginalArtistOffset = offset; 
				}
			else if(!name16.Compare(KWMTrackNumber))
				{
				iTrackNumberOffset = offset; 
				}
			else if(!name16.Compare(KWMUniqueFileIdentifier))
				{
				iUniqueFileIdentifierOffset = offset; 
				}
			else if(!name16.Compare(KWMAudioFileURL))
				{
				iAudioFileURLOffset = offset; 
				}
			else if(!name16.Compare(KWMSharedUserRating))
				{
				iSharedUserRatingOffset = offset; 
				}
			else if(!name16.Compare(KWMDate))
				{
				iDateOffset = offset; 
				}
		#ifdef __WINDOWS_MEDIA
			else if(!name16.Compare(KWMProvider))
				{
				iProviderOffset = offset; 
				}
		#endif

			offset += 2;
			TBuf8<2> valueLengthData  =  iHeaderData->Mid(offset, 2);
			offset += 2;
			TInt valueLength = ConvertToInt16(valueLengthData);
			offset += valueLength;
			CleanupStack::PopAndDestroy(2); //data16, name
			if(iHeaderData->Length() < offset)
				{
#ifdef _DEBUG
	RDebug::Print(_L("CMetaDataParserWMA::ParseExtendedContentDescriptionObjectL: offset[%d] > iHeaderData's Length[%d]."),
			offset, iHeaderData->Length());
#endif
				return;
				}
			}
		}
	}

// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ParseMetadataLibraryObjectL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::ParseMetadataLibraryObjectL()
	{
	TBuf8<8> objectSizedata  =  iHeaderData->Mid(iMetadataLibraryOffset + 16, 8);
	iMetadataLibraryObjectSize = ConvertToInt64(objectSizedata);
	TBuf8<2> recordsCountdata  =  iHeaderData->Mid(iMetadataLibraryOffset + 24, 2);
	iMetadataLibraryDescriptionCount = ConvertToInt16(recordsCountdata);
	TInt offset = iMetadataLibraryOffset + 26;
	for(TInt i = 0 ; i < iMetadataLibraryDescriptionCount; i ++)
		{
		offset += 4; // skipping LanguageID (2 Bytes) and StreamID (2 Bytes) fields
		//Reading Name Length 
		TBuf8<2> nameLengthdata  =  iHeaderData->Mid(offset, 2);
		offset += 2;
		TInt nameLength = ConvertToInt16(nameLengthdata);
		TBuf8<2> dataTypedata  =  iHeaderData->Mid(offset, 2);
		offset += 2;
		TInt dataType = ConvertToInt16(dataTypedata);
		TBuf8<4> dataLengthdata  =  iHeaderData->Mid(offset, 4);
		offset += 4;
		TInt32 dataLength = ConvertToInt32(dataLengthdata);
		
		if(nameLength > 0)
			{
			HBufC8 *name = iHeaderData->Mid(offset, nameLength).AllocLC();
			offset += nameLength;
			HBufC* data16 = HBufC::NewLC( nameLength/2);
			TPtr name16( data16->Des() );
			ConvertDes8toDes16(*name, name16);
			if(!name16.Compare(KWMPicture))
				{
				iPictureOffset = offset;
				iMetadatLibraryObjectJpegExists = ETrue; 
				}
			offset += dataLength;
			CleanupStack::PopAndDestroy(2); //data16, name
			}
		}
	}
	
// -----------------------------------------------------------------------------
// CMetaDataParserWMA::ParseHeaderExtensionObjectL
// -----------------------------------------------------------------------------
//
void CMetaDataParserWMA::ParseHeaderExtensionObjectL()
	{
	TInt length = iHeaderData->Length();
    if ( iHeaderExtensionOffset + 46 > length)
        {
        return; 
        }
        
	TBuf8<8> objectSizedata  =  iHeaderData->Mid(iHeaderExtensionOffset + 16, 8);
	iHeaderExtensionObjectSize = ConvertToInt64(objectSizedata);
	TBuf8<4> headerExtensiondata  =  iHeaderData->Mid(iHeaderExtensionOffset + 42, 4);
	
	TInt32 headerExtensionDataSize = ConvertToInt32(headerExtensiondata);
	if(headerExtensionDataSize != iHeaderExtensionObjectSize - 46)
	{
		return;
	}
	TInt objOffset = iHeaderExtensionOffset + 46;
	
    if ((objOffset < 0) || (objOffset + 16 > length))   // verify objOffset is not negative
        {
        return; 
        }	
	TBuf8<32> objGUID = iHeaderData->Mid(objOffset, 16);
	FormatGUID(objGUID);
	TBool loop = ETrue;
	while (loop)
		{
		if(!iMetadataLibraryObjectExists && objGUID == 
			KASFMetadataLibraryObject)
			{
			iMetadataLibraryObjectExists = ETrue;
			iMetadataLibraryOffset = objOffset;
			}
				
        if (objOffset + 24 > length)
            {
            return; 
            }	
			
		TBuf8<8> size = iHeaderData->Mid(objOffset + 16, 8); 
		TInt objSize = ConvertToInt64(size); // upper 32 bits?
		if(objSize == 0)
			{
			return;
			}
		objOffset += objSize;
		
        if ((objOffset < 0) || (objOffset + 16 > length))
            {
            return; 
            }				
		objGUID = iHeaderData->Mid(objOffset, 16);
		FormatGUID(objGUID);
		TInt val = objOffset - iHeaderExtensionOffset; 
		if((val > 0) && (val < iHeaderExtensionObjectSize))
			{
			loop = ETrue;
			}
		else
			{
			loop = EFalse;
			}
		}
	}	

//  End of File
