|
|
@@ -64,13 +64,13 @@ typedef struct _ZIPentry
|
|
|
char *name; /* Name of file in archive */
|
|
|
struct _ZIPentry *symlink; /* NULL or file we symlink to */
|
|
|
ZipResolveType resolved; /* Have we resolved file/symlink? */
|
|
|
- PHYSFS_uint32 offset; /* offset of data in archive */
|
|
|
+ PHYSFS_uint64 offset; /* offset of data in archive */
|
|
|
PHYSFS_uint16 version; /* version made by */
|
|
|
PHYSFS_uint16 version_needed; /* version needed to extract */
|
|
|
PHYSFS_uint16 compression_method; /* compression method */
|
|
|
PHYSFS_uint32 crc; /* crc-32 */
|
|
|
- PHYSFS_uint32 compressed_size; /* compressed size */
|
|
|
- PHYSFS_uint32 uncompressed_size; /* uncompressed size */
|
|
|
+ PHYSFS_uint64 compressed_size; /* compressed size */
|
|
|
+ PHYSFS_uint64 uncompressed_size; /* uncompressed size */
|
|
|
PHYSFS_sint64 last_mod_time; /* last file mod time */
|
|
|
} ZIPentry;
|
|
|
|
|
|
@@ -80,8 +80,9 @@ typedef struct _ZIPentry
|
|
|
typedef struct
|
|
|
{
|
|
|
PHYSFS_Io *io;
|
|
|
- PHYSFS_uint16 entryCount; /* Number of files in ZIP. */
|
|
|
- ZIPentry *entries; /* info on all files in ZIP. */
|
|
|
+ int zip64; /* non-zero if this is a Zip64 archive. */
|
|
|
+ PHYSFS_uint64 entryCount; /* Number of files in ZIP. */
|
|
|
+ ZIPentry *entries; /* info on all files in ZIP. */
|
|
|
} ZIPinfo;
|
|
|
|
|
|
/*
|
|
|
@@ -99,9 +100,12 @@ typedef struct
|
|
|
|
|
|
|
|
|
/* Magic numbers... */
|
|
|
-#define ZIP_LOCAL_FILE_SIG 0x04034b50
|
|
|
-#define ZIP_CENTRAL_DIR_SIG 0x02014b50
|
|
|
-#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50
|
|
|
+#define ZIP_LOCAL_FILE_SIG 0x04034b50
|
|
|
+#define ZIP_CENTRAL_DIR_SIG 0x02014b50
|
|
|
+#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50
|
|
|
+#define ZIP64_END_OF_CENTRAL_DIR_SIG 0x06064b50
|
|
|
+#define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50
|
|
|
+#define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG 0x0001
|
|
|
|
|
|
/* compression methods... */
|
|
|
#define COMPMETH_NONE 0
|
|
|
@@ -164,6 +168,17 @@ static int zlib_err(const int rc)
|
|
|
} /* zlib_err */
|
|
|
|
|
|
|
|
|
+/*
|
|
|
+ * Read an unsigned 64-bit int and swap to native byte order.
|
|
|
+ */
|
|
|
+static int readui64(PHYSFS_Io *io, PHYSFS_uint64 *val)
|
|
|
+{
|
|
|
+ PHYSFS_uint64 v;
|
|
|
+ BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
|
|
|
+ *val = PHYSFS_swapULE64(v);
|
|
|
+ return 1;
|
|
|
+} /* readui64 */
|
|
|
+
|
|
|
/*
|
|
|
* Read an unsigned 32-bit int and swap to native byte order.
|
|
|
*/
|
|
|
@@ -322,7 +337,7 @@ static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
|
|
|
static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
|
|
|
{
|
|
|
const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
|
|
|
- return finfo->entry->uncompressed_size;
|
|
|
+ return (PHYSFS_sint64) finfo->entry->uncompressed_size;
|
|
|
} /* ZIP_length */
|
|
|
|
|
|
|
|
|
@@ -521,9 +536,9 @@ static int isZip(PHYSFS_Io *io)
|
|
|
} /* isZip */
|
|
|
|
|
|
|
|
|
-static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max)
|
|
|
+static void zip_free_entries(ZIPentry *entries, PHYSFS_uint64 max)
|
|
|
{
|
|
|
- PHYSFS_uint32 i;
|
|
|
+ PHYSFS_uint64 i;
|
|
|
for (i = 0; i < max; i++)
|
|
|
{
|
|
|
ZIPentry *entry = &entries[i];
|
|
|
@@ -545,9 +560,9 @@ static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path,
|
|
|
{
|
|
|
ZIPentry *a = info->entries;
|
|
|
PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path);
|
|
|
- PHYSFS_sint32 lo = 0;
|
|
|
- PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
|
|
- PHYSFS_sint32 middle;
|
|
|
+ PHYSFS_sint64 lo = 0;
|
|
|
+ PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1);
|
|
|
+ PHYSFS_sint64 middle;
|
|
|
const char *thispath = NULL;
|
|
|
int rc;
|
|
|
|
|
|
@@ -695,7 +710,7 @@ static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
|
|
|
|
|
|
static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
|
|
|
{
|
|
|
- const PHYSFS_uint32 size = entry->uncompressed_size;
|
|
|
+ const PHYSFS_uint64 size = entry->uncompressed_size;
|
|
|
char *path = NULL;
|
|
|
int rc = 0;
|
|
|
|
|
|
@@ -716,7 +731,7 @@ static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
|
|
|
else /* symlink target path is compressed... */
|
|
|
{
|
|
|
z_stream stream;
|
|
|
- const PHYSFS_uint32 complen = entry->compressed_size;
|
|
|
+ const PHYSFS_uint64 complen = entry->compressed_size;
|
|
|
PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
|
|
|
if (compressed != NULL)
|
|
|
{
|
|
|
@@ -768,6 +783,8 @@ static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
|
|
|
* archive created with Sun's Java tools, apparently. We only
|
|
|
* consider this archive corrupted if those entries don't match and
|
|
|
* aren't zero. That seems to work well.
|
|
|
+ * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's
|
|
|
+ * possible that's a Zip64 thing.
|
|
|
*/
|
|
|
|
|
|
BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
|
|
|
@@ -781,10 +798,15 @@ static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
|
|
|
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); /* date/time */
|
|
|
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(ui32 && (ui32!=entry->compressed_size),PHYSFS_ERR_CORRUPT,0);
|
|
|
+ BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
|
|
|
+ (ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(ui32&&(ui32!=entry->uncompressed_size),PHYSFS_ERR_CORRUPT,0);
|
|
|
+ BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
|
|
|
+ (ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
|
|
|
BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
|
|
|
|
|
|
@@ -913,10 +935,13 @@ static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
|
|
|
} /* zip_dos_time_to_physfs_time */
|
|
|
|
|
|
|
|
|
-static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixup)
|
|
|
+static int zip_load_entry(PHYSFS_Io *io, const int zip64, ZIPentry *entry,
|
|
|
+ PHYSFS_uint64 ofs_fixup)
|
|
|
{
|
|
|
PHYSFS_uint16 fnamelen, extralen, commentlen;
|
|
|
PHYSFS_uint32 external_attr;
|
|
|
+ PHYSFS_uint32 starting_disk;
|
|
|
+ PHYSFS_uint64 offset;
|
|
|
PHYSFS_uint16 ui16;
|
|
|
PHYSFS_uint32 ui32;
|
|
|
PHYSFS_sint64 si64;
|
|
|
@@ -933,16 +958,19 @@ static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixu
|
|
|
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
entry->last_mod_time = zip_dos_time_to_physfs_time(ui32);
|
|
|
BAIL_IF_MACRO(!readui32(io, &entry->crc), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(!readui32(io, &entry->compressed_size), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(!readui32(io, &entry->uncompressed_size), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ entry->compressed_size = (PHYSFS_uint64) ui32;
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ entry->uncompressed_size = (PHYSFS_uint64) ui32;
|
|
|
BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
|
|
|
BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
|
|
|
BAIL_IF_MACRO(!readui16(io, &commentlen), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* disk number start */
|
|
|
+ BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
|
|
|
+ starting_disk = (PHYSFS_uint32) ui16;
|
|
|
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* internal file attribs */
|
|
|
BAIL_IF_MACRO(!readui32(io, &external_attr), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(!readui32(io, &entry->offset), ERRPASS, 0);
|
|
|
- entry->offset += ofs_fixup;
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ offset = (PHYSFS_uint64) ui32;
|
|
|
|
|
|
entry->symlink = NULL; /* will be resolved later, if necessary. */
|
|
|
entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ?
|
|
|
@@ -960,7 +988,80 @@ static int zip_load_entry(PHYSFS_Io *io, ZIPentry *entry, PHYSFS_uint32 ofs_fixu
|
|
|
if (si64 == -1)
|
|
|
goto zip_load_entry_puked;
|
|
|
|
|
|
- /* seek to the start of the next entry in the central directory... */
|
|
|
+ /*
|
|
|
+ * The actual sizes didn't fit in 32-bits; look for the Zip64
|
|
|
+ * extended information extra field...
|
|
|
+ */
|
|
|
+ if ( (zip64) &&
|
|
|
+ ((offset == 0xFFFFFFFF) ||
|
|
|
+ (starting_disk == 0xFFFFFFFF) ||
|
|
|
+ (entry->compressed_size == 0xFFFFFFFF) ||
|
|
|
+ (entry->uncompressed_size == 0xFFFFFFFF)) )
|
|
|
+ {
|
|
|
+ int found = 0;
|
|
|
+ PHYSFS_uint16 sig, len;
|
|
|
+ while (extralen > 4)
|
|
|
+ {
|
|
|
+ if (!readui16(io, &sig))
|
|
|
+ goto zip_load_entry_puked;
|
|
|
+ else if (!readui16(io, &len))
|
|
|
+ goto zip_load_entry_puked;
|
|
|
+
|
|
|
+ si64 += 4 + len;
|
|
|
+ extralen -= 4 + len;
|
|
|
+ if (sig != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG)
|
|
|
+ {
|
|
|
+ if (!io->seek(io, si64))
|
|
|
+ goto zip_load_entry_puked;
|
|
|
+ continue;
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ found = 1;
|
|
|
+ break;
|
|
|
+ } /* while */
|
|
|
+
|
|
|
+ GOTO_IF_MACRO(!found, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
|
|
|
+
|
|
|
+ if (entry->uncompressed_size == 0xFFFFFFFF)
|
|
|
+ {
|
|
|
+ GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
|
|
|
+ if (!readui64(io, &entry->uncompressed_size))
|
|
|
+ goto zip_load_entry_puked;
|
|
|
+ len -= 8;
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ if (entry->compressed_size == 0xFFFFFFFF)
|
|
|
+ {
|
|
|
+ GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
|
|
|
+ if (!readui64(io, &entry->compressed_size))
|
|
|
+ goto zip_load_entry_puked;
|
|
|
+ len -= 8;
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ if (offset == 0xFFFFFFFF)
|
|
|
+ {
|
|
|
+ GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
|
|
|
+ if (!readui64(io, &offset))
|
|
|
+ goto zip_load_entry_puked;
|
|
|
+ len -= 8;
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ if (starting_disk == 0xFFFFFFFF)
|
|
|
+ {
|
|
|
+ GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
|
|
|
+ if (!readui32(io, &starting_disk))
|
|
|
+ goto zip_load_entry_puked;
|
|
|
+ len -= 4;
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
|
|
|
+
|
|
|
+ entry->offset = offset + ofs_fixup;
|
|
|
+
|
|
|
+ /* seek to the start of the next entry in the central directory... */
|
|
|
if (!io->seek(io, si64 + extralen + commentlen))
|
|
|
goto zip_load_entry_puked;
|
|
|
|
|
|
@@ -972,7 +1073,7 @@ zip_load_entry_puked:
|
|
|
} /* zip_load_entry */
|
|
|
|
|
|
|
|
|
-static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
|
|
+static int zip_entry_cmp(void *_a, size_t one, size_t two)
|
|
|
{
|
|
|
if (one != two)
|
|
|
{
|
|
|
@@ -984,7 +1085,7 @@ static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
|
|
} /* zip_entry_cmp */
|
|
|
|
|
|
|
|
|
-static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
|
|
+static void zip_entry_swap(void *_a, size_t one, size_t two)
|
|
|
{
|
|
|
if (one != two)
|
|
|
{
|
|
|
@@ -999,10 +1100,12 @@ static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
|
|
|
|
|
|
|
|
|
static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
- PHYSFS_uint32 data_ofs, PHYSFS_uint32 central_ofs)
|
|
|
+ const PHYSFS_uint64 data_ofs,
|
|
|
+ const PHYSFS_uint64 central_ofs)
|
|
|
{
|
|
|
- PHYSFS_uint32 max = info->entryCount;
|
|
|
- PHYSFS_uint32 i;
|
|
|
+ const PHYSFS_uint64 max = info->entryCount;
|
|
|
+ const int zip64 = info->zip64;
|
|
|
+ PHYSFS_uint64 i;
|
|
|
|
|
|
BAIL_IF_MACRO(!io->seek(io, central_ofs), ERRPASS, 0);
|
|
|
|
|
|
@@ -1011,26 +1114,203 @@ static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
|
|
|
for (i = 0; i < max; i++)
|
|
|
{
|
|
|
- if (!zip_load_entry(io, &info->entries[i], data_ofs))
|
|
|
+ if (!zip_load_entry(io, zip64, &info->entries[i], data_ofs))
|
|
|
{
|
|
|
zip_free_entries(info->entries, i);
|
|
|
return 0;
|
|
|
} /* if */
|
|
|
} /* for */
|
|
|
|
|
|
- __PHYSFS_sort(info->entries, max, zip_entry_cmp, zip_entry_swap);
|
|
|
+ __PHYSFS_sort(info->entries, (size_t) max, zip_entry_cmp, zip_entry_swap);
|
|
|
return 1;
|
|
|
} /* zip_load_entries */
|
|
|
|
|
|
|
|
|
+static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io,
|
|
|
+ PHYSFS_sint64 _pos,
|
|
|
+ PHYSFS_uint64 offset)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * Naturally, the offset is useless to us; it is the offset from the
|
|
|
+ * start of file, which is meaningless if we've appended this .zip to
|
|
|
+ * a self-extracting .exe. We need to find this on our own. It should
|
|
|
+ * be directly before the locator record, but the record in question,
|
|
|
+ * like the original end-of-central-directory record, ends with a
|
|
|
+ * variable-length field. Unlike the original, which has to store the
|
|
|
+ * size of that variable-length field in a 16-bit int and thus has to be
|
|
|
+ * within 64k, the new one gets 64-bits.
|
|
|
+ *
|
|
|
+ * Fortunately, the only currently-specified record for that variable
|
|
|
+ * length block is some weird proprietary thing that deals with EBCDIC
|
|
|
+ * and tape backups or something. So we don't seek far.
|
|
|
+ */
|
|
|
+
|
|
|
+ PHYSFS_uint32 ui32;
|
|
|
+ const PHYSFS_uint64 pos = (PHYSFS_uint64) _pos;
|
|
|
+
|
|
|
+ assert(_pos > 0);
|
|
|
+
|
|
|
+ /* Try offset specified in the Zip64 end of central directory locator. */
|
|
|
+ /* This works if the entire PHYSFS_Io is the zip file. */
|
|
|
+ BAIL_IF_MACRO(!io->seek(io, offset), ERRPASS, -1);
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
|
|
|
+ if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
|
|
|
+ return offset;
|
|
|
+
|
|
|
+ /* Try 56 bytes before the Zip64 end of central directory locator. */
|
|
|
+ /* This works if the record isn't variable length and is version 1. */
|
|
|
+ if (pos > 56)
|
|
|
+ {
|
|
|
+ BAIL_IF_MACRO(!io->seek(io, pos-56), ERRPASS, -1);
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
|
|
|
+ if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
|
|
|
+ return pos-56;
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ /* Try 84 bytes before the Zip64 end of central directory locator. */
|
|
|
+ /* This works if the record isn't variable length and is version 2. */
|
|
|
+ if (pos > 84)
|
|
|
+ {
|
|
|
+ BAIL_IF_MACRO(!io->seek(io, pos-84), ERRPASS, -1);
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
|
|
|
+ if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
|
|
|
+ return pos-84;
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */
|
|
|
+ /* Just try moving back at most 256k. Oh well. */
|
|
|
+ if ((offset < pos) && (pos > 4))
|
|
|
+ {
|
|
|
+ /* we assume you can eat this stack if you handle Zip64 files. */
|
|
|
+ PHYSFS_uint8 buf[256 * 1024];
|
|
|
+ PHYSFS_uint64 len = pos - offset;
|
|
|
+ PHYSFS_uint32 i;
|
|
|
+
|
|
|
+ if (len > sizeof (buf))
|
|
|
+ len = sizeof (buf);
|
|
|
+
|
|
|
+ BAIL_IF_MACRO(!io->seek(io, pos - len), ERRPASS, -1);
|
|
|
+ BAIL_IF_MACRO(!__PHYSFS_readAll(io, buf, len), ERRPASS, -1);
|
|
|
+ for (i = len - 4; i >= 0; i--)
|
|
|
+ {
|
|
|
+ if (buf[i] != 0x50)
|
|
|
+ continue;
|
|
|
+ if ( (buf[i+1] == 0x4b) &&
|
|
|
+ (buf[i+2] == 0x06) &&
|
|
|
+ (buf[i+3] == 0x06) )
|
|
|
+ return pos - (len - i);
|
|
|
+ } /* for */
|
|
|
+ } /* if */
|
|
|
+
|
|
|
+ BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */
|
|
|
+} /* zip64_find_end_of_central_dir */
|
|
|
+
|
|
|
+
|
|
|
+static int zip64_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
+ PHYSFS_uint64 *data_start,
|
|
|
+ PHYSFS_uint64 *dir_ofs,
|
|
|
+ PHYSFS_sint64 pos)
|
|
|
+{
|
|
|
+ PHYSFS_uint64 ui64;
|
|
|
+ PHYSFS_uint32 ui32;
|
|
|
+ PHYSFS_uint16 ui16;
|
|
|
+ //PHYSFS_sint64 len;
|
|
|
+
|
|
|
+ /* We should be positioned right past the locator signature. */
|
|
|
+
|
|
|
+ if ((pos < 0) || (!io->seek(io, pos)))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ if (ui32 != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG)
|
|
|
+ return -1; /* it's not a Zip64 archive. Not an error, though! */
|
|
|
+
|
|
|
+ info->zip64 = 1;
|
|
|
+
|
|
|
+ /* number of the disk with the start of the central directory. */
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
+ /* offset of Zip64 end of central directory record. */
|
|
|
+ BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* total number of disks */
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(ui32 != 1, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
+ pos = zip64_find_end_of_central_dir(io, pos, ui64);
|
|
|
+ if (pos < 0)
|
|
|
+ return 0; /* oh well. */
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For self-extracting archives, etc, there's crapola in the file
|
|
|
+ * before the zipfile records; we calculate how much data there is
|
|
|
+ * prepended by determining how far the zip64-end-of-central-directory
|
|
|
+ * offset is from where it is supposed to be...the difference in bytes
|
|
|
+ * is how much arbitrary data is at the start of the physical file.
|
|
|
+ */
|
|
|
+ assert(((PHYSFS_uint64) pos) >= ui64);
|
|
|
+ *data_start = ((PHYSFS_uint64) pos) - ui64;
|
|
|
+
|
|
|
+ BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* check signature again, just in case. */
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(ui32 != ZIP64_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
+ /* size of Zip64 end of central directory record. */
|
|
|
+ BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* version made by. */
|
|
|
+ BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* version needed to extract. */
|
|
|
+ BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* number of this disk. */
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
+ /* number of disk with start of central directory record. */
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
+ /* total number of entries in the central dir on this disk */
|
|
|
+ BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* total number of entries in the central dir */
|
|
|
+ BAIL_IF_MACRO(!readui64(io, &info->entryCount), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(ui64 != info->entryCount, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+
|
|
|
+ /* size of the central directory */
|
|
|
+ BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* offset of central directory */
|
|
|
+ BAIL_IF_MACRO(!readui64(io, dir_ofs), ERRPASS, 0);
|
|
|
+
|
|
|
+ /* Since we know the difference, fix up the central dir offset... */
|
|
|
+ *dir_ofs += *data_start;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * There are more fields here, for encryption and feature-specific things,
|
|
|
+ * but we don't care about any of them at the moment.
|
|
|
+ */
|
|
|
+
|
|
|
+ return 1; /* made it. */
|
|
|
+} /* zip64_parse_end_of_central_dir */
|
|
|
+
|
|
|
+
|
|
|
static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
- PHYSFS_uint32 *data_start,
|
|
|
- PHYSFS_uint32 *central_dir_ofs)
|
|
|
+ PHYSFS_uint64 *data_start,
|
|
|
+ PHYSFS_uint64 *dir_ofs)
|
|
|
{
|
|
|
+ PHYSFS_uint16 entryCount16;
|
|
|
+ PHYSFS_uint32 offset32;
|
|
|
PHYSFS_uint32 ui32;
|
|
|
PHYSFS_uint16 ui16;
|
|
|
PHYSFS_sint64 len;
|
|
|
PHYSFS_sint64 pos;
|
|
|
+ int rc;
|
|
|
|
|
|
/* find the end-of-central-dir record, and seek to it. */
|
|
|
pos = zip_find_end_of_central_dir(io, &len);
|
|
|
@@ -1041,6 +1321,18 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
|
|
|
|
|
|
+ /* Seek back to see if "Zip64 end of central directory locator" exists. */
|
|
|
+ /* this record is 20 bytes before end-of-central-dir */
|
|
|
+ rc = zip64_parse_end_of_central_dir(io, info, data_start, dir_ofs, pos-20);
|
|
|
+ BAIL_IF_MACRO(rc == 0, ERRPASS, 0);
|
|
|
+ if (rc == 1)
|
|
|
+ return 1; /* we're done here. */
|
|
|
+
|
|
|
+ assert(rc == -1); /* no error, just not a Zip64 archive. */
|
|
|
+
|
|
|
+ /* Not Zip64? Seek back to where we were and keep processing. */
|
|
|
+ BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0);
|
|
|
+
|
|
|
/* number of this disk */
|
|
|
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
|
|
|
BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
|
|
|
@@ -1053,15 +1345,16 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
|
|
|
|
|
|
/* total number of entries in the central dir */
|
|
|
- BAIL_IF_MACRO(!readui16(io, &info->entryCount), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(ui16 != info->entryCount, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+ BAIL_IF_MACRO(!readui16(io, &entryCount16), ERRPASS, 0);
|
|
|
+ BAIL_IF_MACRO(ui16 != entryCount16, PHYSFS_ERR_CORRUPT, 0);
|
|
|
|
|
|
/* size of the central directory */
|
|
|
BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
|
|
|
|
|
|
/* offset of central directory */
|
|
|
- BAIL_IF_MACRO(!readui32(io, central_dir_ofs), ERRPASS, 0);
|
|
|
- BAIL_IF_MACRO(pos < *central_dir_ofs + ui32, PHYSFS_ERR_CORRUPT, 0);
|
|
|
+ BAIL_IF_MACRO(!readui32(io, &offset32), ERRPASS, 0);
|
|
|
+ *dir_ofs = (PHYSFS_uint64) offset32;
|
|
|
+ BAIL_IF_MACRO(pos < (*dir_ofs + ui32), PHYSFS_ERR_CORRUPT, 0);
|
|
|
|
|
|
/*
|
|
|
* For self-extracting archives, etc, there's crapola in the file
|
|
|
@@ -1071,10 +1364,10 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
* sizeof central dir)...the difference in bytes is how much arbitrary
|
|
|
* data is at the start of the physical file.
|
|
|
*/
|
|
|
- *data_start = (PHYSFS_uint32) (pos - (*central_dir_ofs + ui32));
|
|
|
+ *data_start = (PHYSFS_uint64) (pos - (*dir_ofs + ui32));
|
|
|
|
|
|
/* Now that we know the difference, fix up the central dir offset... */
|
|
|
- *central_dir_ofs += *data_start;
|
|
|
+ *dir_ofs += *data_start;
|
|
|
|
|
|
/* zipfile comment length */
|
|
|
BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
|
|
|
@@ -1093,8 +1386,8 @@ static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
|
|
|
static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
|
|
|
{
|
|
|
ZIPinfo *info = NULL;
|
|
|
- PHYSFS_uint32 data_start;
|
|
|
- PHYSFS_uint32 cent_dir_ofs;
|
|
|
+ PHYSFS_uint64 data_start;
|
|
|
+ PHYSFS_uint64 cent_dir_ofs;
|
|
|
|
|
|
assert(io != NULL); /* shouldn't ever happen. */
|
|
|
|
|
|
@@ -1122,14 +1415,14 @@ ZIP_openarchive_failed:
|
|
|
} /* ZIP_openArchive */
|
|
|
|
|
|
|
|
|
-static PHYSFS_sint32 zip_find_start_of_dir(ZIPinfo *info, const char *path,
|
|
|
+static PHYSFS_sint64 zip_find_start_of_dir(ZIPinfo *info, const char *path,
|
|
|
int stop_on_first_find)
|
|
|
{
|
|
|
- PHYSFS_sint32 lo = 0;
|
|
|
- PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
|
|
|
+ PHYSFS_sint64 lo = 0;
|
|
|
+ PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1);
|
|
|
PHYSFS_sint32 middle;
|
|
|
PHYSFS_uint32 dlen = (PHYSFS_uint32) strlen(path);
|
|
|
- PHYSFS_sint32 retval = -1;
|
|
|
+ PHYSFS_sint64 retval = -1;
|
|
|
const char *name;
|
|
|
int rc;
|
|
|
|
|
|
@@ -1198,7 +1491,8 @@ static void ZIP_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
|
|
|
const char *origdir, void *callbackdata)
|
|
|
{
|
|
|
ZIPinfo *info = ((ZIPinfo *) opaque);
|
|
|
- PHYSFS_sint32 dlen, dlen_inc, max, i;
|
|
|
+ PHYSFS_sint32 dlen, dlen_inc;
|
|
|
+ PHYSFS_sint64 i, max;
|
|
|
|
|
|
i = zip_find_start_of_dir(info, dname, 0);
|
|
|
if (i == -1) /* no such directory. */
|
|
|
@@ -1209,7 +1503,7 @@ static void ZIP_enumerateFiles(PHYSFS_Dir *opaque, const char *dname,
|
|
|
dlen--;
|
|
|
|
|
|
dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
|
|
|
- max = (PHYSFS_sint32) info->entryCount;
|
|
|
+ max = (PHYSFS_sint64) info->entryCount;
|
|
|
while (i < max)
|
|
|
{
|
|
|
char *e = info->entries[i].name;
|
|
|
@@ -1384,7 +1678,7 @@ static int ZIP_stat(PHYSFS_Dir *opaque, const char *filename, int *exists,
|
|
|
|
|
|
else
|
|
|
{
|
|
|
- stat->filesize = entry->uncompressed_size;
|
|
|
+ stat->filesize = (PHYSFS_sint64) entry->uncompressed_size;
|
|
|
stat->filetype = PHYSFS_FILETYPE_REGULAR;
|
|
|
} /* else */
|
|
|
|