imgtools/romtools/rofsbuild/r_build.cpp
author Mike Kinghan <mikek@symbian.org>
Thu, 25 Nov 2010 13:59:07 +0000
changeset 40 68f68128601f
parent 2 39c28ec933dd
permissions -rwxr-xr-x
1) Add the sbsv1 components from sftools/dev/build to make the linux_build package independent of the obsolete buildtools package. 2) Enhance romnibus.pl so that it generate the symbol file for the built rom when invoked by Raptor 3) Make the maksym.pl tool portable for Linux as well as Windows. 4) Remove the of armasm2as.pl from the e32tools component in favour of the copy now exported from sbsv1/e32util.

/*
* Copyright (c) 1995-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 <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <e32std.h>
#include <e32std_private.h>
#include <e32rom.h>
#include <u32std.h>
#include <e32uid.h>
#include <f32file.h>

#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
	#include <iomanip>
	#include <sstream>
#else //!__MSVCDOTNET__
	#include <iomanip.h>
#endif //__MSVCDOTNET__

#ifdef _L
#undef _L
#endif

#include "h_utl.h"
#include "r_obey.h"
#include "rofs.h"
#include "e32image.h"
#include "patchdataprocessor.h"

extern TUint checkSum(const void* aPtr);

extern ECompression gCompress;
extern TUint gCompressionMethod;
extern TInt  gCodePagingOverride;
extern TInt  gDataPagingOverride;
extern TInt  gLogLevel;
TBool gDriveImage=EFalse;	// for drive image support.


TInt TRomNode::Count=0;
TRomNode* TRomNode::TheFirstNode = NULL;
TRomNode* TRomNode::TheLastNode = NULL;

// introduced for data drive files' attribute
TUint8 TRomNode::sDefaultInitialAttr = (TUint8)KEntryAttReadOnly;

struct SortableEntry
	{
	TRofsEntry* iEntry;
	TBool iIsDir;
	TUint16 iOffset;
	};

int compare(const void* l, const void* r)
	{
	const SortableEntry* left  = (const SortableEntry*)l;
	const SortableEntry* right = (const SortableEntry*)r;
	if (left->iIsDir)
		{
		if (!right->iIsDir)
			return -1;	// dir < file
		}
	else
		{
		if (right->iIsDir)
			return +1;	// file > dir
		}

	// both the same type of entry, sort by name
	// must first convert to an 8 bit string
	// array and NULL terminate it.
	char temp1[500];
	char temp2[500];


TInt i=0;
	for (i = 0; i < left->iEntry->iNameLength; i++)
		{
		temp1[i]=(char) left->iEntry->iName[i];
		}
	temp1[i]=0;

	for (i = 0; i < right->iEntry->iNameLength; i++)
		{
		temp2[i]=(char) right->iEntry->iName[i];
		}
	temp2[i]=0;

	return stricmp((const char*)&temp1[0], (const char*)&temp2[0]);
	}

TRomNode::TRomNode(TText* aName, TRomBuilderEntry* aEntry)
//
// Constructor
//
	:
	iNextNode(NULL),
	iParent(NULL), iSibling(0), iChild(0), iNextNodeForSameFile(0), 
	iTotalDirectoryBlockSize(0),
	iTotalFileBlockSize(0),
	iImagePosition(0),
	iFileBlockPosition(0),
	iAtt(sDefaultInitialAttr),
	iAttExtra(0xFF),
	iHidden(EFalse),
	iEntry(aEntry),
	iFileStartOffset(0), 
	iSize(0), 
	iOverride(0),
	iFileUpdate(EFalse),
    iAlias(false)
	{
	iName = (TText*)NormaliseFileName((const char*)aName);
	iIdentifier=TRomNode::Count++;

	// Add this node to the flat linked list
	if( !TheFirstNode )
		{
		TheFirstNode = this;
		}
	else
		{
		TheLastNode->iNextNode = this;
		}
	TheLastNode = this;

	if (iEntry)
		{
		iEntry->SetRomNode(this);
		}
	else
		{
		iAtt = (TUint8)KEntryAttDir;
		}
	}

TRomNode::~TRomNode()
	{
	if (iEntry && !iAlias)
        {
		delete iEntry;
        }
    iEntry = 0;
	if(iName)
		free(iName);
    iName = 0;
	}

TRomNode *TRomNode::FindInDirectory(TText *aName)
//
// Check if the TRomNode for aName exists in aDir, and if so, return it.
//
	{

	TRomNode *entry=iChild; // first subdirectory or file
	while (entry)
		{
		if ((stricmp((const char *)aName, (const char *)entry->iName))==0) 
			return entry;
		else
			entry=entry->iSibling;
		}
	return 0;
	}



TInt indend = 0;

void indendStructure(TInt i)
       {
	while(i > 0)
	   {
	     cout << "    ";
	     i--;
	   }
       };  

// displays the directory structure
void TRomNode::DisplayStructure(ostream* aOut)
	{
	  indendStructure(indend);
      *aOut  << iName << "\n";
	  if (iChild)
	    {
	      indend++; 
	      iChild->DisplayStructure(aOut);
	      indend--;
	    }
	  if (iSibling)
	    iSibling->DisplayStructure(aOut);
	}


void TRomNode::deleteTheFirstNode()
{

	TheFirstNode = NULL;
}


void TRomNode::InitializeCount()
{
	Count = 0;
}
void TRomNode::displayFlatList()
{
	TRomNode* current =	TheFirstNode;
	TInt i = 0;
	while(current)
	{
		i++;
		cout <<  "\n" << i <<": " << current->iName << endl;
		current = current->NextNode();
	}

	}



void TRomNode::AddFile(TRomNode* aChild)
	{
	if (iEntry)
		{
		Print(EError, "Adding subdirectory to a file!!!\n");
		return;
		}
	Add(aChild);
	}

TRomNode* TRomNode::NewSubDir(TText *aName)
	{
	if (iEntry)
		{
		Print(EError, "Adding subdirectory to a file!!!\n");
		return 0;
		}

	TRomNode* node = new TRomNode(aName );
	if (node==0)
		{
		Print(EError, "TRomNode::NewNode: Out of memory\n");
		return 0;
		}
	node->iParent = this;
	Add(node);
	return node;
	}

void TRomNode::Add(TRomNode* aChild)
	{
	if (iChild) // this node is a non-empty directory
		{
		TRomNode* dir = iChild; // find where to link in the new node
		while (dir->iSibling)
			dir = dir->iSibling;
		dir->iSibling = aChild;
		}
	else
		iChild = aChild; // else just set it up as the child of the dir
	aChild->iSibling = 0;
	aChild->iParent = this;
	}

TInt TRomNode::SetAttExtra(TText *anAttWord, TRomBuilderEntry* aFile, enum EKeyword aKeyword)
//
// Set the file extra attribute byte from the letters passed
// Note: The iAttExtra bits are inverted. '0' represent enabled
//
	{
	iAttExtra=0xFF;
	if (anAttWord==0 || anAttWord[0]=='\0')
		return Print(EError, "Missing argument for keyword 'exattrib'.\n");
	for (TText *letter=anAttWord;*letter!=0;letter++)
		{
		switch (*letter)
			{
		case 'u':
			iAttExtra |= (KEntryAttUnique >> 23);	// '1' represents disabled in iAttExtra
			break;
		case 'U':
			iAttExtra &= ~(KEntryAttUnique >> 23);	// '0' represent enabled in iAttExtra
			break;
		default:
			return Print(EError, "Unrecognised exattrib - '%c'.\n", *letter);
			break;
			}
		}

	if((~iAttExtra & (KEntryAttUnique >> 23))!=0)	// If the unique file attribute is set
		{
		if(aKeyword==EKeywordFile || aKeyword==EKeywordData)	// If the Keyword is File or Data
			{
				if(strlen(aFile->iFileName) > (KMaxFileName-KRofsMangleNameLength)) // check whether we have enough space to add the mangle tage
					return Print(EError, "Lengthy filename with unique attribute to name mangle.\n");
			}
		else	// for all other keywords
			return Print(EError, "exattrib field not allowed for entries except data and file.\n");
		}
	return KErrNone;
	}


TInt TRomNode::SetAtt(TText *anAttWord)
//
// Set the file attribute byte from the letters passed
//
	{
	iAtt=0;
	if (anAttWord==0 || anAttWord[0]=='\0')
		return Print(EError, "Missing argument for keyword 'attrib'.\n");
	for (TText *letter=anAttWord;*letter!=0;letter++)
		{
		switch (*letter)
			{
		case 'R':
		case 'w':
			iAtt |= KEntryAttReadOnly;
			break;
		case 'r':
		case 'W':
			iAtt &= ~KEntryAttReadOnly;
			break;
		case 'H':
			iAtt |= KEntryAttHidden;
			break;
		case 'h':
			iAtt &= ~KEntryAttHidden;
			break;
		case 'S':
			iAtt |= KEntryAttSystem;
			break;
		case 's':
			iAtt &= ~KEntryAttSystem;
			break;
		default:
			return Print(EError, "Unrecognised attrib - '%c'.\n", *letter);
			break;
			}
		}
	return KErrNone;
	}



TInt TRomNode::CalculateEntrySize() const
	// Calculates the amount of ROM space required to hold
	// this entry. The return is the actual size of the TRofsEntry
	// structure, not rounded up
	{
	TInt requiredSizeBytes = KRofsEntryHeaderSize +	NameLengthUnicode();
	return requiredSizeBytes;
	}

TInt TRomNode::CalculateDirectoryEntrySize( TInt& aDirectoryBlockSize,
										    TInt& aFileBlockSize )
	// Calculates the total size of the TRofsDir structure required
	// for this directory and the size of the files block. Traverses all the
	// children adding their entry sizes. The result is not rounded up.
	//
	// On return aDirectoryBlockSize is the number of bytes required for the
	//	main directory structure. aFileBlockSize is the number of bytes
	//	required to hold the list of files.
	//
	// Returns KErrNone on success
	{

	TInt offsetBytes=0;
	TInt padBytes=0;
	if( 0 == iTotalDirectoryBlockSize )
		{
		// need to calculate by walking children	
		if( !iChild )
			{
			return Print(EError, "TRomNode structure corrupt\n");
			}

		TInt dirBlockSize = KRofsDirHeaderSize;
		TInt fileBlockSize = 0;
		TInt fileCount=0;
		TInt dirCount=0;

		TRomNode* node = iChild;
		while (node)
			{
			TInt entrySize = node->CalculateEntrySize();
			if( node->IsDirectory() )
				{
				dirBlockSize += (4 - dirBlockSize) & 3;	// pad to next word boundary
				dirBlockSize += entrySize;
				dirCount++;
				}
			else
				{
				fileBlockSize += (4 - fileBlockSize) & 3;	// pad to next word boundary
				fileBlockSize += entrySize;
				fileCount++;
				}
			node = node->iSibling;
			}
		
		offsetBytes = ((fileCount + dirCount) * 2) + 4; //the +4 are the two offset counts,
		padBytes = offsetBytes % 4;

		iTotalDirectoryBlockSize = dirBlockSize;
		iTotalFileBlockSize = fileBlockSize;
		}

	aDirectoryBlockSize = iTotalDirectoryBlockSize + offsetBytes + padBytes;
	aFileBlockSize = iTotalFileBlockSize;
	return KErrNone;
	}

/**
Place the files and it's attributes (incase of executables)
Called for both rofs and datadrive creation.
 
@param aDest   - Destination buffer.
@param aOffset - offset value, used for rofs only.
@param aMaxSize- Maximum size required for rofs.
  
@return - Returns the number of bytes placed or a -ve error code.
*/ 
TInt TRomNode::PlaceFile( TUint8* &aDest, TUint aOffset, TUint aMaxSize, CBytePair *aBPE )
	//
	// Place the file into the ROM image, making any necessary conversions
	// along the way.
	//
	// Returns the number of bytes placed or a -ve error code.
	{

	TInt size=0;
	
	// file hasn't been placed for drive image. 
	if(gDriveImage)
	{
		size = iEntry->PlaceFile(aDest,aMaxSize,aBPE);
		iSize = size;
	}
	else
	{
		if (iEntry->iHidden)
			iFileStartOffset = KFileHidden;
		else
		{
                    if (iEntry->iFileOffset==0)
                    {
                        // file hasn't been placed
                        size = iEntry->PlaceFile( aDest, aMaxSize, aBPE );
                        if (size>=0)
                            iEntry->iFileOffset = aOffset;
                    }
                    else {
                        iFileStartOffset = (TInt)iEntry;
                    }
		}
	}

	// Deal with any override attributes
	// (omit paging overrides as these are dealt with in TRomBuilderEntry::PlaceFile
	//  and may also be legitimately specified for non-executable files in ROM)
	if( iOverride&~(KOverrideCodeUnpaged|KOverrideCodePaged|KOverrideDataUnpaged|KOverrideDataPaged) )
		{
		E32ImageHeaderV* hdr = (E32ImageHeaderV*)aDest;

		TUint hdrfmt = hdr->HeaderFormat();
		if (hdrfmt != KImageHdrFmt_V)
			{
			Print(EError,"%s: Can't load old format binary\n", iEntry->iFileName);
			return KErrNotSupported;
			}
		
		// First need to check that it's a real image header
		if( (TUint)size > sizeof(E32ImageHeader) )
			{
			if( ((TInt)hdr->iSignature == 0x434f5045u) && ((TInt)hdr->iUid1 == KExecutableImageUidValue || (TInt)hdr->iUid1 == KDynamicLibraryUidValue) )
				{
				// Should check the CRC as well here...
				// Something for later

				// Ok, it looks like an image header
				if( iOverride & KOverrideStack )
					{
					hdr->iStackSize = iStackSize;
					}
				if( iOverride & KOverrideHeapMin )
					{
					hdr->iHeapSizeMin = iHeapSizeMin;
					}
				if( iOverride & KOverrideHeapMax )
					{
					hdr->iHeapSizeMax = iHeapSizeMax;
					}
				if( iOverride & KOverrideFixed )
					{
					if( hdr->iFlags & KImageDll )
						{
						Print(EError,"%s: Can't used FIXED keyword on a DLL\n", iEntry->iFileName);
						return KErrNotSupported;
						}
					hdr->iFlags |= KImageFixedAddressExe;
					}
				if( iOverride & (KOverrideUid1|KOverrideUid2|KOverrideUid3))
					{
					if (iOverride & KOverrideUid1)
						{
						hdr->iUid1 = iUid1;
						}
					if (iOverride & KOverrideUid2)
						{
						hdr->iUid2 = iUid2;
						}
					if (iOverride & KOverrideUid3)
						{
						hdr->iUid3 = iUid3;
						}
					// Need to re-checksum the UIDs
					TUidType ut(TUidType(TUid::Uid(hdr->iUid1), TUid::Uid(hdr->iUid2), TUid::Uid(hdr->iUid3)));
					hdr->iUidChecksum =  (checkSum(((TUint8*)&ut)+1)<<16)|checkSum(&ut);
					}
				if( iOverride & KOverridePriority )
					{
					hdr->iProcessPriority = (TUint16)iPriority;
					}
				if( iOverride & KOverrideCapability )
					{
					hdr->iS.iCaps = iCapability;
					}

				// Need to re-CRC the header
				hdr->iHeaderCrc = KImageCrcInitialiser;
				TUint32 crc = 0;
				TInt hdrsz = hdr->TotalSize();
				HMem::Crc32(crc, hdr, hdrsz);
				hdr->iHeaderCrc = crc;
				}
			}
		}

	return size;
	}

