//////////////////////////////////////////////////////////////////////////////// // This source file is part of the ZipArchive library source distribution and // is Copyrighted 2000 - 2011 by Artpol Software - Tadeusz Dracz // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // For the licensing details refer to the License.txt file. // // Web Site: http://www.artpol-software.com //////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "DeflateCompressor.h" #include "zlib/deflate.h" namespace ZipArchiveLib { #ifndef DEF_MEM_LEVEL #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif #endif CDeflateCompressor::CDeflateCompressor(CZipStorage* pStorage) :CBaseLibCompressor(pStorage) { m_stream.zalloc = (zarch_alloc_func)_zipalloc; m_stream.zfree = (zarch_free_func)_zipfree; } void CDeflateCompressor::InitCompression(int iLevel, CZipFileHeader* pFile, CZipCryptograph* pCryptograph) { CZipCompressor::InitCompression(iLevel, pFile, pCryptograph); m_stream.avail_in = (zarch_uInt)0; m_stream.avail_out = (zarch_uInt)m_pBuffer.GetSize(); m_stream.next_out = (zarch_Bytef*)(char*)m_pBuffer; m_stream.total_in = 0; m_stream.total_out = 0; if (pFile->m_uMethod == methodDeflate) { SetOpaque(&m_stream.opaque, &m_options); int err = zarch_deflateInit2_(&m_stream, iLevel, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, ZLIB_VERSION, sizeof(zarch_z_stream)); CheckForError(err); } } void CDeflateCompressor::Compress(const void *pBuffer, DWORD uSize) { UpdateFileCrc(pBuffer, uSize); if (m_pFile->m_uMethod == methodDeflate) { m_stream.next_in = (zarch_Bytef*)pBuffer; m_stream.avail_in = uSize; while (m_stream.avail_in > 0) { if (m_stream.avail_out == 0) { FlushWriteBuffer(); m_stream.avail_out = m_pBuffer.GetSize(); m_stream.next_out = (zarch_Bytef*)(char*)m_pBuffer; } ZIP_ZLIB_TYPE uTotal = m_stream.total_out; CheckForError(zarch_deflate(&m_stream, Z_NO_FLUSH)); m_uComprLeft += m_stream.total_out - uTotal; } } else if (uSize > 0) { if (m_pCryptograph) { if (m_pBuffer.GetSize() < uSize) { m_pBuffer.Allocate(uSize); } memcpy(m_pBuffer, pBuffer, uSize); WriteBuffer(m_pBuffer, uSize); } else m_pStorage->Write(pBuffer, uSize, false); m_stream.total_in += uSize; m_stream.total_out += uSize; } } void CDeflateCompressor::FinishCompression(bool bAfterException) { m_stream.avail_in = 0; if (!bAfterException) { if (m_pFile->m_uMethod == methodDeflate) { int err; do { if (m_stream.avail_out == 0) { FlushWriteBuffer(); m_stream.avail_out = m_pBuffer.GetSize(); m_stream.next_out = (zarch_Bytef*)(char*)m_pBuffer; } ZIP_SIZE_TYPE uTotal = m_stream.total_out; err = zarch_deflate(&m_stream, Z_FINISH); m_uComprLeft += m_stream.total_out - uTotal; } while (err == Z_OK); if (err == Z_STREAM_END) err = Z_OK; CheckForError(err); if (m_uComprLeft > 0) FlushWriteBuffer(); CheckForError(zarch_deflateEnd(&m_stream)); } // it may be increased by the encrypted header size in CZipFileHeader::PrepareData m_pFile->m_uComprSize += m_stream.total_out; m_pFile->m_uUncomprSize = m_stream.total_in; } EmptyPtrList(); ReleaseBuffer(); } void CDeflateCompressor::InitDecompression(CZipFileHeader* pFile, CZipCryptograph* pCryptograph) { CBaseLibCompressor::InitDecompression(pFile, pCryptograph); if (m_pFile->m_uMethod == methodDeflate) { SetOpaque(&m_stream.opaque, &m_options); CheckForError(zarch_inflateInit2_(&m_stream, -MAX_WBITS, ZLIB_VERSION, sizeof(zarch_z_stream))); } m_stream.total_out = 0; m_stream.avail_in = 0; } DWORD CDeflateCompressor::Decompress(void *pBuffer, DWORD uSize) { if (m_bDecompressionDone) return 0; DWORD uRead = 0; if (m_pFile->m_uMethod == methodDeflate) { m_stream.next_out = (zarch_Bytef*)pBuffer; m_stream.avail_out = uSize > m_uUncomprLeft ? (DWORD)m_uUncomprLeft : uSize; // may happen when the file is 0 sized bool bForce = m_stream.avail_out == 0 && m_uComprLeft > 0; while (m_stream.avail_out > 0 || (bForce && m_uComprLeft > 0)) { if ((m_stream.avail_in == 0) && (m_uComprLeft >= 0)) // Also when there are zero bytes left { DWORD uToRead = FillBuffer(); m_stream.next_in = (zarch_Bytef*)(char*)m_pBuffer; m_stream.avail_in = uToRead; } ZIP_SIZE_TYPE uTotal = m_stream.total_out; zarch_Bytef* pOldBuf = m_stream.next_out; int ret = zarch_inflate(&m_stream, Z_SYNC_FLUSH); // will not exceed DWORD DWORD uToCopy = (DWORD)(m_stream.total_out - uTotal); UpdateCrc(pOldBuf, uToCopy); m_uUncomprLeft -= uToCopy; uRead += uToCopy; if (ret == Z_STREAM_END) { m_bDecompressionDone = true; return uRead; } else CheckForError(ret); } if (!uRead && m_options.m_bCheckLastBlock && uSize != 0) { if (zarch_inflate(&m_stream, Z_SYNC_FLUSH) != Z_STREAM_END) // there were no more bytes to read and there was no ending block, // otherwise the method would return earlier ThrowError(CZipException::badZipFile); } } else { if (m_uComprLeft < uSize) uRead = (DWORD)m_uComprLeft; else uRead = uSize; if (uRead > 0) { m_pStorage->Read(pBuffer, uRead, false); if (m_pCryptograph) m_pCryptograph->Decode((char*)pBuffer, uRead); UpdateCrc(pBuffer, uRead); m_uComprLeft -= uRead; m_uUncomprLeft -= uRead; m_stream.total_out += uRead; } } return uRead; } void CDeflateCompressor::FinishDecompression(bool bAfterException) { if (!bAfterException && m_pFile->m_uMethod == methodDeflate) zarch_inflateEnd(&m_stream); EmptyPtrList(); ReleaseBuffer(); } } // namespace