/*
* 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 "common.h"
#include "r_obey.h"
#include "rofs_image_reader.h"
#include "e32_image_reader.h"

RofsImage::RofsImage(RCoreImageReader *aReader) : CCoreImage(aReader) ,
iRofsHeader(0), iRofsExtnHeader(0),iAdjustment(0),  iImageType(RCoreImageReader::E_UNKNOWN) {
}

RofsImageReader::RofsImageReader(const char* aFile) : ImageReader(aFile), iInputFile(0) {
	iImageReader = new RCoreImageReader(aFile);
	iImage = new RofsImage(iImageReader);
}

RofsImageReader::~RofsImageReader() {
	if(iInputFile)
		iInputFile->close();
	delete iInputFile;
	delete iImage;
	delete iImageReader;
}

void RofsImageReader::SetSeek(streampos aOff, ios_base::seek_dir aStartPos) {
	if(!iInputFile)
		return;

	iInputFile->seekg(aOff, aStartPos);
}

void RofsImageReader::ReadImage() {
	if(!iImageReader->Open()) {
		throw ImageReaderException((iImageReader->Filename()), "Failed to open Image File");
	}
}

void RofsImageReader::Validate() {
}

TInt RofsImage::ProcessImage() {
	int result = CreateRootDir();
	if (result == KErrNone) {
		if (iReader->Open()) {
			iImageType = iReader->ReadImageType();
			if (iImageType == RCoreImageReader::E_ROFS) {
				iRofsHeader = new TRofsHeader;
				result = iReader->ReadCoreHeader(*iRofsHeader);
				if (result != KErrNone)
					return result;
				
				SaveDirInfo(*iRofsHeader);
				result = ProcessDirectory(0);
			}

			else if (iImageType == RCoreImageReader::E_ROFX) {
				iRofsExtnHeader = new TExtensionRofsHeader ;
				result = iReader->ReadExtensionHeader(*iRofsExtnHeader);
				if(result != KErrNone)
					return result;

				long filePos = iReader->FilePosition();
				iAdjustment = iRofsExtnHeader->iDirTreeOffset - filePos;

				SaveDirInfo(*iRofsExtnHeader);
				result = ProcessDirectory(iAdjustment);
			}
			else {
				result = KErrNotSupported;
			}
		}
		else {
			result = KErrGeneral;
		}
	}

	return result;
}

void RofsImageReader::ProcessImage() {
	iImage->ProcessImage();
	iRootDirEntry = iImage->RootDirectory();
}

void RofsImageReader::Dump() {
	if( !((iDisplayOptions & EXTRACT_FILES_FLAG) || (iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG) ||
		(iDisplayOptions & EXTRACT_FILE_SET_FLAG)) ) {
		
		MarkNodes();
		if(iDisplayOptions & DUMP_HDR_FLAG) {
			DumpHeader();
		}
		if( (iDisplayOptions & DUMP_DIR_ENTRIES_FLAG) ||
			(iDisplayOptions & DUMP_VERBOSE_FLAG) ) {
			DumpDirStructure();
			DumpFileAttributes();
		}
	}
}

void RofsImageReader::DumpHeader() {
	*out << "Image Name................." << iImgFileName.c_str() << endl;

	int aPos = 0;

	if( ((RofsImage*)iImage)->iImageType == RCoreImageReader::E_ROFS) {
		*out << "ROFS Image" << endl;

		*out << "Image Signature..........." ;
		while(aPos < K_ID_SIZE) {
			*out << ((RofsImage*)iImage)->iRofsHeader->iIdentifier[aPos++];
		}
		*out << endl << endl;

		TUint aTotalDirSz = ((RofsImage*)iImage)->iRofsHeader->iDirTreeSize +
							((RofsImage*)iImage)->iRofsHeader->iDirFileEntriesSize;
		(*out).width(8);

		*out << "Directory block size: 0x" << hex << ((RofsImage*)iImage)->iRofsHeader->iDirTreeSize << endl;
		*out <<	"File block size:      0x" << hex << ((RofsImage*)iImage)->iRofsHeader->iDirFileEntriesSize << endl;
		*out << "Total directory size: 0x" << hex << ( aTotalDirSz ) << endl;
		*out << "Total image size:     0x" << hex << ((RofsImage*)iImage)->iRofsHeader->iImageSize << endl;
	}
	else if(((RofsImage*)iImage)->iImageType == RCoreImageReader::E_ROFX) {
		*out << "Extension ROFS Image" << endl;
		*out << "Image Signature..........." ;
		while(aPos < K_ID_SIZE) {
			*out << ((RofsImage*)iImage)->iRofsExtnHeader->iIdentifier[aPos++];
		}
		*out << endl << endl;

		TUint aTotalDirSz = ((RofsImage*)iImage)->iRofsExtnHeader->iDirTreeSize +
						((RofsImage*)iImage)->iRofsExtnHeader->iDirFileEntriesSize;
		out->width(8);

		*out << "Directory block size: 0x" << hex << ((RofsImage*)iImage)->iRofsExtnHeader->iDirTreeSize << endl;
		*out <<	"File block size:      0x" << hex << ((RofsImage*)iImage)->iRofsExtnHeader->iDirFileEntriesSize << endl;
		*out << "Total directory size: 0x" << hex << ( aTotalDirSz ) << endl;
		*out << "Total image size:     0x" << hex << ((RofsImage*)iImage)->iRofsExtnHeader->iImageSize << endl;
	}
}

void RofsImageReader::DumpDirStructure() {
	 
	*out << "Directory Listing" << endl;
	*out << "=================" << endl; 
	iImage->Display(out);

}

void RofsImageReader::MarkNodes() {
	TRomNode *aNode = iRootDirEntry->NextNode();

	while( aNode ) {
		if(aNode->iEntry) {
			if( ReaderUtil::IsExecutable(aNode->iEntry->iUids) ) {
				aNode->iEntry->iExecutable = true;
			}
			else {
				aNode->iEntry->iExecutable = false;
			}
		}
		aNode = aNode->NextNode();
	}
}

void RofsImageReader::DumpFileAttributes() {
	TRomNode *aNode = iRootDirEntry->NextNode();
	E32ImageFile	aE32Img;
	size_t		aFileOffset;
	std::string			iPath;
	
	while( aNode ) {
		if( aNode->IsFile() ) {
			if( !iInputFile ) {
				// Open the image file once and to access the E32 images within,#
				// seek to the file offsets...
				iInputFile = new ifstream( (iImageReader->Filename()), ios_base::binary|ios_base::in);
				
				if(!iInputFile->is_open()) {
					throw ImageReaderException(iImageReader->Filename(), "Failed to open file");
				}
			}

			try  {
				if( aNode->iEntry->iExecutable) {
					aFileOffset = 0;
					if( ((RofsImage*)iImage)->iImageType == RCoreImageReader::E_ROFX) {
						if((TUint)aNode->iEntry->iFileOffset > ((RofsImage*)iImage)->iRofsExtnHeader->iDirTreeOffset) {
							//This is set only for files within this extended ROFS
							aFileOffset = aNode->iEntry->iFileOffset - ((RofsImage*)iImage)->iAdjustment;
						}
					}
					else {
						//This is set only for files within ROFS
						aFileOffset = aNode->iEntry->iFileOffset;
					}

					if( aFileOffset ) {
						SetSeek(aFileOffset , ios_base::beg);
						memset(&aE32Img, 0, sizeof(aE32Img));
						aE32Img.Adjust(aNode->iSize);
						aE32Img.iFileSize = aNode->iSize;
						if( iInputFile->fail()) {
							// Check why is the fail bit set causing all subsequent
							// istream operations to fail.
							// For now, clear the fail bit...
							iInputFile->clear();
						}
						*iInputFile >> aE32Img;
						if(aE32Img.iError != KErrNone) {
							throw int (0);
						}
					}
				}
			}
			catch(...) {
				// Just in case this was't a valid E32 image and the E32 reader didn't 
				// catch it...
				
				string aStr("Failed to read contents of ");
				aStr.append(aNode->iName);

				throw ImageReaderException(iImageReader->Filename(), aStr.c_str());
			}

			*out << "********************************************************************" << endl;
			//iPath.assign(aNode->iName);	
			GetCompleteNodePath(aNode,iPath);
			*out << "File........................" << iPath.c_str() << endl;
			if( aNode->iEntry->iExecutable ) {
				if(aFileOffset) {
					// When its an E32 Image...
					E32ImageReader::DumpE32Attributes(aE32Img);
					if( iDisplayOptions & DUMP_E32_IMG_FLAG){
						if(stricmp(iE32ImgFileName.c_str(), (const char*)aNode->iName) == 0){
							TUint aSectionOffset = aE32Img.iOrigHdr->iCodeOffset;
							TUint* aCodeSection = (TUint*)(aE32Img.iData + aSectionOffset);
							*out << "\nCode (Size=0x" << hex << aE32Img.iOrigHdr->iCodeSize << ")" << endl;
							DumpData(aCodeSection, aE32Img.iOrigHdr->iCodeSize);

							aSectionOffset = aE32Img.iOrigHdr->iDataOffset;
							TUint* aDataSection = (TUint*)(aE32Img.iData + aSectionOffset);
							if( aE32Img.iOrigHdr->iDataSize){
								*out << "\nData (Size=0x" << hex << aE32Img.iOrigHdr->iDataSize << ")" << endl;
								DumpData(aDataSection, aE32Img.iOrigHdr->iDataSize);
							}
						}
					}
				}
				else {
					*out << "Image "<< aNode->iName << " not in the extended ROFS " << iImgFileName.c_str() << endl;
				}
			}
		}

		aNode = aNode->NextNode();
	}
}


/** 
Function iterates through all the entries in the image.If the entry is a file,
then it makes a call to GetFileExtension to check for the extension.

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

	if( iDisplayOptions & EXTRACT_FILES_FLAG || iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG  ) {
		// get the next Node 
		TRomNode *nextNode = iRootDirEntry->NextNode();
		// current Node.
		TRomNode *currNode = iRootDirEntry;
		// 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.assign(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");
			}
		}

		while( nextNode ){
			if(nextNode->IsDirectory())	{
				// if next node is a directory set current node as next node.
				currNode = nextNode;
			}
			else {
				// get file extension
				CheckFileExtension( nextNode->iName,nextNode->iEntry,currNode,oFile);
			}
			nextNode = nextNode->NextNode();
		}

		if(oFile.is_open()) oFile.close(); 

	}
}


/** 
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 aFile	- file name. 
@param aEntry	- entry of the file in image.
@param aNode	- current node.
@param aLogFile	- output stream.
*/
void RofsImageReader::CheckFileExtension(const char* aFileName,TRomBuilderEntry* aEntry,TRomNode* aNode,ofstream& aLogFile) {
	//create a string to hold path information.
	string path;
	// check whether the node has parent 
	if(aNode->GetParent()) {
		// get the complete path  
		GetCompleteNodePath( aNode, path );
	}
	else {
		// else path is the current path
		path.assign("");
	}
	if( iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG && iDisplayOptions & EXTRACT_FILES_FLAG ) {
	 
		const char* extName = strrchr(aFileName ,'.');	  
		if ( extName && (0 == stricmp(extName,".SIS") || 0 == stricmp(extName,".DAT"))) {
			// if the two strings are same then extract the corresponding file.
			ImageReader::ExtractFile(aEntry->iFileOffset,aEntry->RealFileSize(),aFileName,path.c_str(),ImageReader::iZdrivePath.c_str());
		}
		else {
			// log the entry path information on to the specified file.
			WriteEntryToFile(aFileName,aNode,aLogFile);
		}	 
	}
	else if( iDisplayOptions & LOG_IMAGE_CONTENTS_FLAG ) {
		// log the entry path information on to the specified file.
		WriteEntryToFile(aFileName,aNode,aLogFile);
	}
	else {
		// if the two strings are same then extract the corresponding file.
		ImageReader::ExtractFile(aEntry->iFileOffset,aEntry->RealFileSize(),aFileName,path.c_str(),ImageReader::iZdrivePath.c_str());
	}
}