TInt TRomNode::CountFileAndDir(TInt& aFileCount, TInt& aDirCount)
	{
	//
	// Count the number of file and directory entries for this node
	//
	TRomNode* node = iChild;

	aFileCount=0;
	aDirCount=0;
	while( node )
		{
		if( node->IsFile() )
			{
			aFileCount++;
			}
		else
			{
			aDirCount++;
			}

		node = node->iSibling;
		}
	return KErrNone;
	}

TInt TRomNode::Place( TUint8* aDestBase )
	//
	// Writes this directory entry out to the image.
	// The image starts at aDestBase.
	// The position in the image must already have been set with SetImagePosition()
	// and SetFileBlockPosition().
	// Returns KErrNone on success
	//
	{
	TUint8* dirBlockBase = aDestBase + iImagePosition;
	TUint8* fileBlockBase = aDestBase + iFileBlockPosition;

	TRofsDir* pDir = (TRofsDir*)dirBlockBase;
	pDir->iFirstEntryOffset = KRofsDirFirstEntryOffset;
	pDir->iFileBlockAddress = iFileBlockPosition;
	pDir->iFileBlockSize = iTotalFileBlockSize;
	pDir->iStructSize = (TUint16)iTotalDirectoryBlockSize;

	TRofsEntry* pDirEntry = &(pDir->iSubDir);
	TRofsEntry* pFileEntry = (TRofsEntry*)fileBlockBase;

	TInt dirCount;
	TInt fileCount;
	TInt index = 0;
	CountFileAndDir(fileCount, dirCount);

	SortableEntry* array = new SortableEntry[fileCount + dirCount];
	TRomNode* node = iChild;

	while( node )
		{
		TRofsEntry* entry;

		if( node->IsFile() )
			{
			entry = pFileEntry;

			//Offset in 32bit words from start of file block
			array[index].iOffset = (TUint16) ((((TUint8*) entry) - fileBlockBase) >> 2);
			array[index].iIsDir = EFalse;
			}
		else
			{
			entry = pDirEntry;

			//Offset in 32bit words from start of directory block
			array[index].iOffset = (TUint16) ((((TUint8*) entry) - dirBlockBase) >> 2);
			array[index].iIsDir = ETrue;
			}
		array[index].iEntry = entry;
		index++;

		entry->iNameOffset = KRofsEntryNameOffset;
		entry->iAtt = node->iAtt;
		entry->iAttExtra = node->iAttExtra;

		TInt entryLen = KRofsEntryHeaderSize;
		entryLen += node->NameCpy( (char*)&entry->iName, entry->iNameLength );
		entryLen += (4 - entryLen) & 3;	// round up to nearest word
		entry->iStructSize = (TUint16)entryLen;


		if( node->IsFile() )
			{
			// node is a file, entry points to the file
			// write an entry out into the file block
			pFileEntry->iFileAddress = node->iFileStartOffset;
			node->iAtt &= ~KEntryAttDir;
			pFileEntry->iFileSize = node->iEntry->RealFileSize();
			memcpy(&pFileEntry->iUids[0], &node->iEntry->iUids[0], sizeof(pFileEntry->iUids));
			pFileEntry = (TRofsEntry*)( (TUint8*)pFileEntry + entryLen );
			}
		else
			{
			// node is a subdirectory, entry points to directory
			pDirEntry->iFileAddress = node->iImagePosition;
			node->iAtt |= KEntryAttDir;
			
			// the size is just the size of the directory block
			pDirEntry->iFileSize = node->iTotalDirectoryBlockSize;
			pDirEntry = (TRofsEntry*)( (TUint8*)pDirEntry + entryLen );
			}

		node = node->iSibling;
		}

	qsort(array,fileCount + dirCount,sizeof(SortableEntry),&compare);

	//Now copy the contents of sorted array to the image
	TUint16* currentPtr = (TUint16*) (dirBlockBase + iTotalDirectoryBlockSize);

	*currentPtr=(TUint16)dirCount;
	currentPtr++;
	*currentPtr=(TUint16)fileCount;
	currentPtr++;

	for (index = 0; index < (fileCount + dirCount); index++)
		{
		*currentPtr = array[index].iOffset;
		currentPtr++;
		}
	delete[] array;
	return KErrNone;
	}



