/*
* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
* @internalComponent 
* @released
*
*/

#include <e32rom.h>
#include "rom_image_reader.h"
#include "r_rom.h"
#ifdef __LINUX__
#define _alloca alloca 
#endif
#include "utf16string.h"
void InflateUnCompress(unsigned char* source, int sourcesize, unsigned char* dest, int destsize);
RomImageFSEntry::RomImageFSEntry (const char* aName) : iName(aName), iSibling(0), iChildren(0) {
}
RomImageFSEntry::~RomImageFSEntry() {
	if(iChildren){
		delete iChildren;
		iChildren = NULL ;
	}
	if(iSibling){
		delete iSibling ;
		iSibling = NULL ;
	}	
}

//Rom Image header 
RomImageHeader::RomImageHeader(char* aHdr, EImageType aImgType) {
	switch( aImgType) {
	case EROM_IMAGE:
		iLoaderHdr = (TRomLoaderHeader*)aHdr;
		iRomHdr = (TRomHeader*)(aHdr + sizeof(TRomLoaderHeader));
		iExtRomHdr = 0;
		break;

	case EROMX_IMAGE:
		iExtRomHdr = (TExtensionRomHeader*)(aHdr);
		iRomHdr = 0;
		iLoaderHdr = 0;
		break;

	case EBAREROM_IMAGE:
		iLoaderHdr = 0;
		iRomHdr = (TRomHeader*)aHdr;
		iExtRomHdr = 0;
		break;
	default:
		iExtRomHdr = 0;
		iRomHdr = 0;
		iLoaderHdr = 0;
		break ;
	}
}


void RomImageHeader::DumpRomHdr() {
	if(iLoaderHdr)
		*out << "ROM Image " << endl << endl;
	else
		*out << "Bare ROM Image" << endl << endl;
	TUint aPos = 0;
	bool aContinue = true;

	*out << "Image Signature .................";

	if(iLoaderHdr){     
		char temp[KRomNameSize + 1];
		memcpy(temp,reinterpret_cast<char*>(iLoaderHdr),KRomNameSize);        
		temp[KRomNameSize] = 0;
		*out << temp;
	}

	*out << endl << endl;

	DumpInHex("Timestamp", (iRomHdr->iTime >> 32)) ;
	DumpInHex(" ", (iRomHdr->iTime &0xffffffff), aContinue) << endl;

	DumpInHex("RomBase", iRomHdr->iRomBase) << endl;

	DumpInHex("RomSize", iRomHdr->iRomSize) << endl;
	DumpInHex("KernelDataAddress", iRomHdr->iKernDataAddress) << endl;
	DumpInHex("KernelLimit", iRomHdr->iKernelLimit) << endl;
	DumpInHex("PrimaryFile", iRomHdr->iPrimaryFile) << endl;
	DumpInHex("SecondaryFile", iRomHdr->iSecondaryFile) << endl;
	DumpInHex("CheckSum", iRomHdr->iCheckSum) << endl;
	DumpInHex("Hardware", iRomHdr->iHardware) << endl;

	DumpInHex("Language", (TUint)(iRomHdr->iLanguage >> 32));
	DumpInHex(" ", ((TUint)(iRomHdr->iLanguage & 0xffffffff)), aContinue) <<endl;

	DumpInHex("KernelConfigFlags", iRomHdr->iKernelConfigFlags) << endl;
	DumpInHex("RomExceptionSearchTable", iRomHdr->iRomExceptionSearchTable) << endl;
	DumpInHex("RomHeaderSize", iRomHdr->iRomHeaderSize) << endl;
	DumpInHex("RomSectionHeader", iRomHdr->iRomSectionHeader) << endl;
	DumpInHex("TotalSvDataSize", iRomHdr->iTotalSvDataSize) << endl;
	DumpInHex("VariantFile", iRomHdr->iVariantFile) << endl;
	DumpInHex("ExtensionFile", iRomHdr->iExtensionFile) << endl;
	DumpInHex("RelocInfo", iRomHdr->iRelocInfo) << endl;
	DumpInHex("OldTraceMask", iRomHdr->iOldTraceMask) << endl;
	DumpInHex("UserDataAddress", iRomHdr->iUserDataAddress) << endl;
	DumpInHex("TotalUserDataSize", iRomHdr->iTotalUserDataSize) << endl;
	DumpInHex("DebugPort", iRomHdr->iDebugPort) << endl;

	DumpInHex("Version", iRomHdr->iVersion.iMajor, false, 2);
	DumpInHex(".", iRomHdr->iVersion.iMinor, aContinue, 2);
	DumpInHex("(" ,iRomHdr->iVersion.iBuild, aContinue, 2);
	*out << ")" << endl;

	DumpInHex("CompressionType", iRomHdr->iCompressionType) << endl;
	DumpInHex("CompressedSize", iRomHdr->iCompressedSize) << endl;
	DumpInHex("UncompressedSize", iRomHdr->iUncompressedSize) << endl;
	DumpInHex("HcrFileAddress", iRomHdr->iHcrFileAddress) << endl;

	DumpInHex("DisabledCapabilities", iRomHdr->iDisabledCapabilities[0]);
	DumpInHex(" ", iRomHdr->iDisabledCapabilities[1], aContinue) << endl;

	DumpInHex("TraceMask", iRomHdr->iTraceMask[0]);
	aPos = 1;
	while( aPos < (TUint)KNumTraceMaskWords) {
		if(iRomHdr->iTraceMask[aPos]) {
			DumpInHex(" ", iRomHdr->iTraceMask[aPos++], aContinue);
		}
		else {
			DumpInHex(" ", iRomHdr->iTraceMask[aPos++], aContinue, 1);
		}

	}

	*out << endl << endl;

}
void RomImageHeader::DumpRomXHdr() {
	*out << "Extension ROM Image" << endl << endl;
	bool aContinue = true;

	DumpInHex("Timestamp", (iExtRomHdr->iTime >> 32)) ;
	DumpInHex(" ", (iExtRomHdr->iTime &0xffffffff), aContinue) << endl;

	DumpInHex("RomBase", iExtRomHdr->iRomBase) << endl;

	DumpInHex("RomSize", iExtRomHdr->iRomSize) << endl;
	DumpInHex("CheckSum", iExtRomHdr->iCheckSum) << endl;

	DumpInHex("Version", iExtRomHdr->iVersion.iMajor, false, 2);
	DumpInHex(".", iExtRomHdr->iVersion.iMinor, aContinue, 2);
	DumpInHex("(" ,iExtRomHdr->iVersion.iBuild, aContinue, 2);
	*out << ")" << endl;

	DumpInHex("CompressionType", iExtRomHdr->iCompressionType) << endl;
	DumpInHex("CompressedSize", iExtRomHdr->iCompressedSize) << endl;
	DumpInHex("UncompressedSize", iExtRomHdr->iUncompressedSize) << endl;

	*out << endl << endl;

}
//Rom Image reader
RomImageReader::RomImageReader(const char* aFile, EImageType aImgType) :
ImageReader(aFile), iImageHeader(0),
iRomSize(0),iHeaderBuffer(0),
iRomLayoutData(0),iImgType(aImgType) {
	iRomImageRootDirEntry = new RomImageDirEntry("");
}

