?? zip.c
字號:
/* * ZIP support routines for PhysicsFS. * * Please see the file LICENSE in the source's root directory. * * This file written by Ryan C. Gordon, with some peeking at "unzip.c" * by Gilles Vollant. */#if HAVE_CONFIG_H# include <config.h>#endif#if (defined PHYSFS_SUPPORTS_ZIP)#include <stdio.h>#include <stdlib.h>#include <string.h>#ifndef _WIN32_WCE#include <errno.h>#include <time.h>#endif#include "physfs.h"#include "zlib.h"#define __PHYSICSFS_INTERNAL__#include "physfs_internal.h"/* * 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)/* * Entries are "unresolved" until they are first opened. At that time, * local file headers parsed/validated, data offsets will be updated to look * at the actual file data instead of the header, and symlinks will be * followed and optimized. This means that we don't seek and read around the * archive until forced to do so, and after the first time, we had to do * less reading and parsing, which is very CD-ROM friendly. */typedef enum{ ZIP_UNRESOLVED_FILE, ZIP_UNRESOLVED_SYMLINK, ZIP_RESOLVING, ZIP_RESOLVED, ZIP_BROKEN_FILE, ZIP_BROKEN_SYMLINK} ZipResolveType;/* * One ZIPentry is kept for each file in an open ZIP archive. */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_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_sint64 last_mod_time; /* last file mod time */} ZIPentry;/* * One ZIPinfo is kept for each open ZIP archive. */typedef struct{ 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{ 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 UNIX_FILETYPE_MASK 0170000#define UNIX_FILETYPE_SYMLINK 0120000static PHYSFS_sint64 ZIP_read(FileHandle *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);static PHYSFS_sint64 ZIP_write(FileHandle *handle, const void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);static int ZIP_eof(FileHandle *handle);static PHYSFS_sint64 ZIP_tell(FileHandle *handle);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 DirHandle *ZIP_openArchive(const char *name, int forWriting);static LinkedStringList *ZIP_enumerateFiles(DirHandle *h, const char *dirname, int omitSymLinks);static int ZIP_exists(DirHandle *h, const char *name);static int ZIP_isDirectory(DirHandle *h, const char *name, int *fileExists);static int ZIP_isSymLink(DirHandle *h, const char *name, int *fileExists);static PHYSFS_sint64 ZIP_getLastModTime(DirHandle *h, const char *n, int *e);static FileHandle *ZIP_openRead(DirHandle *h, const char *filename, int *e);static FileHandle *ZIP_openWrite(DirHandle *h, const char *filename);static FileHandle *ZIP_openAppend(DirHandle *h, const char *filename);static void ZIP_dirClose(DirHandle *h);static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry);static int ZIP_remove(DirHandle *h, const char *name);static int ZIP_mkdir(DirHandle *h, const char *name);const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP ={ "ZIP", ZIP_ARCHIVE_DESCRIPTION, "Ryan C. Gordon <icculus@clutteredmind.org>", "http://icculus.org/physfs/",};static const FileFunctions __PHYSFS_FileFunctions_ZIP ={ ZIP_read, /* read() method */ ZIP_write, /* write() method */ ZIP_eof, /* eof() method */ ZIP_tell, /* tell() method */ ZIP_seek, /* seek() method */ ZIP_fileLength, /* fileLength() method */ ZIP_fileClose /* fileClose() method */};const DirFunctions __PHYSFS_DirFunctions_ZIP ={ &__PHYSFS_ArchiveInfo_ZIP, ZIP_isArchive, /* isArchive() method */ ZIP_openArchive, /* openArchive() method */ ZIP_enumerateFiles, /* enumerateFiles() method */ ZIP_exists, /* exists() method */ ZIP_isDirectory, /* isDirectory() method */ ZIP_isSymLink, /* isSymLink() method */ ZIP_getLastModTime, /* getLastModTime() method */ ZIP_openRead, /* openRead() method */ ZIP_openWrite, /* openWrite() method */ ZIP_openAppend, /* openAppend() method */ ZIP_remove, /* remove() method */ ZIP_mkdir, /* mkdir() method */ ZIP_dirClose /* dirClose() method */};static const char *zlib_error_string(int rc){ switch (rc) { case Z_OK: return(NULL); /* not an error. */ case Z_STREAM_END: return(NULL); /* not an error. */#ifndef _WIN32_WCE case Z_ERRNO: return(strerror(errno));#endif case Z_NEED_DICT: return(ERR_ZLIB_NEED_DICT); case Z_DATA_ERROR: return(ERR_ZLIB_DATA_ERROR); case Z_MEM_ERROR: return(ERR_ZLIB_MEMORY_ERROR); case Z_BUF_ERROR: return(ERR_ZLIB_BUFFER_ERROR); case Z_VERSION_ERROR: return(ERR_ZLIB_VERSION_ERROR); default: return(ERR_ZLIB_UNKNOWN_ERROR); } /* switch */ return(NULL);} /* zlib_error_string *//* * Wrap all zlib calls in this, so the physfs error state is set appropriately. */static int zlib_err(int rc){ const char *str = zlib_error_string(rc); if (str != NULL) __PHYSFS_setError(str); 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){ 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 = (PHYSFS_uint32) (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, (PHYSFS_uint32) br); if (br <= 0) break; finfo->compressed_position += (PHYSFS_uint32) br; finfo->stream.next_in = finfo->buffer; finfo->stream.avail_in = (PHYSFS_uint32) br; } /* if */ } /* if */ rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH)); retval += (finfo->stream.total_out - before); if (rc != Z_OK) break; } /* while */ retval /= objSize; } /* else */ if (retval > 0) finfo->uncompressed_position += (PHYSFS_uint32) (retval * objSize); return(retval);} /* ZIP_read */static PHYSFS_sint64 ZIP_write(FileHandle *handle, const void *buf, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount){ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);} /* ZIP_write */static int ZIP_eof(FileHandle *handle){ ZIPfileinfo *finfo = ((ZIPfileinfo *) (handle->opaque)); return(finfo->uncompressed_position >= finfo->entry->uncompressed_size);} /* ZIP_eof */static PHYSFS_sint64 ZIP_tell(FileHandle *handle){ return(((ZIPfileinfo *) (handle->opaque))->uncompressed_position);} /* ZIP_tell */static int ZIP_seek(FileHandle *handle, PHYSFS_uint64 offset){ ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque); ZIPentry *entry = finfo->entry; void *in = finfo->handle; BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0); if (entry->compression_method == COMPMETH_NONE) { PHYSFS_sint64 newpos = offset + entry->offset; BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, newpos), NULL, 0); finfo->uncompressed_position = (PHYSFS_uint32) offset; } /* if */ else { /* * 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 * decode, 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); if (!__PHYSFS_platformSeek(in, entry->offset)) 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; maxread = (PHYSFS_uint32) (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); return(finfo->entry->uncompressed_size);} /* ZIP_fileLength */static int ZIP_fileClose(FileHandle *handle){ ZIPfileinfo *finfo = (ZIPfileinfo *) (handle->opaque); BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); 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 zip_find_end_of_central_dir(void *in, PHYSFS_sint64 *len){ PHYSFS_uint8 buf[256]; PHYSFS_sint32 i = 0; PHYSFS_sint64 filelen; PHYSFS_sint64 filepos; PHYSFS_sint32 maxread; PHYSFS_sint32 totalread = 0; int found = 0; PHYSFS_uint32 extra = 0; filelen = __PHYSFS_platformFileLength(in); BAIL_IF_MACRO(filelen == -1, NULL, 0); BAIL_IF_MACRO(filelen > 0xFFFFFFFF, "ZIP bigger than 2 gigs?!", 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 a little more than 64k at most, * and call it a corrupted zipfile. */ if (sizeof (buf) < filelen) { filepos = filelen - sizeof (buf); maxread = sizeof (buf); } /* if */ else { filepos = 0; maxread = (PHYSFS_uint32) filelen; } /* else */ while ((totalread < filelen) && (totalread < 65557)) { BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, filepos), NULL, -1); /* make sure we catch a signature between buffers. */ if (totalread != 0) { if (__PHYSFS_platformRead(in, buf, maxread - 4, 1) != 1) return(-1); *((PHYSFS_uint32 *) (&buf[maxread - 4])) = extra; totalread += maxread - 4; } /* if */ else { if (__PHYSFS_platformRead(in, buf, maxread, 1) != 1) return(-1); totalread += maxread; } /* else */ extra = *((PHYSFS_uint32 *) (&buf[0])); for (i = maxread - 4; i > 0; i--) { if ((buf[i + 0] == 0x50) && (buf[i + 1] == 0x4B) && (buf[i + 2] == 0x05) && (buf[i + 3] == 0x06) ) { found = 1; /* that's the signature! */ break; } /* if */ } /* for */ if (found)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -