/*
* 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: 
*
*/


#define __REFERENCE_CAPABILITY_NAMES__
#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>
#include "h_utl.h"

#define USE_IAT_FOR_IMPORTS (iOverrideFlags&KOverrideKeepIAT || (iHdr->iCpuIdentifier & 0x1000))

#include <iomanip> 
#include "r_obey.h"
#include "r_global.h"
#include "r_dir.h"

TInt NumRootDirs;

inline TLinAddr ActualToRomAddress(TAny* anAddr) { 
	return TLinAddr(anAddr)-TheRomMem+TheRomLinearAddress; 
}

TBool THardwareVariant::MutuallyExclusive(THardwareVariant a) const {
	if (Layer()<=3 || a.Layer()<=3)
		return EFalse;
	if (Parent()==3 && a.Parent()==3)
		return(Layer()!=a.Layer());
	if (Parent()==3 && a.Parent()!=Layer())
		return ETrue;
	if (a.Parent()==3 && Parent()!=a.Layer())
		return ETrue;
	if (Layer()!=a.Layer())
		return ETrue;
	return((VMask()&a.VMask())==0);
}

TBool THardwareVariant::IsVariant() const {
	if (Layer()<=3 || Parent()==3)
		return EFalse;
	TUint v=VMask();
	TInt i;
	for (i=0; i<16; i++) {
		if (v==TUint(1<<i))
			return ETrue;
	}
	return EFalse;
}

TInt THardwareVariant::Compare(THardwareVariant a) const {
	TUint l1=Layer();
	TUint p1=Parent();
	TUint v1=VMask();
	TUint l2=a.Layer();
	TUint p2=a.Parent();
	TUint v2=a.VMask();

	if (l1<=3) {
		if (l2<=3) {
			return EEqual;
		}
		return EGreater;
	}
	if (l2<=3)
		return ELess;
	if (p1==3) {
		if (p2==3) {
			if (l1==l2)
				return EEqual;
			return EUnordered;
		}
		if (p2==l1)
			return EGreater;
		return EUnordered;
	}
	if (p2==3) {
		if (p1==l2)
			return ELess;
		return EUnordered;
	}
	if (l1!=l2)
		return EUnordered;
	if ((v1&v2)==v1)
		return ELess;
	if ((v1&v2)==v2)
		return EGreater;
	return EUnordered;
}
//
TInt TRomNode::Count=0;

//
// Constructor from TRomBuilderEntry, i.e. for new file or directory
//
TRomNode::TRomNode(const char* aName, TRomBuilderEntry* aEntry) {
	memset(this, 0, sizeof(TRomNode));
	iAtt = (TUint8)KEntryAttReadOnly;
	iName = NormaliseFileName(aName);
	iIdentifier=TRomNode::Count++;
	iRomFile = new TRomFile;

	if (aEntry) {
		iRomFile->iRbEntry = aEntry;
		aEntry->SetRomNode(this);
		size_t len = strlen(aEntry->iBareName) + 1;
		iBareName = new char[len];
		memcpy(iBareName,aEntry->iBareName,len);
		iRomFile->iHwvd = aEntry->iHardwareVariant;
	}
	else {
		iRomFile->iDir = ETrue;
		iAtt |= (TUint8)KEntryAttDir;
		size_t len = strlen(iName) + 1;
		iBareName = new char[len];
		memcpy(iBareName,iName,len);
		iRomFile->iHwvd = KVariantIndependent;
	}
	TRACE(TROMNODE,Print(ELog, "TRomNode %d name %s bare %s att %02x romfile %08x\n", iIdentifier,
		iName, iBareName, iAtt, iRomFile));
}

//
// Constructor from TRomNode, i.e. for aliased file
//	
TRomNode::TRomNode(const char* aName, TRomNode* aNode){
	memset(this, 0, sizeof(TRomNode));
	iAtt = aNode->iAtt;
	iIdentifier=TRomNode::Count++;
	iName = NormaliseFileName(aName);
	iHidden = aNode->iHidden;
	iRomFile = aNode->iRomFile;
	if (iRomFile) {
		iRomFile->Open();
	}
	TRACE(TROMNODE,Print(ELog, "TRomNode %d DUP name %s romfile %08x\n", iIdentifier, iName, iRomFile));
}
//
// Copy constructor - only used in deep copy function
//
TRomNode::TRomNode(const TRomNode& aNode) {
	memset(this, 0, sizeof(TRomNode));
	iAtt = aNode.iAtt;
	iIdentifier=TRomNode::Count++;
	size_t len = strlen(aNode.iName) + 1;
	iName = new char[len];
	memcpy(iName,aNode.iName,len);
	len = strlen(aNode.iBareName) + 1;
	iBareName = new char[len];
	memcpy(iBareName,aNode.iBareName,len);
	iHidden = aNode.iHidden;
	iRomFile = aNode.iRomFile;
	if (iRomFile) {
		iRomFile->Open();
	}
	TRACE(TROMNODE,Print(ELog, "TRomNode %d COPY name %s bare %s att %02x romfile %08x\n", iIdentifier,
		iName, iBareName, iAtt, iRomFile));
}

TRomNode::~TRomNode() {
	if(iName) {
		delete []iName;
		iName = 0 ;
	}
	if(iBareName){
		delete []iBareName;
		iBareName = 0 ;
	}
	if (iRomFile)
		iRomFile->Close();
}
//
// Check if the TRomNode for aName exists in aDir, and if so, return it.
//
TRomNode* TRomNode::FindInDirectory(const char* aName) {

	TRomNode *entry=iChild; // first subdirectory or file
	while (entry){
		if (!entry->iHidden && (stricmp(aName, entry->iName))==0) 
			return entry;
		else
			entry=entry->iSibling;
	}
	return 0;
}
//
// Check for a file with same name and a compatible hardware variant
//
TRomNode* TRomNode::FindInDirectory(const char* aName, THardwareVariant aVariant, TBool aPatchDataFlag) {
	TRomNode *entry=iChild; // first subdirectory or file
	while (entry) {
		if (((!entry->iHidden)||aPatchDataFlag) && entry->iRomFile && 
			(stricmp(aName, entry->iName))==0) 	{
				if (!aVariant.MutuallyExclusive(entry->HardwareVariant()))
					return entry;
		}
		entry=entry->iSibling;
	}
	return 0;
}

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

TRomNode* TRomNode::NewSubDir(const char* aName) {
	if (!(iAtt & KEntryAttDir)) {
		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;
	}
	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;
}

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;
		aChild->iParent = 0;
		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;
	aChild->iParent = 0;
}

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

/**
* Walk the contents of the directory, accumulating the
* files as FileEntry objects in the specified RomFileStructure
* and recursively handling the sub-directories
*
* TRomNode::ProcessDirectory is a pair with
* RomFileStructure::ProcessDirectory
*/
int TRomNode::ProcessDirectory(RomFileStructure* aRFS) {
	TInt r=KErrNone;
	TRomNode *current=iChild;
	while(current) {
		if (current->iAtt & (TUint8)KEntryAttDir) {
			r=aRFS->ProcessDirectory(current);
			if (r!=KErrNone)
				return r;
		}
		else if (!current->iHidden) {
			FileEntry *pE=FileEntry::New(current);
			if (!pE)
				return KErrNoMemory;
			r=aRFS->Add(*pE);
			if (r==KErrOverflow)
				return r;
		}
		current=current->iSibling;
	}
	return r;
}

void TRomNode::AddExecutableFile(TRomNode*& aLast, TRomNode* aNode) {
	aLast->iNextExecutable = aNode;
	aLast = aNode;
	aNode->iAtt |= KEntryAttXIP;
}

const char* TRomNode::BareName() const {
	return iBareName  ;
}

TUint32 TRomNode::Uid3() const {
	return iRomFile->Uid3();
}

TUint32 TRomNode::ModuleVersion() const {
	return iRomFile->ModuleVersion();
}

THardwareVariant TRomNode::HardwareVariant() const {
	return iRomFile->HardwareVariant();
}

TUint32 TRomNode::ABI() const {
	return iRomFile->ABI();
}