/** 
Function to get the complete path information of a file from an image.

@internalComponent
@released

@param aNode	- starting offset of the file in the image.
@param aName	- name of the current entry in the image.
@param aAppStr	- string to append.
@return - returns full path of the given file.
*/
void RofsImageReader::GetCompleteNodePath(TRomNode* aNode,string& aName) {
	// check if the entry has a parent.
	 
	aName = "";
	while(aNode) {
		aName.insert(0,aNode->iName);		
		aNode = aNode->GetParent();
		if(aNode)
			aName.insert((size_t)0,(size_t)1,SLASH_CHAR1);
	}
}


/** 
Function to write the rom entry to an output stream.

@internalComponent
@released

@param aNode		- starting offset of the file in the image.
@param aFileName	- name of the current entry in the image.
@param aLogFile		- output stream.
*/
void RofsImageReader::WriteEntryToFile(const char* aFileName,TRomNode* aNode,ofstream& aLogFile) {
	//create a string to hold path information.
	string path("");
	
	if(aNode->GetParent()) {
		// get the complete path 
		GetCompleteNodePath( aNode, path);
		
	}
	else {
		// else path is the current path
		path.assign("");
	}
	
	if(aLogFile.is_open()) {
		aLogFile.seekp(0,ios_base::end);
		aLogFile<<path.c_str()<<SLASH_CHAR1<<aFileName<< endl;
	}
}