void TRomNode::Remove(TRomNode* aChild)
	{
	if (iChild==0)
		{
		Print(EError, "Removing file from a file!!!\n");
		return;
		}
	if (iChild==aChild) // first child in this directory
		{
		iChild = aChild->iSibling;
		aChild->iSibling = 0;
		if(iChild==0)
			{
				iParent->Remove(this);
				TRomNode * current = TheFirstNode;
				TRomNode * prev = current;
				while(current != this)
					{
						prev = current;
						current = current->NextNode();
					}
				prev->SetNextNode(current->NextNode());
				delete this;
			}
		return;
		}
	TRomNode* prev = iChild;
	while (prev->iSibling && prev->iSibling != aChild)
		prev = prev->iSibling;
	if (prev==0)
		{
		Print(EError, "Attempting to remove file not in this directory!!!\n");
		return;
		}
	prev->iSibling = aChild->iSibling;
	aChild->iSibling = 0;
	}

void TRomNode::CountDirectory(TInt& aFileCount, TInt& aDirCount)
	{
	TRomNode *current=iChild;
	while(current)
		{
		if (current->iChild)
			aDirCount++;
		else
 			aFileCount++;
	current=current->iSibling;
		}
	}

 void TRomNode::Destroy()
//
// Follow the TRomNode tree, destroying it
//
	{

 	TRomNode *current = this; // root has no siblings
	while (current)
		{
		if (current->iChild)
			current->iChild->Destroy();
		TRomNode* prev=current;
		current=current->iSibling;
		delete prev;
        prev = 0;
		}
 	}




