secureswitools/swisistools/source/rscparser/barscimpl.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 16 Apr 2010 15:05:20 +0300
changeset 25 98b66e4fb0be
child 33 8110bf1194d1
permissions -rw-r--r--
Revision: 201011 Kit: 201015

// Copyright (c) 2009 - 2010 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:
//
/** 
* @file BaRscImpl.cpp
*
* @internalComponent
* @released
*/
#include <iostream>
#include <fstream>
#include <cassert>
#include <sys/stat.h>
#include <sstream>
#include "barsc2.h"
#include "barscimpl.h"
#include "ucmp.h"


RResourceFileImpl::TExtra::TExtra():
	iBitArrayOfResourcesContainingCompressedUnicode(NULL),
	iFileOffset(0),
	iFileSize(0)
	{
	}


RResourceFileImpl::TExtra::~TExtra()
	{
	delete iBitArrayOfResourcesContainingCompressedUnicode;
	}


TInt32 RResourceFileImpl::TExtra::ContainsCompressedUnicode(const TInt& aRscIdx) const
	{
	assert(aRscIdx>=0);
	
	if (iBitArrayOfResourcesContainingCompressedUnicode==NULL)
		{
		return EFalse;
		}
	
	TInt index = aRscIdx/8;
	
	return (iBitArrayOfResourcesContainingCompressedUnicode)[index]&(1<<(aRscIdx%8));
	}


RResourceFileImpl::RResourceFileImpl() :
	iResourceContents(NULL),
	iSizeOfLargestResourceWhenCompletelyUncompressed(0),
	iIndex(0),
	iOffset(0),
	iExtra(NULL),
	iFlagsAndNumberOfResources(0)
	{
	// Fixed class size - because of the BC reasons.
	// RResourceFileImpl size must be the same as CResourceFile size.
	enum
		{
		KRscFileImplSize = 24
		};	
	assert(sizeof(RResourceFileImpl) == KRscFileImplSize);

	//Fixed "iOffset" position - because of the BC reasons.
	assert(offsetof(RResourceFileImpl, iOffset)==12);
	}


RResourceFileImpl::~RResourceFileImpl()
{
	if(iResourceContents)
	{
		if (iResourceContents->is_open())
		{
			iResourceContents->close();
		}
		delete iResourceContents;
	}
	iSizeOfLargestResourceWhenCompletelyUncompressed=0;
	delete iIndex;
	iIndex=NULL;
	if (iExtra)
	{
		delete iExtra;
		iExtra=NULL;
	}

	iFlagsAndNumberOfResources=0;
	iOffset=0;
}


void RResourceFileImpl::OpenL(
						  const std::string& aName, 
						  TUint32 aFileOffset, 
						  TInt aFileSize)
{
	iResourceContents= new std::ifstream(aName.c_str(), std::ios::in|std::ios::binary);
		
	if(!iResourceContents->good())
	{
		std::string errMsg= "Unable to open RSC file. " + aName;
		if (iResourceContents->is_open())
			iResourceContents->close();
		if(iResourceContents)
			delete iResourceContents;
		throw CResourceFileException(errMsg);
	}

	iExtra=new TExtra();
	iExtra->iFileOffset = aFileOffset;
	
	TInt fileSize = 0;
	if (aFileSize)
		{
		fileSize = aFileSize;
		assert(fileSize > TInt(aFileOffset));
		}
	else
		{
		// Get the resource file size
		struct stat resourceFileStats;
    
		if (stat(aName.c_str(),&resourceFileStats) == 0)
			{
			// The size of the file in bytes is in
			// resourceFileStats.st_size
			fileSize=resourceFileStats.st_size;
			assert(fileSize > 0);
			}   	
		else
			{
			if (iResourceContents->is_open())
					iResourceContents->close();		
			std::string errMsg="Invalid RSC File";
			throw CResourceFileException(errMsg);
			}		
		}
	
	iExtra->iFileSize = fileSize;	
	
	// Verify the header of the RSC and get the resource index
	ReadHeaderAndResourceIndexL();
}


Ptr8* RResourceFileImpl::GetDecompressedResourceDataL(
													const TInt& aResourceIndex,			
													const TUint32& aFlags) 
	{
	const TInt positionOfResourceData= iIndex[aResourceIndex];
	const TInt numberOfBytes= iIndex[aResourceIndex+1]-positionOfResourceData;
	
	assert(numberOfBytes >= 0);
	
	Ptr8* outputResourceData=new Ptr8(numberOfBytes);
	ReadL(aFlags, positionOfResourceData, outputResourceData->GetPtr(), numberOfBytes);
	outputResourceData->UpdateLength(numberOfBytes);
	
	return outputResourceData;
	}


