From 480a2594526b632e05bdccbb8eabf2f5063b7495 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 13 Jul 2002 10:17:13 +0000 Subject: [PATCH] ZIP archiver rewrite. --- Makefile.am.newautomake | 2 - archivers/Makefile.am | 4 +- archivers/unzip.c | 1324 --------------------------------------- archivers/unzip.h | 275 -------- archivers/zip.c | 914 ++++++++++++++++++++------- physfs_internal.h | 2 + 6 files changed, 701 insertions(+), 1820 deletions(-) delete mode 100644 archivers/unzip.c delete mode 100644 archivers/unzip.h diff --git a/Makefile.am.newautomake b/Makefile.am.newautomake index 970be7eb..8fa50637 100644 --- a/Makefile.am.newautomake +++ b/Makefile.am.newautomake @@ -49,8 +49,6 @@ libphysfs_la_SOURCES = \ archivers/dir.c \ archivers/grp.c \ archivers/zip.c \ - archivers/unzip.c \ - archivers/unzip.h \ platform/unix.c \ platform/posix.c \ $(ZLIB_SRC) diff --git a/archivers/Makefile.am b/archivers/Makefile.am index 277a0c78..e4c5b273 100644 --- a/archivers/Makefile.am +++ b/archivers/Makefile.am @@ -9,7 +9,5 @@ endif libarchivers_la_SOURCES = \ dir.c \ grp.c \ - zip.c \ - unzip.c \ - unzip.h + zip.c diff --git a/archivers/unzip.c b/archivers/unzip.c deleted file mode 100644 index 82bbfc96..00000000 --- a/archivers/unzip.c +++ /dev/null @@ -1,1324 +0,0 @@ -/* unzip.c -- IO on .zip files using zlib - Version 0.15 beta, Mar 19th, 1998, - - Read unzip.h for more info -*/ - -#if HAVE_CONFIG_H -# include -#endif - -#if (defined PHYSFS_SUPPORTS_ZIP) - -#include -#include -#include -#include "zlib.h" -#include "unzip.h" - -#define __PHYSICSFS_INTERNAL__ -#include "physfs_internal.h" - -#ifdef STDC -# include -# include -# include -#endif -#ifdef NO_ERRNO_H - extern int errno; -#else -# include -#endif - - -#ifndef local -# define local static -#endif -/* compile with -Dlocal if your debugger can't find static symbols */ - - - -#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ - !defined(CASESENSITIVITYDEFAULT_NO) -#define CASESENSITIVITYDEFAULT_NO -#endif - - -#ifndef UNZ_BUFSIZE -#define UNZ_BUFSIZE (16384) -#endif - -#ifndef UNZ_MAXFILENAMEINZIP -#define UNZ_MAXFILENAMEINZIP (256) -#endif - -#ifndef ALLOC -# define ALLOC(size) (malloc(size)) -#endif -#ifndef TRYFREE -# define TRYFREE(p) {if (p) free(p);} -#endif - -#define SIZECENTRALDIRITEM (0x2e) -#define SIZEZIPLOCALHEADER (0x1e) - - -/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ - -#ifndef SEEK_CUR -#define SEEK_CUR 1 -#endif - -#ifndef SEEK_END -#define SEEK_END 2 -#endif - -#ifndef SEEK_SET -#define SEEK_SET 0 -#endif - -const char unz_copyright[] = - " unzip 0.15 Copyright 1998 Gilles Vollant "; - -/* unz_file_info_interntal contain internal info about a file in zipfile*/ -typedef struct unz_file_info_internal_s -{ - uLong offset_curfile;/* relative offset of local header 4 bytes */ -} unz_file_info_internal; - - -/* file_in_zip_read_info_s contain internal information about a file in zipfile, - when reading and decompress it */ -typedef struct -{ - char *read_buffer; /* internal buffer for compressed data */ - z_stream stream; /* zLib stream structure for inflate */ - - uLong pos_in_zipfile; /* position in byte on the zipfile, for seek*/ - uLong stream_initialised; /* flag set if stream structure is initialised*/ - - uLong offset_local_extrafield;/* offset of the local extra field */ - uInt size_local_extrafield;/* size of the local extra field */ - uLong pos_local_extrafield; /* position in the local extra field in read*/ - - uLong crc32; /* crc32 of all data uncompressed */ - uLong crc32_wait; /* crc32 we must obtain after decompress all */ - uLong rest_read_compressed; /* number of byte to be decompressed */ - uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ - void* file; /* io structore of the zipfile */ - uLong compression_method; /* compression method (0==store) */ - uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ -} file_in_zip_read_info_s; - - -/* unz_s contain internal information about the zipfile -*/ -typedef struct -{ - void* file; /* io structore of the zipfile */ - unz_global_info gi; /* public global information */ - uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ - uLong num_file; /* number of the current file in the zipfile*/ - uLong pos_in_central_dir; /* pos of the current file in the central dir*/ - uLong current_file_ok; /* flag about the usability of the current file*/ - uLong central_pos; /* position of the beginning of the central dir*/ - - uLong size_central_dir; /* size of the central directory */ - uLong offset_central_dir; /* offset of start of central directory with - respect to the starting disk number */ - - unz_file_info cur_file_info; /* public info about the current file in zip*/ - unz_file_info_internal cur_file_info_internal; /* private info about it*/ - file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current - file if we are decompressing it */ -} unz_s; - - -/* =========================================================================== - Read a byte from a gz_stream; update next_in and avail_in. Return EOF - for end of file. - IN assertion: the stream s has been sucessfully opened for reading. -*/ - - -#if 0 /* not actually used anymore, at this point. */ -local int unzlocal_getByte(fin,pi) - void *fin; - int *pi; -{ - PHYSFS_uint8 c; - PHYSFS_sint64 err = __PHYSFS_platformRead(fin, &c, sizeof (c), 1); - if (err==1) - { - *pi = (int)c; - return UNZ_OK; - } - else - { - if (__PHYSFS_platformEOF(fin)) - return UNZ_EOF; - else - return UNZ_ERRNO; - } -} -#endif - -/* =========================================================================== - Reads a long in LSB order from the given gz_stream. Sets -*/ -local int unzlocal_getShort (fin,pX) - void* fin; - uLong *pX; -{ - PHYSFS_uint16 val; - PHYSFS_sint64 err = __PHYSFS_platformRead(fin, &val, sizeof (val), 1); - if (err==1) - { - *pX = (uLong) PHYSFS_swapULE16(val); - return UNZ_OK; - } - else - { - *pX = 0; - if (__PHYSFS_platformEOF(fin)) - return UNZ_EOF; - else - return UNZ_ERRNO; - } - - return(UNZ_ERRNO); /* shouldn't ever hit this. */ -} - -local int unzlocal_getLong (fin,pX) - void* fin; - uLong *pX; -{ - PHYSFS_uint32 val; - PHYSFS_sint64 err = __PHYSFS_platformRead(fin, &val, sizeof (val), 1); - if (err==1) - { - *pX = (uLong) PHYSFS_swapULE32(val); - return UNZ_OK; - } - else - { - *pX = 0; - if (__PHYSFS_platformEOF(fin)) - return UNZ_EOF; - else - return UNZ_ERRNO; - } - - return(UNZ_ERRNO); /* shouldn't ever hit this. */ -} - - -/* My own strcmpi / strcasecmp */ -#if 1 - -#define strcmpcasenosensitive_internal(x,y) __PHYSFS_platformStricmp(x,y) - -#else -local int strcmpcasenosensitive_internal (fileName1,fileName2) - const char* fileName1; - const char* fileName2; -{ - for (;;) - { - char c1=*(fileName1++); - char c2=*(fileName2++); - if ((c1>='a') && (c1<='z')) - c1 -= 0x20; - if ((c2>='a') && (c2<='z')) - c2 -= 0x20; - if (c1=='\0') - return ((c2=='\0') ? 0 : -1); - if (c2=='\0') - return 1; - if (c1c2) - return 1; - } -} -#endif - -#ifdef CASESENSITIVITYDEFAULT_NO -#define CASESENSITIVITYDEFAULTVALUE 2 -#else -#define CASESENSITIVITYDEFAULTVALUE 1 -#endif - -#ifndef STRCMPCASENOSENTIVEFUNCTION -#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal -#endif - -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) - -*/ -extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) - const char* fileName1; - const char* fileName2; - int iCaseSensitivity; -{ - if (iCaseSensitivity==0) - iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; - - if (iCaseSensitivity==1) - return strcmp(fileName1,fileName2); - - return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); -} - -#define BUFREADCOMMENT (0x400) - -/* - Locate the Central directory of a zipfile (at the end, just before - the global comment) -*/ -local uLong unzlocal_SearchCentralDir(fin) - void *fin; -{ - unsigned char* buf; - uLong uSizeFile; - uLong uBackRead; - uLong uMaxBack=0xffff; /* maximum size of global comment */ - uLong uPosFound=0; - - uSizeFile = (uLong) __PHYSFS_platformFileLength( fin ); - - if (uMaxBack>uSizeFile) - uMaxBack = uSizeFile; - - buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); - if (buf==NULL) - return 0; - - uBackRead = 4; - while (uBackReaduMaxBack) - uBackRead = uMaxBack; - else - uBackRead+=BUFREADCOMMENT; - uReadPos = uSizeFile-uBackRead ; - - uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? - (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); - if (!__PHYSFS_platformSeek(fin,uReadPos)) - break; - - if (__PHYSFS_platformRead(fin,buf,(uInt)uReadSize,1)!=1) - break; - - for (i=(int)uReadSize-3; (i--)>0;) - { - if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && - ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) - { - uPosFound = uReadPos+i; - break; - } - } - - if (uPosFound!=0) - break; - } - TRYFREE(buf); - return uPosFound; -} - -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer - "zlib/zlib109.zip". - If the zipfile cannot be opened (file don't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ -extern unzFile ZEXPORT unzOpen (path) - const char *path; -{ - unz_s us; - unz_s *s; - uLong central_pos,uL; - void* fin ; - - uLong number_disk; /* number of the current dist, used for - spaning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used - for spaning ZIP, unsupported, always 0*/ - uLong number_entry_CD; /* total number of entries in - the central dir - (same than number_entry on nospan) */ - - int err=UNZ_OK; - - if (unz_copyright[0]!=' ') - return NULL; - - fin=__PHYSFS_platformOpenRead(path); - if (fin==NULL) - return NULL; - - central_pos = unzlocal_SearchCentralDir(fin); - if (central_pos==0) - err=UNZ_ERRNO; - - if (!__PHYSFS_platformSeek(fin,central_pos)) - err=UNZ_ERRNO; - - /* the signature, already checked */ - if (unzlocal_getLong(fin,&uL)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of this disk */ - if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) - err=UNZ_ERRNO; - - /* number of the disk with the start of the central directory */ - if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir on this disk */ - if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) - err=UNZ_ERRNO; - - /* total number of entries in the central dir */ - if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((number_entry_CD!=us.gi.number_entry) || - (number_disk_with_CD!=0) || - (number_disk!=0)) - err=UNZ_BADZIPFILE; - - /* size of the central directory */ - if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* offset of start of central directory with respect to the - starting disk number */ - if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) - err=UNZ_ERRNO; - - /* zipfile comment length */ - if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) - err=UNZ_ERRNO; - - if ((central_pospfile_in_zip_read!=NULL) - unzCloseCurrentFile(file); - - __PHYSFS_platformClose(s->file); - TRYFREE(s); - return UNZ_OK; -} - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ -extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) - unzFile file; - unz_global_info *pglobal_info; -{ - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - *pglobal_info=s->gi; - return UNZ_OK; -} - - -/* - Translate date/time from Dos format to tm_unz (readable more easilty) -*/ -local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) - uLong ulDosDate; - tm_unz* ptm; -{ - uLong uDate; - uDate = (uLong)(ulDosDate>>16); - ptm->tm_mday = (uInt)(uDate&0x1f) ; - ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; - ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; - - ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); - ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; - ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; -} - -/* - Get Info about the current file in the zipfile, with internal only info -*/ -local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, - unz_file_info *pfile_info, - unz_file_info_internal - *pfile_info_internal, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); - -local int unzlocal_GetCurrentFileInfoInternal (file, - pfile_info, - pfile_info_internal, - szFileName, fileNameBufferSize, - extraField, extraFieldBufferSize, - szComment, commentBufferSize) - unzFile file; - unz_file_info *pfile_info; - unz_file_info_internal *pfile_info_internal; - char *szFileName; - uLong fileNameBufferSize; - void *extraField; - uLong extraFieldBufferSize; - char *szComment; - uLong commentBufferSize; -{ - unz_s* s; - unz_file_info file_info; - unz_file_info_internal file_info_internal; - int err=UNZ_OK; - uLong uMagic; - long lSeek=0; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!__PHYSFS_platformSeek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile)) - err=UNZ_ERRNO; - - - /* we check the magic */ - if (err==UNZ_OK) - { - if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x02014b50) - err=UNZ_BADZIPFILE; - } - - if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) - err=UNZ_ERRNO; - - unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); - - if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) - err=UNZ_ERRNO; - - lSeek+=file_info.size_filename; - if ((err==UNZ_OK) && (szFileName!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_filename0) && (fileNameBufferSize>0)) - { - if (__PHYSFS_platformRead(s->file,szFileName,(uInt)uSizeRead,1)!=1) - err=UNZ_ERRNO; - } - lSeek -= uSizeRead; - } - - - if ((err==UNZ_OK) && (extraField!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_extrafile); - if ((curpos >= 0) && (__PHYSFS_platformSeek(s->file,lSeek+curpos))) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) - { - if (__PHYSFS_platformRead(s->file,extraField,(uInt)uSizeRead,1)!=1) - err=UNZ_ERRNO; - } - lSeek += file_info.size_file_extra - uSizeRead; - } - else - lSeek+=file_info.size_file_extra; - - - if ((err==UNZ_OK) && (szComment!=NULL)) - { - uLong uSizeRead ; - if (file_info.size_file_commentfile); - if (__PHYSFS_platformSeek(s->file,lSeek+curpos)) - lSeek=0; - else - err=UNZ_ERRNO; - } - if ((file_info.size_file_comment>0) && (commentBufferSize>0)) - { - if (__PHYSFS_platformRead(s->file,szComment,(uInt)uSizeRead,1)!=1) - err=UNZ_ERRNO; - } - lSeek+=file_info.size_file_comment - uSizeRead; - } - else - lSeek+=file_info.size_file_comment; - - if ((err==UNZ_OK) && (pfile_info!=NULL)) - *pfile_info=file_info; - - if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) - *pfile_info_internal=file_info_internal; - - return err; -} - - - -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. -*/ -extern int ZEXPORT unzGetCurrentFileInfo (file, - pfile_info, - szFileName, fileNameBufferSize, - extraField, extraFieldBufferSize, - szComment, commentBufferSize) - unzFile file; - unz_file_info *pfile_info; - char *szFileName; - uLong fileNameBufferSize; - void *extraField; - uLong extraFieldBufferSize; - char *szComment; - uLong commentBufferSize; -{ - return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, - szFileName,fileNameBufferSize, - extraField,extraFieldBufferSize, - szComment,commentBufferSize); -} - -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ -extern int ZEXPORT unzGoToFirstFile (file) - unzFile file; -{ - int err=UNZ_OK; - unz_s* s; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - s->pos_in_central_dir=s->offset_central_dir; - s->num_file=0; - err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - - -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ -extern int ZEXPORT unzGoToNextFile (file) - unzFile file; -{ - unz_s* s; - int err; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - if (s->num_file+1==s->gi.number_entry) - return UNZ_END_OF_LIST_OF_FILE; - - s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + - s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; - s->num_file++; - err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, - &s->cur_file_info_internal, - NULL,0,NULL,0,NULL,0); - s->current_file_ok = (err == UNZ_OK); - return err; -} - - -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzipStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ -extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) - unzFile file; - const char *szFileName; - int iCaseSensitivity; -{ - unz_s* s; - int err; - - - uLong num_fileSaved; - uLong pos_in_central_dirSaved; - - - if (file==NULL) - return UNZ_PARAMERROR; - - if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) - return UNZ_PARAMERROR; - - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_END_OF_LIST_OF_FILE; - - num_fileSaved = s->num_file; - pos_in_central_dirSaved = s->pos_in_central_dir; - - err = unzGoToFirstFile(file); - - while (err == UNZ_OK) - { - char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; - unzGetCurrentFileInfo(file,NULL, - szCurrentFileName,sizeof(szCurrentFileName)-1, - NULL,0,NULL,0); - if (unzStringFileNameCompare(szCurrentFileName, - szFileName,iCaseSensitivity)==0) - return UNZ_OK; - err = unzGoToNextFile(file); - } - - s->num_file = num_fileSaved ; - s->pos_in_central_dir = pos_in_central_dirSaved ; - return err; -} - - -/* - Read the local header of the current zipfile - Check the coherency of the local header and info in the end of central - directory about this file - store in *piSizeVar the size of extra info in local header - (filename and size of extra field data) -*/ -local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, - poffset_local_extrafield, - psize_local_extrafield) - unz_s* s; - uInt* piSizeVar; - uLong *poffset_local_extrafield; - uInt *psize_local_extrafield; -{ - uLong uMagic,uData,uFlags; - uLong size_filename; - uLong size_extra_field; - int err=UNZ_OK; - - *piSizeVar = 0; - *poffset_local_extrafield = 0; - *psize_local_extrafield = 0; - - if (!__PHYSFS_platformSeek(s->file, - s->cur_file_info_internal.offset_curfile + - s->byte_before_the_zipfile)) - { - return UNZ_ERRNO; - } - - if (err==UNZ_OK) - { - if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) - err=UNZ_ERRNO; - else if (uMagic!=0x04034b50) - err=UNZ_BADZIPFILE; - } - - if (unzlocal_getShort(s->file,&uData) != UNZ_OK) - err=UNZ_ERRNO; -/* - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) - err=UNZ_BADZIPFILE; -*/ - if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) - err=UNZ_ERRNO; - - if (unzlocal_getShort(s->file,&uData) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) - err=UNZ_BADZIPFILE; - - if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */ - err=UNZ_ERRNO; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */ - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && - ((uFlags & 8)==0)) - err=UNZ_BADZIPFILE; - - - if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) - err=UNZ_ERRNO; - else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) - err=UNZ_BADZIPFILE; - - *piSizeVar += (uInt)size_filename; - - if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) - err=UNZ_ERRNO; - *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + - SIZEZIPLOCALHEADER + size_filename; - *psize_local_extrafield = (uInt)size_extra_field; - - *piSizeVar += (uInt)size_extra_field; - - return err; -} - -/* - Open for reading data the current file in the zipfile. - If there is no error and the file is opened, the return value is UNZ_OK. -*/ -extern int ZEXPORT unzOpenCurrentFile (file) - unzFile file; -{ - int err=UNZ_OK; - int Store; - uInt iSizeVar; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uLong offset_local_extrafield; /* offset of the local extra field */ - uInt size_local_extrafield; /* size of the local extra field */ - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - if (!s->current_file_ok) - return UNZ_PARAMERROR; - - if (s->pfile_in_zip_read != NULL) - unzCloseCurrentFile(file); - - if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, - &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) - return UNZ_BADZIPFILE; - - pfile_in_zip_read_info = (file_in_zip_read_info_s*) - ALLOC(sizeof(file_in_zip_read_info_s)); - if (pfile_in_zip_read_info==NULL) - return UNZ_INTERNALERROR; - - pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); - pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; - pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; - pfile_in_zip_read_info->pos_local_extrafield=0; - - if (pfile_in_zip_read_info->read_buffer==NULL) - { - TRYFREE(pfile_in_zip_read_info); - return UNZ_INTERNALERROR; - } - - pfile_in_zip_read_info->stream_initialised=0; - - if ((s->cur_file_info.compression_method!=0) && - (s->cur_file_info.compression_method!=Z_DEFLATED)) - err=UNZ_BADZIPFILE; - Store = s->cur_file_info.compression_method==0; - - pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; - pfile_in_zip_read_info->crc32=0; - pfile_in_zip_read_info->compression_method = - s->cur_file_info.compression_method; - pfile_in_zip_read_info->file=s->file; - pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; - - pfile_in_zip_read_info->stream.total_out = 0; - - if (!Store) - { - pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; - pfile_in_zip_read_info->stream.zfree = (free_func)0; - pfile_in_zip_read_info->stream.opaque = (voidpf)0; - - err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); - if (err == Z_OK) - pfile_in_zip_read_info->stream_initialised=1; - /* windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - * In unzip, i don't wait absolutely Z_STREAM_END because I known the - * size of both compressed and uncompressed data - */ - } - pfile_in_zip_read_info->rest_read_compressed = - s->cur_file_info.compressed_size ; - pfile_in_zip_read_info->rest_read_uncompressed = - s->cur_file_info.uncompressed_size ; - - - pfile_in_zip_read_info->pos_in_zipfile = - s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + - iSizeVar; - - pfile_in_zip_read_info->stream.avail_in = (uInt)0; - - - s->pfile_in_zip_read = pfile_in_zip_read_info; - return UNZ_OK; -} - - -/* - Read bytes from the current file. - buf contain buffer where data must be copied - len the size of buf. - - return the number of byte copied if somes bytes are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ -extern int ZEXPORT unzReadCurrentFile (file, buf, len) - unzFile file; - voidp buf; - unsigned len; -{ - int err=UNZ_OK; - uInt iRead = 0; - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - - if ((pfile_in_zip_read_info->read_buffer == NULL)) - return UNZ_END_OF_LIST_OF_FILE; - if (len==0) - return 0; - - pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; - - pfile_in_zip_read_info->stream.avail_out = (uInt)len; - - if (len>pfile_in_zip_read_info->rest_read_uncompressed) - pfile_in_zip_read_info->stream.avail_out = - (uInt)pfile_in_zip_read_info->rest_read_uncompressed; - - while (pfile_in_zip_read_info->stream.avail_out>0) - { - if ((pfile_in_zip_read_info->stream.avail_in==0) && - (pfile_in_zip_read_info->rest_read_compressed>0)) - { - uInt uReadThis = UNZ_BUFSIZE; - if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; - if (uReadThis == 0) - return UNZ_EOF; - if (!__PHYSFS_platformSeek(pfile_in_zip_read_info->file, - pfile_in_zip_read_info->pos_in_zipfile + - pfile_in_zip_read_info->byte_before_the_zipfile)) - return UNZ_ERRNO; - if (__PHYSFS_platformRead(pfile_in_zip_read_info->file, - pfile_in_zip_read_info->read_buffer, - uReadThis,1)!=1) - return UNZ_ERRNO; - pfile_in_zip_read_info->pos_in_zipfile += uReadThis; - - pfile_in_zip_read_info->rest_read_compressed-=uReadThis; - - pfile_in_zip_read_info->stream.next_in = - (Bytef*)pfile_in_zip_read_info->read_buffer; - pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; - } - - if (pfile_in_zip_read_info->compression_method==0) - { - uInt uDoCopy,i ; - if (pfile_in_zip_read_info->stream.avail_out < - pfile_in_zip_read_info->stream.avail_in) - uDoCopy = pfile_in_zip_read_info->stream.avail_out ; - else - uDoCopy = pfile_in_zip_read_info->stream.avail_in ; - - for (i=0;istream.next_out+i) = - *(pfile_in_zip_read_info->stream.next_in+i); - - pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, - pfile_in_zip_read_info->stream.next_out, - uDoCopy); - pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; - pfile_in_zip_read_info->stream.avail_in -= uDoCopy; - pfile_in_zip_read_info->stream.avail_out -= uDoCopy; - pfile_in_zip_read_info->stream.next_out += uDoCopy; - pfile_in_zip_read_info->stream.next_in += uDoCopy; - pfile_in_zip_read_info->stream.total_out += uDoCopy; - iRead += uDoCopy; - } - else - { - uLong uTotalOutBefore,uTotalOutAfter; - const Bytef *bufBefore; - uLong uOutThis; - int flush=Z_SYNC_FLUSH; - - uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; - bufBefore = pfile_in_zip_read_info->stream.next_out; - - /* - if ((pfile_in_zip_read_info->rest_read_uncompressed == - pfile_in_zip_read_info->stream.avail_out) && - (pfile_in_zip_read_info->rest_read_compressed == 0)) - flush = Z_FINISH; - */ - err=inflate(&pfile_in_zip_read_info->stream,flush); - - uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; - uOutThis = uTotalOutAfter-uTotalOutBefore; - - pfile_in_zip_read_info->crc32 = - crc32(pfile_in_zip_read_info->crc32,bufBefore, - (uInt)(uOutThis)); - - pfile_in_zip_read_info->rest_read_uncompressed -= - uOutThis; - - iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); - - if (err==Z_STREAM_END) - return (iRead==0) ? UNZ_EOF : iRead; - if (err!=Z_OK) - break; - } - } - - if (err==Z_OK) - return iRead; - return err; -} - - -/* - Give the current position in uncompressed data -*/ -extern z_off_t ZEXPORT unztell (file) - unzFile file; -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - return (z_off_t)pfile_in_zip_read_info->stream.total_out; -} - - -/* - return 1 if the end of file was reached, 0 elsewhere -*/ -extern int ZEXPORT unzeof (file) - unzFile file; -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - if (pfile_in_zip_read_info->rest_read_uncompressed == 0) - return 1; - else - return 0; -} - - - -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the local-header version of the extra field (sometimes, there is - more info in the local-header version than in the central-header) - - if buf==NULL, it return the size of the local extra field that can be read - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of bytes copied in buf, or (if <0) - the error code -*/ -extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) - unzFile file; - voidp buf; - unsigned len; -{ - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - uInt read_now; - uLong size_to_read; - - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - size_to_read = (pfile_in_zip_read_info->size_local_extrafield - - pfile_in_zip_read_info->pos_local_extrafield); - - if (buf==NULL) - return (int)size_to_read; - - if (len>size_to_read) - read_now = (uInt)size_to_read; - else - read_now = (uInt)len ; - - if (read_now==0) - return 0; - - if (!__PHYSFS_platformSeek(pfile_in_zip_read_info->file, - pfile_in_zip_read_info->offset_local_extrafield + - pfile_in_zip_read_info->pos_local_extrafield)) - return UNZ_ERRNO; - - if (__PHYSFS_platformRead(pfile_in_zip_read_info->file,buf,(uInt)size_to_read,1)!=1) - return UNZ_ERRNO; - - return (int)read_now; -} - -/* - Close the file in zip opened with unzipOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ -extern int ZEXPORT unzCloseCurrentFile (file) - unzFile file; -{ - int err=UNZ_OK; - - unz_s* s; - file_in_zip_read_info_s* pfile_in_zip_read_info; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - pfile_in_zip_read_info=s->pfile_in_zip_read; - - if (pfile_in_zip_read_info==NULL) - return UNZ_PARAMERROR; - - - if (pfile_in_zip_read_info->rest_read_uncompressed == 0) - { - if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) - err=UNZ_CRCERROR; - } - - - TRYFREE(pfile_in_zip_read_info->read_buffer); - pfile_in_zip_read_info->read_buffer = NULL; - if (pfile_in_zip_read_info->stream_initialised) - inflateEnd(&pfile_in_zip_read_info->stream); - - pfile_in_zip_read_info->stream_initialised = 0; - TRYFREE(pfile_in_zip_read_info); - - s->pfile_in_zip_read=NULL; - - return err; -} - - -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of byte copied or an error code <0 -*/ -extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) - unzFile file; - char *szComment; - uLong uSizeBuf; -{ - /*int err=UNZ_OK;*/ - unz_s* s; - uLong uReadThis ; - if (file==NULL) - return UNZ_PARAMERROR; - s=(unz_s*)file; - - uReadThis = uSizeBuf; - if (uReadThis>s->gi.size_comment) - uReadThis = s->gi.size_comment; - - if (!__PHYSFS_platformSeek(s->file,s->central_pos+22)) - return UNZ_ERRNO; - - if (uReadThis>0) - { - *szComment='\0'; - if (__PHYSFS_platformRead(s->file,szComment,(uInt)uReadThis,1)!=1) - return UNZ_ERRNO; - } - - if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) - *(szComment+s->gi.size_comment)='\0'; - return (int)uReadThis; -} - -#endif /* defined PHYSFS_SUPPORTS_ZIP */ - -/* end of unzip.c ... */ - diff --git a/archivers/unzip.h b/archivers/unzip.h deleted file mode 100644 index 76692cb7..00000000 --- a/archivers/unzip.h +++ /dev/null @@ -1,275 +0,0 @@ -/* unzip.h -- IO for uncompress .zip files using zlib - Version 0.15 beta, Mar 19th, 1998, - - Copyright (C) 1998 Gilles Vollant - - This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g - WinZip, InfoZip tools and compatible. - Encryption and multi volume ZipFile (span) are not supported. - Old compressions used by old PKZip 1.x are not supported - - THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE - CAN CHANGE IN FUTURE VERSION !! - I WAIT FEEDBACK at mail info@winimage.com - Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution - - Condition of use and distribution are the same than zlib : - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - -*/ -/* for more info about .ZIP format, see - ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip - PkWare has also a specification at : - ftp://ftp.pkware.com/probdesc.zip */ - -#ifndef _unz_H -#define _unz_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef _ZLIB_H -#include "zlib.h" -#endif - -#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) -/* like the STRICT of WIN32, we define a pointer that cannot be converted - from (void*) without cast */ -typedef struct TagunzFile__ { int unused; } unzFile__; -typedef unzFile__ *unzFile; -#else -typedef voidp unzFile; -#endif - - -#define UNZ_OK (0) -#define UNZ_END_OF_LIST_OF_FILE (-100) -#define UNZ_ERRNO (Z_ERRNO) -#define UNZ_EOF (0) -#define UNZ_PARAMERROR (-102) -#define UNZ_BADZIPFILE (-103) -#define UNZ_INTERNALERROR (-104) -#define UNZ_CRCERROR (-105) - -/* tm_unz contain date/time info */ -typedef struct tm_unz_s -{ - uInt tm_sec; /* seconds after the minute - [0,59] */ - uInt tm_min; /* minutes after the hour - [0,59] */ - uInt tm_hour; /* hours since midnight - [0,23] */ - uInt tm_mday; /* day of the month - [1,31] */ - uInt tm_mon; /* months since January - [0,11] */ - uInt tm_year; /* years - [1980..2044] */ -} tm_unz; - -/* unz_global_info structure contain global data about the ZIPfile - These data comes from the end of central dir */ -typedef struct unz_global_info_s -{ - uLong number_entry; /* total number of entries in - the central dir on this disk */ - uLong size_comment; /* size of the global comment of the zipfile */ -} unz_global_info; - - -/* unz_file_info contain information about a file in the zipfile */ -typedef struct unz_file_info_s -{ - uLong version; /* version made by 2 bytes */ - uLong version_needed; /* version needed to extract 2 bytes */ - uLong flag; /* general purpose bit flag 2 bytes */ - uLong compression_method; /* compression method 2 bytes */ - uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ - uLong crc; /* crc-32 4 bytes */ - uLong compressed_size; /* compressed size 4 bytes */ - uLong uncompressed_size; /* uncompressed size 4 bytes */ - uLong size_filename; /* filename length 2 bytes */ - uLong size_file_extra; /* extra field length 2 bytes */ - uLong size_file_comment; /* file comment length 2 bytes */ - - uLong disk_num_start; /* disk number start 2 bytes */ - uLong internal_fa; /* internal file attributes 2 bytes */ - uLong external_fa; /* external file attributes 4 bytes */ - - tm_unz tmu_date; -} unz_file_info; - -extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, - const char* fileName2, - int iCaseSensitivity)); -/* - Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi - or strcasecmp) - If iCaseSenisivity = 0, case sensitivity is defaut of your operating system - (like 1 on Unix, 2 on Windows) -*/ - - -extern unzFile ZEXPORT unzOpen OF((const char *path)); -/* - Open a Zip file. path contain the full pathname (by example, - on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer - "zlib/zlib111.zip". - If the zipfile cannot be opened (file don't exist or in not valid), the - return value is NULL. - Else, the return value is a unzFile Handle, usable with other function - of this unzip package. -*/ - -extern int ZEXPORT unzClose OF((unzFile file)); -/* - Close a ZipFile opened with unzipOpen. - If there is files inside the .Zip opened with unzOpenCurrentFile (see later), - these files MUST be closed with unzipCloseCurrentFile before call unzipClose. - return UNZ_OK if there is no problem. */ - -extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, - unz_global_info *pglobal_info)); -/* - Write info about the ZipFile in the *pglobal_info structure. - No preparation of the structure is needed - return UNZ_OK if there is no problem. */ - - -extern int ZEXPORT unzGetGlobalComment OF((unzFile file, - char *szComment, - uLong uSizeBuf)); -/* - Get the global comment string of the ZipFile, in the szComment buffer. - uSizeBuf is the size of the szComment buffer. - return the number of byte copied or an error code <0 -*/ - - -/***************************************************************************/ -/* Unzip package allow you browse the directory of the zipfile */ - -extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); -/* - Set the current file of the zipfile to the first file. - return UNZ_OK if there is no problem -*/ - -extern int ZEXPORT unzGoToNextFile OF((unzFile file)); -/* - Set the current file of the zipfile to the next file. - return UNZ_OK if there is no problem - return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. -*/ - -extern int ZEXPORT unzLocateFile OF((unzFile file, - const char *szFileName, - int iCaseSensitivity)); -/* - Try locate the file szFileName in the zipfile. - For the iCaseSensitivity signification, see unzStringFileNameCompare - - return value : - UNZ_OK if the file is found. It becomes the current file. - UNZ_END_OF_LIST_OF_FILE if the file is not found -*/ - - -extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, - unz_file_info *pfile_info, - char *szFileName, - uLong fileNameBufferSize, - void *extraField, - uLong extraFieldBufferSize, - char *szComment, - uLong commentBufferSize)); -/* - Get Info about the current file - if pfile_info!=NULL, the *pfile_info structure will contain somes info about - the current file - if szFileName!=NULL, the filemane string will be copied in szFileName - (fileNameBufferSize is the size of the buffer) - if extraField!=NULL, the extra field information will be copied in extraField - (extraFieldBufferSize is the size of the buffer). - This is the Central-header version of the extra field - if szComment!=NULL, the comment string of the file will be copied in szComment - (commentBufferSize is the size of the buffer) -*/ - -/***************************************************************************/ -/* for reading the content of the current zipfile, you can open it, read data - from it, and close it (you can close it before reading all the file) - */ - -extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); -/* - Open for reading data the current file in the zipfile. - If there is no error, the return value is UNZ_OK. -*/ - -extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); -/* - Close the file in zip opened with unzOpenCurrentFile - Return UNZ_CRCERROR if all the file was read but the CRC is not good -*/ - - -extern int ZEXPORT unzReadCurrentFile OF((unzFile file, - voidp buf, - unsigned len)); -/* - Read bytes from the current file (opened by unzOpenCurrentFile) - buf contain buffer where data must be copied - len the size of buf. - - return the number of byte copied if somes bytes are copied - return 0 if the end of file was reached - return <0 with error code if there is an error - (UNZ_ERRNO for IO error, or zLib error for uncompress error) -*/ - -extern z_off_t ZEXPORT unztell OF((unzFile file)); -/* - Give the current position in uncompressed data -*/ - -extern int ZEXPORT unzeof OF((unzFile file)); -/* - return 1 if the end of file was reached, 0 elsewhere -*/ - -extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, - voidp buf, - unsigned len)); -/* - Read extra field from the current file (opened by unzOpenCurrentFile) - This is the local-header version of the extra field (sometimes, there is - more info in the local-header version than in the central-header) - - if buf==NULL, it return the size of the local extra field - - if buf!=NULL, len is the size of the buffer, the extra header is copied in - buf. - the return value is the number of bytes copied in buf, or (if <0) - the error code -*/ - -#ifdef __cplusplus -} -#endif - -#endif /* _unz_H */ diff --git a/archivers/zip.c b/archivers/zip.c index 5bede36d..d353ff48 100644 --- a/archivers/zip.c +++ b/archivers/zip.c @@ -3,7 +3,8 @@ * * Please see the file LICENSE in the source's root directory. * - * This file written by Ryan C. Gordon. + * This file written by Ryan C. Gordon, with some peeking at "unzip.c" + * by Gilles Vollant. */ #if HAVE_CONFIG_H @@ -12,50 +13,90 @@ #if (defined PHYSFS_SUPPORTS_ZIP) -/* - * !!! FIXME: overall design bugs. - * - * Maybe add a seekToStartOfCurrentFile() in unzip.c if complete seek - * semantics are impossible. - * - * Could be more i/o efficient if we combined unzip.c and this file. - * (and thus lose all the unzGoToNextFile() dummy loops. - */ - #include #include #include #include #include -#include "physfs.h" -#include "unzip.h" +#include +#include "physfs.h" +#include "zlib.h" #define __PHYSICSFS_INTERNAL__ #include "physfs_internal.h" -#define MAXZIPENTRYSIZE 256 +/* + * A buffer of ZIP_READBUFSIZE is malloc() for each compressed file opened, + * and is free()'d when you close the file; compressed data is read into + * this buffer, and then is decompressed into the buffer passed to + * PHYSFS_read(). + * + * Uncompressed entries in a zipfile do not allocate this buffer; they just + * read data directly into the buffer passed to PHYSFS_read(). + * + * Depending on your speed and memory requirements, you should tweak this + * value. + */ +#define ZIP_READBUFSIZE (16 * 1024) + +/* + * One ZIPentry is kept for each file in an open ZIP archive. + */ typedef struct { - char *name; - unz_file_info info; - char *symlink; + char *name; /* Name of file in archive */ + char *symlink; /* NULL or file we link to */ + int fixed_up; /* Have we updated offset? */ + PHYSFS_uint32 offset; /* offset of data in file */ + PHYSFS_uint16 version; /* version made by */ + PHYSFS_uint16 version_needed; /* version needed to extract */ + PHYSFS_uint16 flag; /* general purpose bit flag */ + PHYSFS_uint16 compression_method; /* compression method */ + PHYSFS_uint32 crc; /* crc-32 */ + PHYSFS_uint32 compressed_size; /* compressed size */ + PHYSFS_uint32 uncompressed_size; /* uncompressed size */ + PHYSFS_sint64 last_mod_time; /* last file mod time */ } ZIPentry; +/* + * One ZIPinfo is kept for each open ZIP archive. + */ typedef struct { - char *archiveName; - unz_global_info global; - ZIPentry *entries; + char *archiveName; /* path to ZIP in platform-dependent notation. */ + PHYSFS_uint16 entryCount; /* Number of files in ZIP. */ + ZIPentry *entries; /* info on all files in ZIP. */ } ZIPinfo; +/* + * One ZIPfileinfo is kept for each open file in a ZIP archive. + */ typedef struct { - unzFile handle; + ZIPentry *entry; /* Info on file. */ + void *handle; /* physical file handle. */ + PHYSFS_uint32 compressed_position; /* offset in compressed data. */ + PHYSFS_uint32 uncompressed_position; /* tell() position. */ + PHYSFS_uint8 *buffer; /* decompression buffer. */ + z_stream stream; /* zlib stream state. */ } ZIPfileinfo; +/* Magic numbers... */ +#define ZIP_LOCAL_FILE_SIG 0x04034b50 +#define ZIP_CENTRAL_DIR_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50 + +/* compression methods... */ +#define COMPMETH_NONE 0 +/* ...and others... */ + + +#define MAXZIPENTRYSIZE 256 /* !!! FIXME: get rid of this. */ + + /* Number of symlinks to follow before we assume it's a recursive link... */ #define SYMLINK_RECURSE_COUNT 20 @@ -68,7 +109,7 @@ static int ZIP_seek(FileHandle *handle, PHYSFS_uint64 offset); static PHYSFS_sint64 ZIP_fileLength(FileHandle *handle); static int ZIP_fileClose(FileHandle *handle); static int ZIP_isArchive(const char *filename, int forWriting); -static char *ZIP_realpath(unzFile fh, unz_file_info *info, ZIPentry *entry); +static char *get_zip_realpath(void *in, ZIPentry *entry); static DirHandle *ZIP_openArchive(const char *name, int forWriting); static LinkedStringList *ZIP_enumerateFiles(DirHandle *h, const char *dirname, @@ -120,110 +161,374 @@ const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP = -static PHYSFS_sint64 ZIP_read(FileHandle *handle, void *buffer, +/* + * Wrap all zlib calls in this, so the physfs error state is set appropriately. + */ +static int zlib_err(int rc) +{ + const char *err = NULL; + + switch (rc) + { + case Z_OK: + case Z_STREAM_END: + break; /* not errors. */ + + case Z_ERRNO: + err = strerror(errno); + break; + + case Z_NEED_DICT: + err = "zlib: need dictionary"; + break; + case Z_DATA_ERROR: + err = "zlib: need dictionary"; + break; + case Z_MEM_ERROR: + err = "zlib: memory error"; + break; + case Z_BUF_ERROR: + err = "zlib: buffer error"; + break; + case Z_VERSION_ERROR: + err = "zlib: version error"; + break; + default: + err = "unknown zlib error"; + break; + } /* switch */ + + if (err != NULL) + __PHYSFS_setError(err); + + return(rc); +} /* zlib_err */ + + +/* + * Read an unsigned 32-bit int and swap to native byte order. + */ +static int readui32(void *in, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 v; + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE32(v); + return(1); +} /* readui32 */ + + +/* + * Read an unsigned 16-bit int and swap to native byte order. + */ +static int readui16(void *in, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 v; + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); + *val = PHYSFS_swapULE16(v); + return(1); +} /* readui16 */ + + +static PHYSFS_sint64 ZIP_read(FileHandle *handle, void *buf, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) { - unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle; - int bytes = (int) (objSize * objCount); /* !!! FIXME: overflow? */ - PHYSFS_sint32 rc = unzReadCurrentFile(fh, buffer, bytes); + ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque); + ZIPentry *entry = finfo->entry; + PHYSFS_sint64 retval = 0; + PHYSFS_sint64 maxread = ((PHYSFS_sint64) objSize) * objCount; + PHYSFS_sint64 avail = entry->uncompressed_size - + finfo->uncompressed_position; + + BAIL_IF_MACRO(maxread == 0, NULL, 0); /* quick rejection. */ + + if (avail < maxread) + { + maxread = avail - (avail % objSize); + objCount = maxread / objSize; + BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */ + __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */ + } /* if */ + + if (entry->compression_method == COMPMETH_NONE) + { + retval = __PHYSFS_platformRead(finfo->handle, buf, objSize, objCount); + } /* if */ + + else + { + finfo->stream.next_out = buf; + finfo->stream.avail_out = objSize * objCount; + + while (retval < maxread) + { + PHYSFS_uint32 before = finfo->stream.total_out; + int rc; + + if (finfo->stream.avail_in == 0) + { + PHYSFS_sint64 br; + + br = entry->compressed_size - finfo->compressed_position; + if (br > 0) + { + if (br > ZIP_READBUFSIZE) + br = ZIP_READBUFSIZE; + + br = __PHYSFS_platformRead(finfo->handle, + finfo->buffer, + 1, br); + if (br <= 0) + break; + + finfo->compressed_position += br; + finfo->stream.next_in = finfo->buffer; + finfo->stream.avail_in = br; + } /* if */ + } /* if */ - if (rc < bytes) - __PHYSFS_setError(ERR_PAST_EOF); - else if (rc == UNZ_ERRNO) - __PHYSFS_setError(ERR_IO_ERROR); - else if (rc < 0) - __PHYSFS_setError(ERR_COMPRESSION); + rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH)); + retval += (finfo->stream.total_out - before); - return(rc / objSize); + if (rc != Z_OK) + break; + } /* while */ + + retval /= objSize; + } /* else */ + + if (retval > 0) + finfo->uncompressed_position += (retval * objSize); + + return(retval); } /* ZIP_read */ static int ZIP_eof(FileHandle *handle) { - return(unzeof(((ZIPfileinfo *) (handle->opaque))->handle)); + ZIPfileinfo *finfo = ((ZIPfileinfo *) (handle->opaque)); + return(finfo->uncompressed_position >= finfo->entry->uncompressed_size); } /* ZIP_eof */ static PHYSFS_sint64 ZIP_tell(FileHandle *handle) { - return(unztell(((ZIPfileinfo *) (handle->opaque))->handle)); + return(((ZIPfileinfo *) (handle->opaque))->uncompressed_position); } /* ZIP_tell */ static int ZIP_seek(FileHandle *handle, PHYSFS_uint64 offset) { - /* !!! FIXME : this blows. */ - unzFile fh = ((ZIPfileinfo *) (handle->opaque))->handle; - char *buf = NULL; - PHYSFS_uint32 bufsize = 4096 * 2; - - BAIL_IF_MACRO(unztell(fh) == offset, NULL, 1); - BAIL_IF_MACRO(ZIP_fileLength(handle) <= (PHYSFS_sint64) offset, ERR_PAST_EOF, 0); + ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque); + ZIPentry *entry = finfo->entry; + void *in = finfo->handle; - /* reset to the start of the zipfile. */ - unzCloseCurrentFile(fh); - BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, 0); + BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0); - while ((buf == NULL) && (bufsize >= 512)) + if (entry->compression_method == COMPMETH_NONE) { - bufsize >>= 1; /* divides by two. */ - buf = (char *) malloc(bufsize); - } /* while */ - BAIL_IF_MACRO(buf == NULL, ERR_OUT_OF_MEMORY, 0); + PHYSFS_sint64 newpos = offset + entry->offset; + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, newpos), NULL, 0); + finfo->uncompressed_position = newpos; + } /* if */ - while (offset > 0) + else { - PHYSFS_uint32 chunk = (offset > bufsize) ? bufsize : (PHYSFS_uint32)offset; - PHYSFS_sint32 rc = unzReadCurrentFile(fh, buf, chunk); - BAIL_IF_MACRO(rc == 0, ERR_IO_ERROR, 0); /* shouldn't happen. */ - BAIL_IF_MACRO(rc == UNZ_ERRNO, ERR_IO_ERROR, 0); - BAIL_IF_MACRO(rc < 0, ERR_COMPRESSION, 0); - offset -= rc; - } /* while */ - - free(buf); - return(offset == 0); + /* + * If seeking backwards, we need to redecode the file + * from the start and throw away the compressed bits until we hit + * the offset we need. If seeking forward, we still need to + * redecode, but we don't rewind first. + */ + if (offset < finfo->uncompressed_position) + { + /* we do a copy so state is sane if inflateInit2() fails. */ + z_stream str; + memset(&str, '\0', sizeof (z_stream)); + if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK) + return(0); + + inflateEnd(&finfo->stream); + memcpy(&finfo->stream, &str, sizeof (z_stream)); + finfo->uncompressed_position = finfo->compressed_position = 0; + } /* if */ + + while (finfo->uncompressed_position != offset) + { + PHYSFS_uint8 buf[512]; + PHYSFS_uint32 maxread = offset - finfo->uncompressed_position; + if (maxread > sizeof (buf)) + maxread = sizeof (buf); + + if (ZIP_read(handle, buf, maxread, 1) != 1) + return(0); + } /* while */ + } /* else */ + + return(1); } /* ZIP_seek */ static PHYSFS_sint64 ZIP_fileLength(FileHandle *handle) { ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque); - unz_file_info info; - - unzGetCurrentFileInfo(finfo->handle, &info, NULL, 0, NULL, 0, NULL, 0); - return(info.uncompressed_size); + return(finfo->entry->uncompressed_size); } /* ZIP_fileLength */ static int ZIP_fileClose(FileHandle *handle) { ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque); - unzClose(finfo->handle); + __PHYSFS_platformClose(finfo->handle); + + if (finfo->entry->compression_method != COMPMETH_NONE) + inflateEnd(&finfo->stream); + + if (finfo->buffer != NULL) + free(finfo->buffer); + free(finfo); - free(handle); return(1); } /* ZIP_fileClose */ +static PHYSFS_sint64 find_end_of_central_dir(void *in, PHYSFS_sint64 *len) +{ + /* !!! FIXME: potential race condition! */ + /* !!! FIXME: mutex this or switch to smaller stack-based buffer. */ + static PHYSFS_uint8 buf[0xFFFF + 20]; + + PHYSFS_sint32 i; + PHYSFS_sint64 filelen; + PHYSFS_sint64 filepos; + PHYSFS_sint32 maxread; + + filelen = __PHYSFS_platformFileLength(in); + BAIL_IF_MACRO(filelen == -1, NULL, 0); + + /* + * Jump to the end of the file and start reading backwards. + * The last thing in the file is the zipfile comment, which is variable + * length, and the field that specifies its size is before it in the + * file (argh!)...this means that we need to scan backwards until we + * hit the end-of-central-dir signature. We can then sanity check that + * the comment was as big as it should be to make sure we're in the + * right place. The comment length field is 16 bits, so we can stop + * searching for that signature after 64k at most, and call it a + * corrupted zipfile. + */ + + /* + * !!! FIXME: This was easier than reading backwards in chunks, but it's + * !!! FIXME: rather memory hungry. + */ + if (sizeof (buf) < filelen) + { + filepos = filelen - sizeof (buf); + maxread = sizeof (buf); + } /* if */ + else + { + filepos = 0; + maxread = filelen; + } /* else */ + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, filepos), NULL, -1); + BAIL_IF_MACRO(__PHYSFS_platformRead(in, buf, maxread, 1) != 1, NULL, -1); + + for (i = maxread - 4; i > 0; i--) + { + if ((buf[i + 0] == 0x50) && + (buf[i + 1] == 0x4B) && + (buf[i + 2] == 0x05) && + (buf[i + 3] == 0x06) ) + { + break; /* that's the signature! */ + } /* if */ + } /* for */ + + BAIL_IF_MACRO(i < 0, ERR_NOT_AN_ARCHIVE, -1); + + if (len != NULL) + *len = filelen; + + return(filelen - (maxread - i)); +} /* find_end_of_central_dir */ + + static int ZIP_isArchive(const char *filename, int forWriting) { - int retval = 0; - unzFile unz = unzOpen(filename); - unz_global_info global; + PHYSFS_uint32 sig; + int retval; + void *in; - if (unz != NULL) + in = __PHYSFS_platformOpenRead(filename); + BAIL_IF_MACRO(in == NULL, NULL, 0); + + /* + * The first thing in a zip file might be the signature of the + * first local file record, so it makes for a quick determination. + */ + BAIL_IF_MACRO(!readui32(in, &sig), NULL, 0); + retval = (sig == ZIP_LOCAL_FILE_SIG); + if (!retval) { - if (unzGetGlobalInfo(unz, &global) == UNZ_OK) - retval = 1; - unzClose(unz); + /* + * No sig...might be a ZIP with data at the start + * (a self-extracting executable, etc), so we'll have to do + * it the hard way... + */ + retval = (find_end_of_central_dir(in, NULL) == -1); } /* if */ + __PHYSFS_platformClose(in); return(retval); } /* ZIP_isArchive */ -static void freeEntries(ZIPinfo *info, int count, const char *errmsg) +static int zip_set_open_position(void *in, ZIPentry *entry) +{ + /* + * We fix up the offset to point to the actual data on the + * first open, since we don't want to seek across the whole file on + * archive open (can be SLOW on large, CD-stored files), but we + * need to check the local file header...not just for corruption, + * but since it stores offset info the central directory does not. + */ + if (!entry->fixed_up) + { + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_uint16 fnamelen; + PHYSFS_uint16 extralen; + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits. */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); /* date/time */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != entry->crc, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != entry->compressed_size, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != entry->uncompressed_size, ERR_CORRUPTED, 0); + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); + + entry->offset += fnamelen + extralen + 30; + entry->fixed_up = 1; + } /* if */ + + return(__PHYSFS_platformSeek(in, entry->offset)); +} /* zip_set_open_position */ + + + +static void freeEntries(ZIPinfo *info, int count) { int i; @@ -235,9 +540,6 @@ static void freeEntries(ZIPinfo *info, int count, const char *errmsg) } /* for */ free(info->entries); - - if (errmsg != NULL) - __PHYSFS_setError(errmsg); } /* freeEntries */ @@ -247,33 +549,67 @@ static void freeEntries(ZIPinfo *info, int count, const char *errmsg) * !!! FIXME: "." and ".." entries. These need to be parsed out. * !!! FIXME: For now, though, we're just copying the relative path. Oh well. */ -static char *expand_symlink_path(const char *path, ZIPentry *entry) +static char *expand_symlink_path(char *path, ZIPentry *entry) { +/* char *retval = (char *) malloc(strlen(path) + 1); BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); strcpy(retval, path); return(retval); +*/ + return(path); /* !!! FIXME */ } /* expand_symlink_path */ -static char *ZIP_realpath(unzFile fh, unz_file_info *info, ZIPentry *entry) +static char *get_zip_realpath(void *in, ZIPentry *entry) { - char path[MAXZIPENTRYSIZE]; - int size = info->uncompressed_size; - int rc; + char *path; + PHYSFS_uint32 size = entry->uncompressed_size; + int rc = 0; + + BAIL_IF_MACRO(!zip_set_open_position(in, entry), NULL, NULL); + path = (char *) malloc(size + 1); + BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, NULL); + + if (entry->compression_method == COMPMETH_NONE) + rc = (__PHYSFS_platformRead(in, path, size, 1) == 1); + + else /* symlink target path is compressed... */ + { + z_stream stream; + PHYSFS_uint32 compsize = entry->uncompressed_size; + PHYSFS_uint8 *compressed = (PHYSFS_uint8 *) malloc(compsize); + if (compressed != NULL) + { + if (__PHYSFS_platformRead(in, compressed, compsize, 1) == 1) + { + memset(&stream, '\0', sizeof (z_stream)); + stream.next_in = compressed; + stream.avail_in = compsize; + stream.next_out = path; + stream.avail_out = size; + if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK) + { + rc = (zlib_err(inflate(&stream, Z_FINISH)) == Z_OK); + inflateEnd(&stream); + } /* if */ + } /* if */ + free(compressed); + } /* if */ + } /* else */ - BAIL_IF_MACRO(size >= sizeof (path), ERR_IO_ERROR, NULL); - BAIL_IF_MACRO(unzOpenCurrentFile(fh) != UNZ_OK, ERR_IO_ERROR, NULL); - rc = unzReadCurrentFile(fh, path, size); - unzCloseCurrentFile(fh); - BAIL_IF_MACRO(rc != size, ERR_IO_ERROR, NULL); - path[size] = '\0'; /* null terminate it. */ + if (!rc) + { + free(path); + return(NULL); + } /* if */ + path[entry->uncompressed_size] = '\0'; /* null-terminate it. */ - return(expand_symlink_path(path, entry)); /* retval is malloc()'d. */ -} /* ZIP_realpath */ + return(expand_symlink_path(path, entry)); /* retval is realloc()'d path. */ +} /* get_zip_realpath */ -static int version_does_symlinks(uLong version) +static int version_does_symlinks(PHYSFS_uint32 version) { int retval = 0; PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF); @@ -295,116 +631,271 @@ static int version_does_symlinks(uLong version) } /* version_does_symlinks */ -static int entry_is_symlink(unz_file_info *info) +static int entry_is_symlink(ZIPentry *entry, PHYSFS_uint32 extern_attr) { return ( - (version_does_symlinks(info->version)) && - (info->uncompressed_size > 0) && - (info->external_fa & 0x0120000) /* symlink flag. */ + (version_does_symlinks(entry->version)) && + (entry->uncompressed_size > 0) && + (extern_attr & 0x00120000) /* symlink flag. */ ); } /* entry_is_symlink */ -static int loadZipEntries(ZIPinfo *info, unzFile unz) +PHYSFS_sint64 dos_time_to_physfs_time(PHYSFS_uint32 dostime) { - int i, max; + PHYSFS_uint32 dosdate = (PHYSFS_uint32) (dostime >> 16); + struct tm unixtime; + memset(&unixtime, '\0', sizeof (unixtime)); - BAIL_IF_MACRO(unzGetGlobalInfo(unz, &(info->global)) != UNZ_OK, - ERR_IO_ERROR, 0); - BAIL_IF_MACRO(unzGoToFirstFile(unz) != UNZ_OK, ERR_IO_ERROR, 0); + unixtime.tm_mday = (PHYSFS_uint32) (dosdate & 0x1F); + unixtime.tm_mon = (PHYSFS_uint32) ((((dosdate) & 0x1E0) / 0x20) - 1); + unixtime.tm_year = (PHYSFS_uint32) (((dosdate & 0x0FE00) / 0x0200) + 80); - max = info->global.number_entry; - info->entries = (ZIPentry *) malloc(sizeof (ZIPentry) * max); - BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0); + unixtime.tm_hour = (PHYSFS_uint32) ((dostime & 0xF800) / 0x800); + unixtime.tm_min = (PHYSFS_uint32) ((dostime & 0x7E0) / 0x20); + unixtime.tm_sec = (PHYSFS_uint32) (2 * (dostime & 0x1F)); - for (i = 0; i < max; i++) + return((PHYSFS_sint64) mktime(&unixtime)); +} /* dos_time_to_physfs_time */ + + +static int load_zip_entry(void *in, ZIPentry *entry, PHYSFS_uint32 ofs_fixup) +{ + PHYSFS_uint16 fnamelen, extralen, commentlen; + PHYSFS_uint32 external_attr; + PHYSFS_uint16 ui16; + PHYSFS_uint32 ui32; + PHYSFS_sint64 si64; + + /* sanity check with central directory signature... */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0); + + /* Get the pertinent parts of the record... */ + BAIL_IF_MACRO(!readui16(in, &entry->version), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &entry->version_needed), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &entry->flag), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &entry->compression_method), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + entry->last_mod_time = dos_time_to_physfs_time(ui32); + BAIL_IF_MACRO(!readui32(in, &entry->crc), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->compressed_size), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->uncompressed_size), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &commentlen), NULL, 0); + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* disk number start */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* internal file attribs */ + BAIL_IF_MACRO(!readui32(in, &external_attr), NULL, 0); + BAIL_IF_MACRO(!readui32(in, &entry->offset), NULL, 0); + entry->offset += ofs_fixup; + entry->fixed_up = 0; /* we need to do a second fixup at openRead(). */ + + entry->name = (char *) malloc(fnamelen + 1); + BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0); + if (__PHYSFS_platformRead(in, entry->name, fnamelen, 1) != 1) + goto load_zip_entry_puked; + + entry->name[fnamelen] = '\0'; /* null-terminate the filename. */ + + si64 = __PHYSFS_platformTell(in); + if (si64 == -1) + goto load_zip_entry_puked; + + /* If this is a symlink, resolve it. */ + if (!entry_is_symlink(entry, external_attr)) + entry->symlink = NULL; + else { - unz_file_info *d = &((info->entries[i]).info); - if (unzGetCurrentFileInfo(unz, d, NULL, 0, NULL, 0, NULL, 0) != UNZ_OK) - { - freeEntries(info, i, ERR_IO_ERROR); - return(0); - } /* if */ + entry->symlink = get_zip_realpath(in, entry); + if (entry->symlink == NULL) + goto load_zip_entry_puked; + } /* else */ - (info->entries[i]).name = (char *) malloc(d->size_filename + 1); - if ((info->entries[i]).name == NULL) - { - freeEntries(info, i, ERR_OUT_OF_MEMORY); - return(0); - } /* if */ + /* seek to the start of the next entry in the central directory... */ + if (!__PHYSFS_platformSeek(in, si64 + extralen + commentlen)) + goto load_zip_entry_puked; - info->entries[i].symlink = NULL; + return(1); /* success. */ - if (unzGetCurrentFileInfo(unz, NULL, (info->entries[i]).name, - d->size_filename + 1, NULL, 0, - NULL, 0) != UNZ_OK) - { - freeEntries(info, i + 1, ERR_IO_ERROR); - return(0); - } /* if */ +load_zip_entry_puked: + free(entry->name); + return(0); /* failure. */ +} /* load_zip_entry */ - if (entry_is_symlink(d)) - { - info->entries[i].symlink = ZIP_realpath(unz, d, &info->entries[i]); - if (info->entries[i].symlink == NULL) - { - freeEntries(info, i + 1, NULL); - return(0); - } /* if */ - } /* if */ - if ((unzGoToNextFile(unz) != UNZ_OK) && (i + 1 < max)) +static int load_zip_entries(void *in, DirHandle *dirh, + PHYSFS_uint32 data_ofs, PHYSFS_uint32 central_ofs) +{ + ZIPinfo *info = (ZIPinfo *) dirh->opaque; + PHYSFS_uint32 max = info->entryCount; + PHYSFS_uint32 i; + + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, central_ofs), NULL, 0); + + info->entries = (ZIPentry *) malloc(sizeof (ZIPentry) * max); + BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0); + + for (i = 0; i < max; i++) + { + if (!load_zip_entry(in, &info->entries[i], data_ofs)) { - freeEntries(info, i + 1, ERR_IO_ERROR); + freeEntries(info, i); return(0); } /* if */ } /* for */ return(1); -} /* loadZipEntries */ +} /* load_zip_entries */ -static DirHandle *ZIP_openArchive(const char *name, int forWriting) +static int parse_end_of_central_dir(void *in, DirHandle *dirh, + PHYSFS_uint32 *data_start, + PHYSFS_uint32 *central_dir_ofs) { - unzFile unz = NULL; - DirHandle *retval = NULL; + ZIPinfo *zipinfo = (ZIPinfo *) dirh->opaque; + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_sint64 len; + PHYSFS_sint64 pos; - BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL); + /* find the end-of-central-dir record, and seek to it. */ + pos = find_end_of_central_dir(in, &len); + BAIL_IF_MACRO(pos == -1, NULL, 0); + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, pos), NULL, 0); + + /* check signature again, just in case. */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, ERR_NOT_AN_ARCHIVE, 0); + + /* number of this disk */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* number of the disk with the start of the central directory */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0); - retval = malloc(sizeof (DirHandle)); + /* total number of entries in the central dir on this disk */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + + /* total number of entries in the central dir */ + BAIL_IF_MACRO(!readui16(in, &zipinfo->entryCount), NULL, 0); + BAIL_IF_MACRO(ui16 != zipinfo->entryCount, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* size of the central directory */ + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); + + /* offset of central directory */ + BAIL_IF_MACRO(!readui32(in, central_dir_ofs), NULL, 0); + BAIL_IF_MACRO(pos < *central_dir_ofs + ui32, ERR_UNSUPPORTED_ARCHIVE, 0); + + /* + * 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 central directory offset is + * from where it is supposed to be (start of end-of-central-dir minus + * sizeof central dir)...the difference in bytes is how much arbitrary + * data is at the start of the physical file. + */ + *data_start = pos - (*central_dir_ofs + ui32); + + /* Now that we know the difference, fix up the central dir offset... */ + *central_dir_ofs += *data_start; + + /* zipfile comment length */ + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); + + /* + * Make sure that the comment length matches to the end of file... + * If it doesn't, we're either in the wrong part of the file, or the + * file is corrupted, but we give up either way. + */ + BAIL_IF_MACRO((pos + 22 + ui16) != len, ERR_UNSUPPORTED_ARCHIVE, 0); + + return(1); /* made it. */ +} /* parse_end_of_central_dir */ + + +static DirHandle *allocate_zip_dirhandle(const char *name) +{ + char *ptr; + DirHandle *retval = malloc(sizeof (DirHandle)); BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); - unz = unzOpen(name); - if (unz == NULL) - { - free(retval); - BAIL_IF_MACRO(1, ERR_UNSUPPORTED_ARCHIVE, NULL); - } /* if */ + memset(retval, '\0', sizeof (DirHandle)); retval->opaque = malloc(sizeof (ZIPinfo)); if (retval->opaque == NULL) { free(retval); - unzClose(unz); - BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); } /* if */ - ((ZIPinfo *) (retval->opaque))->archiveName = malloc(strlen(name) + 1); - if ( (((ZIPinfo *) (retval->opaque))->archiveName == NULL) || - (!loadZipEntries( (ZIPinfo *) (retval->opaque), unz)) ) + memset(retval->opaque, '\0', sizeof (ZIPinfo)); + + ptr = (char *) malloc(strlen(name) + 1); + if (ptr == NULL) { - if (((ZIPinfo *) (retval->opaque))->archiveName != NULL) - free(((ZIPinfo *) (retval->opaque))->archiveName); free(retval->opaque); free(retval); - unzClose(unz); - BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); } /* if */ - unzClose(unz); + ((ZIPinfo *) (retval->opaque))->archiveName = ptr; strcpy(((ZIPinfo *) (retval->opaque))->archiveName, name); + retval->funcs = &__PHYSFS_DirFunctions_ZIP; + return(retval); +} /* allocate_zip_dirhandle */ + + +static DirHandle *ZIP_openArchive(const char *name, int forWriting) +{ + DirHandle *retval = NULL; + void *in = NULL; + PHYSFS_uint32 data_start; + PHYSFS_uint32 central_dir_ofs; + int success = 0; + + BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL); + + if ((in = __PHYSFS_platformOpenRead(name)) == NULL) + goto zip_openarchive_end; + + if ((retval = allocate_zip_dirhandle(name)) == NULL) + goto zip_openarchive_end; + + if (!parse_end_of_central_dir(in, retval, &data_start, ¢ral_dir_ofs)) + goto zip_openarchive_end; + + if (!load_zip_entries(in, retval, data_start, central_dir_ofs)) + goto zip_openarchive_end; + + success = 1; /* ...and we're good to go. :) */ + +zip_openarchive_end: + if (!success) /* clean up for failures. */ + { + if (retval != NULL) + { + if (retval->opaque != NULL) + { + if (((ZIPinfo *) (retval->opaque))->archiveName != NULL) + free(((ZIPinfo *) (retval->opaque))->archiveName); + + free(retval->opaque); + } /* if */ + + free(retval); + retval = NULL; + } /* if */ + } /* if */ + + if (in != NULL) + __PHYSFS_platformClose(in); /* Close this even with success. */ + return(retval); } /* ZIP_openArchive */ @@ -428,13 +919,13 @@ static LinkedStringList *ZIP_enumerateFiles(DirHandle *h, d = malloc(dlen + 1); BAIL_IF_MACRO(d == NULL, ERR_OUT_OF_MEMORY, NULL); strcpy(d, dirname); - if ((dlen > 0) && (d[dlen - 1] == '/')) /* no trailing slash. */ + if ((dlen > 0) && (d[dlen - 1] == '/')) /* remove trailing slash. */ { dlen--; d[dlen] = '\0'; } /* if */ - for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++) + for (i = 0, entry = zi->entries; i < zi->entryCount; i++, entry++) { char *ptr; char *add_file; @@ -444,7 +935,7 @@ static LinkedStringList *ZIP_enumerateFiles(DirHandle *h, continue; this_dlen = strlen(entry->name); - if (this_dlen + 1 > MAXZIPENTRYSIZE) + if (this_dlen + 1 > MAXZIPENTRYSIZE) /* !!! FIXME */ continue; /* ugh. */ strcpy(buf, entry->name); @@ -535,7 +1026,7 @@ static int ZIP_exists_symcheck(DirHandle *h, const char *name, int follow) d[dlen] = '\0'; } /* if */ - for (i = 0, entry = zi->entries; i < zi->global.number_entry; i++, entry++) + for (i = 0, entry = zi->entries; i < zi->entryCount; i++, entry++) { int this_dlen = strlen(entry->name); if (this_dlen + 1 > MAXZIPENTRYSIZE) @@ -569,14 +1060,13 @@ static int ZIP_exists_symcheck(DirHandle *h, const char *name, int follow) static int ZIP_exists(DirHandle *h, const char *name) { - int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT); + int pos = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT); int is_sym; - if (retval == -1) - return(0); + BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, 0); /* if it's a symlink, then we ran into a possible symlink loop. */ - is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL ); + is_sym = ( ((ZIPinfo *)(h->opaque))->entries[pos].symlink != NULL ); BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0); return(1); @@ -588,24 +1078,15 @@ static PHYSFS_sint64 ZIP_getLastModTime(DirHandle *h, const char *name) ZIPinfo *zi = (ZIPinfo *) (h->opaque); int pos = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT); ZIPentry *entry; - struct tm t; - if (pos == -1) - return(0); + BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, 0); entry = &zi->entries[pos]; /* if it's a symlink, then we ran into a possible symlink loop. */ BAIL_IF_MACRO(entry->symlink != NULL, ERR_TOO_MANY_SYMLINKS, 0); - memset(&t, '\0', sizeof (t)); - t.tm_year = entry->info.tmu_date.tm_year - 1900; - t.tm_mon = entry->info.tmu_date.tm_mon; - t.tm_mday = entry->info.tmu_date.tm_mday; - t.tm_hour = entry->info.tmu_date.tm_hour; - t.tm_min = entry->info.tmu_date.tm_min; - t.tm_sec = entry->info.tmu_date.tm_sec; - return((PHYSFS_sint64) mktime(&t)); + return(entry->last_mod_time); } /* ZIP_getLastModTime */ @@ -613,30 +1094,26 @@ static int ZIP_isDirectory(DirHandle *h, const char *name) { int dlen; int is_sym; - int retval = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT); + int pos = ZIP_exists_symcheck(h, name, SYMLINK_RECURSE_COUNT); - if (retval == -1) - return(0); + BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, 0); /* if it's a symlink, then we ran into a possible symlink loop. */ - is_sym = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL ); + is_sym = ( ((ZIPinfo *)(h->opaque))->entries[pos].symlink != NULL ); BAIL_IF_MACRO(is_sym, ERR_TOO_MANY_SYMLINKS, 0); dlen = strlen(name); + /* !!! FIXME: yikes. Better way to check? */ - retval = (((ZIPinfo *)(h->opaque))->entries[retval].name[dlen] == '/'); - return(retval); + return((((ZIPinfo *)(h->opaque))->entries[pos].name[dlen] == '/')); } /* ZIP_isDirectory */ static int ZIP_isSymLink(DirHandle *h, const char *name) { - int retval = ZIP_exists_symcheck(h, name, 0); - if (retval == -1) - return(0); - - retval = ( ((ZIPinfo *)(h->opaque))->entries[retval].symlink != NULL ); - return(retval); + int pos = ZIP_exists_symcheck(h, name, 0); + BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, 0); + return( ((ZIPinfo *)(h->opaque))->entries[pos].symlink != NULL ); } /* ZIP_isSymLink */ @@ -646,48 +1123,53 @@ static FileHandle *ZIP_openRead(DirHandle *h, const char *filename) ZIPinfo *zi = ((ZIPinfo *) (h->opaque)); ZIPfileinfo *finfo = NULL; int pos = ZIP_exists_symcheck(h, filename, SYMLINK_RECURSE_COUNT); - unzFile f; + void *in; BAIL_IF_MACRO(pos == -1, ERR_NO_SUCH_FILE, NULL); - f = unzOpen(zi->archiveName); - BAIL_IF_MACRO(f == NULL, ERR_IO_ERROR, NULL); - - if (unzGoToFirstFile(f) != UNZ_OK) - { - unzClose(f); - BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL); - } /* if */ - - for (; pos > 0; pos--) - { - if (unzGoToNextFile(f) != UNZ_OK) - { - unzClose(f); - BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL); - } /* if */ - } /* for */ + /* if it's a symlink, then we ran into a possible symlink loop. */ + BAIL_IF_MACRO(zi->entries[pos].symlink != NULL, ERR_TOO_MANY_SYMLINKS, 0); - if ( (unzOpenCurrentFile(f) != UNZ_OK) || - ( (finfo = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo))) == NULL ) ) + in = __PHYSFS_platformOpenRead(zi->archiveName); + BAIL_IF_MACRO(in == NULL, ERR_IO_ERROR, NULL); + if (!zip_set_open_position(in, &zi->entries[pos])) { - unzClose(f); - BAIL_IF_MACRO(1, ERR_IO_ERROR, NULL); + __PHYSFS_platformClose(in); + return(0); } /* if */ - if ( (!(retval = (FileHandle *) malloc(sizeof (FileHandle)))) || - (!(retval->opaque = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo)))) ) + if ( ((retval = (FileHandle *) malloc(sizeof (FileHandle))) == NULL) || + ((finfo = (ZIPfileinfo *) malloc(sizeof (ZIPfileinfo))) == NULL) ) { if (retval) free(retval); - unzClose(f); + __PHYSFS_platformClose(in); BAIL_IF_MACRO(1, ERR_OUT_OF_MEMORY, NULL); } /* if */ - finfo->handle = f; retval->opaque = (void *) finfo; retval->funcs = &__PHYSFS_FileFunctions_ZIP; retval->dirHandle = h; + + memset(finfo, '\0', sizeof (ZIPfileinfo)); + finfo->handle = in; + finfo->entry = &zi->entries[pos]; + if (finfo->entry->compression_method != COMPMETH_NONE) + { + if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK) + { + ZIP_fileClose(retval); + return(NULL); + } /* if */ + + finfo->buffer = (PHYSFS_uint8 *) malloc(ZIP_READBUFSIZE); + if (finfo->buffer == NULL) + { + ZIP_fileClose(retval); + BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); + } /* if */ + } /* if */ + return(retval); } /* ZIP_openRead */ @@ -695,7 +1177,7 @@ static FileHandle *ZIP_openRead(DirHandle *h, const char *filename) static void ZIP_dirClose(DirHandle *h) { ZIPinfo *zi = (ZIPinfo *) (h->opaque); - freeEntries(zi, zi->global.number_entry, NULL); + freeEntries(zi, zi->entryCount); free(zi->archiveName); free(zi); free(h); diff --git a/physfs_internal.h b/physfs_internal.h index 7a542608..7b741685 100644 --- a/physfs_internal.h +++ b/physfs_internal.h @@ -274,6 +274,8 @@ typedef struct __PHYSFS_DIRFUNCTIONS__ #define ERR_FILE_EXISTS "File already exists" #define ERR_NOT_A_DIR "Not a directory" #define ERR_FILE_NOT_FOUND "File not found" +#define ERR_NOT_AN_ARCHIVE "Not an archive" +#define ERR_CORRUPTED "Corrupted archive" /*