TInt TRomNode::NameCpy(char* aDest, TUint8& aUnicodeLength )
//
// Safely copy a file name in the rom entry
// Returns the number of bytes used. Write the length in unicode characters
// into aUnicodeLength.
//
	{

	if ((aDest==NULL) || (iName==NULL))
		return 0;
	const unsigned char* pSourceByte = (const unsigned char*)iName;
	unsigned char* pTargetByte=(unsigned char*)aDest;
	for (;;)
		{
		const TUint sourceByte=*pSourceByte;
		if (sourceByte==0)
			{
			break;
			}
		if ((sourceByte&0x80)==0)
			{
			*pTargetByte=(unsigned char)sourceByte;
			++pTargetByte;
			*pTargetByte=0;
			++pTargetByte;
			++pSourceByte;
			}
		else if ((sourceByte&0xe0)==0xc0)
			{
			++pSourceByte;
			const TUint secondSourceByte=*pSourceByte;
			if ((secondSourceByte&0xc0)!=0x80)
				{
				Print(EError, "Bad UTF-8 '%s'", iName);
				exit(671);
				}
			*pTargetByte=(unsigned char)((secondSourceByte&0x3f)|((sourceByte&0x03)<<6));
			++pTargetByte;
			*pTargetByte=(unsigned char)((sourceByte>>2)&0x07);
			++pTargetByte;
			++pSourceByte;
			}
		else if ((sourceByte&0xf0)==0xe0)
			{
			++pSourceByte;
			const TUint secondSourceByte=*pSourceByte;
			if ((secondSourceByte&0xc0)!=0x80)
				{
				Print(EError, "Bad UTF-8 '%s'", iName);
				exit(672);
				}
			++pSourceByte;
			const TUint thirdSourceByte=*pSourceByte;
			if ((thirdSourceByte&0xc0)!=0x80)
				{
				Print(EError, "Bad UTF-8 '%s'", iName);
				exit(673);
				}
			*pTargetByte=(unsigned char)((thirdSourceByte&0x3f)|((secondSourceByte&0x03)<<6));
			++pTargetByte;
			*pTargetByte=(unsigned char)(((secondSourceByte>>2)&0x0f)|((sourceByte&0x0f)<<4));
			++pTargetByte;
			++pSourceByte;
			}
		else
			{
			Print(EError, "Bad UTF-8 '%s'", iName);
			exit(674);
			}
		}
	const TInt numberOfBytesInTarget=(pTargetByte-(unsigned char*)aDest); // this number excludes the trailing null-terminator
	if (numberOfBytesInTarget%2!=0)
		{
		Print(EError, "Internal error");
		exit(675);
		}
	aUnicodeLength = (TUint8)(numberOfBytesInTarget/2); // returns the length of aDest (in UTF-16 characters for Unicode, not bytes)
	return numberOfBytesInTarget;
	}