TUint32 TRomFile::Uid3() const {
	assert(!iDir);
	if (iRbEntry) {
		if(iRbEntry->iHdr)
			return iRbEntry->iHdr->iUid3;
	}
	return RomImgHdr()->iUid3;
}

TUint32 TRomFile::ModuleVersion() const {
	assert(!iDir);
	if (iRbEntry) {
		if(iRbEntry->iHdr)
			return iRbEntry->iHdr->ModuleVersion();
	}
	return RomImgHdr()->iModuleVersion;
}

THardwareVariant TRomFile::HardwareVariant() const {
	return iHwvd;
}

TUint32 TRomFile::ABI() const {
	assert(!iDir);
	if (iRbEntry) {
		if(iRbEntry->iHdr)
			return iRbEntry->iHdr->ABI();
	}
	return RomImgHdr()->iFlags & KRomImageABIMask;
}

TInt TRomFile::ExportDirCount() const {
	assert(!iDir);
	if (iRbEntry) {
		if(iRbEntry->iHdr)
			return iRbEntry->iHdr->iExportDirCount;
	}
	return RomImgHdr()->iExportDirCount;
}

TUint32 TRomFile::RomImageFlags() const {
	assert(!iDir);
	if (iRbEntry) {
		if(iRbEntry->iHdr) {
			const TUint KRomFlagMask = KImageDll | KImageNoCallEntryPoint | KImageFixedAddressExe | KImageNmdExpData;
			TUint romflags = iRbEntry->iHdr->iFlags & KRomFlagMask;
			return iRbEntry->iRomImageFlags | romflags;
		}
	}
	return RomImgHdr()->iFlags;
}

TLinAddr TRomFile::DataBssLinearBase() const {
	assert(!iDir);
	if (iRbEntry)
		return iRbEntry->iDataBssLinearBase;
	return RomImgHdr()->iDataBssLinearBase;
}

const SSecurityInfo& TRomFile::SecurityInfo() const {
	assert(!iDir);
	if (iRbEntry)
		return iRbEntry->iS;
	return RomImgHdr()->iS;
}

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(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++ = SLASH_CHAR; 
		++l;
	}
	memcpy(b, iName, nl);
	b += nl;
	*b = 0;
	l += nl;
	return l;
}

//
//	Check that a aSubCaps are a subset of aSuperCaps
//
TInt CompareCapabilities(const SCapabilitySet& aSubCaps, const SCapabilitySet& aSuperCaps, const char* aSubName, const char* aSuperName) {
	if ((!gPlatSecEnforcement)&&(!gPlatSecDiagnostics))
		return KErrNone;
	TInt i;
	TUint32 c = 0;
	for (i=0; i<SCapabilitySet::ENCapW; ++i)
		c |= (aSubCaps[i] &~ aSuperCaps[i]);
	TInt r = c ? KErrPermissionDenied : KErrNone;
	if (r && aSubName && aSuperName) {
		TPrintType printType;
		if(gPlatSecEnforcement)
			printType = EError;
		else {
			printType = EWarning;
			r = KErrNone;
		}
		char* buf = (char*)malloc(2);
		if(!buf)
			return KErrNoMemory;
		TInt len = 0;
		for(i=0; i<ECapability_Limit; i++) {
			if( (aSubCaps[i>>5] &~ aSuperCaps[i>>5]) & (1<<(i&31)) ) {
				// append capability name to buf
				const char* name = CapabilityNames[i];
				if(!name)
					continue;
				if(len) {
					buf[len++] = ' ';
				}
				int nameLen=strlen(name);
				buf = (char*)realloc(buf,len+nameLen+2);
				if(!buf)
					return KErrNoMemory;
				memcpy(buf+len,CapabilityNames[i],nameLen);
				len += nameLen;
			}
		}
		buf[len]=0;
		Print(printType, "*PlatSec* %s - Capability check failed. Can't load %s because it links to %s which has the following capabilities missing: %s\n",gPlatSecEnforcement?"ERROR":"WARNING",aSubName, aSuperName, buf);
		free(buf);
	}
	return r;
}

TDllFindInfo::TDllFindInfo(const char* aImportName, const TRomBuilderEntry* aEntry) {
	TUint32 flags;
	iBareName = SplitFileName(aImportName, iUid3, iModuleVersion, flags);
	assert(iBareName != 0);
	iHwVariant = aEntry->iHardwareVariant.ReturnVariant(); 
}

TDllFindInfo::~TDllFindInfo() {
	delete []iBareName;
}

// Generate name as follows:
// PC filename (UID3:xxxxxxxx HWVD:xxxxxxxx VER:M.m)
TModuleName::TModuleName(const TRomBuilderEntry* a) {
	TInt l = strlen(a->iFileName) + strlen(" (UID3:xxxxxxxx HWVD:xxxxxxxx VER:MMMMM.mmmmm)");
	iName = new char[l + 1];
	assert(iName != 0);
	sprintf(iName, "%s (UID3:%08lx HWVD:%08lx VER:%ld.%ld)", a->iFileName, a->iHdr->iUid3,
		a->iHardwareVariant.ReturnVariant(), a->iHdr->ModuleVersion()>>16, a->iHdr->ModuleVersion()&0x0000ffffu);
}

// Generate name as follows:
// Bare name (UID3:xxxxxxxx HWVD:xxxxxxxx VER:M.m)
TModuleName::TModuleName(const TDllFindInfo& a) {
	TInt l = strlen(a.iBareName) + strlen(" (UID3:xxxxxxxx HWVD:xxxxxxxx VER:MMMMM.mmmmm)");
	iName = new char [l + 1];
	assert(iName != 0);
	sprintf(iName, "%s (UID3:%08lx HWVD:%08lx VER:%ld.%ld)", a.iBareName, a.iUid3,
		a.iHwVariant, a.iModuleVersion>>16, a.iModuleVersion&0x0000ffffu);
}

// Generate name as follows:
// Name (UID3:xxxxxxxx HWVD:xxxxxxxx VER:M.m)
TModuleName::TModuleName(const TRomNode& a) {
	TBool xip = (a.iAtt & KEntryAttXIP);
	TUint32 uid3 = xip ? a.Uid3() : 0;
	TUint32 hwvd = (TUint)a.HardwareVariant();
	TUint32 ver = xip ? a.ModuleVersion() : 0;
	TInt l = a.FullNameLength() + strlen(" (UID3:xxxxxxxx HWVD:xxxxxxxx VER:MMMMM.mmmmm)");
	iName = new char[l + 1];
	assert(iName != 0);
	char* b = iName + a.GetFullName(iName);
	sprintf(b, " (UID3:%08lx HWVD:%08lx VER:%ld.%ld)", uid3, hwvd, ver>>16, ver&0x0000ffffu);
}

TModuleName::TModuleName(const TRomFile& a, const TRomNode* aRootDir) {
	iName = 0;
	if (a.iRbEntry) {
		new (this) TModuleName(a.iRbEntry);
		return;
	}
	TRomNode* x = aRootDir->iNextExecutable;
	for(; x; x=x->iNextExecutable) {
		if (x->iRomFile == &a) {
			new (this) TModuleName(*x);
			return;
		}
	}
}

TModuleName::~TModuleName() {
	if(iName)
		delete []iName;
}


