2 // ZIP file support (mostly ripped from MAME)
4 // Added by James L. Hammons
17 int gUnzipQuiet = 0; /* flag controls error messages */
20 #define ERROR_CORRUPT "The zipfile seems to be corrupt, please check it"
21 #define ERROR_FILESYSTEM "Your filesystem seems to be corrupt, please check it"
22 #define ERROR_UNSUPPORTED "The format of this zipfile is not supported, please recompress it"
24 #define INFLATE_INPUT_BUFFER_MAX 16384
26 #define MIN(x,y) ((x)<(y)?(x):(y))
29 /* Print a error message */
30 void errormsg(const char * extmsg, const char * usermsg, const char * zipname)
32 /* Output to the user with no internal detail */
34 printf("Error in zipfile %s\n%s\n", zipname, usermsg);
35 /* Output to log file with all informations */
36 WriteLog("Error in zipfile %s: %s\n", zipname, extmsg);
39 /* -------------------------------------------------------------------------
41 ------------------------------------------------------------------------- */
43 /* Use these to avoid structure padding and byte-ordering problems */
44 static UINT16 read_word (char *buf) {
45 unsigned char *ubuf = (unsigned char *) buf;
47 return ((UINT16)ubuf[1] << 8) | (UINT16)ubuf[0];
50 /* Use these to avoid structure padding and byte-ordering problems */
51 static UINT32 read_dword (char *buf) {
52 unsigned char *ubuf = (unsigned char *) buf;
54 return ((UINT32)ubuf[3] << 24) | ((UINT32)ubuf[2] << 16) | ((UINT32)ubuf[1] << 8) | (UINT32)ubuf[0];
57 /* Locate end-of-central-dir sig in buffer and return offset
59 *offset offset of cent dir start in buffer
62 !=0 found, *offset valid
64 static int ecd_find_sig (char *buffer, int buflen, int *offset)
66 static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };
68 for (i=buflen-22; i>=0; i--) {
69 if (memcmp(buffer+i, ecdsig, 4) == 0) {
77 /* Read ecd data in zip structure
79 zip->fp, zip->length zip file
81 zip->ecd, zip->ecd_length ecd data
83 static int ecd_read(ZIP * zip)
86 int buf_length = 1024; /* initial buffer length */
92 if (buf_length > zip->length)
93 buf_length = zip->length;
95 if (fseek(zip->fp, zip->length - buf_length, SEEK_SET) != 0)
100 /* allocate buffer */
101 buf = (char *)malloc(buf_length);
107 if (fread(buf, 1, buf_length, zip->fp) != buf_length)
113 if (ecd_find_sig(buf, buf_length, &offset)) {
114 zip->ecd_length = buf_length - offset;
116 zip->ecd = (char*)malloc( zip->ecd_length );
122 memcpy(zip->ecd, buf + offset, zip->ecd_length);
130 if (buf_length < zip->length) {
132 buf_length = 2*buf_length;
134 WriteLog("Retry reading of zip ecd for %d bytes\n",buf_length);
142 /* offsets in end of central directory structure */
147 #define ZIPECENN 0x0a
149 #define ZIPEOFST 0x10
150 #define ZIPECOML 0x14
153 /* offsets in central directory entry structure */
154 #define ZIPCENSIG 0x0
175 /* offsets in local file header structure */
176 #define ZIPLOCSIG 0x00
178 #define ZIPGENFLG 0x06
184 #define ZIPUNCMP 0x16
186 #define ZIPXTRALN 0x1c
190 // Opens a zip stream for reading
192 // !=0 success, zip stream
195 ZIP * openzip(int pathtype, int pathindex, const char * zipfile)
198 ZIP * zip = (ZIP *)malloc(sizeof(ZIP));
205 zip->fp = fopen(zipfile, "rb");
208 errormsg("Opening for reading", ERROR_FILESYSTEM, zipfile);
214 if (fseek(zip->fp, 0L, SEEK_END) != 0)
216 errormsg ("Seeking to end", ERROR_FILESYSTEM, zipfile);
223 zip->length = ftell(zip->fp);
226 errormsg("Get file size", ERROR_FILESYSTEM, zipfile);
231 if (zip->length == 0)
233 errormsg ("Empty file", ERROR_CORRUPT, zipfile);
240 if (ecd_read(zip) != 0)
242 errormsg("Reading ECD (end of central directory)", ERROR_CORRUPT, zipfile);
248 /* compile ecd info */
249 zip->end_of_cent_dir_sig = read_dword (zip->ecd+ZIPESIG);
250 zip->number_of_this_disk = read_word (zip->ecd+ZIPEDSK);
251 zip->number_of_disk_start_cent_dir = read_word (zip->ecd+ZIPECEN);
252 zip->total_entries_cent_dir_this_disk = read_word (zip->ecd+ZIPENUM);
253 zip->total_entries_cent_dir = read_word (zip->ecd+ZIPECENN);
254 zip->size_of_cent_dir = read_dword (zip->ecd+ZIPECSZ);
255 zip->offset_to_start_of_cent_dir = read_dword (zip->ecd+ZIPEOFST);
256 zip->zipfile_comment_length = read_word (zip->ecd+ZIPECOML);
257 zip->zipfile_comment = zip->ecd+ZIPECOM;
259 /* verify that we can work with this zipfile (no disk spanning allowed) */
260 if ((zip->number_of_this_disk != zip->number_of_disk_start_cent_dir) ||
261 (zip->total_entries_cent_dir_this_disk != zip->total_entries_cent_dir) ||
262 (zip->total_entries_cent_dir < 1))
264 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zipfile);
271 if (fseek(zip->fp, zip->offset_to_start_of_cent_dir, SEEK_SET) != 0)
273 errormsg("Seeking to central directory", ERROR_CORRUPT, zipfile);
280 /* read from start of central directory */
281 zip->cd = (char *)malloc(zip->size_of_cent_dir);
290 if (fread(zip->cd, 1, zip->size_of_cent_dir, zip->fp) != zip->size_of_cent_dir)
292 errormsg("Reading central directory", ERROR_CORRUPT, zipfile);
307 zip->zip = (char*)malloc(strlen(zipfile)+1);
316 strcpy(zip->zip, zipfile);
317 zip->pathtype = pathtype;
318 zip->pathindex = pathindex;
323 /* Reads the current entry from a zip stream
330 struct zipent* readzip(ZIP* zip) {
332 /* end of directory */
333 if (zip->cd_pos >= zip->size_of_cent_dir)
336 /* compile zipent info */
337 zip->ent.cent_file_header_sig = read_dword (zip->cd+zip->cd_pos+ZIPCENSIG);
338 zip->ent.version_made_by = *(zip->cd+zip->cd_pos+ZIPCVER);
339 zip->ent.host_os = *(zip->cd+zip->cd_pos+ZIPCOS);
340 zip->ent.version_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCVXT);
341 zip->ent.os_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCEXOS);
342 zip->ent.general_purpose_bit_flag = read_word (zip->cd+zip->cd_pos+ZIPCFLG);
343 zip->ent.compression_method = read_word (zip->cd+zip->cd_pos+ZIPCMTHD);
344 zip->ent.last_mod_file_time = read_word (zip->cd+zip->cd_pos+ZIPCTIM);
345 zip->ent.last_mod_file_date = read_word (zip->cd+zip->cd_pos+ZIPCDAT);
346 zip->ent.crc32 = read_dword (zip->cd+zip->cd_pos+ZIPCCRC);
347 zip->ent.compressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCSIZ);
348 zip->ent.uncompressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCUNC);
349 zip->ent.filename_length = read_word (zip->cd+zip->cd_pos+ZIPCFNL);
350 zip->ent.extra_field_length = read_word (zip->cd+zip->cd_pos+ZIPCXTL);
351 zip->ent.file_comment_length = read_word (zip->cd+zip->cd_pos+ZIPCCML);
352 zip->ent.disk_number_start = read_word (zip->cd+zip->cd_pos+ZIPDSK);
353 zip->ent.internal_file_attrib = read_word (zip->cd+zip->cd_pos+ZIPINT);
354 zip->ent.external_file_attrib = read_dword (zip->cd+zip->cd_pos+ZIPEXT);
355 zip->ent.offset_lcl_hdr_frm_frst_disk = read_dword (zip->cd+zip->cd_pos+ZIPOFST);
357 /* check to see if filename length is illegally long (past the size of this directory
359 if (zip->cd_pos + ZIPCFN + zip->ent.filename_length > zip->size_of_cent_dir)
361 errormsg("Invalid filename length in directory", ERROR_CORRUPT,zip->zip);
367 zip->ent.name = (char*)malloc(zip->ent.filename_length + 1);
368 memcpy(zip->ent.name, zip->cd+zip->cd_pos+ZIPCFN, zip->ent.filename_length);
369 zip->ent.name[zip->ent.filename_length] = 0;
371 /* skip to next entry in central dir */
372 zip->cd_pos += ZIPCFN + zip->ent.filename_length + zip->ent.extra_field_length + zip->ent.file_comment_length;
377 /* Closes a zip stream */
378 void closezip(ZIP * zip)
384 /* only if not suspended */
391 /* Suspend access to a zip file (release file handler)
395 A suspended zip is automatically reopened at first call of
396 readuncompressd() or readcompressed() functions
398 void suspendzip(ZIP * zip)
407 /* Revive a suspended zip file (reopen file handler)
412 ==0 error (zip must be closed with closezip)
414 static ZIP * revivezip(ZIP * zip)
418 zip->fp = fopen(zip->zip, "rb");
426 /* Reset a zip stream to the first entry
430 ZIP file must be opened and not suspended
432 void rewindzip(ZIP * zip)
438 // Seek zip->fp to compressed data
443 int seekcompresszip(ZIP * zip, struct zipent * ent)
454 if (fseek(zip->fp, ent->offset_lcl_hdr_frm_frst_disk, SEEK_SET) != 0)
456 errormsg("Seeking to header", ERROR_CORRUPT, zip->zip);
460 if (fread(buf, 1, ZIPNAME, zip->fp) != ZIPNAME)
462 errormsg("Reading header", ERROR_CORRUPT, zip->zip);
467 UINT16 filename_length = read_word(buf+ZIPFNLN);
468 UINT16 extra_field_length = read_word(buf+ZIPXTRALN);
470 // calculate offset to data and fseek() there
471 offset = ent->offset_lcl_hdr_frm_frst_disk + ZIPNAME + filename_length + extra_field_length;
473 if (fseek(zip->fp, offset, SEEK_SET) != 0)
475 errormsg("Seeking to compressed data", ERROR_CORRUPT, zip->zip);
487 // in_file stream to inflate
488 // in_size size of the compressed data to read
490 // out_size size of decompressed data
491 // out_data buffer for decompressed data
495 // 990525 rewritten for use with zlib MLR
497 static int inflate_file(FILE * in_file, unsigned in_size, unsigned char * out_data, unsigned out_size)
500 unsigned char * in_buffer;
501 z_stream d_stream; // decompression stream
507 d_stream.next_in = 0;
508 d_stream.avail_in = 0;
509 d_stream.next_out = out_data;
510 d_stream.avail_out = out_size;
512 err = inflateInit2(&d_stream, -MAX_WBITS);
513 /* windowBits is passed < 0 to tell that there is no zlib header.
514 * Note that in this case inflate *requires* an extra "dummy" byte
515 * after the compressed stream in order to complete decompression and
516 * return Z_STREAM_END.
520 WriteLog("inflateInit error: %d\n", err);
524 in_buffer = (unsigned char*)malloc(INFLATE_INPUT_BUFFER_MAX+1);
532 WriteLog("inflate error: compressed size too small\n");
536 d_stream.next_in = in_buffer;
537 d_stream.avail_in = fread(in_buffer, 1, MIN(in_size, INFLATE_INPUT_BUFFER_MAX), in_file);
538 in_size -= d_stream.avail_in;
540 d_stream.avail_in++; /* add dummy byte at end of compressed data */
542 err = inflate(&d_stream, Z_NO_FLUSH);
543 if (err == Z_STREAM_END)
547 WriteLog("inflate error: %d\n", err);
553 err = inflateEnd(&d_stream);
556 WriteLog("inflateEnd error: %d\n", err);
563 if ((d_stream.avail_out > 0) || (in_size > 0))
565 WriteLog("zip size mismatch. %i\n", in_size);
573 // Read compressed data
575 // data compressed data read
580 int readcompresszip(ZIP * zip, struct zipent * ent, char * data)
582 int err = seekcompresszip(zip, ent);
586 if (fread(data, 1, ent->compressed_size, zip->fp) != ent->compressed_size)
588 errormsg("Reading compressed data", ERROR_CORRUPT, zip->zip);
596 // Read UNcompressed data
598 // data UNcompressed data
603 int readuncompresszip(ZIP * zip, struct zipent * ent, char * data)
605 if (ent->compression_method == 0x0000)
607 /* file is not compressed, simply stored */
609 /* check if size are equal */
610 if (ent->compressed_size != ent->uncompressed_size)
612 errormsg("Wrong uncompressed size in store compression", ERROR_CORRUPT,zip->zip);
616 return readcompresszip(zip, ent, data);
618 else if (ent->compression_method == 0x0008)
620 /* file is compressed using "Deflate" method */
621 if (ent->version_needed_to_extract > 0x14)
623 errormsg("Version too new", ERROR_UNSUPPORTED, zip->zip);
627 if (ent->os_needed_to_extract != 0x00)
629 errormsg("OS not supported", ERROR_UNSUPPORTED, zip->zip);
633 if (ent->disk_number_start != zip->number_of_this_disk)
635 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zip->zip);
639 /* read compressed data */
640 if (seekcompresszip(zip, ent) != 0)
645 /* configure inflate */
646 if (inflate_file(zip->fp, ent->compressed_size, (unsigned char *)data, ent->uncompressed_size))
648 errormsg("Inflating compressed data", ERROR_CORRUPT, zip->zip);
656 errormsg("Compression method unsupported", ERROR_UNSUPPORTED, zip->zip);
661 /* -------------------------------------------------------------------------
663 ------------------------------------------------------------------------- */
665 /* Use the zip cache */
671 /* ZIP cache entries */
672 #define ZIP_CACHE_MAX 5
674 /* ZIP cache buffer LRU ( Last Recently Used )
675 zip_cache_map[0] is the newer
676 zip_cache_map[ZIP_CACHE_MAX-1] is the older
678 static ZIP* zip_cache_map[ZIP_CACHE_MAX];
680 static ZIP* cache_openzip(int pathtype, int pathindex, const char* zipfile) {
684 /* search in the cache buffer */
685 for(i=0;i<ZIP_CACHE_MAX;++i) {
686 if (zip_cache_map[i] && zip_cache_map[i]->pathtype == pathtype && zip_cache_map[i]->pathindex == pathindex && strcmp(zip_cache_map[i]->zip,zipfile)==0) {
691 WriteLog("Zip cache HIT for %s\n", zipfile);
694 /* reset the zip directory */
695 rewindzip( zip_cache_map[i] );
698 zip = zip_cache_map[i];
702 zip_cache_map[j] = zip_cache_map[j-1];
704 /* set the first entry */
705 zip_cache_map[0] = zip;
707 return zip_cache_map[0];
713 WriteLog("Zip cache FAIL for %s\n", zipfile);
717 zip = openzip( pathtype, pathindex, zipfile );
721 /* close the oldest entry */
722 if (zip_cache_map[ZIP_CACHE_MAX-1]) {
724 closezip(zip_cache_map[ZIP_CACHE_MAX-1]);
725 /* reset the entry */
726 zip_cache_map[ZIP_CACHE_MAX-1] = 0;
730 for(i=ZIP_CACHE_MAX-1;i>0;--i)
731 zip_cache_map[i] = zip_cache_map[i-1];
733 /* set the first entry */
734 zip_cache_map[0] = zip;
736 return zip_cache_map[0];
739 static void cache_closezip(ZIP* zip) {
742 /* search in the cache buffer */
743 for(i=0;i<ZIP_CACHE_MAX;++i) {
744 if (zip_cache_map[i]==zip) {
748 /* reset cache entry */
749 zip_cache_map[i] = 0;
760 /* CK980415 added to allow osd code to clear zip cache for auditing--each time
761 the user opens up an audit for a game we should reread the zip */
762 void unzip_cache_clear()
766 /* search in the cache buffer for any zip info and clear it */
767 for(i=0;i<ZIP_CACHE_MAX;++i) {
768 if (zip_cache_map[i] != NULL) {
770 closezip(zip_cache_map[i]);
772 /* reset cache entry */
773 zip_cache_map[i] = 0;
780 #define cache_suspendzip(a) suspendzip(a)
784 #define cache_openzip(a,b,c) openzip(a,b,c)
785 #define cache_closezip(a) closezip(a)
786 #define cache_suspendzip(a) closezip(a)
788 #define unzip_cache_clear()
792 /* -------------------------------------------------------------------------
793 Backward MAME compatibility
794 ------------------------------------------------------------------------- */
797 // Compare two filenames
798 // NOTE: Don't check directory in zip and ignore case
800 static int equal_filename(const char * zipfile, const char * file)
802 // const char * s1 = file;
803 // start comparison after last / also...!
804 const char * s1 = strrchr(file, '/');
809 // start comparison after last /
810 const char * s2 = strrchr(zipfile, '/');
815 //WriteLog("--> Comparing filenames: [%s] <-> [%s]\n", s1, s2);
817 // This assumes that s1 is longer than s2... Might not be! !!! FIX !!!
818 while (*s1 && toupper(*s1) == toupper(*s2))
828 // Pass the path to the zipfile and the name of the file within the zipfile.
829 // buf will be set to point to the uncompressed image of that zipped file.
830 // length will be set to the length of the uncompressed data.
832 int load_zipped_file(int pathtype, int pathindex, const char * zipfile, const char * filename, unsigned char ** buf, uint32 * length)
837 zip = cache_openzip(pathtype, pathindex, zipfile);
843 /* NS981003: support for "load by CRC" */
847 sprintf(crc, "%08x", (unsigned int)ent->crc32);
849 if (filename == NULL || equal_filename(ent->name, filename)
850 || (ent->crc32 && !strcmp(crc, filename)))
852 *length = ent->uncompressed_size;
854 if (readuncompresszip(zip, ent, (char *)*buf) != 0)
860 cache_suspendzip(zip);
865 cache_suspendzip(zip);