//  // // ##### #### # # -= Zipex Library =-  // //  ## # # ## ## ZippedFile.cpp - File stored in a zip archive  // //  ## #### ###  // //  ## # ### Controls access to a file that's stored within a zip archive  // // ## # ## ##  // // ##### # # # R1 (C)2003-2004 Markus Ewald -> License.txt  // //  // #include "zipex/zippedfile.h" #include "zipex/exception.h" #include "zipdata.h" using namespace Zipex; using namespace std; namespace { // Number of bytes to read at a minimum const size_t ReadChunkSize = 4096; } // ############################################################################################# // // # ZipCheck() # // // ############################################################################################# // /** Translates ZLib return codes into exceptions @param nResult Code returned by a zlib function */ inline static void ZipCheck(int nResult) { switch(nResult) { case Z_BUF_ERROR: throw InternalError("Internal buffering error"); case Z_MEM_ERROR: throw bad_alloc("ZLib ran out of memory"); case Z_DATA_ERROR: throw ArchiveCorruptedError("ZIP file has been corrupted"); case Z_STREAM_ERROR: throw InternalError("Internal parameter error"); case Z_OK: case Z_STREAM_END: break; case Z_NEED_DICT: throw UnsupportedFormatError("Fixed dictionary zips not supported"); default: throw InternalError("Unknown result code returned by ZLib"); } } // ############################################################################################# // // # Zipex::ZippedFile::ZippedFile() Constructor # // // ############################################################################################# // /** Initializes an instance of ZippedFile @param pStream Stream in which the zip archive resides @param nOffset The file data's offset within the zip archive @param eCompressionMethod Compression method used for the file @param sFilename File name of the zipped file @param sComment Comment for this file @param sExtraField User defined data associated with this file @param nCRC32 The crc32 of the file's data @param nCompressedSize Size of file in compressed form @param nUncompressedSize Size of file in uncompressed form @param nLastModifyTime Time and date of the last modification to the file @param nInternalAttributes Internal file attributes @param nExternalAttributes External file attributes */ ZippedFile::ZippedFile(Stream *pStream, size_t nOffset, CompressionMethod eCompressionMethod, const std::string &sFilename, const std::string &sComment, const std::string &sExtraField, unsigned long nCRC32, size_t nCompressedSize, size_t nUncompressedSize, unsigned long nLastModifyTime, unsigned long nInternalAttributes, unsigned long nExternalAttributes) : m_sFilename(sFilename), m_sComment(sComment), m_sExtraField(sExtraField), m_nCRC32(nCRC32), m_nCompressedSize(nCompressedSize), m_nUncompressedSize(nUncompressedSize), m_nLastModifyTime(nLastModifyTime), m_nInternalAttributes(nInternalAttributes), m_nExternalAttributes(nExternalAttributes), m_nPosition(0), m_pStream(pStream), m_nOffset(nOffset), m_bDecompressorInitialized(false), m_nCompressedPosition(0), m_eCompressionMethod(eCompressionMethod) { // Select the extractor matching the zipped file's compression method switch(eCompressionMethod) { // The file is stored uncompressed case CM_STORE: extractTo = &ZippedFile::destoreTo; break; // The file was compressed using the deflate method case CM_DEFLATE: extractTo = &ZippedFile::inflateTo; break; // The file is stored in a unknown way default: extractTo = &ZippedFile::unsupported; break; } } // ############################################################################################# // // # Zipex::ZippedFile::~ZippedFile() Destructor # // // ############################################################################################# // /** Destroys an instance of ZippedFile */ ZippedFile::~ZippedFile() { flush(); } // ############################################################################################# // // # Zipex::ZippedFile::precacheAll() # // // ############################################################################################# // /** Flushes the intenal memory buffers, freeing all memory used for decompression. */ const void *ZippedFile::precacheAll() { (this->*extractTo)(m_nUncompressedSize); if(m_nUncompressedSize > 0) return &m_Data[0]; else return NULL; } // ############################################################################################# // // # Zipex::ZippedFile::flush() # // // ############################################################################################# // /** Flushes the intenal memory buffers, freeing all memory used for decompression. */ void ZippedFile::flush() { if(m_bDecompressorInitialized) { inflateEnd(&m_ZLibStream); m_Data.clear(); m_DecompressionBuffer.clear(); m_nCompressedPosition = 0; } } // ############################################################################################# // // # Zipex::ZippedFile::seekTo() # // // ############################################################################################# // /** Sets the file seek pointer to the specified position @param nPosition Position to seek to */ void ZippedFile::seekTo(size_t nPosition) { m_nPosition = nPosition; } // ############################################################################################# // // # Zipex::ZippedFile::getLocation() # // // ############################################################################################# // /** Retrieves the current file pointer location @return The file pointer's current location */ size_t ZippedFile::getLocation() const { return m_nPosition; } // ############################################################################################# // // # Zipex::ZippedFile::readData() # // // ############################################################################################# // /** Reads data from the file @param pBuffer Buffer into which to read @param nLength Number of bytes to read */ size_t ZippedFile::readData(void *pBuffer, size_t nLength) { nLength = min(m_nUncompressedSize - m_nPosition, nLength); readDataAt(m_nPosition, pBuffer, nLength); m_nPosition += nLength; return nLength; } // ############################################################################################# // // # Zipex::ZippedFile::writeData() # // // ############################################################################################# // /** Writes data into the file @param pBuffer Buffer containing the data to be written @param nLength Number of bytes to write @todo Implement zip file writing */ void ZippedFile::writeData(const void *pBuffer, size_t nLength) { writeDataAt(m_nPosition, pBuffer, nLength); m_nPosition += nLength; } // ############################################################################################# // // # Zipex::ZippedFile::readDataAt() # // // ############################################################################################# // /** Reads data from the file @param nPosition Position at which to read @param pBuffer Buffer into which to read @param nLength Number of bytes to read */ size_t ZippedFile::readDataAt(size_t nPosition, void *pBuffer, size_t nLength) { nLength = min(m_nUncompressedSize - nPosition, nLength); (this->*extractTo)(nPosition + nLength); memcpy(pBuffer, &m_Data[nPosition], nLength); return nLength; } // ############################################################################################# // // # Zipex::ZippedFile::writeDataAt() # // // ############################################################################################# // /** Writes data into the file @param nPosition Position at which to write @param pBuffer Buffer containing the data to be written @param nLength Number of bytes to write @todo Implement zip file writing */ void ZippedFile::writeDataAt(size_t nPosition, const void *pBuffer, size_t nLength) { throw std::exception("Writing to zip files not supported yet"); } // ############################################################################################# // // # Zipex::ZippedFile::inflateTo() # // // ############################################################################################# // /** Ensures that the specified number of bytes are available in uncompressed form. @param nBytes Number of bytes that are required to be uncompressed */ void ZippedFile::inflateTo(size_t nBytes) { size_t nDecompressedBytes = m_Data.size(); // If the required number of bytes is already available then do nothing if(nDecompressedBytes >= nBytes) return; // Calculate the number of uncompressed bytes we will need // Round up to the next 4K multiple, but not beyond the file's size nBytes = min(nBytes + ReadChunkSize - (nBytes % ReadChunkSize), m_nUncompressedSize); // If this is the first time we're called, initialize zlib's decompressor if(!m_bDecompressorInitialized) { size_t nBytesToDecompress = min(nBytes, m_nCompressedSize); // Read the required amount of bytes, possibly including the ZLib header (?) m_DecompressionBuffer.resize(nBytesToDecompress); m_pStream->readDataAt(m_nOffset, &m_DecompressionBuffer[0], nBytesToDecompress); m_nCompressedPosition = nBytesToDecompress; // Initialize the decompressor m_ZLibStream.next_in = &m_DecompressionBuffer[0]; m_ZLibStream.avail_in = static_cast(nBytesToDecompress); // Check ZLib 64 bit safety m_ZLibStream.zalloc = NULL; m_ZLibStream.zfree = NULL; ZipCheck(inflateInit2(&m_ZLibStream, -MAX_WBITS)); m_bDecompressorInitialized = true; } // Grow the file data buffer to the required size m_Data.resize(nBytes); m_ZLibStream.next_out = &m_Data[nDecompressedBytes]; m_ZLibStream.avail_out = static_cast(nBytes - nDecompressedBytes); // Check ZLib 64 bit safety for(;;) { // Extract the available input data and break if sufficient output was generated ZipCheck(inflate(&m_ZLibStream, Z_SYNC_FLUSH)); if(!m_ZLibStream.avail_out) break; // Calculate the number of bytes to be decompressed size_t nBytesToDecompress = m_ZLibStream.avail_out; nBytesToDecompress = nBytes + ReadChunkSize - (nBytes % ReadChunkSize); nBytesToDecompress = min(nBytesToDecompress, m_nCompressedSize - m_nCompressedPosition); // Refill the decompression buffer m_DecompressionBuffer.assign(m_DecompressionBuffer.end() - m_ZLibStream.avail_in, m_DecompressionBuffer.end()); m_DecompressionBuffer.resize(nBytesToDecompress + m_ZLibStream.avail_in); m_pStream->readDataAt(m_nOffset + m_nCompressedPosition, &m_DecompressionBuffer[m_ZLibStream.avail_in], nBytesToDecompress); m_nCompressedPosition += nBytesToDecompress; m_ZLibStream.next_in = &m_DecompressionBuffer[0]; m_ZLibStream.avail_in = static_cast(m_DecompressionBuffer.size()); // Check ZLib 64 bit safety } } // ############################################################################################# // // # Zipex::ZippedFile::destoreTo() # // // ############################################################################################# // /** Ensures that the specified number of bytes are available in uncompressed form. @param nBytes Number of bytes that are required to be uncompressed */ void ZippedFile::destoreTo(size_t nBytes) { size_t nDecompressedBytes = m_Data.size(); // If the required number of bytes is already available then do nothing if(nDecompressedBytes >= nBytes) return; // Calculate the number of uncompressed bytes we will need // Round up to the next 4K multiple, but not beyond the file's size nBytes = min(nBytes + ReadChunkSize - (nBytes % ReadChunkSize), m_nUncompressedSize); m_Data.resize(nBytes); m_pStream->readDataAt( m_nOffset + nDecompressedBytes, &m_Data[nDecompressedBytes], nBytes - nDecompressedBytes ); } // ############################################################################################# // // # Zipex::ZippedFile::unsupported() # // // ############################################################################################# // /** Decompression method for unsupported compression formats. Throws an exception. */ void ZippedFile::unsupported(size_t) { throw UnsupportedFormatError("Compression method not supported"); }