#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_STORAGE_SOURCE 1 #include "ZipDirectoryReader.h" #include "ZippedContainer.h" #include "ZippedFile.h" #include "ZipReader.h" #include "../../Helpers/StringHelper.h" #include "Nuclex/Storage/Binary/BinaryBlobReader.h" #include "Format/LocalFileHeaderSerializer.h" #include "Format/GeneralPurposeFlags.h" #include "Format/DataDescriptorSerializer.h" namespace Nuclex { namespace Storage { namespace FileSystem { namespace Zip { // ------------------------------------------------------------------------------------------- // ZipDirectoryReader::ZipDirectoryReader( const std::string &nativeArchivePath, const std::shared_ptr &archive ) : nativeArchivePath(nativeArchivePath), archive(archive) {} // ------------------------------------------------------------------------------------------- // ZipDirectoryReader::~ZipDirectoryReader() {} // ------------------------------------------------------------------------------------------- // void ZipDirectoryReader::ReadDirectory() { this->zipReader.reset(new ZipReader(this->archive)); // TODO: Pass through ContainerFileCodec to allow nested archives std::string nativePath = this->nativeArchivePath + "#"; std::shared_ptr createdContainer( new ZippedContainer(std::shared_ptr(), nativePath, "/") ); this->containers.insert(std::make_pair(std::string(), createdContainer)); scanLocalFileHeaders(); } // ------------------------------------------------------------------------------------------- // const std::shared_ptr &ZipDirectoryReader::GetRootContainer() const { ZippedContainerMap::const_iterator iterator = this->containers.find(std::string()); if(iterator == this->containers.end()) { throw std::runtime_error("No zip archive directory has been successfully read so far"); } else { return iterator->second; } } // ------------------------------------------------------------------------------------------- // void ZipDirectoryReader::scanLocalFileHeaders() { Nuclex::Storage::Binary::BinaryBlobReader reader(this->archive); reader.SetLittleEndian(); // Scan the local file headers and collect a directory of all files in the zip archive std::uint32_t signature; for(;;) { // If the next thing we find is not the local file header signature, we have progressed // beyond the files and reached the archive's central directory. reader.Read(signature); if(signature != LocalFileHeaderSerializer::Signature) { break; } // Each file begins with the local file header LocalFileHeader localFileHeader; LocalFileHeaderSerializer::Load(localFileHeader, reader, true); // Next follow the compressed contents of the file std::uint64_t compressedDataPosition = reader.GetPosition(); reader.SetPosition(compressedDataPosition + localFileHeader.CompressedSize); // Then the data descriptor may be appended if(localFileHeader.Flags & GeneralPurposeFlags::DataDescriptorPresent) { DataDescriptor dataDescriptor; DataDescriptorSerializer::Load(dataDescriptor, reader); } const std::string &filename = localFileHeader.FileName; if(isDirectory(filename)) { continue; } processLocalFileHeader(localFileHeader, compressedDataPosition); } } // ------------------------------------------------------------------------------------------- // void ZipDirectoryReader::processLocalFileHeader( const LocalFileHeader &localFileHeader, std::uint64_t compressedDataPosition ) { const std::shared_ptr &parentContainer = getOrCreateContainerForDirectory( getParentDirectory(localFileHeader.FileName) ); ZippedFile::MetaData metaData; metaData.Filename = getDirectoryName(localFileHeader.FileName); metaData.NativePath = this->nativeArchivePath + '#' + localFileHeader.FileName; metaData.CompressedDataOffset = compressedDataPosition; metaData.CompressedSize = localFileHeader.CompressedSize; metaData.UncompressedSize = localFileHeader.UncompressedSize; metaData.CompressionMethod = localFileHeader.CompressionMethod; parentContainer->AddFile( std::make_shared(this->zipReader, metaData) ); } // ------------------------------------------------------------------------------------------- // const std::shared_ptr &ZipDirectoryReader::getOrCreateContainerForDirectory( const std::string &directory ) { ZippedContainerMap::iterator iterator = this->containers.find(directory); if(iterator == this->containers.end()) { const std::shared_ptr &parent = getOrCreateContainerForDirectory( getParentDirectory(directory) ); std::string nativePath = this->nativeArchivePath + "#" + directory; std::string name = getDirectoryName(directory); std::shared_ptr createdContainer = std::make_shared( std::shared_ptr(), nativePath, name ); parent->AddContainer(createdContainer); iterator = this->containers.insert(std::make_pair(directory, createdContainer)).first; } return iterator->second; } // ------------------------------------------------------------------------------------------- // std::string ZipDirectoryReader::getParentDirectory(const std::string &directory) { std::size_t lastSlashIndex = directory.rfind('/'); if(lastSlashIndex == std::string::npos) { return std::string(); } else { return directory.substr(0, lastSlashIndex); } } // ------------------------------------------------------------------------------------------- // std::string ZipDirectoryReader::getDirectoryName(const std::string &path) { std::size_t lastSlashIndex = path.rfind('/'); if(lastSlashIndex == std::string::npos) { return path; } else { return path.substr(lastSlashIndex + 1); } } // ------------------------------------------------------------------------------------------- // bool ZipDirectoryReader::isDirectory(const std::string &path) { if(path.empty()) { return false; } char finalCharacter = path[path.length() - 1]; return (finalCharacter == '/'); } // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Storage::FileSystem::Zip