RomImageReader::~RomImageReader() {
	if(iFile.is_open())
		iFile.close();
	if(iHeaderBuffer)
		delete []iHeaderBuffer;
	if(iRomLayoutData)
		delete []iRomLayoutData;  
	delete iRomImageRootDirEntry;


}

void RomImageReader::ReadImage() {
	if(iFile.is_open()) return ;
	iFile.open(iImgFileName.c_str(), ios_base::binary | ios_base::in);
	if( !iFile.is_open() ) {
		throw ImageReaderException(iImgFileName.c_str(), "Cannot open file ");
	}	

	TUint headerSize = GetHdrSize() ;	
	if(headerSize > 0){
		iHeaderBuffer = new char[headerSize];
		iFile.read(iHeaderBuffer,headerSize);
	}	
}

void RomImageReader::Validate() {
}

TUint32 RomImageReader::GetImageCompressionType() {
	if(iImageHeader->iRomHdr )// iImageType == EROM_IMAGE
		return iImageHeader->iRomHdr->iCompressionType;
	else
		return iImageHeader->iExtRomHdr->iCompressionType;
}


TLinAddr RomImageReader::GetRomBase() {
	if(iImageHeader->iRomHdr )// iImageType == EROM_IMAGE
		return iImageHeader->iRomHdr->iRomBase ;
	else
		return iImageHeader->iExtRomHdr->iRomBase;
}

TLinAddr RomImageReader::GetRootDirList() {
	if(iImageHeader->iRomHdr )// iImageType == EROM_IMAGE
		return iImageHeader->iRomHdr->iRomRootDirectoryList;
	else
		return iImageHeader->iExtRomHdr->iRomRootDirectoryList;
}

TUint RomImageReader::GetHdrSize() {
	TUint headerSize = 0;
	if(EROM_IMAGE == iImgType){
		headerSize = sizeof(TRomLoaderHeader) + sizeof(TRomHeader);
	}else if(EROMX_IMAGE == iImgType){
		headerSize = sizeof(TExtensionRomHeader);
	}else if(EBAREROM_IMAGE == iImgType){
		headerSize = sizeof(TRomHeader);
	}
	return headerSize;
}


