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