//  // // ##### #### # # -= Zipex Library =-  // //  ## # # ## ## ZipArchive.cpp - Zip archive  // //  ## #### ###  // //  ## # ### Represents a zip archive and manages its files  // // ## # ## ##  // // ##### # # # R1 (C)2003-2004 Markus Ewald -> License.txt  // //  // #include "zipex/ziparchive.h" #include "zipex/stream.h" #include #include #include #include "zipdata.h" using namespace Zipex; using namespace Detail; using namespace std; //  // //  Zipex::FileStream  // //  // /// Plain file stream /** A stream which accesses a simple file on the harddisk */ class FileStream : public Stream { public: /// Constructor /** Initializes a FileStream @param sFilename File to open @param bReadOnly Whether to open the file in read-only mode */ FileStream(const string &sFilename, bool bReadOnly = false) : m_Stream(sFilename.c_str(), ios::binary | (bReadOnly ? ios::in : (ios::in | ios::out))) { if(!m_Stream.is_open()) throw FileOpenError(string("Could not open file '") + sFilename + "'"); fstream::pos_type nOld = m_Stream.tellg(); m_Stream.seekg(0, ios_base::end); m_nLength = m_Stream.tellg(); m_Stream.seekg(nOld, ios_base::beg); } /// Destructor virtual ~FileStream() {} public: /// Get stream size /** @return The length of the stream in bytes */ size_t getSize() const { return m_nLength; } /// Seek to position /** @param nPosition New location within the file */ void seekTo(size_t nPosition) { m_Stream.seekg(static_cast(nPosition), ios_base::beg); } /// Get current location /** @return The current location within the file */ size_t getLocation() const { return m_Stream.tellg(); } /// Read data into buffer /** @param pBuffer Buffer to read into @param nLength Number of bytes to read @return The number of bytes actually read */ size_t readData(void *pBuffer, size_t nLength) { m_Stream.read(static_cast(pBuffer), static_cast(nLength)); return nLength; } /// Write data from buffer /** @param pBuffer Buffer to write @param nLength Number of bytes to write */ void writeData(const void *pBuffer, size_t nLength) { m_Stream.write(static_cast(pBuffer), static_cast(nLength)); } private: /// The C++ iostreams file stream mutable fstream m_Stream; /// The file's size size_t m_nLength; }; // ############################################################################################# // // # Zipex::ZipArchive::ZipArchive() Constructor # // // ############################################################################################# // /** Initializes an instance of ZipArchive, opening the zip archive with the given file name. @param sFilename File name of the zip archive to open on disk @param sPassword Currently not supported */ ZipArchive::ZipArchive(const string &sFilename, const string &sPassword) : m_sPassword(sPassword), m_pStream(new FileStream(sFilename, true)), m_bDeleteStream(true) { readZipDirectory(); } // ############################################################################################# // // # Zipex::ZipArchive::ZipArchive() Constructor # // // ############################################################################################# // /** Initializes an instance of ZipArchive, opening the zip archive within the specified stream. @param pStream Stream containing the zip archive @param sPassword Currently not supported */ ZipArchive::ZipArchive(Stream *pStream, const string &sPassword) : m_sPassword(sPassword), m_pStream(pStream), m_bDeleteStream(false) { readZipDirectory(); } // ############################################################################################# // // # Zipex::ZipArchive::~ZipArchive() Destructor # // // ############################################################################################# // /** Destroys an instance of ZipArchive */ ZipArchive::~ZipArchive() { if(m_bDeleteStream) delete m_pStream; } // ############################################################################################# // // # Zipex::ZipArchive::getFile() # // // ############################################################################################# // /** Access a file in the zip archive. Zipex does not build a tree from the paths of the zipped files, so to access files in a zipped directory tree, you have to specify a filename like "data/stuff/myfile.dat". If the specified file can not be found, a runtime_error is thrown. @param sFilename File name of the file to access @return The requested file */ ZippedFile &ZipArchive::getFile(const string &sFilename) { FileMap::iterator FileIt = m_Files.find(sFilename); if(FileIt == m_Files.end()) throw FileOpenError(string("Zipped file '") + sFilename + "' doesn't exist"); else return *FileIt->second; } // ############################################################################################# // // # Zipex::ZipArchive::getFile() # // // ############################################################################################# // /** Access a file in the zip archive. Zipex does not build a tree from the paths of the zipped files, so to access files in a zipped directory tree, you have to specify a filename like "data/stuff/myfile.dat". If the specified file can not be found, a runtime_error is thrown. @param sFilename File name of the file to access @return The requested file */ const ZippedFile &ZipArchive::getFile(const string &sFilename) const { FileMap::const_iterator FileIt = m_Files.find(sFilename); if(FileIt == m_Files.end()) throw FileOpenError(string("Zipped file '") + sFilename + "' doesn't exist"); else return *FileIt->second; } // ############################################################################################# // // # Zipex::ZipArchive::readZipDirectory() # // // ############################################################################################# // /** Reads the zip archive's directory and creates a ZippedFile instance for each file found in the zip archive */ void ZipArchive::readZipDirectory() { typedef map OffsetMap; OffsetMap FileOffsets; FileMap Files; unsigned long nSignature; // If the first byte doesn't match a local file header signature then // we're probably not dealing with a zip file. m_pStream->read(nSignature); if(nSignature != LocalFileHeader::SIGNATURE) throw ArchiveCorruptedError("This is not a zip file"); // Read all local file headers until the central directory is found do { LocalFileHeader lfh; // Read the local file header and skip the compressed data lfh.loadFromStream(*m_pStream, false); size_t nDataStart = m_pStream->getLocation(); if(lfh.nFlags & 4) throw UnsupportedFormatError("Seek-free zip archives not supported"); // Here we've got a 1 in 2 billion chance to fail. Thanks pkware! m_pStream->seekTo(nDataStart + lfh.nCompressedSize); m_pStream->read(nSignature); // If the next long doesn't match a signature, we assume a // DataDescriptor has been appended to the header if((nSignature != LocalFileHeader::SIGNATURE) && (nSignature != FileHeader::SIGNATURE)) { lfh.nCRC32 = nSignature; m_pStream->read(lfh.nCompressedSize); m_pStream->read(lfh.nUncompressedSize); m_pStream->read(nSignature); } // Remember the data start offset FileOffsets.insert(OffsetMap::value_type(lfh.sFilename, nDataStart)); } while(nSignature != FileHeader::SIGNATURE); // Read the central directory and collect all files in our list do { FileHeader fh; // Read the current file header from the directory fh.loadFromStream(*m_pStream, false); // Look up the compressed file's offset we previously stored OffsetMap::iterator FHIt = FileOffsets.find(fh.sFilename); // If all required informations are complete, add the zipped file to our list if(FHIt != FileOffsets.end()) Files.insert(FileMap::value_type( fh.sFilename, new ZippedFile( m_pStream, FHIt->second, static_cast(fh.nCompressionMethod), fh.sFilename, fh.sComment, fh.sExtraField, fh.nCRC32, fh.nCompressedSize, fh.nUncompressedSize, static_cast(fh.nLastModifyDate) << 16 | fh.nLastModifyTime, fh.nInternalAttributes, fh.nExternalAttributes ) )); // Break on eof if(m_pStream->getLocation() >= m_pStream->getSize()) break; // Read the next 4 bytes to see if another file's signature follows m_pStream->read(nSignature); } while(nSignature == FileHeader::SIGNATURE); // Everything went well, take the new arguments m_Files.swap(Files); } // ############################################################################################# // // # Zipex::ZipArchive::enumFiles() # // // ############################################################################################# // /** Returns a new enumerator over all files in the archive. @return An enumerator over all files in the archive */ ZipArchive::FileEnumerator ZipArchive::enumFiles() { return FileEnumerator(m_Files); } // ############################################################################################# // // # Zipex::ZipArchive::FileEnumerator::get() # // // ############################################################################################# // /** Returns the current zipped file @return The current zipped file */ ZippedFile &ZipArchive::FileEnumerator::get() { if(m_FileIt == m_FileEnd) throw std::out_of_range("The enumerator is in the empty cycle position"); return *m_FileIt->second; } // ############################################################################################# // // # Zipex::ZipArchive::FileEnumerator::next() # // // ############################################################################################# // /** Advances to the next zipped file @return True until the end is reached */ bool ZipArchive::FileEnumerator::next() { if(m_FileIt == m_FileEnd) m_FileIt = m_Files.begin(); else ++m_FileIt; if(m_FileIt != m_FileEnd) return true; else return false; }