static const TInt KIOBytes = 0x100000;
// reading a huge buffer at a time is very slow, reading 1MB bytes at a time is much faster
void RomImageReader::ReadData(char* aBuffer, TUint aLength) {
	TUint readBytes = 0 ;
	while(readBytes < aLength){
		TUint toRead = KIOBytes;
		if(readBytes + toRead > aLength)
			toRead = aLength - readBytes ;
		iFile.read(&aBuffer[readBytes],toRead);
		readBytes += toRead ;
	}	
}
void RomImageReader::ProcessImage() {
	if(iRomLayoutData) return ;
	iImageHeader = new RomImageHeader(iHeaderBuffer, iImgType);

	iFile.seekg(0, ios_base::end);
	// fileSize
	TUint fileSize = iFile.tellg(); 

	//let's skip the RomLoaderHeader
	TUint romDataBegin = (EROM_IMAGE == iImgType) ? sizeof(TRomLoaderHeader) : 0 ; 
	iFile.seekg(romDataBegin,ios_base::beg);	

	if(EROMX_IMAGE == iImgType){// EROMX_IMAGE, just set the iUnpagedRomBuffer		
		if(GetImageCompressionType() == KUidCompressionDeflate){
			TUint32 readLen = fileSize - sizeof(TExtensionRomHeader) ; 
			iRomSize = iImageHeader->iExtRomHdr->iUncompressedSize ;	 
			iRomLayoutData = new char[iRomSize];
			char* temp = new char[readLen];
			// header is not compressed.
			iFile.read(iRomLayoutData,sizeof(TExtensionRomHeader)); 
			ReadData(temp ,readLen);
			TUint8* uncompressDest = reinterpret_cast<TUint8*>(iRomLayoutData + sizeof(TExtensionRomHeader));
			InflateUnCompress(reinterpret_cast<TUint8*>(temp),readLen,uncompressDest,iRomSize - sizeof(TExtensionRomHeader));
			delete []temp ;	 

		}else{
			iRomSize = fileSize  ;
			iRomLayoutData = new char[iRomSize]; 
			ReadData(iRomLayoutData,iRomSize);
		}	 
	} // end EROMX_IMAGE
	else {
		//EROM_IMAGE or EBAREROM_IMAGE
		const TInt KPageSize = 0x1000; 
		TRomHeader *hdr = iImageHeader->iRomHdr; 
		iRomSize = hdr->iUncompressedSize ; 
		iRomLayoutData = new char[iRomSize]; 
		char* curDataPointer = iRomLayoutData ;
		TUint totalReadBytes = 0;
		bool hasPageableSec = (hdr->iPageableRomStart > 0 && hdr->iPageableRomSize > 0) ;

		// read data before unpaged 
		ReadData(curDataPointer,hdr->iCompressedUnpagedStart); 
		curDataPointer += hdr->iCompressedUnpagedStart ; 
		totalReadBytes += hdr->iCompressedUnpagedStart;		

		// read the unpaged part, if compressed , then decompress		 
		if(GetImageCompressionType() == KUidCompressionDeflate){
			char* temp = new char[hdr->iUnpagedCompressedSize];
			ReadData(temp,hdr->iUnpagedCompressedSize);
			totalReadBytes += hdr->iUnpagedCompressedSize;
			InflateUnCompress(reinterpret_cast<TUint8*>(temp),hdr->iUnpagedCompressedSize,reinterpret_cast<TUint8*>(curDataPointer) ,hdr->iUnpagedUncompressedSize);
			delete []temp ;			
		}else{
			TUint unpagedSeclen ;
			if(hasPageableSec) { // there is paged section 
				unpagedSeclen = hdr->iPageableRomStart - hdr->iCompressedUnpagedStart;
			}else{
				unpagedSeclen = iRomSize - hdr->iCompressedUnpagedStart;
			}
			ReadData(curDataPointer,unpagedSeclen);			 
			totalReadBytes += unpagedSeclen ;
		}
		curDataPointer = iRomLayoutData + totalReadBytes; 
		//if there is a paged section , read and extract it 

		// read the paged section,
		if(hasPageableSec){		
			if((TUint)(hdr->iPageableRomStart + hdr->iPageableRomSize) > iRomSize){
				throw ImageReaderException("Incorrect values of ROM header fields.", "");
			}
			if(0 == hdr->iRomPageIndex){ //
				// no compression for paged section ,just read it 			 
				iFile.read(curDataPointer,iRomSize - totalReadBytes);
			}
			else{
				//aligment calculation
				TUint pagedSecOffset = (totalReadBytes + KPageSize - 1) & (~(KPageSize - 1));
				if(pagedSecOffset > totalReadBytes){
					// there are gap bytes between unpaged and paged sections
					iFile.read(curDataPointer,pagedSecOffset - totalReadBytes);
					curDataPointer = iRomLayoutData + pagedSecOffset;
				}
				TUint pagedIndexCount = ( hdr->iUncompressedSize + KPageSize - 1) / KPageSize;
				// how many bytes ?
				// the page index table include the unpaged part ;
				TUint firstPagedIndexTblItem = hdr->iPageableRomStart / KPageSize;

				TUint tempBufLen = KPageSize << 8;
				char* readBuffer = new char[tempBufLen]; 

				TUint8* src,*srcNext = NULL;
				SRomPageInfo* pageInfo = reinterpret_cast<SRomPageInfo*>(&iRomLayoutData[hdr->iRomPageIndex]);
				CBytePair bpe;
				for(TUint i = firstPagedIndexTblItem ; i < pagedIndexCount ; i+= 256){
					TUint endIndex = i + 255 ;
					if(endIndex >= pagedIndexCount)
						endIndex = pagedIndexCount - 1;
					TUint readLen = pageInfo[endIndex].iDataStart + pageInfo[endIndex].iDataSize - pageInfo[i].iDataStart ; 
					iFile.read(readBuffer,readLen);
					src = reinterpret_cast<TUint8*>(readBuffer);
					for(TUint j = i ; j <= endIndex ; j++){
						switch(pageInfo[j].iCompressionType) {
						case SRomPageInfo::EBytePair: {
							TInt unpacked = bpe.Decompress(reinterpret_cast<TUint8*>(curDataPointer), KPageSize, src, pageInfo[j].iDataSize, srcNext);
							if (unpacked < 0) {
								delete []readBuffer;
								throw ImageReaderException("Corrupted BytePair compressed ROM image", "");
							}
							curDataPointer += unpacked;
							break ;
													  }
						case SRomPageInfo::ENoCompression:
							memcpy(curDataPointer,src,pageInfo[j].iDataSize);
							curDataPointer +=  pageInfo[j].iDataSize ;
							break ;
						default:
							delete []readBuffer;
							throw ImageReaderException("Undefined compression type", "");
							break ;

						}
						src += pageInfo[j].iDataSize; 
					} // end for(TUint j = i ; j <= endIndex ; j++) 
				} // end for(TUint i = firstPagedIndexTblItem ; i < pagedIndexCount ; i+= 256) 
				delete []readBuffer ;
			}// else

		} // if(hasPageableSec)  

	}
	TUint32 offset = GetRootDirList() - GetRomBase(); 
	TRomRootDirectoryList* rootDirList = reinterpret_cast<TRomRootDirectoryList*>(iRomLayoutData + offset );
	TInt dirCount = rootDirList->iNumRootDirs ; 
	string tempName ;
	for(TInt i = 0 ; i < dirCount ; i++) {
		offset = rootDirList->iRootDir[i].iAddressLin - GetRomBase(); 
		TRomDir* romDir = reinterpret_cast<TRomDir*>(iRomLayoutData + offset);	
		UTF16String unistr(reinterpret_cast<const TUint16*>(romDir->iEntry.iName),romDir->iEntry.iNameLength); 
		if(!unistr.ToUTF8(tempName)) //not utf16?
			tempName.assign(reinterpret_cast<const char*>(romDir->iEntry.iName),romDir->iEntry.iNameLength) ;
		BuildDir(romDir, iRomImageRootDirEntry);		 
	}

}


