archiver_zip.c 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716
  1. /*
  2. * ZIP support routines for PhysicsFS.
  3. *
  4. * Please see the file LICENSE.txt in the source's root directory.
  5. *
  6. * This file written by Ryan C. Gordon, with some peeking at "unzip.c"
  7. * by Gilles Vollant.
  8. */
  9. #define __PHYSICSFS_INTERNAL__
  10. #include "physfs_internal.h"
  11. #if PHYSFS_SUPPORTS_ZIP
  12. #include <errno.h>
  13. #include <time.h>
  14. #include "physfs_miniz.h"
  15. /*
  16. * A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened,
  17. * and is freed when you close the file; compressed data is read into
  18. * this buffer, and then is decompressed into the buffer passed to
  19. * PHYSFS_read().
  20. *
  21. * Uncompressed entries in a zipfile do not allocate this buffer; they just
  22. * read data directly into the buffer passed to PHYSFS_read().
  23. *
  24. * Depending on your speed and memory requirements, you should tweak this
  25. * value.
  26. */
  27. #define ZIP_READBUFSIZE (16 * 1024)
  28. /*
  29. * Entries are "unresolved" until they are first opened. At that time,
  30. * local file headers parsed/validated, data offsets will be updated to look
  31. * at the actual file data instead of the header, and symlinks will be
  32. * followed and optimized. This means that we don't seek and read around the
  33. * archive until forced to do so, and after the first time, we had to do
  34. * less reading and parsing, which is very CD-ROM friendly.
  35. */
  36. typedef enum
  37. {
  38. ZIP_UNRESOLVED_FILE,
  39. ZIP_UNRESOLVED_SYMLINK,
  40. ZIP_RESOLVING,
  41. ZIP_RESOLVED,
  42. ZIP_BROKEN_FILE,
  43. ZIP_BROKEN_SYMLINK
  44. } ZipResolveType;
  45. /*
  46. * One ZIPentry is kept for each file in an open ZIP archive.
  47. */
  48. typedef struct _ZIPentry
  49. {
  50. char *name; /* Name of file in archive */
  51. struct _ZIPentry *symlink; /* NULL or file we symlink to */
  52. ZipResolveType resolved; /* Have we resolved file/symlink? */
  53. PHYSFS_uint64 offset; /* offset of data in archive */
  54. PHYSFS_uint16 version; /* version made by */
  55. PHYSFS_uint16 version_needed; /* version needed to extract */
  56. PHYSFS_uint16 compression_method; /* compression method */
  57. PHYSFS_uint32 crc; /* crc-32 */
  58. PHYSFS_uint64 compressed_size; /* compressed size */
  59. PHYSFS_uint64 uncompressed_size; /* uncompressed size */
  60. PHYSFS_sint64 last_mod_time; /* last file mod time */
  61. } ZIPentry;
  62. /*
  63. * One ZIPinfo is kept for each open ZIP archive.
  64. */
  65. typedef struct
  66. {
  67. PHYSFS_Io *io; /* the i/o interface for this archive. */
  68. int zip64; /* non-zero if this is a Zip64 archive. */
  69. PHYSFS_uint64 entryCount; /* Number of files in ZIP. */
  70. ZIPentry *entries; /* info on all files in ZIP. */
  71. } ZIPinfo;
  72. /*
  73. * One ZIPfileinfo is kept for each open file in a ZIP archive.
  74. */
  75. typedef struct
  76. {
  77. ZIPentry *entry; /* Info on file. */
  78. PHYSFS_Io *io; /* physical file handle. */
  79. PHYSFS_uint32 compressed_position; /* offset in compressed data. */
  80. PHYSFS_uint32 uncompressed_position; /* tell() position. */
  81. PHYSFS_uint8 *buffer; /* decompression buffer. */
  82. z_stream stream; /* zlib stream state. */
  83. } ZIPfileinfo;
  84. /* Magic numbers... */
  85. #define ZIP_LOCAL_FILE_SIG 0x04034b50
  86. #define ZIP_CENTRAL_DIR_SIG 0x02014b50
  87. #define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50
  88. #define ZIP64_END_OF_CENTRAL_DIR_SIG 0x06064b50
  89. #define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50
  90. #define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG 0x0001
  91. /* compression methods... */
  92. #define COMPMETH_NONE 0
  93. /* ...and others... */
  94. #define UNIX_FILETYPE_MASK 0170000
  95. #define UNIX_FILETYPE_SYMLINK 0120000
  96. /*
  97. * Bridge physfs allocation functions to zlib's format...
  98. */
  99. static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size)
  100. {
  101. return ((PHYSFS_Allocator *) opaque)->Malloc(items * size);
  102. } /* zlibPhysfsAlloc */
  103. /*
  104. * Bridge physfs allocation functions to zlib's format...
  105. */
  106. static void zlibPhysfsFree(voidpf opaque, voidpf address)
  107. {
  108. ((PHYSFS_Allocator *) opaque)->Free(address);
  109. } /* zlibPhysfsFree */
  110. /*
  111. * Construct a new z_stream to a sane state.
  112. */
  113. static void initializeZStream(z_stream *pstr)
  114. {
  115. memset(pstr, '\0', sizeof (z_stream));
  116. pstr->zalloc = zlibPhysfsAlloc;
  117. pstr->zfree = zlibPhysfsFree;
  118. pstr->opaque = &allocator;
  119. } /* initializeZStream */
  120. static PHYSFS_ErrorCode zlib_error_code(int rc)
  121. {
  122. switch (rc)
  123. {
  124. case Z_OK: return PHYSFS_ERR_OK; /* not an error. */
  125. case Z_STREAM_END: return PHYSFS_ERR_OK; /* not an error. */
  126. case Z_ERRNO: return PHYSFS_ERR_IO;
  127. case Z_MEM_ERROR: return PHYSFS_ERR_OUT_OF_MEMORY;
  128. default: return PHYSFS_ERR_CORRUPT;
  129. } /* switch */
  130. } /* zlib_error_string */
  131. /*
  132. * Wrap all zlib calls in this, so the physfs error state is set appropriately.
  133. */
  134. static int zlib_err(const int rc)
  135. {
  136. PHYSFS_setErrorCode(zlib_error_code(rc));
  137. return rc;
  138. } /* zlib_err */
  139. /*
  140. * Read an unsigned 64-bit int and swap to native byte order.
  141. */
  142. static int readui64(PHYSFS_Io *io, PHYSFS_uint64 *val)
  143. {
  144. PHYSFS_uint64 v;
  145. BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
  146. *val = PHYSFS_swapULE64(v);
  147. return 1;
  148. } /* readui64 */
  149. /*
  150. * Read an unsigned 32-bit int and swap to native byte order.
  151. */
  152. static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
  153. {
  154. PHYSFS_uint32 v;
  155. BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
  156. *val = PHYSFS_swapULE32(v);
  157. return 1;
  158. } /* readui32 */
  159. /*
  160. * Read an unsigned 16-bit int and swap to native byte order.
  161. */
  162. static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val)
  163. {
  164. PHYSFS_uint16 v;
  165. BAIL_IF_MACRO(!__PHYSFS_readAll(io, &v, sizeof (v)), ERRPASS, 0);
  166. *val = PHYSFS_swapULE16(v);
  167. return 1;
  168. } /* readui16 */
  169. static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len)
  170. {
  171. ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
  172. PHYSFS_Io *io = finfo->io;
  173. ZIPentry *entry = finfo->entry;
  174. PHYSFS_sint64 retval = 0;
  175. PHYSFS_sint64 maxread = (PHYSFS_sint64) len;
  176. PHYSFS_sint64 avail = entry->uncompressed_size -
  177. finfo->uncompressed_position;
  178. if (avail < maxread)
  179. maxread = avail;
  180. BAIL_IF_MACRO(maxread == 0, ERRPASS, 0); /* quick rejection. */
  181. if (entry->compression_method == COMPMETH_NONE)
  182. retval = io->read(io, buf, maxread);
  183. else
  184. {
  185. finfo->stream.next_out = buf;
  186. finfo->stream.avail_out = (uInt) maxread;
  187. while (retval < maxread)
  188. {
  189. PHYSFS_uint32 before = finfo->stream.total_out;
  190. int rc;
  191. if (finfo->stream.avail_in == 0)
  192. {
  193. PHYSFS_sint64 br;
  194. br = entry->compressed_size - finfo->compressed_position;
  195. if (br > 0)
  196. {
  197. if (br > ZIP_READBUFSIZE)
  198. br = ZIP_READBUFSIZE;
  199. br = io->read(io, finfo->buffer, (PHYSFS_uint64) br);
  200. if (br <= 0)
  201. break;
  202. finfo->compressed_position += (PHYSFS_uint32) br;
  203. finfo->stream.next_in = finfo->buffer;
  204. finfo->stream.avail_in = (PHYSFS_uint32) br;
  205. } /* if */
  206. } /* if */
  207. rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH));
  208. retval += (finfo->stream.total_out - before);
  209. if (rc != Z_OK)
  210. break;
  211. } /* while */
  212. } /* else */
  213. if (retval > 0)
  214. finfo->uncompressed_position += (PHYSFS_uint32) retval;
  215. return retval;
  216. } /* ZIP_read */
  217. static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len)
  218. {
  219. BAIL_MACRO(PHYSFS_ERR_READ_ONLY, -1);
  220. } /* ZIP_write */
  221. static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io)
  222. {
  223. return ((ZIPfileinfo *) io->opaque)->uncompressed_position;
  224. } /* ZIP_tell */
  225. static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset)
  226. {
  227. ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque;
  228. ZIPentry *entry = finfo->entry;
  229. PHYSFS_Io *io = finfo->io;
  230. BAIL_IF_MACRO(offset > entry->uncompressed_size, PHYSFS_ERR_PAST_EOF, 0);
  231. if (entry->compression_method == COMPMETH_NONE)
  232. {
  233. const PHYSFS_sint64 newpos = offset + entry->offset;
  234. BAIL_IF_MACRO(!io->seek(io, newpos), ERRPASS, 0);
  235. finfo->uncompressed_position = (PHYSFS_uint32) offset;
  236. } /* if */
  237. else
  238. {
  239. /*
  240. * If seeking backwards, we need to redecode the file
  241. * from the start and throw away the compressed bits until we hit
  242. * the offset we need. If seeking forward, we still need to
  243. * decode, but we don't rewind first.
  244. */
  245. if (offset < finfo->uncompressed_position)
  246. {
  247. /* we do a copy so state is sane if inflateInit2() fails. */
  248. z_stream str;
  249. initializeZStream(&str);
  250. if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
  251. return 0;
  252. if (!io->seek(io, entry->offset))
  253. return 0;
  254. inflateEnd(&finfo->stream);
  255. memcpy(&finfo->stream, &str, sizeof (z_stream));
  256. finfo->uncompressed_position = finfo->compressed_position = 0;
  257. } /* if */
  258. while (finfo->uncompressed_position != offset)
  259. {
  260. PHYSFS_uint8 buf[512];
  261. PHYSFS_uint32 maxread;
  262. maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position);
  263. if (maxread > sizeof (buf))
  264. maxread = sizeof (buf);
  265. if (ZIP_read(_io, buf, maxread) != maxread)
  266. return 0;
  267. } /* while */
  268. } /* else */
  269. return 1;
  270. } /* ZIP_seek */
  271. static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io)
  272. {
  273. const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
  274. return (PHYSFS_sint64) finfo->entry->uncompressed_size;
  275. } /* ZIP_length */
  276. static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry);
  277. static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io)
  278. {
  279. ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque;
  280. PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
  281. ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
  282. GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, failed);
  283. GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, failed);
  284. memset(finfo, '\0', sizeof (*finfo));
  285. finfo->entry = origfinfo->entry;
  286. finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry);
  287. GOTO_IF_MACRO(!finfo->io, ERRPASS, failed);
  288. if (finfo->entry->compression_method != COMPMETH_NONE)
  289. {
  290. finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
  291. GOTO_IF_MACRO(!finfo->buffer, PHYSFS_ERR_OUT_OF_MEMORY, failed);
  292. if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
  293. goto failed;
  294. } /* if */
  295. memcpy(retval, io, sizeof (PHYSFS_Io));
  296. retval->opaque = finfo;
  297. return retval;
  298. failed:
  299. if (finfo != NULL)
  300. {
  301. if (finfo->io != NULL)
  302. finfo->io->destroy(finfo->io);
  303. if (finfo->buffer != NULL)
  304. {
  305. allocator.Free(finfo->buffer);
  306. inflateEnd(&finfo->stream);
  307. } /* if */
  308. allocator.Free(finfo);
  309. } /* if */
  310. if (retval != NULL)
  311. allocator.Free(retval);
  312. return NULL;
  313. } /* ZIP_duplicate */
  314. static int ZIP_flush(PHYSFS_Io *io) { return 1; /* no write support. */ }
  315. static void ZIP_destroy(PHYSFS_Io *io)
  316. {
  317. ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque;
  318. finfo->io->destroy(finfo->io);
  319. if (finfo->entry->compression_method != COMPMETH_NONE)
  320. inflateEnd(&finfo->stream);
  321. if (finfo->buffer != NULL)
  322. allocator.Free(finfo->buffer);
  323. allocator.Free(finfo);
  324. allocator.Free(io);
  325. } /* ZIP_destroy */
  326. static const PHYSFS_Io ZIP_Io =
  327. {
  328. CURRENT_PHYSFS_IO_API_VERSION, NULL,
  329. ZIP_read,
  330. ZIP_write,
  331. ZIP_seek,
  332. ZIP_tell,
  333. ZIP_length,
  334. ZIP_duplicate,
  335. ZIP_flush,
  336. ZIP_destroy
  337. };
  338. static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len)
  339. {
  340. PHYSFS_uint8 buf[256];
  341. PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 };
  342. PHYSFS_sint32 i = 0;
  343. PHYSFS_sint64 filelen;
  344. PHYSFS_sint64 filepos;
  345. PHYSFS_sint32 maxread;
  346. PHYSFS_sint32 totalread = 0;
  347. int found = 0;
  348. filelen = io->length(io);
  349. BAIL_IF_MACRO(filelen == -1, ERRPASS, -1);
  350. /*
  351. * Jump to the end of the file and start reading backwards.
  352. * The last thing in the file is the zipfile comment, which is variable
  353. * length, and the field that specifies its size is before it in the
  354. * file (argh!)...this means that we need to scan backwards until we
  355. * hit the end-of-central-dir signature. We can then sanity check that
  356. * the comment was as big as it should be to make sure we're in the
  357. * right place. The comment length field is 16 bits, so we can stop
  358. * searching for that signature after a little more than 64k at most,
  359. * and call it a corrupted zipfile.
  360. */
  361. if (sizeof (buf) < filelen)
  362. {
  363. filepos = filelen - sizeof (buf);
  364. maxread = sizeof (buf);
  365. } /* if */
  366. else
  367. {
  368. filepos = 0;
  369. maxread = (PHYSFS_uint32) filelen;
  370. } /* else */
  371. while ((totalread < filelen) && (totalread < 65557))
  372. {
  373. BAIL_IF_MACRO(!io->seek(io, filepos), ERRPASS, -1);
  374. /* make sure we catch a signature between buffers. */
  375. if (totalread != 0)
  376. {
  377. if (!__PHYSFS_readAll(io, buf, maxread - 4))
  378. return -1;
  379. memcpy(&buf[maxread - 4], &extra, sizeof (extra));
  380. totalread += maxread - 4;
  381. } /* if */
  382. else
  383. {
  384. if (!__PHYSFS_readAll(io, buf, maxread))
  385. return -1;
  386. totalread += maxread;
  387. } /* else */
  388. memcpy(&extra, buf, sizeof (extra));
  389. for (i = maxread - 4; i > 0; i--)
  390. {
  391. if ((buf[i + 0] == 0x50) &&
  392. (buf[i + 1] == 0x4B) &&
  393. (buf[i + 2] == 0x05) &&
  394. (buf[i + 3] == 0x06) )
  395. {
  396. found = 1; /* that's the signature! */
  397. break;
  398. } /* if */
  399. } /* for */
  400. if (found)
  401. break;
  402. filepos -= (maxread - 4);
  403. if (filepos < 0)
  404. filepos = 0;
  405. } /* while */
  406. BAIL_IF_MACRO(!found, PHYSFS_ERR_UNSUPPORTED, -1);
  407. if (len != NULL)
  408. *len = filelen;
  409. return (filepos + i);
  410. } /* zip_find_end_of_central_dir */
  411. static int isZip(PHYSFS_Io *io)
  412. {
  413. PHYSFS_uint32 sig = 0;
  414. int retval = 0;
  415. /*
  416. * The first thing in a zip file might be the signature of the
  417. * first local file record, so it makes for a quick determination.
  418. */
  419. if (readui32(io, &sig))
  420. {
  421. retval = (sig == ZIP_LOCAL_FILE_SIG);
  422. if (!retval)
  423. {
  424. /*
  425. * No sig...might be a ZIP with data at the start
  426. * (a self-extracting executable, etc), so we'll have to do
  427. * it the hard way...
  428. */
  429. retval = (zip_find_end_of_central_dir(io, NULL) != -1);
  430. } /* if */
  431. } /* if */
  432. return retval;
  433. } /* isZip */
  434. static void zip_free_entries(ZIPentry *entries, PHYSFS_uint64 max)
  435. {
  436. PHYSFS_uint64 i;
  437. for (i = 0; i < max; i++)
  438. {
  439. ZIPentry *entry = &entries[i];
  440. if (entry->name != NULL)
  441. allocator.Free(entry->name);
  442. } /* for */
  443. allocator.Free(entries);
  444. } /* zip_free_entries */
  445. /*
  446. * This will find the ZIPentry associated with a path in platform-independent
  447. * notation. Directories don't have ZIPentries associated with them, but
  448. * (*isDir) will be set to non-zero if a dir was hit.
  449. */
  450. static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path,
  451. int *isDir)
  452. {
  453. ZIPentry *a = info->entries;
  454. PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path);
  455. PHYSFS_sint64 lo = 0;
  456. PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1);
  457. PHYSFS_sint64 middle;
  458. const char *thispath = NULL;
  459. int rc;
  460. while (lo <= hi)
  461. {
  462. middle = lo + ((hi - lo) / 2);
  463. thispath = a[middle].name;
  464. rc = strncmp(path, thispath, pathlen);
  465. if (rc > 0)
  466. lo = middle + 1;
  467. else if (rc < 0)
  468. hi = middle - 1;
  469. else /* substring match...might be dir or entry or nothing. */
  470. {
  471. if (isDir != NULL)
  472. {
  473. *isDir = (thispath[pathlen] == '/');
  474. if (*isDir)
  475. return NULL;
  476. } /* if */
  477. if (thispath[pathlen] == '\0') /* found entry? */
  478. return &a[middle];
  479. /* adjust search params, try again. */
  480. else if (thispath[pathlen] > '/')
  481. hi = middle - 1;
  482. else
  483. lo = middle + 1;
  484. } /* if */
  485. } /* while */
  486. if (isDir != NULL)
  487. *isDir = 0;
  488. BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, NULL);
  489. } /* zip_find_entry */
  490. /* Convert paths from old, buggy DOS zippers... */
  491. static void zip_convert_dos_path(ZIPentry *entry, char *path)
  492. {
  493. PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF);
  494. if (hosttype == 0) /* FS_FAT_ */
  495. {
  496. while (*path)
  497. {
  498. if (*path == '\\')
  499. *path = '/';
  500. path++;
  501. } /* while */
  502. } /* if */
  503. } /* zip_convert_dos_path */
  504. static void zip_expand_symlink_path(char *path)
  505. {
  506. char *ptr = path;
  507. char *prevptr = path;
  508. while (1)
  509. {
  510. ptr = strchr(ptr, '/');
  511. if (ptr == NULL)
  512. break;
  513. if (*(ptr + 1) == '.')
  514. {
  515. if (*(ptr + 2) == '/')
  516. {
  517. /* current dir in middle of string: ditch it. */
  518. memmove(ptr, ptr + 2, strlen(ptr + 2) + 1);
  519. } /* else if */
  520. else if (*(ptr + 2) == '\0')
  521. {
  522. /* current dir at end of string: ditch it. */
  523. *ptr = '\0';
  524. } /* else if */
  525. else if (*(ptr + 2) == '.')
  526. {
  527. if (*(ptr + 3) == '/')
  528. {
  529. /* parent dir in middle: move back one, if possible. */
  530. memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1);
  531. ptr = prevptr;
  532. while (prevptr != path)
  533. {
  534. prevptr--;
  535. if (*prevptr == '/')
  536. {
  537. prevptr++;
  538. break;
  539. } /* if */
  540. } /* while */
  541. } /* if */
  542. if (*(ptr + 3) == '\0')
  543. {
  544. /* parent dir at end: move back one, if possible. */
  545. *prevptr = '\0';
  546. } /* if */
  547. } /* if */
  548. } /* if */
  549. else
  550. {
  551. prevptr = ptr;
  552. ptr++;
  553. } /* else */
  554. } /* while */
  555. } /* zip_expand_symlink_path */
  556. /* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
  557. static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry);
  558. /*
  559. * Look for the entry named by (path). If it exists, resolve it, and return
  560. * a pointer to that entry. If it's another symlink, keep resolving until you
  561. * hit a real file and then return a pointer to the final non-symlink entry.
  562. * If there's a problem, return NULL.
  563. */
  564. static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path)
  565. {
  566. ZIPentry *entry;
  567. zip_expand_symlink_path(path);
  568. entry = zip_find_entry(info, path, NULL);
  569. if (entry != NULL)
  570. {
  571. if (!zip_resolve(io, info, entry)) /* recursive! */
  572. entry = NULL;
  573. else
  574. {
  575. if (entry->symlink != NULL)
  576. entry = entry->symlink;
  577. } /* else */
  578. } /* if */
  579. return entry;
  580. } /* zip_follow_symlink */
  581. static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
  582. {
  583. const PHYSFS_uint64 size = entry->uncompressed_size;
  584. char *path = NULL;
  585. int rc = 0;
  586. /*
  587. * We've already parsed the local file header of the symlink at this
  588. * point. Now we need to read the actual link from the file data and
  589. * follow it.
  590. */
  591. BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
  592. path = (char *) __PHYSFS_smallAlloc(size + 1);
  593. BAIL_IF_MACRO(!path, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  594. if (entry->compression_method == COMPMETH_NONE)
  595. rc = __PHYSFS_readAll(io, path, size);
  596. else /* symlink target path is compressed... */
  597. {
  598. z_stream stream;
  599. const PHYSFS_uint64 complen = entry->compressed_size;
  600. PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
  601. if (compressed != NULL)
  602. {
  603. if (__PHYSFS_readAll(io, compressed, complen))
  604. {
  605. initializeZStream(&stream);
  606. stream.next_in = compressed;
  607. stream.avail_in = complen;
  608. stream.next_out = (unsigned char *) path;
  609. stream.avail_out = size;
  610. if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
  611. {
  612. rc = zlib_err(inflate(&stream, Z_FINISH));
  613. inflateEnd(&stream);
  614. /* both are acceptable outcomes... */
  615. rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
  616. } /* if */
  617. } /* if */
  618. __PHYSFS_smallFree(compressed);
  619. } /* if */
  620. } /* else */
  621. if (rc)
  622. {
  623. path[entry->uncompressed_size] = '\0'; /* null-terminate it. */
  624. zip_convert_dos_path(entry, path);
  625. entry->symlink = zip_follow_symlink(io, info, path);
  626. } /* else */
  627. __PHYSFS_smallFree(path);
  628. return (entry->symlink != NULL);
  629. } /* zip_resolve_symlink */
  630. /*
  631. * Parse the local file header of an entry, and update entry->offset.
  632. */
  633. static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry)
  634. {
  635. PHYSFS_uint32 ui32;
  636. PHYSFS_uint16 ui16;
  637. PHYSFS_uint16 fnamelen;
  638. PHYSFS_uint16 extralen;
  639. /*
  640. * crc and (un)compressed_size are always zero if this is a "JAR"
  641. * archive created with Sun's Java tools, apparently. We only
  642. * consider this archive corrupted if those entries don't match and
  643. * aren't zero. That seems to work well.
  644. * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's
  645. * possible that's a Zip64 thing.
  646. */
  647. BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0);
  648. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  649. BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0);
  650. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  651. BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0);
  652. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* general bits. */
  653. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  654. BAIL_IF_MACRO(ui16 != entry->compression_method, PHYSFS_ERR_CORRUPT, 0);
  655. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); /* date/time */
  656. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  657. BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0);
  658. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  659. BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
  660. (ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0);
  661. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  662. BAIL_IF_MACRO(ui32 && (ui32 != 0xFFFFFFFF) &&
  663. (ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0);
  664. BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
  665. BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
  666. entry->offset += fnamelen + extralen + 30;
  667. return 1;
  668. } /* zip_parse_local */
  669. static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry)
  670. {
  671. int retval = 1;
  672. ZipResolveType resolve_type = entry->resolved;
  673. /* Don't bother if we've failed to resolve this entry before. */
  674. BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, PHYSFS_ERR_CORRUPT, 0);
  675. BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, PHYSFS_ERR_CORRUPT, 0);
  676. /* uhoh...infinite symlink loop! */
  677. BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, PHYSFS_ERR_SYMLINK_LOOP, 0);
  678. /*
  679. * We fix up the offset to point to the actual data on the
  680. * first open, since we don't want to seek across the whole file on
  681. * archive open (can be SLOW on large, CD-stored files), but we
  682. * need to check the local file header...not just for corruption,
  683. * but since it stores offset info the central directory does not.
  684. */
  685. if (resolve_type != ZIP_RESOLVED)
  686. {
  687. entry->resolved = ZIP_RESOLVING;
  688. retval = zip_parse_local(io, entry);
  689. if (retval)
  690. {
  691. /*
  692. * If it's a symlink, find the original file. This will cause
  693. * resolution of other entries (other symlinks and, eventually,
  694. * the real file) if all goes well.
  695. */
  696. if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
  697. retval = zip_resolve_symlink(io, info, entry);
  698. } /* if */
  699. if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
  700. entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK);
  701. else if (resolve_type == ZIP_UNRESOLVED_FILE)
  702. entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE);
  703. } /* if */
  704. return retval;
  705. } /* zip_resolve */
  706. static int zip_version_does_symlinks(PHYSFS_uint32 version)
  707. {
  708. int retval = 0;
  709. PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
  710. switch (hosttype)
  711. {
  712. /*
  713. * These are the platforms that can NOT build an archive with
  714. * symlinks, according to the Info-ZIP project.
  715. */
  716. case 0: /* FS_FAT_ */
  717. case 1: /* AMIGA_ */
  718. case 2: /* VMS_ */
  719. case 4: /* VM_CSM_ */
  720. case 6: /* FS_HPFS_ */
  721. case 11: /* FS_NTFS_ */
  722. case 14: /* FS_VFAT_ */
  723. case 13: /* ACORN_ */
  724. case 15: /* MVS_ */
  725. case 18: /* THEOS_ */
  726. break; /* do nothing. */
  727. default: /* assume the rest to be unix-like. */
  728. retval = 1;
  729. break;
  730. } /* switch */
  731. return retval;
  732. } /* zip_version_does_symlinks */
  733. static int zip_entry_is_symlink(const ZIPentry *entry)
  734. {
  735. return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
  736. (entry->resolved == ZIP_BROKEN_SYMLINK) ||
  737. (entry->symlink));
  738. } /* zip_entry_is_symlink */
  739. static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr)
  740. {
  741. PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
  742. return ( (zip_version_does_symlinks(entry->version)) &&
  743. (entry->uncompressed_size > 0) &&
  744. ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) );
  745. } /* zip_has_symlink_attr */
  746. static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
  747. {
  748. PHYSFS_uint32 dosdate;
  749. struct tm unixtime;
  750. memset(&unixtime, '\0', sizeof (unixtime));
  751. dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
  752. dostime &= 0xFFFF;
  753. /* dissect date */
  754. unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80;
  755. unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1;
  756. unixtime.tm_mday = ((dosdate ) & 0x1F);
  757. /* dissect time */
  758. unixtime.tm_hour = ((dostime >> 11) & 0x1F);
  759. unixtime.tm_min = ((dostime >> 5) & 0x3F);
  760. unixtime.tm_sec = ((dostime << 1) & 0x3E);
  761. /* let mktime calculate daylight savings time. */
  762. unixtime.tm_isdst = -1;
  763. return ((PHYSFS_sint64) mktime(&unixtime));
  764. } /* zip_dos_time_to_physfs_time */
  765. static int zip_load_entry(PHYSFS_Io *io, const int zip64, ZIPentry *entry,
  766. PHYSFS_uint64 ofs_fixup)
  767. {
  768. PHYSFS_uint16 fnamelen, extralen, commentlen;
  769. PHYSFS_uint32 external_attr;
  770. PHYSFS_uint32 starting_disk;
  771. PHYSFS_uint64 offset;
  772. PHYSFS_uint16 ui16;
  773. PHYSFS_uint32 ui32;
  774. PHYSFS_sint64 si64;
  775. /* sanity check with central directory signature... */
  776. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  777. BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
  778. /* Get the pertinent parts of the record... */
  779. BAIL_IF_MACRO(!readui16(io, &entry->version), ERRPASS, 0);
  780. BAIL_IF_MACRO(!readui16(io, &entry->version_needed), ERRPASS, 0);
  781. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* general bits */
  782. BAIL_IF_MACRO(!readui16(io, &entry->compression_method), ERRPASS, 0);
  783. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  784. entry->last_mod_time = zip_dos_time_to_physfs_time(ui32);
  785. BAIL_IF_MACRO(!readui32(io, &entry->crc), ERRPASS, 0);
  786. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  787. entry->compressed_size = (PHYSFS_uint64) ui32;
  788. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  789. entry->uncompressed_size = (PHYSFS_uint64) ui32;
  790. BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0);
  791. BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0);
  792. BAIL_IF_MACRO(!readui16(io, &commentlen), ERRPASS, 0);
  793. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  794. starting_disk = (PHYSFS_uint32) ui16;
  795. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* internal file attribs */
  796. BAIL_IF_MACRO(!readui32(io, &external_attr), ERRPASS, 0);
  797. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  798. offset = (PHYSFS_uint64) ui32;
  799. entry->symlink = NULL; /* will be resolved later, if necessary. */
  800. entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ?
  801. ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE;
  802. entry->name = (char *) allocator.Malloc(fnamelen + 1);
  803. BAIL_IF_MACRO(entry->name == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  804. if (!__PHYSFS_readAll(io, entry->name, fnamelen))
  805. goto zip_load_entry_puked;
  806. entry->name[fnamelen] = '\0'; /* null-terminate the filename. */
  807. zip_convert_dos_path(entry, entry->name);
  808. si64 = io->tell(io);
  809. if (si64 == -1)
  810. goto zip_load_entry_puked;
  811. /*
  812. * The actual sizes didn't fit in 32-bits; look for the Zip64
  813. * extended information extra field...
  814. */
  815. if ( (zip64) &&
  816. ((offset == 0xFFFFFFFF) ||
  817. (starting_disk == 0xFFFFFFFF) ||
  818. (entry->compressed_size == 0xFFFFFFFF) ||
  819. (entry->uncompressed_size == 0xFFFFFFFF)) )
  820. {
  821. int found = 0;
  822. PHYSFS_uint16 sig, len;
  823. while (extralen > 4)
  824. {
  825. if (!readui16(io, &sig))
  826. goto zip_load_entry_puked;
  827. else if (!readui16(io, &len))
  828. goto zip_load_entry_puked;
  829. si64 += 4 + len;
  830. extralen -= 4 + len;
  831. if (sig != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG)
  832. {
  833. if (!io->seek(io, si64))
  834. goto zip_load_entry_puked;
  835. continue;
  836. } /* if */
  837. found = 1;
  838. break;
  839. } /* while */
  840. GOTO_IF_MACRO(!found, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
  841. if (entry->uncompressed_size == 0xFFFFFFFF)
  842. {
  843. GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
  844. if (!readui64(io, &entry->uncompressed_size))
  845. goto zip_load_entry_puked;
  846. len -= 8;
  847. } /* if */
  848. if (entry->compressed_size == 0xFFFFFFFF)
  849. {
  850. GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
  851. if (!readui64(io, &entry->compressed_size))
  852. goto zip_load_entry_puked;
  853. len -= 8;
  854. } /* if */
  855. if (offset == 0xFFFFFFFF)
  856. {
  857. GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
  858. if (!readui64(io, &offset))
  859. goto zip_load_entry_puked;
  860. len -= 8;
  861. } /* if */
  862. if (starting_disk == 0xFFFFFFFF)
  863. {
  864. GOTO_IF_MACRO(len < 8, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
  865. if (!readui32(io, &starting_disk))
  866. goto zip_load_entry_puked;
  867. len -= 4;
  868. } /* if */
  869. GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
  870. } /* if */
  871. GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked);
  872. entry->offset = offset + ofs_fixup;
  873. /* seek to the start of the next entry in the central directory... */
  874. if (!io->seek(io, si64 + extralen + commentlen))
  875. goto zip_load_entry_puked;
  876. return 1; /* success. */
  877. zip_load_entry_puked:
  878. allocator.Free(entry->name);
  879. return 0; /* failure. */
  880. } /* zip_load_entry */
  881. static int zip_entry_cmp(void *_a, size_t one, size_t two)
  882. {
  883. if (one != two)
  884. {
  885. const ZIPentry *a = (const ZIPentry *) _a;
  886. return strcmp(a[one].name, a[two].name);
  887. } /* if */
  888. return 0;
  889. } /* zip_entry_cmp */
  890. static void zip_entry_swap(void *_a, size_t one, size_t two)
  891. {
  892. if (one != two)
  893. {
  894. ZIPentry tmp;
  895. ZIPentry *first = &(((ZIPentry *) _a)[one]);
  896. ZIPentry *second = &(((ZIPentry *) _a)[two]);
  897. memcpy(&tmp, first, sizeof (ZIPentry));
  898. memcpy(first, second, sizeof (ZIPentry));
  899. memcpy(second, &tmp, sizeof (ZIPentry));
  900. } /* if */
  901. } /* zip_entry_swap */
  902. static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info,
  903. const PHYSFS_uint64 data_ofs,
  904. const PHYSFS_uint64 central_ofs)
  905. {
  906. const PHYSFS_uint64 max = info->entryCount;
  907. const int zip64 = info->zip64;
  908. PHYSFS_uint64 i;
  909. BAIL_IF_MACRO(!io->seek(io, central_ofs), ERRPASS, 0);
  910. info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max);
  911. BAIL_IF_MACRO(!info->entries, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  912. for (i = 0; i < max; i++)
  913. {
  914. if (!zip_load_entry(io, zip64, &info->entries[i], data_ofs))
  915. {
  916. zip_free_entries(info->entries, i);
  917. return 0;
  918. } /* if */
  919. } /* for */
  920. __PHYSFS_sort(info->entries, (size_t) max, zip_entry_cmp, zip_entry_swap);
  921. return 1;
  922. } /* zip_load_entries */
  923. static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io,
  924. PHYSFS_sint64 _pos,
  925. PHYSFS_uint64 offset)
  926. {
  927. /*
  928. * Naturally, the offset is useless to us; it is the offset from the
  929. * start of file, which is meaningless if we've appended this .zip to
  930. * a self-extracting .exe. We need to find this on our own. It should
  931. * be directly before the locator record, but the record in question,
  932. * like the original end-of-central-directory record, ends with a
  933. * variable-length field. Unlike the original, which has to store the
  934. * size of that variable-length field in a 16-bit int and thus has to be
  935. * within 64k, the new one gets 64-bits.
  936. *
  937. * Fortunately, the only currently-specified record for that variable
  938. * length block is some weird proprietary thing that deals with EBCDIC
  939. * and tape backups or something. So we don't seek far.
  940. */
  941. PHYSFS_uint32 ui32;
  942. const PHYSFS_uint64 pos = (PHYSFS_uint64) _pos;
  943. assert(_pos > 0);
  944. /* Try offset specified in the Zip64 end of central directory locator. */
  945. /* This works if the entire PHYSFS_Io is the zip file. */
  946. BAIL_IF_MACRO(!io->seek(io, offset), ERRPASS, -1);
  947. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
  948. if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
  949. return offset;
  950. /* Try 56 bytes before the Zip64 end of central directory locator. */
  951. /* This works if the record isn't variable length and is version 1. */
  952. if (pos > 56)
  953. {
  954. BAIL_IF_MACRO(!io->seek(io, pos-56), ERRPASS, -1);
  955. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
  956. if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
  957. return pos-56;
  958. } /* if */
  959. /* Try 84 bytes before the Zip64 end of central directory locator. */
  960. /* This works if the record isn't variable length and is version 2. */
  961. if (pos > 84)
  962. {
  963. BAIL_IF_MACRO(!io->seek(io, pos-84), ERRPASS, -1);
  964. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, -1);
  965. if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG)
  966. return pos-84;
  967. } /* if */
  968. /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */
  969. /* Just try moving back at most 256k. Oh well. */
  970. if ((offset < pos) && (pos > 4))
  971. {
  972. const PHYSFS_uint64 maxbuflen = 256 * 1024;
  973. PHYSFS_uint64 len = pos - offset;
  974. PHYSFS_uint8 *buf = NULL;
  975. PHYSFS_sint32 i;
  976. if (len > maxbuflen)
  977. len = maxbuflen;
  978. buf = (PHYSFS_uint8 *) __PHYSFS_smallAlloc(len);
  979. BAIL_IF_MACRO(!buf, PHYSFS_ERR_OUT_OF_MEMORY, -1);
  980. if (!io->seek(io, pos - len) || !__PHYSFS_readAll(io, buf, len))
  981. {
  982. __PHYSFS_smallFree(buf);
  983. return -1; /* error was set elsewhere. */
  984. } /* if */
  985. for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--)
  986. {
  987. if ( (buf[i] == 0x50) && (buf[i+1] == 0x4b) &&
  988. (buf[i+2] == 0x06) && (buf[i+3] == 0x06) )
  989. {
  990. __PHYSFS_smallFree(buf);
  991. return pos - (len - i);
  992. } /* if */
  993. } /* for */
  994. __PHYSFS_smallFree(buf);
  995. } /* if */
  996. BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */
  997. } /* zip64_find_end_of_central_dir */
  998. static int zip64_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
  999. PHYSFS_uint64 *data_start,
  1000. PHYSFS_uint64 *dir_ofs,
  1001. PHYSFS_sint64 pos)
  1002. {
  1003. PHYSFS_uint64 ui64;
  1004. PHYSFS_uint32 ui32;
  1005. PHYSFS_uint16 ui16;
  1006. /* We should be positioned right past the locator signature. */
  1007. if ((pos < 0) || (!io->seek(io, pos)))
  1008. return 0;
  1009. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1010. if (ui32 != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG)
  1011. return -1; /* it's not a Zip64 archive. Not an error, though! */
  1012. info->zip64 = 1;
  1013. /* number of the disk with the start of the central directory. */
  1014. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1015. BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
  1016. /* offset of Zip64 end of central directory record. */
  1017. BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
  1018. /* total number of disks */
  1019. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1020. BAIL_IF_MACRO(ui32 != 1, PHYSFS_ERR_CORRUPT, 0);
  1021. pos = zip64_find_end_of_central_dir(io, pos, ui64);
  1022. if (pos < 0)
  1023. return 0; /* oh well. */
  1024. /*
  1025. * For self-extracting archives, etc, there's crapola in the file
  1026. * before the zipfile records; we calculate how much data there is
  1027. * prepended by determining how far the zip64-end-of-central-directory
  1028. * offset is from where it is supposed to be...the difference in bytes
  1029. * is how much arbitrary data is at the start of the physical file.
  1030. */
  1031. assert(((PHYSFS_uint64) pos) >= ui64);
  1032. *data_start = ((PHYSFS_uint64) pos) - ui64;
  1033. BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0);
  1034. /* check signature again, just in case. */
  1035. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1036. BAIL_IF_MACRO(ui32 != ZIP64_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
  1037. /* size of Zip64 end of central directory record. */
  1038. BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
  1039. /* version made by. */
  1040. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  1041. /* version needed to extract. */
  1042. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  1043. /* number of this disk. */
  1044. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1045. BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
  1046. /* number of disk with start of central directory record. */
  1047. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1048. BAIL_IF_MACRO(ui32 != 0, PHYSFS_ERR_CORRUPT, 0);
  1049. /* total number of entries in the central dir on this disk */
  1050. BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
  1051. /* total number of entries in the central dir */
  1052. BAIL_IF_MACRO(!readui64(io, &info->entryCount), ERRPASS, 0);
  1053. BAIL_IF_MACRO(ui64 != info->entryCount, PHYSFS_ERR_CORRUPT, 0);
  1054. /* size of the central directory */
  1055. BAIL_IF_MACRO(!readui64(io, &ui64), ERRPASS, 0);
  1056. /* offset of central directory */
  1057. BAIL_IF_MACRO(!readui64(io, dir_ofs), ERRPASS, 0);
  1058. /* Since we know the difference, fix up the central dir offset... */
  1059. *dir_ofs += *data_start;
  1060. /*
  1061. * There are more fields here, for encryption and feature-specific things,
  1062. * but we don't care about any of them at the moment.
  1063. */
  1064. return 1; /* made it. */
  1065. } /* zip64_parse_end_of_central_dir */
  1066. static int zip_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info,
  1067. PHYSFS_uint64 *data_start,
  1068. PHYSFS_uint64 *dir_ofs)
  1069. {
  1070. PHYSFS_uint16 entryCount16;
  1071. PHYSFS_uint32 offset32;
  1072. PHYSFS_uint32 ui32;
  1073. PHYSFS_uint16 ui16;
  1074. PHYSFS_sint64 len;
  1075. PHYSFS_sint64 pos;
  1076. int rc;
  1077. /* find the end-of-central-dir record, and seek to it. */
  1078. pos = zip_find_end_of_central_dir(io, &len);
  1079. BAIL_IF_MACRO(pos == -1, ERRPASS, 0);
  1080. BAIL_IF_MACRO(!io->seek(io, pos), ERRPASS, 0);
  1081. /* check signature again, just in case. */
  1082. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1083. BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0);
  1084. /* Seek back to see if "Zip64 end of central directory locator" exists. */
  1085. /* this record is 20 bytes before end-of-central-dir */
  1086. rc = zip64_parse_end_of_central_dir(io, info, data_start, dir_ofs, pos-20);
  1087. BAIL_IF_MACRO(rc == 0, ERRPASS, 0);
  1088. if (rc == 1)
  1089. return 1; /* we're done here. */
  1090. assert(rc == -1); /* no error, just not a Zip64 archive. */
  1091. /* Not Zip64? Seek back to where we were and keep processing. */
  1092. BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0);
  1093. /* number of this disk */
  1094. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  1095. BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
  1096. /* number of the disk with the start of the central directory */
  1097. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  1098. BAIL_IF_MACRO(ui16 != 0, PHYSFS_ERR_CORRUPT, 0);
  1099. /* total number of entries in the central dir on this disk */
  1100. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  1101. /* total number of entries in the central dir */
  1102. BAIL_IF_MACRO(!readui16(io, &entryCount16), ERRPASS, 0);
  1103. BAIL_IF_MACRO(ui16 != entryCount16, PHYSFS_ERR_CORRUPT, 0);
  1104. info->entryCount = entryCount16;
  1105. /* size of the central directory */
  1106. BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0);
  1107. /* offset of central directory */
  1108. BAIL_IF_MACRO(!readui32(io, &offset32), ERRPASS, 0);
  1109. *dir_ofs = (PHYSFS_uint64) offset32;
  1110. BAIL_IF_MACRO(pos < (*dir_ofs + ui32), PHYSFS_ERR_CORRUPT, 0);
  1111. /*
  1112. * For self-extracting archives, etc, there's crapola in the file
  1113. * before the zipfile records; we calculate how much data there is
  1114. * prepended by determining how far the central directory offset is
  1115. * from where it is supposed to be (start of end-of-central-dir minus
  1116. * sizeof central dir)...the difference in bytes is how much arbitrary
  1117. * data is at the start of the physical file.
  1118. */
  1119. *data_start = (PHYSFS_uint64) (pos - (*dir_ofs + ui32));
  1120. /* Now that we know the difference, fix up the central dir offset... */
  1121. *dir_ofs += *data_start;
  1122. /* zipfile comment length */
  1123. BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0);
  1124. /*
  1125. * Make sure that the comment length matches to the end of file...
  1126. * If it doesn't, we're either in the wrong part of the file, or the
  1127. * file is corrupted, but we give up either way.
  1128. */
  1129. BAIL_IF_MACRO((pos + 22 + ui16) != len, PHYSFS_ERR_CORRUPT, 0);
  1130. return 1; /* made it. */
  1131. } /* zip_parse_end_of_central_dir */
  1132. static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting)
  1133. {
  1134. ZIPinfo *info = NULL;
  1135. PHYSFS_uint64 data_start;
  1136. PHYSFS_uint64 cent_dir_ofs;
  1137. assert(io != NULL); /* shouldn't ever happen. */
  1138. BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
  1139. BAIL_IF_MACRO(!isZip(io), ERRPASS, NULL);
  1140. info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
  1141. BAIL_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  1142. memset(info, '\0', sizeof (ZIPinfo));
  1143. info->io = io;
  1144. if (!zip_parse_end_of_central_dir(io, info, &data_start, &cent_dir_ofs))
  1145. goto ZIP_openarchive_failed;
  1146. if (!zip_load_entries(io, info, data_start, cent_dir_ofs))
  1147. goto ZIP_openarchive_failed;
  1148. return info;
  1149. ZIP_openarchive_failed:
  1150. if (info != NULL)
  1151. allocator.Free(info);
  1152. return NULL;
  1153. } /* ZIP_openArchive */
  1154. static PHYSFS_sint64 zip_find_start_of_dir(ZIPinfo *info, const char *path,
  1155. int stop_on_first_find)
  1156. {
  1157. PHYSFS_sint64 lo = 0;
  1158. PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1);
  1159. PHYSFS_sint64 middle;
  1160. PHYSFS_uint32 dlen = (PHYSFS_uint32) strlen(path);
  1161. PHYSFS_sint64 retval = -1;
  1162. const char *name;
  1163. int rc;
  1164. if (*path == '\0') /* root dir? */
  1165. return 0;
  1166. if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
  1167. dlen--;
  1168. while (lo <= hi)
  1169. {
  1170. middle = lo + ((hi - lo) / 2);
  1171. name = info->entries[middle].name;
  1172. rc = strncmp(path, name, dlen);
  1173. if (rc == 0)
  1174. {
  1175. char ch = name[dlen];
  1176. if ('/' < ch) /* make sure this isn't just a substr match. */
  1177. rc = -1;
  1178. else if ('/' > ch)
  1179. rc = 1;
  1180. else
  1181. {
  1182. if (stop_on_first_find) /* Just checking dir's existance? */
  1183. return middle;
  1184. if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
  1185. return (middle + 1);
  1186. /* there might be more entries earlier in the list. */
  1187. retval = middle;
  1188. hi = middle - 1;
  1189. } /* else */
  1190. } /* if */
  1191. if (rc > 0)
  1192. lo = middle + 1;
  1193. else
  1194. hi = middle - 1;
  1195. } /* while */
  1196. return retval;
  1197. } /* zip_find_start_of_dir */
  1198. /*
  1199. * Moved to seperate function so we can use alloca then immediately throw
  1200. * away the allocated stack space...
  1201. */
  1202. static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
  1203. const char *odir, const char *str, PHYSFS_sint32 ln)
  1204. {
  1205. char *newstr = __PHYSFS_smallAlloc(ln + 1);
  1206. if (newstr == NULL)
  1207. return;
  1208. memcpy(newstr, str, ln);
  1209. newstr[ln] = '\0';
  1210. cb(callbackdata, odir, newstr);
  1211. __PHYSFS_smallFree(newstr);
  1212. } /* doEnumCallback */
  1213. static void ZIP_enumerateFiles(void *opaque, const char *dname,
  1214. PHYSFS_EnumFilesCallback cb,
  1215. const char *origdir, void *callbackdata)
  1216. {
  1217. ZIPinfo *info = ((ZIPinfo *) opaque);
  1218. PHYSFS_sint32 dlen, dlen_inc;
  1219. PHYSFS_sint64 i, max;
  1220. i = zip_find_start_of_dir(info, dname, 0);
  1221. if (i == -1) /* no such directory. */
  1222. return;
  1223. dlen = (PHYSFS_sint32) strlen(dname);
  1224. if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
  1225. dlen--;
  1226. dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
  1227. max = (PHYSFS_sint64) info->entryCount;
  1228. while (i < max)
  1229. {
  1230. char *e = info->entries[i].name;
  1231. if ((dlen) && ((strncmp(e, dname, dlen) != 0) || (e[dlen] != '/')))
  1232. break; /* past end of this dir; we're done. */
  1233. else
  1234. {
  1235. char *add = e + dlen_inc;
  1236. char *ptr = strchr(add, '/');
  1237. PHYSFS_sint32 ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
  1238. doEnumCallback(cb, callbackdata, origdir, add, ln);
  1239. ln += dlen_inc; /* point past entry to children... */
  1240. /* increment counter and skip children of subdirs... */
  1241. while ((++i < max) && (ptr != NULL))
  1242. {
  1243. char *e_new = info->entries[i].name;
  1244. if ((strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/'))
  1245. break;
  1246. } /* while */
  1247. } /* else */
  1248. } /* while */
  1249. } /* ZIP_enumerateFiles */
  1250. static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry)
  1251. {
  1252. int success;
  1253. PHYSFS_Io *retval = io->duplicate(io);
  1254. BAIL_IF_MACRO(!retval, ERRPASS, NULL);
  1255. /* !!! FIXME: if you open a dir here, it should bail ERR_NOT_A_FILE */
  1256. /* (inf) can be NULL if we already resolved. */
  1257. success = (inf == NULL) || zip_resolve(retval, inf, entry);
  1258. if (success)
  1259. {
  1260. PHYSFS_sint64 offset;
  1261. offset = ((entry->symlink) ? entry->symlink->offset : entry->offset);
  1262. success = retval->seek(retval, offset);
  1263. } /* if */
  1264. if (!success)
  1265. {
  1266. retval->destroy(retval);
  1267. retval = NULL;
  1268. } /* if */
  1269. return retval;
  1270. } /* zip_get_io */
  1271. static PHYSFS_Io *ZIP_openRead(void *opaque, const char *filename)
  1272. {
  1273. PHYSFS_Io *retval = NULL;
  1274. ZIPinfo *info = (ZIPinfo *) opaque;
  1275. ZIPentry *entry = zip_find_entry(info, filename, NULL);
  1276. ZIPfileinfo *finfo = NULL;
  1277. BAIL_IF_MACRO(!entry, ERRPASS, NULL);
  1278. retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io));
  1279. GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
  1280. finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
  1281. GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
  1282. memset(finfo, '\0', sizeof (ZIPfileinfo));
  1283. finfo->io = zip_get_io(info->io, info, entry);
  1284. GOTO_IF_MACRO(!finfo->io, ERRPASS, ZIP_openRead_failed);
  1285. finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry);
  1286. initializeZStream(&finfo->stream);
  1287. if (finfo->entry->compression_method != COMPMETH_NONE)
  1288. {
  1289. finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
  1290. if (!finfo->buffer)
  1291. GOTO_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed);
  1292. else if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
  1293. goto ZIP_openRead_failed;
  1294. } /* if */
  1295. memcpy(retval, &ZIP_Io, sizeof (PHYSFS_Io));
  1296. retval->opaque = finfo;
  1297. return retval;
  1298. ZIP_openRead_failed:
  1299. if (finfo != NULL)
  1300. {
  1301. if (finfo->io != NULL)
  1302. finfo->io->destroy(finfo->io);
  1303. if (finfo->buffer != NULL)
  1304. {
  1305. allocator.Free(finfo->buffer);
  1306. inflateEnd(&finfo->stream);
  1307. } /* if */
  1308. allocator.Free(finfo);
  1309. } /* if */
  1310. if (retval != NULL)
  1311. allocator.Free(retval);
  1312. return NULL;
  1313. } /* ZIP_openRead */
  1314. static PHYSFS_Io *ZIP_openWrite(void *opaque, const char *filename)
  1315. {
  1316. BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
  1317. } /* ZIP_openWrite */
  1318. static PHYSFS_Io *ZIP_openAppend(void *opaque, const char *filename)
  1319. {
  1320. BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL);
  1321. } /* ZIP_openAppend */
  1322. static void ZIP_closeArchive(void *opaque)
  1323. {
  1324. ZIPinfo *zi = (ZIPinfo *) (opaque);
  1325. zi->io->destroy(zi->io);
  1326. zip_free_entries(zi->entries, zi->entryCount);
  1327. allocator.Free(zi);
  1328. } /* ZIP_closeArchive */
  1329. static int ZIP_remove(void *opaque, const char *name)
  1330. {
  1331. BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
  1332. } /* ZIP_remove */
  1333. static int ZIP_mkdir(void *opaque, const char *name)
  1334. {
  1335. BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0);
  1336. } /* ZIP_mkdir */
  1337. static int ZIP_stat(void *opaque, const char *filename, PHYSFS_Stat *stat)
  1338. {
  1339. int isDir = 0;
  1340. const ZIPinfo *info = (const ZIPinfo *) opaque;
  1341. const ZIPentry *entry = zip_find_entry(info, filename, &isDir);
  1342. /* !!! FIXME: does this need to resolve entries here? */
  1343. if ((!isDir) && (entry == NULL))
  1344. return 0;
  1345. else if (isDir)
  1346. {
  1347. stat->filesize = 0;
  1348. stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
  1349. } /* if */
  1350. else if (zip_entry_is_symlink(entry))
  1351. {
  1352. stat->filesize = 0;
  1353. stat->filetype = PHYSFS_FILETYPE_SYMLINK;
  1354. } /* else if */
  1355. else
  1356. {
  1357. stat->filesize = (PHYSFS_sint64) entry->uncompressed_size;
  1358. stat->filetype = PHYSFS_FILETYPE_REGULAR;
  1359. } /* else */
  1360. stat->modtime = ((entry) ? entry->last_mod_time : 0);
  1361. stat->createtime = stat->modtime;
  1362. stat->accesstime = 0;
  1363. stat->readonly = 1; /* .zip files are always read only */
  1364. return 1;
  1365. } /* ZIP_stat */
  1366. const PHYSFS_Archiver __PHYSFS_Archiver_ZIP =
  1367. {
  1368. CURRENT_PHYSFS_ARCHIVER_API_VERSION,
  1369. {
  1370. "ZIP",
  1371. "PkZip/WinZip/Info-Zip compatible",
  1372. "Ryan C. Gordon <icculus@icculus.org>",
  1373. "http://icculus.org/physfs/",
  1374. 1, /* supportsSymlinks */
  1375. },
  1376. ZIP_openArchive,
  1377. ZIP_enumerateFiles,
  1378. ZIP_openRead,
  1379. ZIP_openWrite,
  1380. ZIP_openAppend,
  1381. ZIP_remove,
  1382. ZIP_mkdir,
  1383. ZIP_stat,
  1384. ZIP_closeArchive
  1385. };
  1386. #endif /* defined PHYSFS_SUPPORTS_ZIP */
  1387. /* end of archiver_zip.c ... */