misc/libphysfs/archiver_zip.c
branchui-scaling
changeset 15283 c4fd2813b127
parent 13390 0135e64c6c66
parent 15279 7ab5cf405686
child 15663 d92eeb468dad
equal deleted inserted replaced
13390:0135e64c6c66 15283:c4fd2813b127
     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     if (finfo->entry->compression_method != COMPMETH_NONE)
       
   472     {
       
   473         finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
       
   474         GOTO_IF_MACRO(!finfo->buffer, PHYSFS_ERR_OUT_OF_MEMORY, failed);
       
   475         if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
       
   476             goto failed;
       
   477     } /* if */
       
   478 
       
   479     memcpy(retval, io, sizeof (PHYSFS_Io));
       
   480     retval->opaque = finfo;
       
   481     return retval;
       
   482 
       
   483 failed:
       
   484     if (finfo != NULL)
       
   485     {
       
   486         if (finfo->io != NULL)
       
   487             finfo->io->destroy(finfo->io);
       
   488 
       
   489         if (finfo->buffer != NULL)
       
   490         {
       
   491             allocator.Free(finfo->buffer);
       
   492             inflateEnd(&finfo->stream);
       
   493         } /* if */
       
   494 
       
   495         allocator.Free(finfo);
       
   496     } /* if */
       
   497 
       
   498     if (retval != NULL)
       
   499         allocator.Free(retval);
       
   500 
       
   501     return NULL;
       
   502 } /* ZIP_duplicate */
       
   503 
       
   504 static int ZIP_flush(PHYSFS_Io *io) { return 1;  /* no write support. */ }
       
   505 
       
   506 static void ZIP_destroy(PHYSFS_Io *io)
       
   507 {
       
   508     ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
       
   509     finfo->io->destroy(finfo->io);
       
   510 
       
   511     if (finfo->entry->compression_method != COMPMETH_NONE)
       
   512         inflateEnd(&finfo->stream);
       
   513 
       
   514     if (finfo->buffer != NULL)
       
   515         allocator.Free(finfo->buffer);
       
   516 
       
   517     allocator.Free(finfo);
       
   518     allocator.Free(io);
       
   519 } /* ZIP_destroy */
       
   520 
       
   521 
       
   522 static const PHYSFS_Io ZIP_Io =
       
   523 {
       
   524     CURRENT_PHYSFS_IO_API_VERSION, NULL,
       
   525     ZIP_read,
       
   526     ZIP_write,
       
   527     ZIP_seek,
       
   528     ZIP_tell,
       
   529     ZIP_length,
       
   530     ZIP_duplicate,
       
   531     ZIP_flush,
       
   532     ZIP_destroy
       
   533 };
       
   534 
       
   535 
       
   536 
       
   537 static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
       
   538 {
       
   539     PHYSFS_uint8 buf[256];
       
   540     PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
       
   541     PHYSFS_sint32 i = 0;
       
   542     PHYSFS_sint64 filelen;
       
   543     PHYSFS_sint64 filepos;
       
   544     PHYSFS_sint32 maxread;
       
   545     PHYSFS_sint32 totalread = 0;
       
   546     int found = 0;
       
   547 
       
   548     filelen = io->length(io);
       
   549     BAIL_IF_MACRO(filelen == -1, ERRPASS, -1);
       
   550 
       
   551     /*
       
   552      * Jump to the end of the file and start reading backwards.
       
   553      *  The last thing in the file is the zipfile comment, which is variable
       
   554      *  length, and the field that specifies its size is before it in the
       
   555      *  file (argh!)...this means that we need to scan backwards until we
       
   556      *  hit the end-of-central-dir signature. We can then sanity check that
       
   557      *  the comment was as big as it should be to make sure we're in the
       
   558      *  right place. The comment length field is 16 bits, so we can stop
       
   559      *  searching for that signature after a little more than 64k at most,
       
   560      *  and call it a corrupted zipfile.
       
   561      */
       
   562 
       
   563     if (sizeof (buf) < filelen)
       
   564     {
       
   565         filepos = filelen - sizeof (buf);
       
   566         maxread = sizeof (buf);
       
   567     } /* if */
       
   568     else
       
   569     {
       
   570         filepos = 0;
       
   571         maxread = (PHYSFS_uint32) filelen;
       
   572     } /* else */
       
   573 
       
   574     while ((totalread < filelen) && (totalread < 65557))
       
   575     {
       
   576         BAIL_IF_MACRO(!io->seek(io, filepos), ERRPASS, -1);
       
   577 
       
   578         /* make sure we catch a signature between buffers. */
       
   579         if (totalread != 0)
       
   580         {
       
   581             if (!__PHYSFS_readAll(io, buf, maxread - 4))
       
   582                 return -1;
       
   583             memcpy(&buf[maxread - 4], &extra, sizeof (extra));
       
   584             totalread += maxread - 4;
       
   585         } /* if */
       
   586         else
       
   587         {
       
   588             if (!__PHYSFS_readAll(io, buf, maxread))
       
   589                 return -1;
       
   590             totalread += maxread;
       
   591         } /* else */
       
   592 
       
   593         memcpy(&extra, buf, sizeof (extra));
       
   594 
       
   595         for (i = maxread - 4; i > 0; i--)
       
   596         {
       
   597             if ((buf[i + 0] == 0x50) &&
       
   598                 (buf[i + 1] == 0x4B) &&
       
   599                 (buf[i + 2] == 0x05) &&
       
   600                 (buf[i + 3] == 0x06) )
       
   601             {
       
   602                 found = 1;  /* that's the signature! */
       
   603                 break;  
       
   604             } /* if */
       
   605         } /* for */
       
   606 
       
   607         if (found)
       
   608             break;
       
   609 
       
   610         filepos -= (maxread - 4);
       
   611         if (filepos < 0)
       
   612             filepos = 0;
       
   613     } /* while */
       
   614 
       
   615     BAIL_IF_MACRO(!found, PHYSFS_ERR_UNSUPPORTED, -1);
       
   616 
       
   617     if (len != NULL)
       
   618         *len = filelen;
       
   619 
       
   620     return (filepos + i);
       
   621 } /* zip_find_end_of_central_dir */
       
   622 
       
   623 
       
   624 static int isZip(PHYSFS_Io *io)
       
   625 {
       
   626     PHYSFS_uint32 sig = 0;
       
   627     int retval = 0;
       
   628 
       
   629     /*
       
   630      * The first thing in a zip file might be the signature of the
       
   631      *  first local file record, so it makes for a quick determination.
       
   632      */
       
   633     if (readui32(io, &sig))
       
   634     {
       
   635         retval = (sig == ZIP_LOCAL_FILE_SIG);
       
   636         if (!retval)
       
   637         {
       
   638             /*
       
   639              * No sig...might be a ZIP with data at the start
       
   640              *  (a self-extracting executable, etc), so we'll have to do
       
   641              *  it the hard way...
       
   642              */
       
   643             retval = (zip_find_end_of_central_dir(io, NULL) != -1);
       
   644         } /* if */
       
   645     } /* if */
       
   646 
       
   647     return retval;
       
   648 } /* isZip */
       
   649 
       
   650 
       
   651 /* Find the ZIPentry for a path in platform-independent notation. */
       
   652 static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path)
       
   653 {
       
   654     PHYSFS_uint32 hashval;
       
   655     ZIPentry *prev = NULL;
       
   656     ZIPentry *retval;
       
   657 
       
   658     if (*path == '\0')
       
   659         return &info->root;
       
   660 
       
   661     hashval = zip_hash_string(info, path);
       
   662     for (retval = info->hash[hashval]; retval; retval = retval->hashnext)
       
   663     {
       
   664         if (strcmp(retval->name, path) == 0)
       
   665         {
       
   666             if (prev != NULL)  /* move this to the front of the list */
       
   667             {
       
   668                 prev->hashnext = retval->hashnext;
       
   669                 retval->hashnext = info->hash[hashval];
       
   670                 info->hash[hashval] = retval;
       
   671             } /* if */
       
   672 
       
   673             return retval;
       
   674         } /* if */
       
   675 
       
   676         prev = retval;
       
   677     } /* for */
       
   678 
       
   679     BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, NULL);
       
   680 } /* zip_find_entry */
       
   681 
       
   682 
       
   683 /* Convert paths from old, buggy DOS zippers... */
       
   684 static void zip_convert_dos_path(ZIPentry *entry, char *path)
       
   685 {
       
   686     PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF);
       
   687     if (hosttype == 0)  /* FS_FAT_ */
       
   688     {
       
   689         while (*path)
       
   690         {
       
   691             if (*path == '\\')
       
   692                 *path = '/';
       
   693             path++;
       
   694         } /* while */
       
   695     } /* if */
       
   696 } /* zip_convert_dos_path */
       
   697 
       
   698 
       
   699 static void zip_expand_symlink_path(char *path)
       
   700 {
       
   701     char *ptr = path;
       
   702     char *prevptr = path;
       
   703 
       
   704     while (1)
       
   705     {
       
   706         ptr = strchr(ptr, '/');
       
   707         if (ptr == NULL)
       
   708             break;
       
   709 
       
   710         if (*(ptr + 1) == '.')
       
   711         {
       
   712             if (*(ptr + 2) == '/')
       
   713             {
       
   714                 /* current dir in middle of string: ditch it. */
       
   715                 memmove(ptr, ptr + 2, strlen(ptr + 2) + 1);
       
   716             } /* else if */
       
   717 
       
   718             else if (*(ptr + 2) == '\0')
       
   719             {
       
   720                 /* current dir at end of string: ditch it. */
       
   721                 *ptr = '\0';
       
   722             } /* else if */
       
   723 
       
   724             else if (*(ptr + 2) == '.')
       
   725             {
       
   726                 if (*(ptr + 3) == '/')
       
   727                 {
       
   728                     /* parent dir in middle: move back one, if possible. */
       
   729                     memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1);
       
   730                     ptr = prevptr;
       
   731                     while (prevptr != path)
       
   732                     {
       
   733                         prevptr--;
       
   734                         if (*prevptr == '/')
       
   735                         {
       
   736                             prevptr++;
       
   737                             break;
       
   738                         } /* if */
       
   739                     } /* while */
       
   740                 } /* if */
       
   741 
       
   742                 if (*(ptr + 3) == '\0')
       
   743                 {
       
   744                     /* parent dir at end: move back one, if possible. */
       
   745                     *prevptr = '\0';
       
   746                 } /* if */
       
   747             } /* if */
       
   748         } /* if */
       
   749         else
       
   750         {
       
   751             prevptr = ptr;
       
   752             ptr++;
       
   753         } /* else */
       
   754     } /* while */
       
   755 } /* zip_expand_symlink_path */
       
   756 
       
   757 /* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
       
   758 static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
       
   759 
       
   760 /*
       
   761  * Look for the entry named by (path). If it exists, resolve it, and return
       
   762  *  a pointer to that entry. If it's another symlink, keep resolving until you
       
   763  *  hit a real file and then return a pointer to the final non-symlink entry.
       
   764  *  If there's a problem, return NULL.
       
   765  */
       
   766 static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
       
   767 {
       
   768     ZIPentry *entry;
       
   769 
       
   770     zip_expand_symlink_path(path);
       
   771     entry = zip_find_entry(info, path);
       
   772     if (entry != NULL)
       
   773     {
       
   774         if (!zip_resolve(io, info, entry))  /* recursive! */
       
   775             entry = NULL;
       
   776         else
       
   777         {
       
   778             if (entry->symlink != NULL)
       
   779                 entry = entry->symlink;
       
   780         } /* else */
       
   781     } /* if */
       
   782 
       
   783     return entry;
       
   784 } /* zip_follow_symlink */
       
   785 
       
   786 
       
   787 static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
       
   788 {
       
   789     const PHYSFS_uint64 size = entry->uncompressed_size;
       
   790     char *path = NULL;
       
   791     int rc = 0;
       
   792 
       
   793     /*
       
   794      * We've already parsed the local file header of the symlink at this
       
   795      *  point. Now we need to read the actual link from the file data and
       
   796      *  follow it.
       
   797      */
       
   798 
       
   799     BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
       
   800 
       
   801     path = (char *) __PHYSFS_smallAlloc(size + 1);
       
   802     BAIL_IF_MACRO(!path, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
   803     
       
   804     if (entry->compression_method == COMPMETH_NONE)
       
   805         rc = __PHYSFS_readAll(io, path, size);
       
   806 
       
   807     else  /* symlink target path is compressed... */
       
   808     {
       
   809         z_stream stream;
       
   810         const PHYSFS_uint64 complen = entry->compressed_size;
       
   811         PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
       
   812         if (compressed != NULL)
       
   813         {
       
   814             if (__PHYSFS_readAll(io, compressed, complen))
       
   815             {
       
   816                 initializeZStream(&stream);
       
   817                 stream.next_in = compressed;
       
   818                 stream.avail_in = complen;
       
   819                 stream.next_out = (unsigned char *) path;
       
   820                 stream.avail_out = size;
       
   821                 if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
       
   822                 {
       
   823                     rc = zlib_err(inflate(&stream, Z_FINISH));
       
   824                     inflateEnd(&stream);
       
   825 
       
   826                     /* both are acceptable outcomes... */
       
   827                     rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
       
   828                 } /* if */
       
   829             } /* if */
       
   830             __PHYSFS_smallFree(compressed);
       
   831         } /* if */
       
   832     } /* else */
       
   833 
       
   834     if (rc)
       
   835     {
       
   836         path[entry->uncompressed_size] = '\0';    /* null-terminate it. */
       
   837         zip_convert_dos_path(entry, path);
       
   838         entry->symlink = zip_follow_symlink(io, info, path);
       
   839     } /* else */
       
   840 
       
   841     __PHYSFS_smallFree(path);
       
   842 
       
   843     return (entry->symlink != NULL);
       
   844 } /* zip_resolve_symlink */
       
   845 
       
   846 
       
   847 /*
       
   848  * Parse the local file header of an entry, and update entry->offset.
       
   849  */
       
   850 static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
       
   851 {
       
   852     PHYSFS_uint32 ui32;
       
   853     PHYSFS_uint16 ui16;
       
   854     PHYSFS_uint16 fnamelen;
       
   855     PHYSFS_uint16 extralen;
       
   856 
       
   857     /*
       
   858      * crc and (un)compressed_size are always zero if this is a "JAR"
       
   859      *  archive created with Sun's Java tools, apparently. We only
       
   860      *  consider this archive corrupted if those entries don't match and
       
   861      *  aren't zero. That seems to work well.
       
   862      * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's
       
   863      *  possible that's a Zip64 thing.
       
   864      */
       
   865 
       
   866     /* !!! FIXME: apparently these are zero if general purpose bit 3 is set,
       
   867        !!! FIXME:  which is probably true for Jar files, fwiw, but we don't
       
   868        !!! FIXME:  care about these values anyhow. */
       
   869 
       
   870     BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
       
   871     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   872     BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0);
       
   873     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
   874     BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0);
       
   875     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);  /* general bits. */
       
   876     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
   877     BAIL_IF_MACRO(ui16 != entry->compression_method, PHYSFS_ERR_CORRUPT, 0);
       
   878     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);  /* date/time */
       
   879     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   880     BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0);
       
   881 
       
   882     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   883     BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
       
   884                   (ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0);
       
   885 
       
   886     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
   887     BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
       
   888                  (ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0);
       
   889 
       
   890     BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
       
   891     BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
       
   892 
       
   893     entry->offset += fnamelen + extralen + 30;
       
   894     return 1;
       
   895 } /* zip_parse_local */
       
   896 
       
   897 
       
   898 static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
       
   899 {
       
   900     int retval = 1;
       
   901     const ZipResolveType resolve_type = entry->resolved;
       
   902 
       
   903     if (resolve_type == ZIP_DIRECTORY)
       
   904         return 1;   /* we're good. */
       
   905 
       
   906     /* Don't bother if we've failed to resolve this entry before. */
       
   907     BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, PHYSFS_ERR_CORRUPT, 0);
       
   908     BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, PHYSFS_ERR_CORRUPT, 0);
       
   909 
       
   910     /* uhoh...infinite symlink loop! */
       
   911     BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, PHYSFS_ERR_SYMLINK_LOOP, 0);
       
   912 
       
   913     /*
       
   914      * We fix up the offset to point to the actual data on the
       
   915      *  first open, since we don't want to seek across the whole file on
       
   916      *  archive open (can be SLOW on large, CD-stored files), but we
       
   917      *  need to check the local file header...not just for corruption,
       
   918      *  but since it stores offset info the central directory does not.
       
   919      */
       
   920     if (resolve_type != ZIP_RESOLVED)
       
   921     {
       
   922         entry->resolved = ZIP_RESOLVING;
       
   923 
       
   924         retval = zip_parse_local(io, entry);
       
   925         if (retval)
       
   926         {
       
   927             /*
       
   928              * If it's a symlink, find the original file. This will cause
       
   929              *  resolution of other entries (other symlinks and, eventually,
       
   930              *  the real file) if all goes well.
       
   931              */
       
   932             if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
       
   933                 retval = zip_resolve_symlink(io, info, entry);
       
   934         } /* if */
       
   935 
       
   936         if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
       
   937             entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK);
       
   938         else if (resolve_type == ZIP_UNRESOLVED_FILE)
       
   939             entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE);
       
   940     } /* if */
       
   941 
       
   942     return retval;
       
   943 } /* zip_resolve */
       
   944 
       
   945 
       
   946 static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry);
       
   947 
       
   948 /* Fill in missing parent directories. */
       
   949 static ZIPentry *zip_hash_ancestors(ZIPinfo *info, char *name)
       
   950 {
       
   951     ZIPentry *retval = &info->root;
       
   952     char *sep = strrchr(name, '/');
       
   953 
       
   954     if (sep)
       
   955     {
       
   956         const size_t namelen = (sep - name) + 1;
       
   957 
       
   958         *sep = '\0';  /* chop off last piece. */
       
   959         retval = zip_find_entry(info, name);
       
   960         *sep = '/';
       
   961 
       
   962         if (retval != NULL)
       
   963         {
       
   964             if (retval->resolved != ZIP_DIRECTORY)
       
   965                 BAIL_MACRO(PHYSFS_ERR_CORRUPT, NULL);
       
   966             return retval;  /* already hashed. */
       
   967         } /* if */
       
   968 
       
   969         /* okay, this is a new dir. Build and hash us. */
       
   970         retval = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) + namelen);
       
   971         BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   972         memset(retval, '\0', sizeof (*retval));
       
   973         retval->name = ((char *) retval) + sizeof (ZIPentry);
       
   974         memcpy(retval->name, name, namelen - 1);
       
   975         retval->name[namelen - 1] = '\0';
       
   976         retval->resolved = ZIP_DIRECTORY;
       
   977         if (!zip_hash_entry(info, retval))
       
   978         {
       
   979             allocator.Free(retval);
       
   980             return NULL;
       
   981         } /* if */
       
   982     } /* else */
       
   983 
       
   984     return retval;
       
   985 } /* zip_hash_ancestors */
       
   986 
       
   987 
       
   988 static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry)
       
   989 {
       
   990     PHYSFS_uint32 hashval;
       
   991     ZIPentry *parent;
       
   992 
       
   993     assert(!zip_find_entry(info, entry->name));  /* checked elsewhere */
       
   994 
       
   995     parent = zip_hash_ancestors(info, entry->name);
       
   996     if (!parent)
       
   997         return 0;
       
   998 
       
   999     hashval = zip_hash_string(info, entry->name);
       
  1000     entry->hashnext = info->hash[hashval];
       
  1001     info->hash[hashval] = entry;
       
  1002 
       
  1003     entry->sibling = parent->children;
       
  1004     parent->children = entry;
       
  1005     return 1;
       
  1006 } /* zip_hash_entry */
       
  1007 
       
  1008 
       
  1009 static int zip_entry_is_symlink(const ZIPentry *entry)
       
  1010 {
       
  1011     return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
       
  1012             (entry->resolved == ZIP_BROKEN_SYMLINK) ||
       
  1013             (entry->symlink));
       
  1014 } /* zip_entry_is_symlink */
       
  1015 
       
  1016 
       
  1017 static int zip_version_does_symlinks(PHYSFS_uint32 version)
       
  1018 {
       
  1019     int retval = 0;
       
  1020     PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
       
  1021 
       
  1022     switch (hosttype)
       
  1023     {
       
  1024             /*
       
  1025              * These are the platforms that can NOT build an archive with
       
  1026              *  symlinks, according to the Info-ZIP project.
       
  1027              */
       
  1028         case 0:  /* FS_FAT_  */
       
  1029         case 1:  /* AMIGA_   */
       
  1030         case 2:  /* VMS_     */
       
  1031         case 4:  /* VM_CSM_  */
       
  1032         case 6:  /* FS_HPFS_ */
       
  1033         case 11: /* FS_NTFS_ */
       
  1034         case 14: /* FS_VFAT_ */
       
  1035         case 13: /* ACORN_   */
       
  1036         case 15: /* MVS_     */
       
  1037         case 18: /* THEOS_   */
       
  1038             break;  /* do nothing. */
       
  1039 
       
  1040         default:  /* assume the rest to be unix-like. */
       
  1041             retval = 1;
       
  1042             break;
       
  1043     } /* switch */
       
  1044 
       
  1045     return retval;
       
  1046 } /* zip_version_does_symlinks */
       
  1047 
       
  1048 
       
  1049 static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr)
       
  1050 {
       
  1051     PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
       
  1052     return ( (zip_version_does_symlinks(entry->version)) &&
       
  1053              (entry->uncompressed_size > 0) &&
       
  1054              ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );
       
  1055 } /* zip_has_symlink_attr */
       
  1056 
       
  1057 
       
  1058 static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
       
  1059 {
       
  1060     PHYSFS_uint32 dosdate;
       
  1061     struct tm unixtime;
       
  1062     memset(&unixtime, '\0', sizeof (unixtime));
       
  1063 
       
  1064     dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
       
  1065     dostime &= 0xFFFF;
       
  1066 
       
  1067     /* dissect date */
       
  1068     unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80;
       
  1069     unixtime.tm_mon  = ((dosdate >> 5) & 0x0F) - 1;
       
  1070     unixtime.tm_mday = ((dosdate     ) & 0x1F);
       
  1071 
       
  1072     /* dissect time */
       
  1073     unixtime.tm_hour = ((dostime >> 11) & 0x1F);
       
  1074     unixtime.tm_min  = ((dostime >>  5) & 0x3F);
       
  1075     unixtime.tm_sec  = ((dostime <<  1) & 0x3E);
       
  1076 
       
  1077     /* let mktime calculate daylight savings time. */
       
  1078     unixtime.tm_isdst = -1;
       
  1079 
       
  1080     return ((PHYSFS_sint64) mktime(&unixtime));
       
  1081 } /* zip_dos_time_to_physfs_time */
       
  1082 
       
  1083 
       
  1084 static ZIPentry *zip_load_entry(PHYSFS_Io *io, const int zip64,
       
  1085                                 const PHYSFS_uint64 ofs_fixup)
       
  1086 {
       
  1087     ZIPentry entry;
       
  1088     ZIPentry *retval = NULL;
       
  1089     PHYSFS_uint16 fnamelen, extralen, commentlen;
       
  1090     PHYSFS_uint32 external_attr;
       
  1091     PHYSFS_uint32 starting_disk;
       
  1092     PHYSFS_uint64 offset;
       
  1093     PHYSFS_uint16 ui16;
       
  1094     PHYSFS_uint32 ui32;
       
  1095     PHYSFS_sint64 si64;
       
  1096 
       
  1097     memset(&entry, '\0', sizeof (entry));
       
  1098 
       
  1099     /* sanity check with central directory signature... */
       
  1100     if (!readui32(io, &ui32)) return NULL;
       
  1101     BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, NULL);
       
  1102 
       
  1103     /* Get the pertinent parts of the record... */
       
  1104     if (!readui16(io, &entry.version)) return NULL;
       
  1105     if (!readui16(io, &entry.version_needed)) return NULL;
       
  1106     if (!readui16(io, &entry.general_bits)) return NULL;  /* general bits */
       
  1107     if (!readui16(io, &entry.compression_method)) return NULL;
       
  1108     if (!readui32(io, &entry.dos_mod_time)) return NULL;
       
  1109     entry.last_mod_time = zip_dos_time_to_physfs_time(entry.dos_mod_time);
       
  1110     if (!readui32(io, &entry.crc)) return NULL;
       
  1111     if (!readui32(io, &ui32)) return NULL;
       
  1112     entry.compressed_size = (PHYSFS_uint64) ui32;
       
  1113     if (!readui32(io, &ui32)) return NULL;
       
  1114     entry.uncompressed_size = (PHYSFS_uint64) ui32;
       
  1115     if (!readui16(io, &fnamelen)) return NULL;
       
  1116     if (!readui16(io, &extralen)) return NULL;
       
  1117     if (!readui16(io, &commentlen)) return NULL;
       
  1118     if (!readui16(io, &ui16)) return NULL;
       
  1119     starting_disk = (PHYSFS_uint32) ui16;
       
  1120     if (!readui16(io, &ui16)) return NULL;  /* internal file attribs */
       
  1121     if (!readui32(io, &external_attr)) return NULL;
       
  1122     if (!readui32(io, &ui32)) return NULL;
       
  1123     offset = (PHYSFS_uint64) ui32;
       
  1124 
       
  1125     retval = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) + fnamelen + 1);
       
  1126     BAIL_IF_MACRO(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  1127     memcpy(retval, &entry, sizeof (*retval));
       
  1128     retval->name = ((char *) retval) + sizeof (ZIPentry);
       
  1129 
       
  1130     if (!__PHYSFS_readAll(io, retval->name, fnamelen))
       
  1131         goto zip_load_entry_puked;
       
  1132 
       
  1133     retval->name[fnamelen] = '\0';  /* null-terminate the filename. */
       
  1134     zip_convert_dos_path(retval, retval->name);
       
  1135 
       
  1136     retval->symlink = NULL;  /* will be resolved later, if necessary. */
       
  1137 
       
  1138     if (retval->name[fnamelen - 1] == '/')
       
  1139     {
       
  1140         retval->name[fnamelen - 1] = '\0';
       
  1141         retval->resolved = ZIP_DIRECTORY;
       
  1142     } /* if */
       
  1143     else
       
  1144     {
       
  1145         retval->resolved = (zip_has_symlink_attr(&entry, external_attr)) ?
       
  1146                                 ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE;
       
  1147     } /* else */
       
  1148 
       
  1149     si64 = io->tell(io);
       
  1150     if (si64 == -1)
       
  1151         goto zip_load_entry_puked;
       
  1152 
       
  1153     /*
       
  1154      * The actual sizes didn't fit in 32-bits; look for the Zip64
       
  1155      *  extended information extra field...
       
  1156      */
       
  1157     if ( (zip64) &&
       
  1158          ((offset == 0xFFFFFFFF) ||
       
  1159           (starting_disk == 0xFFFFFFFF) ||
       
  1160           (retval->compressed_size == 0xFFFFFFFF) ||
       
  1161           (retval->uncompressed_size == 0xFFFFFFFF)) )
       
  1162     {
       
  1163         int found = 0;
       
  1164         PHYSFS_uint16 sig, len;
       
  1165         while (extralen > 4)
       
  1166         {
       
  1167             if (!readui16(io, &sig))
       
  1168                 goto zip_load_entry_puked;
       
  1169             else if (!readui16(io, &len))
       
  1170                 goto zip_load_entry_puked;
       
  1171 
       
  1172             si64 += 4 + len;
       
  1173             extralen -= 4 + len;
       
  1174             if (sig != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG)
       
  1175             {
       
  1176                 if (!io->seek(io, si64))
       
  1177                     goto zip_load_entry_puked;
       
  1178                 continue;
       
  1179             } /* if */
       
  1180 
       
  1181             found = 1;
       
  1182             break;
       
  1183         } /* while */
       
  1184 
       
  1185         GOTO_IF_MACRO(!found, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1186 
       
  1187         if (retval->uncompressed_size == 0xFFFFFFFF)
       
  1188         {
       
  1189             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1190             if (!readui64(io, &retval->uncompressed_size))
       
  1191                 goto zip_load_entry_puked;
       
  1192             len -= 8;
       
  1193         } /* if */
       
  1194 
       
  1195         if (retval->compressed_size == 0xFFFFFFFF)
       
  1196         {
       
  1197             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1198             if (!readui64(io, &retval->compressed_size))
       
  1199                 goto zip_load_entry_puked;
       
  1200             len -= 8;
       
  1201         } /* if */
       
  1202 
       
  1203         if (offset == 0xFFFFFFFF)
       
  1204         {
       
  1205             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1206             if (!readui64(io, &offset))
       
  1207                 goto zip_load_entry_puked;
       
  1208             len -= 8;
       
  1209         } /* if */
       
  1210 
       
  1211         if (starting_disk == 0xFFFFFFFF)
       
  1212         {
       
  1213             GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1214             if (!readui32(io, &starting_disk))
       
  1215                 goto zip_load_entry_puked;
       
  1216             len -= 4;
       
  1217         } /* if */
       
  1218 
       
  1219         GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1220     } /* if */
       
  1221 
       
  1222     GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
       
  1223 
       
  1224     retval->offset = offset + ofs_fixup;
       
  1225 
       
  1226     /* seek to the start of the next entry in the central directory... */
       
  1227     if (!io->seek(io, si64 + extralen + commentlen))
       
  1228         goto zip_load_entry_puked;
       
  1229 
       
  1230     return retval;  /* success. */
       
  1231 
       
  1232 zip_load_entry_puked:
       
  1233     allocator.Free(retval);
       
  1234     return NULL;  /* failure. */
       
  1235 } /* zip_load_entry */
       
  1236 
       
  1237 
       
  1238 /* This leaves things allocated on error; the caller will clean up the mess. */
       
  1239 static int zip_load_entries(ZIPinfo *info,
       
  1240                             const PHYSFS_uint64 data_ofs,
       
  1241                             const PHYSFS_uint64 central_ofs,
       
  1242                             const PHYSFS_uint64 entry_count)
       
  1243 {
       
  1244     PHYSFS_Io *io = info->io;
       
  1245     const int zip64 = info->zip64;
       
  1246     PHYSFS_uint64 i;
       
  1247 
       
  1248     if (!io->seek(io, central_ofs))
       
  1249         return 0;
       
  1250 
       
  1251     for (i = 0; i < entry_count; i++)
       
  1252     {
       
  1253         ZIPentry *entry = zip_load_entry(io, zip64, data_ofs);
       
  1254         ZIPentry *find;
       
  1255 
       
  1256         if (!entry)
       
  1257             return 0;
       
  1258 
       
  1259         find = zip_find_entry(info, entry->name);
       
  1260         if (find != NULL)  /* duplicate? */
       
  1261         {
       
  1262             if (find->last_mod_time != 0)  /* duplicate? */
       
  1263             {
       
  1264                 allocator.Free(entry);
       
  1265                 BAIL_MACRO(PHYSFS_ERR_CORRUPT, 0);
       
  1266             } /* if */
       
  1267             else  /* we filled this in as a placeholder. Update it. */
       
  1268             {
       
  1269                 find->offset = entry->offset;
       
  1270                 find->version = entry->version;
       
  1271                 find->version_needed = entry->version_needed;
       
  1272                 find->compression_method = entry->compression_method;
       
  1273                 find->crc = entry->crc;
       
  1274                 find->compressed_size = entry->compressed_size;
       
  1275                 find->uncompressed_size = entry->uncompressed_size;
       
  1276                 find->last_mod_time = entry->last_mod_time;
       
  1277                 allocator.Free(entry);
       
  1278                 continue;
       
  1279             } /* else */
       
  1280         } /* if */
       
  1281 
       
  1282         if (!zip_hash_entry(info, entry))
       
  1283         {
       
  1284             allocator.Free(entry);
       
  1285             return 0;
       
  1286         } /* if */
       
  1287 
       
  1288         if (zip_entry_is_tradional_crypto(entry))
       
  1289             info->has_crypto = 1;
       
  1290     } /* for */
       
  1291 
       
  1292     return 1;
       
  1293 } /* zip_load_entries */
       
  1294 
       
  1295 
       
  1296 static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io,
       
  1297                                                    PHYSFS_sint64 _pos,
       
  1298                                                    PHYSFS_uint64 offset)
       
  1299 {
       
  1300     /*
       
  1301      * Naturally, the offset is useless to us; it is the offset from the
       
  1302      *  start of file, which is meaningless if we've appended this .zip to
       
  1303      *  a self-extracting .exe. We need to find this on our own. It should
       
  1304      *  be directly before the locator record, but the record in question,
       
  1305      *  like the original end-of-central-directory record, ends with a
       
  1306      *  variable-length field. Unlike the original, which has to store the
       
  1307      *  size of that variable-length field in a 16-bit int and thus has to be
       
  1308      *  within 64k, the new one gets 64-bits.
       
  1309      *
       
  1310      * Fortunately, the only currently-specified record for that variable
       
  1311      *  length block is some weird proprietary thing that deals with EBCDIC
       
  1312      *  and tape backups or something. So we don't seek far.
       
  1313      */
       
  1314 
       
  1315     PHYSFS_uint32 ui32;
       
  1316     const PHYSFS_uint64 pos = (PHYSFS_uint64) _pos;
       
  1317 
       
  1318     assert(_pos > 0);
       
  1319 
       
  1320     /* Try offset specified in the Zip64 end of central directory locator. */
       
  1321     /* This works if the entire PHYSFS_Io is the zip file. */
       
  1322     BAIL_IF_MACRO(!io->seek(io, offset), ERRPASS, -1);
       
  1323     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
       
  1324     if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
       
  1325         return offset;
       
  1326 
       
  1327     /* Try 56 bytes before the Zip64 end of central directory locator. */
       
  1328     /* This works if the record isn't variable length and is version 1. */
       
  1329     if (pos > 56)
       
  1330     {
       
  1331         BAIL_IF_MACRO(!io->seek(io, pos-56), ERRPASS, -1);
       
  1332         BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
       
  1333         if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
       
  1334             return pos-56;
       
  1335     } /* if */
       
  1336 
       
  1337     /* Try 84 bytes before the Zip64 end of central directory locator. */
       
  1338     /* This works if the record isn't variable length and is version 2. */
       
  1339     if (pos > 84)
       
  1340     {
       
  1341         BAIL_IF_MACRO(!io->seek(io, pos-84), ERRPASS, -1);
       
  1342         BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
       
  1343         if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
       
  1344             return pos-84;
       
  1345     } /* if */
       
  1346 
       
  1347     /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */
       
  1348     /*  Just try moving back at most 256k. Oh well. */
       
  1349     if ((offset < pos) && (pos > 4))
       
  1350     {
       
  1351         const PHYSFS_uint64 maxbuflen = 256 * 1024;
       
  1352         PHYSFS_uint64 len = pos - offset;
       
  1353         PHYSFS_uint8 *buf = NULL;
       
  1354         PHYSFS_sint32 i;
       
  1355 
       
  1356         if (len > maxbuflen)
       
  1357             len = maxbuflen;
       
  1358 
       
  1359         buf = (PHYSFS_uint8 *) __PHYSFS_smallAlloc(len);
       
  1360         BAIL_IF_MACRO(!buf, PHYSFS_ERR_OUT_OF_MEMORY, -1);
       
  1361 
       
  1362         if (!io->seek(io, pos - len) || !__PHYSFS_readAll(io, buf, len))
       
  1363         {
       
  1364             __PHYSFS_smallFree(buf);
       
  1365             return -1;  /* error was set elsewhere. */
       
  1366         } /* if */
       
  1367 
       
  1368         for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--)
       
  1369         {
       
  1370             if ( (buf[i] == 0x50) && (buf[i+1] == 0x4b) &&
       
  1371                  (buf[i+2] == 0x06) && (buf[i+3] == 0x06) )
       
  1372             {
       
  1373                 __PHYSFS_smallFree(buf);
       
  1374                 return pos - (len - i);
       
  1375             } /* if */
       
  1376         } /* for */
       
  1377 
       
  1378         __PHYSFS_smallFree(buf);
       
  1379     } /* if */
       
  1380 
       
  1381     BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1);  /* didn't find it. */
       
  1382 } /* zip64_find_end_of_central_dir */
       
  1383 
       
  1384 
       
  1385 static int zip64_parse_end_of_central_dir(ZIPinfo *info,
       
  1386                                           PHYSFS_uint64 *data_start,
       
  1387                                           PHYSFS_uint64 *dir_ofs,
       
  1388                                           PHYSFS_uint64 *entry_count,
       
  1389                                           PHYSFS_sint64 pos)
       
  1390 {
       
  1391     PHYSFS_Io *io = info->io;
       
  1392     PHYSFS_uint64 ui64;
       
  1393     PHYSFS_uint32 ui32;
       
  1394     PHYSFS_uint16 ui16;
       
  1395 
       
  1396     /* We should be positioned right past the locator signature. */
       
  1397 
       
  1398     if ((pos < 0) || (!io->seek(io, pos)))
       
  1399         return 0;
       
  1400 
       
  1401     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1402     if (ui32 != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG)
       
  1403         return -1;  /* it's not a Zip64 archive. Not an error, though! */
       
  1404 
       
  1405     info->zip64 = 1;
       
  1406 
       
  1407     /* number of the disk with the start of the central directory. */
       
  1408     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1409     BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1410 
       
  1411     /* offset of Zip64 end of central directory record. */
       
  1412     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1413 
       
  1414     /* total number of disks */
       
  1415     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1416     BAIL_IF_MACRO(ui32 != 1, PHYSFS_ERR_CORRUPT, 0);
       
  1417 
       
  1418     pos = zip64_find_end_of_central_dir(io, pos, ui64);
       
  1419     if (pos < 0)
       
  1420         return 0;  /* oh well. */
       
  1421 
       
  1422     /*
       
  1423      * For self-extracting archives, etc, there's crapola in the file
       
  1424      *  before the zipfile records; we calculate how much data there is
       
  1425      *  prepended by determining how far the zip64-end-of-central-directory
       
  1426      *  offset is from where it is supposed to be...the difference in bytes
       
  1427      *  is how much arbitrary data is at the start of the physical file.
       
  1428      */
       
  1429     assert(((PHYSFS_uint64) pos) >= ui64);
       
  1430     *data_start = ((PHYSFS_uint64) pos) - ui64;
       
  1431 
       
  1432     BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0);
       
  1433 
       
  1434     /* check signature again, just in case. */
       
  1435     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1436     BAIL_IF_MACRO(ui32 != ZIP64_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
       
  1437 
       
  1438     /* size of Zip64 end of central directory record. */
       
  1439     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1440 
       
  1441     /* version made by. */
       
  1442     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1443 
       
  1444     /* version needed to extract. */
       
  1445     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1446 
       
  1447     /* number of this disk. */
       
  1448     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1449     BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1450 
       
  1451     /* number of disk with start of central directory record. */
       
  1452     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1453     BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1454 
       
  1455     /* total number of entries in the central dir on this disk */
       
  1456     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1457 
       
  1458     /* total number of entries in the central dir */
       
  1459     BAIL_IF_MACRO(!readui64(io, entry_count), ERRPASS, 0);
       
  1460     BAIL_IF_MACRO(ui64 != *entry_count, PHYSFS_ERR_CORRUPT, 0);
       
  1461 
       
  1462     /* size of the central directory */
       
  1463     BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
       
  1464 
       
  1465     /* offset of central directory */
       
  1466     BAIL_IF_MACRO(!readui64(io, dir_ofs), ERRPASS, 0);
       
  1467 
       
  1468     /* Since we know the difference, fix up the central dir offset... */
       
  1469     *dir_ofs += *data_start;
       
  1470 
       
  1471     /*
       
  1472      * There are more fields here, for encryption and feature-specific things,
       
  1473      *  but we don't care about any of them at the moment.
       
  1474      */
       
  1475 
       
  1476     return 1;  /* made it. */
       
  1477 } /* zip64_parse_end_of_central_dir */
       
  1478 
       
  1479 
       
  1480 static int zip_parse_end_of_central_dir(ZIPinfo *info,
       
  1481                                         PHYSFS_uint64 *data_start,
       
  1482                                         PHYSFS_uint64 *dir_ofs,
       
  1483                                         PHYSFS_uint64 *entry_count)
       
  1484 {
       
  1485     PHYSFS_Io *io = info->io;
       
  1486     PHYSFS_uint16 entryCount16;
       
  1487     PHYSFS_uint32 offset32;
       
  1488     PHYSFS_uint32 ui32;
       
  1489     PHYSFS_uint16 ui16;
       
  1490     PHYSFS_sint64 len;
       
  1491     PHYSFS_sint64 pos;
       
  1492     int rc;
       
  1493 
       
  1494     /* find the end-of-central-dir record, and seek to it. */
       
  1495     pos = zip_find_end_of_central_dir(io, &len);
       
  1496     BAIL_IF_MACRO(pos == -1, ERRPASS, 0);
       
  1497     BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0);
       
  1498 
       
  1499     /* check signature again, just in case. */
       
  1500     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1501     BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
       
  1502 
       
  1503     /* Seek back to see if "Zip64 end of central directory locator" exists. */
       
  1504     /* this record is 20 bytes before end-of-central-dir */
       
  1505     rc = zip64_parse_end_of_central_dir(info, data_start, dir_ofs,
       
  1506                                         entry_count, pos - 20);
       
  1507 
       
  1508     /* Error or success? Bounce out of here. Keep going if not zip64. */
       
  1509     if ((rc == 0) || (rc == 1))
       
  1510         return rc;
       
  1511 
       
  1512     assert(rc == -1);  /* no error, just not a Zip64 archive. */
       
  1513 
       
  1514     /* Not Zip64? Seek back to where we were and keep processing. */
       
  1515     BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0);
       
  1516 
       
  1517     /* number of this disk */
       
  1518     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1519     BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1520 
       
  1521     /* number of the disk with the start of the central directory */
       
  1522     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1523     BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
       
  1524 
       
  1525     /* total number of entries in the central dir on this disk */
       
  1526     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1527 
       
  1528     /* total number of entries in the central dir */
       
  1529     BAIL_IF_MACRO(!readui16(io, &entryCount16), ERRPASS, 0);
       
  1530     BAIL_IF_MACRO(ui16 != entryCount16, PHYSFS_ERR_CORRUPT, 0);
       
  1531 
       
  1532     *entry_count = entryCount16;
       
  1533 
       
  1534     /* size of the central directory */
       
  1535     BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
       
  1536 
       
  1537     /* offset of central directory */
       
  1538     BAIL_IF_MACRO(!readui32(io, &offset32), ERRPASS, 0);
       
  1539     *dir_ofs = (PHYSFS_uint64) offset32;
       
  1540     BAIL_IF_MACRO(pos < (*dir_ofs + ui32), PHYSFS_ERR_CORRUPT, 0);
       
  1541 
       
  1542     /*
       
  1543      * For self-extracting archives, etc, there's crapola in the file
       
  1544      *  before the zipfile records; we calculate how much data there is
       
  1545      *  prepended by determining how far the central directory offset is
       
  1546      *  from where it is supposed to be (start of end-of-central-dir minus
       
  1547      *  sizeof central dir)...the difference in bytes is how much arbitrary
       
  1548      *  data is at the start of the physical file.
       
  1549      */
       
  1550     *data_start = (PHYSFS_uint64) (pos - (*dir_ofs + ui32));
       
  1551 
       
  1552     /* Now that we know the difference, fix up the central dir offset... */
       
  1553     *dir_ofs += *data_start;
       
  1554 
       
  1555     /* zipfile comment length */
       
  1556     BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
       
  1557 
       
  1558     /*
       
  1559      * Make sure that the comment length matches to the end of file...
       
  1560      *  If it doesn't, we're either in the wrong part of the file, or the
       
  1561      *  file is corrupted, but we give up either way.
       
  1562      */
       
  1563     BAIL_IF_MACRO((pos + 22 + ui16) != len, PHYSFS_ERR_CORRUPT, 0);
       
  1564 
       
  1565     return 1;  /* made it. */
       
  1566 } /* zip_parse_end_of_central_dir */
       
  1567 
       
  1568 
       
  1569 static int zip_alloc_hashtable(ZIPinfo *info, const PHYSFS_uint64 entry_count)
       
  1570 {
       
  1571     size_t alloclen;
       
  1572 
       
  1573     info->hashBuckets = (size_t) (entry_count / 5);
       
  1574     if (!info->hashBuckets)
       
  1575         info->hashBuckets = 1;
       
  1576 
       
  1577     alloclen = info->hashBuckets * sizeof (ZIPentry *);
       
  1578     info->hash = (ZIPentry **) allocator.Malloc(alloclen);
       
  1579     BAIL_IF_MACRO(!info->hash, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
  1580     memset(info->hash, '\0', alloclen);
       
  1581 
       
  1582     return 1;
       
  1583 } /* zip_alloc_hashtable */
       
  1584 
       
  1585 static void ZIP_closeArchive(void *opaque);
       
  1586 
       
  1587 static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
       
  1588 {
       
  1589     ZIPinfo *info = NULL;
       
  1590     PHYSFS_uint64 dstart;  /* data start */
       
  1591     PHYSFS_uint64 cdir_ofs;  /* central dir offset */
       
  1592     PHYSFS_uint64 entry_count;
       
  1593 
       
  1594     assert(io != NULL);  /* shouldn't ever happen. */
       
  1595 
       
  1596     BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
       
  1597     BAIL_IF_MACRO(!isZip(io), ERRPASS, NULL);
       
  1598 
       
  1599     info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
       
  1600     BAIL_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  1601     memset(info, '\0', sizeof (ZIPinfo));
       
  1602     info->root.resolved = ZIP_DIRECTORY;
       
  1603     info->io = io;
       
  1604 
       
  1605     if (!zip_parse_end_of_central_dir(info, &dstart, &cdir_ofs, &entry_count))
       
  1606         goto ZIP_openarchive_failed;
       
  1607     else if (!zip_alloc_hashtable(info, entry_count))
       
  1608         goto ZIP_openarchive_failed;
       
  1609     else if (!zip_load_entries(info, dstart, cdir_ofs, entry_count))
       
  1610         goto ZIP_openarchive_failed;
       
  1611 
       
  1612     assert(info->root.sibling == NULL);
       
  1613     return info;
       
  1614 
       
  1615 ZIP_openarchive_failed:
       
  1616     info->io = NULL;  /* don't let ZIP_closeArchive destroy (io). */
       
  1617     ZIP_closeArchive(info);
       
  1618     return NULL;
       
  1619 } /* ZIP_openArchive */
       
  1620 
       
  1621 
       
  1622 static void ZIP_enumerateFiles(void *opaque, const char *dname,
       
  1623                                PHYSFS_EnumFilesCallback cb,
       
  1624                                const char *origdir, void *callbackdata)
       
  1625 {
       
  1626     ZIPinfo *info = ((ZIPinfo *) opaque);
       
  1627     const ZIPentry *entry = zip_find_entry(info, dname);
       
  1628     if (entry && (entry->resolved == ZIP_DIRECTORY))
       
  1629     {
       
  1630         for (entry = entry->children; entry; entry = entry->sibling)
       
  1631         {
       
  1632             const char *ptr = strrchr(entry->name, '/');
       
  1633             cb(callbackdata, origdir, ptr ? ptr + 1 : entry->name);
       
  1634         } /* for */
       
  1635     } /* if */
       
  1636 } /* ZIP_enumerateFiles */
       
  1637 
       
  1638 
       
  1639 static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry)
       
  1640 {
       
  1641     int success;
       
  1642     PHYSFS_Io *retval = io->duplicate(io);
       
  1643     BAIL_IF_MACRO(!retval, ERRPASS, NULL);
       
  1644 
       
  1645     /* !!! FIXME: if you open a dir here, it should bail ERR_NOT_A_FILE */
       
  1646 
       
  1647     /* (inf) can be NULL if we already resolved. */
       
  1648     success = (inf == NULL) || zip_resolve(retval, inf, entry);
       
  1649     if (success)
       
  1650     {
       
  1651         PHYSFS_sint64 offset;
       
  1652         offset = ((entry->symlink) ? entry->symlink->offset : entry->offset);
       
  1653         success = retval->seek(retval, offset);
       
  1654     } /* if */
       
  1655 
       
  1656     if (!success)
       
  1657     {
       
  1658         retval->destroy(retval);
       
  1659         retval = NULL;
       
  1660     } /* if */
       
  1661 
       
  1662     return retval;
       
  1663 } /* zip_get_io */
       
  1664 
       
  1665 
       
  1666 static PHYSFS_Io *ZIP_openRead(void *opaque, const char *filename)
       
  1667 {
       
  1668     PHYSFS_Io *retval = NULL;
       
  1669     ZIPinfo *info = (ZIPinfo *) opaque;
       
  1670     ZIPentry *entry = zip_find_entry(info, filename);
       
  1671     ZIPfileinfo *finfo = NULL;
       
  1672     PHYSFS_Io *io = NULL;
       
  1673     PHYSFS_uint8 *password = NULL;
       
  1674     int i;
       
  1675 
       
  1676     /* if not found, see if maybe "$PASSWORD" is appended. */
       
  1677     if ((!entry) && (info->has_crypto))
       
  1678     {
       
  1679         const char *ptr = strrchr(filename, '$');
       
  1680         if (ptr != NULL)
       
  1681         {
       
  1682             const PHYSFS_uint64 len = (PHYSFS_uint64) (ptr - filename);
       
  1683             char *str = (char *) __PHYSFS_smallAlloc(len + 1);
       
  1684             BAIL_IF_MACRO(!str, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
  1685             memcpy(str, filename, len);
       
  1686             str[len] = '\0';
       
  1687             entry = zip_find_entry(info, str);
       
  1688             __PHYSFS_smallFree(str);
       
  1689             password = (PHYSFS_uint8 *) (ptr + 1);
       
  1690         } /* if */
       
  1691     } /* if */
       
  1692 
       
  1693     BAIL_IF_MACRO(!entry, ERRPASS, NULL);
       
  1694 
       
  1695     retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
       
  1696     GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
       
  1697 
       
  1698     finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
       
  1699     GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
       
  1700     memset(finfo, '\0', sizeof (ZIPfileinfo));
       
  1701 
       
  1702     io = zip_get_io(info->io, info, entry);
       
  1703     GOTO_IF_MACRO(!io, ERRPASS, ZIP_openRead_failed);
       
  1704     finfo->io = io;
       
  1705     finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry);
       
  1706     initializeZStream(&finfo->stream);
       
  1707 
       
  1708     if (finfo->entry->compression_method != COMPMETH_NONE)
       
  1709     {
       
  1710         finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
       
  1711         if (!finfo->buffer)
       
  1712             GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
       
  1713         else if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
       
  1714             goto ZIP_openRead_failed;
       
  1715     } /* if */
       
  1716 
       
  1717     if (!zip_entry_is_tradional_crypto(entry))
       
  1718         GOTO_IF_MACRO(password != NULL, PHYSFS_ERR_BAD_PASSWORD, ZIP_openRead_failed);
       
  1719     else
       
  1720     {
       
  1721         PHYSFS_uint8 crypto_header[12];
       
  1722         GOTO_IF_MACRO(password == NULL, PHYSFS_ERR_BAD_PASSWORD, ZIP_openRead_failed);
       
  1723         if (io->read(io, crypto_header, 12) != 12)
       
  1724             goto ZIP_openRead_failed;
       
  1725         else if (!zip_prep_crypto_keys(finfo, crypto_header, password))
       
  1726             goto ZIP_openRead_failed;
       
  1727     } /* if */
       
  1728 
       
  1729     memcpy(retval, &ZIP_Io, sizeof (PHYSFS_Io));
       
  1730     retval->opaque = finfo;
       
  1731 
       
  1732     return retval;
       
  1733 
       
  1734 ZIP_openRead_failed:
       
  1735     if (finfo != NULL)
       
  1736     {
       
  1737         if (finfo->io != NULL)
       
  1738             finfo->io->destroy(finfo->io);
       
  1739 
       
  1740         if (finfo->buffer != NULL)
       
  1741         {
       
  1742             allocator.Free(finfo->buffer);
       
  1743             inflateEnd(&finfo->stream);
       
  1744         } /* if */
       
  1745 
       
  1746         allocator.Free(finfo);
       
  1747     } /* if */
       
  1748 
       
  1749     if (retval != NULL)
       
  1750         allocator.Free(retval);
       
  1751 
       
  1752     return NULL;
       
  1753 } /* ZIP_openRead */
       
  1754 
       
  1755 
       
  1756 static PHYSFS_Io *ZIP_openWrite(void *opaque, const char *filename)
       
  1757 {
       
  1758     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
  1759 } /* ZIP_openWrite */
       
  1760 
       
  1761 
       
  1762 static PHYSFS_Io *ZIP_openAppend(void *opaque, const char *filename)
       
  1763 {
       
  1764     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
       
  1765 } /* ZIP_openAppend */
       
  1766 
       
  1767 
       
  1768 static void ZIP_closeArchive(void *opaque)
       
  1769 {
       
  1770     ZIPinfo *info = (ZIPinfo *) (opaque);
       
  1771 
       
  1772     if (!info)
       
  1773         return;
       
  1774 
       
  1775     if (info->io)
       
  1776         info->io->destroy(info->io);
       
  1777 
       
  1778     assert(info->root.sibling == NULL);
       
  1779     assert(info->hash || (info->root.children == NULL));
       
  1780 
       
  1781     if (info->hash)
       
  1782     {
       
  1783         size_t i;
       
  1784         for (i = 0; i < info->hashBuckets; i++)
       
  1785         {
       
  1786             ZIPentry *entry;
       
  1787             ZIPentry *next;
       
  1788             for (entry = info->hash[i]; entry; entry = next)
       
  1789             {
       
  1790                 next = entry->hashnext;
       
  1791                 allocator.Free(entry);
       
  1792             } /* for */
       
  1793         } /* for */
       
  1794         allocator.Free(info->hash);
       
  1795     } /* if */
       
  1796 
       
  1797     allocator.Free(info);
       
  1798 } /* ZIP_closeArchive */
       
  1799 
       
  1800 
       
  1801 static int ZIP_remove(void *opaque, const char *name)
       
  1802 {
       
  1803     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
  1804 } /* ZIP_remove */
       
  1805 
       
  1806 
       
  1807 static int ZIP_mkdir(void *opaque, const char *name)
       
  1808 {
       
  1809     BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
       
  1810 } /* ZIP_mkdir */
       
  1811 
       
  1812 
       
  1813 static int ZIP_stat(void *opaque, const char *filename, PHYSFS_Stat *stat)
       
  1814 {
       
  1815     ZIPinfo *info = (ZIPinfo *) opaque;
       
  1816     const ZIPentry *entry = zip_find_entry(info, filename);
       
  1817 
       
  1818     /* !!! FIXME: does this need to resolve entries here? */
       
  1819 
       
  1820     if (entry == NULL)
       
  1821         return 0;
       
  1822 
       
  1823     else if (entry->resolved == ZIP_DIRECTORY)
       
  1824     {
       
  1825         stat->filesize = 0;
       
  1826         stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
       
  1827     } /* if */
       
  1828 
       
  1829     else if (zip_entry_is_symlink(entry))
       
  1830     {
       
  1831         stat->filesize = 0;
       
  1832         stat->filetype = PHYSFS_FILETYPE_SYMLINK;
       
  1833     } /* else if */
       
  1834 
       
  1835     else
       
  1836     {
       
  1837         stat->filesize = (PHYSFS_sint64) entry->uncompressed_size;
       
  1838         stat->filetype = PHYSFS_FILETYPE_REGULAR;
       
  1839     } /* else */
       
  1840 
       
  1841     stat->modtime = ((entry) ? entry->last_mod_time : 0);
       
  1842     stat->createtime = stat->modtime;
       
  1843     stat->accesstime = 0;
       
  1844     stat->readonly = 1; /* .zip files are always read only */
       
  1845 
       
  1846     return 1;
       
  1847 } /* ZIP_stat */
       
  1848 
       
  1849 
       
  1850 const PHYSFS_Archiver __PHYSFS_Archiver_ZIP =
       
  1851 {
       
  1852     CURRENT_PHYSFS_ARCHIVER_API_VERSION,
       
  1853     {
       
  1854         "ZIP",
       
  1855         "PkZip/WinZip/Info-Zip compatible",
       
  1856         "Ryan C. Gordon <icculus@icculus.org>",
       
  1857         "https://icculus.org/physfs/",
       
  1858         1,  /* supportsSymlinks */
       
  1859     },
       
  1860     ZIP_openArchive,
       
  1861     ZIP_enumerateFiles,
       
  1862     ZIP_openRead,
       
  1863     ZIP_openWrite,
       
  1864     ZIP_openAppend,
       
  1865     ZIP_remove,
       
  1866     ZIP_mkdir,
       
  1867     ZIP_stat,
       
  1868     ZIP_closeArchive
       
  1869 };
       
  1870 
       
  1871 #endif  /* defined PHYSFS_SUPPORTS_ZIP */
       
  1872 
       
  1873 /* end of archiver_zip.c ... */
       
  1874