]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/archivezip/pkzip.h
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / plugins / archivezip / pkzip.h
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 public:
31     bool operator==(const zip_magic &other) const
32     {
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
39     bool operator!=(const zip_magic &other) const
40     {
41         return !(*this == other);
42     }
43
44     char m_value[4];
45 };
46
47 inline void istream_read_zip_magic(InputStream &istream, zip_magic &magic)
48 {
49     istream.read(reinterpret_cast<InputStream::byte_type *>( magic.m_value ), 4);
50 }
51
52 struct zip_version {
53     char version;
54     char ostype;
55 };
56
57 inline void istream_read_zip_version(InputStream &istream, zip_version &version)
58 {
59     version.version = istream_read_byte(istream);
60     version.ostype = istream_read_byte(istream);
61 }
62
63 struct zip_dostime {
64     unsigned short time;
65     unsigned short date;
66 };
67
68 inline void istream_read_zip_dostime(InputStream &istream, zip_dostime &dostime)
69 {
70     dostime.time = istream_read_int16_le(istream);
71     dostime.date = istream_read_int16_le(istream);
72 }
73
74 const zip_magic zip_file_header_magic = {{'P', 'K', 0x03, 0x04}};
75
76 /* A. Local file header */
77 struct zip_file_header {
78     zip_magic z_magic; /* local file header signature (0x04034b50) */
79     zip_version z_extract; /* version needed to extract */
80     unsigned short z_flags; /* general purpose bit flag */
81     unsigned short z_compr; /* compression method */
82     zip_dostime z_dostime; /* last mod file time (dos format) */
83     unsigned int z_crc32; /* crc-32 */
84     unsigned int z_csize; /* compressed size */
85     unsigned int z_usize; /* uncompressed size */
86     unsigned short z_namlen; /* filename length (null if stdin) */
87     unsigned short z_extras; /* extra field length */
88     /* followed by filename (of variable size) */
89     /* followed by extra field (of variable size) */
90 };
91
92 inline void istream_read_zip_file_header(SeekableInputStream &istream, zip_file_header &file_header)
93 {
94     istream_read_zip_magic(istream, file_header.z_magic);
95     istream_read_zip_version(istream, file_header.z_extract);
96     file_header.z_flags = istream_read_uint16_le(istream);
97     file_header.z_compr = istream_read_uint16_le(istream);
98     istream_read_zip_dostime(istream, file_header.z_dostime);
99     file_header.z_crc32 = istream_read_uint32_le(istream);
100     file_header.z_csize = istream_read_uint32_le(istream);
101     file_header.z_usize = istream_read_uint32_le(istream);
102     file_header.z_namlen = istream_read_uint16_le(istream);
103     file_header.z_extras = istream_read_uint16_le(istream);
104     istream.seek(file_header.z_namlen + file_header.z_extras, SeekableInputStream::cur);
105 }
106
107 /* B. data descriptor
108  * the data descriptor exists only if bit 3 of z_flags is set. It is byte aligned
109  * and immediately follows the last byte of compressed data. It is only used if
110  * the output media of the compressor was not seekable, eg. standard output.
111  */
112 const zip_magic zip_file_trailer_magic = {{'P', 'K', 0x07, 0x08}};
113
114 struct zip_file_trailer {
115     zip_magic z_magic;
116     unsigned int z_crc32; /* crc-32 */
117     unsigned int z_csize; /* compressed size */
118     unsigned int z_usize; /* uncompressed size */
119 };
120
121 inline void istream_read_zip_file_trailer(InputStream &istream, zip_file_trailer &file_trailer)
122 {
123     istream_read_zip_magic(istream, file_trailer.z_magic);
124     file_trailer.z_crc32 = istream_read_uint32_le(istream);
125     file_trailer.z_csize = istream_read_uint32_le(istream);
126     file_trailer.z_usize = istream_read_uint32_le(istream);
127 }
128
129
130 /* C. central directory structure:
131     [file header] . . . end of central dir record
132  */
133
134 /* directory file header
135  * - a single entry including filename, extras and comment may not exceed 64k.
136  */
137
138 const zip_magic zip_root_dirent_magic = {{'P', 'K', 0x01, 0x02}};
139
140 struct zip_root_dirent {
141     zip_magic z_magic;
142     zip_version z_encoder; /* version made by */
143     zip_version z_extract; /* version need to extract */
144     unsigned short z_flags; /* general purpose bit flag */
145     unsigned short z_compr; /* compression method */
146     zip_dostime z_dostime; /* last mod file time&date (dos format) */
147     unsigned int z_crc32; /* crc-32 */
148     unsigned int z_csize; /* compressed size */
149     unsigned int z_usize; /* uncompressed size */
150     unsigned short z_namlen; /* filename length (null if stdin) */
151     unsigned short z_extras; /* extra field length */
152     unsigned short z_comment; /* file comment length */
153     unsigned short z_diskstart; /* disk number of start (if spanning zip over multiple disks) */
154     unsigned short z_filetype; /* internal file attributes, bit0 = ascii */
155     unsigned int z_filemode; /* extrnal file attributes, eg. msdos attrib byte */
156     unsigned int z_off;  /* relative offset of local file header, seekval if singledisk */
157     /* followed by filename (of variable size) */
158     /* followed by extra field (of variable size) */
159     /* followed by file comment (of variable size) */
160 };
161
162 inline void istream_read_zip_root_dirent(SeekableInputStream &istream, zip_root_dirent &root_dirent)
163 {
164     istream_read_zip_magic(istream, root_dirent.z_magic);
165     istream_read_zip_version(istream, root_dirent.z_encoder);
166     istream_read_zip_version(istream, root_dirent.z_extract);
167     root_dirent.z_flags = istream_read_uint16_le(istream);
168     root_dirent.z_compr = istream_read_uint16_le(istream);
169     istream_read_zip_dostime(istream, root_dirent.z_dostime);
170     root_dirent.z_crc32 = istream_read_uint32_le(istream);
171     root_dirent.z_csize = istream_read_uint32_le(istream);
172     root_dirent.z_usize = istream_read_uint32_le(istream);
173     root_dirent.z_namlen = istream_read_uint16_le(istream);
174     root_dirent.z_extras = istream_read_uint16_le(istream);
175     root_dirent.z_comment = istream_read_uint16_le(istream);
176     root_dirent.z_diskstart = istream_read_uint16_le(istream);
177     root_dirent.z_filetype = istream_read_uint16_le(istream);
178     root_dirent.z_filemode = istream_read_uint32_le(istream);
179     root_dirent.z_off = istream_read_uint32_le(istream);
180     istream.seek(root_dirent.z_namlen + root_dirent.z_extras + root_dirent.z_comment, SeekableInputStream::cur);
181 }
182
183 /* end of central dir record */
184 const zip_magic zip_disk_trailer_magic = {{'P', 'K', 0x05, 0x06}};
185 const unsigned int disk_trailer_length = 22;
186 struct zip_disk_trailer {
187     zip_magic z_magic;
188     unsigned short z_disk; /* number of this disk */
189     unsigned short z_finaldisk; /* number of the disk with the start of the central dir */
190     unsigned short z_entries; /* total number of entries in the central dir on this disk */
191     unsigned short z_finalentries; /* total number of entries in the central dir */
192     unsigned int z_rootsize; /* size of the central directory */
193     unsigned int z_rootseek; /* offset of start of central directory with respect to *
194                                   * the starting disk number */
195     unsigned short z_comment; /* zipfile comment length */
196     /* followed by zipfile comment (of variable size) */
197 };
198
199 inline void istream_read_zip_disk_trailer(SeekableInputStream &istream, zip_disk_trailer &disk_trailer)
200 {
201     istream_read_zip_magic(istream, disk_trailer.z_magic);
202     disk_trailer.z_disk = istream_read_uint16_le(istream);
203     disk_trailer.z_finaldisk = istream_read_uint16_le(istream);
204     disk_trailer.z_entries = istream_read_uint16_le(istream);
205     disk_trailer.z_finalentries = istream_read_uint16_le(istream);
206     disk_trailer.z_rootsize = istream_read_uint32_le(istream);
207     disk_trailer.z_rootseek = istream_read_uint32_le(istream);
208     disk_trailer.z_comment = istream_read_uint16_le(istream);
209     istream.seek(disk_trailer.z_comment, SeekableInputStream::cur);
210 }
211
212 inline SeekableStream::position_type pkzip_find_disk_trailer(SeekableInputStream &istream)
213 {
214     istream.seek(0, SeekableInputStream::end);
215     SeekableStream::position_type start_position = istream.tell();
216     if (start_position < disk_trailer_length) {
217         return 0;
218     }
219     start_position -= disk_trailer_length;
220
221     zip_magic magic;
222     istream.seek(start_position);
223     istream_read_zip_magic(istream, magic);
224
225     if (magic == zip_disk_trailer_magic) {
226         return start_position;
227     } else {
228         const SeekableStream::position_type max_comment = 0x10000;
229         const SeekableStream::position_type bufshift = 6;
230         const SeekableStream::position_type bufsize = max_comment >> bufshift;
231         unsigned char buffer[bufsize];
232
233         SeekableStream::position_type search_end = (max_comment < start_position) ? start_position - max_comment : 0;
234         SeekableStream::position_type position = start_position;
235         while (position != search_end) {
236             StreamBase::size_type to_read = std::min(bufsize, position - search_end);
237             position -= to_read;
238
239             istream.seek(position);
240             StreamBase::size_type size = istream.read(buffer, to_read);
241
242             unsigned char *p = buffer + size;
243             while (p != buffer) {
244                 --p;
245                 magic.m_value[3] = magic.m_value[2];
246                 magic.m_value[2] = magic.m_value[1];
247                 magic.m_value[1] = magic.m_value[0];
248                 magic.m_value[0] = *p;
249                 if (magic == zip_disk_trailer_magic) {
250                     return position + (p - buffer);
251                 }
252             }
253         }
254         return 0;
255     }
256 }
257
258 #endif