/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined( INCLUDED_PKZIP_H ) #define INCLUDED_PKZIP_H #include "bytestreamutils.h" #include "idatastream.h" #include class zip_magic { public: bool operator==( const zip_magic& other ) const { return m_value[0] == other.m_value[0] && m_value[1] == other.m_value[1] && m_value[2] == other.m_value[2] && m_value[3] == other.m_value[3]; } bool operator!=( const zip_magic& other ) const { return !( *this == other ); } char m_value[4]; }; inline void istream_read_zip_magic( InputStream& istream, zip_magic& magic ){ istream.read( reinterpret_cast( magic.m_value ), 4 ); } struct zip_version { char version; char ostype; }; inline void istream_read_zip_version( InputStream& istream, zip_version& version ){ version.version = istream_read_byte( istream ); version.ostype = istream_read_byte( istream ); } struct zip_dostime { unsigned short time; unsigned short date; }; inline void istream_read_zip_dostime( InputStream& istream, zip_dostime& dostime ){ dostime.time = istream_read_int16_le( istream ); dostime.date = istream_read_int16_le( istream ); } const zip_magic zip_file_header_magic = { { 'P', 'K', 0x03, 0x04 } }; /* A. Local file header */ struct zip_file_header { zip_magic z_magic; /* local file header signature (0x04034b50) */ zip_version z_extract; /* version needed to extract */ unsigned short z_flags; /* general purpose bit flag */ unsigned short z_compr; /* compression method */ zip_dostime z_dostime; /* last mod file time (dos format) */ unsigned int z_crc32; /* crc-32 */ unsigned int z_csize; /* compressed size */ unsigned int z_usize; /* uncompressed size */ unsigned short z_namlen; /* filename length (null if stdin) */ unsigned short z_extras; /* extra field length */ /* followed by filename (of variable size) */ /* followed by extra field (of variable size) */ }; inline void istream_read_zip_file_header( SeekableInputStream& istream, zip_file_header& file_header ){ istream_read_zip_magic( istream, file_header.z_magic ); istream_read_zip_version( istream, file_header.z_extract ); file_header.z_flags = istream_read_uint16_le( istream ); file_header.z_compr = istream_read_uint16_le( istream ); istream_read_zip_dostime( istream, file_header.z_dostime ); file_header.z_crc32 = istream_read_uint32_le( istream ); file_header.z_csize = istream_read_uint32_le( istream ); file_header.z_usize = istream_read_uint32_le( istream ); file_header.z_namlen = istream_read_uint16_le( istream ); file_header.z_extras = istream_read_uint16_le( istream ); istream.seek( file_header.z_namlen + file_header.z_extras, SeekableInputStream::cur ); }; /* B. data descriptor * the data descriptor exists only if bit 3 of z_flags is set. It is byte aligned * and immediately follows the last byte of compressed data. It is only used if * the output media of the compressor was not seekable, eg. standard output. */ const zip_magic zip_file_trailer_magic = { { 'P', 'K', 0x07, 0x08} }; struct zip_file_trailer { zip_magic z_magic; unsigned int z_crc32; /* crc-32 */ unsigned int z_csize; /* compressed size */ unsigned int z_usize; /* uncompressed size */ }; inline void istream_read_zip_file_trailer( InputStream& istream, zip_file_trailer& file_trailer ){ istream_read_zip_magic( istream, file_trailer.z_magic ); file_trailer.z_crc32 = istream_read_uint32_le( istream ); file_trailer.z_csize = istream_read_uint32_le( istream ); file_trailer.z_usize = istream_read_uint32_le( istream ); }; /* C. central directory structure: [file header] . . . end of central dir record */ /* directory file header * - a single entry including filename, extras and comment may not exceed 64k. */ const zip_magic zip_root_dirent_magic = { { 'P', 'K', 0x01, 0x02 } }; struct zip_root_dirent { zip_magic z_magic; zip_version z_encoder; /* version made by */ zip_version z_extract; /* version need to extract */ unsigned short z_flags; /* general purpose bit flag */ unsigned short z_compr; /* compression method */ zip_dostime z_dostime; /* last mod file time&date (dos format) */ unsigned int z_crc32; /* crc-32 */ unsigned int z_csize; /* compressed size */ unsigned int z_usize; /* uncompressed size */ unsigned short z_namlen; /* filename length (null if stdin) */ unsigned short z_extras; /* extra field length */ unsigned short z_comment; /* file comment length */ unsigned short z_diskstart; /* disk number of start (if spanning zip over multiple disks) */ unsigned short z_filetype; /* internal file attributes, bit0 = ascii */ unsigned int z_filemode; /* extrnal file attributes, eg. msdos attrib byte */ unsigned int z_off; /* relative offset of local file header, seekval if singledisk */ /* followed by filename (of variable size) */ /* followed by extra field (of variable size) */ /* followed by file comment (of variable size) */ }; inline void istream_read_zip_root_dirent( SeekableInputStream& istream, zip_root_dirent& root_dirent ){ istream_read_zip_magic( istream, root_dirent.z_magic ); istream_read_zip_version( istream, root_dirent.z_encoder ); istream_read_zip_version( istream, root_dirent.z_extract ); root_dirent.z_flags = istream_read_uint16_le( istream ); root_dirent.z_compr = istream_read_uint16_le( istream ); istream_read_zip_dostime( istream, root_dirent.z_dostime ); root_dirent.z_crc32 = istream_read_uint32_le( istream ); root_dirent.z_csize = istream_read_uint32_le( istream ); root_dirent.z_usize = istream_read_uint32_le( istream ); root_dirent.z_namlen = istream_read_uint16_le( istream ); root_dirent.z_extras = istream_read_uint16_le( istream ); root_dirent.z_comment = istream_read_uint16_le( istream ); root_dirent.z_diskstart = istream_read_uint16_le( istream ); root_dirent.z_filetype = istream_read_uint16_le( istream ); root_dirent.z_filemode = istream_read_uint32_le( istream ); root_dirent.z_off = istream_read_uint32_le( istream ); istream.seek( root_dirent.z_namlen + root_dirent.z_extras + root_dirent.z_comment, SeekableInputStream::cur ); } /* end of central dir record */ const zip_magic zip_disk_trailer_magic = { { 'P', 'K', 0x05, 0x06 } }; const unsigned int disk_trailer_length = 22; struct zip_disk_trailer { zip_magic z_magic; unsigned short z_disk; /* number of this disk */ unsigned short z_finaldisk; /* number of the disk with the start of the central dir */ unsigned short z_entries; /* total number of entries in the central dir on this disk */ unsigned short z_finalentries; /* total number of entries in the central dir */ unsigned int z_rootsize; /* size of the central directory */ unsigned int z_rootseek; /* offset of start of central directory with respect to * * the starting disk number */ unsigned short z_comment; /* zipfile comment length */ /* followed by zipfile comment (of variable size) */ }; inline void istream_read_zip_disk_trailer( SeekableInputStream& istream, zip_disk_trailer& disk_trailer ){ istream_read_zip_magic( istream, disk_trailer.z_magic ); disk_trailer.z_disk = istream_read_uint16_le( istream ); disk_trailer.z_finaldisk = istream_read_uint16_le( istream ); disk_trailer.z_entries = istream_read_uint16_le( istream ); disk_trailer.z_finalentries = istream_read_uint16_le( istream ); disk_trailer.z_rootsize = istream_read_uint32_le( istream ); disk_trailer.z_rootseek = istream_read_uint32_le( istream ); disk_trailer.z_comment = istream_read_uint16_le( istream ); istream.seek( disk_trailer.z_comment, SeekableInputStream::cur ); } inline SeekableStream::position_type pkzip_find_disk_trailer( SeekableInputStream& istream ){ istream.seek( 0, SeekableInputStream::end ); SeekableStream::position_type start_position = istream.tell(); if ( start_position < disk_trailer_length ) { return 0; } start_position -= disk_trailer_length; zip_magic magic; istream.seek( start_position ); istream_read_zip_magic( istream, magic ); if ( magic == zip_disk_trailer_magic ) { return start_position; } else { const SeekableStream::position_type max_comment = 0x10000; const SeekableStream::position_type bufshift = 6; const SeekableStream::position_type bufsize = max_comment >> bufshift; unsigned char buffer[bufsize]; SeekableStream::position_type search_end = ( max_comment < start_position ) ? start_position - max_comment : 0; SeekableStream::position_type position = start_position; while ( position != search_end ) { StreamBase::size_type to_read = std::min( bufsize, position - search_end ); position -= to_read; istream.seek( position ); StreamBase::size_type size = istream.read( buffer, to_read ); unsigned char* p = buffer + size; while ( p != buffer ) { --p; magic.m_value[3] = magic.m_value[2]; magic.m_value[2] = magic.m_value[1]; magic.m_value[1] = magic.m_value[0]; magic.m_value[0] = *p; if ( magic == zip_disk_trailer_magic ) { return position + ( p - buffer ); } } } return 0; } } #endif