/** \brief Attempt to read an ZipEndOfCentralDirectory structure. * * This function tries to read an ZipEndOfCentralDirectory structure from the * specified buffer. This function expects a BackBuffer, which is used * because that is generally the fastest way to read the data (instead of * scanning the entire file). * * \note * If a read from the buffer fails, then an exception is raised. Since * we are reading from a buffer, it should not happen except if the * ZipEndOfCentralDirectory indicates that there is a comment and the comment * is not there or some characters are missing. * * \exception FileCollectionException * This exception is raised if the number of entries is not equal to * the total number of entries, as expected. * * \param[in] buf The buffer with the file data. * \param[in] pos The position at which we are expected to check. * * \return true if the ZipEndOfCentralDirectory was found, false otherwise. */ bool ZipEndOfCentralDirectory::read(::zipios::buffer_t const& buf, size_t pos) { // the number of bytes we are going to read in the buffer // (including the signature) ssize_t const HEADER_SIZE(static_cast<ssize_t>(sizeof(uint32_t) * 3 + sizeof(uint16_t) * 5)); // enough data in the buffer? // // Note: this quick check assumes a 0 length comment which is possible; // if there is a comment and we find the signature too early, then // it will throw // if(static_cast<ssize_t>(buf.size() - pos) < HEADER_SIZE) { return false; } // first read and check the signature uint32_t signature; zipRead(buf, pos, signature); // 32 if(signature != g_signature) { return false; } // next we read the other parameters uint16_t disk_number; uint16_t central_directory_entries; uint16_t central_directory_total_entries; uint32_t central_directory_size; uint32_t central_directory_offset; uint16_t comment_len; zipRead(buf, pos, disk_number); // 16 zipRead(buf, pos, disk_number); // 16 zipRead(buf, pos, central_directory_entries); // 16 zipRead(buf, pos, central_directory_total_entries); // 16 zipRead(buf, pos, central_directory_size); // 32 zipRead(buf, pos, central_directory_offset); // 32 zipRead(buf, pos, comment_len); // 16 zipRead(buf, pos, m_zip_comment, comment_len); // string // note that if disk_number is defined, then these following two // numbers should differ too if(central_directory_entries != central_directory_total_entries) { throw FileCollectionException("ZipEndOfCentralDirectory with a number of entries and total entries that differ is not supported, spanned zip files are not supported"); } m_central_directory_entries = central_directory_entries; m_central_directory_size = central_directory_size; m_central_directory_offset = central_directory_offset; return true; }
/** \brief Open a zip archive that was previously appened to another file. * * Opens a Zip archive embedded in another file, by writing the zip * archive to the end of the file followed by the start offset of * the zip file on 4 bytes. The offset must be written in zip-file * byte-order (little endian). * * The program appendzip, which is part of the Zipios++ distribution can * be used to append a Zip archive to a file, e.g. a binary program. * * The function may throw various exception if the named file does not * seem to include a valid zip archive attached. * * \note * Only one file can be appended and opened in this way. Although * the appendzip tool can be used to append any number of files, * only the last one is accessible. * * \return A ZipFile that one can use to read compressed data. */ ZipFile::pointer_t ZipFile::openEmbeddedZipFile(std::string const& name) { // open zipfile, read 4 last bytes close file // create ZipFile object. uint32_t start_offset; { std::ifstream ifs(name, std::ios::in | std::ios::binary); ifs.seekg(-4, std::ios::end); zipRead(ifs, start_offset); // TODO: add support for 64 bit (files of more than 4Gb) } return ZipFile::pointer_t(new ZipFile(name, start_offset, 4)); }
BinaryInput::BinaryInput( const std::string& filename, G3DEndian fileEndian, bool compressed) : m_filename(filename), m_bitPos(0), m_bitString(0), m_beginEndBits(0), m_alreadyRead(0), m_length(0), m_bufferLength(0), m_buffer(NULL), m_pos(0), m_freeBuffer(true) { setEndian(fileEndian); // Update global file tracker _internal::currentFilesUsed.insert(m_filename); if (! fileExists(m_filename, false)) { std::string zipfile; std::string internalfile; if (zipfileExists(m_filename, zipfile, internalfile)) { // Load from zipfile void* v; size_t s; zipRead(filename, v, s); m_buffer = reinterpret_cast<uint8*>(v); m_bufferLength = m_length = s; if (compressed) { decompress(); } m_freeBuffer = true; } else { Log::common()->printf("Warning: File not found: %s\n", m_filename.c_str()); } return; } // Figure out how big the file is and verify that it exists. m_length = fileLength(m_filename); // Read the file into memory FILE* file = fopen(m_filename.c_str(), "rb"); if (! file || (m_length == -1)) { throw format("File not found: \"%s\"", m_filename.c_str()); return; } if (! compressed && (m_length > INITIAL_BUFFER_LENGTH)) { // Read only a subset of the file so we don't consume // all available memory. m_bufferLength = INITIAL_BUFFER_LENGTH; } else { // Either the length is fine or the file is compressed // and requires us to read the whole thing for zlib. m_bufferLength = m_length; } debugAssert(m_freeBuffer); m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16); if (m_buffer == NULL) { if (compressed) { throw "Not enough memory to load compressed file. (1)"; } // Try to allocate a small array; not much memory is available. // Give up if we can't allocate even 1k. while ((m_buffer == NULL) && (m_bufferLength > 1024)) { m_bufferLength /= 2; m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16); } } debugAssert(m_buffer); fread(m_buffer, m_bufferLength, sizeof(int8), file); fclose(file); file = NULL; if (compressed) { if (m_bufferLength != m_length) { throw "Not enough memory to load compressed file. (2)"; } decompress(); } }