initial import
[picodrive.git] / unzip / unzip.c
CommitLineData
cc68a136 1#include "unzip.h"\r
2\r
3#include <stdlib.h>\r
4#include <string.h>\r
5#include <ctype.h>\r
6#include <assert.h>\r
7\r
8#ifdef __SYMBIAN32__\r
9#include <ezlib.h>\r
10#else\r
11#include "zlib/zlib.h"\r
12#endif\r
13\r
14/* public globals */\r
15//int gUnzipQuiet = 0; /* flag controls error messages */\r
16\r
17#define ERROR_CORRUPT "The zipfile seems to be corrupt, please check it"\r
18#define ERROR_FILESYSTEM "Your filesystem seems to be corrupt, please check it"\r
19#define ERROR_UNSUPPORTED "The format of this zipfile is not supported, please recompress it"\r
20\r
21#define INFLATE_INPUT_BUFFER_MAX 16384\r
22#ifndef MIN\r
23#define MIN(x,y) ((x)<(y)?(x):(y))\r
24#endif\r
25\r
26\r
27// notaz\r
28#ifdef __DEBUG_PRINT\r
29void dprintf(char *format, ...);\r
30#define logerror dprintf\r
31void errormsg(const char* extmsg, const char* usermsg, const char* zipname)\r
32{\r
33 dprintf("Error in zipfile %s: %s", zipname, extmsg);\r
34}\r
35#else\r
36#define logerror(x...)\r
37#define errormsg(x...)\r
38#endif\r
39\r
40/* Print a error message */\r
41//void errormsg(const char* extmsg, const char* usermsg, const char* zipname) {\r
42 /* Output to the user with no internal detail */\r
43// if (!gUnzipQuiet)\r
44// printf("Error in zipfile %s\n%s\n", zipname, usermsg);\r
45 /* Output to log file with all informations */\r
46// logerror("Error in zipfile %s: %s\n", zipname, extmsg);\r
47// printf("Error in zipfile %s: %s\n", zipname, extmsg);\r
48//}\r
49\r
50/* -------------------------------------------------------------------------\r
51 Unzip support\r
52 ------------------------------------------------------------------------- */\r
53\r
54/* Use these to avoid structure padding and byte-ordering problems */\r
55static UINT16 read_word (char *buf) {\r
56 unsigned char *ubuf = (unsigned char *) buf;\r
57\r
58 return ((UINT16)ubuf[1] << 8) | (UINT16)ubuf[0];\r
59}\r
60\r
61/* Use these to avoid structure padding and byte-ordering problems */\r
62static UINT32 read_dword (char *buf) {\r
63 unsigned char *ubuf = (unsigned char *) buf;\r
64\r
65 return ((UINT32)ubuf[3] << 24) | ((UINT32)ubuf[2] << 16) | ((UINT32)ubuf[1] << 8) | (UINT32)ubuf[0];\r
66}\r
67\r
68/* Locate end-of-central-dir sig in buffer and return offset\r
69 out:\r
70 *offset offset of cent dir start in buffer\r
71 return:\r
72 ==0 not found\r
73 !=0 found, *offset valid\r
74*/\r
75static int ecd_find_sig (char *buffer, int buflen, int *offset)\r
76{\r
77 static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };\r
78 int i;\r
79 for (i=buflen-22; i>=0; i--) {\r
80 if (memcmp(buffer+i, ecdsig, 4) == 0) {\r
81 *offset = i;\r
82 return 1;\r
83 }\r
84 }\r
85 return 0;\r
86}\r
87\r
88/* Read ecd data in zip structure\r
89 in:\r
90 zip->fp, zip->length zip file\r
91 out:\r
92 zip->ecd, zip->ecd_length ecd data\r
93*/\r
94static int ecd_read(ZIP* zip) {\r
95 char* buf;\r
96 int buf_length = 1024; /* initial buffer length */\r
97\r
98 while (1) {\r
99 int offset;\r
100\r
101 if (buf_length > zip->length)\r
102 buf_length = zip->length;\r
103\r
104 if (fseek(zip->fp, zip->length - buf_length, SEEK_SET) != 0) {\r
105 return -1;\r
106 }\r
107\r
108 /* allocate buffer */\r
109 buf = (char*)malloc( buf_length );\r
110 if (!buf) {\r
111 return -1;\r
112 }\r
113\r
114 if (fread( buf, buf_length, 1, zip->fp ) != 1) {\r
115 free(buf);\r
116 return -1;\r
117 }\r
118\r
119 if (ecd_find_sig(buf, buf_length, &offset)) {\r
120 zip->ecd_length = buf_length - offset;\r
121\r
122 zip->ecd = (char*)malloc( zip->ecd_length );\r
123 if (!zip->ecd) {\r
124 free(buf);\r
125 return -1;\r
126 }\r
127\r
128 memcpy(zip->ecd, buf + offset, zip->ecd_length);\r
129\r
130 free(buf);\r
131 return 0;\r
132 }\r
133\r
134 free(buf);\r
135\r
136 if (buf_length < zip->length) {\r
137 /* double buffer */\r
138 buf_length = 2*buf_length;\r
139\r
140 logerror("Retry reading of zip ecd for %d bytes\n",buf_length);\r
141\r
142 } else {\r
143 return -1;\r
144 }\r
145 }\r
146}\r
147\r
148/* offsets in end of central directory structure */\r
149#define ZIPESIG 0x00\r
150#define ZIPEDSK 0x04\r
151#define ZIPECEN 0x06\r
152#define ZIPENUM 0x08\r
153#define ZIPECENN 0x0a\r
154#define ZIPECSZ 0x0c\r
155#define ZIPEOFST 0x10\r
156#define ZIPECOML 0x14\r
157#define ZIPECOM 0x16\r
158\r
159/* offsets in central directory entry structure */\r
160#define ZIPCENSIG 0x0\r
161#define ZIPCVER 0x4\r
162#define ZIPCOS 0x5\r
163#define ZIPCVXT 0x6\r
164#define ZIPCEXOS 0x7\r
165#define ZIPCFLG 0x8\r
166#define ZIPCMTHD 0xa\r
167#define ZIPCTIM 0xc\r
168#define ZIPCDAT 0xe\r
169#define ZIPCCRC 0x10\r
170#define ZIPCSIZ 0x14\r
171#define ZIPCUNC 0x18\r
172#define ZIPCFNL 0x1c\r
173#define ZIPCXTL 0x1e\r
174#define ZIPCCML 0x20\r
175#define ZIPDSK 0x22\r
176#define ZIPINT 0x24\r
177#define ZIPEXT 0x26\r
178#define ZIPOFST 0x2a\r
179#define ZIPCFN 0x2e\r
180\r
181/* offsets in local file header structure */\r
182#define ZIPLOCSIG 0x00\r
183#define ZIPVER 0x04\r
184#define ZIPGENFLG 0x06\r
185#define ZIPMTHD 0x08\r
186#define ZIPTIME 0x0a\r
187#define ZIPDATE 0x0c\r
188#define ZIPCRC 0x0e\r
189#define ZIPSIZE 0x12\r
190#define ZIPUNCMP 0x16\r
191#define ZIPFNLN 0x1a\r
192#define ZIPXTRALN 0x1c\r
193#define ZIPNAME 0x1e\r
194\r
195/* Opens a zip stream for reading\r
196 return:\r
197 !=0 success, zip stream\r
198 ==0 error\r
199*/\r
200ZIP* openzip(const char* zipfile) {\r
201 /* allocate */\r
202 ZIP* zip = (ZIP*)malloc( sizeof(ZIP) );\r
203 if (!zip) {\r
204 return 0;\r
205 }\r
206\r
207 /* open */\r
208 zip->fp = fopen(zipfile, "rb");\r
209 if (!zip->fp) {\r
210 errormsg ("Opening for reading", ERROR_FILESYSTEM, zipfile);\r
211 free(zip);\r
212 return 0;\r
213 }\r
214\r
215 /* go to end */\r
216 if (fseek(zip->fp, 0L, SEEK_END) != 0) {\r
217 errormsg ("Seeking to end", ERROR_FILESYSTEM, zipfile);\r
218 fclose(zip->fp);\r
219 free(zip);\r
220 return 0;\r
221 }\r
222\r
223 /* get length */\r
224 zip->length = ftell(zip->fp);\r
225 if (zip->length < 0) {\r
226 errormsg ("Get file size", ERROR_FILESYSTEM, zipfile);\r
227 fclose(zip->fp);\r
228 free(zip);\r
229 return 0;\r
230 }\r
231 if (zip->length == 0) {\r
232 errormsg ("Empty file", ERROR_CORRUPT, zipfile);\r
233 fclose(zip->fp);\r
234 free(zip);\r
235 return 0;\r
236 }\r
237\r
238 /* read ecd data */\r
239 if (ecd_read(zip)!=0) {\r
240 errormsg ("Reading ECD (end of central directory)", ERROR_CORRUPT, zipfile);\r
241 fclose(zip->fp);\r
242 free(zip);\r
243 return 0;\r
244 }\r
245\r
246 /* compile ecd info */\r
247 zip->end_of_cent_dir_sig = read_dword (zip->ecd+ZIPESIG);\r
248 zip->number_of_this_disk = read_word (zip->ecd+ZIPEDSK);\r
249 zip->number_of_disk_start_cent_dir = read_word (zip->ecd+ZIPECEN);\r
250 zip->total_entries_cent_dir_this_disk = read_word (zip->ecd+ZIPENUM);\r
251 zip->total_entries_cent_dir = read_word (zip->ecd+ZIPECENN);\r
252 zip->size_of_cent_dir = read_dword (zip->ecd+ZIPECSZ);\r
253 zip->offset_to_start_of_cent_dir = read_dword (zip->ecd+ZIPEOFST);\r
254 zip->zipfile_comment_length = read_word (zip->ecd+ZIPECOML);\r
255 zip->zipfile_comment = zip->ecd+ZIPECOM;\r
256\r
257 /* verify that we can work with this zipfile (no disk spanning allowed) */\r
258 if ((zip->number_of_this_disk != zip->number_of_disk_start_cent_dir) ||\r
259 (zip->total_entries_cent_dir_this_disk != zip->total_entries_cent_dir) ||\r
260 (zip->total_entries_cent_dir < 1)) {\r
261 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zipfile);\r
262 free(zip->ecd);\r
263 fclose(zip->fp);\r
264 free(zip);\r
265 return 0;\r
266 }\r
267\r
268 if (fseek(zip->fp, zip->offset_to_start_of_cent_dir, SEEK_SET)!=0) {\r
269 errormsg ("Seeking to central directory", ERROR_CORRUPT, zipfile);\r
270 free(zip->ecd);\r
271 fclose(zip->fp);\r
272 free(zip);\r
273 return 0;\r
274 }\r
275\r
276 /* read from start of central directory */\r
277 zip->cd = (char*)malloc( zip->size_of_cent_dir );\r
278 if (!zip->cd) {\r
279 free(zip->ecd);\r
280 fclose(zip->fp);\r
281 free(zip);\r
282 return 0;\r
283 }\r
284\r
285 if (fread(zip->cd, zip->size_of_cent_dir, 1, zip->fp)!=1) {\r
286 errormsg ("Reading central directory", ERROR_CORRUPT, zipfile);\r
287 free(zip->cd);\r
288 free(zip->ecd);\r
289 fclose(zip->fp);\r
290 free(zip);\r
291 return 0;\r
292 }\r
293\r
294 /* reset ent */\r
295 zip->ent.name = 0;\r
296\r
297 /* rewind */\r
298 zip->cd_pos = 0;\r
299\r
300 /* file name */\r
301 zip->zip = (char*)malloc(strlen(zipfile)+1);\r
302 if (!zip->zip) {\r
303 free(zip->cd);\r
304 free(zip->ecd);\r
305 fclose(zip->fp);\r
306 free(zip);\r
307 return 0;\r
308 }\r
309 strcpy(zip->zip, zipfile);\r
310\r
311 return zip;\r
312}\r
313\r
314/* Reads the current entry from a zip stream\r
315 in:\r
316 zip opened zip\r
317 return:\r
318 !=0 success\r
319 ==0 error\r
320*/\r
321struct zipent* readzip(ZIP* zip) {\r
322\r
323 /* end of directory */\r
324 if (zip->cd_pos >= zip->size_of_cent_dir)\r
325 return 0;\r
326\r
327 /* compile zipent info */\r
328 zip->ent.cent_file_header_sig = read_dword (zip->cd+zip->cd_pos+ZIPCENSIG);\r
329 zip->ent.version_made_by = *(zip->cd+zip->cd_pos+ZIPCVER);\r
330 zip->ent.host_os = *(zip->cd+zip->cd_pos+ZIPCOS);\r
331 zip->ent.version_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCVXT);\r
332 zip->ent.os_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCEXOS);\r
333 zip->ent.general_purpose_bit_flag = read_word (zip->cd+zip->cd_pos+ZIPCFLG);\r
334 zip->ent.compression_method = read_word (zip->cd+zip->cd_pos+ZIPCMTHD);\r
335 zip->ent.last_mod_file_time = read_word (zip->cd+zip->cd_pos+ZIPCTIM);\r
336 zip->ent.last_mod_file_date = read_word (zip->cd+zip->cd_pos+ZIPCDAT);\r
337 zip->ent.crc32 = read_dword (zip->cd+zip->cd_pos+ZIPCCRC);\r
338 zip->ent.compressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCSIZ);\r
339 zip->ent.uncompressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCUNC);\r
340 zip->ent.filename_length = read_word (zip->cd+zip->cd_pos+ZIPCFNL);\r
341 zip->ent.extra_field_length = read_word (zip->cd+zip->cd_pos+ZIPCXTL);\r
342 zip->ent.file_comment_length = read_word (zip->cd+zip->cd_pos+ZIPCCML);\r
343 zip->ent.disk_number_start = read_word (zip->cd+zip->cd_pos+ZIPDSK);\r
344 zip->ent.internal_file_attrib = read_word (zip->cd+zip->cd_pos+ZIPINT);\r
345 zip->ent.external_file_attrib = read_dword (zip->cd+zip->cd_pos+ZIPEXT);\r
346 zip->ent.offset_lcl_hdr_frm_frst_disk = read_dword (zip->cd+zip->cd_pos+ZIPOFST);\r
347\r
348 /* check to see if filename length is illegally long (past the size of this directory\r
349 entry) */\r
350 if (zip->cd_pos + ZIPCFN + zip->ent.filename_length > zip->size_of_cent_dir)\r
351 {\r
352 errormsg("Invalid filename length in directory", ERROR_CORRUPT,zip->zip);\r
353 return 0;\r
354 }\r
355\r
356 /* copy filename */\r
357 free(zip->ent.name);\r
358 zip->ent.name = (char*)malloc(zip->ent.filename_length + 1);\r
359 memcpy(zip->ent.name, zip->cd+zip->cd_pos+ZIPCFN, zip->ent.filename_length);\r
360 zip->ent.name[zip->ent.filename_length] = 0;\r
361\r
362 /* skip to next entry in central dir */\r
363 zip->cd_pos += ZIPCFN + zip->ent.filename_length + zip->ent.extra_field_length + zip->ent.file_comment_length;\r
364\r
365 return &zip->ent;\r
366}\r
367\r
368/* Closes a zip stream */\r
369void closezip(ZIP* zip) {\r
370 /* release all */\r
371 free(zip->ent.name);\r
372 free(zip->cd);\r
373 free(zip->ecd);\r
374 /* only if not suspended */\r
375 if (zip->fp)\r
376 fclose(zip->fp);\r
377 free(zip->zip);\r
378 free(zip);\r
379}\r
380\r
381/* Suspend access to a zip file (release file handler)\r
382 in:\r
383 zip opened zip\r
384 note:\r
385 A suspended zip is automatically reopened at first call of\r
386 readuncompressd() or readcompressed() functions\r
387*/\r
388void suspendzip(ZIP* zip) {\r
389 if (zip->fp) {\r
390 fclose(zip->fp);\r
391 zip->fp = 0;\r
392 }\r
393}\r
394\r
395/* Revive a suspended zip file (reopen file handler)\r
396 in:\r
397 zip suspended zip\r
398 return:\r
399 zip success\r
400 ==0 error (zip must be closed with closezip)\r
401*/\r
402static ZIP* revivezip(ZIP* zip) {\r
403 if (!zip->fp) {\r
404 zip->fp = fopen(zip->zip, "rb");\r
405 if (!zip->fp) {\r
406 return 0;\r
407 }\r
408 }\r
409 return zip;\r
410\r
411}\r
412\r
413/* Reset a zip stream to the first entry\r
414 in:\r
415 zip opened zip\r
416 note:\r
417 ZIP file must be opened and not suspended\r
418*/\r
419void rewindzip(ZIP* zip) {\r
420 zip->cd_pos = 0;\r
421}\r
422\r
423/* Seek zip->fp to compressed data\r
424 return:\r
425 ==0 success\r
426 <0 error\r
427*/\r
428int seekcompresszip(ZIP* zip, struct zipent* ent) {\r
429 char buf[ZIPNAME];\r
430 long offset;\r
431\r
432 if (!zip->fp) {\r
433 if (!revivezip(zip))\r
434 return -1;\r
435 }\r
436\r
437 if (fseek(zip->fp, ent->offset_lcl_hdr_frm_frst_disk, SEEK_SET)!=0) {\r
438 errormsg ("Seeking to header", ERROR_CORRUPT, zip->zip);\r
439 return -1;\r
440 }\r
441\r
442 if (fread(buf, ZIPNAME, 1, zip->fp)!=1) {\r
443 errormsg ("Reading header", ERROR_CORRUPT, zip->zip);\r
444 return -1;\r
445 }\r
446\r
447 {\r
448 UINT16 filename_length = read_word (buf+ZIPFNLN);\r
449 UINT16 extra_field_length = read_word (buf+ZIPXTRALN);\r
450\r
451 /* calculate offset to data and fseek() there */\r
452 offset = ent->offset_lcl_hdr_frm_frst_disk + ZIPNAME + filename_length + extra_field_length;\r
453\r
454 if (fseek(zip->fp, offset, SEEK_SET) != 0) {\r
455 errormsg ("Seeking to compressed data", ERROR_CORRUPT, zip->zip);\r
456 return -1;\r
457 }\r
458\r
459 }\r
460\r
461 return 0;\r
462}\r
463\r
464/* Inflate a file\r
465 in:\r
466 in_file stream to inflate\r
467 in_size size of the compressed data to read\r
468 out_size size of decompressed data\r
469 out:\r
470 out_data buffer for decompressed data\r
471 return:\r
472 ==0 ok\r
473\r
474 990525 rewritten for use with zlib MLR\r
475*/\r
476static int inflate_file(FILE* in_file, unsigned in_size, unsigned char* out_data, unsigned out_size)\r
477{\r
478 int err;\r
479 unsigned char* in_buffer;\r
480 z_stream d_stream; /* decompression stream */\r
481\r
482 d_stream.zalloc = 0;\r
483 d_stream.zfree = 0;\r
484 d_stream.opaque = 0;\r
485\r
486 d_stream.next_in = 0;\r
487 d_stream.avail_in = 0;\r
488 d_stream.next_out = out_data;\r
489 d_stream.avail_out = out_size;\r
490\r
491 err = inflateInit2(&d_stream, -MAX_WBITS);\r
492 /* windowBits is passed < 0 to tell that there is no zlib header.\r
493 * Note that in this case inflate *requires* an extra "dummy" byte\r
494 * after the compressed stream in order to complete decompression and\r
495 * return Z_STREAM_END.\r
496 */\r
497 if (err != Z_OK)\r
498 {\r
499 logerror("inflateInit error: %d\n", err);\r
500 return -1;\r
501 }\r
502\r
503 in_buffer = (unsigned char*)malloc(INFLATE_INPUT_BUFFER_MAX+1);\r
504 if (!in_buffer)\r
505 return -1;\r
506\r
507 for (;;)\r
508 {\r
509 if (in_size <= 0)\r
510 {\r
511 logerror("inflate error: compressed size too small\n");\r
512 free (in_buffer);\r
513 return -1;\r
514 }\r
515 d_stream.next_in = in_buffer;\r
516 d_stream.avail_in = fread (in_buffer, 1, MIN(in_size, INFLATE_INPUT_BUFFER_MAX), in_file);\r
517 in_size -= d_stream.avail_in;\r
518 if (in_size == 0)\r
519 d_stream.avail_in++; /* add dummy byte at end of compressed data */\r
520\r
521 err = inflate(&d_stream, Z_NO_FLUSH);\r
522 if (err == Z_STREAM_END)\r
523 break;\r
524 if (err != Z_OK)\r
525 {\r
526 logerror("inflate error: %d\n", err);\r
527 free (in_buffer);\r
528 return -1;\r
529 }\r
530 }\r
531\r
532 err = inflateEnd(&d_stream);\r
533 if (err != Z_OK)\r
534 {\r
535 logerror("inflateEnd error: %d\n", err);\r
536 free (in_buffer);\r
537 return -1;\r
538 }\r
539\r
540 free (in_buffer);\r
541\r
542 if ((d_stream.avail_out > 0) || (in_size > 0))\r
543 {\r
544 logerror("zip size mismatch. %i\n", in_size);\r
545 return -1;\r
546 }\r
547\r
548 return 0;\r
549}\r
550\r
551/* Read compressed data\r
552 out:\r
553 data compressed data read\r
554 return:\r
555 ==0 success\r
556 <0 error\r
557*/\r
558int readcompresszip(ZIP* zip, struct zipent* ent, char* data) {\r
559 int err = seekcompresszip(zip,ent);\r
560 if (err!=0)\r
561 return err;\r
562\r
563 if (fread(data, ent->compressed_size, 1, zip->fp)!=1) {\r
564 errormsg ("Reading compressed data", ERROR_CORRUPT, zip->zip);\r
565 return -1;\r
566 }\r
567\r
568 return 0;\r
569}\r
570\r
571/* Read UNcompressed data\r
572 out:\r
573 data UNcompressed data\r
574 return:\r
575 ==0 success\r
576 <0 error\r
577*/\r
578int readuncompresszip(ZIP* zip, struct zipent* ent, char* data) {\r
579 if (ent->compression_method == 0x0000) {\r
580 /* file is not compressed, simply stored */\r
581\r
582 /* check if size are equal */\r
583 if (ent->compressed_size != ent->uncompressed_size) {\r
584 errormsg("Wrong uncompressed size in store compression", ERROR_CORRUPT,zip->zip);\r
585 return -3;\r
586 }\r
587\r
588 return readcompresszip(zip,ent,data);\r
589 } else if (ent->compression_method == 0x0008) {\r
590 /* file is compressed using "Deflate" method */\r
591 if (ent->version_needed_to_extract > 0x14) {\r
592 errormsg("Version too new", ERROR_UNSUPPORTED,zip->zip);\r
593 return -2;\r
594 }\r
595\r
596 if (ent->os_needed_to_extract != 0x00) {\r
597 errormsg("OS not supported", ERROR_UNSUPPORTED,zip->zip);\r
598 return -2;\r
599 }\r
600\r
601 if (ent->disk_number_start != zip->number_of_this_disk) {\r
602 errormsg("Cannot span disks", ERROR_UNSUPPORTED,zip->zip);\r
603 return -2;\r
604 }\r
605\r
606 /* read compressed data */\r
607 if (seekcompresszip(zip,ent)!=0) {\r
608 return -1;\r
609 }\r
610\r
611 /* configure inflate */\r
612 if (inflate_file( zip->fp, ent->compressed_size, (unsigned char*)data, ent->uncompressed_size))\r
613 {\r
614 errormsg("Inflating compressed data", ERROR_CORRUPT, zip->zip);\r
615 return -3;\r
616 }\r
617\r
618 return 0;\r
619 } else {\r
620 errormsg("Compression method unsupported", ERROR_UNSUPPORTED, zip->zip);\r
621 return -2;\r
622 }\r
623}\r