Fix for fsh-buildsis when fshell source dir isn't under epocroot.
// fzipup.cpp
//
// Copyright (c) 2008 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "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:
// Accenture - Initial contribution
//
#include <ezstream.h>
#include <ezcompressor.h>
#include <ezdecompressor.h>
#include <ezfilebuffer.h>
#include <f32file.h>
#include "fzipup.h"
const TInt KDefaultDiskNumber = 0;
const TInt CZipEntry::iOffset = _FOFF(CZipEntry, iLink);
const TUint16 KFZipVersion = 0x0014;
TInt KWindowBits = -CEZCompressor::EMaxWBits; // negative value is an undocumented feature of our zlib implementation, ensuring we do not include the zlib header in the compressed data (which we must do for zip-file format support)
const TInt KCompressedSizeRelativePosition = 18; // iCompressedSize is 18 bytes in from the current entry's LFH
//
// CBufferManager
//
CBufferManager *CBufferManager::NewLC(RFileReadStream& aInput, RFileWriteStream& aOutput, TInt aInputStreamBytes, TInt aBufferSize)
{
CBufferManager *bm = new (ELeave) CBufferManager(aInput, aOutput, aInputStreamBytes, aBufferSize);
CleanupStack::PushL(bm);
bm->ConstructL();
return bm;
}
CBufferManager::CBufferManager(RFileReadStream& aInput, RFileWriteStream& aOutput, TInt aInputStreamBytes, TInt aBufferSize)
:iInput(aInput), iOutput(aOutput), iInputStreamBytes(aInputStreamBytes), iBufferSize(aBufferSize), iInputDescriptor(NULL,0), iOutputDescriptor(NULL,0)
{
}
CBufferManager::~CBufferManager()
{
delete[] iInputBuffer;
delete[] iOutputBuffer;
}
void CBufferManager::ConstructL()
{
if (iInputStreamBytes < iBufferSize)
iBufferSize = iInputStreamBytes;
if (iBufferSize <=0)
{
User::Leave(KErrUnderflow);
}
iInputBuffer = new (ELeave) TUint8[iBufferSize];
iOutputBuffer = new (ELeave) TUint8[iBufferSize];
iInputDescriptor.Set(iInputBuffer,0,iBufferSize);
iOutputDescriptor.Set(iOutputBuffer,0,iBufferSize);
iReadLength = iBufferSize;
}
void CBufferManager::InitializeL(CEZZStream &aZStream)
{
ReadInputL();
aZStream.SetInput(iInputDescriptor);
aZStream.SetOutput(iOutputDescriptor);
}
void CBufferManager::NeedInputL(CEZZStream &aZStream)
{
ReadInputL();
aZStream.SetInput(iInputDescriptor);
}
void CBufferManager::NeedOutputL(CEZZStream &aZStream)
{
WriteOutputL(aZStream);
aZStream.SetOutput(iOutputDescriptor);
}
void CBufferManager::FinalizeL(CEZZStream &aZStream)
{
WriteOutputL(aZStream);
}
void CBufferManager::ReadInputL()
{
iInput.ReadL(iInputDescriptor, iReadLength);
iInputStreamBytes -= iReadLength;
if (iInputStreamBytes < iBufferSize)
{
iReadLength = iInputStreamBytes;
}
if (iReadLength < 0)
{
User::Leave(KErrOverflow);
}
}
void CBufferManager::WriteOutputL(CEZZStream& aZStream)
{
TPtrC8 od = aZStream.OutputDescriptor();
iOutput.WriteL(od);
iBytesWritten += od.Size();
}
//
// CZipEntry
//
CZipEntry* CZipEntry::NewLC(RFs& aFs, const TDesC& aFilename, const TInt aUid)
{
CZipEntry* self = new (ELeave) CZipEntry(aFs, aUid);
CleanupStack::PushL(self);
self->ConstructL(aFilename);
return self;
}
CZipEntry::CZipEntry(RFs& aFs, const TInt aUid):
iFs(aFs), iUid(aUid)
{
iLFH.iCRC32 = crc32(iLFH.iCRC32, NULL, 0);
}
CZipEntry::~CZipEntry()
{
if (iFileData)
delete iFileData;
if (iInput.SubSessionHandle() > 0)
iInput.Close();
}
void CZipEntry::ConstructL(const TDesC& aFilename)
{
User::LeaveIfError(iInput.Open(iFs, aFilename, EFileRead | EFileShareReadersOnly));
// ensure aFileName is stored in accordance with zip file-format specification
TFileName2 file(aFilename);
if (file.HasDriveLetter())
{
file.Delete(0, 3); // remove '<drive>:\'
}
if (file.HasLeadingSlash())
{
file.Delete(0, 1); // remove '\'
}
iAsciiName.Copy(file);
TUint8* ptr = const_cast<TUint8*> (iAsciiName.Ptr());
TInt count = 0;
do
{
if (*ptr == '\\')
{
*ptr = '/';
}
ptr++;
} while (count++ < iAsciiName.Length());
// config. local file header
iLFH.iSignature = KLocalHeaderSignature;
iLFH.iVersionNeeded = KFZipVersion;
iLFH.iFlags = 0x0002; // best (-exx/-ex) compression was used
iLFH.iCompressionMethod = EDeflated;
TTime time;
User::LeaveIfError(iInput.Modified(time));
TDateTime td = time.DateTime();
TUint16 param1 = td.Year() - 1980;
TUint16 param2 = td.Month() + 1;
TUint16 param3 = td.Day() + 1;
iLFH.iLastModifiedFileDate = (param1 << 9) | (param2 << 5) | param3;
param1 = td.Hour() + 1;
param2 = td.Minute() + 1;
param3 = td.Second() + 1;
iLFH.iLastModifiedFileTime = (param1 << 11) | (param2 << 5) | (param3 >> 1);
TInt uSize = 0;
User::LeaveIfError(iInput.Size(uSize));
if (uSize <= 0)
{
User::Leave(KErrAbort);
}
iLFH.iUncompressedSize = uSize;
iLFH.iCompressedSize = uSize; // note: to be adjusted later once the compression is performed
CalcCRCL();
iLFH.iFileNameLength = iAsciiName.Size();
iLFH.iExtraFieldLength = 0;
// config. file header
iFH.iSignature = KCentralDirectoryHeaderSignature;
iFH.iMadeBy = KFZipVersion;
iFH.iRequired = KFZipVersion;
iFH.iFlags = iLFH.iFlags;
iFH.iCompressionMethod = iLFH.iCompressionMethod;
iFH.iLastModifiedFileTime = iLFH.iLastModifiedFileTime;
iFH.iLastModifiedFileDate = iLFH.iLastModifiedFileDate;
iFH.iCRC32 = iLFH.iCRC32;
iFH.iCompressedSize = iLFH.iCompressedSize; // note: to be adjusted later once the compression is performed
iFH.iUncompressedSize = iLFH.iUncompressedSize;
iFH.iFileNameLength = iLFH.iFileNameLength;
iFH.iExtraFieldLength = iLFH.iExtraFieldLength;
iFH.iFileCommentLength = 0x0000;
iFH.iDiskNumberStart = KDefaultDiskNumber;
iFH.iInternalFileAttributes = 0x0001;
iFH.iExternalFileAttributes = 0x00000020; // hardcoded to 'Archive'
iFH.iLocalHeaderOffset = 0x00000000; // to be adjusted later (if necessary)
}
void CZipEntry::SetOffset(TUint32 aOffset)
{
iFH.iLocalHeaderOffset = aOffset;
}
TUint32 CZipEntry::ReturnOffset()
{
TUint32 result = iFH.iLocalHeaderOffset; // start with the offset of the LFH
result += KLocalHeaderFixedLength; // add the size of the LFH itself
result += iAsciiName.Size(); // add the size of the filename
result += iLFH.iCompressedSize; // add any compressed data
return result;
}
TUint32 CZipEntry::FileHeaderSize()
{
TUint32 result = KCentralDirectoryHeaderFixedLength;
result += iAsciiName.Size();
return result;
}
//
// CZipEntry::CalcCRCL
// mem. efficient means of calculating the crc on uncompressed data
// we require a maximum of KDefaultZipBufferLength heap space regardless size of data
//
void CZipEntry::CalcCRCL()
{
ASSERT(iInput.SubSessionHandle() > 0);
TInt bytesRead = 0;
TInt length = KDefaultZipBufferLength; // 32Kb
if (iLFH.iUncompressedSize < KDefaultZipBufferLength)
{
length = iLFH.iUncompressedSize;
}
iLFH.iCRC32 = crc32(0, NULL, 0);
HBufC8* crc = HBufC8::NewLC(length);
TPtr8 crcPtr = crc->Des();
User::LeaveIfError(iInput.Seek(ESeekStart, bytesRead));
do
{
User::LeaveIfError(iInput.Read(crcPtr, length));
iLFH.iCRC32 = crc32(iLFH.iCRC32, crcPtr.Ptr(), length);
bytesRead += length;
if ((iLFH.iUncompressedSize - bytesRead) < KDefaultZipBufferLength)
{
length = iLFH.iUncompressedSize - bytesRead;
}
} while (length > 0);
CleanupStack::PopAndDestroy(1); // crc
}
//
// CZipItUp
//
CZipItUp* CZipItUp::NewLC(RFs& aFs, TDesC& aArchive)
{
CZipItUp* self = new (ELeave) CZipItUp(aFs, aArchive);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
CZipItUp::CZipItUp(RFs& aFs, TDesC& aArchive):
iFs(aFs), iFileName(aArchive), iZipMemberLinkedList(CZipEntry::iOffset), iZipMemberLinkedListIter(iZipMemberLinkedList)
{
}
CZipItUp::~CZipItUp()
{
if (iFile.SubSessionHandle() > 0)
{
iFile.Close();
}
while (!iZipMemberLinkedList.IsEmpty())
{
CZipEntry* entry = iZipMemberLinkedList.First();
iZipMemberLinkedList.Remove(*entry);
delete entry;
}
iZipMemberLinkedList.Reset();
}
void CZipItUp::ConstructL()
{
User::LeaveIfError(iFile.Create(iFs, iFileName, EFileStream | EFileShareExclusive));
}
//
// CZipItUp::AddFileL
//
void CZipItUp::AddFileL(const TDesC& aFile)
{
if (!DuplicateEntryL(aFile))
{
// spawn a new zip entry, compress the file, add it to the list of zip file contained in the archive
CZipEntry* zipEntry = CZipEntry::NewLC(iFs, aFile, ++iEntryUid);
AddEntryL(*zipEntry);
CleanupStack::Pop(zipEntry);
}
}
//
// CZipItUp::DuplicateEntryL
// run a check against the zip archive's current entries (if any!) for duplicate files
// return ETrue if a duplicate is found
//
TBool CZipItUp::DuplicateEntryL(const TDesC& aFile)
{
if (iZipMemberLinkedList.IsEmpty())
{
return EFalse;
}
// turn aFile into a zip-file format filename
TFileName2 file(aFile);
if (file.HasDriveLetter())
{
file.Delete(0, 3); // remove '<drive>:\'
}
if (file.HasLeadingSlash())
{
file.Delete(0, 1); // remove '\'
}
TBuf8<KMaxFileName> ascii;
ascii.Copy(file);
TUint8* ptr = const_cast<TUint8*> (ascii.Ptr());
TInt count = 0;
do
{
if (*ptr == '\\')
{
*ptr = '/';
}
ptr++;
} while (count++ < ascii.Length());
// iterate through zip archive entries looking for a duplicate
iZipMemberLinkedListIter.SetToFirst();
CZipEntry* entry = iZipMemberLinkedListIter++;
while (entry != NULL)
{
if (entry->iAsciiName == ascii)
{
return ETrue;
}
entry = iZipMemberLinkedListIter++;
}
return EFalse;
}
//
// CZipItUp::AddEntryL
// Add an entry to the zip archive.
//
void CZipItUp::AddEntryL(CZipEntry& aEntry)
{
// append the new entry to the queue
if (!iZipMemberLinkedList.IsEmpty())
{
// prior entries in the list therefore need to adjust this entry's local file header offset
CZipEntry* lastEntry = iZipMemberLinkedList.Last();
User::LeaveIfNull(lastEntry);
}
iZipMemberLinkedList.AddLast(aEntry);
}
//
// CZipItUp::CreateZipL
// Creates the actual file, including all the previously compressed zip files in the archive
//
void CZipItUp::CreateZipL()
{
ASSERT(iFile.SubSessionHandle() > 0);
if (iZipMemberLinkedList.IsEmpty())
{
User::Leave(KErrGeneral);
}
RFileWriteStream stream(iFile);
stream.PushL();
// initialise central directory header
iCDT.iSignature = CZipArchive::KCentralDirectorySignature;
iCDT.iDiskNumber = KDefaultDiskNumber;
iCDT.iStartDiskNumber = KDefaultDiskNumber;
iCDT.iLocalEntryCount = 0;
iCDT.iTotalEntryCount = 0;
iCDT.iSize = 0;
iCDT.iCommentLength = 0;
// iterate through each zip entry, appending local file header (LFH), file data (FD) to file
iZipMemberLinkedListIter.SetToFirst();
CZipEntry* entry = iZipMemberLinkedListIter++;
CZipEntry* prior = NULL;
User::LeaveIfNull(entry);
do
{
if (prior)
{
// adjust file header local offset if necessary
entry->SetOffset(prior->ReturnOffset());
}
// insert entry's Local File Header
AppendLocalFileHeaderL(stream, *entry);
// insert entry's File Data
AppendCompressedDataL(stream, *entry);
// adjust cdt parameters
iCDT.iLocalEntryCount++;
iCDT.iTotalEntryCount++;
iCDT.iSize += entry->FileHeaderSize();
// move onto the next entry
prior = entry;
entry = iZipMemberLinkedListIter++;
} while (entry != NULL);
// adjust cdt offset parameter
iCDT.iOffset = iZipMemberLinkedList.Last()->ReturnOffset();
// iterate through each zip entry, appending the central directory File Headers to file (these must go after LFH, FD entries)
iZipMemberLinkedListIter.SetToFirst();
entry = iZipMemberLinkedListIter++;
do
{
// central directory File Header
AppendCentralDirectoryFileHeaderL(stream, *entry);
// move onto the next entry
entry = iZipMemberLinkedListIter++;
} while (entry != NULL);
// append the central directory trailer
AppendCentralDirectoryTrailerL(stream);
// commit the transaction
stream.CommitL();
stream.Pop();
stream.Close();
// go back through & adjust compressed size values for each zip entry post-mortem
ASSERT(iFile.SubSessionHandle() == 0);
User::LeaveIfError(iFile.Open(iFs, iFileName, EFileWrite | EFileShareExclusive));
TInt pos = 0;
const TInt remainder = 8; // CZipArchive::KLocalHeaderFixedLength - KCompressedSizeRelativePosition
User::LeaveIfError(iFile.Seek(ESeekStart, pos));
iZipMemberLinkedListIter.SetToFirst();
entry = iZipMemberLinkedListIter++;
do
{
pos += KCompressedSizeRelativePosition; // step to iCompressedSize location in the current entry's LFH
TUint32 cs = entry->iLFH.iCompressedSize;
TUint32 KMyByte = 0x000000FF;
TInt bytecount = 0;
TBuf8<8> compSize;
do
{
TUint32 csChar((cs & KMyByte));
compSize.Append(csChar);
cs >>= 8;
} while (++bytecount < 4); // zip-file format allows us 4 bytes only for the compressed size value
User::LeaveIfError(iFile.Write(pos, compSize));
pos += bytecount; // the 4 bytes we just wrote
// step over remaing bytes to the beginning of the next entry's LFH
pos += remainder;
pos += entry->iAsciiName.Size();
pos += entry->iLFH.iCompressedSize;
// we're now positioned at the next entry's LFH
entry = iZipMemberLinkedListIter++;
} while (entry != NULL);
}
void CZipItUp::AppendLocalFileHeaderL(RFileWriteStream& aStream, CZipEntry& aZipEntry)
{
aStream.WriteUint32L(aZipEntry.iLFH.iSignature);
aStream.WriteUint16L(aZipEntry.iLFH.iVersionNeeded);
aStream.WriteUint16L(aZipEntry.iLFH.iFlags);
aStream.WriteUint16L(aZipEntry.iLFH.iCompressionMethod);
aStream.WriteUint16L(aZipEntry.iLFH.iLastModifiedFileTime);
aStream.WriteUint16L(aZipEntry.iLFH.iLastModifiedFileDate);
aStream.WriteUint32L(aZipEntry.iLFH.iCRC32);
aStream.WriteUint32L(aZipEntry.iLFH.iCompressedSize);
aStream.WriteUint32L(aZipEntry.iLFH.iUncompressedSize);
aStream.WriteUint16L(aZipEntry.iLFH.iFileNameLength);
aStream.WriteUint16L(aZipEntry.iLFH.iExtraFieldLength);
aStream.WriteL(aZipEntry.iAsciiName);
}
void CZipItUp::AppendCompressedDataL(RFileWriteStream& aStream, CZipEntry& aZipEntry)
{
// compress data & stream it to the zip archive (aStream)
RFileReadStream inStream;
inStream.Attach(aZipEntry.iInput); // note takes ownership of iInput subsession handle
CBufferManager* fb = CBufferManager::NewLC(inStream, aStream, aZipEntry.iLFH.iUncompressedSize, KDefaultZipBufferLength);
CEZCompressor* compressor = CEZCompressor::NewLC(*fb, CEZCompressor::EBestCompression, KWindowBits, CEZCompressor::EDefMemLevel, CEZCompressor::EDefaultStrategy);
while (compressor->DeflateL()){/* do nothing */}
// update the entry's LocalFileHeader and FileHeader
aZipEntry.iLFH.iCompressedSize = fb->TotalBytesOut();
aZipEntry.iFH.iCompressedSize = aZipEntry.iLFH.iCompressedSize;
// cleanup
CleanupStack::PopAndDestroy(2, fb); // compressor, fb
inStream.Close();
}
void CZipItUp::AppendCentralDirectoryFileHeaderL(RFileWriteStream& aStream, CZipEntry& aEntry)
{
aStream.WriteUint32L(aEntry.iFH.iSignature);
aStream.WriteUint16L(aEntry.iFH.iMadeBy);
aStream.WriteUint16L(aEntry.iFH.iRequired);
aStream.WriteUint16L(aEntry.iFH.iFlags);
aStream.WriteUint16L(aEntry.iFH.iCompressionMethod);
aStream.WriteUint16L(aEntry.iFH.iLastModifiedFileTime);
aStream.WriteUint16L(aEntry.iFH.iLastModifiedFileDate);
aStream.WriteUint32L(aEntry.iFH.iCRC32);
aStream.WriteUint32L(aEntry.iFH.iCompressedSize);
aStream.WriteUint32L(aEntry.iFH.iUncompressedSize);
aStream.WriteUint16L(aEntry.iFH.iFileNameLength);
aStream.WriteUint16L(aEntry.iFH.iExtraFieldLength);
aStream.WriteUint16L(aEntry.iFH.iFileCommentLength);
aStream.WriteUint16L(aEntry.iFH.iDiskNumberStart);
aStream.WriteUint16L(aEntry.iFH.iInternalFileAttributes);
aStream.WriteUint32L(aEntry.iFH.iExternalFileAttributes);
aStream.WriteUint32L(aEntry.iFH.iLocalHeaderOffset);
aStream.WriteL(aEntry.iAsciiName);
}
void CZipItUp::AppendCentralDirectoryTrailerL(RFileWriteStream& aStream)
{
aStream.WriteUint32L(iCDT.iSignature);
aStream.WriteUint16L(iCDT.iDiskNumber);
aStream.WriteUint16L(iCDT.iStartDiskNumber);
aStream.WriteUint16L(iCDT.iLocalEntryCount);
aStream.WriteUint16L(iCDT.iTotalEntryCount);
aStream.WriteUint32L(iCDT.iSize);
aStream.WriteUint32L(iCDT.iOffset);
aStream.WriteUint16L(iCDT.iCommentLength);
}