lowlevellibsandfws/apputils/src/BaArchiveImpl.cpp
author Stefan Karlsson <stefan.karlsson@nokia.com>
Mon, 29 Mar 2010 12:27:51 +0100
branchCompilerCompatibility
changeset 14 69a2780c0013
parent 0 e4d67989cc36
permissions -rw-r--r--
Merge.

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

#include <barsc2.h>
#include "BaArchiveImpl.h"
#include <baflpan.h>
#include <bautils.h>

/** TCleanupItem function.
@internalComponent
*/
LOCAL_C void CloseAndDeleteFile(TAny* aFile)
	{
	RFile* const file=static_cast<RFile*>(aFile);
	if (file!=NULL)
		{
		file->Close();
		delete file;
		}
	}

/** TCleanupItem function.
@internalComponent
*/
LOCAL_C void CloseAndDeleteChunk(TAny* aChunk)
	{
	RChunk* const rchunk=static_cast<RChunk*>(aChunk);
	if (rchunk!=NULL)
		{
		rchunk->Close();
		delete rchunk;
		}
	}

/** Default Constructor
@internalComponent
*/
CResourceArchiveImpl::CResourceArchiveImpl() :
	iCurrentIndex(0),
	iSpiFileType(KNullUid)
	{}

/** Class destructor
@internalComponent 
*/
CResourceArchiveImpl::~CResourceArchiveImpl()
	{
	iRscList.Close();
	
	for(TInt i=0; i<iSpiChunkBufferArray.Count(); i++)
		{
		RChunk* const chunk = iSpiChunkBufferArray[i];                     
		chunk->Close();
		delete chunk;
		}
	
	iSpiChunkBufferArray.Reset();
	iSpiChunkBufferArray.Close();

	iSpiBufferArray.ResetAndDestroy();
	iSpiBufferArray.Close();
	iCurrentIndex=0;
	iSpiFileType=KNullUid;
#ifdef __BASPITEST__
	iSpiFileNameArray.Reset();
	iSpiFileNameArray.Close();
#endif	
	}

/** Creates a CResourceArchiveImpl instance to read a single spi file
@internalComponent
*/
CResourceArchiveImpl* CResourceArchiveImpl::NewL(RFs& aFs,const TDesC& aName)
	{
	CResourceArchiveImpl* self=new (ELeave) CResourceArchiveImpl;
	CleanupStack::PushL(self);
	self->ConstructL(aFs,aName);
	CleanupStack::Pop();
	return self;
	}

/** Creates a CResourceArchiveImpl instance to read a set of spi files
@internalComponent
*/	
CResourceArchiveImpl* CResourceArchiveImpl::NewL(RFs& aFs,const TDesC& aPath,const TDesC& aPattern)
	{
	CResourceArchiveImpl* self=new (ELeave) CResourceArchiveImpl;
	CleanupStack::PushL(self);
	self->ConstructL(aFs,aPath,aPattern);
	CleanupStack::Pop();
	return self;	
	}

/** ConstructL method of CResourceArchiveImpl
@internalComponent
*/
void CResourceArchiveImpl::ConstructL(RFs& aFs,const TDesC& aName)
	{
	RArray<TPtrC8> hiddenRscList;
	CleanupClosePushL(hiddenRscList);
	//Open the file and initialise the buffer pointer
	TPtr8 bufferPtr(NULL,0,0);
	OpenFileL(aFs,aName,bufferPtr);
	//Now validate the header
	ValidateHeaderL(bufferPtr);
	//Process all the rsc entry
	ProcessEntryL(bufferPtr,hiddenRscList);	
	CleanupStack::PopAndDestroy();
	}
	

/** Function to get the string representation of a language code
assume language code has max N digits currently equal to ELangMaximum
note that the minimum suffix required is NN so TLang(0-9) needs to have a 0
e.g s01...s09, s10, s100 etc
@internalComponent
*/
static void GetLangCodeStringRep(TLanguage lang,TDes& aStringRep)
{
	aStringRep.Zero();
	//special case for 0-9 where you need the 0 appended, langcode min two digit
	if (lang<10)
		aStringRep.AppendNumFixedWidthUC(lang,EDecimal,2);
	else
		aStringRep.AppendFormat(_L("%d"),lang);
}