void RomImageReader::BuildDir(TRomDir* aDir, RomImageFSEntry* aPaFSEntry)
{	 

	TInt processBytes = 0 ;
	TInt totalDirBytes = aDir->iSize - sizeof(aDir->iSize);
	TRomEntry* entry = &aDir->iEntry;
	RomImageFSEntry *fsEntry ;
	string pritableName ; 

	while(processBytes < totalDirBytes){ 
		UTF16String unistr(reinterpret_cast<const TUint16*>(entry->iName),entry->iNameLength);
		if(!unistr.ToUTF8(pritableName)) 
			pritableName.assign(reinterpret_cast<const char*>(entry->iName),entry->iNameLength);
		if(entry->iAtt & 0x10) { // is a directory
			fsEntry = new RomImageDirEntry(pritableName.c_str()); 
			AddChild(aPaFSEntry,fsEntry,NULL);
			TUint offset = entry->iAddressLin - GetRomBase();
			TRomDir* subFolder = reinterpret_cast<TRomDir*>(iRomLayoutData + offset);		 
			BuildDir(subFolder,fsEntry); 
		}
		else{
			fsEntry = new RomImageFileEntry(pritableName.c_str()); 
			AddChild(aPaFSEntry,fsEntry,entry);
		} 
		// increase the processedBytes
		processBytes += (entry->iNameLength << 1) +  reinterpret_cast<TInt>(entry->iName) - reinterpret_cast<TInt>(entry); 

		//align to next 4 bytes
		processBytes = (processBytes + 3) & ( ~3 ); 
		// get next entry
		entry =  reinterpret_cast<TRomEntry*>( reinterpret_cast<char*>(&aDir->iEntry) + processBytes);
	} 

}