TInt TRomNode::NameLengthUnicode() const
//
// Find the unicode lenght of the name
//
	{

	if (iName==NULL)
		return 0;

	const unsigned char* pSourceByte = (const unsigned char*)iName;
	TInt len = 0;
	for (;;)
		{
		const TUint sourceByte=*pSourceByte;
		if (sourceByte==0)
			{
			break;
			}
		if ((sourceByte&0x80)==0)
			{
			len += 2;
			++pSourceByte;
			}
		else if ((sourceByte&0xe0)==0xc0)
			{
			++pSourceByte;
			const TUint secondSourceByte=*pSourceByte;
			if ((secondSourceByte&0xc0)!=0x80)
				{
				Print(EError, "Bad UTF-8 '%s'", iName);
				exit(671);
				}
			len += 2;
			++pSourceByte;
			}
		else if ((sourceByte&0xf0)==0xe0)
			{
			++pSourceByte;
			const TUint secondSourceByte=*pSourceByte;
			if ((secondSourceByte&0xc0)!=0x80)
				{
				Print(EError, "Bad UTF-8 '%s'", iName);
				exit(672);
				}
			++pSourceByte;
			const TUint thirdSourceByte=*pSourceByte;
			if ((thirdSourceByte&0xc0)!=0x80)
				{
				Print(EError, "Bad UTF-8 '%s'", iName);
				exit(673);
				}
			len += 2;
			++pSourceByte;
			}
		else
			{
			Print(EError, "Bad UTF-8 '%s'", iName);
			exit(674);
			}
		}
	return len;
	}


void TRomNode::AddNodeForSameFile(TRomNode* aPreviousNode, TRomBuilderEntry* aFile)
	{
	// sanity checking
	if (iNextNodeForSameFile != 0 || iEntry != aFile || (aPreviousNode && aPreviousNode->iEntry != iEntry))
		{
		Print(EError, "Adding Node for same file: TRomNode structure corrupted\n");
		exit(666);
		}
	iNextNodeForSameFile = aPreviousNode;
	}





//**************************************
// TRomBuilderEntry
//**************************************



TRomBuilderEntry::TRomBuilderEntry(const char *aFileName,TText *aName)
//
// Constructor
//
:iFirstDllDataEntry(0),	iName(0),iFileName(0),iNext(0), iNextInArea(0),
iExecutable(EFalse), iFileOffset(EFalse), iCompressEnabled(0),
iHidden(0), iRomNode(0), iRealFileSize(0)		 
{
	if (aFileName)
   		iFileName = NormaliseFileName(aFileName);
	if (aName)
		iName = (TText*)NormaliseFileName((const char*)aName);
	memset(iUids,0 ,sizeof(TCheckedUid));
}
	
