Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

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