Ptr8* RResourceFileImpl::AllocReadL(const TInt& aResourceId) 
	{
	
	// Check if the resource id is present in the RSC file
	if (!OwnsResourceId(aResourceId))
		{
		std::ostringstream errDispStream;
		
		errDispStream<<"Resource ID:";
		errDispStream<<aResourceId;
		errDispStream<<" is not present in the RSC file";
		
		throw CResourceFileException(errDispStream.str());
		}
	
	//"-1" because the first resource has ID 0x*****001 (not 0x*****000)
	TInt resourceIndex= (aResourceId & EIdBits)-1; 
	assert(resourceIndex>=0);
	
	Ptr8* decompressedResourceData= 
		GetDecompressedResourceDataL(
								resourceIndex,
								iFlagsAndNumberOfResources & static_cast<TUint32>(EAllFlags));
	
	// Return the resource data if its not unicode compressed.
	if (!iExtra->ContainsCompressedUnicode(resourceIndex))
		{
		return decompressedResourceData;
		}
	
	// Get the decompressed unicode data.
	Ptr8* finalResourceData= DecompressUnicodeL(decompressedResourceData);	
	
	delete decompressedResourceData;	
	return finalResourceData;	
	}


Ptr8* RResourceFileImpl::DecompressUnicodeL(const Ptr8* aInputResourceData) const
	{
	const TInt numberOfBytesInInput= aInputResourceData->GetLength();
	assert(iSizeOfLargestResourceWhenCompletelyUncompressed>0);
							
	Ptr8* outputResourceData= new Ptr8(iSizeOfLargestResourceWhenCompletelyUncompressed);
	
	const TUint8* input= aInputResourceData->GetPtr();
	TInt index=0;
	
	TBool decompressRun=ETrue;
	while (1)
		{
		assert(index<numberOfBytesInInput);
		
		TInt runLength=input[index];
		
		// The run-length occupies a single byte if it is less than 128, 
		// otherwise it occupies two bytes (in little-endian byte order), 
		// with the most significant bit of the first byte set to non-zero 
		//to indicate that the run-length occupies two bytes.
		if (runLength & 0x80)
			{
			++index;
			if (index>=numberOfBytesInInput)
				{
				std::string errMsg="Invalid Rsc File";
				throw CResourceFileException(errMsg);
				}
			runLength &= ~0x80;
			runLength <<= 8;
			runLength |= input[index];
			}
		++index;
		if (runLength>0)
			{			
			if (decompressRun)
				{
				AppendDecompressedUnicodeL(
										outputResourceData,				
										const_cast<unsigned char *>(input+index),
										runLength);
				}
			else
				{
				assert(
					(outputResourceData->GetLength() + runLength) <= 
					iSizeOfLargestResourceWhenCompletelyUncompressed);
				
				memcpy((char*)(outputResourceData->GetPtr()+outputResourceData->GetLength()),(char*)(input+index),runLength);				
				outputResourceData->UpdateLength(runLength);
				}
			index+=runLength;
			}
		if (index>numberOfBytesInInput)
			{
			std::string errMsg="Invalid Rsc File";
			throw CResourceFileException(errMsg);
			}
		if (index>=numberOfBytesInInput)
			{
			break;
			}
			decompressRun=!decompressRun;
		}	
	return outputResourceData;
	}



/** @internalComponent
@return The first resource record.
@panic Some BAFL panic codes, if the file is corrupted.
@leave KErrCorrupt The file is corrupted.
Some other error codes are possible too.
The method could panic or leave depending on the state of
iAssertObj member of RResourceFileImpl::TExtra class. */
RResourceFileImpl::SSigRecord RResourceFileImpl::FirstRecordL() const
	{
	// Added to support reading of rel 6.x resource files.
	// rel 6.x files do not have signatures!
	Ptr8* const firstResource=AllocReadL(1);

	// Basic check to test if the signature is of the correct size.
	if (firstResource->GetLength()!= sizeof(SSigRecord))
	{
		std::string errMsg="Invalid RSS Signature";
		throw CResourceFileException(errMsg);
	}
	SSigRecord sigRecord = *reinterpret_cast<const SSigRecord*>(firstResource->GetPtr());
	delete firstResource;
	return sigRecord;
}

