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