Added Zip64 support to the .zip archiver.

Now we can handle .zip files > 4 gigabytes, etc.
This commit is contained in:
Ryan C. Gordon 2012-06-01 05:44:50 -04:00
parent 3e750a903f
commit a865b14bec
5 changed files with 366 additions and 72 deletions

View File

@ -177,7 +177,7 @@ static int lzma_file_cmp_stdlib(const void *key, const void *object)
* Compare two files with each other based on the name
* Used for sorting
*/
static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
static int lzma_file_cmp(void *_a, size_t one, size_t two)
{
LZMAfile *files = (LZMAfile *) _a;
return strcmp(files[one].item->Name, files[two].item->Name);
@ -187,7 +187,7 @@ static int lzma_file_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
/*
* Swap two entries in the file array
*/
static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
static void lzma_file_swap(void *_a, size_t one, size_t two)
{
LZMAfile tmp;
LZMAfile *first = &(((LZMAfile *) _a)[one]);
@ -245,7 +245,7 @@ static int lzma_files_init(LZMAarchive *archive)
}
} /* for */
__PHYSFS_sort(archive->files, numFiles, lzma_file_cmp, lzma_file_swap);
__PHYSFS_sort(archive->files, (size_t) numFiles, lzma_file_cmp, lzma_file_swap);
return 1;
} /* lzma_load_files */

View File

@ -145,7 +145,7 @@ static const PHYSFS_Io UNPK_Io =
};
static int entryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
static int entryCmp(void *_a, size_t one, size_t two)
{
if (one != two)
{
@ -157,7 +157,7 @@ static int entryCmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
} /* entryCmp */
static void entrySwap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
static void entrySwap(void *_a, size_t one, size_t two)
{
if (one != two)
{
@ -458,7 +458,7 @@ PHYSFS_Dir *UNPK_openArchive(PHYSFS_Io *io, UNPKentry *e,
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
} /* if */
__PHYSFS_sort(e, num, entryCmp, entrySwap);
__PHYSFS_sort(e, (size_t) num, entryCmp, entrySwap);
info->io = io;
info->entryCount = num;
info->entries = e;

View File

@ -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 int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
PHYSFS_uint32 *data_start,
PHYSFS_uint32 *central_dir_ofs)
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_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 */

View File

@ -589,11 +589,11 @@ static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *))
} /* doEnumStringList */
static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
static void __PHYSFS_bubble_sort(void *a, size_t lo, size_t hi,
int (*cmpfn)(void *, size_t, size_t),
void (*swapfn)(void *, size_t, size_t))
{
PHYSFS_uint32 i;
size_t i;
int sorted;
do
@ -611,13 +611,13 @@ static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
} /* __PHYSFS_bubble_sort */
static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
static void __PHYSFS_quick_sort(void *a, size_t lo, size_t hi,
int (*cmpfn)(void *, size_t, size_t),
void (*swapfn)(void *, size_t, size_t))
{
PHYSFS_uint32 i;
PHYSFS_uint32 j;
PHYSFS_uint32 v;
size_t i;
size_t j;
size_t v;
if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD)
__PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn);
@ -649,9 +649,9 @@ static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
} /* __PHYSFS_quick_sort */
void __PHYSFS_sort(void *entries, PHYSFS_uint32 max,
int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
void __PHYSFS_sort(void *entries, size_t max,
int (*cmpfn)(void *, size_t, size_t),
void (*swapfn)(void *, size_t, size_t))
{
/*
* Quicksort w/ Bubblesort fallback algorithm inspired by code from here:

View File

@ -299,9 +299,9 @@ void __PHYSFS_setError(const PHYSFS_ErrorCode err);
*
* See zip.c for an example.
*/
void __PHYSFS_sort(void *entries, PHYSFS_uint32 max,
int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32));
void __PHYSFS_sort(void *entries, size_t max,
int (*cmpfn)(void *, size_t, size_t),
void (*swapfn)(void *, size_t, size_t));
/*
* This isn't a formal error code, it's just for BAIL_MACRO.