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