/** Initialises the offset value from the first resource.

The function tests to catch cases where the first resource is not an RSS_SIGNATURE.
It assumes that the first resource in the file consists of
two 32-bit integers. The first integer contains the version number and
the second is a self-referencing link whose value is the offset for
the resources in the file, plus 1.This function must be called before
calling Offset(), AllocReadL(), AllocReadLC() or ReadL().

@see Offset()
@see AllocReadL()
@see AllocReadLC() 
@see ReadL()
@internalComponent
@pre OpenL() is called.
@panic Some BAFL panic codes, if the file is corrupted.
@leave KErrCorrupt The file is corrupted.
Some other error codes are possible too.
The method could panic or leave depending on the state of
iAssertObj member of RResourceFileImpl::TExtra class. */
void RResourceFileImpl::ConfirmSignatureL()
{
	// Added to support reading of rel 6.x resource files.
	SSigRecord firstRecord=FirstRecordL();

	// If the resource offset does not correspond to the first resource
	// this is not a resource signature.
	if ((firstRecord.offset & EIdBits) != 1)
	{
		std::string errMsg="Failed : Invalid RSS Signature";
		throw CResourceFileException(errMsg);
	}
	iOffset=(firstRecord.offset & EOffsetBits);
}

void RResourceFileImpl::AppendDecompressedUnicodeL(
												   Ptr8* aBuffer,
												   const TUint8*  aCompressedUnicode,
												   const TInt& aLengthOfCompressedUnicode) const
	{
	
	if (aLengthOfCompressedUnicode>0)
		{
		TUint8* startOfDecompressedUnicode= aBuffer->GetPtr() + aBuffer->GetLength();
		
		if (reinterpret_cast<TUint32>(startOfDecompressedUnicode) & 0x01)
			{			
			TUint8 padChar = 0xab;
			memcpy(startOfDecompressedUnicode,&padChar,1);
			++startOfDecompressedUnicode;
			aBuffer->UpdateLength(1);
			}
		
		const TInt maximumOutputLength= (
			iSizeOfLargestResourceWhenCompletelyUncompressed - (aBuffer->GetLength()))/2; 
		
		TMemoryUnicodeSink decompressedUnicode(reinterpret_cast<TUint16*>(startOfDecompressedUnicode));
		
		TInt lengthOfDecompressedUnicode;
		TInt numberOfInputBytesConsumed;
		TUnicodeExpander unicodeExpander;
		
		unicodeExpander.ExpandL(decompressedUnicode,
								aCompressedUnicode,
								maximumOutputLength,
								aLengthOfCompressedUnicode,
								&lengthOfDecompressedUnicode,
								&numberOfInputBytesConsumed);
		TInt temp;
		unicodeExpander.FlushL(decompressedUnicode,maximumOutputLength,temp);
		lengthOfDecompressedUnicode+=temp;
		aBuffer->UpdateLength(lengthOfDecompressedUnicode*2);
		
		assert(numberOfInputBytesConsumed == aLengthOfCompressedUnicode);		
		}
	}


TBool RResourceFileImpl::OwnsResourceId(const TInt& aResourceId) const
	{ 
	// Checks whether Rsc file owns the resource:
	// does so if offset is 0, or matches that given, 
	// and id is in index.
	const TInt offset=(aResourceId & EOffsetBits);
//	if ((offset!=0) && (offset!=iOffset))
//		{
//		return EFalse;
//		}
	
	const TInt resourceIndex=(aResourceId & EIdBits)-1;
	TInt numberOfResources=(iFlagsAndNumberOfResources & ~EAllFlags);
	
	return (resourceIndex >= 0) && (resourceIndex < numberOfResources);
	}


void RResourceFileImpl::ReadL(
							const TUint32& aFlags, 
							TInt aPos,
							TUint8* aData,
							const TInt& aLength) 
	{
	aPos += iExtra->iFileOffset;
	
	assert(aPos >= iExtra->iFileOffset);
	assert(aLength >= 0);
	assert((aPos + aLength) <= (iExtra->iFileOffset + iExtra->iFileSize));	
	
	// Seek to the offset specified by "aPos"
	iResourceContents->seekg(aPos, std::ios_base::beg);
	iResourceContents->read((char*)aData, aLength);		
	}


void RResourceFileImpl::ReadL(TInt aPos, TUint8* aData, const TInt& aLength) 
	{
	ReadL(iFlagsAndNumberOfResources & static_cast<TUint32>(EAllFlags),aPos,aData,aLength);
	}


TInt RResourceFileImpl::LittleEndianTwoByteInteger(
													TUint8* aBuffer,
													const TInt& aIndexOfFirstByte) const
	{
	return aBuffer[aIndexOfFirstByte] | (aBuffer[aIndexOfFirstByte+1]<<8);
	}


