2 // ZIP file support (mostly ripped from MAME--thx MAME team!)
3 // Mostly this is here to simplify interfacing to zlib...
5 // Added by James L. Hammons
6 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -------------------------------------------------------------
12 // JLH 01/16/2010 Created this log ;-)
13 // JLH 02/28/2010 Removed unnecessary cruft
26 int gUnzipQuiet = 0; /* flag controls error messages */
29 #define ERROR_CORRUPT "The zipfile seems to be corrupt, please check it"
30 #define ERROR_FILESYSTEM "Your filesystem seems to be corrupt, please check it"
31 #define ERROR_UNSUPPORTED "The format of this zipfile is not supported, please recompress it"
33 #define INFLATE_INPUT_BUFFER_MAX 16384
35 #define MIN(x,y) ((x)<(y)?(x):(y))
38 /* Print a error message */
39 void errormsg(const char * extmsg, const char * usermsg, const char * zipname)
41 /* Output to the user with no internal detail */
43 printf("Error in zipfile %s\n%s\n", zipname, usermsg);
44 /* Output to log file with all informations */
45 // WriteLog("Error in zipfile %s: %s\n", zipname, extmsg);
48 /* -------------------------------------------------------------------------
50 ------------------------------------------------------------------------- */
52 /* Use these to avoid structure padding and byte-ordering problems */
53 static uint16_t read_word(char * buf)
55 unsigned char * ubuf = (unsigned char *)buf;
57 return ((uint16_t)ubuf[1] << 8) | (uint16_t)ubuf[0];
60 /* Use these to avoid structure padding and byte-ordering problems */
61 static uint32_t read_dword(char * buf)
63 unsigned char * ubuf = (unsigned char *)buf;
65 return ((uint32_t)ubuf[3] << 24) | ((uint32_t)ubuf[2] << 16) | ((uint32_t)ubuf[1] << 8) | (uint32_t)ubuf[0];
68 /* Locate end-of-central-dir sig in buffer and return offset
70 *offset offset of cent dir start in buffer
73 !=0 found, *offset valid
75 static int ecd_find_sig(char * buffer, int buflen, int * offset)
77 static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };
80 for(i=buflen-22; i>=0; i--)
82 if (memcmp(buffer+i, ecdsig, 4) == 0)
92 /* Read ecd data in zip structure
94 zip->fp, zip->length zip file
96 zip->ecd, zip->ecd_length ecd data
98 static int ecd_read(ZIP * zip)
101 int buf_length = 1024; /* initial buffer length */
107 if (buf_length > zip->length)
108 buf_length = zip->length;
110 if (fseek(zip->fp, zip->length - buf_length, SEEK_SET) != 0)
115 /* allocate buffer */
116 buf = (char *)malloc(buf_length);
123 if (fread(buf, 1, buf_length, zip->fp) != (unsigned int)buf_length)
129 if (ecd_find_sig(buf, buf_length, &offset))
131 zip->ecd_length = buf_length - offset;
133 zip->ecd = (char *)malloc(zip->ecd_length);
141 memcpy(zip->ecd, buf + offset, zip->ecd_length);
148 if (buf_length < zip->length)
151 buf_length = 2 * buf_length;
152 WriteLog("Retry reading of zip ecd for %d bytes\n",buf_length);
160 /* offsets in end of central directory structure */
165 #define ZIPECENN 0x0a
167 #define ZIPEOFST 0x10
168 #define ZIPECOML 0x14
171 /* offsets in central directory entry structure */
172 #define ZIPCENSIG 0x0
193 /* offsets in local file header structure */
194 #define ZIPLOCSIG 0x00
196 #define ZIPGENFLG 0x06
202 #define ZIPUNCMP 0x16
204 #define ZIPXTRALN 0x1c
208 // Opens a zip stream for reading
210 // !=0 success, zip stream
213 ZIP * openzip(int pathtype, int pathindex, const char * zipfile)
216 ZIP * zip = (ZIP *)malloc(sizeof(ZIP));
221 zip->fp = fopen(zipfile, "rb");
224 errormsg("Opening for reading", ERROR_FILESYSTEM, zipfile);
230 if (fseek(zip->fp, 0L, SEEK_END) != 0)
232 errormsg ("Seeking to end", ERROR_FILESYSTEM, zipfile);
239 zip->length = ftell(zip->fp);
242 errormsg("Get file size", ERROR_FILESYSTEM, zipfile);
247 if (zip->length == 0)
249 errormsg ("Empty file", ERROR_CORRUPT, zipfile);
256 if (ecd_read(zip) != 0)
258 errormsg("Reading ECD (end of central directory)", ERROR_CORRUPT, zipfile);
264 /* compile ecd info */
265 zip->end_of_cent_dir_sig = read_dword (zip->ecd+ZIPESIG);
266 zip->number_of_this_disk = read_word (zip->ecd+ZIPEDSK);
267 zip->number_of_disk_start_cent_dir = read_word (zip->ecd+ZIPECEN);
268 zip->total_entries_cent_dir_this_disk = read_word (zip->ecd+ZIPENUM);
269 zip->total_entries_cent_dir = read_word (zip->ecd+ZIPECENN);
270 zip->size_of_cent_dir = read_dword (zip->ecd+ZIPECSZ);
271 zip->offset_to_start_of_cent_dir = read_dword (zip->ecd+ZIPEOFST);
272 zip->zipfile_comment_length = read_word (zip->ecd+ZIPECOML);
273 zip->zipfile_comment = zip->ecd+ZIPECOM;
275 /* verify that we can work with this zipfile (no disk spanning allowed) */
276 if ((zip->number_of_this_disk != zip->number_of_disk_start_cent_dir)
277 || (zip->total_entries_cent_dir_this_disk != zip->total_entries_cent_dir)
278 || (zip->total_entries_cent_dir < 1))
280 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zipfile);
287 if (fseek(zip->fp, zip->offset_to_start_of_cent_dir, SEEK_SET) != 0)
289 errormsg("Seeking to central directory", ERROR_CORRUPT, zipfile);
296 /* read from start of central directory */
297 zip->cd = (char *)malloc(zip->size_of_cent_dir);
306 if (fread(zip->cd, 1, zip->size_of_cent_dir, zip->fp) != zip->size_of_cent_dir)
308 errormsg("Reading central directory", ERROR_CORRUPT, zipfile);
323 zip->zip = (char*)malloc(strlen(zipfile)+1);
332 strcpy(zip->zip, zipfile);
333 zip->pathtype = pathtype;
334 zip->pathindex = pathindex;
339 /* Reads the current entry from a zip stream
346 struct zipent * readzip(ZIP * zip)
349 /* end of directory */
350 if (zip->cd_pos >= zip->size_of_cent_dir)
353 /* compile zipent info */
354 zip->ent.cent_file_header_sig = read_dword (zip->cd+zip->cd_pos+ZIPCENSIG);
355 zip->ent.version_made_by = *(zip->cd+zip->cd_pos+ZIPCVER);
356 zip->ent.host_os = *(zip->cd+zip->cd_pos+ZIPCOS);
357 zip->ent.version_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCVXT);
358 zip->ent.os_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCEXOS);
359 zip->ent.general_purpose_bit_flag = read_word (zip->cd+zip->cd_pos+ZIPCFLG);
360 zip->ent.compression_method = read_word (zip->cd+zip->cd_pos+ZIPCMTHD);
361 zip->ent.last_mod_file_time = read_word (zip->cd+zip->cd_pos+ZIPCTIM);
362 zip->ent.last_mod_file_date = read_word (zip->cd+zip->cd_pos+ZIPCDAT);
363 zip->ent.crc32 = read_dword (zip->cd+zip->cd_pos+ZIPCCRC);
364 zip->ent.compressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCSIZ);
365 zip->ent.uncompressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCUNC);
366 zip->ent.filename_length = read_word (zip->cd+zip->cd_pos+ZIPCFNL);
367 zip->ent.extra_field_length = read_word (zip->cd+zip->cd_pos+ZIPCXTL);
368 zip->ent.file_comment_length = read_word (zip->cd+zip->cd_pos+ZIPCCML);
369 zip->ent.disk_number_start = read_word (zip->cd+zip->cd_pos+ZIPDSK);
370 zip->ent.internal_file_attrib = read_word (zip->cd+zip->cd_pos+ZIPINT);
371 zip->ent.external_file_attrib = read_dword (zip->cd+zip->cd_pos+ZIPEXT);
372 zip->ent.offset_lcl_hdr_frm_frst_disk = read_dword (zip->cd+zip->cd_pos+ZIPOFST);
374 /* check to see if filename length is illegally long (past the size of this directory
376 if (zip->cd_pos + ZIPCFN + zip->ent.filename_length > zip->size_of_cent_dir)
378 errormsg("Invalid filename length in directory", ERROR_CORRUPT,zip->zip);
384 zip->ent.name = (char*)malloc(zip->ent.filename_length + 1);
385 memcpy(zip->ent.name, zip->cd+zip->cd_pos+ZIPCFN, zip->ent.filename_length);
386 zip->ent.name[zip->ent.filename_length] = 0;
388 /* skip to next entry in central dir */
389 zip->cd_pos += ZIPCFN + zip->ent.filename_length + zip->ent.extra_field_length + zip->ent.file_comment_length;
394 /* Closes a zip stream */
395 void closezip(ZIP * zip)
402 /* only if not suspended */
410 /* Suspend access to a zip file (release file handler)
414 A suspended zip is automatically reopened at first call of
415 readuncompressd() or readcompressed() functions
417 void suspendzip(ZIP * zip)
426 /* Revive a suspended zip file (reopen file handler)
431 ==0 error (zip must be closed with closezip)
433 static ZIP * revivezip(ZIP * zip)
437 zip->fp = fopen(zip->zip, "rb");
445 /* Reset a zip stream to the first entry
449 ZIP file must be opened and not suspended
451 void rewindzip(ZIP * zip)
457 // Seek zip->fp to compressed data
462 int seekcompresszip(ZIP * zip, struct zipent * ent)
473 if (fseek(zip->fp, ent->offset_lcl_hdr_frm_frst_disk, SEEK_SET) != 0)
475 errormsg("Seeking to header", ERROR_CORRUPT, zip->zip);
479 if (fread(buf, 1, ZIPNAME, zip->fp) != ZIPNAME)
481 errormsg("Reading header", ERROR_CORRUPT, zip->zip);
486 uint16_t filename_length = read_word(buf+ZIPFNLN);
487 uint16_t extra_field_length = read_word(buf+ZIPXTRALN);
489 // calculate offset to data and fseek() there
490 offset = ent->offset_lcl_hdr_frm_frst_disk + ZIPNAME + filename_length + extra_field_length;
492 if (fseek(zip->fp, offset, SEEK_SET) != 0)
494 errormsg("Seeking to compressed data", ERROR_CORRUPT, zip->zip);
506 // in_file stream to inflate
507 // in_size size of the compressed data to read
509 // out_size size of decompressed data
510 // out_data buffer for decompressed data
514 // 990525 rewritten for use with zlib MLR
516 static int inflate_file(FILE * in_file, unsigned in_size, unsigned char * out_data, unsigned out_size)
519 unsigned char * in_buffer;
520 z_stream d_stream; // decompression stream
526 d_stream.next_in = 0;
527 d_stream.avail_in = 0;
528 d_stream.next_out = out_data;
529 d_stream.avail_out = out_size;
531 err = inflateInit2(&d_stream, -MAX_WBITS);
532 /* windowBits is passed < 0 to tell that there is no zlib header.
533 * Note that in this case inflate *requires* an extra "dummy" byte
534 * after the compressed stream in order to complete decompression and
535 * return Z_STREAM_END.
539 WriteLog("inflateInit error: %d\n", err);
543 in_buffer = (unsigned char*)malloc(INFLATE_INPUT_BUFFER_MAX+1);
551 WriteLog("inflate error: compressed size too small\n");
555 d_stream.next_in = in_buffer;
556 d_stream.avail_in = fread(in_buffer, 1, MIN(in_size, INFLATE_INPUT_BUFFER_MAX), in_file);
557 in_size -= d_stream.avail_in;
559 d_stream.avail_in++; /* add dummy byte at end of compressed data */
561 err = inflate(&d_stream, Z_NO_FLUSH);
562 if (err == Z_STREAM_END)
566 WriteLog("inflate error: %d\n", err);
572 err = inflateEnd(&d_stream);
575 WriteLog("inflateEnd error: %d\n", err);
582 if ((d_stream.avail_out > 0) || (in_size > 0))
584 WriteLog("zip size mismatch. %i\n", in_size);
592 // Read compressed data
594 // data compressed data read
599 int readcompresszip(ZIP * zip, struct zipent * ent, char * data)
601 int err = seekcompresszip(zip, ent);
605 if (fread(data, 1, ent->compressed_size, zip->fp) != ent->compressed_size)
607 errormsg("Reading compressed data", ERROR_CORRUPT, zip->zip);
615 // Read UNcompressed data
617 // data UNcompressed data
622 int readuncompresszip(ZIP * zip, struct zipent * ent, char * data)
624 if (ent->compression_method == 0x0000)
626 /* file is not compressed, simply stored */
628 /* check if size are equal */
629 if (ent->compressed_size != ent->uncompressed_size)
631 errormsg("Wrong uncompressed size in store compression", ERROR_CORRUPT,zip->zip);
635 return readcompresszip(zip, ent, data);
637 else if (ent->compression_method == 0x0008)
639 /* file is compressed using "Deflate" method */
640 if (ent->version_needed_to_extract > 0x14)
642 errormsg("Version too new", ERROR_UNSUPPORTED, zip->zip);
646 if (ent->os_needed_to_extract != 0x00)
648 errormsg("OS not supported", ERROR_UNSUPPORTED, zip->zip);
652 if (ent->disk_number_start != zip->number_of_this_disk)
654 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zip->zip);
658 /* read compressed data */
659 if (seekcompresszip(zip, ent) != 0)
664 /* configure inflate */
665 if (inflate_file(zip->fp, ent->compressed_size, (unsigned char *)data, ent->uncompressed_size))
667 errormsg("Inflating compressed data", ERROR_CORRUPT, zip->zip);
675 errormsg("Compression method unsupported", ERROR_UNSUPPORTED, zip->zip);
680 /* -------------------------------------------------------------------------
681 Backward MAME compatibility
682 ------------------------------------------------------------------------- */
685 // Compare two filenames
686 // NOTE: Don't check directory in zip and ignore case
688 static int equal_filename(const char * zipfile, const char * file)
690 // const char * s1 = file;
691 // start comparison after last / also...!
692 const char * s1 = strrchr(file, '/');
697 // start comparison after last /
698 const char * s2 = strrchr(zipfile, '/');
703 //WriteLog("--> Comparing filenames: [%s] <-> [%s]\n", s1, s2);
705 // This assumes that s1 is longer than s2... Might not be! !!! FIX !!!
706 while (*s1 && toupper(*s1) == toupper(*s2))
716 // Pass the path to the zipfile and the name of the file within the zipfile.
717 // buf is set to point to the uncompressed image of that zipped file (preallocated by user!).
718 // length will be set to the length of the uncompressed data.
720 int load_zipped_file(int pathtype, int pathindex, const char * zipfile, const char * filename, unsigned char ** buf, uint32_t * length)
722 ZIP * zip = openzip(pathtype, pathindex, zipfile);
729 /* NS981003: support for "load by CRC" */
732 struct zipent * ent = &(zip->ent);
733 sprintf(crc, "%08x", (unsigned int)ent->crc32);
735 if (filename == NULL || equal_filename(ent->name, filename)
736 || (ent->crc32 && !strcmp(crc, filename)))
738 *length = ent->uncompressed_size;
740 if (readuncompresszip(zip, ent, (char *)*buf) != 0)