misc/physfs/src/archiver_zip.c
changeset 8593 9d1d0fa8db02
parent 8591 9afb44f030b6
parent 8558 e96bf10216ef
child 8595 d2940421d3d4
equal deleted inserted replaced
8591:9afb44f030b6 8593:9d1d0fa8db02
     1 /*
       
     2  * ZIP support routines for PhysicsFS.
       
     3  *
       
     4  * Please see the file LICENSE.txt in the source's root directory.
       
     5  *
       
     6  *  This file written by Ryan C. Gordon, with some peeking at "unzip.c"
       
     7  *   by Gilles Vollant.
       
     8  */
       
     9 
       
    10 #define __PHYSICSFS_INTERNAL__
       
    11 #include "physfs_internal.h"
       
    12 
       
    13 #if PHYSFS_SUPPORTS_ZIP
       
    14 
       
    15 #include <errno.h>
       
    16 #include <time.h>
       
    17 
       
    18 #define USE_MINIZ 1
       
    19 #if USE_MINIZ
       
    20 #include "physfs_miniz.h"
       
    21 #else
       
    22 #include <zlib.h>
       
    23 #endif
       
    24 
       
    25 /*
       
    26  * A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened,
       
    27  *  and is freed when you close the file; compressed data is read into
       
    28  *  this buffer, and then is decompressed into the buffer passed to
       
    29  *  PHYSFS_read().
       
    30  *
       
    31  * Uncompressed entries in a zipfile do not allocate this buffer; they just
       
    32  *  read data directly into the buffer passed to PHYSFS_read().
       
    33  *
       
    34  * Depending on your speed and memory requirements, you should tweak this
       
    35  *  value.
       
    36  */
       
    37 #define ZIP_READBUFSIZE   (16 * 1024)
       
    38 
       
    39 
       
    40 /*
       
    41  * Entries are "unresolved" until they are first opened. At that time,
       
    42  *  local file headers parsed/validated, data offsets will be updated to look
       
    43  *  at the actual file data instead of the header, and symlinks will be
       
    44  *  followed and optimized. This means that we don't seek and read around the
       
    45  *  archive until forced to do so, and after the first time, we had to do
       
    46  *  less reading and parsing, which is very CD-ROM friendly.
       
    47  */
       
    48 typedef enum
       
    49 {
       
    50     ZIP_UNRESOLVED_FILE,
       
    51     ZIP_UNRESOLVED_SYMLINK,
       
    52     ZIP_RESOLVING,
       
    53     ZIP_RESOLVED,
       
    54     ZIP_BROKEN_FILE,
       
    55     ZIP_BROKEN_SYMLINK
       
    56 } ZipResolveType;
       
    57 
       
    58 
       
    59 /*
       
    60  * One ZIPentry is kept for each file in an open ZIP archive.
       
    61  */
       
    62 typedef struct _ZIPentry
       
    63 {
       
    64     char *name;                         /* Name of file in archive        */
       
    65     struct _ZIPentry *symlink;          /* NULL or file we symlink to     */
       
    66     ZipResolveType resolved;            /* Have we resolved file/symlink? */
       
    67     PHYSFS_uint64 offset;               /* offset of data in archive      */
       
    68     PHYSFS_uint16 version;              /* version made by                */
       
    69     PHYSFS_uint16 version_needed;       /* version needed to extract      */
       
    70     PHYSFS_uint16 compression_method;   /* compression method             */
       
    71     PHYSFS_uint32 crc;                  /* crc-32                         */
       
    72     PHYSFS_uint64 compressed_size;      /* compressed size                */
       
    73     PHYSFS_uint64 uncompressed_size;    /* uncompressed size              */
       
    74     PHYSFS_sint64 last_mod_time;        /* last file mod time             */
       
    75 } ZIPentry;
       
    76 
       
    77 /*
       
    78  * One ZIPinfo is kept for each open ZIP archive.
       
    79  */
       
    80 typedef struct
       
    81 {
       
    82     PHYSFS_Io *io;
       
    83     int zip64;                /* non-zero if this is a Zip64 archive. */
       
    84     PHYSFS_uint64 entryCount; /* Number of files in ZIP.              */
       
    85     ZIPentry *entries;        /* info on all files in ZIP.            */
       
    86 } ZIPinfo;
       
    87 
       
    88 /*
       
    89  * One ZIPfileinfo is kept for each open file in a ZIP archive.
       
    90  */
       
    91 typedef struct
       
    92 {
       
    93     ZIPentry *entry;                      /* Info on file.              */
       
    94     PHYSFS_Io *io;                        /* physical file handle.      */
       
    95     PHYSFS_uint32 compressed_position;    /* offset in compressed data. */
       
    96     PHYSFS_uint32 uncompressed_position;  /* tell() position.           */
       
    97     PHYSFS_uint8 *buffer;                 /* decompression buffer.      */
       
    98     z_stream stream;                      /* zlib stream state.         */
       
    99 } ZIPfileinfo;
       
   100 
       
   101 
       
   102 /* Magic numbers... */
       
   103 #define ZIP_LOCAL_FILE_SIG                          0x04034b50
       
   104 #define ZIP_CENTRAL_DIR_SIG                         0x02014b50
       
   105 #define ZIP_END_OF_CENTRAL_DIR_SIG                  0x06054b50
       
   106 #define ZIP64_END_OF_CENTRAL_DIR_SIG                0x06064b50
       
   107 #define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG  0x07064b50
       
   108 #define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG         0x0001
       
   109 
       
   110 /* compression methods... */
       
   111 #define COMPMETH_NONE 0
       
   112 /* ...and others... */
       
   113 
       
   114 
       
   115 #define UNIX_FILETYPE_MASK    0170000
       
   116 #define UNIX_FILETYPE_SYMLINK 0120000
       
   117 
       
   118 
       
   119 /*
       
   120  * Bridge physfs allocation functions to zlib's format...
       
   121  */
       
   122 static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size)
       
   123 {
       
   124     return ((PHYSFS_Allocator *) opaque)->Malloc(items * size);
       
   125 } /* zlibPhysfsAlloc */
       
   126 
       
   127 /*
       
   128  * Bridge physfs allocation functions to zlib's format...
       
   129  */
       
   130 static void zlibPhysfsFree(voidpf opaque, voidpf address)
       
   131 {
       
   132     ((PHYSFS_Allocator *) opaque)->Free(address);
       
   133 } /* zlibPhysfsFree */
       
   134 
       
   135 
       
   136 /*
       
   137  * Construct a new z_stream to a sane state.
       
   138  */
       
   139 static void initializeZStream(z_stream *pstr)
       
   140 {
       
   141     memset(pstr, '\0', sizeof (z_stream));
       
   142     pstr->zalloc = zlibPhysfsAlloc;
       
   143     pstr->zfree = zlibPhysfsFree;
       
   144     pstr->opaque = &allocator;
       
   145 } /* initializeZStream */
       
   146 
       
   147 
       
   148 static PHYSFS_ErrorCode zlib_error_code(int rc)
       
   149 {
       
   150     switch (rc)
       
   151     {
       
   152         case Z_OK: return PHYSFS_ERR_OK;  /* not an error. */
       
   153         case Z_STREAM_END: return PHYSFS_ERR_OK; /* not an error. */
       
   154         case Z_ERRNO: return PHYSFS_ERR_IO;
       
   155         case Z_MEM_ERROR: return PHYSFS_ERR_OUT_OF_MEMORY;
       
   156         default: return PHYSFS_ERR_CORRUPT;
       
   157     } /* switch */
       
   158 } /* zlib_error_string */
       
   159 
       
   160 
       
   161 /*
       
   162  * Wrap all zlib calls in this, so the physfs error state is set appropriately.
       
   163  */
       
   164 static int zlib_err(const int rc)
       
   165 {
       
   166     __PHYSFS_setError(zlib_error_code(rc));
       
   167     return rc;
       
   168 } /* zlib_err */
       
   169 
       
   170 
       
   171 /*
       
   172  * Read an unsigned 64-bit int and swap to native byte order.
       
   173  */
       
   174 static int readui64(PHYSFS_Io *io, PHYSFS_uint64 *val)
       
   175 {
       
   176     PHYSFS_uint64 v;
       
   177     BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
       
   178     *val = PHYSFS_swapULE64(v);
       
   179     return 1;
       
   180 } /* readui64 */
       
   181 
       
   182 /*
       
   183  * Read an unsigned 32-bit int and swap to native byte order.
       
   184  */
       
   185 static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
       
   186 {
       
   187     PHYSFS_uint32 v;
       
   188     BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
       
   189     *val = PHYSFS_swapULE32(v);
       
   190     return 1;
       
   191 } /* readui32 */
       
   192 
       
   193 
       
   194 /*
       
   195  * Read an unsigned 16-bit int and swap to native byte order.
       
   196  */
       
   197 static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val)
       
   198 {
       
   199     PHYSFS_uint16 v;
       
   200     BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
       
   201     *val = PHYSFS_swapULE16(v);
       
   202     return 1;
       
   203 } /* readui16 */
       
   204 
       
   205 
       
   206 static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len)
       
   207 {
       
   208     ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
       
   209     PHYSFS_Io *io = finfo->io;
       
   210     ZIPentry *entry = finfo->entry;
       
   211     PHYSFS_sint64 retval = 0;
       
   212     PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
       
   213     PHYSFS_sint64 avail = entry->uncompressed_size -
       
   214                           finfo->uncompressed_position;
       
   215 
       
   216     if (avail < maxread)
       
   217         maxread = avail;
       
   218 
       
   219     BAIL_IF_MACRO(maxread == 0, ERRPASS, 0);    /* quick rejection. */
       
   220 
       
   221     if (entry->compression_method == COMPMETH_NONE)
       
   222         retval = io->read(io, buf, maxread);
       
   223     else
       
   224     {
       
   225         finfo->stream.next_out = buf;
       
   226         finfo->stream.avail_out = (uInt) maxread;
       
   227 
       
   228         while (retval < maxread)
       
   229         {
       
   230             PHYSFS_uint32 before = finfo->stream.total_out;
       
   231             int rc;
       
   232 
       
   233             if (finfo->stream.avail_in == 0)
       
   234             {
       
   235                 PHYSFS_sint64 br;
       
   236 
       
   237                 br = entry->compressed_size - finfo->compressed_position;
       
   238                 if (br > 0)
       
   239                 {
       
   240                     if (br > ZIP_READBUFSIZE)
       
   241                         br = ZIP_READBUFSIZE;
       
   242 
       
   243                     br = io->read(io, finfo->buffer, (PHYSFS_uint64) br);
       
   244                     if (br <= 0)
       
   245                         break;
       
   246 
       
   247                     finfo->compressed_position += (PHYSFS_uint32) br;
       
   248                     finfo->stream.next_in = finfo->buffer;
       
   249                     finfo->stream.avail_in = (PHYSFS_uint32) br;
       
   250                 } /* if */
       
   251             } /* if */
       
   252 
       
   253             rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH));
       
   254             retval += (finfo->stream.total_out - before);
       
   255 
       
   256             if (rc != Z_OK)
       
   257                 break;
       
   258         } /* while */
       
   259     } /* else */
       
   260 
       
   261     if (retval > 0)
       
   262         finfo->uncompressed_position += (PHYSFS_uint32) retval;
       
   263 
       
   264     return retval;
       
   265 } /* ZIP_read */
       
   266 
       
   267 
       
   268 static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
       
   269 {
       
   270     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1);
       
   271 } /* ZIP_write */
       
   272 
       
   273 
       
   274 static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io)
       
   275 {
       
   276     return ((ZIPfileinfo *) io->opaque)->uncompressed_position;
       
   277 } /* ZIP_tell */
       
   278 
       
   279 
       
   280 static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
       
   281 {
       
   282     ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
       
   283     ZIPentry *entry = finfo->entry;
       
   284     PHYSFS_Io *io = finfo->io;
       
   285 
       
   286     BAIL_IF_MACRO(offset > entry->uncompressed_size, PHYSFS_ERR_PAST_EOF, 0);
       
   287 
       
   288     if (entry->compression_method == COMPMETH_NONE)
       
   289     {
       
   290         const PHYSFS_sint64 newpos = offset + entry->offset;
       
   291         BAIL_IF_MACRO(!io->seek(io, newpos), ERRPASS, 0);
       
   292         finfo->uncompressed_position = (PHYSFS_uint32) offset;
       
   293     } /* if */
       
   294 
       
   295     else
       
   296     {
       
   297         /*
       
   298          * If seeking backwards, we need to redecode the file
       
   299          *  from the start and throw away the compressed bits until we hit
       
   300          *  the offset we need. If seeking forward, we still need to
       
   301          *  decode, but we don't rewind first.
       
   302          */
       
   303         if (offset < finfo->uncompressed_position)
       
   304         {
       
   305             /* we do a copy so state is sane if inflateInit2() fails. */
       
   306             z_stream str;
       
   307             initializeZStream(&str);
       
   308             if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
       
   309                 return 0;
       
   310 
       
   311             if (!io->seek(io, entry->offset))
       
   312                 return 0;
       
   313 
       
   314             inflateEnd(&finfo->stream);
       
   315             memcpy(&finfo->stream, &str, sizeof (z_stream));
       
   316             finfo->uncompressed_position = finfo->compressed_position = 0;
       
   317         } /* if */
       
   318 
       
   319         while (finfo->uncompressed_position != offset)
       
   320         {
       
   321             PHYSFS_uint8 buf[512];
       
   322             PHYSFS_uint32 maxread;
       
   323 
       
   324             maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position);
       
   325             if (maxread > sizeof (buf))
       
   326                 maxread = sizeof (buf);
       
   327 
       
   328             if (ZIP_read(_io, buf, maxread) != maxread)
       
   329                 return 0;
       
   330         } /* while */
       
   331     } /* else */
       
   332 
       
   333     return 1;
       
   334 } /* ZIP_seek */
       
   335 
       
   336 
       
   337 static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
       
   338 {
       
   339     const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
       
   340     return (PHYSFS_sint64) finfo->entry->uncompressed_size;
       
   341 } /* ZIP_length */
       
   342 
       
   343 
       
   344 static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry);
       
   345 
       
   346 static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
       
   347 {
       
   348     ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque;
       
   349     PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
   350     ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
       
   351     GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, failed);
       
   352     GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, failed);
       
   353     memset(finfo, '\0', sizeof (*finfo));
       
   354 
       
   355     finfo->entry = origfinfo->entry;
       
   356     finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
       
   357     GOTO_IF_MACRO(!finfo->io, ERRPASS, failed);
       
   358 
       
   359     if (finfo->entry->compression_method != COMPMETH_NONE)
       
   360     {
       
   361         finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
       
   362         GOTO_IF_MACRO(!finfo->buffer, PHYSFS_ERR_OUT_OF_MEMORY, failed);
       
   363         if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
       
   364             goto failed;
       
   365     } /* if */
       
   366 
       
   367     memcpy(retval, io, sizeof (PHYSFS_Io));
       
   368     retval->opaque = finfo;
       
   369     return retval;
       
   370 
       
   371 failed:
       
   372     if (finfo != NULL)
       
   373     {
       
   374         if (finfo->io != NULL)
       
   375             finfo->io->destroy(finfo->io);
       
   376 
       
   377         if (finfo->buffer != NULL)
       
   378         {
       
   379             allocator.Free(finfo->buffer);
       
   380             inflateEnd(&finfo->stream);
       
   381         } /* if */
       
   382 
       
   383         allocator.Free(finfo);
       
   384     } /* if */
       
   385 
       
   386     if (retval != NULL)
       
   387         allocator.Free(retval);
       
   388 
       
   389     return NULL;
       
   390 } /* ZIP_duplicate */
       
   391 
       
   392 static int ZIP_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
       
   393 
       
   394 static void ZIP_destroy(PHYSFS_Io *io)
       
   395 {
       
   396     ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
       
   397     finfo->io->destroy(finfo->io);
       
   398 
       
   399     if (finfo->entry->compression_method != COMPMETH_NONE)
       
   400         inflateEnd(&finfo->stream);
       
   401 
       
   402     if (finfo->buffer != NULL)
       
   403         allocator.Free(finfo->buffer);
       
   404 
       
   405     allocator.Free(finfo);
       
   406     allocator.Free(io);
       
   407 } /* ZIP_destroy */
       
   408 
       
   409 
       
   410 static const PHYSFS_Io ZIP_Io =
       
   411 {
       
   412     CURRENT_PHYSFS_IO_API_VERSION, NULL,
       
   413     ZIP_read,
       
   414     ZIP_write,
       
   415     ZIP_seek,
       
   416     ZIP_tell,
       
   417     ZIP_length,
       
   418     ZIP_duplicate,
       
   419     ZIP_flush,
       
   420     ZIP_destroy
       
   421 };
       
   422 
       
   423 
       
   424 
       
   425 static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
       
   426 {
       
   427     PHYSFS_uint8 buf[256];
       
   428     PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
       
   429     PHYSFS_sint32 i = 0;
       
   430     PHYSFS_sint64 filelen;
       
   431     PHYSFS_sint64 filepos;
       
   432     PHYSFS_sint32 maxread;
       
   433     PHYSFS_sint32 totalread = 0;
       
   434     int found = 0;
       
   435 
       
   436     filelen = io->length(io);
       
   437     BAIL_IF_MACRO(filelen == -1, ERRPASS, 0);
       
   438 
       
   439     /*
       
   440      * Jump to the end of the file and start reading backwards.
       
   441      *  The last thing in the file is the zipfile comment, which is variable
       
   442      *  length, and the field that specifies its size is before it in the
       
   443      *  file (argh!)...this means that we need to scan backwards until we
       
   444      *  hit the end-of-central-dir signature. We can then sanity check that
       
   445      *  the comment was as big as it should be to make sure we're in the
       
   446      *  right place. The comment length field is 16 bits, so we can stop
       
   447      *  searching for that signature after a little more than 64k at most,
       
   448      *  and call it a corrupted zipfile.
       
   449      */
       
   450 
       
   451     if (sizeof (buf) < filelen)
       
   452     {
       
   453         filepos = filelen - sizeof (buf);
       
   454         maxread = sizeof (buf);
       
   455     } /* if */
       
   456     else
       
   457     {
       
   458         filepos = 0;
       
   459         maxread = (PHYSFS_uint32) filelen;
       
   460     } /* else */
       
   461 
       
   462     while ((totalread < filelen) && (totalread < 65557))
       
   463     {
       
   464         BAIL_IF_MACRO(!io->seek(io, filepos), ERRPASS, -1);
       
   465 
       
   466         /* make sure we catch a signature between buffers. */
       
   467         if (totalread != 0)
       
   468         {
       
   469             if (!__PHYSFS_readAll(io, buf, maxread - 4))
       
   470                 return -1;
       
   471             memcpy(&buf[maxread - 4], &extra, sizeof (extra));
       
   472             totalread += maxread - 4;
       
   473         } /* if */
       
   474         else
       
   475         {
       
   476             if (!__PHYSFS_readAll(io, buf, maxread))
       
   477                 return -1;
       
   478             totalread += maxread;
       
   479         } /* else */
       
   480 
       
   481         memcpy(&extra, buf, sizeof (extra));
       
   482 
       
   483         for (i = maxread - 4; i > 0; i--)
       
   484         {
       
   485             if ((buf[i + 0] == 0x50) &&
       
   486                 (buf[i + 1] == 0x4B) &&
       
   487                 (buf[i + 2] == 0x05) &&
       
   488                 (buf[i + 3] == 0x06) )
       
   489             {
       
   490                 found = 1;  /* that's the signature! */
       
   491                 break;  
       
   492             } /* if */
       
   493         } /* for */
       
   494 
       
   495         if (found)
       
   496             break;
       
   497 
       
   498         filepos -= (maxread - 4);
       
   499         if (filepos < 0)
       
   500             filepos = 0;
       
   501     } /* while */
       
   502 
       
   503     BAIL_IF_MACRO(!found, PHYSFS_ERR_UNSUPPORTED, -1);
       
   504 
       
   505     if (len != NULL)
       
   506         *len = filelen;
       
   507 
       
   508     return (filepos + i);
       
   509 } /* zip_find_end_of_central_dir */
       
   510 
       
   511 
       
   512 static int isZip(PHYSFS_Io *io)
       
   513 {
       
   514     PHYSFS_uint32 sig = 0;
       
   515     int retval = 0;
       
   516 
       
   517     /*
       
   518      * The first thing in a zip file might be the signature of the
       
   519      *  first local file record, so it makes for a quick determination.
       
   520      */
       
   521     if (readui32(io, &sig))
       
   522     {
       
   523         retval = (sig == ZIP_LOCAL_FILE_SIG);
       
   524         if (!retval)
       
   525         {
       
   526             /*
       
   527              * No sig...might be a ZIP with data at the start
       
   528              *  (a self-extracting executable, etc), so we'll have to do
       
   529              *  it the hard way...
       
   530              */
       
   531             retval = (zip_find_end_of_central_dir(io, NULL) != -1);
       
   532         } /* if */
       
   533     } /* if */
       
   534 
       
   535     return retval;
       
   536 } /* isZip */
       
   537 
       
   538 
       
   539 static void zip_free_entries(ZIPentry *entries, PHYSFS_uint64 max)
       
   540 {
       
   541     PHYSFS_uint64 i;
       
   542     for (i = 0; i < max; i++)
       
   543     {
       
   544         ZIPentry *entry = &entries[i];
       
   545         if (entry->name != NULL)
       
   546             allocator.Free(entry->name);
       
   547     } /* for */
       
   548 
       
   549     allocator.Free(entries);
       
   550 } /* zip_free_entries */
       
   551 
       
   552 
       
   553 /*
       
   554  * This will find the ZIPentry associated with a path in platform-independent
       
   555  *  notation. Directories don't have ZIPentries associated with them, but 
       
   556  *  (*isDir) will be set to non-zero if a dir was hit.
       
   557  */
       
   558 static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path,
       
   559                                 int *isDir)
       
   560 {
       
   561     ZIPentry *a = info->entries;
       
   562     PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path);
       
   563     PHYSFS_sint64 lo = 0;
       
   564     PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1);
       
   565     PHYSFS_sint64 middle;
       
   566     const char *thispath = NULL;
       
   567     int rc;
       
   568 
       
   569     while (lo <= hi)
       
   570     {
       
   571         middle = lo + ((hi - lo) / 2);
       
   572         thispath = a[middle].name;
       
   573         rc = strncmp(path, thispath, pathlen);
       
   574 
       
   575         if (rc > 0)
       
   576             lo = middle + 1;
       
   577 
       
   578         else if (rc < 0)
       
   579             hi = middle - 1;
       
   580 
       
   581         else /* substring match...might be dir or entry or nothing. */
       
   582         {
       
   583             if (isDir != NULL)
       
   584             {
       
   585                 *isDir = (thispath[pathlen] == '/');
       
   586                 if (*isDir)
       
   587                     return NULL;
       
   588             } /* if */
       
   589 
       
   590             if (thispath[pathlen] == '\0') /* found entry? */
       
   591                 return &a[middle];
       
   592             /* adjust search params, try again. */
       
   593             else if (thispath[pathlen] > '/')
       
   594                 hi = middle - 1;
       
   595             else
       
   596                 lo = middle + 1;
       
   597         } /* if */
       
   598     } /* while */
       
   599 
       
   600     if (isDir != NULL)
       
   601         *isDir = 0;
       
   602 
       
   603     BAIL_MACRO(PHYSFS_ERR_NO_SUCH_PATH, NULL);
       
   604 } /* zip_find_entry */
       
   605 
       
   606 
       
   607 /* Convert paths from old, buggy DOS zippers... */
       
   608 static void zip_convert_dos_path(ZIPentry *entry, char *path)
       
   609 {
       
   610     PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF);
       
   611     if (hosttype == 0)  /* FS_FAT_ */
       
   612     {
       
   613         while (*path)
       
   614         {
       
   615             if (*path == '\\')
       
   616                 *path = '/';
       
   617             path++;
       
   618         } /* while */
       
   619     } /* if */
       
   620 } /* zip_convert_dos_path */
       
   621 
       
   622 
       
   623 static void zip_expand_symlink_path(char *path)
       
   624 {
       
   625     char *ptr = path;
       
   626     char *prevptr = path;
       
   627 
       
   628     while (1)
       
   629     {
       
   630         ptr = strchr(ptr, '/');
       
   631         if (ptr == NULL)
       
   632             break;
       
   633 
       
   634         if (*(ptr + 1) == '.')
       
   635         {
       
   636             if (*(ptr + 2) == '/')
       
   637             {
       
   638                 /* current dir in middle of string: ditch it. */
       
   639                 memmove(ptr, ptr + 2, strlen(ptr + 2) + 1);
       
   640             } /* else if */
       
   641 
       
   642             else if (*(ptr + 2) == '\0')
       
   643             {
       
   644                 /* current dir at end of string: ditch it. */
       
   645                 *ptr = '\0';
       
   646             } /* else if */
       
   647 
       
   648             else if (*(ptr + 2) == '.')
       
   649             {
       
   650                 if (*(ptr + 3) == '/')
       
   651                 {
       
   652                     /* parent dir in middle: move back one, if possible. */
       
   653                     memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1);
       
   654                     ptr = prevptr;
       
   655                     while (prevptr != path)
       
   656                     {
       
   657                         prevptr--;
       
   658                         if (*prevptr == '/')
       
   659                         {
       
   660                             prevptr++;
       
   661                             break;
       
   662                         } /* if */
       
   663                     } /* while */
       
   664                 } /* if */
       
   665 
       
   666                 if (*(ptr + 3) == '\0')
       
   667                 {
       
   668                     /* parent dir at end: move back one, if possible. */
       
   669                     *prevptr = '\0';
       
   670                 } /* if */
       
   671             } /* if */
       
   672         } /* if */
       
   673         else
       
   674         {
       
   675             prevptr = ptr;
       
   676             ptr++;
       
   677         } /* else */
       
   678     } /* while */
       
   679 } /* zip_expand_symlink_path */
       
   680 
       
   681 /* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
       
   682 static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
       
   683 
       
   684 /*
       
   685  * Look for the entry named by (path). If it exists, resolve it, and return
       
   686  *  a pointer to that entry. If it's another symlink, keep resolving until you
       
   687  *  hit a real file and then return a pointer to the final non-symlink entry.
       
   688  *  If there's a problem, return NULL.
       
   689  */
       
   690 static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
       
   691 {
       
   692     ZIPentry *entry;
       
   693 
       
   694     zip_expand_symlink_path(path);
       
   695     entry = zip_find_entry(info, path, NULL);
       
   696     if (entry != NULL)
       
   697     {
       
   698         if (!zip_resolve(io, info, entry))  /* recursive! */
       
   699             entry = NULL;
       
   700         else
       
   701         {
       
   702             if (entry->symlink != NULL)
       
   703                 entry = entry->symlink;
       
   704         } /* else */
       
   705     } /* if */
       
   706 
       
   707     return entry;
       
   708 } /* zip_follow_symlink */
       
   709 
       
   710 
       
   711 static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
       
   712 {
       
   713     const PHYSFS_uint64 size = entry->uncompressed_size;
       
   714     char *path = NULL;
       
   715     int rc = 0;
       
   716 
       
   717     /*
       
   718      * We've already parsed the local file header of the symlink at this
       
   719      *  point. Now we need to read the actual link from the file data and
       
   720      *  follow it.
       
   721      */
       
   722 
       
   723     BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
       
   724 
       
   725     path = (char *) __PHYSFS_smallAlloc(size + 1);
       
   726     BAIL_IF_MACRO(!path, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
   727     
       
   728     if (entry->compression_method == COMPMETH_NONE)
       
   729         rc = __PHYSFS_readAll(io, path, size);
       
   730 
       
   731     else  /* symlink target path is compressed... */
       
   732     {
       
   733         z_stream stream;
       
   734         const PHYSFS_uint64 complen = entry->compressed_size;
       
   735         PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
       
   736         if (compressed != NULL)
       
   737         {
       
   738             if (__PHYSFS_readAll(io, compressed, complen))
       
   739             {
       
   740                 initializeZStream(&stream);
       
   741                 stream.next_in = compressed;
       
   742                 stream.avail_in = complen;
       
   743                 stream.next_out = (unsigned char *) path;
       
   744                 stream.avail_out = size;
       
   745                 if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
       
   746                 {
       
   747                     rc = zlib_err(inflate(&stream, Z_FINISH));
       
   748                     inflateEnd(&stream);
       
   749 
       
   750                     /* both are acceptable outcomes... */
       
   751                     rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
       
   752                 } /* if */
       
   753             } /* if */
       
   754             __PHYSFS_smallFree(compressed);
       
   755         } /* if */
       
   756     } /* else */
       
   757 
       
   758     if (rc)
       
   759     {
       
   760         path[entry->uncompressed_size] = '\0';    /* null-terminate it. */
       
   761         zip_convert_dos_path(entry, path);
       
   762         entry->symlink = zip_follow_symlink(io, info, path);
       
   763     } /* else */
       
   764 
       
   765     __PHYSFS_smallFree(path);
       
   766 
       
   767     return (entry->symlink != NULL);
       
   768 } /* zip_resolve_symlink */
       
   769 
       
   770 
       
   771 /*
       
   772  * Parse the local file header of an entry, and update entry->offset.
       
   773  */
       
   774 static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
       
   775 {
       
   776     PHYSFS_uint32 ui32;
       
   777     PHYSFS_uint16 ui16;
       
   778     PHYSFS_uint16 fnamelen;
       
   779     PHYSFS_uint16 extralen;
       
   780 
       
   781     /*
       
   782      * crc and (un)compressed_size are always zero if this is a "JAR"
       
   783      *  archive created with Sun's Java tools, apparently. We only
       
   784      *  consider this archive corrupted if those entries don't match and
       
   785      *  aren't zero. That seems to work well.
       
   786      * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's
       
   787      *  possible that's a Zip64 thing.
       
   788      */
       
   789 
       
   790     BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
       
   791     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   792     BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0);
       
   793     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
   794     BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0);
       
   795     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);  /* general bits. */
       
   796     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
   797     BAIL_IF_MACRO(ui16 != entry->compression_method, PHYSFS_ERR_CORRUPT, 0);
       
   798     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);  /* date/time */
       
   799     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   800     BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0);
       
   801 
       
   802     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   803     BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
       
   804                   (ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0);
       
   805 
       
   806     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   807     BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
       
   808                  (ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0);
       
   809 
       
   810     BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
       
   811     BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
       
   812 
       
   813     entry->offset += fnamelen + extralen + 30;
       
   814     return 1;
       
   815 } /* zip_parse_local */
       
   816 
       
   817 
       
   818 static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
       
   819 {
       
   820     int retval = 1;
       
   821     ZipResolveType resolve_type = entry->resolved;
       
   822 
       
   823     /* Don't bother if we've failed to resolve this entry before. */
       
   824     BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, PHYSFS_ERR_CORRUPT, 0);
       
   825     BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, PHYSFS_ERR_CORRUPT, 0);
       
   826 
       
   827     /* uhoh...infinite symlink loop! */
       
   828     BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, PHYSFS_ERR_SYMLINK_LOOP, 0);
       
   829 
       
   830     /*
       
   831      * We fix up the offset to point to the actual data on the
       
   832      *  first open, since we don't want to seek across the whole file on
       
   833      *  archive open (can be SLOW on large, CD-stored files), but we
       
   834      *  need to check the local file header...not just for corruption,
       
   835      *  but since it stores offset info the central directory does not.
       
   836      */
       
   837     if (resolve_type != ZIP_RESOLVED)
       
   838     {
       
   839         entry->resolved = ZIP_RESOLVING;
       
   840 
       
   841         retval = zip_parse_local(io, entry);
       
   842         if (retval)
       
   843         {
       
   844             /*
       
   845              * If it's a symlink, find the original file. This will cause
       
   846              *  resolution of other entries (other symlinks and, eventually,
       
   847              *  the real file) if all goes well.
       
   848              */
       
   849             if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
       
   850                 retval = zip_resolve_symlink(io, info, entry);
       
   851         } /* if */
       
   852 
       
   853         if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
       
   854             entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK);
       
   855         else if (resolve_type == ZIP_UNRESOLVED_FILE)
       
   856             entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE);
       
   857     } /* if */
       
   858 
       
   859     return retval;
       
   860 } /* zip_resolve */
       
   861 
       
   862 
       
   863 static int zip_version_does_symlinks(PHYSFS_uint32 version)
       
   864 {
       
   865     int retval = 0;
       
   866     PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
       
   867 
       
   868     switch (hosttype)
       
   869     {
       
   870             /*
       
   871              * These are the platforms that can NOT build an archive with
       
   872              *  symlinks, according to the Info-ZIP project.
       
   873              */
       
   874         case 0:  /* FS_FAT_  */
       
   875         case 1:  /* AMIGA_   */
       
   876         case 2:  /* VMS_     */
       
   877         case 4:  /* VM_CSM_  */
       
   878         case 6:  /* FS_HPFS_ */
       
   879         case 11: /* FS_NTFS_ */
       
   880         case 14: /* FS_VFAT_ */
       
   881         case 13: /* ACORN_   */
       
   882         case 15: /* MVS_     */
       
   883         case 18: /* THEOS_   */
       
   884             break;  /* do nothing. */
       
   885 
       
   886         default:  /* assume the rest to be unix-like. */
       
   887             retval = 1;
       
   888             break;
       
   889     } /* switch */
       
   890 
       
   891     return retval;
       
   892 } /* zip_version_does_symlinks */
       
   893 
       
   894 
       
   895 static int zip_entry_is_symlink(const ZIPentry *entry)
       
   896 {
       
   897     return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
       
   898             (entry->resolved == ZIP_BROKEN_SYMLINK) ||
       
   899             (entry->symlink));
       
   900 } /* zip_entry_is_symlink */
       
   901 
       
   902 
       
   903 static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr)
       
   904 {
       
   905     PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
       
   906     return ( (zip_version_does_symlinks(entry->version)) &&
       
   907              (entry->uncompressed_size > 0) &&
       
   908              ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );
       
   909 } /* zip_has_symlink_attr */
       
   910 
       
   911 
       
   912 static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
       
   913 {
       
   914     PHYSFS_uint32 dosdate;
       
   915     struct tm unixtime;
       
   916     memset(&unixtime, '\0', sizeof (unixtime));
       
   917 
       
   918     dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
       
   919     dostime &= 0xFFFF;
       
   920 
       
   921     /* dissect date */
       
   922     unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80;
       
   923     unixtime.tm_mon  = ((dosdate >> 5) & 0x0F) - 1;
       
   924     unixtime.tm_mday = ((dosdate     ) & 0x1F);
       
   925 
       
   926     /* dissect time */
       
   927     unixtime.tm_hour = ((dostime >> 11) & 0x1F);
       
   928     unixtime.tm_min  = ((dostime >>  5) & 0x3F);
       
   929     unixtime.tm_sec  = ((dostime <<  1) & 0x3E);
       
   930 
       
   931     /* let mktime calculate daylight savings time. */
       
   932     unixtime.tm_isdst = -1;
       
   933 
       
   934     return ((PHYSFS_sint64) mktime(&unixtime));
       
   935 } /* zip_dos_time_to_physfs_time */
       
   936 
       
   937 
       
   938 static int zip_load_entry(PHYSFS_Io *io, const int zip64, ZIPentry *entry,
       
   939                           PHYSFS_uint64 ofs_fixup)
       
   940 {
       
   941     PHYSFS_uint16 fnamelen, extralen, commentlen;
       
   942     PHYSFS_uint32 external_attr;
       
   943     PHYSFS_uint32 starting_disk;
       
   944     PHYSFS_uint64 offset;
       
   945     PHYSFS_uint16 ui16;
       
   946     PHYSFS_uint32 ui32;
       
   947     PHYSFS_sint64 si64;
       
   948 
       
   949     /* sanity check with central directory signature... */
       
   950     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   951     BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
       
   952 
       
   953     /* Get the pertinent parts of the record... */
       
   954     BAIL_IF_MACRO(!readui16(io, &entry->version), ERRPASS, 0);
       
   955     BAIL_IF_MACRO(!readui16(io, &entry->version_needed), ERRPASS, 0);
       
   956     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);  /* general bits */
       
   957     BAIL_IF_MACRO(!readui16(io, &entry->compression_method), ERRPASS, 0);
       
   958     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   959     entry->last_mod_time = zip_dos_time_to_physfs_time(ui32);
       
   960     BAIL_IF_MACRO(!readui32(io, &entry->crc), ERRPASS, 0);
       
   961     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   962     entry->compressed_size = (PHYSFS_uint64) ui32;
       
   963     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   964     entry->uncompressed_size = (PHYSFS_uint64) ui32;
       
   965     BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
       
   966     BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
       
   967     BAIL_IF_MACRO(!readui16(io, &commentlen), ERRPASS, 0);
       
   968     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
   969     starting_disk = (PHYSFS_uint32) ui16;
       
   970     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);  /* internal file attribs */
       
   971     BAIL_IF_MACRO(!readui32(io, &external_attr), ERRPASS, 0);
       
   972     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   973     offset = (PHYSFS_uint64) ui32;
       
   974 
       
   975     entry->symlink = NULL;  /* will be resolved later, if necessary. */
       
   976     entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ?
       
   977                             ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE;
       
   978 
       
   979     entry->name = (char *) allocator.Malloc(fnamelen + 1);
       
   980     BAIL_IF_MACRO(entry->name == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
   981     if (!__PHYSFS_readAll(io, entry->name, fnamelen))
       
   982         goto zip_load_entry_puked;
       
   983 
       
   984     entry->name[fnamelen] = '\0';  /* null-terminate the filename. */
       
   985     zip_convert_dos_path(entry, entry->name);
       
   986 
       
   987     si64 = io->tell(io);
       
   988     if (si64 == -1)
       
   989         goto zip_load_entry_puked;
       
   990 
       
   991     /*
       
   992      * The actual sizes didn't fit in 32-bits; look for the Zip64
       
   993      *  extended information extra field...
       
   994      */
       
   995     if ( (zip64) &&
       
   996          ((offset == 0xFFFFFFFF) ||
       
   997           (starting_disk == 0xFFFFFFFF) ||
       
   998           (entry->compressed_size == 0xFFFFFFFF) ||
       
   999           (entry->uncompressed_size == 0xFFFFFFFF)) )
       
  1000     {
       
  1001         int found = 0;
       
  1002         PHYSFS_uint16 sig, len;
       
  1003         while (extralen > 4)
       
  1004         {
       
  1005             if (!readui16(io, &sig))
       
  1006                 goto zip_load_entry_puked;
       
  1007             else if (!readui16(io, &len))
       
  1008                 goto zip_load_entry_puked;
       
  1009 
       
  1010             si64 += 4 + len;
       
  1011             extralen -= 4 + len;
       
  1012             if (sig != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG)
       
  1013             {
       
  1014                 if (!io->seek(io, si64))
       
  1015                     goto zip_load_entry_puked;
       
  1016                 continue;
       
  1017             } /* if */
       
  1018 
       
  1019             found = 1;
       
  1020             break;
       
  1021         } /* while */
       
  1022 
       
  1023         GOTO_IF_MACRO(!found, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1024 
       
  1025         if (entry->uncompressed_size == 0xFFFFFFFF)
       
  1026         {
       
  1027             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1028             if (!readui64(io, &entry->uncompressed_size))
       
  1029                 goto zip_load_entry_puked;
       
  1030             len -= 8;
       
  1031         } /* if */
       
  1032 
       
  1033         if (entry->compressed_size == 0xFFFFFFFF)
       
  1034         {
       
  1035             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1036             if (!readui64(io, &entry->compressed_size))
       
  1037                 goto zip_load_entry_puked;
       
  1038             len -= 8;
       
  1039         } /* if */
       
  1040 
       
  1041         if (offset == 0xFFFFFFFF)
       
  1042         {
       
  1043             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1044             if (!readui64(io, &offset))
       
  1045                 goto zip_load_entry_puked;
       
  1046             len -= 8;
       
  1047         } /* if */
       
  1048 
       
  1049         if (starting_disk == 0xFFFFFFFF)
       
  1050         {
       
  1051             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1052             if (!readui32(io, &starting_disk))
       
  1053                 goto zip_load_entry_puked;
       
  1054             len -= 4;
       
  1055         } /* if */
       
  1056 
       
  1057         GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1058     } /* if */
       
  1059 
       
  1060     GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1061 
       
  1062     entry->offset = offset + ofs_fixup;
       
  1063 
       
  1064     /* seek to the start of the next entry in the central directory... */
       
  1065     if (!io->seek(io, si64 + extralen + commentlen))
       
  1066         goto zip_load_entry_puked;
       
  1067 
       
  1068     return 1;  /* success. */
       
  1069 
       
  1070 zip_load_entry_puked:
       
  1071     allocator.Free(entry->name);
       
  1072     return 0;  /* failure. */
       
  1073 } /* zip_load_entry */
       
  1074 
       
  1075 
       
  1076 static int zip_entry_cmp(void *_a, size_t one, size_t two)
       
  1077 {
       
  1078     if (one != two)
       
  1079     {
       
  1080         const ZIPentry *a = (const ZIPentry *) _a;
       
  1081         return strcmp(a[one].name, a[two].name);
       
  1082     } /* if */
       
  1083 
       
  1084     return 0;
       
  1085 } /* zip_entry_cmp */
       
  1086 
       
  1087 
       
  1088 static void zip_entry_swap(void *_a, size_t one, size_t two)
       
  1089 {
       
  1090     if (one != two)
       
  1091     {
       
  1092         ZIPentry tmp;
       
  1093         ZIPentry *first = &(((ZIPentry *) _a)[one]);
       
  1094         ZIPentry *second = &(((ZIPentry *) _a)[two]);
       
  1095         memcpy(&tmp, first, sizeof (ZIPentry));
       
  1096         memcpy(first, second, sizeof (ZIPentry));
       
  1097         memcpy(second, &tmp, sizeof (ZIPentry));
       
  1098     } /* if */
       
  1099 } /* zip_entry_swap */
       
  1100 
       
  1101 
       
  1102 static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info,
       
  1103                             const PHYSFS_uint64 data_ofs,
       
  1104                             const PHYSFS_uint64 central_ofs)
       
  1105 {
       
  1106     const PHYSFS_uint64 max = info->entryCount;
       
  1107     const int zip64 = info->zip64;
       
  1108     PHYSFS_uint64 i;
       
  1109 
       
  1110     BAIL_IF_MACRO(!io->seek(io, central_ofs), ERRPASS, 0);
       
  1111 
       
  1112     info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max);
       
  1113     BAIL_IF_MACRO(!info->entries, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  1114 
       
  1115     for (i = 0; i < max; i++)
       
  1116     {
       
  1117         if (!zip_load_entry(io, zip64, &info->entries[i], data_ofs))
       
  1118         {
       
  1119             zip_free_entries(info->entries, i);
       
  1120             return 0;
       
  1121         } /* if */
       
  1122     } /* for */
       
  1123 
       
  1124     __PHYSFS_sort(info->entries, (size_t) max, zip_entry_cmp, zip_entry_swap);
       
  1125     return 1;
       
  1126 } /* zip_load_entries */
       
  1127 
       
  1128 
       
  1129 static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io,
       
  1130                                                    PHYSFS_sint64 _pos,
       
  1131                                                    PHYSFS_uint64 offset)
       
  1132 {
       
  1133     /*
       
  1134      * Naturally, the offset is useless to us; it is the offset from the
       
  1135      *  start of file, which is meaningless if we've appended this .zip to
       
  1136      *  a self-extracting .exe. We need to find this on our own. It should
       
  1137      *  be directly before the locator record, but the record in question,
       
  1138      *  like the original end-of-central-directory record, ends with a
       
  1139      *  variable-length field. Unlike the original, which has to store the
       
  1140      *  size of that variable-length field in a 16-bit int and thus has to be
       
  1141      *  within 64k, the new one gets 64-bits.
       
  1142      *
       
  1143      * Fortunately, the only currently-specified record for that variable
       
  1144      *  length block is some weird proprietary thing that deals with EBCDIC
       
  1145      *  and tape backups or something. So we don't seek far.
       
  1146      */
       
  1147 
       
  1148     PHYSFS_uint32 ui32;
       
  1149     const PHYSFS_uint64 pos = (PHYSFS_uint64) _pos;
       
  1150 
       
  1151     assert(_pos > 0);
       
  1152 
       
  1153     /* Try offset specified in the Zip64 end of central directory locator. */
       
  1154     /* This works if the entire PHYSFS_Io is the zip file. */
       
  1155     BAIL_IF_MACRO(!io->seek(io, offset), ERRPASS, -1);
       
  1156     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
       
  1157     if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
       
  1158         return offset;
       
  1159 
       
  1160     /* Try 56 bytes before the Zip64 end of central directory locator. */
       
  1161     /* This works if the record isn't variable length and is version 1. */
       
  1162     if (pos > 56)
       
  1163     {
       
  1164         BAIL_IF_MACRO(!io->seek(io, pos-56), ERRPASS, -1);
       
  1165         BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
       
  1166         if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
       
  1167             return pos-56;
       
  1168     } /* if */
       
  1169 
       
  1170     /* Try 84 bytes before the Zip64 end of central directory locator. */
       
  1171     /* This works if the record isn't variable length and is version 2. */
       
  1172     if (pos > 84)
       
  1173     {
       
  1174         BAIL_IF_MACRO(!io->seek(io, pos-84), ERRPASS, -1);
       
  1175         BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
       
  1176         if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
       
  1177             return pos-84;
       
  1178     } /* if */
       
  1179 
       
  1180     /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */
       
  1181     /*  Just try moving back at most 256k. Oh well. */
       
  1182     if ((offset < pos) && (pos > 4))
       
  1183     {
       
  1184         /* we assume you can eat this stack if you handle Zip64 files. */
       
  1185         PHYSFS_uint8 buf[256 * 1024];
       
  1186         PHYSFS_uint64 len = pos - offset;
       
  1187         PHYSFS_sint32 i;
       
  1188 
       
  1189         if (len > sizeof (buf))
       
  1190             len = sizeof (buf);
       
  1191 
       
  1192         BAIL_IF_MACRO(!io->seek(io, pos - len), ERRPASS, -1);
       
  1193         BAIL_IF_MACRO(!__PHYSFS_readAll(io, buf, len), ERRPASS, -1);
       
  1194         for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--)
       
  1195         {
       
  1196             if (buf[i] != 0x50)
       
  1197                 continue;
       
  1198             if ( (buf[i+1] == 0x4b) &&
       
  1199                  (buf[i+2] == 0x06) &&
       
  1200                  (buf[i+3] == 0x06) )
       
  1201                 return pos - (len - i);
       
  1202         } /* for */
       
  1203     } /* if */
       
  1204 
       
  1205     BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1);  /* didn't find it. */
       
  1206 } /* zip64_find_end_of_central_dir */
       
  1207 
       
  1208 
       
  1209 static int zip64_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
       
  1210                                           PHYSFS_uint64 *data_start,
       
  1211                                           PHYSFS_uint64 *dir_ofs,
       
  1212                                           PHYSFS_sint64 pos)
       
  1213 {
       
  1214     PHYSFS_uint64 ui64;
       
  1215     PHYSFS_uint32 ui32;
       
  1216     PHYSFS_uint16 ui16;
       
  1217 
       
  1218     /* We should be positioned right past the locator signature. */
       
  1219 
       
  1220     if ((pos < 0) || (!io->seek(io, pos)))
       
  1221         return 0;
       
  1222 
       
  1223     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1224     if (ui32 != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG)
       
  1225         return -1;  /* it's not a Zip64 archive. Not an error, though! */
       
  1226 
       
  1227     info->zip64 = 1;
       
  1228 
       
  1229     /* number of the disk with the start of the central directory. */
       
  1230     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1231     BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1232 
       
  1233     /* offset of Zip64 end of central directory record. */
       
  1234     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1235 
       
  1236     /* total number of disks */
       
  1237     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1238     BAIL_IF_MACRO(ui32 != 1, PHYSFS_ERR_CORRUPT, 0);
       
  1239 
       
  1240     pos = zip64_find_end_of_central_dir(io, pos, ui64);
       
  1241     if (pos < 0)
       
  1242         return 0;  /* oh well. */
       
  1243 
       
  1244     /*
       
  1245      * For self-extracting archives, etc, there's crapola in the file
       
  1246      *  before the zipfile records; we calculate how much data there is
       
  1247      *  prepended by determining how far the zip64-end-of-central-directory
       
  1248      *  offset is from where it is supposed to be...the difference in bytes
       
  1249      *  is how much arbitrary data is at the start of the physical file.
       
  1250      */
       
  1251     assert(((PHYSFS_uint64) pos) >= ui64);
       
  1252     *data_start = ((PHYSFS_uint64) pos) - ui64;
       
  1253 
       
  1254     BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0);
       
  1255 
       
  1256     /* check signature again, just in case. */
       
  1257     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1258     BAIL_IF_MACRO(ui32 != ZIP64_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
       
  1259 
       
  1260     /* size of Zip64 end of central directory record. */
       
  1261     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1262 
       
  1263     /* version made by. */
       
  1264     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1265 
       
  1266     /* version needed to extract. */
       
  1267     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1268 
       
  1269     /* number of this disk. */
       
  1270     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1271     BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1272 
       
  1273     /* number of disk with start of central directory record. */
       
  1274     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1275     BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1276 
       
  1277     /* total number of entries in the central dir on this disk */
       
  1278     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1279 
       
  1280     /* total number of entries in the central dir */
       
  1281     BAIL_IF_MACRO(!readui64(io, &info->entryCount), ERRPASS, 0);
       
  1282     BAIL_IF_MACRO(ui64 != info->entryCount, PHYSFS_ERR_CORRUPT, 0);
       
  1283 
       
  1284     /* size of the central directory */
       
  1285     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1286 
       
  1287     /* offset of central directory */
       
  1288     BAIL_IF_MACRO(!readui64(io, dir_ofs), ERRPASS, 0);
       
  1289 
       
  1290     /* Since we know the difference, fix up the central dir offset... */
       
  1291     *dir_ofs += *data_start;
       
  1292 
       
  1293     /*
       
  1294      * There are more fields here, for encryption and feature-specific things,
       
  1295      *  but we don't care about any of them at the moment.
       
  1296      */
       
  1297 
       
  1298     return 1;  /* made it. */
       
  1299 } /* zip64_parse_end_of_central_dir */
       
  1300 
       
  1301 
       
  1302 static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
       
  1303                                         PHYSFS_uint64 *data_start,
       
  1304                                         PHYSFS_uint64 *dir_ofs)
       
  1305 {
       
  1306     PHYSFS_uint16 entryCount16;
       
  1307     PHYSFS_uint32 offset32;
       
  1308     PHYSFS_uint32 ui32;
       
  1309     PHYSFS_uint16 ui16;
       
  1310     PHYSFS_sint64 len;
       
  1311     PHYSFS_sint64 pos;
       
  1312     int rc;
       
  1313 
       
  1314     /* find the end-of-central-dir record, and seek to it. */
       
  1315     pos = zip_find_end_of_central_dir(io, &len);
       
  1316     BAIL_IF_MACRO(pos == -1, ERRPASS, 0);
       
  1317     BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0);
       
  1318 
       
  1319     /* check signature again, just in case. */
       
  1320     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1321     BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
       
  1322 
       
  1323     /* Seek back to see if "Zip64 end of central directory locator" exists. */
       
  1324     /* this record is 20 bytes before end-of-central-dir */
       
  1325     rc = zip64_parse_end_of_central_dir(io, info, data_start, dir_ofs, pos-20);
       
  1326     BAIL_IF_MACRO(rc == 0, ERRPASS, 0);
       
  1327     if (rc == 1)
       
  1328         return 1;  /* we're done here. */
       
  1329 
       
  1330     assert(rc == -1);  /* no error, just not a Zip64 archive. */
       
  1331 
       
  1332     /* Not Zip64? Seek back to where we were and keep processing. */
       
  1333     BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0);
       
  1334 
       
  1335     /* number of this disk */
       
  1336     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1337     BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1338 
       
  1339     /* number of the disk with the start of the central directory */
       
  1340     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1341     BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1342 
       
  1343     /* total number of entries in the central dir on this disk */
       
  1344     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1345 
       
  1346     /* total number of entries in the central dir */
       
  1347     BAIL_IF_MACRO(!readui16(io, &entryCount16), ERRPASS, 0);
       
  1348     BAIL_IF_MACRO(ui16 != entryCount16, PHYSFS_ERR_CORRUPT, 0);
       
  1349 
       
  1350     info->entryCount = entryCount16;
       
  1351 
       
  1352     /* size of the central directory */
       
  1353     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1354 
       
  1355     /* offset of central directory */
       
  1356     BAIL_IF_MACRO(!readui32(io, &offset32), ERRPASS, 0);
       
  1357     *dir_ofs = (PHYSFS_uint64) offset32;
       
  1358     BAIL_IF_MACRO(pos < (*dir_ofs + ui32), PHYSFS_ERR_CORRUPT, 0);
       
  1359 
       
  1360     /*
       
  1361      * For self-extracting archives, etc, there's crapola in the file
       
  1362      *  before the zipfile records; we calculate how much data there is
       
  1363      *  prepended by determining how far the central directory offset is
       
  1364      *  from where it is supposed to be (start of end-of-central-dir minus
       
  1365      *  sizeof central dir)...the difference in bytes is how much arbitrary
       
  1366      *  data is at the start of the physical file.
       
  1367      */
       
  1368     *data_start = (PHYSFS_uint64) (pos - (*dir_ofs + ui32));
       
  1369 
       
  1370     /* Now that we know the difference, fix up the central dir offset... */
       
  1371     *dir_ofs += *data_start;
       
  1372 
       
  1373     /* zipfile comment length */
       
  1374     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1375 
       
  1376     /*
       
  1377      * Make sure that the comment length matches to the end of file...
       
  1378      *  If it doesn't, we're either in the wrong part of the file, or the
       
  1379      *  file is corrupted, but we give up either way.
       
  1380      */
       
  1381     BAIL_IF_MACRO((pos + 22 + ui16) != len, PHYSFS_ERR_CORRUPT, 0);
       
  1382 
       
  1383     return 1;  /* made it. */
       
  1384 } /* zip_parse_end_of_central_dir */
       
  1385 
       
  1386 
       
  1387 static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
       
  1388 {
       
  1389     ZIPinfo *info = NULL;
       
  1390     PHYSFS_uint64 data_start;
       
  1391     PHYSFS_uint64 cent_dir_ofs;
       
  1392 
       
  1393     assert(io != NULL);  /* shouldn't ever happen. */
       
  1394 
       
  1395     BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
       
  1396     BAIL_IF_MACRO(!isZip(io), ERRPASS, NULL);
       
  1397 
       
  1398     info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
       
  1399     BAIL_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  1400     memset(info, '\0', sizeof (ZIPinfo));
       
  1401     info->io = io;
       
  1402 
       
  1403     if (!zip_parse_end_of_central_dir(io, info, &data_start, &cent_dir_ofs))
       
  1404         goto ZIP_openarchive_failed;
       
  1405 
       
  1406     if (!zip_load_entries(io, info, data_start, cent_dir_ofs))
       
  1407         goto ZIP_openarchive_failed;
       
  1408 
       
  1409     return info;
       
  1410 
       
  1411 ZIP_openarchive_failed:
       
  1412     if (info != NULL)
       
  1413         allocator.Free(info);
       
  1414 
       
  1415     return NULL;
       
  1416 } /* ZIP_openArchive */
       
  1417 
       
  1418 
       
  1419 static PHYSFS_sint64 zip_find_start_of_dir(ZIPinfo *info, const char *path,
       
  1420                                             int stop_on_first_find)
       
  1421 {
       
  1422     PHYSFS_sint64 lo = 0;
       
  1423     PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1);
       
  1424     PHYSFS_sint64 middle;
       
  1425     PHYSFS_uint32 dlen = (PHYSFS_uint32) strlen(path);
       
  1426     PHYSFS_sint64 retval = -1;
       
  1427     const char *name;
       
  1428     int rc;
       
  1429 
       
  1430     if (*path == '\0')  /* root dir? */
       
  1431         return 0;
       
  1432 
       
  1433     if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
       
  1434         dlen--;
       
  1435 
       
  1436     while (lo <= hi)
       
  1437     {
       
  1438         middle = lo + ((hi - lo) / 2);
       
  1439         name = info->entries[middle].name;
       
  1440         rc = strncmp(path, name, dlen);
       
  1441         if (rc == 0)
       
  1442         {
       
  1443             char ch = name[dlen];
       
  1444             if ('/' < ch) /* make sure this isn't just a substr match. */
       
  1445                 rc = -1;
       
  1446             else if ('/' > ch)
       
  1447                 rc = 1;
       
  1448             else 
       
  1449             {
       
  1450                 if (stop_on_first_find) /* Just checking dir's existance? */
       
  1451                     return middle;
       
  1452 
       
  1453                 if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
       
  1454                     return (middle + 1);
       
  1455 
       
  1456                 /* there might be more entries earlier in the list. */
       
  1457                 retval = middle;
       
  1458                 hi = middle - 1;
       
  1459             } /* else */
       
  1460         } /* if */
       
  1461 
       
  1462         if (rc > 0)
       
  1463             lo = middle + 1;
       
  1464         else
       
  1465             hi = middle - 1;
       
  1466     } /* while */
       
  1467 
       
  1468     return retval;
       
  1469 } /* zip_find_start_of_dir */
       
  1470 
       
  1471 
       
  1472 /*
       
  1473  * Moved to seperate function so we can use alloca then immediately throw
       
  1474  *  away the allocated stack space...
       
  1475  */
       
  1476 static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
       
  1477                            const char *odir, const char *str, PHYSFS_sint32 ln)
       
  1478 {
       
  1479     char *newstr = __PHYSFS_smallAlloc(ln + 1);
       
  1480     if (newstr == NULL)
       
  1481         return;
       
  1482 
       
  1483     memcpy(newstr, str, ln);
       
  1484     newstr[ln] = '\0';
       
  1485     cb(callbackdata, odir, newstr);
       
  1486     __PHYSFS_smallFree(newstr);
       
  1487 } /* doEnumCallback */
       
  1488 
       
  1489 
       
  1490 static void ZIP_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
       
  1491                                int omitSymLinks, PHYSFS_EnumFilesCallback cb,
       
  1492                                const char *origdir, void *callbackdata)
       
  1493 {
       
  1494     ZIPinfo *info = ((ZIPinfo *) opaque);
       
  1495     PHYSFS_sint32 dlen, dlen_inc;
       
  1496     PHYSFS_sint64 i, max;
       
  1497 
       
  1498     i = zip_find_start_of_dir(info, dname, 0);
       
  1499     if (i == -1)  /* no such directory. */
       
  1500         return;
       
  1501 
       
  1502     dlen = (PHYSFS_sint32) strlen(dname);
       
  1503     if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
       
  1504         dlen--;
       
  1505 
       
  1506     dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
       
  1507     max = (PHYSFS_sint64) info->entryCount;
       
  1508     while (i < max)
       
  1509     {
       
  1510         char *e = info->entries[i].name;
       
  1511         if ((dlen) && ((strncmp(e, dname, dlen) != 0) || (e[dlen] != '/')))
       
  1512             break;  /* past end of this dir; we're done. */
       
  1513 
       
  1514         if ((omitSymLinks) && (zip_entry_is_symlink(&info->entries[i])))
       
  1515             i++;
       
  1516         else
       
  1517         {
       
  1518             char *add = e + dlen_inc;
       
  1519             char *ptr = strchr(add, '/');
       
  1520             PHYSFS_sint32 ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
       
  1521             doEnumCallback(cb, callbackdata, origdir, add, ln);
       
  1522             ln += dlen_inc;  /* point past entry to children... */
       
  1523 
       
  1524             /* increment counter and skip children of subdirs... */
       
  1525             while ((++i < max) && (ptr != NULL))
       
  1526             {
       
  1527                 char *e_new = info->entries[i].name;
       
  1528                 if ((strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/'))
       
  1529                     break;
       
  1530             } /* while */
       
  1531         } /* else */
       
  1532     } /* while */
       
  1533 } /* ZIP_enumerateFiles */
       
  1534 
       
  1535 
       
  1536 static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry)
       
  1537 {
       
  1538     int success;
       
  1539     PHYSFS_Io *retval = io->duplicate(io);
       
  1540     BAIL_IF_MACRO(!retval, ERRPASS, NULL);
       
  1541 
       
  1542     /* !!! FIXME: if you open a dir here, it should bail ERR_NOT_A_FILE */
       
  1543 
       
  1544     /* (inf) can be NULL if we already resolved. */
       
  1545     success = (inf == NULL) || zip_resolve(retval, inf, entry);
       
  1546     if (success)
       
  1547     {
       
  1548         PHYSFS_sint64 offset;
       
  1549         offset = ((entry->symlink) ? entry->symlink->offset : entry->offset);
       
  1550         success = retval->seek(retval, offset);
       
  1551     } /* if */
       
  1552 
       
  1553     if (!success)
       
  1554     {
       
  1555         retval->destroy(retval);
       
  1556         retval = NULL;
       
  1557     } /* if */
       
  1558 
       
  1559     return retval;
       
  1560 } /* zip_get_io */
       
  1561 
       
  1562 
       
  1563 static PHYSFS_Io *ZIP_openRead(PHYSFS_Dir *opaque, const char *fnm,
       
  1564                                int *fileExists)
       
  1565 {
       
  1566     PHYSFS_Io *retval = NULL;
       
  1567     ZIPinfo *info = (ZIPinfo *) opaque;
       
  1568     ZIPentry *entry = zip_find_entry(info, fnm, NULL);
       
  1569     ZIPfileinfo *finfo = NULL;
       
  1570 
       
  1571     *fileExists = (entry != NULL);
       
  1572     BAIL_IF_MACRO(!entry, ERRPASS, NULL);
       
  1573 
       
  1574     retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
  1575     GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
       
  1576 
       
  1577     finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
       
  1578     GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
       
  1579     memset(finfo, '\0', sizeof (ZIPfileinfo));
       
  1580 
       
  1581     finfo->io = zip_get_io(info->io, info, entry);
       
  1582     GOTO_IF_MACRO(!finfo->io, ERRPASS, ZIP_openRead_failed);
       
  1583     finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry);
       
  1584     initializeZStream(&finfo->stream);
       
  1585 
       
  1586     if (finfo->entry->compression_method != COMPMETH_NONE)
       
  1587     {
       
  1588         finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
       
  1589         if (!finfo->buffer)
       
  1590             GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
       
  1591         else if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
       
  1592             goto ZIP_openRead_failed;
       
  1593     } /* if */
       
  1594 
       
  1595     memcpy(retval, &ZIP_Io, sizeof (PHYSFS_Io));
       
  1596     retval->opaque = finfo;
       
  1597 
       
  1598     return retval;
       
  1599 
       
  1600 ZIP_openRead_failed:
       
  1601     if (finfo != NULL)
       
  1602     {
       
  1603         if (finfo->io != NULL)
       
  1604             finfo->io->destroy(finfo->io);
       
  1605 
       
  1606         if (finfo->buffer != NULL)
       
  1607         {
       
  1608             allocator.Free(finfo->buffer);
       
  1609             inflateEnd(&finfo->stream);
       
  1610         } /* if */
       
  1611 
       
  1612         allocator.Free(finfo);
       
  1613     } /* if */
       
  1614 
       
  1615     if (retval != NULL)
       
  1616         allocator.Free(retval);
       
  1617 
       
  1618     return NULL;
       
  1619 } /* ZIP_openRead */
       
  1620 
       
  1621 
       
  1622 static PHYSFS_Io *ZIP_openWrite(PHYSFS_Dir *opaque, const char *filename)
       
  1623 {
       
  1624     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
  1625 } /* ZIP_openWrite */
       
  1626 
       
  1627 
       
  1628 static PHYSFS_Io *ZIP_openAppend(PHYSFS_Dir *opaque, const char *filename)
       
  1629 {
       
  1630     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
  1631 } /* ZIP_openAppend */
       
  1632 
       
  1633 
       
  1634 static void ZIP_closeArchive(PHYSFS_Dir *opaque)
       
  1635 {
       
  1636     ZIPinfo *zi = (ZIPinfo *) (opaque);
       
  1637     zi->io->destroy(zi->io);
       
  1638     zip_free_entries(zi->entries, zi->entryCount);
       
  1639     allocator.Free(zi);
       
  1640 } /* ZIP_closeArchive */
       
  1641 
       
  1642 
       
  1643 static int ZIP_remove(PHYSFS_Dir *opaque, const char *name)
       
  1644 {
       
  1645     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
  1646 } /* ZIP_remove */
       
  1647 
       
  1648 
       
  1649 static int ZIP_mkdir(PHYSFS_Dir *opaque, const char *name)
       
  1650 {
       
  1651     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
  1652 } /* ZIP_mkdir */
       
  1653 
       
  1654 
       
  1655 static int ZIP_stat(PHYSFS_Dir *opaque, const char *filename, int *exists,
       
  1656                     PHYSFS_Stat *stat)
       
  1657 {
       
  1658     int isDir = 0;
       
  1659     const ZIPinfo *info = (const ZIPinfo *) opaque;
       
  1660     const ZIPentry *entry = zip_find_entry(info, filename, &isDir);
       
  1661 
       
  1662     /* !!! FIXME: does this need to resolve entries here? */
       
  1663 
       
  1664     *exists = isDir || (entry != 0);
       
  1665     if (!*exists)
       
  1666         return 0;
       
  1667 
       
  1668     if (isDir)
       
  1669     {
       
  1670         stat->filesize = 0;
       
  1671         stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
       
  1672     } /* if */
       
  1673 
       
  1674     else if (zip_entry_is_symlink(entry))
       
  1675     {
       
  1676         stat->filesize = 0;
       
  1677         stat->filetype = PHYSFS_FILETYPE_SYMLINK;
       
  1678     } /* else if */
       
  1679 
       
  1680     else
       
  1681     {
       
  1682         stat->filesize = (PHYSFS_sint64) entry->uncompressed_size;
       
  1683         stat->filetype = PHYSFS_FILETYPE_REGULAR;
       
  1684     } /* else */
       
  1685 
       
  1686     stat->modtime = ((entry) ? entry->last_mod_time : 0);
       
  1687     stat->createtime = stat->modtime;
       
  1688     stat->accesstime = 0;
       
  1689     stat->readonly = 1; /* .zip files are always read only */
       
  1690 
       
  1691     return 1;
       
  1692 } /* ZIP_stat */
       
  1693 
       
  1694 
       
  1695 const PHYSFS_Archiver __PHYSFS_Archiver_ZIP =
       
  1696 {
       
  1697     {
       
  1698         "ZIP",
       
  1699         "PkZip/WinZip/Info-Zip compatible",
       
  1700         "Ryan C. Gordon <icculus@icculus.org>",
       
  1701         "http://icculus.org/physfs/",
       
  1702     },
       
  1703     ZIP_openArchive,        /* openArchive() method    */
       
  1704     ZIP_enumerateFiles,     /* enumerateFiles() method */
       
  1705     ZIP_openRead,           /* openRead() method       */
       
  1706     ZIP_openWrite,          /* openWrite() method      */
       
  1707     ZIP_openAppend,         /* openAppend() method     */
       
  1708     ZIP_remove,             /* remove() method         */
       
  1709     ZIP_mkdir,              /* mkdir() method          */
       
  1710     ZIP_closeArchive,       /* closeArchive() method   */
       
  1711     ZIP_stat                /* stat() method           */
       
  1712 };
       
  1713 
       
  1714 #endif  /* defined PHYSFS_SUPPORTS_ZIP */
       
  1715 
       
  1716 /* end of zip.c ... */
       
  1717