void RResourceFileImpl::ReadHeaderAndResourceIndexL()
	{
	
	// Unicode compressed RSC file will have 19 bytes header.
	const TUint8 kHeaderSize= 19;
	TUint8 header[kHeaderSize];
			
	sTUid uid;
	
	// Verify the header of the RSC file.
	if(iExtra->iFileSize >= kHeaderSize)
		{
		
		// Get the RSC header
		ReadL(0,0,header,kHeaderSize);
		// Get the first UID
		memcpy((TUint8*)&uid.iUid1,header,4);
		
		// First uid of unicode compressed RSC is "0x101f4a6b"
		TUint32 unicodeCompressedFirstUid = 0x101f4a6b;
		
		if (uid.iUid1 == unicodeCompressedFirstUid)
			{
			iFlagsAndNumberOfResources |= EFlagPotentiallyContainsCompressedUnicode;
			iSizeOfLargestResourceWhenCompletelyUncompressed=	LittleEndianTwoByteInteger(header,16+1);
			}
		else
			{
				if (iResourceContents->is_open())
						iResourceContents->close();
			std::string errMsg="Failed : Unsupported RSC file type";
			throw CResourceFileException(errMsg);
			}
		}
		
		TInt numberOfResources= 0;
		TUint8* bitArrayOfResourcesContainingCompressedUnicode= NULL;
		
		if (iFlagsAndNumberOfResources & EFlagPotentiallyContainsCompressedUnicode)
		{
			// Cache the resource-index (in iIndex) to minimize disk access.		
			const TInt KMaximumNumberOfBytesCached= 256;
			TUint8 cache[KMaximumNumberOfBytesCached];
			const TInt numberOfBytesCached=
				((iExtra->iFileSize>KMaximumNumberOfBytesCached) ? KMaximumNumberOfBytesCached : iExtra->iFileSize);
			
			ReadL(iExtra->iFileSize-numberOfBytesCached, cache, numberOfBytesCached);
			
			const TInt positionOfStartOfIndex=
				((cache[numberOfBytesCached-1]<<8) | cache[numberOfBytesCached-2]);
			const TInt numberOfBytesOfIndex=iExtra->iFileSize-positionOfStartOfIndex;
			
			assert(numberOfBytesOfIndex%2==0);
			assert(numberOfBytesOfIndex>=0);
			
			const TInt numberOfBytesOfIndexStillToRetrieve = numberOfBytesOfIndex-numberOfBytesCached;
			
			if (numberOfBytesOfIndexStillToRetrieve<=0)
				{
				iIndex= new TUint16[numberOfBytesOfIndex/2];
				memcpy(iIndex, cache+(numberOfBytesCached-numberOfBytesOfIndex), numberOfBytesOfIndex);
				}
			else
				{
				TUint16* index= new TUint16(numberOfBytesOfIndex/2);			
				ReadL(positionOfStartOfIndex, reinterpret_cast<TUint8*>(index), numberOfBytesOfIndexStillToRetrieve);
				memcpy((index+numberOfBytesOfIndexStillToRetrieve),cache,numberOfBytesCached); 		

				iIndex=index;			
				}

			//"-1" because the last thing in the index (which is in fact the last thing in the 
			//file itself) is the position of the start of the index which is therefore not 
			//pointing to a resource
			numberOfResources=(numberOfBytesOfIndex/2) - 1; 
			
			if (numberOfResources <= 0)
				{
				if (iResourceContents->is_open())
						iResourceContents->close();
				std::string errMsg="Failed : Invalid RSC file.";
				throw CResourceFileException(errMsg);
				}
				
			const TInt lengthOfBitArrayInBytes=(numberOfResources+7)/8;
			bitArrayOfResourcesContainingCompressedUnicode=
								new TUint8(lengthOfBitArrayInBytes);
			
			//"16+1+2": 16 bytes of checked-UID + 1 byte of flags (these flags are for a 
			//dictionary-compressing program's use rather than directly for Bafl's use, 
			//so we ignore them) + 2 bytes containing the size of the largest resource when 
			//uncompressed
			ReadL(16+1+2,bitArrayOfResourcesContainingCompressedUnicode,lengthOfBitArrayInBytes); 
		
		}
		
		assert((numberOfResources & EAllFlags)==0);
		assert((iFlagsAndNumberOfResources & ~EAllFlags)==0);
		
		iFlagsAndNumberOfResources |= (numberOfResources & ~EAllFlags);
		iExtra->iUid = uid;
		iExtra->iBitArrayOfResourcesContainingCompressedUnicode = bitArrayOfResourcesContainingCompressedUnicode;
			
	}