1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4 
5    This file is part of GtkRadiant.
6 
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #if !defined( INCLUDED_PKZIP_H )
23 #define INCLUDED_PKZIP_H
24 
25 #include "bytestreamutils.h"
26 #include "idatastream.h"
27 #include <algorithm>
28 
29 class zip_magic
30 {
31 public:
32 bool operator==( const zip_magic& other ) const {
33 	return m_value[0] == other.m_value[0]
34 		   && m_value[1] == other.m_value[1]
35 		   && m_value[2] == other.m_value[2]
36 		   && m_value[3] == other.m_value[3];
37 }
38 bool operator!=( const zip_magic& other ) const {
39 	return !( *this == other );
40 }
41 char m_value[4];
42 };
43 
istream_read_zip_magic(InputStream & istream,zip_magic & magic)44 inline void istream_read_zip_magic( InputStream& istream, zip_magic& magic ){
45 	istream.read( reinterpret_cast<InputStream::byte_type*>( magic.m_value ), 4 );
46 }
47 
48 struct zip_version
49 {
50 	char version;
51 	char ostype;
52 };
53 
istream_read_zip_version(InputStream & istream,zip_version & version)54 inline void istream_read_zip_version( InputStream& istream, zip_version& version ){
55 	version.version = istream_read_byte( istream );
56 	version.ostype = istream_read_byte( istream );
57 }
58 
59 struct zip_dostime
60 {
61 	unsigned short time;
62 	unsigned short date;
63 };
64 
istream_read_zip_dostime(InputStream & istream,zip_dostime & dostime)65 inline void istream_read_zip_dostime( InputStream& istream, zip_dostime& dostime ){
66 	dostime.time = istream_read_int16_le( istream );
67 	dostime.date = istream_read_int16_le( istream );
68 }
69 
70 const zip_magic zip_file_header_magic =  { { 'P', 'K', 0x03, 0x04 } };
71 
72 /* A. Local file header */
73 struct zip_file_header
74 {
75 	zip_magic z_magic; /* local file header signature (0x04034b50) */
76 	zip_version z_extract; /* version needed to extract */
77 	unsigned short z_flags; /* general purpose bit flag */
78 	unsigned short z_compr; /* compression method */
79 	zip_dostime z_dostime; /* last mod file time (dos format) */
80 	unsigned int z_crc32; /* crc-32 */
81 	unsigned int z_csize; /* compressed size */
82 	unsigned int z_usize; /* uncompressed size */
83 	unsigned short z_namlen; /* filename length (null if stdin) */
84 	unsigned short z_extras; /* extra field length */
85 	/* followed by filename (of variable size) */
86 	/* followed by extra field (of variable size) */
87 };
88 
istream_read_zip_file_header(SeekableInputStream & istream,zip_file_header & file_header)89 inline void istream_read_zip_file_header( SeekableInputStream& istream, zip_file_header& file_header ){
90 	istream_read_zip_magic( istream, file_header.z_magic );
91 	istream_read_zip_version( istream, file_header.z_extract );
92 	file_header.z_flags = istream_read_uint16_le( istream );
93 	file_header.z_compr = istream_read_uint16_le( istream );
94 	istream_read_zip_dostime( istream, file_header.z_dostime );
95 	file_header.z_crc32 = istream_read_uint32_le( istream );
96 	file_header.z_csize = istream_read_uint32_le( istream );
97 	file_header.z_usize = istream_read_uint32_le( istream );
98 	file_header.z_namlen = istream_read_uint16_le( istream );
99 	file_header.z_extras = istream_read_uint16_le( istream );
100 	istream.seek( file_header.z_namlen + file_header.z_extras, SeekableInputStream::cur );
101 };
102 
103 /* B. data descriptor
104  * the data descriptor exists only if bit 3 of z_flags is set. It is byte aligned
105  * and immediately follows the last byte of compressed data. It is only used if
106  * the output media of the compressor was not seekable, eg. standard output.
107  */
108 const zip_magic zip_file_trailer_magic = { { 'P', 'K', 0x07, 0x08} };
109 
110 struct zip_file_trailer
111 {
112 	zip_magic z_magic;
113 	unsigned int z_crc32; /* crc-32 */
114 	unsigned int z_csize; /* compressed size */
115 	unsigned int z_usize; /* uncompressed size */
116 };
117 
istream_read_zip_file_trailer(InputStream & istream,zip_file_trailer & file_trailer)118 inline void istream_read_zip_file_trailer( InputStream& istream, zip_file_trailer& file_trailer ){
119 	istream_read_zip_magic( istream, file_trailer.z_magic );
120 	file_trailer.z_crc32 = istream_read_uint32_le( istream );
121 	file_trailer.z_csize = istream_read_uint32_le( istream );
122 	file_trailer.z_usize = istream_read_uint32_le( istream );
123 };
124 
125 
126 /* C. central directory structure:
127     [file header] . . . end of central dir record
128  */
129 
130 /* directory file header
131  * - a single entry including filename, extras and comment may not exceed 64k.
132  */
133 
134 const zip_magic zip_root_dirent_magic = { { 'P', 'K', 0x01, 0x02 } };
135 
136 struct zip_root_dirent
137 {
138 	zip_magic z_magic;
139 	zip_version z_encoder; /* version made by */
140 	zip_version z_extract; /* version need to extract */
141 	unsigned short z_flags; /* general purpose bit flag */
142 	unsigned short z_compr; /* compression method */
143 	zip_dostime z_dostime; /* last mod file time&date (dos format) */
144 	unsigned int z_crc32; /* crc-32 */
145 	unsigned int z_csize; /* compressed size */
146 	unsigned int z_usize; /* uncompressed size */
147 	unsigned short z_namlen; /* filename length (null if stdin) */
148 	unsigned short z_extras; /* extra field length */
149 	unsigned short z_comment; /* file comment length */
150 	unsigned short z_diskstart; /* disk number of start (if spanning zip over multiple disks) */
151 	unsigned short z_filetype; /* internal file attributes, bit0 = ascii */
152 	unsigned int z_filemode; /* extrnal file attributes, eg. msdos attrib byte */
153 	unsigned int z_off;  /* relative offset of local file header, seekval if singledisk */
154 	/* followed by filename (of variable size) */
155 	/* followed by extra field (of variable size) */
156 	/* followed by file comment (of variable size) */
157 };
158 
istream_read_zip_root_dirent(SeekableInputStream & istream,zip_root_dirent & root_dirent)159 inline void istream_read_zip_root_dirent( SeekableInputStream& istream, zip_root_dirent& root_dirent ){
160 	istream_read_zip_magic( istream, root_dirent.z_magic );
161 	istream_read_zip_version( istream, root_dirent.z_encoder );
162 	istream_read_zip_version( istream, root_dirent.z_extract );
163 	root_dirent.z_flags = istream_read_uint16_le( istream );
164 	root_dirent.z_compr = istream_read_uint16_le( istream );
165 	istream_read_zip_dostime( istream, root_dirent.z_dostime );
166 	root_dirent.z_crc32 = istream_read_uint32_le( istream );
167 	root_dirent.z_csize = istream_read_uint32_le( istream );
168 	root_dirent.z_usize = istream_read_uint32_le( istream );
169 	root_dirent.z_namlen = istream_read_uint16_le( istream );
170 	root_dirent.z_extras = istream_read_uint16_le( istream );
171 	root_dirent.z_comment = istream_read_uint16_le( istream );
172 	root_dirent.z_diskstart = istream_read_uint16_le( istream );
173 	root_dirent.z_filetype = istream_read_uint16_le( istream );
174 	root_dirent.z_filemode = istream_read_uint32_le( istream );
175 	root_dirent.z_off = istream_read_uint32_le( istream );
176 	istream.seek( root_dirent.z_namlen + root_dirent.z_extras + root_dirent.z_comment, SeekableInputStream::cur );
177 }
178 
179 /* end of central dir record */
180 const zip_magic zip_disk_trailer_magic = { { 'P', 'K', 0x05, 0x06 } };
181 const unsigned int disk_trailer_length = 22;
182 struct zip_disk_trailer
183 {
184 	zip_magic z_magic;
185 	unsigned short z_disk; /* number of this disk */
186 	unsigned short z_finaldisk; /* number of the disk with the start of the central dir */
187 	unsigned short z_entries; /* total number of entries in the central dir on this disk */
188 	unsigned short z_finalentries; /* total number of entries in the central dir */
189 	unsigned int z_rootsize; /* size of the central directory */
190 	unsigned int z_rootseek; /* offset of start of central directory with respect to *
191 	                          * the starting disk number */
192 	unsigned short z_comment; /* zipfile comment length */
193 	/* followed by zipfile comment (of variable size) */
194 };
195 
istream_read_zip_disk_trailer(SeekableInputStream & istream,zip_disk_trailer & disk_trailer)196 inline void istream_read_zip_disk_trailer( SeekableInputStream& istream, zip_disk_trailer& disk_trailer ){
197 	istream_read_zip_magic( istream, disk_trailer.z_magic );
198 	disk_trailer.z_disk = istream_read_uint16_le( istream );
199 	disk_trailer.z_finaldisk = istream_read_uint16_le( istream );
200 	disk_trailer.z_entries = istream_read_uint16_le( istream );
201 	disk_trailer.z_finalentries = istream_read_uint16_le( istream );
202 	disk_trailer.z_rootsize = istream_read_uint32_le( istream );
203 	disk_trailer.z_rootseek = istream_read_uint32_le( istream );
204 	disk_trailer.z_comment = istream_read_uint16_le( istream );
205 	istream.seek( disk_trailer.z_comment, SeekableInputStream::cur );
206 }
207 
pkzip_find_disk_trailer(SeekableInputStream & istream)208 inline SeekableStream::position_type pkzip_find_disk_trailer( SeekableInputStream& istream ){
209 	istream.seek( 0, SeekableInputStream::end );
210 	SeekableStream::position_type start_position = istream.tell();
211 	if ( start_position < disk_trailer_length ) {
212 		return 0;
213 	}
214 	start_position -= disk_trailer_length;
215 
216 	zip_magic magic;
217 	istream.seek( start_position );
218 	istream_read_zip_magic( istream, magic );
219 
220 	if ( magic == zip_disk_trailer_magic ) {
221 		return start_position;
222 	}
223 	else
224 	{
225 		const SeekableStream::position_type max_comment = 0x10000;
226 		const SeekableStream::position_type bufshift = 6;
227 		const SeekableStream::position_type bufsize = max_comment >> bufshift;
228 		unsigned char buffer[bufsize];
229 
230 		SeekableStream::position_type search_end = ( max_comment < start_position ) ? start_position - max_comment : 0;
231 		SeekableStream::position_type position = start_position;
232 		while ( position != search_end )
233 		{
234 			StreamBase::size_type to_read = std::min( bufsize, position - search_end );
235 			position -= to_read;
236 
237 			istream.seek( position );
238 			StreamBase::size_type size = istream.read( buffer, to_read );
239 
240 			unsigned char* p = buffer + size;
241 			while ( p != buffer )
242 			{
243 				--p;
244 				magic.m_value[3] = magic.m_value[2];
245 				magic.m_value[2] = magic.m_value[1];
246 				magic.m_value[1] = magic.m_value[0];
247 				magic.m_value[0] = *p;
248 				if ( magic == zip_disk_trailer_magic ) {
249 					return position + ( p - buffer );
250 				}
251 			}
252 		}
253 		return 0;
254 	}
255 }
256 
257 #endif
258