/**
* TRomNode::FindImageFileByName is always called on the root TRomNode, so
* it doesn't consider the current TRomNode, just the linked items in the
* iNextExecutable list.
*/
TRomNode* TRomNode::FindImageFileByName(const TDllFindInfo& aInfo, TBool aPrintDiag, TBool& aFallBack) {
	TUint r_major = aInfo.iModuleVersion >> 16;
	TUint r_minor = aInfo.iModuleVersion & 0x0000ffffu;
	TRomNode* fallback = NULL;
	aFallBack = EFalse;
	for (TRomNode* x=iNextExecutable; x!=0; x=x->iNextExecutable) {
		if (stricmp(x->BareName(), aInfo.iBareName))
			continue;	// name doesn't match
		if (aPrintDiag)
			Print(ELog, "Candidate: %s ", (const char*)TModuleName(*x) );
		if ( !(THardwareVariant(aInfo.iHwVariant) <= x->HardwareVariant()) ) {
			if (aPrintDiag)
				Print(ELog, "HWVD mismatch - requested %08x\n", aInfo.iHwVariant);
			continue;
		}
		if (aInfo.iUid3 && (aInfo.iUid3 != x->Uid3())) {
			if (aPrintDiag)
				Print(ELog, "UID3 mismatch - requested %08x\n", aInfo.iUid3);
			continue;
		}
		TUint x_major = x->ModuleVersion() >> 16;
		TUint x_minor = x->ModuleVersion() & 0x0000ffffu;
		if ( x->ModuleVersion() == 0x00010000 && aInfo.iModuleVersion == 0 ) {
			// allow requested version 0.0 to link to 1.0 with a warning
			fallback = x;
		}
		if ( x_major != r_major ) {
			if (aPrintDiag)
				Print(ELog, "Major version mismatch - requested %d\n", r_major);
			continue;
		}
		if ( x_minor < r_minor ) {
			if (aPrintDiag)
				Print(ELog, "??? Minor version mismatch - requested %d\n", r_minor);
			continue;
		}
		if (aPrintDiag)
			Print(ELog, "OK\n");
		return x;
	}
	if (fallback) {
		aFallBack = ETrue;
		return fallback;
	}
	return 0;
}

TInt TRomNode::CheckForVersionConflicts(const TRomBuilderEntry* a) {
	TUint r_major = a->iHdr->ModuleVersion() >> 16;
	TUint r_minor = a->iHdr->ModuleVersion() & 0x0000ffffu;
	TInt errors = 0;
	for (TRomNode* x=iNextExecutable; x!=0; x=x->iNextExecutable) {
		if (x->iRomFile->iRbEntry == a)
			continue;	// don't compare a with itself
		if (stricmp(x->BareName(), a->iBareName))
			continue;	// name doesn't match
		if ( a->iHardwareVariant.MutuallyExclusive(x->HardwareVariant()) )
			continue;	// HWVDs are mutually exclusive
		if ( a->iHdr->iUid3 && x->Uid3() && (a->iHdr->iUid3 != x->Uid3()) )
			continue;	// UID3's don't match
		TUint x_major = x->ModuleVersion() >> 16;
		TUint x_minor = x->ModuleVersion() & 0x0000ffffu;
		if (x_major == r_major && x_minor != r_minor)  {	// allow two copies of same file
			Print(EError, "Version Conflict %s with %s\n", (const char*)TModuleName(a), (const char*)TModuleName(*x) );
			++errors;
		}
	}
	return errors;
}

//
// Follow the TRomBuilderEntry tree, writing TRomEntry headers to the rom.
//
TInt E32Rom::WriteHeadersToRom(char *anAddr) {
	TRACE(TTIMING,Print(EAlways,"0\n"));
	TRACE(TDIR,Print(EAlways,"WriteHeadersToRom()\n"));
	char* start=anAddr;
	TRomRootDirectoryList* dirPointers=(TRomRootDirectoryList*)anAddr;
	anAddr+=NumberOfVariants*sizeof(TRootDirInfo)+sizeof(TInt);
	RomFileStructure* pS=RomFileStructure::New(NumberOfVariants);
	if (!pS)
		Print(EError,"Error creating RomFileStructure\n");
	TInt r=pS->ProcessDirectory(iObey->iRootDirectory);
	if (r!=KErrNone)
		Print(EError,"Error %d processing directory tree\n",r);
	TInt c=pS->Count();
	NumRootDirs=c;
	if (c!=NumberOfVariants)
		Print(EError,"Error processing directory tree NR=%d NV=%d\n",c,NumberOfVariants);
	dirPointers->iNumRootDirs=c;
	TInt i;
	TRACE(TDIR,Print(EAlways,"Count=%d\n",c));
	TRACE(TDIR,pS->DebugPrint());
	for(i=0; i<c; i++) {
		DirEntry* pD=(DirEntry*)&(*pS)[i];
		TRomDir* pR=pD->CreateRomEntries(anAddr);
		dirPointers->iRootDir[i].iHardwareVariant=TUint(pD->Variants().Lookup());
		dirPointers->iRootDir[i].iAddressLin=ActualToRomAddress(pR);
	}
	TRACE(TDIR,Print(EAlways,"Beginning final cleanup\n"));
	delete pS;
	TRACE(TTIMING,Print(EAlways,"1\n"));
	return anAddr-start;
}

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

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

