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) |
534 |
646 |
535 return retval; |
647 return retval; |
536 } /* isZip */ |
648 } /* isZip */ |
537 |
649 |
538 |
650 |
539 static void zip_free_entries(ZIPentry *entries, PHYSFS_uint64 max) |
651 /* Find the ZIPentry for a path in platform-independent notation. */ |
540 { |
652 static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path) |
541 PHYSFS_uint64 i; |
653 { |
542 for (i = 0; i < max; i++) |
654 PHYSFS_uint32 hashval; |
543 { |
655 ZIPentry *prev = NULL; |
544 ZIPentry *entry = &entries[i]; |
656 ZIPentry *retval; |
545 if (entry->name != NULL) |
657 |
546 allocator.Free(entry->name); |
658 if (*path == '\0') |
|
659 return &info->root; |
|
660 |
|
661 hashval = zip_hash_string(info, path); |
|
662 for (retval = info->hash[hashval]; retval; retval = retval->hashnext) |
|
663 { |
|
664 if (strcmp(retval->name, path) == 0) |
|
665 { |
|
666 if (prev != NULL) /* move this to the front of the list */ |
|
667 { |
|
668 prev->hashnext = retval->hashnext; |
|
669 retval->hashnext = info->hash[hashval]; |
|
670 info->hash[hashval] = retval; |
|
671 } /* if */ |
|
672 |
|
673 return retval; |
|
674 } /* if */ |
|
675 |
|
676 prev = retval; |
547 } /* for */ |
677 } /* for */ |
548 |
678 |
549 allocator.Free(entries); |
679 BAIL_MACRO(PHYSFS_ERR_NOT_FOUND, NULL); |
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 */ |
680 } /* zip_find_entry */ |
605 |
681 |
606 |
682 |
607 /* Convert paths from old, buggy DOS zippers... */ |
683 /* Convert paths from old, buggy DOS zippers... */ |
608 static void zip_convert_dos_path(ZIPentry *entry, char *path) |
684 static void zip_convert_dos_path(ZIPentry *entry, char *path) |
785 * aren't zero. That seems to work well. |
861 * aren't zero. That seems to work well. |
786 * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's |
862 * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's |
787 * possible that's a Zip64 thing. |
863 * possible that's a Zip64 thing. |
788 */ |
864 */ |
789 |
865 |
|
866 /* !!! FIXME: apparently these are zero if general purpose bit 3 is set, |
|
867 !!! FIXME: which is probably true for Jar files, fwiw, but we don't |
|
868 !!! FIXME: care about these values anyhow. */ |
|
869 |
790 BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0); |
870 BAIL_IF_MACRO(!io->seek(io, entry->offset), ERRPASS, 0); |
791 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
871 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
792 BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0); |
872 BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0); |
793 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); |
873 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); |
794 BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0); |
874 BAIL_IF_MACRO(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0); |
856 entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); |
939 entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); |
857 } /* if */ |
940 } /* if */ |
858 |
941 |
859 return retval; |
942 return retval; |
860 } /* zip_resolve */ |
943 } /* zip_resolve */ |
|
944 |
|
945 |
|
946 static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry); |
|
947 |
|
948 /* Fill in missing parent directories. */ |
|
949 static ZIPentry *zip_hash_ancestors(ZIPinfo *info, char *name) |
|
950 { |
|
951 ZIPentry *retval = &info->root; |
|
952 char *sep = strrchr(name, '/'); |
|
953 |
|
954 if (sep) |
|
955 { |
|
956 const size_t namelen = (sep - name) + 1; |
|
957 |
|
958 *sep = '\0'; /* chop off last piece. */ |
|
959 retval = zip_find_entry(info, name); |
|
960 *sep = '/'; |
|
961 |
|
962 if (retval != NULL) |
|
963 { |
|
964 if (retval->resolved != ZIP_DIRECTORY) |
|
965 BAIL_MACRO(PHYSFS_ERR_CORRUPT, NULL); |
|
966 return retval; /* already hashed. */ |
|
967 } /* if */ |
|
968 |
|
969 /* okay, this is a new dir. Build and hash us. */ |
|
970 retval = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) + namelen); |
|
971 BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
|
972 memset(retval, '\0', sizeof (*retval)); |
|
973 retval->name = ((char *) retval) + sizeof (ZIPentry); |
|
974 memcpy(retval->name, name, namelen - 1); |
|
975 retval->name[namelen - 1] = '\0'; |
|
976 retval->resolved = ZIP_DIRECTORY; |
|
977 if (!zip_hash_entry(info, retval)) |
|
978 { |
|
979 allocator.Free(retval); |
|
980 return NULL; |
|
981 } /* if */ |
|
982 } /* else */ |
|
983 |
|
984 return retval; |
|
985 } /* zip_hash_ancestors */ |
|
986 |
|
987 |
|
988 static int zip_hash_entry(ZIPinfo *info, ZIPentry *entry) |
|
989 { |
|
990 PHYSFS_uint32 hashval; |
|
991 ZIPentry *parent; |
|
992 |
|
993 assert(!zip_find_entry(info, entry->name)); /* checked elsewhere */ |
|
994 |
|
995 parent = zip_hash_ancestors(info, entry->name); |
|
996 if (!parent) |
|
997 return 0; |
|
998 |
|
999 hashval = zip_hash_string(info, entry->name); |
|
1000 entry->hashnext = info->hash[hashval]; |
|
1001 info->hash[hashval] = entry; |
|
1002 |
|
1003 entry->sibling = parent->children; |
|
1004 parent->children = entry; |
|
1005 return 1; |
|
1006 } /* zip_hash_entry */ |
|
1007 |
|
1008 |
|
1009 static int zip_entry_is_symlink(const ZIPentry *entry) |
|
1010 { |
|
1011 return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) || |
|
1012 (entry->resolved == ZIP_BROKEN_SYMLINK) || |
|
1013 (entry->symlink)); |
|
1014 } /* zip_entry_is_symlink */ |
861 |
1015 |
862 |
1016 |
863 static int zip_version_does_symlinks(PHYSFS_uint32 version) |
1017 static int zip_version_does_symlinks(PHYSFS_uint32 version) |
864 { |
1018 { |
865 int retval = 0; |
1019 int retval = 0; |
933 |
1079 |
934 return ((PHYSFS_sint64) mktime(&unixtime)); |
1080 return ((PHYSFS_sint64) mktime(&unixtime)); |
935 } /* zip_dos_time_to_physfs_time */ |
1081 } /* zip_dos_time_to_physfs_time */ |
936 |
1082 |
937 |
1083 |
938 static int zip_load_entry(PHYSFS_Io *io, const int zip64, ZIPentry *entry, |
1084 static ZIPentry *zip_load_entry(PHYSFS_Io *io, const int zip64, |
939 PHYSFS_uint64 ofs_fixup) |
1085 const PHYSFS_uint64 ofs_fixup) |
940 { |
1086 { |
|
1087 ZIPentry entry; |
|
1088 ZIPentry *retval = NULL; |
941 PHYSFS_uint16 fnamelen, extralen, commentlen; |
1089 PHYSFS_uint16 fnamelen, extralen, commentlen; |
942 PHYSFS_uint32 external_attr; |
1090 PHYSFS_uint32 external_attr; |
943 PHYSFS_uint32 starting_disk; |
1091 PHYSFS_uint32 starting_disk; |
944 PHYSFS_uint64 offset; |
1092 PHYSFS_uint64 offset; |
945 PHYSFS_uint16 ui16; |
1093 PHYSFS_uint16 ui16; |
946 PHYSFS_uint32 ui32; |
1094 PHYSFS_uint32 ui32; |
947 PHYSFS_sint64 si64; |
1095 PHYSFS_sint64 si64; |
948 |
1096 |
|
1097 memset(&entry, '\0', sizeof (entry)); |
|
1098 |
949 /* sanity check with central directory signature... */ |
1099 /* sanity check with central directory signature... */ |
950 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1100 if (!readui32(io, &ui32)) return NULL; |
951 BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); |
1101 BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, NULL); |
952 |
1102 |
953 /* Get the pertinent parts of the record... */ |
1103 /* Get the pertinent parts of the record... */ |
954 BAIL_IF_MACRO(!readui16(io, &entry->version), ERRPASS, 0); |
1104 if (!readui16(io, &entry.version)) return NULL; |
955 BAIL_IF_MACRO(!readui16(io, &entry->version_needed), ERRPASS, 0); |
1105 if (!readui16(io, &entry.version_needed)) return NULL; |
956 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* general bits */ |
1106 if (!readui16(io, &entry.general_bits)) return NULL; /* general bits */ |
957 BAIL_IF_MACRO(!readui16(io, &entry->compression_method), ERRPASS, 0); |
1107 if (!readui16(io, &entry.compression_method)) return NULL; |
958 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1108 if (!readui32(io, &entry.dos_mod_time)) return NULL; |
959 entry->last_mod_time = zip_dos_time_to_physfs_time(ui32); |
1109 entry.last_mod_time = zip_dos_time_to_physfs_time(entry.dos_mod_time); |
960 BAIL_IF_MACRO(!readui32(io, &entry->crc), ERRPASS, 0); |
1110 if (!readui32(io, &entry.crc)) return NULL; |
961 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1111 if (!readui32(io, &ui32)) return NULL; |
962 entry->compressed_size = (PHYSFS_uint64) ui32; |
1112 entry.compressed_size = (PHYSFS_uint64) ui32; |
963 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1113 if (!readui32(io, &ui32)) return NULL; |
964 entry->uncompressed_size = (PHYSFS_uint64) ui32; |
1114 entry.uncompressed_size = (PHYSFS_uint64) ui32; |
965 BAIL_IF_MACRO(!readui16(io, &fnamelen), ERRPASS, 0); |
1115 if (!readui16(io, &fnamelen)) return NULL; |
966 BAIL_IF_MACRO(!readui16(io, &extralen), ERRPASS, 0); |
1116 if (!readui16(io, &extralen)) return NULL; |
967 BAIL_IF_MACRO(!readui16(io, &commentlen), ERRPASS, 0); |
1117 if (!readui16(io, &commentlen)) return NULL; |
968 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); |
1118 if (!readui16(io, &ui16)) return NULL; |
969 starting_disk = (PHYSFS_uint32) ui16; |
1119 starting_disk = (PHYSFS_uint32) ui16; |
970 BAIL_IF_MACRO(!readui16(io, &ui16), ERRPASS, 0); /* internal file attribs */ |
1120 if (!readui16(io, &ui16)) return NULL; /* internal file attribs */ |
971 BAIL_IF_MACRO(!readui32(io, &external_attr), ERRPASS, 0); |
1121 if (!readui32(io, &external_attr)) return NULL; |
972 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1122 if (!readui32(io, &ui32)) return NULL; |
973 offset = (PHYSFS_uint64) ui32; |
1123 offset = (PHYSFS_uint64) ui32; |
974 |
1124 |
975 entry->symlink = NULL; /* will be resolved later, if necessary. */ |
1125 retval = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) + fnamelen + 1); |
976 entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ? |
1126 BAIL_IF_MACRO(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
977 ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; |
1127 memcpy(retval, &entry, sizeof (*retval)); |
978 |
1128 retval->name = ((char *) retval) + sizeof (ZIPentry); |
979 entry->name = (char *) allocator.Malloc(fnamelen + 1); |
1129 |
980 BAIL_IF_MACRO(entry->name == NULL, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
1130 if (!__PHYSFS_readAll(io, retval->name, fnamelen)) |
981 if (!__PHYSFS_readAll(io, entry->name, fnamelen)) |
|
982 goto zip_load_entry_puked; |
1131 goto zip_load_entry_puked; |
983 |
1132 |
984 entry->name[fnamelen] = '\0'; /* null-terminate the filename. */ |
1133 retval->name[fnamelen] = '\0'; /* null-terminate the filename. */ |
985 zip_convert_dos_path(entry, entry->name); |
1134 zip_convert_dos_path(retval, retval->name); |
|
1135 |
|
1136 retval->symlink = NULL; /* will be resolved later, if necessary. */ |
|
1137 |
|
1138 if (retval->name[fnamelen - 1] == '/') |
|
1139 { |
|
1140 retval->name[fnamelen - 1] = '\0'; |
|
1141 retval->resolved = ZIP_DIRECTORY; |
|
1142 } /* if */ |
|
1143 else |
|
1144 { |
|
1145 retval->resolved = (zip_has_symlink_attr(&entry, external_attr)) ? |
|
1146 ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; |
|
1147 } /* else */ |
986 |
1148 |
987 si64 = io->tell(io); |
1149 si64 = io->tell(io); |
988 if (si64 == -1) |
1150 if (si64 == -1) |
989 goto zip_load_entry_puked; |
1151 goto zip_load_entry_puked; |
990 |
1152 |
1057 GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1219 GOTO_IF_MACRO(len != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1058 } /* if */ |
1220 } /* if */ |
1059 |
1221 |
1060 GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1222 GOTO_IF_MACRO(starting_disk != 0, PHYSFS_ERR_CORRUPT, zip_load_entry_puked); |
1061 |
1223 |
1062 entry->offset = offset + ofs_fixup; |
1224 retval->offset = offset + ofs_fixup; |
1063 |
1225 |
1064 /* seek to the start of the next entry in the central directory... */ |
1226 /* seek to the start of the next entry in the central directory... */ |
1065 if (!io->seek(io, si64 + extralen + commentlen)) |
1227 if (!io->seek(io, si64 + extralen + commentlen)) |
1066 goto zip_load_entry_puked; |
1228 goto zip_load_entry_puked; |
1067 |
1229 |
1068 return 1; /* success. */ |
1230 return retval; /* success. */ |
1069 |
1231 |
1070 zip_load_entry_puked: |
1232 zip_load_entry_puked: |
1071 allocator.Free(entry->name); |
1233 allocator.Free(retval); |
1072 return 0; /* failure. */ |
1234 return NULL; /* failure. */ |
1073 } /* zip_load_entry */ |
1235 } /* zip_load_entry */ |
1074 |
1236 |
1075 |
1237 |
1076 static int zip_entry_cmp(void *_a, size_t one, size_t two) |
1238 /* This leaves things allocated on error; the caller will clean up the mess. */ |
1077 { |
1239 static int zip_load_entries(ZIPinfo *info, |
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, |
1240 const PHYSFS_uint64 data_ofs, |
1104 const PHYSFS_uint64 central_ofs) |
1241 const PHYSFS_uint64 central_ofs, |
1105 { |
1242 const PHYSFS_uint64 entry_count) |
1106 const PHYSFS_uint64 max = info->entryCount; |
1243 { |
|
1244 PHYSFS_Io *io = info->io; |
1107 const int zip64 = info->zip64; |
1245 const int zip64 = info->zip64; |
1108 PHYSFS_uint64 i; |
1246 PHYSFS_uint64 i; |
1109 |
1247 |
1110 BAIL_IF_MACRO(!io->seek(io, central_ofs), ERRPASS, 0); |
1248 if (!io->seek(io, central_ofs)) |
1111 |
1249 return 0; |
1112 info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max); |
1250 |
1113 BAIL_IF_MACRO(!info->entries, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
1251 for (i = 0; i < entry_count; i++) |
1114 |
1252 { |
1115 for (i = 0; i < max; i++) |
1253 ZIPentry *entry = zip_load_entry(io, zip64, data_ofs); |
1116 { |
1254 ZIPentry *find; |
1117 if (!zip_load_entry(io, zip64, &info->entries[i], data_ofs)) |
1255 |
1118 { |
1256 if (!entry) |
1119 zip_free_entries(info->entries, i); |
1257 return 0; |
|
1258 |
|
1259 find = zip_find_entry(info, entry->name); |
|
1260 if (find != NULL) /* duplicate? */ |
|
1261 { |
|
1262 if (find->last_mod_time != 0) /* duplicate? */ |
|
1263 { |
|
1264 allocator.Free(entry); |
|
1265 BAIL_MACRO(PHYSFS_ERR_CORRUPT, 0); |
|
1266 } /* if */ |
|
1267 else /* we filled this in as a placeholder. Update it. */ |
|
1268 { |
|
1269 find->offset = entry->offset; |
|
1270 find->version = entry->version; |
|
1271 find->version_needed = entry->version_needed; |
|
1272 find->compression_method = entry->compression_method; |
|
1273 find->crc = entry->crc; |
|
1274 find->compressed_size = entry->compressed_size; |
|
1275 find->uncompressed_size = entry->uncompressed_size; |
|
1276 find->last_mod_time = entry->last_mod_time; |
|
1277 allocator.Free(entry); |
|
1278 continue; |
|
1279 } /* else */ |
|
1280 } /* if */ |
|
1281 |
|
1282 if (!zip_hash_entry(info, entry)) |
|
1283 { |
|
1284 allocator.Free(entry); |
1120 return 0; |
1285 return 0; |
1121 } /* if */ |
1286 } /* if */ |
|
1287 |
|
1288 if (zip_entry_is_tradional_crypto(entry)) |
|
1289 info->has_crypto = 1; |
1122 } /* for */ |
1290 } /* for */ |
1123 |
1291 |
1124 __PHYSFS_sort(info->entries, (size_t) max, zip_entry_cmp, zip_entry_swap); |
|
1125 return 1; |
1292 return 1; |
1126 } /* zip_load_entries */ |
1293 } /* zip_load_entries */ |
1127 |
1294 |
1128 |
1295 |
1129 static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io, |
1296 static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io, |
1179 |
1346 |
1180 /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */ |
1347 /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */ |
1181 /* Just try moving back at most 256k. Oh well. */ |
1348 /* Just try moving back at most 256k. Oh well. */ |
1182 if ((offset < pos) && (pos > 4)) |
1349 if ((offset < pos) && (pos > 4)) |
1183 { |
1350 { |
1184 /* we assume you can eat this stack if you handle Zip64 files. */ |
1351 const PHYSFS_uint64 maxbuflen = 256 * 1024; |
1185 PHYSFS_uint8 buf[256 * 1024]; |
|
1186 PHYSFS_uint64 len = pos - offset; |
1352 PHYSFS_uint64 len = pos - offset; |
|
1353 PHYSFS_uint8 *buf = NULL; |
1187 PHYSFS_sint32 i; |
1354 PHYSFS_sint32 i; |
1188 |
1355 |
1189 if (len > sizeof (buf)) |
1356 if (len > maxbuflen) |
1190 len = sizeof (buf); |
1357 len = maxbuflen; |
1191 |
1358 |
1192 BAIL_IF_MACRO(!io->seek(io, pos - len), ERRPASS, -1); |
1359 buf = (PHYSFS_uint8 *) __PHYSFS_smallAlloc(len); |
1193 BAIL_IF_MACRO(!__PHYSFS_readAll(io, buf, len), ERRPASS, -1); |
1360 BAIL_IF_MACRO(!buf, PHYSFS_ERR_OUT_OF_MEMORY, -1); |
|
1361 |
|
1362 if (!io->seek(io, pos - len) || !__PHYSFS_readAll(io, buf, len)) |
|
1363 { |
|
1364 __PHYSFS_smallFree(buf); |
|
1365 return -1; /* error was set elsewhere. */ |
|
1366 } /* if */ |
|
1367 |
1194 for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--) |
1368 for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--) |
1195 { |
1369 { |
1196 if (buf[i] != 0x50) |
1370 if ( (buf[i] == 0x50) && (buf[i+1] == 0x4b) && |
1197 continue; |
1371 (buf[i+2] == 0x06) && (buf[i+3] == 0x06) ) |
1198 if ( (buf[i+1] == 0x4b) && |
1372 { |
1199 (buf[i+2] == 0x06) && |
1373 __PHYSFS_smallFree(buf); |
1200 (buf[i+3] == 0x06) ) |
|
1201 return pos - (len - i); |
1374 return pos - (len - i); |
|
1375 } /* if */ |
1202 } /* for */ |
1376 } /* for */ |
|
1377 |
|
1378 __PHYSFS_smallFree(buf); |
1203 } /* if */ |
1379 } /* if */ |
1204 |
1380 |
1205 BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */ |
1381 BAIL_MACRO(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */ |
1206 } /* zip64_find_end_of_central_dir */ |
1382 } /* zip64_find_end_of_central_dir */ |
1207 |
1383 |
1208 |
1384 |
1209 static int zip64_parse_end_of_central_dir(PHYSFS_Io *io, ZIPinfo *info, |
1385 static int zip64_parse_end_of_central_dir(ZIPinfo *info, |
1210 PHYSFS_uint64 *data_start, |
1386 PHYSFS_uint64 *data_start, |
1211 PHYSFS_uint64 *dir_ofs, |
1387 PHYSFS_uint64 *dir_ofs, |
|
1388 PHYSFS_uint64 *entry_count, |
1212 PHYSFS_sint64 pos) |
1389 PHYSFS_sint64 pos) |
1213 { |
1390 { |
|
1391 PHYSFS_Io *io = info->io; |
1214 PHYSFS_uint64 ui64; |
1392 PHYSFS_uint64 ui64; |
1215 PHYSFS_uint32 ui32; |
1393 PHYSFS_uint32 ui32; |
1216 PHYSFS_uint16 ui16; |
1394 PHYSFS_uint16 ui16; |
1217 |
1395 |
1218 /* We should be positioned right past the locator signature. */ |
1396 /* We should be positioned right past the locator signature. */ |
1320 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1500 BAIL_IF_MACRO(!readui32(io, &ui32), ERRPASS, 0); |
1321 BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); |
1501 BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); |
1322 |
1502 |
1323 /* Seek back to see if "Zip64 end of central directory locator" exists. */ |
1503 /* Seek back to see if "Zip64 end of central directory locator" exists. */ |
1324 /* this record is 20 bytes before end-of-central-dir */ |
1504 /* 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); |
1505 rc = zip64_parse_end_of_central_dir(info, data_start, dir_ofs, |
1326 BAIL_IF_MACRO(rc == 0, ERRPASS, 0); |
1506 entry_count, pos - 20); |
1327 if (rc == 1) |
1507 |
1328 return 1; /* we're done here. */ |
1508 /* Error or success? Bounce out of here. Keep going if not zip64. */ |
|
1509 if ((rc == 0) || (rc == 1)) |
|
1510 return rc; |
1329 |
1511 |
1330 assert(rc == -1); /* no error, just not a Zip64 archive. */ |
1512 assert(rc == -1); /* no error, just not a Zip64 archive. */ |
1331 |
1513 |
1332 /* Not Zip64? Seek back to where we were and keep processing. */ |
1514 /* Not Zip64? Seek back to where we were and keep processing. */ |
1333 BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0); |
1515 BAIL_IF_MACRO(!io->seek(io, pos + 4), ERRPASS, 0); |
1382 |
1564 |
1383 return 1; /* made it. */ |
1565 return 1; /* made it. */ |
1384 } /* zip_parse_end_of_central_dir */ |
1566 } /* zip_parse_end_of_central_dir */ |
1385 |
1567 |
1386 |
1568 |
|
1569 static int zip_alloc_hashtable(ZIPinfo *info, const PHYSFS_uint64 entry_count) |
|
1570 { |
|
1571 size_t alloclen; |
|
1572 |
|
1573 info->hashBuckets = (size_t) (entry_count / 5); |
|
1574 if (!info->hashBuckets) |
|
1575 info->hashBuckets = 1; |
|
1576 |
|
1577 alloclen = info->hashBuckets * sizeof (ZIPentry *); |
|
1578 info->hash = (ZIPentry **) allocator.Malloc(alloclen); |
|
1579 BAIL_IF_MACRO(!info->hash, PHYSFS_ERR_OUT_OF_MEMORY, 0); |
|
1580 memset(info->hash, '\0', alloclen); |
|
1581 |
|
1582 return 1; |
|
1583 } /* zip_alloc_hashtable */ |
|
1584 |
|
1585 static void ZIP_closeArchive(void *opaque); |
|
1586 |
1387 static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) |
1587 static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, int forWriting) |
1388 { |
1588 { |
1389 ZIPinfo *info = NULL; |
1589 ZIPinfo *info = NULL; |
1390 PHYSFS_uint64 data_start; |
1590 PHYSFS_uint64 dstart; /* data start */ |
1391 PHYSFS_uint64 cent_dir_ofs; |
1591 PHYSFS_uint64 cdir_ofs; /* central dir offset */ |
|
1592 PHYSFS_uint64 entry_count; |
1392 |
1593 |
1393 assert(io != NULL); /* shouldn't ever happen. */ |
1594 assert(io != NULL); /* shouldn't ever happen. */ |
1394 |
1595 |
1395 BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL); |
1596 BAIL_IF_MACRO(forWriting, PHYSFS_ERR_READ_ONLY, NULL); |
1396 BAIL_IF_MACRO(!isZip(io), ERRPASS, NULL); |
1597 BAIL_IF_MACRO(!isZip(io), ERRPASS, NULL); |
1397 |
1598 |
1398 info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo)); |
1599 info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo)); |
1399 BAIL_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
1600 BAIL_IF_MACRO(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
1400 memset(info, '\0', sizeof (ZIPinfo)); |
1601 memset(info, '\0', sizeof (ZIPinfo)); |
|
1602 info->root.resolved = ZIP_DIRECTORY; |
1401 info->io = io; |
1603 info->io = io; |
1402 |
1604 |
1403 if (!zip_parse_end_of_central_dir(io, info, &data_start, ¢_dir_ofs)) |
1605 if (!zip_parse_end_of_central_dir(info, &dstart, &cdir_ofs, &entry_count)) |
1404 goto ZIP_openarchive_failed; |
1606 goto ZIP_openarchive_failed; |
1405 |
1607 else if (!zip_alloc_hashtable(info, entry_count)) |
1406 if (!zip_load_entries(io, info, data_start, cent_dir_ofs)) |
|
1407 goto ZIP_openarchive_failed; |
1608 goto ZIP_openarchive_failed; |
1408 |
1609 else if (!zip_load_entries(info, dstart, cdir_ofs, entry_count)) |
|
1610 goto ZIP_openarchive_failed; |
|
1611 |
|
1612 assert(info->root.sibling == NULL); |
1409 return info; |
1613 return info; |
1410 |
1614 |
1411 ZIP_openarchive_failed: |
1615 ZIP_openarchive_failed: |
1412 if (info != NULL) |
1616 info->io = NULL; /* don't let ZIP_closeArchive destroy (io). */ |
1413 allocator.Free(info); |
1617 ZIP_closeArchive(info); |
1414 |
|
1415 return NULL; |
1618 return NULL; |
1416 } /* ZIP_openArchive */ |
1619 } /* ZIP_openArchive */ |
1417 |
1620 |
1418 |
1621 |
1419 static PHYSFS_sint64 zip_find_start_of_dir(ZIPinfo *info, const char *path, |
1622 static void ZIP_enumerateFiles(void *opaque, const char *dname, |
1420 int stop_on_first_find) |
1623 PHYSFS_EnumFilesCallback cb, |
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) |
1624 const char *origdir, void *callbackdata) |
1493 { |
1625 { |
1494 ZIPinfo *info = ((ZIPinfo *) opaque); |
1626 ZIPinfo *info = ((ZIPinfo *) opaque); |
1495 PHYSFS_sint32 dlen, dlen_inc; |
1627 const ZIPentry *entry = zip_find_entry(info, dname); |
1496 PHYSFS_sint64 i, max; |
1628 if (entry && (entry->resolved == ZIP_DIRECTORY)) |
1497 |
1629 { |
1498 i = zip_find_start_of_dir(info, dname, 0); |
1630 for (entry = entry->children; entry; entry = entry->sibling) |
1499 if (i == -1) /* no such directory. */ |
1631 { |
1500 return; |
1632 const char *ptr = strrchr(entry->name, '/'); |
1501 |
1633 cb(callbackdata, origdir, ptr ? ptr + 1 : entry->name); |
1502 dlen = (PHYSFS_sint32) strlen(dname); |
1634 } /* for */ |
1503 if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */ |
1635 } /* if */ |
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 */ |
1636 } /* ZIP_enumerateFiles */ |
1534 |
1637 |
1535 |
1638 |
1536 static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry) |
1639 static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry) |
1537 { |
1640 { |
1558 |
1661 |
1559 return retval; |
1662 return retval; |
1560 } /* zip_get_io */ |
1663 } /* zip_get_io */ |
1561 |
1664 |
1562 |
1665 |
1563 static PHYSFS_Io *ZIP_openRead(PHYSFS_Dir *opaque, const char *fnm, |
1666 static PHYSFS_Io *ZIP_openRead(void *opaque, const char *filename) |
1564 int *fileExists) |
|
1565 { |
1667 { |
1566 PHYSFS_Io *retval = NULL; |
1668 PHYSFS_Io *retval = NULL; |
1567 ZIPinfo *info = (ZIPinfo *) opaque; |
1669 ZIPinfo *info = (ZIPinfo *) opaque; |
1568 ZIPentry *entry = zip_find_entry(info, fnm, NULL); |
1670 ZIPentry *entry = zip_find_entry(info, filename); |
1569 ZIPfileinfo *finfo = NULL; |
1671 ZIPfileinfo *finfo = NULL; |
1570 |
1672 PHYSFS_Io *io = NULL; |
1571 *fileExists = (entry != NULL); |
1673 PHYSFS_uint8 *password = NULL; |
|
1674 int i; |
|
1675 |
|
1676 /* if not found, see if maybe "$PASSWORD" is appended. */ |
|
1677 if ((!entry) && (info->has_crypto)) |
|
1678 { |
|
1679 const char *ptr = strrchr(filename, '$'); |
|
1680 if (ptr != NULL) |
|
1681 { |
|
1682 const PHYSFS_uint64 len = (PHYSFS_uint64) (ptr - filename); |
|
1683 char *str = (char *) __PHYSFS_smallAlloc(len + 1); |
|
1684 BAIL_IF_MACRO(!str, PHYSFS_ERR_OUT_OF_MEMORY, NULL); |
|
1685 memcpy(str, filename, len); |
|
1686 str[len] = '\0'; |
|
1687 entry = zip_find_entry(info, str); |
|
1688 __PHYSFS_smallFree(str); |
|
1689 password = (PHYSFS_uint8 *) (ptr + 1); |
|
1690 } /* if */ |
|
1691 } /* if */ |
|
1692 |
1572 BAIL_IF_MACRO(!entry, ERRPASS, NULL); |
1693 BAIL_IF_MACRO(!entry, ERRPASS, NULL); |
1573 |
1694 |
1574 retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); |
1695 retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); |
1575 GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1696 GOTO_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1576 |
1697 |
1577 finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); |
1698 finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); |
1578 GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1699 GOTO_IF_MACRO(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); |
1579 memset(finfo, '\0', sizeof (ZIPfileinfo)); |
1700 memset(finfo, '\0', sizeof (ZIPfileinfo)); |
1580 |
1701 |
1581 finfo->io = zip_get_io(info->io, info, entry); |
1702 io = zip_get_io(info->io, info, entry); |
1582 GOTO_IF_MACRO(!finfo->io, ERRPASS, ZIP_openRead_failed); |
1703 GOTO_IF_MACRO(!io, ERRPASS, ZIP_openRead_failed); |
|
1704 finfo->io = io; |
1583 finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry); |
1705 finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry); |
1584 initializeZStream(&finfo->stream); |
1706 initializeZStream(&finfo->stream); |
1585 |
1707 |
1586 if (finfo->entry->compression_method != COMPMETH_NONE) |
1708 if (finfo->entry->compression_method != COMPMETH_NONE) |
1587 { |
1709 { |
1617 |
1751 |
1618 return NULL; |
1752 return NULL; |
1619 } /* ZIP_openRead */ |
1753 } /* ZIP_openRead */ |
1620 |
1754 |
1621 |
1755 |
1622 static PHYSFS_Io *ZIP_openWrite(PHYSFS_Dir *opaque, const char *filename) |
1756 static PHYSFS_Io *ZIP_openWrite(void *opaque, const char *filename) |
1623 { |
1757 { |
1624 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1758 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1625 } /* ZIP_openWrite */ |
1759 } /* ZIP_openWrite */ |
1626 |
1760 |
1627 |
1761 |
1628 static PHYSFS_Io *ZIP_openAppend(PHYSFS_Dir *opaque, const char *filename) |
1762 static PHYSFS_Io *ZIP_openAppend(void *opaque, const char *filename) |
1629 { |
1763 { |
1630 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1764 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, NULL); |
1631 } /* ZIP_openAppend */ |
1765 } /* ZIP_openAppend */ |
1632 |
1766 |
1633 |
1767 |
1634 static void ZIP_closeArchive(PHYSFS_Dir *opaque) |
1768 static void ZIP_closeArchive(void *opaque) |
1635 { |
1769 { |
1636 ZIPinfo *zi = (ZIPinfo *) (opaque); |
1770 ZIPinfo *info = (ZIPinfo *) (opaque); |
1637 zi->io->destroy(zi->io); |
1771 |
1638 zip_free_entries(zi->entries, zi->entryCount); |
1772 if (!info) |
1639 allocator.Free(zi); |
1773 return; |
|
1774 |
|
1775 if (info->io) |
|
1776 info->io->destroy(info->io); |
|
1777 |
|
1778 assert(info->root.sibling == NULL); |
|
1779 assert(info->hash || (info->root.children == NULL)); |
|
1780 |
|
1781 if (info->hash) |
|
1782 { |
|
1783 size_t i; |
|
1784 for (i = 0; i < info->hashBuckets; i++) |
|
1785 { |
|
1786 ZIPentry *entry; |
|
1787 ZIPentry *next; |
|
1788 for (entry = info->hash[i]; entry; entry = next) |
|
1789 { |
|
1790 next = entry->hashnext; |
|
1791 allocator.Free(entry); |
|
1792 } /* for */ |
|
1793 } /* for */ |
|
1794 allocator.Free(info->hash); |
|
1795 } /* if */ |
|
1796 |
|
1797 allocator.Free(info); |
1640 } /* ZIP_closeArchive */ |
1798 } /* ZIP_closeArchive */ |
1641 |
1799 |
1642 |
1800 |
1643 static int ZIP_remove(PHYSFS_Dir *opaque, const char *name) |
1801 static int ZIP_remove(void *opaque, const char *name) |
1644 { |
1802 { |
1645 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1803 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1646 } /* ZIP_remove */ |
1804 } /* ZIP_remove */ |
1647 |
1805 |
1648 |
1806 |
1649 static int ZIP_mkdir(PHYSFS_Dir *opaque, const char *name) |
1807 static int ZIP_mkdir(void *opaque, const char *name) |
1650 { |
1808 { |
1651 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1809 BAIL_MACRO(PHYSFS_ERR_READ_ONLY, 0); |
1652 } /* ZIP_mkdir */ |
1810 } /* ZIP_mkdir */ |
1653 |
1811 |
1654 |
1812 |
1655 static int ZIP_stat(PHYSFS_Dir *opaque, const char *filename, int *exists, |
1813 static int ZIP_stat(void *opaque, const char *filename, PHYSFS_Stat *stat) |
1656 PHYSFS_Stat *stat) |
1814 { |
1657 { |
1815 ZIPinfo *info = (ZIPinfo *) opaque; |
1658 int isDir = 0; |
1816 const ZIPentry *entry = zip_find_entry(info, filename); |
1659 const ZIPinfo *info = (const ZIPinfo *) opaque; |
|
1660 const ZIPentry *entry = zip_find_entry(info, filename, &isDir); |
|
1661 |
1817 |
1662 /* !!! FIXME: does this need to resolve entries here? */ |
1818 /* !!! FIXME: does this need to resolve entries here? */ |
1663 |
1819 |
1664 *exists = isDir || (entry != 0); |
1820 if (entry == NULL) |
1665 if (!*exists) |
|
1666 return 0; |
1821 return 0; |
1667 |
1822 |
1668 if (isDir) |
1823 else if (entry->resolved == ZIP_DIRECTORY) |
1669 { |
1824 { |
1670 stat->filesize = 0; |
1825 stat->filesize = 0; |
1671 stat->filetype = PHYSFS_FILETYPE_DIRECTORY; |
1826 stat->filetype = PHYSFS_FILETYPE_DIRECTORY; |
1672 } /* if */ |
1827 } /* if */ |
1673 |
1828 |