]> Shamusworld >> Repos - virtualjaguar/blob - src/unzip.c
Start of model/view class implementation, since QListViewWidget ultimately
[virtualjaguar] / src / unzip.c
1 //
2 // ZIP file support (mostly ripped from MAME--thx MAME team!)
3 //
4 // Added by James L. Hammons
5 // (C) 2010 Underground Software
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  01/16/2010  Created this log ;-)
12 //
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <assert.h>
18 #include <zlib.h>
19
20 #include "unzip.h"
21 #include "log.h"
22
23 /* public globals */
24 int     gUnzipQuiet = 0;                /* flag controls error messages */
25
26
27 #define ERROR_CORRUPT "The zipfile seems to be corrupt, please check it"
28 #define ERROR_FILESYSTEM "Your filesystem seems to be corrupt, please check it"
29 #define ERROR_UNSUPPORTED "The format of this zipfile is not supported, please recompress it"
30
31 #define INFLATE_INPUT_BUFFER_MAX 16384
32 #ifndef MIN
33 #define MIN(x,y) ((x)<(y)?(x):(y))
34 #endif
35
36 /* Print a error message */
37 void errormsg(const char * extmsg, const char * usermsg, const char * zipname)
38 {
39         /* Output to the user with no internal detail */
40         if (!gUnzipQuiet)
41                 printf("Error in zipfile %s\n%s\n", zipname, usermsg);
42         /* Output to log file with all informations */
43         WriteLog("Error in zipfile %s: %s\n", zipname, extmsg);
44 }
45
46 /* -------------------------------------------------------------------------
47    Unzip support
48  ------------------------------------------------------------------------- */
49
50 /* Use these to avoid structure padding and byte-ordering problems */
51 static uint16_t read_word (char *buf) {
52    unsigned char *ubuf = (unsigned char *) buf;
53
54    return ((uint16_t)ubuf[1] << 8) | (uint16_t)ubuf[0];
55 }
56
57 /* Use these to avoid structure padding and byte-ordering problems */
58 static uint32_t read_dword (char *buf) {
59    unsigned char *ubuf = (unsigned char *) buf;
60
61    return ((uint32_t)ubuf[3] << 24) | ((uint32_t)ubuf[2] << 16) | ((uint32_t)ubuf[1] << 8) | (uint32_t)ubuf[0];
62 }
63
64 /* Locate end-of-central-dir sig in buffer and return offset
65    out:
66         *offset offset of cent dir start in buffer
67    return:
68         ==0 not found
69         !=0 found, *offset valid
70 */
71 static int ecd_find_sig (char *buffer, int buflen, int *offset)
72 {
73         static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };
74         int i;
75         for (i=buflen-22; i>=0; i--) {
76                 if (memcmp(buffer+i, ecdsig, 4) == 0) {
77                         *offset = i;
78                         return 1;
79                 }
80         }
81         return 0;
82 }
83
84 /* Read ecd data in zip structure
85    in:
86      zip->fp, zip->length zip file
87    out:
88      zip->ecd, zip->ecd_length ecd data
89 */
90 static int ecd_read(ZIP * zip)
91 {
92         char * buf;
93         int buf_length = 1024; /* initial buffer length */
94
95         while (1)
96         {
97                 int offset;
98
99                 if (buf_length > zip->length)
100                         buf_length = zip->length;
101
102                 if (fseek(zip->fp, zip->length - buf_length, SEEK_SET) != 0)
103                 {
104                         return -1;
105                 }
106
107                 /* allocate buffer */
108                 buf = (char *)malloc(buf_length);
109                 if (!buf)
110                 {
111                         return -1;
112                 }
113
114                 if (fread(buf, 1, buf_length, zip->fp) != buf_length)
115                 {
116                         free(buf);
117                         return -1;
118                 }
119
120                 if (ecd_find_sig(buf, buf_length, &offset)) {
121                         zip->ecd_length = buf_length - offset;
122
123                         zip->ecd = (char*)malloc( zip->ecd_length );
124                         if (!zip->ecd) {
125                                 free(buf);
126                                 return -1;
127                         }
128
129                         memcpy(zip->ecd, buf + offset, zip->ecd_length);
130
131                         free(buf);
132                         return 0;
133                 }
134
135                 free(buf);
136
137                 if (buf_length < zip->length) {
138                         /* double buffer */
139                         buf_length = 2*buf_length;
140
141                         WriteLog("Retry reading of zip ecd for %d bytes\n",buf_length);
142
143                 } else {
144                         return -1;
145                 }
146         }
147 }
148
149 /* offsets in end of central directory structure */
150 #define ZIPESIG         0x00
151 #define ZIPEDSK         0x04
152 #define ZIPECEN         0x06
153 #define ZIPENUM         0x08
154 #define ZIPECENN        0x0a
155 #define ZIPECSZ         0x0c
156 #define ZIPEOFST        0x10
157 #define ZIPECOML        0x14
158 #define ZIPECOM         0x16
159
160 /* offsets in central directory entry structure */
161 #define ZIPCENSIG       0x0
162 #define ZIPCVER         0x4
163 #define ZIPCOS          0x5
164 #define ZIPCVXT         0x6
165 #define ZIPCEXOS        0x7
166 #define ZIPCFLG         0x8
167 #define ZIPCMTHD        0xa
168 #define ZIPCTIM         0xc
169 #define ZIPCDAT         0xe
170 #define ZIPCCRC         0x10
171 #define ZIPCSIZ         0x14
172 #define ZIPCUNC         0x18
173 #define ZIPCFNL         0x1c
174 #define ZIPCXTL         0x1e
175 #define ZIPCCML         0x20
176 #define ZIPDSK          0x22
177 #define ZIPINT          0x24
178 #define ZIPEXT          0x26
179 #define ZIPOFST         0x2a
180 #define ZIPCFN          0x2e
181
182 /* offsets in local file header structure */
183 #define ZIPLOCSIG       0x00
184 #define ZIPVER          0x04
185 #define ZIPGENFLG       0x06
186 #define ZIPMTHD         0x08
187 #define ZIPTIME         0x0a
188 #define ZIPDATE         0x0c
189 #define ZIPCRC          0x0e
190 #define ZIPSIZE         0x12
191 #define ZIPUNCMP        0x16
192 #define ZIPFNLN         0x1a
193 #define ZIPXTRALN       0x1c
194 #define ZIPNAME         0x1e
195
196 //
197 // Opens a zip stream for reading
198 //  return:
199 //     !=0 success, zip stream
200 //     ==0 error
201 //
202 ZIP * openzip(int pathtype, int pathindex, const char * zipfile)
203 {
204         /* allocate */
205         ZIP * zip = (ZIP *)malloc(sizeof(ZIP));
206         if (!zip)
207         {
208                 return 0;
209         }
210
211         /* open */
212         zip->fp = fopen(zipfile, "rb");
213         if (!zip->fp)
214         {
215                 errormsg("Opening for reading", ERROR_FILESYSTEM, zipfile);
216                 free(zip);
217                 return 0;
218         }
219
220         /* go to end */
221         if (fseek(zip->fp, 0L, SEEK_END) != 0)
222         {
223                 errormsg ("Seeking to end", ERROR_FILESYSTEM, zipfile);
224                 fclose(zip->fp);
225                 free(zip);
226                 return 0;
227         }
228
229         /* get length */
230         zip->length = ftell(zip->fp);
231         if (zip->length < 0)
232         {
233                 errormsg("Get file size", ERROR_FILESYSTEM, zipfile);
234                 fclose(zip->fp);
235                 free(zip);
236                 return 0;
237         }
238         if (zip->length == 0)
239         {
240                 errormsg ("Empty file", ERROR_CORRUPT, zipfile);
241                 fclose(zip->fp);
242                 free(zip);
243                 return 0;
244         }
245
246         /* read ecd data */
247         if (ecd_read(zip) != 0)
248         {
249                 errormsg("Reading ECD (end of central directory)", ERROR_CORRUPT, zipfile);
250                 fclose(zip->fp);
251                 free(zip);
252                 return 0;
253         }
254
255         /* compile ecd info */
256         zip->end_of_cent_dir_sig = read_dword (zip->ecd+ZIPESIG);
257         zip->number_of_this_disk = read_word (zip->ecd+ZIPEDSK);
258         zip->number_of_disk_start_cent_dir = read_word (zip->ecd+ZIPECEN);
259         zip->total_entries_cent_dir_this_disk = read_word (zip->ecd+ZIPENUM);
260         zip->total_entries_cent_dir = read_word (zip->ecd+ZIPECENN);
261         zip->size_of_cent_dir = read_dword (zip->ecd+ZIPECSZ);
262         zip->offset_to_start_of_cent_dir = read_dword (zip->ecd+ZIPEOFST);
263         zip->zipfile_comment_length = read_word (zip->ecd+ZIPECOML);
264         zip->zipfile_comment = zip->ecd+ZIPECOM;
265
266         /* verify that we can work with this zipfile (no disk spanning allowed) */
267         if ((zip->number_of_this_disk != zip->number_of_disk_start_cent_dir) ||
268                 (zip->total_entries_cent_dir_this_disk != zip->total_entries_cent_dir) ||
269                 (zip->total_entries_cent_dir < 1))
270         {
271                 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zipfile);
272                 free(zip->ecd);
273                 fclose(zip->fp);
274                 free(zip);
275                 return 0;
276         }
277
278         if (fseek(zip->fp, zip->offset_to_start_of_cent_dir, SEEK_SET) != 0)
279         {
280                 errormsg("Seeking to central directory", ERROR_CORRUPT, zipfile);
281                 free(zip->ecd);
282                 fclose(zip->fp);
283                 free(zip);
284                 return 0;
285         }
286
287         /* read from start of central directory */
288         zip->cd = (char *)malloc(zip->size_of_cent_dir);
289         if (!zip->cd)
290         {
291                 free(zip->ecd);
292                 fclose(zip->fp);
293                 free(zip);
294                 return 0;
295         }
296
297         if (fread(zip->cd, 1, zip->size_of_cent_dir, zip->fp) != zip->size_of_cent_dir)
298         {
299                 errormsg("Reading central directory", ERROR_CORRUPT, zipfile);
300                 free(zip->cd);
301                 free(zip->ecd);
302                 fclose(zip->fp);
303                 free(zip);
304                 return 0;
305         }
306
307         /* reset ent */
308         zip->ent.name = 0;
309
310         /* rewind */
311         zip->cd_pos = 0;
312
313         /* file name */
314         zip->zip = (char*)malloc(strlen(zipfile)+1);
315         if (!zip->zip)
316         {
317                 free(zip->cd);
318                 free(zip->ecd);
319                 fclose(zip->fp);
320                 free(zip);
321                 return 0;
322         }
323         strcpy(zip->zip, zipfile);
324         zip->pathtype = pathtype;
325         zip->pathindex = pathindex;
326
327         return zip;
328 }
329
330 /* Reads the current entry from a zip stream
331    in:
332      zip opened zip
333    return:
334      !=0 success
335      ==0 error
336 */
337 struct zipent* readzip(ZIP* zip) {
338
339         /* end of directory */
340         if (zip->cd_pos >= zip->size_of_cent_dir)
341                 return 0;
342
343         /* compile zipent info */
344         zip->ent.cent_file_header_sig = read_dword (zip->cd+zip->cd_pos+ZIPCENSIG);
345         zip->ent.version_made_by = *(zip->cd+zip->cd_pos+ZIPCVER);
346         zip->ent.host_os = *(zip->cd+zip->cd_pos+ZIPCOS);
347         zip->ent.version_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCVXT);
348         zip->ent.os_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCEXOS);
349         zip->ent.general_purpose_bit_flag = read_word (zip->cd+zip->cd_pos+ZIPCFLG);
350         zip->ent.compression_method = read_word (zip->cd+zip->cd_pos+ZIPCMTHD);
351         zip->ent.last_mod_file_time = read_word (zip->cd+zip->cd_pos+ZIPCTIM);
352         zip->ent.last_mod_file_date = read_word (zip->cd+zip->cd_pos+ZIPCDAT);
353         zip->ent.crc32 = read_dword (zip->cd+zip->cd_pos+ZIPCCRC);
354         zip->ent.compressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCSIZ);
355         zip->ent.uncompressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCUNC);
356         zip->ent.filename_length = read_word (zip->cd+zip->cd_pos+ZIPCFNL);
357         zip->ent.extra_field_length = read_word (zip->cd+zip->cd_pos+ZIPCXTL);
358         zip->ent.file_comment_length = read_word (zip->cd+zip->cd_pos+ZIPCCML);
359         zip->ent.disk_number_start = read_word (zip->cd+zip->cd_pos+ZIPDSK);
360         zip->ent.internal_file_attrib = read_word (zip->cd+zip->cd_pos+ZIPINT);
361         zip->ent.external_file_attrib = read_dword (zip->cd+zip->cd_pos+ZIPEXT);
362         zip->ent.offset_lcl_hdr_frm_frst_disk = read_dword (zip->cd+zip->cd_pos+ZIPOFST);
363
364     /* check to see if filename length is illegally long (past the size of this directory
365        entry) */
366     if (zip->cd_pos + ZIPCFN + zip->ent.filename_length > zip->size_of_cent_dir)
367     {
368         errormsg("Invalid filename length in directory", ERROR_CORRUPT,zip->zip);
369         return 0;
370     }
371
372         /* copy filename */
373         free(zip->ent.name);
374         zip->ent.name = (char*)malloc(zip->ent.filename_length + 1);
375         memcpy(zip->ent.name, zip->cd+zip->cd_pos+ZIPCFN, zip->ent.filename_length);
376         zip->ent.name[zip->ent.filename_length] = 0;
377
378         /* skip to next entry in central dir */
379         zip->cd_pos += ZIPCFN + zip->ent.filename_length + zip->ent.extra_field_length + zip->ent.file_comment_length;
380
381         return &zip->ent;
382 }
383
384 /* Closes a zip stream */
385 void closezip(ZIP * zip)
386 {
387         /* release all */
388         free(zip->ent.name);
389         free(zip->cd);
390         free(zip->ecd);
391         /* only if not suspended */
392         if (zip->fp)
393                 fclose(zip->fp);
394         free(zip->zip);
395         free(zip);
396 }
397
398 /* Suspend access to a zip file (release file handler)
399    in:
400       zip opened zip
401    note:
402      A suspended zip is automatically reopened at first call of
403      readuncompressd() or readcompressed() functions
404 */
405 void suspendzip(ZIP * zip)
406 {
407         if (zip->fp)
408         {
409                 fclose(zip->fp);
410                 zip->fp = 0;
411         }
412 }
413
414 /* Revive a suspended zip file (reopen file handler)
415    in:
416      zip suspended zip
417    return:
418         zip success
419         ==0 error (zip must be closed with closezip)
420 */
421 static ZIP * revivezip(ZIP * zip)
422 {
423         if (!zip->fp)
424         {
425                 zip->fp = fopen(zip->zip, "rb");
426                 if (!zip->fp)
427                         return 0;
428         }
429
430         return zip;
431 }
432
433 /* Reset a zip stream to the first entry
434    in:
435      zip opened zip
436    note:
437      ZIP file must be opened and not suspended
438 */
439 void rewindzip(ZIP * zip)
440 {
441         zip->cd_pos = 0;
442 }
443
444 //
445 // Seek zip->fp to compressed data
446 //  return:
447 //      ==0 success
448 //      <0 error
449 //
450 int seekcompresszip(ZIP * zip, struct zipent * ent)
451 {
452         char buf[ZIPNAME];
453         long offset;
454
455         if (!zip->fp)
456         {
457                 if (!revivezip(zip))
458                         return -1;
459         }
460
461         if (fseek(zip->fp, ent->offset_lcl_hdr_frm_frst_disk, SEEK_SET) != 0)
462         {
463                 errormsg("Seeking to header", ERROR_CORRUPT, zip->zip);
464                 return -1;
465         }
466
467         if (fread(buf, 1, ZIPNAME, zip->fp) != ZIPNAME)
468         {
469                 errormsg("Reading header", ERROR_CORRUPT, zip->zip);
470                 return -1;
471         }
472
473         {
474                 uint16_t filename_length = read_word(buf+ZIPFNLN);
475                 uint16_t extra_field_length = read_word(buf+ZIPXTRALN);
476
477                 // calculate offset to data and fseek() there
478                 offset = ent->offset_lcl_hdr_frm_frst_disk + ZIPNAME + filename_length + extra_field_length;
479
480                 if (fseek(zip->fp, offset, SEEK_SET) != 0)
481                 {
482                         errormsg("Seeking to compressed data", ERROR_CORRUPT, zip->zip);
483                         return -1;
484                 }
485
486         }
487
488         return 0;
489 }
490
491 //
492 // Inflate a file
493 // in:
494 //   in_file stream to inflate
495 //   in_size size of the compressed data to read
496 // out:
497 //   out_size size of decompressed data
498 //   out_data buffer for decompressed data
499 // return:
500 //   ==0 ok
501 //
502 // 990525 rewritten for use with zlib MLR
503 //
504 static int inflate_file(FILE * in_file, unsigned in_size, unsigned char * out_data, unsigned out_size)
505 {
506     int err;
507         unsigned char * in_buffer;
508     z_stream d_stream;                  // decompression stream
509
510     d_stream.zalloc = 0;
511     d_stream.zfree = 0;
512     d_stream.opaque = 0;
513
514         d_stream.next_in  = 0;
515         d_stream.avail_in = 0;
516     d_stream.next_out = out_data;
517     d_stream.avail_out = out_size;
518
519     err = inflateInit2(&d_stream, -MAX_WBITS);
520         /* windowBits is passed < 0 to tell that there is no zlib header.
521          * Note that in this case inflate *requires* an extra "dummy" byte
522          * after the compressed stream in order to complete decompression and
523          * return Z_STREAM_END.
524          */
525     if (err != Z_OK)
526         {
527                 WriteLog("inflateInit error: %d\n", err);
528         return -1;
529         }
530
531         in_buffer = (unsigned char*)malloc(INFLATE_INPUT_BUFFER_MAX+1);
532         if (!in_buffer)
533                 return -1;
534
535     for (;;)
536         {
537                 if (in_size <= 0)
538                 {
539                         WriteLog("inflate error: compressed size too small\n");
540                         free (in_buffer);
541                         return -1;
542                 }
543                 d_stream.next_in  = in_buffer;
544                 d_stream.avail_in = fread(in_buffer, 1, MIN(in_size, INFLATE_INPUT_BUFFER_MAX), in_file);
545                 in_size -= d_stream.avail_in;
546                 if (in_size == 0)
547                         d_stream.avail_in++; /* add dummy byte at end of compressed data */
548
549         err = inflate(&d_stream, Z_NO_FLUSH);
550         if (err == Z_STREAM_END)
551                         break;
552                 if (err != Z_OK)
553                 {
554                         WriteLog("inflate error: %d\n", err);
555                         free (in_buffer);
556                         return -1;
557                 }
558     }
559
560     err = inflateEnd(&d_stream);
561         if (err != Z_OK)
562         {
563                 WriteLog("inflateEnd error: %d\n", err);
564                 free (in_buffer);
565                 return -1;
566         }
567
568         free (in_buffer);
569
570         if ((d_stream.avail_out > 0) || (in_size > 0))
571         {
572                 WriteLog("zip size mismatch. %i\n", in_size);
573                 return -1;
574         }
575
576         return 0;
577 }
578
579 //
580 // Read compressed data
581 //   out:
582 //      data compressed data read
583 //   return:
584 //      ==0 success
585 // <0 error
586 //
587 int readcompresszip(ZIP * zip, struct zipent * ent, char * data)
588 {
589         int err = seekcompresszip(zip, ent);
590         if (err != 0)
591                 return err;
592
593         if (fread(data, 1, ent->compressed_size, zip->fp) != ent->compressed_size)
594         {
595                 errormsg("Reading compressed data", ERROR_CORRUPT, zip->zip);
596                 return -1;
597         }
598
599         return 0;
600 }
601
602 //
603 // Read UNcompressed data
604 //   out:
605 //      data UNcompressed data
606 //   return:
607 //      ==0 success
608 //      <0 error
609 //
610 int readuncompresszip(ZIP * zip, struct zipent * ent, char * data)
611 {
612         if (ent->compression_method == 0x0000)
613         {
614                 /* file is not compressed, simply stored */
615
616                 /* check if size are equal */
617                 if (ent->compressed_size != ent->uncompressed_size)
618                 {
619                         errormsg("Wrong uncompressed size in store compression", ERROR_CORRUPT,zip->zip);
620                         return -3;
621                 }
622
623                 return readcompresszip(zip, ent, data);
624         }
625         else if (ent->compression_method == 0x0008)
626         {
627                 /* file is compressed using "Deflate" method */
628                 if (ent->version_needed_to_extract > 0x14)
629                 {
630                         errormsg("Version too new", ERROR_UNSUPPORTED, zip->zip);
631                         return -2;
632                 }
633
634                 if (ent->os_needed_to_extract != 0x00)
635                 {
636                         errormsg("OS not supported", ERROR_UNSUPPORTED, zip->zip);
637                         return -2;
638                 }
639
640                 if (ent->disk_number_start != zip->number_of_this_disk)
641                 {
642                         errormsg("Cannot span disks", ERROR_UNSUPPORTED, zip->zip);
643                         return -2;
644                 }
645
646                 /* read compressed data */
647                 if (seekcompresszip(zip, ent) != 0)
648                 {
649                         return -1;
650                 }
651
652                 /* configure inflate */
653                 if (inflate_file(zip->fp, ent->compressed_size, (unsigned char *)data, ent->uncompressed_size))
654                 {
655                         errormsg("Inflating compressed data", ERROR_CORRUPT, zip->zip);
656                         return -3;
657                 }
658
659                 return 0;
660         }
661         else
662         {
663                 errormsg("Compression method unsupported", ERROR_UNSUPPORTED, zip->zip);
664                 return -2;
665         }
666 }
667
668 /* -------------------------------------------------------------------------
669    Zip cache support
670  ------------------------------------------------------------------------- */
671
672 /* Use the zip cache */
673 // No, don't
674 //#define ZIP_CACHE
675
676 #ifdef ZIP_CACHE
677
678 /* ZIP cache entries */
679 #define ZIP_CACHE_MAX 5
680
681 /* ZIP cache buffer LRU ( Last Recently Used )
682      zip_cache_map[0] is the newer
683      zip_cache_map[ZIP_CACHE_MAX-1] is the older
684 */
685 static ZIP* zip_cache_map[ZIP_CACHE_MAX];
686
687 static ZIP* cache_openzip(int pathtype, int pathindex, const char* zipfile) {
688         ZIP* zip;
689         unsigned i;
690
691         /* search in the cache buffer */
692         for(i=0;i<ZIP_CACHE_MAX;++i) {
693                 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) {
694                         /* found */
695                         unsigned j;
696
697 /*
698                         WriteLog("Zip cache HIT  for %s\n", zipfile);
699 */
700
701                         /* reset the zip directory */
702                         rewindzip( zip_cache_map[i] );
703
704                         /* store */
705                         zip = zip_cache_map[i];
706
707                         /* shift */
708                         for(j=i;j>0;--j)
709                                 zip_cache_map[j] = zip_cache_map[j-1];
710
711                         /* set the first entry */
712                         zip_cache_map[0] = zip;
713
714                         return zip_cache_map[0];
715                 }
716         }
717         /* not found */
718
719 /*
720         WriteLog("Zip cache FAIL for %s\n", zipfile);
721 */
722
723         /* open the zip */
724         zip = openzip( pathtype, pathindex, zipfile );
725         if (!zip)
726                 return 0;
727
728         /* close the oldest entry */
729         if (zip_cache_map[ZIP_CACHE_MAX-1]) {
730                 /* close last zip */
731                 closezip(zip_cache_map[ZIP_CACHE_MAX-1]);
732                 /* reset the entry */
733                 zip_cache_map[ZIP_CACHE_MAX-1] = 0;
734         }
735
736         /* shift */
737         for(i=ZIP_CACHE_MAX-1;i>0;--i)
738                 zip_cache_map[i] = zip_cache_map[i-1];
739
740         /* set the first entry */
741         zip_cache_map[0] = zip;
742
743         return zip_cache_map[0];
744 }
745
746 static void cache_closezip(ZIP* zip) {
747         unsigned i;
748
749         /* search in the cache buffer */
750         for(i=0;i<ZIP_CACHE_MAX;++i) {
751                 if (zip_cache_map[i]==zip) {
752                         /* close zip */
753                         closezip(zip);
754
755                         /* reset cache entry */
756                         zip_cache_map[i] = 0;
757                         return;
758
759                 }
760         }
761         /* not found */
762
763         /* close zip */
764         closezip(zip);
765 }
766
767 /* CK980415 added to allow osd code to clear zip cache for auditing--each time
768    the user opens up an audit for a game we should reread the zip */
769 void unzip_cache_clear()
770 {
771         unsigned i;
772
773         /* search in the cache buffer for any zip info and clear it */
774         for(i=0;i<ZIP_CACHE_MAX;++i) {
775                 if (zip_cache_map[i] != NULL) {
776                         /* close zip */
777                         closezip(zip_cache_map[i]);
778
779                         /* reset cache entry */
780                         zip_cache_map[i] = 0;
781 /*                      return; */
782
783                 }
784         }
785 }
786
787 #define cache_suspendzip(a) suspendzip(a)
788
789 #else
790
791 #define cache_openzip(a,b,c) openzip(a,b,c)
792 #define cache_closezip(a) closezip(a)
793 #define cache_suspendzip(a) closezip(a)
794
795 #define unzip_cache_clear()
796
797 #endif
798
799 /* -------------------------------------------------------------------------
800    Backward MAME compatibility
801  ------------------------------------------------------------------------- */
802
803 //
804 // Compare two filenames
805 // NOTE: Don't check directory in zip and ignore case
806 //
807 static int equal_filename(const char * zipfile, const char * file)
808 {
809 //      const char * s1 = file;
810         // start comparison after last / also...!
811         const char * s1 = strrchr(file, '/');
812         if (s1)
813                 s1++;
814         else
815                 s1 = file;
816         // start comparison after last /
817         const char * s2 = strrchr(zipfile, '/');
818         if (s2)
819                 s2++;
820         else
821                 s2 = zipfile;
822 //WriteLog("--> Comparing filenames: [%s] <-> [%s]\n", s1, s2);
823
824         // This assumes that s1 is longer than s2... Might not be! !!! FIX !!!
825         while (*s1 && toupper(*s1) == toupper(*s2))
826         {
827                 s1++;
828                 s2++;
829         }
830
831         return !*s1 && !*s2;
832 }
833
834 //
835 // Pass the path to the zipfile and the name of the file within the zipfile.
836 // buf is set to point to the uncompressed image of that zipped file (preallocated by user!).
837 // length will be set to the length of the uncompressed data.
838 //
839 int load_zipped_file(int pathtype, int pathindex, const char * zipfile, const char * filename, unsigned char ** buf, uint32_t * length)
840 {
841         ZIP * zip;
842         struct zipent * ent;
843
844         zip = cache_openzip(pathtype, pathindex, zipfile);
845         if (!zip)
846                 return -1;
847
848         while (readzip(zip))
849         {
850                 /* NS981003: support for "load by CRC" */
851                 char crc[9];
852
853                 ent = &(zip->ent);
854                 sprintf(crc, "%08x", (unsigned int)ent->crc32);
855
856                 if (filename == NULL || equal_filename(ent->name, filename)
857                         || (ent->crc32 && !strcmp(crc, filename)))
858                 {
859                         *length = ent->uncompressed_size;
860
861                         if (readuncompresszip(zip, ent, (char *)*buf) != 0)
862                         {
863                                 cache_closezip(zip);
864                                 return -1;
865                         }
866
867                         cache_suspendzip(zip);
868                         return 0;
869                 }
870         }
871
872         cache_suspendzip(zip);
873         return -1;
874 }