void RomImageReader::AddChild(RomImageFSEntry *aParent, RomImageFSEntry *aChild, TRomEntry* aRomEntry) {
	if(!aParent->iChildren)
		aParent->iChildren = aChild;
	else {
		RomImageFSEntry *aLast = aParent->iChildren;

		while(aLast->iSibling)
			aLast = aLast->iSibling;
		aLast->iSibling = aChild;
	}

	if(!aChild->IsDirectory()) { 
		RomImageFileEntry* file = static_cast<RomImageFileEntry*>(aChild);
		file->iTRomEntryPtr = aRomEntry;

		if(aRomEntry->iAddressLin > GetRomBase()) {
			TUint32 offset = aRomEntry->iAddressLin - GetRomBase();	 

			TRomImageHeader* imgHdr = reinterpret_cast<TRomImageHeader*>(iRomLayoutData + offset);

			file->ImagePtr.iRomFileEntry = imgHdr;

			TUint8 aUid1[4];
			memcpy(aUid1, &file->ImagePtr.iRomFileEntry->iUid1, 4);

			if( ReaderUtil::IsExecutable(aUid1) ) {
				file->iExecutable = true;
			}
			else {
				file->iExecutable = false;
				file->ImagePtr.iDataFileAddr = aRomEntry->iAddressLin;
			}
		}
		else {
			file->ImagePtr.iRomFileEntry = NULL; 
		}
	}

	if(aParent != iRomImageRootDirEntry) {
		aChild->iPath = aParent->iPath;
		aChild->iPath += SLASH_CHAR1;
		aChild->iPath += aParent->iName.c_str();
	}
}

void RomImageReader::DumpTree() {
	RomImageFSEntry* aFsEntry = iRomImageRootDirEntry;
	if( aFsEntry->iChildren ) 
		DumpSubTree(aFsEntry->iChildren); 
}

void RomImageReader::DumpDirStructure() {
	*out << "Directory Listing" << endl;
	*out << "=================" << endl;
	int aPadding = 0;
	if( iRomImageRootDirEntry ){
		DumpDirStructure(iRomImageRootDirEntry, aPadding);
	}	
	*out << endl << endl;
}

void RomImageReader::DumpDirStructure(RomImageFSEntry* aEntry, int &aPadding) {
	if(!aEntry)
		return;

	int aPadLen = 2 * aPadding;//scaling for legibility
	for (int i = 0; i < aPadLen; i++)
		*out << " ";

	*out << aEntry->Name() << endl;

	if( aEntry->iChildren){
		aPadding++;
		DumpDirStructure(aEntry->iChildren, aPadding);
		aPadding--;
	}
	DumpDirStructure(aEntry->iSibling, aPadding);
}

void RomImageReader::DumpSubTree(RomImageFSEntry* aFsEntry) {
	if(!aFsEntry)
		return;

	if( aFsEntry->iChildren ){
		DumpSubTree(aFsEntry->iChildren);
	}

	if( aFsEntry->iSibling )
		DumpSubTree(aFsEntry->iSibling);

	RomImageFSEntry *aEntry = aFsEntry;

	if(!aEntry->IsDirectory()) {
		*out << "********************************************************************" << endl;
		*out << "File........................" << aEntry->iPath.c_str() << SLASH_CHAR1 << aEntry->Name() << endl;

		if( ((RomImageFileEntry*)aEntry)->iExecutable )	{
			DumpImage((RomImageFileEntry*)aEntry);
		}
		else{
			*out << "Linear Addr................." << ((RomImageFileEntry*)aEntry)->ImagePtr.iDataFileAddr << endl;
		}
	}
}