TRomBuilderEntry::~TRomBuilderEntry()
//
// Destructor
//
	{
	if(iFileName)
	{
	free(iFileName);
	}
	iFileName = 0;
	if(iName)
	{
	free(iName);
	}
	}

void TRomBuilderEntry::SetRomNode(TRomNode* aNode)
	{
	aNode->AddNodeForSameFile(iRomNode, this);
	iRomNode = aNode;
	}


TInt isNumber(TText *aString)
	{
	if (aString==NULL)
		return 0;
	if (strlen((char *)aString)==0)
		return 0;
	return isdigit(aString[0]);
	}

TInt getNumber(TText *aStr)
	{
	TUint a;
	#ifdef __TOOLS2__
	istringstream val((char *)aStr);
	#else
	istrstream val((char *)aStr,strlen((char *)aStr));
	#endif

#if defined(__MSVCDOTNET__) || defined(__TOOLS2__)
	val >> setbase(0);
#endif //__MSVCDOTNET__

	val >> a;
	return a;
	}


TInt TRomBuilderEntry::PlaceFile( TUint8* &aDest,TUint aMaxSize, CBytePair *aBPE )
	//
	// Place the file in ROM. Since we don't support compression yet all
	// we have to do is read the file into memory
	// compress it, if it isn't already compressed.
	//
	// Returns the number of bytes used, or -ve error code
	{
	TUint compression = 0;
	TBool executable = iExecutable;
	Print(ELog,"Reading file %s to image\n", iFileName );
	TUint32 size=HFile::GetLength((TText*)iFileName);
	if (size==0)
		Print(EWarning, "File %s does not exist or is 0 bytes in length.\n",iFileName);
        if (aDest == NULL) {
            aMaxSize = size*2;
            aMaxSize = (aMaxSize>0) ? aMaxSize : 2;
            aDest = new TUint8[aMaxSize];
        }

	if (executable)
		{
		// indicate if the image will overflow without compression
	TBool overflow;
			if(size>aMaxSize)
			overflow = ETrue;
		else
			overflow = EFalse;

		// try to compress this executable
		E32ImageFile f(aBPE);
		TInt r = f.Open(iFileName);
		// is it really a valid E32ImageFile?
		if (r != KErrNone)
			{
			Print(EWarning, "File '%s' is not a valid executable.  Placing file as data.\n", iFileName);
			executable = EFalse;
			}
		else
			{

			if(iRomNode->iOverride & KOverrideDllData)
			{
				DllDataEntry *aDllEntry = iRomNode->iEntry->GetFirstDllDataEntry();
				TLinAddr* aExportTbl;
				void *aLocation;
				TUint aDataAddr;
				char *aCodeSeg, *aDataSeg;

				aExportTbl = (TLinAddr*)((char*)f.iData + f.iOrigHdr->iExportDirOffset);

				// const data symbol may belong in the Code section. If the address lies within the Code or data section limits, 
				// get the corresponding location and update it.While considering the Data section limits
				// don't include the Bss section, as it doesn't exist as yet in the image.
				while( aDllEntry ){
					if(aDllEntry->iOrdinal != (TUint32)-1){
						if(aDllEntry->iOrdinal < 1 || aDllEntry->iOrdinal > (TUint)f.iOrigHdr->iExportDirCount){
							Print(EWarning, "Invalid ordinal %d specified for DLL %s\n", aDllEntry->iOrdinal, iRomNode->iName);
							aDllEntry = aDllEntry->NextDllDataEntry();
							continue;
						}
					
				//	Get the address of the data field via the export table.
					aDataAddr = (TInt32)(aExportTbl[aDllEntry->iOrdinal - 1] + aDllEntry->iOffset);
					if( aDataAddr >= f.iOrigHdr->iCodeBase && aDataAddr <= (f.iOrigHdr->iCodeBase + f.iOrigHdr->iCodeSize)){
						aCodeSeg = (char*)(f.iData + f.iOrigHdr->iCodeOffset);
						aLocation = (void*)(aCodeSeg + aDataAddr - f.iOrigHdr->iCodeBase );
						memcpy(aLocation, &aDllEntry->iNewValue, aDllEntry->iSize);
					}
					else if(aDataAddr >= f.iOrigHdr->iDataBase && aDataAddr <= (f.iOrigHdr->iDataBase + f.iOrigHdr->iDataSize)){
						aDataSeg = (char*)(f.iData + f.iOrigHdr->iDataOffset);
						aLocation = (void*)(aDataSeg + aDataAddr - f.iOrigHdr->iDataBase );
						memcpy(aLocation, &aDllEntry->iNewValue, aDllEntry->iSize);
					}
					else
					{
						Print(EWarning, "Patchdata failed as address pointed by ordinal %d of DLL %s doesn't lie within Code or Data section limits\n", aDllEntry->iOrdinal, iRomNode->iName);
					}
					}
					else if(aDllEntry->iDataAddress != (TLinAddr)-1){
						aDataAddr = aDllEntry->iDataAddress + aDllEntry->iOffset;
					if( aDataAddr >= f.iOrigHdr->iCodeBase && aDataAddr <= (f.iOrigHdr->iCodeBase + f.iOrigHdr->iCodeSize)){
						aCodeSeg = (char*)(f.iData + f.iOrigHdr->iCodeOffset);
						aLocation = (void*)(aCodeSeg + aDataAddr - f.iOrigHdr->iCodeBase );
						memcpy(aLocation, &aDllEntry->iNewValue, aDllEntry->iSize);
					}
					else if(aDataAddr >= f.iOrigHdr->iDataBase && aDataAddr <= (f.iOrigHdr->iDataBase + f.iOrigHdr->iDataSize)){
						aDataSeg = (char*)(f.iData + f.iOrigHdr->iDataOffset);
						aLocation = (void*)(aDataSeg + aDataAddr - f.iOrigHdr->iDataBase );
						memcpy(aLocation, &aDllEntry->iNewValue, aDllEntry->iSize);
					}
					else
					{
						Print(EWarning, "Patchdata failed as address 0x%x of DLL %s doesn't lie within Code or Data section limits\n", aDllEntry->iOrdinal, iRomNode->iName);
					}
					}
					aDllEntry = aDllEntry->NextDllDataEntry();
					}
			}

			compression = f.iHdr->CompressionType();
			Print(ELog,"Original file:'%s' is compressed by method:%08x\n", iFileName, compression);


			TUint32 oldFileComp;
			TUint32 newFileComp;

			if(compression)
			{
				// The E32 image in release directory is compressed
				oldFileComp = compression;
			}
			else
			{
				// The E32 image in release directory is uncompressed
				oldFileComp = 0;
			}

			if( iCompressEnabled != ECompressionUnknown)
			{
				// The new state would be as stated in obey file, i.e. 
				// filecompress or fileuncompress
				newFileComp = gCompressionMethod;
			}
			else if (gCompress != ECompressionUnknown)
			{
				// The new state would be as stated set globally
				newFileComp = gCompressionMethod;
			}
			else
			{
				// When not known if compression is to be applied or not,
				// set it same as that of the E32 image in release directory
				newFileComp = oldFileComp;
			}

			if(!gDriveImage)
			{
				// overide paging flags...
				E32ImageHeaderV* h=f.iHdr;
				if (iRomNode->iOverride & KOverrideCodePaged)
					{
					h->iFlags &= ~KImageCodeUnpaged;
					h->iFlags |= KImageCodePaged;
					}
				if (iRomNode->iOverride & KOverrideCodeUnpaged)
					{
					h->iFlags |= KImageCodeUnpaged;
					h->iFlags &= ~KImageCodePaged;
					}
				if (iRomNode->iOverride & KOverrideDataPaged)
					{
					h->iFlags &= ~KImageDataUnpaged;
					h->iFlags |= KImageDataPaged;
					}
				if (iRomNode->iOverride & KOverrideDataUnpaged)
					{
					h->iFlags |= KImageDataUnpaged;
					h->iFlags &= ~KImageDataPaged;
					}

				// apply global paging override...
				switch(gCodePagingOverride)
					{
				case EKernelConfigPagingPolicyNoPaging:
					h->iFlags |= KImageCodeUnpaged;
					h->iFlags &= ~KImageCodePaged;
					break;
				case EKernelConfigPagingPolicyAlwaysPage:
					h->iFlags |= KImageCodePaged;
					h->iFlags &= ~KImageCodeUnpaged;
					break;
				case EKernelConfigPagingPolicyDefaultUnpaged:
					if(!(h->iFlags&(KImageCodeUnpaged|KImageCodePaged)))
						h->iFlags |= KImageCodeUnpaged;
					break;
				case EKernelConfigPagingPolicyDefaultPaged:
					if(!(h->iFlags&(KImageCodeUnpaged|KImageCodePaged)))
						h->iFlags |= KImageCodePaged;
					break;
					}
				switch(gDataPagingOverride)
					{
				case EKernelConfigPagingPolicyNoPaging:
					h->iFlags |= KImageDataUnpaged;
					h->iFlags &= ~KImageDataPaged;
					break;
				case EKernelConfigPagingPolicyAlwaysPage:
					h->iFlags |= KImageDataPaged;
					h->iFlags &= ~KImageDataUnpaged;
					break;
				case EKernelConfigPagingPolicyDefaultUnpaged:
					if(!(h->iFlags&(KImageDataUnpaged|KImageDataPaged)))
						h->iFlags |= KImageDataUnpaged;
					break;
				case EKernelConfigPagingPolicyDefaultPaged:
					if(!(h->iFlags&(KImageDataUnpaged|KImageDataPaged)))
						h->iFlags |= KImageDataPaged;
					break;
					}
				f.UpdateHeaderCrc();

				// make sure paged code has correct compression type...
				if(h->iFlags&KImageCodePaged)
					{
					if(newFileComp!=0)
						newFileComp = KUidCompressionBytePair;
					}
			}

			if ( oldFileComp != newFileComp )
				{
				
				if( newFileComp == 0)
					{
					Print(ELog,"Decompressing executable '%s'\n", iFileName);
					f.iHdr->iCompressionType = 0;
					}
				else
					{
					Print(ELog,"Compressing executable '%s' with method:%08x\n", iFileName, newFileComp);
					f.iHdr->iCompressionType = newFileComp;
					}
				f.UpdateHeaderCrc();
				if (overflow)
					{
					ostringstream os(ios_base::out|ios_base::binary);
					os << f;
					TUint compressedSize = os.str().size();
					if (compressedSize <= aMaxSize)
						overflow = EFalse;	
					}
				}
			if (overflow)
				{
				Print(EError, "Can't fit '%s' in image\n", iFileName);
				Print(EError, "Overflowed by approximately 0x%x bytes.\n", size - aMaxSize);
				exit(667);
				}
  			ostringstream os(ios_base::out|ios_base::binary);
			os << f;
			size = os.str().size();
			compression = f.iHdr->CompressionType();
			memcpy(&iUids[0], aDest, sizeof(iUids));
			}
		}
	if (!executable)
		{
		if ( size > aMaxSize )
			{
			Print(EError, "Can't fit '%s' in image\n", iFileName);
			Print(EError, "Overflowed by approximately 0x%x bytes.\n", size - aMaxSize);
			exit(667);
			}
		size = HFile::Read((TText*)iFileName, (TAny *)aDest);
                TUint32 Uidslen = (size > sizeof(iUids)) ? sizeof(iUids) : size;
                memcpy(&iUids[0], aDest, Uidslen);
		}

	if (compression)
		Print(ELog,"Compressed executable File '%s' size: %08x, mode:%08x\n", iFileName, size, compression);
	else if (iExecutable)
		Print(ELog,"Executable File '%s' size: %08x\n", iFileName, size);
	else
		Print(ELog,"File '%s' size: %08x\n", iFileName, size);
	iRealFileSize = size;	// required later when directory is written
	
	return size;
	}


