diff -r 000000000000 -r 7f656887cf89 commands/fzip/fzipup.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commands/fzip/fzipup.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,554 @@ +// 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 +#include +#include +#include +#include +#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 ':\' + } + if (file.HasLeadingSlash()) + { + file.Delete(0, 1); // remove '\' + } + iAsciiName.Copy(file); + TUint8* ptr = const_cast (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 ':\' + } + if (file.HasLeadingSlash()) + { + file.Delete(0, 1); // remove '\' + } + TBuf8 ascii; + ascii.Copy(file); + TUint8* ptr = const_cast (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); + }