void RomImageReader::DumpImage(RomImageFileEntry * aEntry) {
	bool aContinue = true;

	DumpInHex("Load Address", aEntry->iTRomEntryPtr->iAddressLin) << endl;
	DumpInHex("Size", aEntry->iTRomEntryPtr->iSize) << endl;

	TRomImageHeader		*aRomImgEntry = aEntry->ImagePtr.iRomFileEntry;

	if( !aRomImgEntry )
		return;
	//UIDs
	DumpInHex("Uids", aRomImgEntry->iUid1);
	DumpInHex(" ", aRomImgEntry->iUid2, aContinue);
	DumpInHex(" ", aRomImgEntry->iUid3, aContinue);
	DumpInHex(" ", aRomImgEntry->iUidChecksum, aContinue) << endl;

	DumpInHex("Entry point", aRomImgEntry->iEntryPoint) << endl;
	DumpInHex("Code start addr", aRomImgEntry->iCodeAddress) << endl;
	DumpInHex("Data start addr", aRomImgEntry->iDataAddress) << endl;
	DumpInHex("DataBssLinearBase", aRomImgEntry->iDataBssLinearBase) << endl;
	DumpInHex("Text size", aRomImgEntry->iTextSize) << endl;
	DumpInHex("Code size", aRomImgEntry->iCodeSize) << endl;
	DumpInHex("Data size", aRomImgEntry->iDataSize) << endl;
	DumpInHex("Bss size", (aRomImgEntry->iBssSize)) << endl;
	DumpInHex("Total data size", aRomImgEntry->iTotalDataSize) << endl;
	DumpInHex("Heap min", aRomImgEntry->iHeapSizeMin) << endl;
	DumpInHex("Heap max", aRomImgEntry->iHeapSizeMax) << endl;
	DumpInHex("Stack size", aRomImgEntry->iStackSize) << endl;

	TDllRefTable *aRefTbl = NULL;

	if( aRomImgEntry->iDllRefTable ) {
		TUint32 aOff = (TUint32)aRomImgEntry->iDllRefTable - iImageHeader->iRomHdr->iRomBase;
		aRefTbl = (TDllRefTable*) ((char*)iImageHeader->iRomHdr + aOff);
		TUint32 aVirtualAddr = (TUint32)aRefTbl->iEntry[0];
		DumpInHex("Dll ref table", aVirtualAddr) << endl;
	}

	DumpInHex("Export directory", aRomImgEntry->iExportDir) << endl;
	DumpInHex("Export dir count", aRomImgEntry->iExportDirCount) << endl;
	DumpInHex("Hardware variant", aRomImgEntry->iHardwareVariant) << endl;
	DumpInHex("Flags", aRomImgEntry->iFlags) << endl;
	DumpInHex("Secure ID", aRomImgEntry->iS.iSecureId) << endl;
	DumpInHex("Vendor ID", aRomImgEntry->iS.iVendorId) << endl;

	DumpInHex("Capability", aRomImgEntry->iS.iCaps[1]);
	DumpInHex(" ", aRomImgEntry->iS.iCaps[0], aContinue) << endl;

	*out << "Tools Version..............." << dec << (TUint)aRomImgEntry->iToolsVersion.iMajor;
	*out << "." ; 
	out->width(2) ;
	out->fill('0');
	*out << dec << (TUint)aRomImgEntry->iToolsVersion.iMinor ;
	*out << "(" << dec << aRomImgEntry->iToolsVersion.iBuild << ")";
	*out << endl;

	*out << "Module Version.............." << dec << (aRomImgEntry->iModuleVersion >> 16) << endl;
	DumpInHex("Exception Descriptor", aRomImgEntry->iExceptionDescriptor) << endl;
	*out << "Priority...................." << dec << aRomImgEntry->iPriority << endl;

	if( aRefTbl )
		DumpInHex("Dll ref table size", aRefTbl->iNumberOfEntries*8) << endl;
	else
		DumpInHex("Dll ref table size", 0) << endl;

	if( iDisplayOptions & DUMP_E32_IMG_FLAG){
		if(stricmp(iE32ImgFileName.c_str(), aEntry->Name()) == 0){
			TUint aSectionOffset = aRomImgEntry->iCodeAddress - iImageHeader->iRomHdr->iRomBase;
			TUint* aCodeSection = (TUint*)((char*)iImageHeader->iRomHdr + aSectionOffset);
			*out << "\nCode (Size=0x" << hex << aRomImgEntry->iCodeSize << ")" << endl;
			DumpData(aCodeSection, aRomImgEntry->iCodeSize);

			aSectionOffset = aRomImgEntry->iDataAddress - iImageHeader->iRomHdr->iRomBase;
			TUint* aDataSection = (TUint*)((char*)iImageHeader->iRomHdr + aSectionOffset);
			if( aRomImgEntry->iDataSize){
				*out << "\nData (Size=0x" << hex << aRomImgEntry->iDataSize << ")" << endl;
				DumpData(aDataSection, aRomImgEntry->iDataSize);
			}
		}
	}

	*out << endl << endl;
}