/**
Open the file and initialise the aBufferPtr to point at the buffer
@internalComponent
*/
void CResourceArchiveImpl::OpenFileL(RFs& aFs,const TDesC& aName,TPtr8& aBufferPtr)
	{
	#ifdef __WINS__
	TUint8* romAddress=NULL;
	#else // ! __WINS__
	TUint8* romAddress=aFs.IsFileInRom(aName);
	#endif // ! __WINS__
	
	RFile* const file=new(ELeave) RFile;
	CleanupStack::PushL(TCleanupItem(CloseAndDeleteFile,file));
	User::LeaveIfError(file->Open(aFs, aName, EFileStream | EFileRead | EFileShareReadersOnly));
	TInt fileSize=0;
	User::LeaveIfError(file->Size(fileSize));						
	if (romAddress)
		{	
		aBufferPtr.Set(romAddress,fileSize,fileSize);
		}
	else
		{
		RChunk* rchunk = new(ELeave) RChunk;
		
		CleanupStack::PushL(TCleanupItem(CloseAndDeleteChunk,rchunk));
		
		TInt createRet = rchunk->CreateDisconnectedLocal(0, 0, fileSize);
		TInt commitRet = rchunk->Commit(0, fileSize);
		
		// Create a chunk to store the large file contents of which is closed and destroyed in destructor...
		if(createRet==KErrNone && commitRet==KErrNone)
			{
			TUint8* chunkBase = (TUint8*)rchunk->Base();
			TPtr8 tempBuffer(chunkBase, fileSize);
			User::LeaveIfError(file->Read(0, tempBuffer, fileSize));
			aBufferPtr.Set(tempBuffer);
			iSpiChunkBufferArray.AppendL(rchunk);
			
			CleanupStack::Pop(rchunk);
			}
		else	// Unable to create a chunk so use heap memory...
			{
			HBufC8* fileBuffer=HBufC8::NewMaxLC(fileSize);
			aBufferPtr.Set(fileBuffer->Des());
			User::LeaveIfError(file->Read(0,aBufferPtr,fileSize));
			iSpiBufferArray.AppendL(fileBuffer);
			
			CleanupStack::Pop(fileBuffer);
			CleanupStack::PopAndDestroy(rchunk);
			}
		}
	//can close the file now
	CleanupStack::PopAndDestroy(file);
#ifdef __BASPITEST__
	iSpiFileNameArray.AppendL(aName);
#endif		
	}

/**
Validate the spi header in the file buffer
On return it will update the bufferPtr to point to start of the first
rsc entry
@internalComponent
*/
void CResourceArchiveImpl::ValidateHeaderL(TPtr8& aBufferPtr)
	{
	//Getting the 32 bytes header information. At the moment maybe we should just buffer 16 bytes
	//of the header as the remaining 16 bytes are padding bytes
	TUidType uidType=TCheckedUid(aBufferPtr.Left(16)).UidType();			
	if (uidType[0]!=KSpiFileUid)
		User::Leave(KErrCorrupt);
	//now get the spi file type
	TUid spiFileType=TUid::Uid(uidType[1].iUid);
	if (iSpiFileType==KNullUid)
		iSpiFileType=spiFileType;
	//also check consistency with previous spi files
	__ASSERT_DEBUG(iSpiFileType==spiFileType,::Panic(EBafPanicBadResourceFileFormat));							

	//update the bufferPtr to point to start of first rsc entry
	aBufferPtr.Set(aBufferPtr.MidTPtr(KSpiFirstRscOffset));	
	}

/**
Process all the entry found in the buffer and update the internal rscList
@internalComponent
*/
void CResourceArchiveImpl::ProcessEntryL(TPtr8& aBufferPtr,RArray<TPtrC8>& aHiddenList)
	{
	//now traverse content of the spi file and build up the hidden list and the TRscEntry array
	TRscEntry rscEntry;
	while (aBufferPtr.Length()>0)
		{
		//length(first 4 bytes) and the actual rsc file size(second 4 bytes)
		//Retrieving the rscfilename length
		TUint32 rscFileNameLength=LittleEndianFourByteInteger(aBufferPtr,0);
		TUint32 rscFileSize=LittleEndianFourByteInteger(aBufferPtr,4);
		TUint32 paddingbyte=(4-((rscFileNameLength+rscFileSize)%4))%4;
		__ASSERT_DEBUG((rscFileNameLength+rscFileSize+paddingbyte)%4==0,::Panic(EBafPanicFileSize));
		//construct the TRscEntry				
		rscEntry.iRscName.Set(aBufferPtr.Mid(8,rscFileNameLength));
		rscEntry.iRscData.Set(aBufferPtr.Mid(8+rscFileNameLength,rscFileSize));
#ifdef __BASPITEST__
		rscEntry.iFileNamePtr.Set(iSpiFileNameArray[iSpiFileNameArray.Count()-1].Mid(0));
#endif				
		//update the buffer pointer
		aBufferPtr.Set(aBufferPtr.MidTPtr(8+rscFileNameLength+rscFileSize+paddingbyte));

		//process the TRscEntry
		if (rscEntry.iRscData.Length()==0)
			aHiddenList.AppendL(rscEntry.iRscName);
		else
			{
			//if can find a matching resource name entry in the hidden list ignore this entry
			TIdentityRelation<TPtrC8> identity(MatchDescriptor);
			if (aHiddenList.Find(rscEntry.iRscName,identity)==KErrNotFound)
				{
				//note no duplicate entry, this implies the lastly mounted resource
				//entry is preferred over earlier mounted resource entry.(REPLACING)
				TInt ret=iRscList.InsertInOrder(rscEntry,EntryOrder);
				if (ret!=KErrNone && ret!=KErrAlreadyExists)
					User::Leave(ret);
				}		
			}
		}//end while loop	
	}
	