TRomNode* TRomNode::CopyDirectory(TRomNode*& aLastExecutable)
	{

	if (iHidden && iChild==0)
		{
		// Hidden file - do not copy (as it wouldn't be visible in the ROM filestructure)
		if (iSibling)
			return iSibling->CopyDirectory(aLastExecutable);
		else
			return 0;
		}

	TRomNode* copy = new TRomNode(iName);
	if(aLastExecutable==0)
		aLastExecutable = copy;		// this must be the root of the structure
	// recursively copy the sub-structures
	if (iChild)
		copy->iChild = iChild->CopyDirectory(aLastExecutable);
	if (iSibling)
		copy->iSibling = iSibling->CopyDirectory(aLastExecutable);
	copy->Clone(this);
	return copy;
	}




void TRomNode::Clone(TRomNode* aOriginal)
	{
	iAtt = aOriginal->iAtt;
	iAttExtra = aOriginal->iAttExtra;
	iEntry = aOriginal->iEntry;
	iHidden = aOriginal->iHidden;
	iFileStartOffset = aOriginal->iFileStartOffset;
	iSize = aOriginal->iSize;
	iParent = aOriginal->iParent;
    iAlias = aOriginal->iAlias;
	}


void TRomNode::Alias(TRomNode* aNode)
	{
	  // sanity checking
	if (aNode->iEntry == 0) 
	{
		Print(EError, "Aliasing: TRomNode structure corrupted\n");
		exit(666);
	}
	Clone(aNode);
	iEntry = aNode->iEntry;
	if (iEntry)
		{
		iEntry->SetRomNode(this);
		}
    iAlias = true;
	}