//
// Set the file attribute byte from the letters passed
//
TInt TRomNode::SetAtt(const char* anAttWord) {
	iAtt=0;
	if (anAttWord==0 || anAttWord[0]=='\0')
		return Print(EError, "Missing argument for keyword 'attrib'.\n");
	for (const char* 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;
}
//
// Constructor
//
TRomBuilderEntry::TRomBuilderEntry(const char *aFileName,const char* aName) :
E32ImageFile(),
iName(0),
iResource(EFalse), iNonXIP(EFalse), iPreferred(EFalse), iCompression(0), iPatched(EFalse),iArea(0),
iOverrideFlags(0),iCodeAlignment(0),iDataAlignment(0),iUid1(0), iUid2(0), iUid3(0),iBareName(0), 
iHardwareVariant(KVariantIndependent),iDataBssOffset(0xffffffff), 
iStackReserve(0),iIATRefs(0), iNext(0), iNextInArea(0), 
iRomImageFlags(0),iProcessName(0), iRomNode(NULL) {
	if (aFileName){
		if(iFileName)
			delete []iFileName;
		iFileName = NormaliseFileName(aFileName);	 
	}
	if (aName)
		iName = NormaliseFileName(aName);
}
//
// Destructor
//
TRomBuilderEntry::~TRomBuilderEntry() {
	 
	if(iName){
		delete []iName; 
		iName = 0 ;
	}
	if(iProcessName){	
		delete []iProcessName;
		iProcessName = 0 ;
	}
	if(iBareName) {
		delete []iBareName;
		iBareName = 0 ;
	}

	if(iIATRefs){
		char* tmp = reinterpret_cast<char*>(iIATRefs);
		delete []tmp;
		iIATRefs = 0 ;
	}
}

TInt TRomBuilderEntry::SetCodeAlignment(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'code-align'.\n"); 
	TInt err = Val(iCodeAlignment,aStr);	 
	return err;
}

TInt TRomBuilderEntry::SetDataAlignment(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'data-align'.\n"); 
	TInt align = 0;
	Val(align,aStr);
	if (align < 0 || (align & 0x0F) != 0) 
		return Print(EError, "Positive multiple of 16 required for 'data-align'.\n");
	iDataAlignment = align;
	return KErrNone;
}

TInt TRomBuilderEntry::SetRelocationAddress(const char* aStr) {
	if (aStr && !IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'reloc'.\n");
	iOverrideFlags |= KOverrideAddress;
	iRelocationAddress =  0xFFFFFFFF; 
	if(aStr){
		Val(iRelocationAddress,aStr);
	}
	return KErrNone;
}

TInt TRomBuilderEntry::SetStackReserve(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'stackreserve'.\n");	
	TInt err = Val(iStackReserve,aStr);
	if(err == KErrNone)
		iOverrideFlags |= KOverrideStackReserve;
	return err;
}

TInt TRomBuilderEntry::SetStackSize(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'stack'.\n");	
	TInt err = Val(iStackSize,aStr);
	if(err == KErrNone)
		iOverrideFlags |= KOverrideStack;
	return err;
}

TInt TRomBuilderEntry::SetHeapSizeMin(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'heapmin'.\n");	
	TInt err = Val(iHeapSizeMin,aStr); 
	if(err == KErrNone)
		iOverrideFlags |= KOverrideHeapMin;
	return KErrNone;
}

TInt TRomBuilderEntry::SetHeapSizeMax(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'heapmax'.\n");
	TInt err = Val(iHeapSizeMax,aStr); 
	if(err == KErrNone)
		iOverrideFlags |= KOverrideHeapMax;
	return err;
}

TInt TRomBuilderEntry::SetCapability(const char* aStr) {
	iOverrideFlags |= KOverrideCapability;
	if (IsValidNumber(aStr)) {
		Print(EDiagnostic,"Old style numeric CAPABILTY specification ignored.\n");
		return KErrNone;
	}
	return ParseCapabilitiesArg(iS.iCaps, aStr);
}

TInt TRomBuilderEntry::SetPriority(const char* aStr) {
	if (IsValidNumber(aStr)){
		TUint32 temp = 0 ;
		Val(temp,aStr);
		iPriority=(TProcessPriority)temp;
	}
	else {		 
		if (stricmp(aStr, "low")==0)
			iPriority=EPriorityLow;
		else if (strnicmp(aStr, "background", 4)==0)
			iPriority=EPriorityBackground;
		else if (strnicmp(aStr, "foreground", 4)==0)
			iPriority=EPriorityForeground;
		else if (stricmp(aStr, "high")==0)
			iPriority=EPriorityHigh;
		else if (strnicmp(aStr, "windowserver",3)==0)
			iPriority=EPriorityWindowServer;
		else if (strnicmp(aStr, "fileserver",4)==0)
			iPriority=EPriorityFileServer;
		else if (strnicmp(aStr, "realtimeserver",4)==0)
			iPriority=EPriorityRealTimeServer;
		else if (strnicmp(aStr, "supervisor",3)==0)
			iPriority=EPrioritySupervisor;
		else
			return Print(EError, "Unrecognised priority keyword.\n");
	}
	if (iPriority<EPriorityLow || iPriority>EPrioritySupervisor)
		return Print(EError, "Priority out of range.\n");
	iOverrideFlags |= KOverridePriority;
	return KErrNone;
}

TInt TRomBuilderEntry::SetUid1(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'uid1'.\n");
	iOverrideFlags |= KOverrideUid1;
	Val(iUid1,aStr);
	return KErrNone;
}
TInt TRomBuilderEntry::SetUid2(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'uid2'.\n");
	iOverrideFlags |= KOverrideUid2;
	Val(iUid2,aStr);
	return KErrNone;
}
TInt TRomBuilderEntry::SetUid3(const char* aStr) {
	if (!IsValidNumber(aStr))
		return Print(EError, "Number required as argument for keyword 'uid3'.\n");
	iOverrideFlags |= KOverrideUid3;
	Val(iUid3,aStr);
	return KErrNone;
}
TInt TRomBuilderEntry::SetCallEntryPoint(TBool aState) {
	if (aState)
		iOverrideFlags|=KOverrideCallEntryPoint;
	else
		iOverrideFlags|=KOverrideNoCallEntryPoint;
	return KErrNone;
}

TInt TRomBuilderEntry::SetAttachProcess(const char* aStr) {

	TInt nd=0;
	if (*aStr==SLASH_CHAR) {
		++aStr;
		++nd;
	}
	size_t l = strlen(aStr);
	if (l == 0)
		return KErrGeneral;
	const char* ss=aStr;
	while(*ss != 0 && *ss!='.') ++ss;
	int ext = *ss;	// 0 if no extension
	iProcessName=new char[l+2+(ext?0:4)];
	char* d=(char *)iProcessName;
	strcpy(d+1, aStr);
	if (!ext) {
		char* t=d+1+l;
		*t++='.';
		*t++='e';
		*t++='x';
		*t++='e';
		*t++=0;
	}
	char* dd=d;
	int ind=nd;
	while(*dd) {
		while(*dd && *dd!=SLASH_CHAR) ++dd;
		if (*dd==SLASH_CHAR) {
			*dd++=0;	// change \ to NUL
			++nd;		// count path elements
		}
	}
	if (!ind && nd)
		++nd;			// add initial \ if not present
	*d=(char)nd;
	return 0;
}

TInt TRomBuilderEntry::OpenImageFile()
	{
	TInt err = Open(iFileName);
	return err;
	}
TInt TRomBuilderEntry::GetImageFileInfo(TInt aResult)
{
	TInt err = aResult;
	Print(ELog,"Loading E32Image file %s \n", iFileName);
	if (err != KErrNone) {
		Print(EError,"File %s is not a valid E32Image file (error %d)\n", iFileName, err);
		return err;
	}
	TUint hdrfmt = iHdr->HeaderFormat();
	if (hdrfmt != KImageHdrFmt_V) {
		Print(EError,"%s: Can't load old format binary\n", iFileName);
		return KErrNotSupported;
	}
	E32ImageHeaderV* h = iHdr;

	// Overide any settings in the image file with those in the obey file
	if (iOverrideFlags & (KOverrideUid1|KOverrideUid2|KOverrideUid3)) {
		TUint uid1 = h->iUid1;
		TUint uid2 = h->iUid2;
		TUint uid3 = h->iUid3;
		if (iOverrideFlags & KOverrideUid1)
			uid1 = iUid1;
		if (iOverrideFlags & KOverrideUid2)
			uid2 = iUid2;
		if (iOverrideFlags & KOverrideUid3)
			uid3 = iUid3;
		SetUids(TUid::Uid(uid1), TUid::Uid(uid2), TUid::Uid(uid3));
	}
	if (iOverrideFlags & KOverrideStack)
		h->iStackSize = iStackSize;
	if (iOverrideFlags & KOverrideHeapMax)
		h->iHeapSizeMax = iHeapSizeMax;
	if (iOverrideFlags & KOverrideHeapMin)
		h->iHeapSizeMin = iHeapSizeMin;
	if (iOverrideFlags & KOverridePriority)
		h->iProcessPriority = (TUint16)iPriority;
	if (iOverrideFlags & KOverrideCapability)
		h->iS.iCaps = iS.iCaps;
	for (TInt i=0; i<SCapabilitySet::ENCapW; ++i) {
		h->iS.iCaps[i] |= gPlatSecDisabledCaps[i];
		h->iS.iCaps[i] &= gPlatSecAllCaps[i];
	}

	if (iOverrideFlags & KOverrideCodePaged) {
		h->iFlags &= ~KImageCodeUnpaged;
		h->iFlags |= KImageCodePaged;
	}
	if (iOverrideFlags & KOverrideCodeUnpaged) {
		h->iFlags |= KImageCodeUnpaged;
		h->iFlags &= ~KImageCodePaged;
	}

	if ((TInt)h->iUid1 == KExecutableImageUidValue) {
		if (iOverrideFlags & KOverrideDataPaged) {
			h->iFlags &= ~KImageDataUnpaged;
			h->iFlags |= KImageDataPaged;
		}
		if (iOverrideFlags & KOverrideDataUnpaged) {
			h->iFlags |= KImageDataUnpaged;
			h->iFlags &= ~KImageDataPaged;
		}
	}

	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;
	}

	h->iCompressionType=KUidCompressionDeflate; // XIP images are always uncompressed

	Print(ELog,"\t\tcompression format:0x%08x \n", h->iCompressionType);
	if (gLogLevel & LOG_LEVEL_COMPRESSION_INFO)
		Print(ELog,"\t\tgCompress:%d, gCompressionMethod: 0x%08x \n", gEnableCompress , gCompressionMethod);


	// Check the uids

	if ((TInt)h->iUid1 != KExecutableImageUidValue && (TInt)h->iUid1 != KDynamicLibraryUidValue) 
		return Print(EError, "First Uid for %s is not KExecutableImageUid or KDynamicLibraryUid\n", iFileName);

	// Set up the sizes and location of the distinct areas

	iHeaderRange.iSize = sizeof(TRomImageHeader);

	iCodeSection.iSize    = h->iTextSize;
	iCodeSection.iFilePtr = iData + iOrigHdr->iCodeOffset;

	TUint impfmt = h->ImportFormat();

	if (impfmt==KImageImpFmt_PE || impfmt==KImageImpFmt_PE2) {
		TInt nimports = NumberOfImports();
		if (nimports) {
			iImportAddressTableSection.iSize    = (nimports+1)* sizeof(TLinAddr*);
			iImportAddressTableSection.iFilePtr = iData + iOrigHdr->iCodeOffset + iOrigHdr->iTextSize;
			iIATRefs = reinterpret_cast<TLinAddr **>(new char[iImportAddressTableSection.iSize ]);
			memcpy(iIATRefs, iImportAddressTableSection.iFilePtr, iImportAddressTableSection.iSize);
		}

		if (h->iExportDirCount) {
			iExportDirSection.iSize    = h->iExportDirCount*4;
			iExportDirSection.iFilePtr = iData + iOrigHdr->iExportDirOffset;
		}

		// assertion - there's no rdata between IAT and Export Directory
		TInt rdatasize = h->iCodeSize;		// overall "readonly" size
		rdatasize -= iCodeSection.iSize;				// text
		rdatasize -= iImportAddressTableSection.iSize;	// IAT plus trailing 0
		rdatasize -= iExportDirSection.iSize;			// export data

		if (rdatasize != 0) {
			Print(EWarning, "Unexpected code in %s: %d bytes unexplained\n", iFileName, rdatasize);
			// expand the code to cover text+IAT+rdata
			iCodeSection.iSize = h->iCodeSize - iExportDirSection.iSize;
		}
		else {
			if (USE_IAT_FOR_IMPORTS)
				iCodeSection.iSize += iImportAddressTableSection.iSize;	// include IAT
		}
	}
	else {
		// ELF-derived images have no IAT and the export directory is included in the code section
		iImportAddressTableSection.iSize    = 0;
		iImportAddressTableSection.iFilePtr = NULL;
		iExportDirSection.iSize    = 0;
		iExportDirSection.iFilePtr = NULL;
	}

	if (h->iDataSize) {
		iDataSection.iSize    = h->iDataSize;
		iDataSection.iFilePtr = iData + iOrigHdr->iDataOffset;
	}

	iRomNode->iRomFile->iTotalDataBss = h->iDataSize + h->iBssSize;
	iS = h->iS;
	if (iVersionPresentInName && iVersionInName != h->ModuleVersion()) {
		Print(EError,"%s: Version in name (%d.%d) does not match version in header (%d.%d)\n", iFileName,
			iVersionInName>>16, iVersionInName&0x0000ffffu, h->ModuleVersion()>>16, h->ModuleVersion()&0x0000ffffu);
		return KErrGeneral;
	}
	return KErrNone;
}

//
// Approximate the required size of the file when rommed
//
TInt TRomBuilderEntry::SizeInRom() {
	TInt size1, size2;
	SizeInSections(size1,size2);
	return size1+size2;
}
//
//
//
void TRomBuilderEntry::LoadToRom() {
	// Copy fixed stuff into iRomImageHeader
	E32ImageHeaderV* h = iHdr;
	const TUint KRomFlagMask = KImageDll | KImageNoCallEntryPoint | KImageFixedAddressExe | KImageNmdExpData | KImageDataPagingMask;
	TUint romflags = h->iFlags & KRomFlagMask;
	TUint abi = h->ABI();
	TUint ept = h->EntryPointFormat();
	TUint impfmt = h->ImportFormat();
	romflags |= (abi | ept);

	iRomImageHeader = (TRomImageHeader*)iHeaderRange.iImagePtr;

	iRomImageHeader->iUid1              = h->iUid1;
	iRomImageHeader->iUid2              = h->iUid2;
	iRomImageHeader->iUid3              = h->iUid3;
	iRomImageHeader->iUidChecksum       = h->iUidChecksum;
	iRomImageHeader->iEntryPoint        = iCodeSection.iRunAddr + h->iEntryPoint;
	iRomImageHeader->iCodeAddress       = iCodeSection.iRunAddr;
	iRomImageHeader->iDataAddress       = iDataSection.iRunAddr;
	iRomImageHeader->iCodeSize          = iCodeSection.iSize+iExportDirSection.iSize;
	iRomImageHeader->iTextSize          = iCodeSection.iSize;
	iRomImageHeader->iDataSize          = iDataSection.iSize;
	iRomImageHeader->iBssSize           = h->iBssSize;
	iRomImageHeader->iTotalDataSize		= iRomNode->iRomFile->iTotalDataBss;
	iRomImageHeader->iHeapSizeMin       = h->iHeapSizeMin;
	iRomImageHeader->iHeapSizeMax       = h->iHeapSizeMax;
	iRomImageHeader->iStackSize         = h->iStackSize;
	iRomImageHeader->iDllRefTable       = (TDllRefTable*)(iDllRefTableRange.iImageAddr);
	iRomImageHeader->iExportDirCount    = h->iExportDirCount;
	iRomImageHeader->iExportDir         = (impfmt==KImageImpFmt_ELF) ?
		iCodeSection.iRunAddr + (h->iExportDirOffset - h->iCodeOffset)
		: iExportDirSection.iRunAddr;
	iRomImageHeader->iS					= h->iS;
	iRomImageHeader->iToolsVersion		= h->iToolsVersion;
	iRomImageHeader->iModuleVersion		= h->ModuleVersion();
	iRomImageHeader->iFlags             = romflags | iRomImageFlags;
	iRomImageHeader->iPriority          = h->ProcessPriority();
	iRomImageHeader->iDataBssLinearBase = iDataBssLinearBase;
	iRomImageHeader->iNextExtension     = 0;
	iRomImageHeader->iHardwareVariant   = iHardwareVariant;
	iRomImageHeader->iExceptionDescriptor = 0;
	TUint32 xd = h->iExceptionDescriptor;
	if ((xd & 1) && (xd != 0xffffffffu))
		iRomImageHeader->iExceptionDescriptor = (xd & ~1) + iRomImageHeader->iCodeAddress;

	if (iPreferred) {
		iRomImageHeader->iModuleVersion	&= ~0xffffu;
		iRomImageHeader->iModuleVersion	|= 0x8000u;
	}

	// Relocate the file to reflect the new addresses
	Relocate();

	// Copy the sections
	iCodeSection.Load();
	iExportDirSection.Load();
	iDataSection.Load();
}
//
// Relocates the iData to new Code and Data addresses
//
void TRomBuilderEntry::Relocate() {
	TUint codeDelta=iRomImageHeader->iCodeAddress       - iHdr->iCodeBase;
	TUint dataDelta=iRomImageHeader->iDataBssLinearBase - iHdr->iDataBase;

	// code section (text, IAT, export directory)

	if (iOrigHdr->iCodeRelocOffset)
		RelocateSection(iData + iOrigHdr->iCodeOffset, iData + iOrigHdr->iCodeRelocOffset,
		codeDelta, dataDelta, (char*)iCodeSection.iImagePtr, iIATRefs);

	// data section 

	if (iOrigHdr->iDataRelocOffset)
		RelocateSection(iData + iOrigHdr->iDataOffset, iData + iOrigHdr->iDataRelocOffset, 
		codeDelta, dataDelta, (char*)iDataSection.iImagePtr, iIATRefs);

	// export directory (only for PE-derived files)
	if (iExportDirSection.iSize) {
		TLinAddr* ptr=(TLinAddr*)(iData + iOrigHdr->iExportDirOffset);

		TLinAddr textStart = iHdr->iCodeBase;
		TLinAddr textFinish = textStart + iHdr->iTextSize;
		TLinAddr dataStart = textStart + iHdr->iCodeSize;
		TLinAddr dataFinish = dataStart + iHdr->iDataSize + iHdr->iBssSize;

		TInt i;
		for (i=0; i<iHdr->iExportDirCount; i++, ptr++) {
			TLinAddr data=*ptr+textStart;
			if ((data>=textStart) && (data<textFinish))
				*ptr=data+codeDelta; // export something from the text/rdata section
			else if ((data>=dataStart) && (data<dataFinish))
				*ptr=data+dataDelta; // export some data or bss item
			else {
				Print(EWarning, "Export directory in %s: item %d -> %08x, which is not text or data!\n", iFileName, i, data);
				*ptr=0x13;	// unlucky for some
			}
		}
	}

	// Replace absent exports with 0
	TLinAddr* ptr = (TLinAddr*)(iData + iOrigHdr->iExportDirOffset);
	TInt i;
	for (i=0; i<iHdr->iExportDirCount; i++, ptr++) {
		if ( !( iExportBitMap[i>>3] & (1u << (i&7)) ) )
			*ptr = 0;
	}

	// Update E32ImageHeader, in case we want to do this process again later

	iHdr->iCodeBase += codeDelta;
	iHdr->iDataBase += dataDelta;
}


//
// Modify the import stubs to point directly into the export directory of the corresponding DLLs
// using the back pointers captured by detecting relocations referring to the Import Address Table
// The old-style Import Address Table behaviour can be retained by specifying the "keepIAT" attribute.
//

TInt TRomBuilderEntry::FixupImports(E32Rom& aRom) {
	if (iHdr->iImportOffset == 0)
		return KErrNone;	// nothing to do



	TUint impfmt = iHdr->ImportFormat();
	TUint my_abi = iHdr->ABI();
	TRACE(TIMPORT,Print(ELog,"%40s[%08x] flags %08x\n",iFileName,(TUint)iHardwareVariant,iHdr->iFlags));
	const E32ImportSection* importsection = (const E32ImportSection*)(iData + iOrigHdr->iImportOffset);
	TLinAddr **iatRef=iIATRefs;
	TAddressRange iatRange = iCodeSection;
	iatRange.Move(iHdr->iTextSize);
	if (USE_IAT_FOR_IMPORTS) {
		if (impfmt == KImageImpFmt_ELF)
			return Print(EError, "Can't retain IAT for %s since it never existed\n", iFileName);
		if (iRomSectionNumber==0 && aRom.iObey->iSectionPosition!=-1)
			return Print(EError, "Can't retain IAT for %s in first section - not yet implemented\n", iFileName);
		Print(ELog, "%s has IAT at %08x\n", iFileName, iatRange.iRunAddr);
	}
	const E32ImportBlock* b = (const E32ImportBlock*)(importsection + 1);
	TInt i = iHdr->iDllRefTableCount;
	TInt numberOfImports=0;
	TUint *impOrdinalP = (TUint*)iImportAddressTableSection.iFilePtr;	// points to original IAT in file
	while (i-->0) {
		char* dllname = (char*)importsection + b->iOffsetOfDllName;
		TDllFindInfo find_info(dllname, this);
		TBool fallback;
		TRomNode* romnode = aRom.FindImageFileByName(find_info, EFalse, fallback);
		if (!romnode) {
			Print(EError, "Can't fixup imports for\n\t%s\nbecause\n\t%s\nis not in rom.\n",
				(const char*)TModuleName(this), (const char*)TModuleName(find_info));
			aRom.FindImageFileByName(find_info, ETrue, fallback);
			return KErrGeneral;
		}
		TRomFile* dll=romnode->iRomFile;
		TRACE(TIMPORT,Print(ELog,"%s importing from %s\n", (const char*)TModuleName(this), (const char*)TModuleName(find_info)));
		if (romnode->ABI() != my_abi) {
			Print(EWarning, "File %s links to %s with different ABI\n", (const char*)TModuleName(this), (const char*)TModuleName(find_info));
		}
		TInt j;
		numberOfImports += b->iNumberOfImports;
		if (impfmt==KImageImpFmt_ELF)
			impOrdinalP = (TUint*)(b->Imports());	// for ELF must look in import block
		char* codeBase = (char*)iCodeSection.iImagePtr;
		for (j=0; j<b->iNumberOfImports; j++) {
			TLinAddr exportAddr = 0xdeadbeef;
			TLinAddr exporter = 0xdeadbeef;
			TUint impOrdinal = *impOrdinalP;
			TUint impOffset = 0;
			if (impfmt==KImageImpFmt_ELF) {
				TUint impd = *(TUint*)(codeBase + impOrdinal);
				impOrdinal = impd & 0xffff;
				impOffset = impd >> 16;
			}
			TRACE(TIMPORT,Print(ELog,"Ordinal %d\n", impOrdinal));
			TInt ret=dll->AddressFromOrdinal(exporter, exportAddr, impOrdinal);
			TRACE(TIMPORT,Print(ELog,"export %08x exporter %08x\n",exportAddr,exporter));
			if (ret!=KErrNone) {
				Print(EError, "%s wants ordinal %d from %s which only exports %d functions\n",
					iFileName, impOrdinal, 	(const char*)TModuleName(find_info), dll->ExportDirCount());
				exporter=0x13;	// unlucky for some...
				exportAddr=0x13;
			}
			else if (exportAddr == 0 && impOrdinal != 0) {
				Print(EError, "%s wants ordinal %d from %s which is absent\n",
					iFileName, impOrdinal, 	(const char*)TModuleName(find_info));
				exporter=0x13;	// unlucky for some...
				exportAddr=0x13;
			}
			if (USE_IAT_FOR_IMPORTS) {
				// must be PE-derived
				*iatRef=(unsigned long*)exportAddr; //iatRange.iRunAddr;					// point into IAT ...
				*(TLinAddr*)(iatRange.iImagePtr)=exportAddr;	// ... which has a copy of the export
				iatRange.Move(sizeof(TLinAddr));
				iatRef++;
			}
			else if (impfmt==KImageImpFmt_PE || impfmt==KImageImpFmt_PE2) {
				**iatRef=exporter;	// point directly into export directory
				iatRef++;
			}
			else {
				// ELF-derived
				*(TUint*)(codeBase + *impOrdinalP) = exportAddr + impOffset;
			}
			impOrdinalP++;
		}
		b = b->NextBlock(impfmt);
	}
	iImportCount=numberOfImports;
	return KErrNone;
}

const char* KF32ProcessName="efile.exe";
const char* KWservProcessName="ewsrv.exe";
const char* KFbservProcessName="fbserv.exe";
const char* KMdaSvrProcessName="mediaserverstub.exe";
const char* KC32ProcessName="c32exe.exe";

//
// Work out the attach process from the file extension
//
// Only need to handle DLLs which run in F32, WSERV, FBSERV, MEDIASVR, C32
// F32:		FSY FXT
// WSERV:	ANI
// FBSERV:
// MDASVR:	MDA
// C32:		CSY, PRT, TSY, AGT, AGX
//
const char* TRomBuilderEntry::GetDefaultAttachProcess() {
	const char* s=(const char*)iName;
	TInt l=strlen(s);
	if (l<4 || s[l-4]!='.')
		return NULL;
	s+=(l-3);
	if (stricmp(s,"fsy")==0)
		return KF32ProcessName;
	if (stricmp(s,"fxt")==0)
		return KF32ProcessName;
	if (stricmp(s,"ani")==0)
		return KWservProcessName;
	if (stricmp(s,"mda")==0)
		return KMdaSvrProcessName;
	if (stricmp(s,"csy")==0)
		return KC32ProcessName;
	if (stricmp(s,"prt")==0)
		return KC32ProcessName;
	if (stricmp(s,"tsy")==0)
		return KC32ProcessName;
	if (stricmp(s,"agt")==0)
		return KC32ProcessName;
	if (stricmp(s,"agx")==0)
		return KC32ProcessName;
	return NULL;
}

TInt TRomBuilderEntry::FindAttachProcess(E32Rom& aRom) {
	if (iRomImageFlags & (KRomImageFlagVariant|KRomImageFlagExtension|KRomImageFlagDevice))
		return KErrNone;
	const char* attp_name=(const char*)iProcessName;
	int nd=0;
	if (attp_name)
		nd=*attp_name++;
	else
		attp_name=GetDefaultAttachProcess();
	if (!attp_name)
		return KErrNone;
	TInt i;
	TUint my_abi = iHdr->ABI();
	TUint abi = 0;
	if (nd) {
		// path search
		TRomNode* rn=aRom.iObey->iRootDirectory;
		for (; nd; --nd) {
			rn=rn->FindInDirectory(attp_name);
			if (!rn) {
				Print(EError, "Invalid attach process name element %s\n", attp_name);
				return KErrGeneral;
			}
			attp_name+=(strlen(attp_name)+1);
		}
		iRomNode->iRomFile->iAttachProcess=rn->iRomFile;
		abi = iRomNode->iRomFile->iAttachProcess->ABI();
	}
	else {
		// filename only search
		for (i=0; i<aRom.iObey->iNumberOfPeFiles; i++) {
			TRomBuilderEntry* e=aRom.iPeFiles[i];
			abi = e->iHdr->ABI();
			if (stricmp((const char*)e->iName, attp_name)==0) {
				if (iRomNode->iRomFile->iAttachProcess) {
					Print(EError, "Ambiguous attach process name %s\n", attp_name);
					return KErrGeneral;
				}
				iRomNode->iRomFile->iAttachProcess=e->iRomNode->iRomFile;
			}
		}
	}
	if (abi != my_abi) {
		Print(EWarning, "File %s: Attach process has different ABI\n", (const char*)TModuleName(this));
	}
	return KErrNone;
}
//
// Fill in the iDeps
//
TInt TRomBuilderEntry::BuildDependenceGraph(E32Rom& aRom) {
	TBool is_kernel = ((iRomImageFlags & KRomImageFlagsKernelMask) != 0);
	TRomNode* rn = iRomNode;
	TRomFile* rf = rn->iRomFile;
	TUint my_abi = iHdr->ABI();
	TUint impfmt = iHdr->ImportFormat();
	rf->iNumDeps = iHdr->iDllRefTableCount;
	if (IsDll() && aRom.iObey->iMemModel!=E_MM_Flexible && aRom.iObey->iMemModel!=E_MM_Multiple && (iHdr->iDataSize!=0 || iHdr->iBssSize!=0)) {
		TInt r=FindAttachProcess(aRom);
		if (r!=KErrNone)
			return r;
		if (aRom.iObey->iMemModel==E_MM_Moving) {
			if (rf->iAttachProcess && !(rf->iAttachProcess->RomImageFlags() & KRomImageFlagFixedAddressExe))
				rf->iAttachProcess=NULL;	// ignore attach process if not fixed
		}
	}
	TRomFile* attp=rf->iAttachProcess;
	if (attp)
		++rf->iNumDeps;		// extra implicit dependence on process
	if (rf->iNumDeps) {
		rf->iDeps=new TRomFile* [rf->iNumDeps];
		memset(rf->iDeps, 0, rf->iNumDeps*sizeof(TRomFile*));
	}

	TInt err = KErrNone;
	const E32ImportSection* importSection = (const E32ImportSection*)(iData + iOrigHdr->iImportOffset);
	const E32ImportBlock* block = (const E32ImportBlock*)(importSection + 1);
	TInt i;
	for (i=0; i<iHdr->iDllRefTableCount; i++, block = block->NextBlock(impfmt), TRACE(TIMPORT,Print(ELog,"DllRef/dll done\n")) ) {
		char* dllname = (char*)importSection + block->iOffsetOfDllName;
		TDllFindInfo find_info(dllname, this);
		TBool fallback;
		TRomNode* romnode = aRom.FindImageFileByName(find_info, EFalse, fallback);
		if (!romnode) {
			Print(EError, "Can't build dependence graph for\n\t%s\nbecause\n\t%s\nis not in rom.\n",
				(const char*)TModuleName(this), (const char*)TModuleName(find_info));
			aRom.FindImageFileByName(find_info, ETrue, fallback);
			err = KErrNotFound;
			continue;
		}
		if (fallback) {
			Print(EWarning, "File %s links to %s\n\twhich is not in ROM. Version 1.0 of latter used instead.\n",
				(const char*)TModuleName(this), (const char*)TModuleName(find_info));
		}
		TRACE(TIMPORT,Print(ELog,"%s links to %s\n", (const char*)TModuleName(this), (const char*)TModuleName(find_info)));
		TRACE(TIMPORT,Print(ELog,"Resolves to %s\n", (const char*)TModuleName(*romnode)));
		TBool dep_is_kernel = ((romnode->iRomFile->RomImageFlags() & KRomImageFlagsKernelMask) != 0);
		if (dep_is_kernel != is_kernel) {
			if (is_kernel) {
				Print(EError, "Kernel side executable\n\t%s\nlinks to user side executable\n\t%s\n",
					(const char*)TModuleName(this), (const char*)TModuleName(find_info));
			}
			else {
				Print(EError, "User side executable\n\t%s\nlinks to kernel side executable\n\t%s\n",
					(const char*)TModuleName(this), (const char*)TModuleName(find_info));
			}
			err = KErrGeneral;
			continue;
		}
		// prevent the situiation which importer is primary, variant or extension, exporter is device
		if (is_kernel && !Device() && romnode->iRomFile->iRbEntry->Device())	 {
			Print(EWarning, "Kernel/variant/extension\n\t%s\nlinks to non-extension LDD/PDD\n\t%s\n",
				(const char*)TModuleName(this), (const char*)TModuleName(find_info));
		}
		if (romnode->ABI() != my_abi) {
			Print(EWarning, "File %s links to %s with different ABI\n", (const char*)TModuleName(this), (const char*)TModuleName(find_info));
		}

		rf->iDeps[i]=romnode->iRomFile;
		const SSecurityInfo& s1 = iHdr->iS;
		const SSecurityInfo& s2 = romnode->iRomFile->SecurityInfo();
		TInt r = CompareCapabilities(s1.iCaps, s2.iCaps, iFileName, dllname);
		if (r != KErrNone)
			err = r;
		if (romnode->iRomFile==attp)
			attp=NULL;
	}

	if (attp)
		rf->iDeps[rf->iNumDeps-1]=attp;
	TRACE(TIMPORT,Print(ELog,"BuildDep done all\n"));
	return err;
}
//
// Fill in the DLLRefTable
//
TInt TRomBuilderEntry::ResolveDllRefTable(E32Rom& aRom) {
	TRomNode* rn = iRomNode;
	TRomFile* rf = rn->iRomFile;
	(void)aRom;
	if (rf->iNumPDeps==0)
		return KErrNone;	// nothing to do

	TDllRefTable* dllRefTable=(TDllRefTable*)(iDllRefTableRange.iImagePtr);

	TUint16 flags=0;

	dllRefTable->iFlags=flags;
	dllRefTable->iNumberOfEntries=(TUint16)rf->iNumPDeps;

	TInt err = KErrNone;
	TInt i;
	for (i=0; i<rf->iNumPDeps; i++) {
		dllRefTable->iEntry[i]=(TRomImageHeader*)rf->iPDeps[i]->iAddresses.iRunAddr;
	}
	TRACE(TIMPORT,Print(ELog,"DllRef done all\n"));
	return err;
}

void TRomBuilderEntry::DisplaySize(TPrintType aWhere) {
	if(gLogLevel > DEFAULT_LOG_LEVEL){

		if(gLogLevel & LOG_LEVEL_FILE_DETAILS) {
			// More detailed information about file name in .
			TBool aIgnoreHiddenAttrib = ETrue;
			TInt aLen = iRomNode->FullNameLength(aIgnoreHiddenAttrib);
			char * aBuf = new char[aLen+1];
			iRomNode->GetFullName(aBuf, aIgnoreHiddenAttrib);
			if (iPatched|iRomNode->iHidden)
				Print(aWhere, "%s\t%d\t%s\t%s\n", iFileName, SizeInRom(), iPatched?"patched":"hidden", aBuf);
			else
				Print(aWhere, "%s\t%d\t%s\n", iFileName, SizeInRom(), aBuf);
			delete[] aBuf;
		}
	}
	else{
		if (iPatched|iRomNode->iHidden)
			Print(aWhere, "%s\t%d\t%s\n", iFileName, SizeInRom(), iPatched?"patched":"hidden");
		else
			Print(aWhere, "%s\t%d\n", iFileName, SizeInRom());
	}
}

/**
* TRomFile iRomEntry is a linked list through the various
* distinct TRomEntry objects which may exist for the associated file
* due to variant processing / aliasing
*/

void TRomFile::SetRomEntry(TRomEntry* aEntry) {
	// Need to add to the tail of the list, for backwards compatibility
	// Adding to the front of the list changes the iPrimary and iSecondary
	// values in the TRomHeader when multiple variants are present

	if (iFinal)
		return;			// address already established so no fixup required
	if (iRomEntry==0) {
		iRomEntry = aEntry;
		return;
	}
	TRomEntry* entry = iRomEntry;
	while (entry->iAddressLin != 0)
		entry = (TRomEntry*)entry->iAddressLin;
	entry->iAddressLin = (TLinAddr)aEntry;
}

void TRomBuilderEntry::FixupRomEntries(TInt aSize) {
	if (iPatched)
		return;		// patched files don't appear in the ROM file system

	iRomNode->Finalise(aSize);
}

/**
* TRomNode::Finalise updates the associated TRomEntry objects with the final size and
* linear address information supplied by the TRomBuilderEntry. It also stores the
* summary information for resolving static linkages to this executable (if appropriate)
* and adjusts the TRomNodes so that they are identical to the ones which would be 
* obtained by walking the directory structure in an existing ROM.
*/
void TRomNode::Finalise(TInt aSize) {
	TRACE(TIMPORT,Print(ELog,"TRomNode %s Finalise %08x %d\n", (const char*)TModuleName(*this), iRomFile->iFinal));
	iRomFile->Finalise(aSize);
}

/**
* TRomFile::Finalise updates the associated TRomEntry objects with the final size and
* linear address information supplied by the TRomBuilderEntry. It also stores the
* summary information for resolving static linkages to this executable (if appropriate)
* and adjusts the TRomFiles so that they are identical to the ones which would be 
* obtained by walking the directory structure in an existing ROM.
*/
void TRomFile::Finalise(TInt aSize) {
	if (iFinal)
		return;
	TLinAddr ra = iRbEntry->iHeaderRange.iImageAddr;
	TRomEntry* entry = iRomEntry;
	while (entry) {
		TRomEntry* next = (TRomEntry*)entry->iAddressLin;
		entry->iSize = aSize;
		entry->iAddressLin = ra;
		entry = next;
	}
	iAddresses = iRbEntry->iHeaderRange;
	iAddresses.iSize = aSize;
	if ((!iRbEntry->iResource) && (!iRbEntry->HCRDataFile())) {
		iExportDir = iAddresses;
		iExportDir.iSize = iRbEntry->iHdr->iExportDirCount * sizeof(TLinAddr);
		iExportDir.Move(RomImgHdr()->iExportDir - iAddresses.iRunAddr);
	}
	iRbEntry = 0;
	iFinal = ETrue;
}

void TRomBuilderEntry::SetRomNode(TRomNode* aNode) {
	iRomNode = aNode;
}


void TImageSection::Load() const
{ 
	if (iSize && iImagePtr && iFilePtr) 
		memcpy(iImagePtr,iFilePtr,iSize); 
}

/**
* TDllExportInfo is the information about a DLL which is necessary to
* resolve a static link to that DLL. It all comes from the TRomImageHeader,
* as it would with a static linkage resolved at runtime.
*/
TRomFile::TRomFile() {
	memset(this, 0, sizeof(TRomFile));
	iRefCount = 1;
	iHwvd = KVariantIndependent;
	iDataBssOffsetInExe = -1;
}

TRomFile::~TRomFile() {
	if(iDeps) delete[] iDeps;
	if(iPDeps) delete[] iPDeps;
}
//
// Get the export address of symbol aOrdinal
//

TInt TRomFile::AddressFromOrdinal(TLinAddr& aEDataAddr, TLinAddr& aExport, TUint aOrdinal) {
	if(aOrdinal == 0) {
		aEDataAddr = iExportDir.iRunAddr -1 ;
		aExport = *(TLinAddr*)((TLinAddr*)iExportDir.iImagePtr - 1);
		if((TInt)aExport == ExportDirCount()) {
			aEDataAddr = 0;
			aExport = 0;
		}
		return KErrNone;
	}

	TUint index = aOrdinal - KOrdinalBase;
	if (index >= (TUint)ExportDirCount())
		return KErrNotFound;
	aEDataAddr = iExportDir.iRunAddr + index * sizeof(TLinAddr);
	aExport = ((TLinAddr*)iExportDir.iImagePtr)[index];
	return KErrNone;
}


bool TRomFile::ComputeSmpSafe(const TRomBuilderEntry* aRbEntry) {
	// A component is SMP safe if:
	//
	// 1. It's E32 image file is marked as SMP safe (MMP keyword SMPSAFE).
	// 2. All components it links to are SMP safe.
	//
	// This implies a recursive dependency structure.

	if (iSmpInfo.isInit) {
		// We have already visited this node.
		return iSmpInfo.isSafe;
	}

	// Mark this node as "active," meaning that we are currently evaluating it. We
	// use this to detect cycles in the dependency graph.
	iSmpInfo.isActive = 1;

	iSmpInfo.isSafe = 1;

	// Have we found any cycle in the graph?
	bool is_cycle = 0;

	if ( aRbEntry->iOrigHdr->iFlags & KImageSMPSafe ) {
		// OK, the E32 file for this node is marked as SMPSAFE. Now we need to check
		// that all nodes we depend on are SMP safe.

		for (int i = 0; i < iNumDeps; i++) {
			TRomFile* e = iDeps[i];

			assert(this != e);

			if (e->iSmpInfo.isActive) {
				is_cycle = 1;
			}
			else if ( ! e->ComputeSmpSafe(e->iRbEntry) ) {
				if (gLogLevel & LOG_LEVEL_SMP_INFO) {
					Print(ELog,"SMP-unsafe: %s: links to unsafe component %s.\n",
						aRbEntry->iBareName , e->iRbEntry->iBareName);
				}

				iSmpInfo.isSafe = 0;
				break;
			}
		}
	}
	else {
		if (gLogLevel & LOG_LEVEL_SMP_INFO) {
			Print(ELog,"SMP-unsafe: %s: MMP keyword SMPSAFE not used.\n", aRbEntry->iBareName);
		}

		iSmpInfo.isSafe = 0;
	}

	iSmpInfo.isActive = 0;

	if (!iSmpInfo.isSafe || !is_cycle) {
		iSmpInfo.isInit = 1;
	}

	return iSmpInfo.isSafe;
}

/**
* TRomNode::CopyDirectory performs a deep copy of the TRomNode structure
*/
TRomNode* TRomNode::CopyDirectory(TRomNode*& aLastExecutable, TRomNode* aParent) {
	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, aParent);
		else
			return 0;
	}

	TRomNode* copy = new TRomNode(*this);
	copy->iParent = aParent;
	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, copy);
	if (iSibling)
		copy->iSibling = iSibling->CopyDirectory(aLastExecutable, aParent);

	if (copy->iAtt & KEntryAttXIP)
		AddExecutableFile(aLastExecutable,copy);
	return copy;
}

TInt TRomNode::Alias(TRomNode* aNode, TRomNode*& aLastExecutable) {
	if (aNode->iAtt & KEntryAttXIP)
		AddExecutableFile(aLastExecutable,this);
	return SetBareName();
}

TInt TRomNode::Rename(TRomNode *aOldParent, TRomNode* aNewParent, const char* aNewName) {
	aOldParent->Remove(this);
	aNewParent->Add(this);
	if(iName)
		delete []iName;
	iName = NormaliseFileName(aNewName);
	return SetBareName();
}

TInt TRomNode::SetBareName() {
	if(iBareName) {
		delete []iBareName;
		iBareName = 0 ;
	}
	TUint32 uid;
	TUint32 vin;
	TUint32 flg;
	iBareName = SplitFileName(iName, uid, vin, flg);
	if (uid || (flg & EUidPresent))
		return KErrBadName;
	if (strchr(iBareName, '{') || strchr(iBareName, '}'))
		return KErrBadName;
	if ((iAtt & KEntryAttXIP) && (flg & EVerPresent)) {
		TUint32 ver = iRomFile->ModuleVersion();
		if (ver != vin)
			return KErrArgument;
	}
	return KErrNone;
}