/** ConstructL method that accepts a spi path and a matching pattern
aPath here must end with \\ as well e.g. z:\\private\\10009d8f\\ 
aPattern should be the spi file withou rom image id and extension
@internalComponent
*/
void CResourceArchiveImpl::ConstructL(RFs& aFs,const TDesC& aPath,const TDesC& aPattern)
	{
	//Get the downgradepath
	RArray<TLanguage> downgradeList;
	BaflUtils::GetDowngradePathL(aFs,User::Language(),downgradeList);
	CleanupClosePushL(downgradeList);
	
	//used to check if there is any matching pattern and leave with KErrNotFound if
	//no matching spi
	TInt spiDiscovered=0;
	
	//sort out the language specific spi first
	for (int i=0;i<=downgradeList.Count();i++)	
		{
		//list all the files in the directory that matches this language
		TFileName matchPattern;
		TBuf<5> langExtension;
		if (i<downgradeList.Count())
			GetLangCodeStringRep(downgradeList[i],langExtension);
		else
			{
			//process the default spi lastly
			langExtension.Append(_L("pi"));			
			}
		matchPattern.AppendFormat(_L("%S%S-*-*.s%S"),&aPath,&aPattern,&langExtension);

		//Get all the spis that match the language pattern
		CDir* spiList=NULL;
		User::LeaveIfError(aFs.GetDir(matchPattern,KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive,ESortByName,spiList));
		CleanupStack::PushL(spiList);

		//the hidden list is only a temporary variable for each language
		RArray<TPtrC8> hiddenRscList;
		CleanupClosePushL(hiddenRscList);
		
		//reverse processing
		for (TInt i=spiList->Count()-1;i>=0;i--)
			{
			//reuse the matchPattern buffer to hold the full path of the spi file name
			matchPattern.Zero();
			matchPattern.Append(aPath);
			matchPattern.Append((*spiList)[i].iName);

			//Open the file and initialise the buffer pointer
			TPtr8 bufferPtr(NULL,0,0);
			OpenFileL(aFs,matchPattern,bufferPtr);
	
			//Now validate the header
			ValidateHeaderL(bufferPtr);
	
			//Process all the rsc entry
			ProcessEntryL(bufferPtr,hiddenRscList);
			}//end for loop
		spiDiscovered+=spiList->Count();
		CleanupStack::PopAndDestroy(2);				//hiddenRscList and spiList
		}//end for loop
	//if no spi files are discovered at all leave with KErrNotFound
	if (spiDiscovered==0)
		User::Leave(KErrNotFound);
	CleanupStack::PopAndDestroy(&downgradeList);
	}

/** Create the next CResourceFile that correponds to the next resource file
in the resource archive(SPI) file
@internalComponent
*/
CResourceFile* CResourceArchiveImpl::NextL(HBufC*& aRscFileName)
	{
	//if end of list return nothing
	if (iCurrentIndex==iRscList.Count())
		return NULL;
	//Construct the resource file name buffer
	TRscEntry currentEntry=iRscList[iCurrentIndex];
	HBufC* rscFileName=HBufC::NewLC(currentEntry.iRscName.Length());
	TPtr modifiable(rscFileName->Des());
	modifiable.Copy(currentEntry.iRscName);
	//Construct the CResourceFile object	
	CResourceFile* resourceFile=CResourceFile::NewL(currentEntry.iRscData);
	CleanupStack::Pop(rscFileName);
	//update the index
	iCurrentIndex++;
	aRscFileName=rscFileName;
	return resourceFile;
	}

/** Reset the archive reader to start reading from the first resource
@internalComponent 
*/
void CResourceArchiveImpl::Reset()
	{
	iCurrentIndex=0;
	}

/** Return the type of the resource archive(SPI) file being read
@internalComponent 
*/
TUid CResourceArchiveImpl::Type()
	{
	return iSpiFileType;
	}

/** Function to look ahead whether next resource exists in the resource archive
@internalComponent
*/
TBool CResourceArchiveImpl::NextResourceExist() const
	{
	return (iCurrentIndex<iRscList.Count());
	}

/** Function used to retrieve a 4 bytes signed integer stored in Little Endian Format
@internalComponent
*/
TInt32 CResourceArchiveImpl::LittleEndianFourByteInteger(const TDesC8& aBuffer,TInt aIndexOfFirstByte) const
	{
	__ASSERT_DEBUG((aIndexOfFirstByte + 3) < aBuffer.Length(), ::Panic(EBafPanicBadIndex));
	return aBuffer[aIndexOfFirstByte] | (aBuffer[aIndexOfFirstByte+1]<<8) | (aBuffer[aIndexOfFirstByte+2]<<16) | (aBuffer[aIndexOfFirstByte+3]<<24);
	}