/** 
Function to get the directory structure information.

@internalComponent
@released

@param aFileMap		- map of filename with its size and offset values.
*/
void RofsImageReader::GetFileInfo(FILEINFOMAP &aFileMap) {
	// get the next Node 
	TRomNode *nextNode = iRootDirEntry->NextNode();
	// current Node.
	TRomNode *currNode = iRootDirEntry;
	// image size
	TUint32 imgSize = GetImageSize();

	while(nextNode) {
		if(nextNode->IsDirectory()) {
			// if next node is a directory set current node as next node.
			currNode = nextNode;
		}
		else {
			PFILEINFO fileInfo = new FILEINFO;
			//create a string to hold path information.
			string fileName;
			TUint32 aFileOffset = 0;

			// check whether the node has parent 
			if(currNode->GetParent()) {
				if( !((currNode->GetParent() == currNode->FirstNode()) && !(currNode->IsDirectory())) ) {
					// get the complete path 
					 
					GetCompleteNodePath( currNode, fileName );
					fileName += SLASH_CHAR1;
					fileName.append(nextNode->iName);
				}
			}
			else {
				// else path is the current path
				fileName.assign(nextNode->iName);
			}
			
			

			// get the size of the entity.
			fileInfo->iSize = nextNode->iEntry->RealFileSize();

			// get the offset of the entity.
			if( ((RofsImage*)iImage)->iImageType == RCoreImageReader::E_ROFX) {
				if((TUint)nextNode->iEntry->iFileOffset > ((RofsImage*)iImage)->iRofsExtnHeader->iDirTreeOffset) {
					//This is set only for files within this extended ROFS
					aFileOffset = nextNode->iEntry->iFileOffset - ((RofsImage*)iImage)->iAdjustment;
				}
			}
			else {
				//This is set only for files within ROFS
				aFileOffset = nextNode->iEntry->iFileOffset;
			}

			fileInfo->iOffset = aFileOffset;

			if((!fileInfo->iOffset) || ((fileInfo->iOffset + fileInfo->iSize) > imgSize)) {
				fileInfo->iOffset = 0;
				fileInfo->iSize = 0;
			}

			aFileMap[fileName] = fileInfo;
		}

		nextNode = nextNode->NextNode();
	}
}

/** 
Function to get the ROFS image size.

@internalComponent
@released
*/
TUint32 RofsImageReader::GetImageSize() {
	TUint32 result = 0;

	if( ((RofsImage*)iImage)->iImageType == RCoreImageReader::E_ROFS) {
		result = ((RofsImage*)iImage)->iRofsHeader->iImageSize;
	}
	else if(((RofsImage*)iImage)->iImageType == RCoreImageReader::E_ROFX) {
		result = ((RofsImage*)iImage)->iRofsExtnHeader->iImageSize;
	}

	return result;
}