void RomImageReader::DumpAttribs(RomImageFSEntry* aFsEntry) {

	// a larger rom image cause stack overflow under visual studio if we use recursion algorithm here.
	// can gcc compiler guarantee this overflow will never be happen ?
	if( aFsEntry->iChildren )
		DumpAttribs(aFsEntry->iChildren);

	if(aFsEntry->iSibling)
		DumpAttribs(aFsEntry->iSibling);
	if(aFsEntry->IsDirectory()) return ;
	RomImageFileEntry* file = static_cast<RomImageFileEntry*>(aFsEntry);
	if(!file->iExecutable) return ; 
	TRomImageHeader* aRomImgEntry = file->ImagePtr.iRomFileEntry;

	if( !aRomImgEntry)  return; 

	const char* prefix ;
	if(aRomImgEntry->iFlags & KRomImageFlagPrimary){
		prefix = "Primary";
	}
	else if(aRomImgEntry->iFlags & KRomImageFlagVariant){
		prefix = "Variant";
	}
	else if(aRomImgEntry->iFlags & KRomImageFlagExtension){
		prefix = "Extension";
	}
	else if(aRomImgEntry->iFlags & KRomImageFlagDevice){
		prefix = "Device";
	}
	else
		return;

	out->width(10);
	out->fill(' '); 
	*out << left << prefix;
	out->width(40);	
	*out << right << file->Name() << "[" ;
	DumpInHex( "", aRomImgEntry->iHardwareVariant, true) << "] ";
	DumpInHex( " DataSize=", (aRomImgEntry->iBssSize + aRomImgEntry->iDataSize), true) << endl;

}

void RomImageReader::Dump() {
	if( !((iDisplayOptions & EXTRACT_FILES_FLAG) || 
		(iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG) ||
		(iDisplayOptions & EXTRACT_FILE_SET_FLAG)) ) {
			*out << "Image Name................." << iImgFileName.c_str() << endl;

			if( iImageHeader->iRomHdr )
				iImageHeader->DumpRomHdr();
			else
				iImageHeader->DumpRomXHdr();

			DumpAttribs(iRomImageRootDirEntry);
			*out << endl ;

			if(iDisplayOptions & DUMP_VERBOSE_FLAG) {
				DumpDirStructure();
				DumpTree();
			}

			if(iDisplayOptions & DUMP_DIR_ENTRIES_FLAG) {
				DumpDirStructure();
			}
	}
}


/** 
Function iterates through all the entries in the image 
by making a call to TraverseImage function.

@internalComponent
@released
*/
void RomImageReader::ExtractImageContents() {
	if( (iDisplayOptions & EXTRACT_FILE_SET_FLAG) ) { 
		ImageReader::ExtractFileSet(iRomLayoutData);
	}

	if( iDisplayOptions & EXTRACT_FILES_FLAG || iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG ) {
		if(iRomImageRootDirEntry) {
			// name of the log file.
			string logFile;
			// output stream for the log file. 
			ofstream oFile;

			if( iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG){
				if( ImageReader::iZdrivePath.compare("")){				 
					// create specified directory.
					CreateSpecifiedDir(ImageReader::iZdrivePath); 
					int len = ImageReader::iZdrivePath.length() ;
					const char* z = ImageReader::iZdrivePath.c_str();
					logFile = "";
					for(int i = 0 ; i < len ; i++){
						if(z[i] == SLASH_CHAR2)
							logFile += SLASH_CHAR1;
						else
							logFile += z[i];
					}
					len -- ;
					if(z[len] != SLASH_CHAR1)
						logFile += SLASH_CHAR1;
					logFile += ImageReader::iLogFileName ;
				}
				else {				
					logFile = ImageReader::iLogFileName;
				}
				// open the specified file in append mode.
				oFile.open(logFile.c_str() ,ios_base::out|ios_base::app);

				if(!oFile.is_open()){
					throw ImageReaderException(ImageReader::iLogFileName.c_str(), "Failed to open the log file");
				}
			}
			TraverseImage(iRomImageRootDirEntry,oFile);
			if(oFile.is_open())  oFile.close();  
		}
	}
}


/** 
Function to traverse entire image and check for an entity.If the entity found in the image is a file 
then it makes a call to CheckFileExtension to check for extension.

@internalComponent
@released

@param aEntity		- pointer to the entry in rom image.
@param aFile		- output stream.
*/
void RomImageReader::TraverseImage(RomImageFSEntry*  aEntity,ofstream& aFile) {
	if(!aEntity->IsDirectory())	{
		CheckFileExtension(aEntity,aFile);
	}

	if (aEntity->iChildren)	{
		TraverseImage(aEntity->iChildren,aFile);
	}

	if (aEntity->iSibling)	{
		TraverseImage(aEntity->iSibling,aFile);
	}
}