void TRomNode::Rename(TRomNode *aOldParent, TRomNode* aNewParent, TText* aNewName)
	{
	aOldParent->Remove(this);
	aNewParent->Add(this);
	delete [] iName;
	iName = new TText[strlen((const char *)aNewName)+1];
	strcpy ((char *)iName, (const char *)aNewName);
	}

TInt TRomNode::FullNameLength(TBool aIgnoreHiddenAttrib) const
	{
	TInt l = 0;
	// aIgnoreHiddenAttrib is used to find the complete file name length as 
	// in ROM of a hidden file.
	if (iParent && ( !iHidden || aIgnoreHiddenAttrib))
		l = iParent->FullNameLength() + 1;
	l += strlen((const char*)iName);
	return l;
	}

TInt TRomNode::GetFullName(char* aBuf, TBool aIgnoreHiddenAttrib) const
	{
	TInt l = 0;
	TInt nl = strlen((const char*)iName);
	// aIgnoreHiddenAttrib is used to find the complete file name as in ROM of a hidden file.
	if (iParent && ( !iHidden || aIgnoreHiddenAttrib))
		l = iParent->GetFullName(aBuf);
	char* b = aBuf + l;
	if (l)
		*b++ = '\\', ++l;
	memcpy(b, iName, nl);
	b += nl;
	*b = 0;
	l += nl;
	return l;
	}

// Fuction to return first node in the patchdata linked list
DllDataEntry *TRomBuilderEntry::GetFirstDllDataEntry() const
{
	if (iFirstDllDataEntry)
	{
		return iFirstDllDataEntry;
	}
	else
	{
		return NULL;
	}
}

// Fuction to set first node in the patchdata linked list
void TRomBuilderEntry::SetFirstDllDataEntry(DllDataEntry *aDllDataEntry)
{
	iFirstDllDataEntry = aDllDataEntry;	
}
void TRomBuilderEntry::DisplaySize(TPrintType aWhere)
{
	TBool aIgnoreHiddenAttrib = ETrue;
	TInt aLen = iRomNode->FullNameLength(aIgnoreHiddenAttrib);
	char *aBuf = new char[aLen+1];
	if(gLogLevel & LOG_LEVEL_FILE_DETAILS)
		{
		iRomNode->GetFullName(aBuf, aIgnoreHiddenAttrib);
		if(iFileName)
			Print(aWhere, "%s\t%d\t%s\t%s\n", iFileName, RealFileSize(), (iRomNode->iHidden || iHidden)?"hidden":"", aBuf);
		else
			Print(aWhere, "%s\t%s\n", (iRomNode->iHidden || iHidden)?"hidden":"", aBuf);
		}
	else
		{
		if(iFileName)
			Print(aWhere, "%s\t%d\n", iFileName, RealFileSize());
		}

}