65 struct _ZIPentry *symlink; /* NULL or file we symlink to */ |
61 struct _ZIPentry *symlink; /* NULL or file we symlink to */ |
66 ZipResolveType resolved; /* Have we resolved file/symlink? */ |
62 ZipResolveType resolved; /* Have we resolved file/symlink? */ |
67 PHYSFS_uint64 offset; /* offset of data in archive */ |
63 PHYSFS_uint64 offset; /* offset of data in archive */ |
68 PHYSFS_uint16 version; /* version made by */ |
64 PHYSFS_uint16 version; /* version made by */ |
69 PHYSFS_uint16 version_needed; /* version needed to extract */ |
65 PHYSFS_uint16 version_needed; /* version needed to extract */ |
|
66 PHYSFS_uint16 general_bits; /* general purpose bits */ |
70 PHYSFS_uint16 compression_method; /* compression method */ |
67 PHYSFS_uint16 compression_method; /* compression method */ |
71 PHYSFS_uint32 crc; /* crc-32 */ |
68 PHYSFS_uint32 crc; /* crc-32 */ |
72 PHYSFS_uint64 compressed_size; /* compressed size */ |
69 PHYSFS_uint64 compressed_size; /* compressed size */ |
73 PHYSFS_uint64 uncompressed_size; /* uncompressed size */ |
70 PHYSFS_uint64 uncompressed_size; /* uncompressed size */ |
74 PHYSFS_sint64 last_mod_time; /* last file mod time */ |
71 PHYSFS_sint64 last_mod_time; /* last file mod time */ |
|
72 PHYSFS_uint32 dos_mod_time; /* original MS-DOS style mod time */ |
|
73 struct _ZIPentry *hashnext; /* next item in this hash bucket */ |
|
74 struct _ZIPentry *children; /* linked list of kids, if dir */ |
|
75 struct _ZIPentry *sibling; /* next item in same dir */ |
75 } ZIPentry; |
76 } ZIPentry; |
76 |
77 |
77 /* |
78 /* |
78 * One ZIPinfo is kept for each open ZIP archive. |
79 * One ZIPinfo is kept for each open ZIP archive. |
79 */ |
80 */ |
80 typedef struct |
81 typedef struct |
81 { |
82 { |
82 PHYSFS_Io *io; |
83 PHYSFS_Io *io; /* the i/o interface for this archive. */ |
83 int zip64; /* non-zero if this is a Zip64 archive. */ |
84 ZIPentry root; /* root of directory tree. */ |
84 PHYSFS_uint64 entryCount; /* Number of files in ZIP. */ |
85 ZIPentry **hash; /* all entries hashed for fast lookup. */ |
85 ZIPentry *entries; /* info on all files in ZIP. */ |
86 size_t hashBuckets; /* number of buckets in hash. */ |
|
87 int zip64; /* non-zero if this is a Zip64 archive. */ |
|
88 int has_crypto; /* non-zero if any entry uses encryption. */ |
86 } ZIPinfo; |
89 } ZIPinfo; |
87 |
90 |
88 /* |
91 /* |
89 * One ZIPfileinfo is kept for each open file in a ZIP archive. |
92 * One ZIPfileinfo is kept for each open file in a ZIP archive. |
90 */ |
93 */ |
113 |
118 |
114 |
119 |
115 #define UNIX_FILETYPE_MASK 0170000 |
120 #define UNIX_FILETYPE_MASK 0170000 |
116 #define UNIX_FILETYPE_SYMLINK 0120000 |
121 #define UNIX_FILETYPE_SYMLINK 0120000 |
117 |
122 |
|
123 #define ZIP_GENERAL_BITS_TRADITIONAL_CRYPTO (1 << 0) |
|
124 #define ZIP_GENERAL_BITS_IGNORE_LOCAL_HEADER (1 << 3) |
|
125 |
|
126 /* support for "traditional" PKWARE encryption. */ |
|
127 static int zip_entry_is_tradional_crypto(const ZIPentry *entry) |
|
128 { |
|
129 return (entry->general_bits & ZIP_GENERAL_BITS_TRADITIONAL_CRYPTO) != 0; |
|
130 } /* zip_entry_is_traditional_crypto */ |
|
131 |
|
132 static int zip_entry_ignore_local_header(const ZIPentry *entry) |
|
133 { |
|
134 return (entry->general_bits & ZIP_GENERAL_BITS_IGNORE_LOCAL_HEADER) != 0; |
|
135 } /* zip_entry_is_traditional_crypto */ |
|
136 |
|
137 static PHYSFS_uint32 zip_crypto_crc32(const PHYSFS_uint32 crc, const PHYSFS_uint8 val) |
|
138 { |
|
139 int i; |
|
140 PHYSFS_uint32 xorval = (crc ^ ((PHYSFS_uint32) val)) & 0xFF; |
|
141 for (i = 0; i < 8; i++) |
|
142 xorval = ((xorval & 1) ? (0xEDB88320 ^ (xorval >> 1)) : (xorval >> 1)); |
|
143 return xorval ^ (crc >> 8); |
|
144 } /* zip_crc32 */ |
|
145 |
|
146 static void zip_update_crypto_keys(PHYSFS_uint32 *keys, const PHYSFS_uint8 val) |
|
147 { |
|
148 keys[0] = zip_crypto_crc32(keys[0], val); |
|
149 keys[1] = keys[1] + (keys[0] & 0x000000FF); |
|
150 keys[1] = (keys[1] * 134775813) + 1; |
|
151 keys[2] = zip_crypto_crc32(keys[2], (PHYSFS_uint8) ((keys[1] >> 24) & 0xFF)); |
|
152 } /* zip_update_crypto_keys */ |
|
153 |
|
154 static PHYSFS_uint8 zip_decrypt_byte(const PHYSFS_uint32 *keys) |
|
155 { |
|
156 const PHYSFS_uint16 tmp = keys[2] | 2; |
|
157 return (PHYSFS_uint8) ((tmp * (tmp ^ 1)) >> 8); |
|
158 } /* zip_decrypt_byte */ |
|
159 |
|
160 static PHYSFS_sint64 zip_read_decrypt(ZIPfileinfo *finfo, void *buf, PHYSFS_uint64 len) |
|
161 { |
|
162 PHYSFS_Io *io = finfo->io; |
|
163 const PHYSFS_sint64 br = io->read(io, buf, len); |
|
164 |
|
165 /* Decompression the new data if necessary. */ |
|
166 if (zip_entry_is_tradional_crypto(finfo->entry) && (br > 0)) |
|
167 { |
|
168 PHYSFS_uint32 *keys = finfo->crypto_keys; |
|
169 PHYSFS_uint8 *ptr = (PHYSFS_uint8 *) buf; |
|
170 PHYSFS_sint64 i; |
|
171 for (i = 0; i < br; i++, ptr++) |
|
172 { |
|
173 const PHYSFS_uint8 ch = *ptr ^ zip_decrypt_byte(keys); |
|
174 zip_update_crypto_keys(keys, ch); |
|
175 *ptr = ch; |
|
176 } /* for */ |
|
177 } /* if */ |
|
178 |
|
179 return br; |
|
180 } /* zip_read_decrypt */ |
|
181 |
|
182 static int zip_prep_crypto_keys(ZIPfileinfo *finfo, const PHYSFS_uint8 *crypto_header, const PHYSFS_uint8 *password) |
|
183 { |
|
184 /* It doesn't appear to be documented in PKWare's APPNOTE.TXT, but you |
|
185 need to use a different byte in the header to verify the password |
|
186 if general purpose bit 3 is set. Discovered this from Info-Zip. |
|
187 That's what the (verifier) value is doing, below. */ |
|
188 |
|
189 PHYSFS_uint32 *keys = finfo->crypto_keys; |
|
190 const ZIPentry *entry = finfo->entry; |
|
191 const int usedate = zip_entry_ignore_local_header(entry); |
|
192 const PHYSFS_uint8 verifier = (PHYSFS_uint8) ((usedate ? (entry->dos_mod_time >> 8) : (entry->crc >> 24)) & 0xFF); |
|
193 PHYSFS_uint8 finalbyte = 0; |
|
194 int i = 0; |
|
195 |
|
196 /* initialize vector with defaults, then password, then header. */ |
|
197 keys[0] = 305419896; |
|
198 keys[1] = 591751049; |
|
199 keys[2] = 878082192; |
|
200 |
|
201 while (*password) |
|
202 zip_update_crypto_keys(keys, *(password++)); |
|
203 |
|
204 for (i = 0; i < 12; i++) |
|
205 { |
|
206 const PHYSFS_uint8 c = crypto_header[i] ^ zip_decrypt_byte(keys); |
|
207 zip_update_crypto_keys(keys, c); |
|
208 finalbyte = c; |
|
209 } /* for */ |
|
210 |
|
211 /* you have a 1/256 chance of passing this test incorrectly. :/ */ |
|
212 if (finalbyte != verifier) |
|
213 BAIL_MACRO(PHYSFS_ERR_BAD_PASSWORD, 0); |
|
214 |
|
215 /* save the initial vector for seeking purposes. Not secure!! */ |
|
216 memcpy(finfo->initial_crypto_keys, finfo->crypto_keys, 12); |
|
217 return 1; |
|
218 } /* zip_prep_crypto_keys */ |
|
219 |
118 |
220 |
119 /* |
221 /* |
120 * Bridge physfs allocation functions to zlib's format... |
222 * Bridge physfs allocation functions to zlib's format... |
121 */ |
223 */ |
122 static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size) |
224 static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size) |
535 |
647 |
536 return retval; |
648 return retval; |
537 } /* isZip */ |
649 } /* isZip */ |
538 |
650 |
539 |
651 |
540 static void zip_free_entries(ZIPentry *entries, PHYSFS_uint64 max) |
652 /* Find the ZIPentry for a path in platform-independent notation. */ |
541 { |
653 static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path) |
542 PHYSFS_uint64 i; |
654 { |
543 for (i = 0; i < max; i++) |
655 PHYSFS_uint32 hashval; |
544 { |
656 ZIPentry *prev = NULL; |
545 ZIPentry *entry = &entries[i]; |
657 ZIPentry *retval; |
546 if (entry->name != NULL) |
658 |
547 allocator.Free(entry->name); |
659 if (*path == '\0') |
|
660 return &info->root; |
|
661 |
|
662 hashval = zip_hash_string(info, path); |
|
663 for (retval = info->hash[hashval]; retval; retval = retval->hashnext) |
|
664 { |
|
665 if (strcmp(retval->name, path) == 0) |
|
666 { |
|
667 if (prev != NULL) /* move this to the front of the list */ |
|
668 { |
|
669 prev->hashnext = retval->hashnext; |
|
670 retval->hashnext = info->hash[hashval]; |
|
671 info->hash[hashval] = retval; |
|
672 } /* if */ |
|
673 |
|
674 return retval; |
|
675 } /* if */ |
|
676 |
|
677 prev = retval; |
548 } /* for */ |
678 } /* for */ |
549 |
679 |
550 allocator.Free(entries); |
680 BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, NULL); |
551 } /* zip_free_entries */ |
|
552 |
|
553 |
|
554 /* |
|
555 * This will find the ZIPentry associated with a path in platform-independent |
|
556 * notation. Directories don't have ZIPentries associated with them, but |
|
557 * (*isDir) will be set to non-zero if a dir was hit. |
|
558 */ |
|
559 static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path, |
|
560 int *isDir) |
|
561 { |
|
562 ZIPentry *a = info->entries; |
|
563 PHYSFS_sint32 pathlen = (PHYSFS_sint32) strlen(path); |
|
564 PHYSFS_sint64 lo = 0; |
|
565 PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1); |
|
566 PHYSFS_sint64 middle; |
|
567 const char *thispath = NULL; |
|
568 int rc; |
|
569 |
|
570 while (lo <= hi) |
|
571 { |
|
572 middle = lo + ((hi - lo) / 2); |
|
573 thispath = a[middle].name; |
|
574 rc = strncmp(path, thispath, pathlen); |
|
575 |
|
576 if (rc > 0) |
|
577 lo = middle + 1; |
|
578 |
|
579 else if (rc < 0) |
|
580 hi = middle - 1; |
|
581 |
|
582 else /* substring match...might be dir or entry or nothing. */ |
|
583 { |
|
584 if (isDir != NULL) |
|
585 { |
|
586 *isDir = (thispath[pathlen] == '/'); |
|
587 if (*isDir) |
|
588 return NULL; |
|
589 } /* if */ |
|
590 |
|
591 if (thispath[pathlen] == '\0') /* found entry? */ |
|
592 return &a[middle]; |
|
593 /* adjust search params, try again. */ |
|
594 else if (thispath[pathlen] > '/') |
|
595 hi = middle - 1; |
|
596 else |
|
597 lo = middle + 1; |
|
598 } /* if */ |
|
599 } /* while */ |
|
600 |
|
601 if (isDir != NULL) |
|
602 *isDir = 0; |
|
603 |
|
604 BAIL_MACRO(PHYSFS_ERR_NO_SUCH_PATH, NULL); |
|
605 } /* zip_find_entry */ |
681 } /* zip_find_entry */ |
606 |
682 |
607 |
683 |
608 /* Convert paths from old, buggy DOS zippers... */ |
684 /* Convert paths from old, buggy DOS zippers... */ |
609 static void zip_convert_dos_path(ZIPentry *entry, char *path) |
685 static void zip_convert_dos_path(ZIPentry *entry, char *path) |
786 * aren't zero. That seems to work well. |
862 * aren't zero. That seems to work well. |
787 * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's |
863 * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's |
788 * possible that's a Zip64 thing. |
864 * possible that's a Zip64 thing. |
789 */ |
865 */ |
790 |
866 |
|
867 /* !!! FIXME: apparently these are zero if general purpose bit 3 is set, |
|
868 !!! FIXME: which is probably true for Jar files, fwiw, but we don't |
|
869 !!! FIXME: care about these values anyhow. */ |
|
870 |
791 BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0); |
871 BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0); |
792 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
872 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
793 BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0); |
873 BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0); |
794 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); |
874 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); |
795 BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0); |
875 BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0); |
857 entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); |
940 entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); |
858 } /* if */ |
941 } /* if */ |
859 |
942 |
860 return retval; |
943 return retval; |
861 } /* zip_resolve */ |
944 } /* zip_resolve */ |
|
945 |
|
946 |
|
947 static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry); |
|
948 |
|
949 /* Fill in missing parent directories. */ |
|
950 static ZIPentry *zip_hash_ancestors(ZIPinfo *info, char *name) |
|
951 { |
|
952 ZIPentry *retval = &info->root; |
|
953 char *sep = strrchr(name, '/'); |
|
954 |
|
955 if (sep) |
|
956 { |
|
957 const size_t namelen = (sep - name) + 1; |
|
958 |
|
959 *sep = '\0'; /* chop off last piece. */ |
|
960 retval = zip_find_entry(info, name); |
|
961 *sep = '/'; |
|
962 |
|
963 if (retval != NULL) |
|
964 { |
|
965 if (retval->resolved != ZIP_DIRECTORY) |
|
966 BAIL_MACRO(PHYSFS_ERR_CORRUPT, NULL); |
|
967 return retval; /* already hashed. */ |
|
968 } /* if */ |
|
969 |
|
970 /* okay, this is a new dir. Build and hash us. */ |
|
971 retval = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) + namelen); |
|
972 BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
|
973 memset(retval, '\0', sizeof (*retval)); |
|
974 retval->name = ((char *) retval) + sizeof (ZIPentry); |
|
975 memcpy(retval->name, name, namelen); |
|
976 retval->name[namelen] = '\0'; |
|
977 retval->resolved = ZIP_DIRECTORY; |
|
978 if (!zip_hash_entry(info, retval)) |
|
979 { |
|
980 allocator.Free(retval); |
|
981 return NULL; |
|
982 } /* if */ |
|
983 } /* else */ |
|
984 |
|
985 return retval; |
|
986 } /* zip_hash_ancestors */ |
|
987 |
|
988 |
|
989 static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry) |
|
990 { |
|
991 PHYSFS_uint32 hashval; |
|
992 ZIPentry *parent; |
|
993 |
|
994 assert(!zip_find_entry(info, entry->name)); /* checked elsewhere */ |
|
995 |
|
996 parent = zip_hash_ancestors(info, entry->name); |
|
997 if (!parent) |
|
998 return 0; |
|
999 |
|
1000 hashval = zip_hash_string(info, entry->name); |
|
1001 entry->hashnext = info->hash[hashval]; |
|
1002 info->hash[hashval] = entry; |
|
1003 |
|
1004 entry->sibling = parent->children; |
|
1005 parent->children = entry; |
|
1006 return 1; |
|
1007 } /* zip_hash_entry */ |
|
1008 |
|
1009 |
|
1010 static int zip_entry_is_symlink(const ZIPentry *entry) |
|
1011 { |
|
1012 return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) || |
|
1013 (entry->resolved == ZIP_BROKEN_SYMLINK) || |
|
1014 (entry->symlink)); |
|
1015 } /* zip_entry_is_symlink */ |
862 |
1016 |
863 |
1017 |
864 static int zip_version_does_symlinks(PHYSFS_uint32 version) |
1018 static int zip_version_does_symlinks(PHYSFS_uint32 version) |
865 { |
1019 { |
866 int retval = 0; |
1020 int retval = 0; |
934 |
1080 |
935 return ((PHYSFS_sint64) mktime(&unixtime)); |
1081 return ((PHYSFS_sint64) mktime(&unixtime)); |
936 } /* zip_dos_time_to_physfs_time */ |
1082 } /* zip_dos_time_to_physfs_time */ |
937 |
1083 |
938 |
1084 |
939 static int zip_load_entry(PHYSFS_Io *io, const int zip64, ZIPentry *entry, |
1085 static ZIPentry *zip_load_entry(PHYSFS_Io *io, const int zip64, |
940 PHYSFS_uint64 ofs_fixup) |
1086 const PHYSFS_uint64 ofs_fixup) |
941 { |
1087 { |
|
1088 ZIPentry entry; |
|
1089 ZIPentry *retval = NULL; |
942 PHYSFS_uint16 fnamelen, extralen, commentlen; |
1090 PHYSFS_uint16 fnamelen, extralen, commentlen; |
943 PHYSFS_uint32 external_attr; |
1091 PHYSFS_uint32 external_attr; |
944 PHYSFS_uint32 starting_disk; |
1092 PHYSFS_uint32 starting_disk; |
945 PHYSFS_uint64 offset; |
1093 PHYSFS_uint64 offset; |
946 PHYSFS_uint16 ui16; |
1094 PHYSFS_uint16 ui16; |
947 PHYSFS_uint32 ui32; |
1095 PHYSFS_uint32 ui32; |
948 PHYSFS_sint64 si64; |
1096 PHYSFS_sint64 si64; |
949 |
1097 |
|
1098 memset(&entry, '\0', sizeof (entry)); |
|
1099 |
950 /* sanity check with central directory signature... */ |
1100 /* sanity check with central directory signature... */ |
951 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1101 if (!readui32(io, &ui32)) return NULL; |
952 BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); |
1102 BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, NULL); |
953 |
1103 |
954 /* Get the pertinent parts of the record... */ |
1104 /* Get the pertinent parts of the record... */ |
955 BAIL_IF_MACRO(!readui16(io, &entry->version), ERRPASS, 0); |
1105 if (!readui16(io, &entry.version)) return NULL; |
956 BAIL_IF_MACRO(!readui16(io, &entry->version_needed), ERRPASS, 0); |
1106 if (!readui16(io, &entry.version_needed)) return NULL; |
957 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* general bits */ |
1107 if (!readui16(io, &entry.general_bits)) return NULL; /* general bits */ |
958 BAIL_IF_MACRO(!readui16(io, &entry->compression_method), ERRPASS, 0); |
1108 if (!readui16(io, &entry.compression_method)) return NULL; |
959 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1109 if (!readui32(io, &entry.dos_mod_time)) return NULL; |
960 entry->last_mod_time = zip_dos_time_to_physfs_time(ui32); |
1110 entry.last_mod_time = zip_dos_time_to_physfs_time(entry.dos_mod_time); |
961 BAIL_IF_MACRO(!readui32(io, &entry->crc), ERRPASS, 0); |
1111 if (!readui32(io, &entry.crc)) return NULL; |
962 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1112 if (!readui32(io, &ui32)) return NULL; |
963 entry->compressed_size = (PHYSFS_uint64) ui32; |
1113 entry.compressed_size = (PHYSFS_uint64) ui32; |
964 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1114 if (!readui32(io, &ui32)) return NULL; |
965 entry->uncompressed_size = (PHYSFS_uint64) ui32; |
1115 entry.uncompressed_size = (PHYSFS_uint64) ui32; |
966 BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0); |
1116 if (!readui16(io, &fnamelen)) return NULL; |
967 BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0); |
1117 if (!readui16(io, &extralen)) return NULL; |
968 BAIL_IF_MACRO(!readui16(io, &commentlen), ERRPASS, 0); |
1118 if (!readui16(io, &commentlen)) return NULL; |
969 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); |
1119 if (!readui16(io, &ui16)) return NULL; |
970 starting_disk = (PHYSFS_uint32) ui16; |
1120 starting_disk = (PHYSFS_uint32) ui16; |
971 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* internal file attribs */ |
1121 if (!readui16(io, &ui16)) return NULL; /* internal file attribs */ |
972 BAIL_IF_MACRO(!readui32(io, &external_attr), ERRPASS, 0); |
1122 if (!readui32(io, &external_attr)) return NULL; |
973 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1123 if (!readui32(io, &ui32)) return NULL; |
974 offset = (PHYSFS_uint64) ui32; |
1124 offset = (PHYSFS_uint64) ui32; |
975 |
1125 |
976 entry->symlink = NULL; /* will be resolved later, if necessary. */ |
1126 retval = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) + fnamelen + 1); |
977 entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ? |
1127 BAIL_IF_MACRO(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
978 ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; |
1128 memcpy(retval, &entry, sizeof (*retval)); |
979 |
1129 retval->name = ((char *) retval) + sizeof (ZIPentry); |
980 entry->name = (char *) allocator.Malloc(fnamelen + 1); |
1130 |
981 BAIL_IF_MACRO(entry->name == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
1131 if (!__PHYSFS_readAll(io, retval->name, fnamelen)) |
982 if (!__PHYSFS_readAll(io, entry->name, fnamelen)) |
|
983 goto zip_load_entry_puked; |
1132 goto zip_load_entry_puked; |
984 |
1133 |
985 entry->name[fnamelen] = '\0'; /* null-terminate the filename. */ |
1134 retval->name[fnamelen] = '\0'; /* null-terminate the filename. */ |
986 zip_convert_dos_path(entry, entry->name); |
1135 zip_convert_dos_path(retval, retval->name); |
|
1136 |
|
1137 retval->symlink = NULL; /* will be resolved later, if necessary. */ |
|
1138 |
|
1139 if (retval->name[fnamelen - 1] == '/') |
|
1140 { |
|
1141 retval->name[fnamelen - 1] = '\0'; |
|
1142 retval->resolved = ZIP_DIRECTORY; |
|
1143 } /* if */ |
|
1144 else |
|
1145 { |
|
1146 retval->resolved = (zip_has_symlink_attr(&entry, external_attr)) ? |
|
1147 ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; |
|
1148 } /* else */ |
987 |
1149 |
988 si64 = io->tell(io); |
1150 si64 = io->tell(io); |
989 if (si64 == -1) |
1151 if (si64 == -1) |
990 goto zip_load_entry_puked; |
1152 goto zip_load_entry_puked; |
991 |
1153 |
1058 GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1220 GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1059 } /* if */ |
1221 } /* if */ |
1060 |
1222 |
1061 GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1223 GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1062 |
1224 |
1063 entry->offset = offset + ofs_fixup; |
1225 retval->offset = offset + ofs_fixup; |
1064 |
1226 |
1065 /* seek to the start of the next entry in the central directory... */ |
1227 /* seek to the start of the next entry in the central directory... */ |
1066 if (!io->seek(io, si64 + extralen + commentlen)) |
1228 if (!io->seek(io, si64 + extralen + commentlen)) |
1067 goto zip_load_entry_puked; |
1229 goto zip_load_entry_puked; |
1068 |
1230 |
1069 return 1; /* success. */ |
1231 return retval; /* success. */ |
1070 |
1232 |
1071 zip_load_entry_puked: |
1233 zip_load_entry_puked: |
1072 allocator.Free(entry->name); |
1234 allocator.Free(retval); |
1073 return 0; /* failure. */ |
1235 return NULL; /* failure. */ |
1074 } /* zip_load_entry */ |
1236 } /* zip_load_entry */ |
1075 |
1237 |
1076 |
1238 |
1077 static int zip_entry_cmp(void *_a, size_t one, size_t two) |
1239 /* This leaves things allocated on error; the caller will clean up the mess. */ |
1078 { |
1240 static int zip_load_entries(ZIPinfo *info, |
1079 if (one != two) |
|
1080 { |
|
1081 const ZIPentry *a = (const ZIPentry *) _a; |
|
1082 return strcmp(a[one].name, a[two].name); |
|
1083 } /* if */ |
|
1084 |
|
1085 return 0; |
|
1086 } /* zip_entry_cmp */ |
|
1087 |
|
1088 |
|
1089 static void zip_entry_swap(void *_a, size_t one, size_t two) |
|
1090 { |
|
1091 if (one != two) |
|
1092 { |
|
1093 ZIPentry tmp; |
|
1094 ZIPentry *first = &(((ZIPentry *) _a)[one]); |
|
1095 ZIPentry *second = &(((ZIPentry *) _a)[two]); |
|
1096 memcpy(&tmp, first, sizeof (ZIPentry)); |
|
1097 memcpy(first, second, sizeof (ZIPentry)); |
|
1098 memcpy(second, &tmp, sizeof (ZIPentry)); |
|
1099 } /* if */ |
|
1100 } /* zip_entry_swap */ |
|
1101 |
|
1102 |
|
1103 static int zip_load_entries(PHYSFS_Io *io, ZIPinfo *info, |
|
1104 const PHYSFS_uint64 data_ofs, |
1241 const PHYSFS_uint64 data_ofs, |
1105 const PHYSFS_uint64 central_ofs) |
1242 const PHYSFS_uint64 central_ofs, |
1106 { |
1243 const PHYSFS_uint64 entry_count) |
1107 const PHYSFS_uint64 max = info->entryCount; |
1244 { |
|
1245 PHYSFS_Io *io = info->io; |
1108 const int zip64 = info->zip64; |
1246 const int zip64 = info->zip64; |
1109 PHYSFS_uint64 i; |
1247 PHYSFS_uint64 i; |
1110 |
1248 |
1111 BAIL_IF_MACRO(!io->seek(io, central_ofs), ERRPASS, 0); |
1249 if (!io->seek(io, central_ofs)) |
1112 |
1250 return 0; |
1113 info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max); |
1251 |
1114 BAIL_IF_MACRO(!info->entries, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
1252 for (i = 0; i < entry_count; i++) |
1115 |
1253 { |
1116 for (i = 0; i < max; i++) |
1254 ZIPentry *entry = zip_load_entry(io, zip64, data_ofs); |
1117 { |
1255 ZIPentry *find; |
1118 if (!zip_load_entry(io, zip64, &info->entries[i], data_ofs)) |
1256 |
1119 { |
1257 if (!entry) |
1120 zip_free_entries(info->entries, i); |
1258 return 0; |
|
1259 |
|
1260 find = zip_find_entry(info, entry->name); |
|
1261 if (find != NULL) /* duplicate? */ |
|
1262 { |
|
1263 if (find->last_mod_time != 0) /* duplicate? */ |
|
1264 { |
|
1265 allocator.Free(entry); |
|
1266 BAIL_MACRO(PHYSFS_ERR_CORRUPT, 0); |
|
1267 } /* if */ |
|
1268 else /* we filled this in as a placeholder. Update it. */ |
|
1269 { |
|
1270 find->offset = entry->offset; |
|
1271 find->version = entry->version; |
|
1272 find->version_needed = entry->version_needed; |
|
1273 find->compression_method = entry->compression_method; |
|
1274 find->crc = entry->crc; |
|
1275 find->compressed_size = entry->compressed_size; |
|
1276 find->uncompressed_size = entry->uncompressed_size; |
|
1277 find->last_mod_time = entry->last_mod_time; |
|
1278 allocator.Free(entry); |
|
1279 continue; |
|
1280 } /* else */ |
|
1281 } /* if */ |
|
1282 |
|
1283 if (!zip_hash_entry(info, entry)) |
|
1284 { |
|
1285 allocator.Free(entry); |
1121 return 0; |
1286 return 0; |
1122 } /* if */ |
1287 } /* if */ |
|
1288 |
|
1289 if (zip_entry_is_tradional_crypto(entry)) |
|
1290 info->has_crypto = 1; |
1123 } /* for */ |
1291 } /* for */ |
1124 |
1292 |
1125 __PHYSFS_sort(info->entries, (size_t) max, zip_entry_cmp, zip_entry_swap); |
|
1126 return 1; |
1293 return 1; |
1127 } /* zip_load_entries */ |
1294 } /* zip_load_entries */ |
1128 |
1295 |
1129 |
1296 |
1130 static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io, |
1297 static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io, |
1180 |
1347 |
1181 /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */ |
1348 /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */ |
1182 /* Just try moving back at most 256k. Oh well. */ |
1349 /* Just try moving back at most 256k. Oh well. */ |
1183 if ((offset < pos) && (pos > 4)) |
1350 if ((offset < pos) && (pos > 4)) |
1184 { |
1351 { |
1185 /* we assume you can eat this stack if you handle Zip64 files. */ |
1352 const PHYSFS_uint64 maxbuflen = 256 * 1024; |
1186 PHYSFS_uint8 buf[256 * 1024]; |
|
1187 PHYSFS_uint64 len = pos - offset; |
1353 PHYSFS_uint64 len = pos - offset; |
|
1354 PHYSFS_uint8 *buf = NULL; |
1188 PHYSFS_sint32 i; |
1355 PHYSFS_sint32 i; |
1189 |
1356 |
1190 if (len > sizeof (buf)) |
1357 if (len > maxbuflen) |
1191 len = sizeof (buf); |
1358 len = maxbuflen; |
1192 |
1359 |
1193 BAIL_IF_MACRO(!io->seek(io, pos - len), ERRPASS, -1); |
1360 buf = (PHYSFS_uint8 *) __PHYSFS_smallAlloc(len); |
1194 BAIL_IF_MACRO(!__PHYSFS_readAll(io, buf, len), ERRPASS, -1); |
1361 BAIL_IF_MACRO(!buf, PHYSFS_ERR_OUT_OF_MEMORY, -1); |
|
1362 |
|
1363 if (!io->seek(io, pos - len) || !__PHYSFS_readAll(io, buf, len)) |
|
1364 { |
|
1365 __PHYSFS_smallFree(buf); |
|
1366 return -1; /* error was set elsewhere. */ |
|
1367 } /* if */ |
|
1368 |
1195 for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--) |
1369 for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--) |
1196 { |
1370 { |
1197 if (buf[i] != 0x50) |
1371 if ( (buf[i] == 0x50) && (buf[i+1] == 0x4b) && |
1198 continue; |
1372 (buf[i+2] == 0x06) && (buf[i+3] == 0x06) ) |
1199 if ( (buf[i+1] == 0x4b) && |
1373 { |
1200 (buf[i+2] == 0x06) && |
1374 __PHYSFS_smallFree(buf); |
1201 (buf[i+3] == 0x06) ) |
|
1202 return pos - (len - i); |
1375 return pos - (len - i); |
|
1376 } /* if */ |
1203 } /* for */ |
1377 } /* for */ |
|
1378 |
|
1379 __PHYSFS_smallFree(buf); |
1204 } /* if */ |
1380 } /* if */ |
1205 |
1381 |
1206 BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */ |
1382 BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */ |
1207 } /* zip64_find_end_of_central_dir */ |
1383 } /* zip64_find_end_of_central_dir */ |
1208 |
1384 |
1209 |
1385 |
1210 static int zip64_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, |
1386 static int zip64_parse_end_of_central_dir(ZIPinfo *info, |
1211 PHYSFS_uint64 *data_start, |
1387 PHYSFS_uint64 *data_start, |
1212 PHYSFS_uint64 *dir_ofs, |
1388 PHYSFS_uint64 *dir_ofs, |
|
1389 PHYSFS_uint64 *entry_count, |
1213 PHYSFS_sint64 pos) |
1390 PHYSFS_sint64 pos) |
1214 { |
1391 { |
|
1392 PHYSFS_Io *io = info->io; |
1215 PHYSFS_uint64 ui64; |
1393 PHYSFS_uint64 ui64; |
1216 PHYSFS_uint32 ui32; |
1394 PHYSFS_uint32 ui32; |
1217 PHYSFS_uint16 ui16; |
1395 PHYSFS_uint16 ui16; |
1218 |
1396 |
1219 /* We should be positioned right past the locator signature. */ |
1397 /* We should be positioned right past the locator signature. */ |
1321 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1501 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1322 BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); |
1502 BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); |
1323 |
1503 |
1324 /* Seek back to see if "Zip64 end of central directory locator" exists. */ |
1504 /* Seek back to see if "Zip64 end of central directory locator" exists. */ |
1325 /* this record is 20 bytes before end-of-central-dir */ |
1505 /* this record is 20 bytes before end-of-central-dir */ |
1326 rc = zip64_parse_end_of_central_dir(io, info, data_start, dir_ofs, pos-20); |
1506 rc = zip64_parse_end_of_central_dir(info, data_start, dir_ofs, |
1327 BAIL_IF_MACRO(rc == 0, ERRPASS, 0); |
1507 entry_count, pos - 20); |
1328 if (rc == 1) |
1508 |
1329 return 1; /* we're done here. */ |
1509 /* Error or success? Bounce out of here. Keep going if not zip64. */ |
|
1510 if ((rc == 0) || (rc == 1)) |
|
1511 return rc; |
1330 |
1512 |
1331 assert(rc == -1); /* no error, just not a Zip64 archive. */ |
1513 assert(rc == -1); /* no error, just not a Zip64 archive. */ |
1332 |
1514 |
1333 /* Not Zip64? Seek back to where we were and keep processing. */ |
1515 /* Not Zip64? Seek back to where we were and keep processing. */ |
1334 BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0); |
1516 BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0); |
1383 |
1565 |
1384 return 1; /* made it. */ |
1566 return 1; /* made it. */ |
1385 } /* zip_parse_end_of_central_dir */ |
1567 } /* zip_parse_end_of_central_dir */ |
1386 |
1568 |
1387 |
1569 |
|
1570 static int zip_alloc_hashtable(ZIPinfo *info, const PHYSFS_uint64 entry_count) |
|
1571 { |
|
1572 size_t alloclen; |
|
1573 |
|
1574 info->hashBuckets = (size_t) (entry_count / 5); |
|
1575 if (!info->hashBuckets) |
|
1576 info->hashBuckets = 1; |
|
1577 |
|
1578 alloclen = info->hashBuckets * sizeof (ZIPentry *); |
|
1579 info->hash = (ZIPentry **) allocator.Malloc(alloclen); |
|
1580 BAIL_IF_MACRO(!info->hash, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
|
1581 memset(info->hash, '\0', alloclen); |
|
1582 |
|
1583 return 1; |
|
1584 } /* zip_alloc_hashtable */ |
|
1585 |
|
1586 static void ZIP_closeArchive(void *opaque); |
|
1587 |
1388 static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) |
1588 static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) |
1389 { |
1589 { |
1390 ZIPinfo *info = NULL; |
1590 ZIPinfo *info = NULL; |
1391 PHYSFS_uint64 data_start; |
1591 PHYSFS_uint64 dstart; /* data start */ |
1392 PHYSFS_uint64 cent_dir_ofs; |
1592 PHYSFS_uint64 cdir_ofs; /* central dir offset */ |
|
1593 PHYSFS_uint64 entry_count; |
1393 |
1594 |
1394 assert(io != NULL); /* shouldn't ever happen. */ |
1595 assert(io != NULL); /* shouldn't ever happen. */ |
1395 |
1596 |
1396 BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL); |
1597 BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL); |
1397 BAIL_IF_MACRO(!isZip(io), ERRPASS, NULL); |
1598 BAIL_IF_MACRO(!isZip(io), ERRPASS, NULL); |
1398 |
1599 |
1399 info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo)); |
1600 info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo)); |
1400 BAIL_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
1601 BAIL_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
1401 memset(info, '\0', sizeof (ZIPinfo)); |
1602 memset(info, '\0', sizeof (ZIPinfo)); |
|
1603 info->root.resolved = ZIP_DIRECTORY; |
1402 info->io = io; |
1604 info->io = io; |
1403 |
1605 |
1404 if (!zip_parse_end_of_central_dir(io, info, &data_start, ¢_dir_ofs)) |
1606 if (!zip_parse_end_of_central_dir(info, &dstart, &cdir_ofs, &entry_count)) |
1405 goto ZIP_openarchive_failed; |
1607 goto ZIP_openarchive_failed; |
1406 |
1608 else if (!zip_alloc_hashtable(info, entry_count)) |
1407 if (!zip_load_entries(io, info, data_start, cent_dir_ofs)) |
|
1408 goto ZIP_openarchive_failed; |
1609 goto ZIP_openarchive_failed; |
1409 |
1610 else if (!zip_load_entries(info, dstart, cdir_ofs, entry_count)) |
|
1611 goto ZIP_openarchive_failed; |
|
1612 |
|
1613 assert(info->root.sibling == NULL); |
1410 return info; |
1614 return info; |
1411 |
1615 |
1412 ZIP_openarchive_failed: |
1616 ZIP_openarchive_failed: |
1413 if (info != NULL) |
1617 info->io = NULL; /* don't let ZIP_closeArchive destroy (io). */ |
1414 allocator.Free(info); |
1618 ZIP_closeArchive(info); |
1415 |
|
1416 return NULL; |
1619 return NULL; |
1417 } /* ZIP_openArchive */ |
1620 } /* ZIP_openArchive */ |
1418 |
1621 |
1419 |
1622 |
1420 static PHYSFS_sint64 zip_find_start_of_dir(ZIPinfo *info, const char *path, |
1623 static void ZIP_enumerateFiles(void *opaque, const char *dname, |
1421 int stop_on_first_find) |
1624 PHYSFS_EnumFilesCallback cb, |
1422 { |
|
1423 PHYSFS_sint64 lo = 0; |
|
1424 PHYSFS_sint64 hi = (PHYSFS_sint64) (info->entryCount - 1); |
|
1425 PHYSFS_sint64 middle; |
|
1426 PHYSFS_uint32 dlen = (PHYSFS_uint32) strlen(path); |
|
1427 PHYSFS_sint64 retval = -1; |
|
1428 const char *name; |
|
1429 int rc; |
|
1430 |
|
1431 if (*path == '\0') /* root dir? */ |
|
1432 return 0; |
|
1433 |
|
1434 if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */ |
|
1435 dlen--; |
|
1436 |
|
1437 while (lo <= hi) |
|
1438 { |
|
1439 middle = lo + ((hi - lo) / 2); |
|
1440 name = info->entries[middle].name; |
|
1441 rc = strncmp(path, name, dlen); |
|
1442 if (rc == 0) |
|
1443 { |
|
1444 char ch = name[dlen]; |
|
1445 if ('/' < ch) /* make sure this isn't just a substr match. */ |
|
1446 rc = -1; |
|
1447 else if ('/' > ch) |
|
1448 rc = 1; |
|
1449 else |
|
1450 { |
|
1451 if (stop_on_first_find) /* Just checking dir's existance? */ |
|
1452 return middle; |
|
1453 |
|
1454 if (name[dlen + 1] == '\0') /* Skip initial dir entry. */ |
|
1455 return (middle + 1); |
|
1456 |
|
1457 /* there might be more entries earlier in the list. */ |
|
1458 retval = middle; |
|
1459 hi = middle - 1; |
|
1460 } /* else */ |
|
1461 } /* if */ |
|
1462 |
|
1463 if (rc > 0) |
|
1464 lo = middle + 1; |
|
1465 else |
|
1466 hi = middle - 1; |
|
1467 } /* while */ |
|
1468 |
|
1469 return retval; |
|
1470 } /* zip_find_start_of_dir */ |
|
1471 |
|
1472 |
|
1473 /* |
|
1474 * Moved to seperate function so we can use alloca then immediately throw |
|
1475 * away the allocated stack space... |
|
1476 */ |
|
1477 static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata, |
|
1478 const char *odir, const char *str, PHYSFS_sint32 ln) |
|
1479 { |
|
1480 char *newstr = __PHYSFS_smallAlloc(ln + 1); |
|
1481 if (newstr == NULL) |
|
1482 return; |
|
1483 |
|
1484 memcpy(newstr, str, ln); |
|
1485 newstr[ln] = '\0'; |
|
1486 cb(callbackdata, odir, newstr); |
|
1487 __PHYSFS_smallFree(newstr); |
|
1488 } /* doEnumCallback */ |
|
1489 |
|
1490 |
|
1491 static void ZIP_enumerateFiles(PHYSFS_Dir *opaque, const char *dname, |
|
1492 int omitSymLinks, PHYSFS_EnumFilesCallback cb, |
|
1493 const char *origdir, void *callbackdata) |
1625 const char *origdir, void *callbackdata) |
1494 { |
1626 { |
1495 ZIPinfo *info = ((ZIPinfo *) opaque); |
1627 ZIPinfo *info = ((ZIPinfo *) opaque); |
1496 PHYSFS_sint32 dlen, dlen_inc; |
1628 const ZIPentry *entry = zip_find_entry(info, dname); |
1497 PHYSFS_sint64 i, max; |
1629 if (entry && (entry->resolved == ZIP_DIRECTORY)) |
1498 |
1630 { |
1499 i = zip_find_start_of_dir(info, dname, 0); |
1631 for (entry = entry->children; entry; entry = entry->sibling) |
1500 if (i == -1) /* no such directory. */ |
1632 { |
1501 return; |
1633 const char *ptr = strrchr(entry->name, '/'); |
1502 |
1634 cb(callbackdata, origdir, ptr ? ptr + 1 : entry->name); |
1503 dlen = (PHYSFS_sint32) strlen(dname); |
1635 } /* for */ |
1504 if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */ |
1636 } /* if */ |
1505 dlen--; |
|
1506 |
|
1507 dlen_inc = ((dlen > 0) ? 1 : 0) + dlen; |
|
1508 max = (PHYSFS_sint64) info->entryCount; |
|
1509 while (i < max) |
|
1510 { |
|
1511 char *e = info->entries[i].name; |
|
1512 if ((dlen) && ((strncmp(e, dname, dlen) != 0) || (e[dlen] != '/'))) |
|
1513 break; /* past end of this dir; we're done. */ |
|
1514 |
|
1515 if ((omitSymLinks) && (zip_entry_is_symlink(&info->entries[i]))) |
|
1516 i++; |
|
1517 else |
|
1518 { |
|
1519 char *add = e + dlen_inc; |
|
1520 char *ptr = strchr(add, '/'); |
|
1521 PHYSFS_sint32 ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add)); |
|
1522 doEnumCallback(cb, callbackdata, origdir, add, ln); |
|
1523 ln += dlen_inc; /* point past entry to children... */ |
|
1524 |
|
1525 /* increment counter and skip children of subdirs... */ |
|
1526 while ((++i < max) && (ptr != NULL)) |
|
1527 { |
|
1528 char *e_new = info->entries[i].name; |
|
1529 if ((strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/')) |
|
1530 break; |
|
1531 } /* while */ |
|
1532 } /* else */ |
|
1533 } /* while */ |
|
1534 } /* ZIP_enumerateFiles */ |
1637 } /* ZIP_enumerateFiles */ |
1535 |
1638 |
1536 |
1639 |
1537 static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry) |
1640 static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry) |
1538 { |
1641 { |
1559 |
1662 |
1560 return retval; |
1663 return retval; |
1561 } /* zip_get_io */ |
1664 } /* zip_get_io */ |
1562 |
1665 |
1563 |
1666 |
1564 static PHYSFS_Io *ZIP_openRead(PHYSFS_Dir *opaque, const char *fnm, |
1667 static PHYSFS_Io *ZIP_openRead(void *opaque, const char *filename) |
1565 int *fileExists) |
|
1566 { |
1668 { |
1567 PHYSFS_Io *retval = NULL; |
1669 PHYSFS_Io *retval = NULL; |
1568 ZIPinfo *info = (ZIPinfo *) opaque; |
1670 ZIPinfo *info = (ZIPinfo *) opaque; |
1569 ZIPentry *entry = zip_find_entry(info, fnm, NULL); |
1671 ZIPentry *entry = zip_find_entry(info, filename); |
1570 ZIPfileinfo *finfo = NULL; |
1672 ZIPfileinfo *finfo = NULL; |
1571 |
1673 PHYSFS_Io *io = NULL; |
1572 *fileExists = (entry != NULL); |
1674 PHYSFS_uint8 *password = NULL; |
|
1675 int i; |
|
1676 |
|
1677 /* if not found, see if maybe "$PASSWORD" is appended. */ |
|
1678 if ((!entry) && (info->has_crypto)) |
|
1679 { |
|
1680 const char *ptr = strrchr(filename, '$'); |
|
1681 if (ptr != NULL) |
|
1682 { |
|
1683 const PHYSFS_uint64 len = (PHYSFS_uint64) (ptr - filename); |
|
1684 char *str = (char *) __PHYSFS_smallAlloc(len + 1); |
|
1685 BAIL_IF_MACRO(!str, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
|
1686 memcpy(str, filename, len); |
|
1687 str[len] = '\0'; |
|
1688 entry = zip_find_entry(info, str); |
|
1689 __PHYSFS_smallFree(str); |
|
1690 password = (PHYSFS_uint8 *) (ptr + 1); |
|
1691 } /* if */ |
|
1692 } /* if */ |
|
1693 |
1573 BAIL_IF_MACRO(!entry, ERRPASS, NULL); |
1694 BAIL_IF_MACRO(!entry, ERRPASS, NULL); |
1574 |
1695 |
1575 retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); |
1696 retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); |
1576 GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1697 GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1577 |
1698 |
1578 finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); |
1699 finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); |
1579 GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1700 GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1580 memset(finfo, '\0', sizeof (ZIPfileinfo)); |
1701 memset(finfo, '\0', sizeof (ZIPfileinfo)); |
1581 |
1702 |
1582 finfo->io = zip_get_io(info->io, info, entry); |
1703 io = zip_get_io(info->io, info, entry); |
1583 GOTO_IF_MACRO(!finfo->io, ERRPASS, ZIP_openRead_failed); |
1704 GOTO_IF_MACRO(!io, ERRPASS, ZIP_openRead_failed); |
|
1705 finfo->io = io; |
1584 finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry); |
1706 finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry); |
1585 initializeZStream(&finfo->stream); |
1707 initializeZStream(&finfo->stream); |
1586 |
1708 |
1587 if (finfo->entry->compression_method != COMPMETH_NONE) |
1709 if (finfo->entry->compression_method != COMPMETH_NONE) |
1588 { |
1710 { |
1618 |
1752 |
1619 return NULL; |
1753 return NULL; |
1620 } /* ZIP_openRead */ |
1754 } /* ZIP_openRead */ |
1621 |
1755 |
1622 |
1756 |
1623 static PHYSFS_Io *ZIP_openWrite(PHYSFS_Dir *opaque, const char *filename) |
1757 static PHYSFS_Io *ZIP_openWrite(void *opaque, const char *filename) |
1624 { |
1758 { |
1625 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1759 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1626 } /* ZIP_openWrite */ |
1760 } /* ZIP_openWrite */ |
1627 |
1761 |
1628 |
1762 |
1629 static PHYSFS_Io *ZIP_openAppend(PHYSFS_Dir *opaque, const char *filename) |
1763 static PHYSFS_Io *ZIP_openAppend(void *opaque, const char *filename) |
1630 { |
1764 { |
1631 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1765 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1632 } /* ZIP_openAppend */ |
1766 } /* ZIP_openAppend */ |
1633 |
1767 |
1634 |
1768 |
1635 static void ZIP_closeArchive(PHYSFS_Dir *opaque) |
1769 static void ZIP_closeArchive(void *opaque) |
1636 { |
1770 { |
1637 ZIPinfo *zi = (ZIPinfo *) (opaque); |
1771 ZIPinfo *info = (ZIPinfo *) (opaque); |
1638 zi->io->destroy(zi->io); |
1772 |
1639 zip_free_entries(zi->entries, zi->entryCount); |
1773 if (!info) |
1640 allocator.Free(zi); |
1774 return; |
|
1775 |
|
1776 if (info->io) |
|
1777 info->io->destroy(info->io); |
|
1778 |
|
1779 assert(info->root.sibling == NULL); |
|
1780 assert(info->hash || (info->root.children == NULL)); |
|
1781 |
|
1782 if (info->hash) |
|
1783 { |
|
1784 size_t i; |
|
1785 for (i = 0; i < info->hashBuckets; i++) |
|
1786 { |
|
1787 ZIPentry *entry; |
|
1788 ZIPentry *next; |
|
1789 for (entry = info->hash[i]; entry; entry = next) |
|
1790 { |
|
1791 next = entry->hashnext; |
|
1792 allocator.Free(entry); |
|
1793 } /* for */ |
|
1794 } /* for */ |
|
1795 allocator.Free(info->hash); |
|
1796 } /* if */ |
|
1797 |
|
1798 allocator.Free(info); |
1641 } /* ZIP_closeArchive */ |
1799 } /* ZIP_closeArchive */ |
1642 |
1800 |
1643 |
1801 |
1644 static int ZIP_remove(PHYSFS_Dir *opaque, const char *name) |
1802 static int ZIP_remove(void *opaque, const char *name) |
1645 { |
1803 { |
1646 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1804 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1647 } /* ZIP_remove */ |
1805 } /* ZIP_remove */ |
1648 |
1806 |
1649 |
1807 |
1650 static int ZIP_mkdir(PHYSFS_Dir *opaque, const char *name) |
1808 static int ZIP_mkdir(void *opaque, const char *name) |
1651 { |
1809 { |
1652 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1810 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1653 } /* ZIP_mkdir */ |
1811 } /* ZIP_mkdir */ |
1654 |
1812 |
1655 |
1813 |
1656 static int ZIP_stat(PHYSFS_Dir *opaque, const char *filename, int *exists, |
1814 static int ZIP_stat(void *opaque, const char *filename, PHYSFS_Stat *stat) |
1657 PHYSFS_Stat *stat) |
1815 { |
1658 { |
1816 ZIPinfo *info = (ZIPinfo *) opaque; |
1659 int isDir = 0; |
1817 const ZIPentry *entry = zip_find_entry(info, filename); |
1660 const ZIPinfo *info = (const ZIPinfo *) opaque; |
|
1661 const ZIPentry *entry = zip_find_entry(info, filename, &isDir); |
|
1662 |
1818 |
1663 /* !!! FIXME: does this need to resolve entries here? */ |
1819 /* !!! FIXME: does this need to resolve entries here? */ |
1664 |
1820 |
1665 *exists = isDir || (entry != 0); |
1821 if (entry == NULL) |
1666 if (!*exists) |
|
1667 return 0; |
1822 return 0; |
1668 |
1823 |
1669 if (isDir) |
1824 else if (entry->resolved == ZIP_DIRECTORY) |
1670 { |
1825 { |
1671 stat->filesize = 0; |
1826 stat->filesize = 0; |
1672 stat->filetype = PHYSFS_FILETYPE_DIRECTORY; |
1827 stat->filetype = PHYSFS_FILETYPE_DIRECTORY; |
1673 } /* if */ |
1828 } /* if */ |
1674 |
1829 |