/** 
Function to get check extension of the given file.If the extension of the file is "sis"
then call ExtractFile function to extract the file from the image.  

@internalComponent
@released

@param aEntity		- pointer to the entry in rom image.
@param aFile		- output stream.
*/
void RomImageReader::CheckFileExtension(RomImageFSEntry*  aEntity,ofstream& aFile) {
	RomImageFileEntry*	romEntry = (RomImageFileEntry*)aEntity;
	// get the size of the entity.
	TUint32 size = romEntry->iTRomEntryPtr->iSize;
	// get the offset of the entity.
	TUint32 offset =  romEntry->iTRomEntryPtr->iAddressLin - GetRomBase() ;

	const char* fileName = aEntity->iName.c_str();

	// create a string to hold the path information.
	string romfilePath(romEntry->iPath);
	int len = romfilePath.length();
	char* str = const_cast<char*>(romfilePath.c_str());
	for(int i = 0 ; i < len ; i++){
		if(str[i] == SLASH_CHAR2)
			str[i] = SLASH_CHAR1;
	}
	if(str[len - 1] != SLASH_CHAR1)
		romfilePath += SLASH_CHAR1;

	if( iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG && iDisplayOptions & EXTRACT_FILES_FLAG ) {
		// get the position.
		size_t pos = aEntity->iName.find_last_of(".");

		const char* extName = fileName + ( pos + 1 );
		if ( !stricmp(extName ,"SIS")	||  !stricmp(extName ,"DAT") ) {
			// if the two strings are same then extract the corresponding file.
			ImageReader::ExtractFile(offset,size,fileName,romfilePath.c_str(),ImageReader::iZdrivePath.c_str(),iRomLayoutData);
		}
		else {
			LogRomEnrtyToFile(romfilePath.c_str(),fileName,aFile);
		}
	}
	else if( iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG ) {
		LogRomEnrtyToFile(romfilePath.c_str(),fileName,aFile);
	}
	else {
		if(romEntry->iExecutable) {
			size += sizeof(TRomImageHeader);
		}
		// extract the corresponding file. 
		ImageReader::ExtractFile(offset,size,fileName,romfilePath.c_str(),ImageReader::iZdrivePath.c_str(),iRomLayoutData);
	}
}


/** 
Function to log the rom entry to a specified file. 

@internalComponent
@released

@param aPath		- Complete path of an entity.
@param aEntityName	- Entity name. 
@param aFile		- output stream.
*/
void RomImageReader::LogRomEnrtyToFile(const char* aPath,const char* aEntityName,ofstream& aFile) {
	if(aFile.is_open()) {
		aFile.seekp(0,ios_base::end);
		aFile<<aPath <<aEntityName << endl;
	}
}


/** 
Function to read the directory structure details of te ROM image.  

@internalComponent
@released

@param aEntity		- pointer to the entry in rom image.
@param aFileMap		- map of filename with its size and offset values.
@param aImgSize		- Image size
*/
void RomImageReader::ProcessDirectory(RomImageFSEntry *aEntity, FILEINFOMAP &aFileMap) {
	if(!aEntity->IsDirectory()) { 

		RomImageFileEntry*	romEntry = (RomImageFileEntry*)aEntity;

		PFILEINFO fileInfo = new FILEINFO;

		// get the size of the entity.
		fileInfo->iSize = romEntry->iTRomEntryPtr->iSize;
		// get the offset of the entity.
		fileInfo->iOffset = (romEntry->iTRomEntryPtr->iAddressLin - GetRomBase()); 

		if(romEntry->iExecutable) {
			fileInfo->iSize += sizeof(TRomImageHeader);
		}

		if((fileInfo->iOffset + fileInfo->iSize) > iRomSize) {
			fileInfo->iOffset = 0;
			fileInfo->iSize = 0;
		}

		string fileName(romEntry->iPath);
		fileName += SLASH_CHAR1;
		fileName.append(aEntity->iName);
		aFileMap[fileName] = fileInfo;
	}

	if (aEntity->iChildren) {
		ProcessDirectory(aEntity->iChildren, aFileMap);
	}

	if (aEntity->iSibling) {
		ProcessDirectory(aEntity->iSibling, aFileMap);
	}
}

/** 
Function to read the directory structure details of te ROM image.  

@internalComponent
@released

@param aFileMap		- map of filename with its size and offset values.
*/
void RomImageReader::GetFileInfo(FILEINFOMAP &aFileMap) {
	ProcessDirectory(iRomImageRootDirEntry, aFileMap);
}

/** 
Function to get the ROM image size.

@internalComponent
@released
*/
TUint32 RomImageReader::GetImageSize() {
	return iRomSize;
}

