Commit d51ea00a0cc955651804fe445bc84882cce28b53
0 parents
first import
git-svn-id: http://svn.net-core.org/repos/t-engine4@1 51575b47-30f0-44d4-a5cc-537603b46e54
Showing
7 changed files
with
4063 additions
and
0 deletions
Too many changes to show.
To preserve performance only 7 of 7+ files are displayed.
game/engine/Entity.lua
0 → 100644
1 | +module(..., package.seeall, class.make) | |
2 | + | |
3 | +local next_uid = 1 | |
4 | + | |
5 | +function _M:init(t) | |
6 | + t = t or {} | |
7 | + self.uid = next_uid | |
8 | + __uids[self.uid] = self | |
9 | + | |
10 | + self.display = t.display or '.' | |
11 | + self.color_r = t.color_r or 0 | |
12 | + self.color_g = t.color_g or 0 | |
13 | + self.color_b = t.color_b or 0 | |
14 | + self.block_sight = t.block_sight | |
15 | + | |
16 | + next_uid = next_uid + 1 | |
17 | +end | |
18 | + | |
19 | +-- If we are cloned we need a new uid | |
20 | +function _M:cloned() | |
21 | + self.uid = next_uid | |
22 | + __uids[self.uid] = self | |
23 | + next_uid = next_uid + 1 | |
24 | +end | |
25 | + | |
26 | +function _M:display() | |
27 | + return self.display, self.color_r, self.color_g, self.color_b | |
28 | +end | ... | ... |
game/engine/Map.lua
0 → 100644
1 | +local Entity = require "engine.Entity" | |
2 | + | |
3 | +module(..., package.seeall, class.make) | |
4 | + | |
5 | +function _M:init(w, h) | |
6 | + getmetatable(self).__call = _M.call | |
7 | + self.map = core.new_map(w, h) | |
8 | +end | |
9 | + | |
10 | +function _M:setCurrent() | |
11 | + self.map:setCurrent() | |
12 | +end | |
13 | + | |
14 | +function _M:call(x, y, pos, entity) | |
15 | + if entity then | |
16 | + self.map(x, y, pos, entity.uid) | |
17 | + else | |
18 | + return __uids[self.map(x, y, pos)] | |
19 | + end | |
20 | +end | ... | ... |
game/engine/class.lua
0 → 100644
1 | +module("class", package.seeall) | |
2 | + | |
3 | +local base = _G | |
4 | + | |
5 | +function make(c) | |
6 | + c.new = function(...) | |
7 | + local obj = {} | |
8 | + setmetatable(c, {__index=_M}) | |
9 | + setmetatable(obj, {__index=c}) | |
10 | + if obj.init then obj:init(...) end | |
11 | + return obj | |
12 | + end | |
13 | + return c | |
14 | +end | |
15 | + | |
16 | +function _M:clone(deep) | |
17 | + local n = {} | |
18 | + for k, e in pairs(self) do | |
19 | + n[k] = e | |
20 | + end | |
21 | + setmetatable(n, getmetatable(self)) | |
22 | + if n.cloned then n:cloned(self) end | |
23 | + return n | |
24 | +end | ... | ... |
game/engine/init.lua
0 → 100644
1 | +require "engine.class" | |
2 | +require "engine.Map" | |
3 | +require "engine.Entity" | |
4 | + | |
5 | +map = engine.Map.new(20, 20) | |
6 | + | |
7 | +local floor = engine.Entity.new{color_r=50, color_g=50, color_b=50} | |
8 | +local e1 = engine.Entity.new{color_r=255, block_sight=true} | |
9 | +local e2 = engine.Entity.new{color_g=255, block_sight=true} | |
10 | +local e3 = engine.Entity.new{color_b=255} | |
11 | +local e4 = e3:clone() | |
12 | +e4.color_r=255 | |
13 | + | |
14 | +for i = 0, 19 do for j = 0, 19 do | |
15 | + map(i, j, 1, floor) | |
16 | +end end | |
17 | + | |
18 | +map(8, 6, 1, e4) | |
19 | +map(8, 7, 1, e2) | |
20 | +map(8, 8, 1, e3) | |
21 | +map(9, 6, 1, e1) | |
22 | +map(9, 7, 1, e2) | |
23 | +map(9, 8, 1, e3) | |
24 | +map(10, 6, 1, e1) | |
25 | +map(10, 7, 1, e2) | |
26 | +map(10, 8, 1, e3) | |
27 | + | |
28 | +print(map(8, 8, 1)) | |
29 | + | |
30 | +map:setCurrent() | |
31 | + | |
32 | +--dofile("/game/modules/tome/") | ... | ... |
physfs/archivers/dir.c
0 → 100644
1 | +/* | |
2 | + * Standard directory I/O 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. | |
7 | + */ | |
8 | + | |
9 | +#include <stdio.h> | |
10 | +#include <stdlib.h> | |
11 | +#include <string.h> | |
12 | +#include "physfs.h" | |
13 | + | |
14 | +#define __PHYSICSFS_INTERNAL__ | |
15 | +#include "physfs_internal.h" | |
16 | + | |
17 | +static PHYSFS_sint64 DIR_read(fvoid *opaque, void *buffer, | |
18 | + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) | |
19 | +{ | |
20 | + PHYSFS_sint64 retval; | |
21 | + retval = __PHYSFS_platformRead(opaque, buffer, objSize, objCount); | |
22 | + return(retval); | |
23 | +} /* DIR_read */ | |
24 | + | |
25 | + | |
26 | +static PHYSFS_sint64 DIR_write(fvoid *opaque, const void *buffer, | |
27 | + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) | |
28 | +{ | |
29 | + PHYSFS_sint64 retval; | |
30 | + retval = __PHYSFS_platformWrite(opaque, buffer, objSize, objCount); | |
31 | + return(retval); | |
32 | +} /* DIR_write */ | |
33 | + | |
34 | + | |
35 | +static int DIR_eof(fvoid *opaque) | |
36 | +{ | |
37 | + return(__PHYSFS_platformEOF(opaque)); | |
38 | +} /* DIR_eof */ | |
39 | + | |
40 | + | |
41 | +static PHYSFS_sint64 DIR_tell(fvoid *opaque) | |
42 | +{ | |
43 | + return(__PHYSFS_platformTell(opaque)); | |
44 | +} /* DIR_tell */ | |
45 | + | |
46 | + | |
47 | +static int DIR_seek(fvoid *opaque, PHYSFS_uint64 offset) | |
48 | +{ | |
49 | + return(__PHYSFS_platformSeek(opaque, offset)); | |
50 | +} /* DIR_seek */ | |
51 | + | |
52 | + | |
53 | +static PHYSFS_sint64 DIR_fileLength(fvoid *opaque) | |
54 | +{ | |
55 | + return(__PHYSFS_platformFileLength(opaque)); | |
56 | +} /* DIR_fileLength */ | |
57 | + | |
58 | + | |
59 | +static int DIR_fileClose(fvoid *opaque) | |
60 | +{ | |
61 | + /* | |
62 | + * we manually flush the buffer, since that's the place a close will | |
63 | + * most likely fail, but that will leave the file handle in an undefined | |
64 | + * state if it fails. Flush failures we can recover from. | |
65 | + */ | |
66 | + BAIL_IF_MACRO(!__PHYSFS_platformFlush(opaque), NULL, 0); | |
67 | + BAIL_IF_MACRO(!__PHYSFS_platformClose(opaque), NULL, 0); | |
68 | + return(1); | |
69 | +} /* DIR_fileClose */ | |
70 | + | |
71 | + | |
72 | +static int DIR_isArchive(const char *filename, int forWriting) | |
73 | +{ | |
74 | + /* directories ARE archives in this driver... */ | |
75 | + return(__PHYSFS_platformIsDirectory(filename)); | |
76 | +} /* DIR_isArchive */ | |
77 | + | |
78 | + | |
79 | +static void *DIR_openArchive(const char *name, int forWriting) | |
80 | +{ | |
81 | + const char *dirsep = PHYSFS_getDirSeparator(); | |
82 | + char *retval = NULL; | |
83 | + size_t namelen = strlen(name); | |
84 | + size_t seplen = strlen(dirsep); | |
85 | + | |
86 | + /* !!! FIXME: when is this not called right before openArchive? */ | |
87 | + BAIL_IF_MACRO(!DIR_isArchive(name, forWriting), | |
88 | + ERR_UNSUPPORTED_ARCHIVE, 0); | |
89 | + | |
90 | + retval = allocator.Malloc(namelen + seplen + 1); | |
91 | + BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); | |
92 | + | |
93 | + /* make sure there's a dir separator at the end of the string */ | |
94 | + strcpy(retval, name); | |
95 | + if (strcmp((name + namelen) - seplen, dirsep) != 0) | |
96 | + strcat(retval, dirsep); | |
97 | + | |
98 | + return(retval); | |
99 | +} /* DIR_openArchive */ | |
100 | + | |
101 | + | |
102 | +static void DIR_enumerateFiles(dvoid *opaque, const char *dname, | |
103 | + int omitSymLinks, PHYSFS_EnumFilesCallback cb, | |
104 | + const char *origdir, void *callbackdata) | |
105 | +{ | |
106 | + char *d = __PHYSFS_platformCvtToDependent((char *)opaque, dname, NULL); | |
107 | + if (d != NULL) | |
108 | + { | |
109 | + __PHYSFS_platformEnumerateFiles(d, omitSymLinks, cb, | |
110 | + origdir, callbackdata); | |
111 | + allocator.Free(d); | |
112 | + } /* if */ | |
113 | +} /* DIR_enumerateFiles */ | |
114 | + | |
115 | + | |
116 | +static int DIR_exists(dvoid *opaque, const char *name) | |
117 | +{ | |
118 | + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); | |
119 | + int retval; | |
120 | + | |
121 | + BAIL_IF_MACRO(f == NULL, NULL, 0); | |
122 | + retval = __PHYSFS_platformExists(f); | |
123 | + allocator.Free(f); | |
124 | + return(retval); | |
125 | +} /* DIR_exists */ | |
126 | + | |
127 | + | |
128 | +static int DIR_isDirectory(dvoid *opaque, const char *name, int *fileExists) | |
129 | +{ | |
130 | + char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); | |
131 | + int retval = 0; | |
132 | + | |
133 | + BAIL_IF_MACRO(d == NULL, NULL, 0); | |
134 | + *fileExists = __PHYSFS_platformExists(d); | |
135 | + if (*fileExists) | |
136 | + retval = __PHYSFS_platformIsDirectory(d); | |
137 | + allocator.Free(d); | |
138 | + return(retval); | |
139 | +} /* DIR_isDirectory */ | |
140 | + | |
141 | + | |
142 | +static int DIR_isSymLink(dvoid *opaque, const char *name, int *fileExists) | |
143 | +{ | |
144 | + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); | |
145 | + int retval = 0; | |
146 | + | |
147 | + BAIL_IF_MACRO(f == NULL, NULL, 0); | |
148 | + *fileExists = __PHYSFS_platformExists(f); | |
149 | + if (*fileExists) | |
150 | + retval = __PHYSFS_platformIsSymLink(f); | |
151 | + allocator.Free(f); | |
152 | + return(retval); | |
153 | +} /* DIR_isSymLink */ | |
154 | + | |
155 | + | |
156 | +static PHYSFS_sint64 DIR_getLastModTime(dvoid *opaque, | |
157 | + const char *name, | |
158 | + int *fileExists) | |
159 | +{ | |
160 | + char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); | |
161 | + PHYSFS_sint64 retval = -1; | |
162 | + | |
163 | + BAIL_IF_MACRO(d == NULL, NULL, 0); | |
164 | + *fileExists = __PHYSFS_platformExists(d); | |
165 | + if (*fileExists) | |
166 | + retval = __PHYSFS_platformGetLastModTime(d); | |
167 | + allocator.Free(d); | |
168 | + return(retval); | |
169 | +} /* DIR_getLastModTime */ | |
170 | + | |
171 | + | |
172 | +static fvoid *doOpen(dvoid *opaque, const char *name, | |
173 | + void *(*openFunc)(const char *filename), | |
174 | + int *fileExists) | |
175 | +{ | |
176 | + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); | |
177 | + void *rc = NULL; | |
178 | + | |
179 | + BAIL_IF_MACRO(f == NULL, NULL, NULL); | |
180 | + | |
181 | + if (fileExists != NULL) | |
182 | + { | |
183 | + *fileExists = __PHYSFS_platformExists(f); | |
184 | + if (!(*fileExists)) | |
185 | + { | |
186 | + allocator.Free(f); | |
187 | + return(NULL); | |
188 | + } /* if */ | |
189 | + } /* if */ | |
190 | + | |
191 | + rc = openFunc(f); | |
192 | + allocator.Free(f); | |
193 | + | |
194 | + return((fvoid *) rc); | |
195 | +} /* doOpen */ | |
196 | + | |
197 | + | |
198 | +static fvoid *DIR_openRead(dvoid *opaque, const char *fnm, int *exist) | |
199 | +{ | |
200 | + return(doOpen(opaque, fnm, __PHYSFS_platformOpenRead, exist)); | |
201 | +} /* DIR_openRead */ | |
202 | + | |
203 | + | |
204 | +static fvoid *DIR_openWrite(dvoid *opaque, const char *filename) | |
205 | +{ | |
206 | + return(doOpen(opaque, filename, __PHYSFS_platformOpenWrite, NULL)); | |
207 | +} /* DIR_openWrite */ | |
208 | + | |
209 | + | |
210 | +static fvoid *DIR_openAppend(dvoid *opaque, const char *filename) | |
211 | +{ | |
212 | + return(doOpen(opaque, filename, __PHYSFS_platformOpenAppend, NULL)); | |
213 | +} /* DIR_openAppend */ | |
214 | + | |
215 | + | |
216 | +static int DIR_remove(dvoid *opaque, const char *name) | |
217 | +{ | |
218 | + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); | |
219 | + int retval; | |
220 | + | |
221 | + BAIL_IF_MACRO(f == NULL, NULL, 0); | |
222 | + retval = __PHYSFS_platformDelete(f); | |
223 | + allocator.Free(f); | |
224 | + return(retval); | |
225 | +} /* DIR_remove */ | |
226 | + | |
227 | + | |
228 | +static int DIR_mkdir(dvoid *opaque, const char *name) | |
229 | +{ | |
230 | + char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); | |
231 | + int retval; | |
232 | + | |
233 | + BAIL_IF_MACRO(f == NULL, NULL, 0); | |
234 | + retval = __PHYSFS_platformMkDir(f); | |
235 | + allocator.Free(f); | |
236 | + return(retval); | |
237 | +} /* DIR_mkdir */ | |
238 | + | |
239 | + | |
240 | +static void DIR_dirClose(dvoid *opaque) | |
241 | +{ | |
242 | + allocator.Free(opaque); | |
243 | +} /* DIR_dirClose */ | |
244 | + | |
245 | + | |
246 | + | |
247 | +const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR = | |
248 | +{ | |
249 | + "", | |
250 | + DIR_ARCHIVE_DESCRIPTION, | |
251 | + "Ryan C. Gordon <icculus@icculus.org>", | |
252 | + "http://icculus.org/physfs/", | |
253 | +}; | |
254 | + | |
255 | + | |
256 | + | |
257 | +const PHYSFS_Archiver __PHYSFS_Archiver_DIR = | |
258 | +{ | |
259 | + &__PHYSFS_ArchiveInfo_DIR, | |
260 | + DIR_isArchive, /* isArchive() method */ | |
261 | + DIR_openArchive, /* openArchive() method */ | |
262 | + DIR_enumerateFiles, /* enumerateFiles() method */ | |
263 | + DIR_exists, /* exists() method */ | |
264 | + DIR_isDirectory, /* isDirectory() method */ | |
265 | + DIR_isSymLink, /* isSymLink() method */ | |
266 | + DIR_getLastModTime, /* getLastModTime() method */ | |
267 | + DIR_openRead, /* openRead() method */ | |
268 | + DIR_openWrite, /* openWrite() method */ | |
269 | + DIR_openAppend, /* openAppend() method */ | |
270 | + DIR_remove, /* remove() method */ | |
271 | + DIR_mkdir, /* mkdir() method */ | |
272 | + DIR_dirClose, /* dirClose() method */ | |
273 | + DIR_read, /* read() method */ | |
274 | + DIR_write, /* write() method */ | |
275 | + DIR_eof, /* eof() method */ | |
276 | + DIR_tell, /* tell() method */ | |
277 | + DIR_seek, /* seek() method */ | |
278 | + DIR_fileLength, /* fileLength() method */ | |
279 | + DIR_fileClose /* fileClose() method */ | |
280 | +}; | |
281 | + | |
282 | +/* end of dir.c ... */ | |
283 | + | ... | ... |
physfs/archivers/zip.c
0 → 100644
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 | +#if (defined PHYSFS_SUPPORTS_ZIP) | |
11 | + | |
12 | +#include <stdio.h> | |
13 | +#include <stdlib.h> | |
14 | +#include <string.h> | |
15 | +#ifndef _WIN32_WCE | |
16 | +#include <errno.h> | |
17 | +#include <time.h> | |
18 | +#endif | |
19 | +#include "physfs.h" | |
20 | +#include "zlib.h" | |
21 | + | |
22 | +#define __PHYSICSFS_INTERNAL__ | |
23 | +#include "physfs_internal.h" | |
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_uint32 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_uint32 compressed_size; /* compressed size */ | |
73 | + PHYSFS_uint32 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 | + char *archiveName; /* path to ZIP in platform-dependent notation. */ | |
83 | + PHYSFS_uint16 entryCount; /* Number of files in ZIP. */ | |
84 | + ZIPentry *entries; /* info on all files in ZIP. */ | |
85 | +} ZIPinfo; | |
86 | + | |
87 | +/* | |
88 | + * One ZIPfileinfo is kept for each open file in a ZIP archive. | |
89 | + */ | |
90 | +typedef struct | |
91 | +{ | |
92 | + ZIPentry *entry; /* Info on file. */ | |
93 | + void *handle; /* physical file handle. */ | |
94 | + PHYSFS_uint32 compressed_position; /* offset in compressed data. */ | |
95 | + PHYSFS_uint32 uncompressed_position; /* tell() position. */ | |
96 | + PHYSFS_uint8 *buffer; /* decompression buffer. */ | |
97 | + z_stream stream; /* zlib stream state. */ | |
98 | +} ZIPfileinfo; | |
99 | + | |
100 | + | |
101 | +/* Magic numbers... */ | |
102 | +#define ZIP_LOCAL_FILE_SIG 0x04034b50 | |
103 | +#define ZIP_CENTRAL_DIR_SIG 0x02014b50 | |
104 | +#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50 | |
105 | + | |
106 | +/* compression methods... */ | |
107 | +#define COMPMETH_NONE 0 | |
108 | +/* ...and others... */ | |
109 | + | |
110 | + | |
111 | +#define UNIX_FILETYPE_MASK 0170000 | |
112 | +#define UNIX_FILETYPE_SYMLINK 0120000 | |
113 | + | |
114 | + | |
115 | +/* | |
116 | + * Bridge physfs allocation functions to zlib's format... | |
117 | + */ | |
118 | +static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size) | |
119 | +{ | |
120 | + return(((PHYSFS_Allocator *) opaque)->Malloc(items * size)); | |
121 | +} /* zlibPhysfsAlloc */ | |
122 | + | |
123 | +/* | |
124 | + * Bridge physfs allocation functions to zlib's format... | |
125 | + */ | |
126 | +static void zlibPhysfsFree(voidpf opaque, voidpf address) | |
127 | +{ | |
128 | + ((PHYSFS_Allocator *) opaque)->Free(address); | |
129 | +} /* zlibPhysfsFree */ | |
130 | + | |
131 | + | |
132 | +/* | |
133 | + * Construct a new z_stream to a sane state. | |
134 | + */ | |
135 | +static void initializeZStream(z_stream *pstr) | |
136 | +{ | |
137 | + memset(pstr, '\0', sizeof (z_stream)); | |
138 | + pstr->zalloc = zlibPhysfsAlloc; | |
139 | + pstr->zfree = zlibPhysfsFree; | |
140 | + pstr->opaque = &allocator; | |
141 | +} /* initializeZStream */ | |
142 | + | |
143 | + | |
144 | +static const char *zlib_error_string(int rc) | |
145 | +{ | |
146 | + switch (rc) | |
147 | + { | |
148 | + case Z_OK: return(NULL); /* not an error. */ | |
149 | + case Z_STREAM_END: return(NULL); /* not an error. */ | |
150 | +#ifndef _WIN32_WCE | |
151 | + case Z_ERRNO: return(strerror(errno)); | |
152 | +#endif | |
153 | + case Z_NEED_DICT: return(ERR_NEED_DICT); | |
154 | + case Z_DATA_ERROR: return(ERR_DATA_ERROR); | |
155 | + case Z_MEM_ERROR: return(ERR_MEMORY_ERROR); | |
156 | + case Z_BUF_ERROR: return(ERR_BUFFER_ERROR); | |
157 | + case Z_VERSION_ERROR: return(ERR_VERSION_ERROR); | |
158 | + default: return(ERR_UNKNOWN_ERROR); | |
159 | + } /* switch */ | |
160 | + | |
161 | + return(NULL); | |
162 | +} /* zlib_error_string */ | |
163 | + | |
164 | + | |
165 | +/* | |
166 | + * Wrap all zlib calls in this, so the physfs error state is set appropriately. | |
167 | + */ | |
168 | +static int zlib_err(int rc) | |
169 | +{ | |
170 | + const char *str = zlib_error_string(rc); | |
171 | + if (str != NULL) | |
172 | + __PHYSFS_setError(str); | |
173 | + return(rc); | |
174 | +} /* zlib_err */ | |
175 | + | |
176 | + | |
177 | +/* | |
178 | + * Read an unsigned 32-bit int and swap to native byte order. | |
179 | + */ | |
180 | +static int readui32(void *in, PHYSFS_uint32 *val) | |
181 | +{ | |
182 | + PHYSFS_uint32 v; | |
183 | + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); | |
184 | + *val = PHYSFS_swapULE32(v); | |
185 | + return(1); | |
186 | +} /* readui32 */ | |
187 | + | |
188 | + | |
189 | +/* | |
190 | + * Read an unsigned 16-bit int and swap to native byte order. | |
191 | + */ | |
192 | +static int readui16(void *in, PHYSFS_uint16 *val) | |
193 | +{ | |
194 | + PHYSFS_uint16 v; | |
195 | + BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0); | |
196 | + *val = PHYSFS_swapULE16(v); | |
197 | + return(1); | |
198 | +} /* readui16 */ | |
199 | + | |
200 | + | |
201 | +static PHYSFS_sint64 ZIP_read(fvoid *opaque, void *buf, | |
202 | + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) | |
203 | +{ | |
204 | + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; | |
205 | + ZIPentry *entry = finfo->entry; | |
206 | + PHYSFS_sint64 retval = 0; | |
207 | + PHYSFS_sint64 maxread = ((PHYSFS_sint64) objSize) * objCount; | |
208 | + PHYSFS_sint64 avail = entry->uncompressed_size - | |
209 | + finfo->uncompressed_position; | |
210 | + | |
211 | + BAIL_IF_MACRO(maxread == 0, NULL, 0); /* quick rejection. */ | |
212 | + | |
213 | + if (avail < maxread) | |
214 | + { | |
215 | + maxread = avail - (avail % objSize); | |
216 | + objCount = (PHYSFS_uint32) (maxread / objSize); | |
217 | + BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */ | |
218 | + __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */ | |
219 | + } /* if */ | |
220 | + | |
221 | + if (entry->compression_method == COMPMETH_NONE) | |
222 | + { | |
223 | + retval = __PHYSFS_platformRead(finfo->handle, buf, objSize, objCount); | |
224 | + } /* if */ | |
225 | + | |
226 | + else | |
227 | + { | |
228 | + finfo->stream.next_out = buf; | |
229 | + finfo->stream.avail_out = objSize * objCount; | |
230 | + | |
231 | + while (retval < maxread) | |
232 | + { | |
233 | + PHYSFS_uint32 before = finfo->stream.total_out; | |
234 | + int rc; | |
235 | + | |
236 | + if (finfo->stream.avail_in == 0) | |
237 | + { | |
238 | + PHYSFS_sint64 br; | |
239 | + | |
240 | + br = entry->compressed_size - finfo->compressed_position; | |
241 | + if (br > 0) | |
242 | + { | |
243 | + if (br > ZIP_READBUFSIZE) | |
244 | + br = ZIP_READBUFSIZE; | |
245 | + | |
246 | + br = __PHYSFS_platformRead(finfo->handle, | |
247 | + finfo->buffer, | |
248 | + 1, (PHYSFS_uint32) br); | |
249 | + if (br <= 0) | |
250 | + break; | |
251 | + | |
252 | + finfo->compressed_position += (PHYSFS_uint32) br; | |
253 | + finfo->stream.next_in = finfo->buffer; | |
254 | + finfo->stream.avail_in = (PHYSFS_uint32) br; | |
255 | + } /* if */ | |
256 | + } /* if */ | |
257 | + | |
258 | + rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH)); | |
259 | + retval += (finfo->stream.total_out - before); | |
260 | + | |
261 | + if (rc != Z_OK) | |
262 | + break; | |
263 | + } /* while */ | |
264 | + | |
265 | + retval /= objSize; | |
266 | + } /* else */ | |
267 | + | |
268 | + if (retval > 0) | |
269 | + finfo->uncompressed_position += (PHYSFS_uint32) (retval * objSize); | |
270 | + | |
271 | + return(retval); | |
272 | +} /* ZIP_read */ | |
273 | + | |
274 | + | |
275 | +static PHYSFS_sint64 ZIP_write(fvoid *opaque, const void *buf, | |
276 | + PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) | |
277 | +{ | |
278 | + BAIL_MACRO(ERR_NOT_SUPPORTED, -1); | |
279 | +} /* ZIP_write */ | |
280 | + | |
281 | + | |
282 | +static int ZIP_eof(fvoid *opaque) | |
283 | +{ | |
284 | + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; | |
285 | + return(finfo->uncompressed_position >= finfo->entry->uncompressed_size); | |
286 | +} /* ZIP_eof */ | |
287 | + | |
288 | + | |
289 | +static PHYSFS_sint64 ZIP_tell(fvoid *opaque) | |
290 | +{ | |
291 | + return(((ZIPfileinfo *) opaque)->uncompressed_position); | |
292 | +} /* ZIP_tell */ | |
293 | + | |
294 | + | |
295 | +static int ZIP_seek(fvoid *opaque, PHYSFS_uint64 offset) | |
296 | +{ | |
297 | + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; | |
298 | + ZIPentry *entry = finfo->entry; | |
299 | + void *in = finfo->handle; | |
300 | + | |
301 | + BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0); | |
302 | + | |
303 | + if (entry->compression_method == COMPMETH_NONE) | |
304 | + { | |
305 | + PHYSFS_sint64 newpos = offset + entry->offset; | |
306 | + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, newpos), NULL, 0); | |
307 | + finfo->uncompressed_position = (PHYSFS_uint32) offset; | |
308 | + } /* if */ | |
309 | + | |
310 | + else | |
311 | + { | |
312 | + /* | |
313 | + * If seeking backwards, we need to redecode the file | |
314 | + * from the start and throw away the compressed bits until we hit | |
315 | + * the offset we need. If seeking forward, we still need to | |
316 | + * decode, but we don't rewind first. | |
317 | + */ | |
318 | + if (offset < finfo->uncompressed_position) | |
319 | + { | |
320 | + /* we do a copy so state is sane if inflateInit2() fails. */ | |
321 | + z_stream str; | |
322 | + initializeZStream(&str); | |
323 | + if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK) | |
324 | + return(0); | |
325 | + | |
326 | + if (!__PHYSFS_platformSeek(in, entry->offset)) | |
327 | + return(0); | |
328 | + | |
329 | + inflateEnd(&finfo->stream); | |
330 | + memcpy(&finfo->stream, &str, sizeof (z_stream)); | |
331 | + finfo->uncompressed_position = finfo->compressed_position = 0; | |
332 | + } /* if */ | |
333 | + | |
334 | + while (finfo->uncompressed_position != offset) | |
335 | + { | |
336 | + PHYSFS_uint8 buf[512]; | |
337 | + PHYSFS_uint32 maxread; | |
338 | + | |
339 | + maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position); | |
340 | + if (maxread > sizeof (buf)) | |
341 | + maxread = sizeof (buf); | |
342 | + | |
343 | + if (ZIP_read(finfo, buf, maxread, 1) != 1) | |
344 | + return(0); | |
345 | + } /* while */ | |
346 | + } /* else */ | |
347 | + | |
348 | + return(1); | |
349 | +} /* ZIP_seek */ | |
350 | + | |
351 | + | |
352 | +static PHYSFS_sint64 ZIP_fileLength(fvoid *opaque) | |
353 | +{ | |
354 | + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; | |
355 | + return(finfo->entry->uncompressed_size); | |
356 | +} /* ZIP_fileLength */ | |
357 | + | |
358 | + | |
359 | +static int ZIP_fileClose(fvoid *opaque) | |
360 | +{ | |
361 | + ZIPfileinfo *finfo = (ZIPfileinfo *) opaque; | |
362 | + BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0); | |
363 | + | |
364 | + if (finfo->entry->compression_method != COMPMETH_NONE) | |
365 | + inflateEnd(&finfo->stream); | |
366 | + | |
367 | + if (finfo->buffer != NULL) | |
368 | + allocator.Free(finfo->buffer); | |
369 | + | |
370 | + allocator.Free(finfo); | |
371 | + return(1); | |
372 | +} /* ZIP_fileClose */ | |
373 | + | |
374 | + | |
375 | +static PHYSFS_sint64 zip_find_end_of_central_dir(void *in, PHYSFS_sint64 *len) | |
376 | +{ | |
377 | + PHYSFS_uint8 buf[256]; | |
378 | + PHYSFS_sint32 i = 0; | |
379 | + PHYSFS_sint64 filelen; | |
380 | + PHYSFS_sint64 filepos; | |
381 | + PHYSFS_sint32 maxread; | |
382 | + PHYSFS_sint32 totalread = 0; | |
383 | + int found = 0; | |
384 | + PHYSFS_uint32 extra = 0; | |
385 | + | |
386 | + filelen = __PHYSFS_platformFileLength(in); | |
387 | + BAIL_IF_MACRO(filelen == -1, NULL, 0); /* !!! FIXME: unlocalized string */ | |
388 | + BAIL_IF_MACRO(filelen > 0xFFFFFFFF, "ZIP bigger than 2 gigs?!", 0); | |
389 | + | |
390 | + /* | |
391 | + * Jump to the end of the file and start reading backwards. | |
392 | + * The last thing in the file is the zipfile comment, which is variable | |
393 | + * length, and the field that specifies its size is before it in the | |
394 | + * file (argh!)...this means that we need to scan backwards until we | |
395 | + * hit the end-of-central-dir signature. We can then sanity check that | |
396 | + * the comment was as big as it should be to make sure we're in the | |
397 | + * right place. The comment length field is 16 bits, so we can stop | |
398 | + * searching for that signature after a little more than 64k at most, | |
399 | + * and call it a corrupted zipfile. | |
400 | + */ | |
401 | + | |
402 | + if (sizeof (buf) < filelen) | |
403 | + { | |
404 | + filepos = filelen - sizeof (buf); | |
405 | + maxread = sizeof (buf); | |
406 | + } /* if */ | |
407 | + else | |
408 | + { | |
409 | + filepos = 0; | |
410 | + maxread = (PHYSFS_uint32) filelen; | |
411 | + } /* else */ | |
412 | + | |
413 | + while ((totalread < filelen) && (totalread < 65557)) | |
414 | + { | |
415 | + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, filepos), NULL, -1); | |
416 | + | |
417 | + /* make sure we catch a signature between buffers. */ | |
418 | + if (totalread != 0) | |
419 | + { | |
420 | + if (__PHYSFS_platformRead(in, buf, maxread - 4, 1) != 1) | |
421 | + return(-1); | |
422 | + *((PHYSFS_uint32 *) (&buf[maxread - 4])) = extra; | |
423 | + totalread += maxread - 4; | |
424 | + } /* if */ | |
425 | + else | |
426 | + { | |
427 | + if (__PHYSFS_platformRead(in, buf, maxread, 1) != 1) | |
428 | + return(-1); | |
429 | + totalread += maxread; | |
430 | + } /* else */ | |
431 | + | |
432 | + extra = *((PHYSFS_uint32 *) (&buf[0])); | |
433 | + | |
434 | + for (i = maxread - 4; i > 0; i--) | |
435 | + { | |
436 | + if ((buf[i + 0] == 0x50) && | |
437 | + (buf[i + 1] == 0x4B) && | |
438 | + (buf[i + 2] == 0x05) && | |
439 | + (buf[i + 3] == 0x06) ) | |
440 | + { | |
441 | + found = 1; /* that's the signature! */ | |
442 | + break; | |
443 | + } /* if */ | |
444 | + } /* for */ | |
445 | + | |
446 | + if (found) | |
447 | + break; | |
448 | + | |
449 | + filepos -= (maxread - 4); | |
450 | + if (filepos < 0) | |
451 | + filepos = 0; | |
452 | + } /* while */ | |
453 | + | |
454 | + BAIL_IF_MACRO(!found, ERR_NOT_AN_ARCHIVE, -1); | |
455 | + | |
456 | + if (len != NULL) | |
457 | + *len = filelen; | |
458 | + | |
459 | + return(filepos + i); | |
460 | +} /* zip_find_end_of_central_dir */ | |
461 | + | |
462 | + | |
463 | +static int ZIP_isArchive(const char *filename, int forWriting) | |
464 | +{ | |
465 | + PHYSFS_uint32 sig; | |
466 | + int retval = 0; | |
467 | + void *in; | |
468 | + | |
469 | + in = __PHYSFS_platformOpenRead(filename); | |
470 | + BAIL_IF_MACRO(in == NULL, NULL, 0); | |
471 | + | |
472 | + /* | |
473 | + * The first thing in a zip file might be the signature of the | |
474 | + * first local file record, so it makes for a quick determination. | |
475 | + */ | |
476 | + if (readui32(in, &sig)) | |
477 | + { | |
478 | + retval = (sig == ZIP_LOCAL_FILE_SIG); | |
479 | + if (!retval) | |
480 | + { | |
481 | + /* | |
482 | + * No sig...might be a ZIP with data at the start | |
483 | + * (a self-extracting executable, etc), so we'll have to do | |
484 | + * it the hard way... | |
485 | + */ | |
486 | + retval = (zip_find_end_of_central_dir(in, NULL) != -1); | |
487 | + } /* if */ | |
488 | + } /* if */ | |
489 | + | |
490 | + __PHYSFS_platformClose(in); | |
491 | + return(retval); | |
492 | +} /* ZIP_isArchive */ | |
493 | + | |
494 | + | |
495 | +static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max) | |
496 | +{ | |
497 | + PHYSFS_uint32 i; | |
498 | + for (i = 0; i < max; i++) | |
499 | + { | |
500 | + ZIPentry *entry = &entries[i]; | |
501 | + if (entry->name != NULL) | |
502 | + allocator.Free(entry->name); | |
503 | + } /* for */ | |
504 | + | |
505 | + allocator.Free(entries); | |
506 | +} /* zip_free_entries */ | |
507 | + | |
508 | + | |
509 | +/* | |
510 | + * This will find the ZIPentry associated with a path in platform-independent | |
511 | + * notation. Directories don't have ZIPentries associated with them, but | |
512 | + * (*isDir) will be set to non-zero if a dir was hit. | |
513 | + */ | |
514 | +static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path, int *isDir) | |
515 | +{ | |
516 | + ZIPentry *a = info->entries; | |
517 | + PHYSFS_sint32 pathlen = strlen(path); | |
518 | + PHYSFS_sint32 lo = 0; | |
519 | + PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1); | |
520 | + PHYSFS_sint32 middle; | |
521 | + const char *thispath = NULL; | |
522 | + int rc; | |
523 | + | |
524 | + while (lo <= hi) | |
525 | + { | |
526 | + middle = lo + ((hi - lo) / 2); | |
527 | + thispath = a[middle].name; | |
528 | + rc = strncmp(path, thispath, pathlen); | |
529 | + | |
530 | + if (rc > 0) | |
531 | + lo = middle + 1; | |
532 | + | |
533 | + else if (rc < 0) | |
534 | + hi = middle - 1; | |
535 | + | |
536 | + else /* substring match...might be dir or entry or nothing. */ | |
537 | + { | |
538 | + if (isDir != NULL) | |
539 | + { | |
540 | + *isDir = (thispath[pathlen] == '/'); | |
541 | + if (*isDir) | |
542 | + return(NULL); | |
543 | + } /* if */ | |
544 | + | |
545 | + if (thispath[pathlen] == '\0') /* found entry? */ | |
546 | + return(&a[middle]); | |
547 | + else | |
548 | + hi = middle - 1; /* adjust search params, try again. */ | |
549 | + } /* if */ | |
550 | + } /* while */ | |
551 | + | |
552 | + if (isDir != NULL) | |
553 | + *isDir = 0; | |
554 | + | |
555 | + BAIL_MACRO(ERR_NO_SUCH_FILE, NULL); | |
556 | +} /* zip_find_entry */ | |
557 | + | |
558 | + | |
559 | +/* Convert paths from old, buggy DOS zippers... */ | |
560 | +static void zip_convert_dos_path(ZIPentry *entry, char *path) | |
561 | +{ | |
562 | + PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF); | |
563 | + if (hosttype == 0) /* FS_FAT_ */ | |
564 | + { | |
565 | + while (*path) | |
566 | + { | |
567 | + if (*path == '\\') | |
568 | + *path = '/'; | |
569 | + path++; | |
570 | + } /* while */ | |
571 | + } /* if */ | |
572 | +} /* zip_convert_dos_path */ | |
573 | + | |
574 | + | |
575 | +static void zip_expand_symlink_path(char *path) | |
576 | +{ | |
577 | + char *ptr = path; | |
578 | + char *prevptr = path; | |
579 | + | |
580 | + while (1) | |
581 | + { | |
582 | + ptr = strchr(ptr, '/'); | |
583 | + if (ptr == NULL) | |
584 | + break; | |
585 | + | |
586 | + if (*(ptr + 1) == '.') | |
587 | + { | |
588 | + if (*(ptr + 2) == '/') | |
589 | + { | |
590 | + /* current dir in middle of string: ditch it. */ | |
591 | + memmove(ptr, ptr + 2, strlen(ptr + 2) + 1); | |
592 | + } /* else if */ | |
593 | + | |
594 | + else if (*(ptr + 2) == '\0') | |
595 | + { | |
596 | + /* current dir at end of string: ditch it. */ | |
597 | + *ptr = '\0'; | |
598 | + } /* else if */ | |
599 | + | |
600 | + else if (*(ptr + 2) == '.') | |
601 | + { | |
602 | + if (*(ptr + 3) == '/') | |
603 | + { | |
604 | + /* parent dir in middle: move back one, if possible. */ | |
605 | + memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1); | |
606 | + ptr = prevptr; | |
607 | + while (prevptr != path) | |
608 | + { | |
609 | + prevptr--; | |
610 | + if (*prevptr == '/') | |
611 | + { | |
612 | + prevptr++; | |
613 | + break; | |
614 | + } /* if */ | |
615 | + } /* while */ | |
616 | + } /* if */ | |
617 | + | |
618 | + if (*(ptr + 3) == '\0') | |
619 | + { | |
620 | + /* parent dir at end: move back one, if possible. */ | |
621 | + *prevptr = '\0'; | |
622 | + } /* if */ | |
623 | + } /* if */ | |
624 | + } /* if */ | |
625 | + else | |
626 | + { | |
627 | + prevptr = ptr; | |
628 | + } /* else */ | |
629 | + } /* while */ | |
630 | +} /* zip_expand_symlink_path */ | |
631 | + | |
632 | +/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */ | |
633 | +static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry); | |
634 | + | |
635 | +/* | |
636 | + * Look for the entry named by (path). If it exists, resolve it, and return | |
637 | + * a pointer to that entry. If it's another symlink, keep resolving until you | |
638 | + * hit a real file and then return a pointer to the final non-symlink entry. | |
639 | + * If there's a problem, return NULL. (path) is always free()'d by this | |
640 | + * function. | |
641 | + */ | |
642 | +static ZIPentry *zip_follow_symlink(void *in, ZIPinfo *info, char *path) | |
643 | +{ | |
644 | + ZIPentry *entry; | |
645 | + | |
646 | + zip_expand_symlink_path(path); | |
647 | + entry = zip_find_entry(info, path, NULL); | |
648 | + if (entry != NULL) | |
649 | + { | |
650 | + if (!zip_resolve(in, info, entry)) /* recursive! */ | |
651 | + entry = NULL; | |
652 | + else | |
653 | + { | |
654 | + if (entry->symlink != NULL) | |
655 | + entry = entry->symlink; | |
656 | + } /* else */ | |
657 | + } /* if */ | |
658 | + | |
659 | + allocator.Free(path); | |
660 | + return(entry); | |
661 | +} /* zip_follow_symlink */ | |
662 | + | |
663 | + | |
664 | +static int zip_resolve_symlink(void *in, ZIPinfo *info, ZIPentry *entry) | |
665 | +{ | |
666 | + char *path; | |
667 | + PHYSFS_uint32 size = entry->uncompressed_size; | |
668 | + int rc = 0; | |
669 | + | |
670 | + /* | |
671 | + * We've already parsed the local file header of the symlink at this | |
672 | + * point. Now we need to read the actual link from the file data and | |
673 | + * follow it. | |
674 | + */ | |
675 | + | |
676 | + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); | |
677 | + | |
678 | + path = (char *) allocator.Malloc(size + 1); | |
679 | + BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, 0); | |
680 | + | |
681 | + if (entry->compression_method == COMPMETH_NONE) | |
682 | + rc = (__PHYSFS_platformRead(in, path, size, 1) == 1); | |
683 | + | |
684 | + else /* symlink target path is compressed... */ | |
685 | + { | |
686 | + z_stream stream; | |
687 | + PHYSFS_uint32 complen = entry->compressed_size; | |
688 | + PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen); | |
689 | + if (compressed != NULL) | |
690 | + { | |
691 | + if (__PHYSFS_platformRead(in, compressed, complen, 1) == 1) | |
692 | + { | |
693 | + initializeZStream(&stream); | |
694 | + stream.next_in = compressed; | |
695 | + stream.avail_in = complen; | |
696 | + stream.next_out = (unsigned char *) path; | |
697 | + stream.avail_out = size; | |
698 | + if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK) | |
699 | + { | |
700 | + rc = zlib_err(inflate(&stream, Z_FINISH)); | |
701 | + inflateEnd(&stream); | |
702 | + | |
703 | + /* both are acceptable outcomes... */ | |
704 | + rc = ((rc == Z_OK) || (rc == Z_STREAM_END)); | |
705 | + } /* if */ | |
706 | + } /* if */ | |
707 | + __PHYSFS_smallFree(compressed); | |
708 | + } /* if */ | |
709 | + } /* else */ | |
710 | + | |
711 | + if (!rc) | |
712 | + allocator.Free(path); | |
713 | + else | |
714 | + { | |
715 | + path[entry->uncompressed_size] = '\0'; /* null-terminate it. */ | |
716 | + zip_convert_dos_path(entry, path); | |
717 | + entry->symlink = zip_follow_symlink(in, info, path); | |
718 | + } /* else */ | |
719 | + | |
720 | + return(entry->symlink != NULL); | |
721 | +} /* zip_resolve_symlink */ | |
722 | + | |
723 | + | |
724 | +/* | |
725 | + * Parse the local file header of an entry, and update entry->offset. | |
726 | + */ | |
727 | +static int zip_parse_local(void *in, ZIPentry *entry) | |
728 | +{ | |
729 | + PHYSFS_uint32 ui32; | |
730 | + PHYSFS_uint16 ui16; | |
731 | + PHYSFS_uint16 fnamelen; | |
732 | + PHYSFS_uint16 extralen; | |
733 | + | |
734 | + /* | |
735 | + * crc and (un)compressed_size are always zero if this is a "JAR" | |
736 | + * archive created with Sun's Java tools, apparently. We only | |
737 | + * consider this archive corrupted if those entries don't match and | |
738 | + * aren't zero. That seems to work well. | |
739 | + */ | |
740 | + | |
741 | + BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0); | |
742 | + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); | |
743 | + BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0); | |
744 | + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); | |
745 | + BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0); | |
746 | + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits. */ | |
747 | + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); | |
748 | + BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0); | |
749 | + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); /* date/time */ | |
750 | + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); | |
751 | + BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), ERR_CORRUPTED, 0); | |
752 | + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); | |
753 | + BAIL_IF_MACRO(ui32 && (ui32 != entry->compressed_size), ERR_CORRUPTED, 0); | |
754 | + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); | |
755 | + BAIL_IF_MACRO(ui32 && (ui32 != entry->uncompressed_size),ERR_CORRUPTED,0); | |
756 | + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); | |
757 | + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); | |
758 | + | |
759 | + entry->offset += fnamelen + extralen + 30; | |
760 | + return(1); | |
761 | +} /* zip_parse_local */ | |
762 | + | |
763 | + | |
764 | +static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry) | |
765 | +{ | |
766 | + int retval = 1; | |
767 | + ZipResolveType resolve_type = entry->resolved; | |
768 | + | |
769 | + /* Don't bother if we've failed to resolve this entry before. */ | |
770 | + BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, ERR_CORRUPTED, 0); | |
771 | + BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, ERR_CORRUPTED, 0); | |
772 | + | |
773 | + /* uhoh...infinite symlink loop! */ | |
774 | + BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, ERR_SYMLINK_LOOP, 0); | |
775 | + | |
776 | + /* | |
777 | + * We fix up the offset to point to the actual data on the | |
778 | + * first open, since we don't want to seek across the whole file on | |
779 | + * archive open (can be SLOW on large, CD-stored files), but we | |
780 | + * need to check the local file header...not just for corruption, | |
781 | + * but since it stores offset info the central directory does not. | |
782 | + */ | |
783 | + if (resolve_type != ZIP_RESOLVED) | |
784 | + { | |
785 | + entry->resolved = ZIP_RESOLVING; | |
786 | + | |
787 | + retval = zip_parse_local(in, entry); | |
788 | + if (retval) | |
789 | + { | |
790 | + /* | |
791 | + * If it's a symlink, find the original file. This will cause | |
792 | + * resolution of other entries (other symlinks and, eventually, | |
793 | + * the real file) if all goes well. | |
794 | + */ | |
795 | + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) | |
796 | + retval = zip_resolve_symlink(in, info, entry); | |
797 | + } /* if */ | |
798 | + | |
799 | + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) | |
800 | + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK); | |
801 | + else if (resolve_type == ZIP_UNRESOLVED_FILE) | |
802 | + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); | |
803 | + } /* if */ | |
804 | + | |
805 | + return(retval); | |
806 | +} /* zip_resolve */ | |
807 | + | |
808 | + | |
809 | +static int zip_version_does_symlinks(PHYSFS_uint32 version) | |
810 | +{ | |
811 | + int retval = 0; | |
812 | + PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF); | |
813 | + | |
814 | + switch (hosttype) | |
815 | + { | |
816 | + /* | |
817 | + * These are the platforms that can NOT build an archive with | |
818 | + * symlinks, according to the Info-ZIP project. | |
819 | + */ | |
820 | + case 0: /* FS_FAT_ */ | |
821 | + case 1: /* AMIGA_ */ | |
822 | + case 2: /* VMS_ */ | |
823 | + case 4: /* VM_CSM_ */ | |
824 | + case 6: /* FS_HPFS_ */ | |
825 | + case 11: /* FS_NTFS_ */ | |
826 | + case 14: /* FS_VFAT_ */ | |
827 | + case 13: /* ACORN_ */ | |
828 | + case 15: /* MVS_ */ | |
829 | + case 18: /* THEOS_ */ | |
830 | + break; /* do nothing. */ | |
831 | + | |
832 | + default: /* assume the rest to be unix-like. */ | |
833 | + retval = 1; | |
834 | + break; | |
835 | + } /* switch */ | |
836 | + | |
837 | + return(retval); | |
838 | +} /* zip_version_does_symlinks */ | |
839 | + | |
840 | + | |
841 | +static int zip_entry_is_symlink(const ZIPentry *entry) | |
842 | +{ | |
843 | + return((entry->resolved == ZIP_UNRESOLVED_SYMLINK) || | |
844 | + (entry->resolved == ZIP_BROKEN_SYMLINK) || | |
845 | + (entry->symlink)); | |
846 | +} /* zip_entry_is_symlink */ | |
847 | + | |
848 | + | |
849 | +static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr) | |
850 | +{ | |
851 | + PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF); | |
852 | + | |
853 | + return ( | |
854 | + (zip_version_does_symlinks(entry->version)) && | |
855 | + (entry->uncompressed_size > 0) && | |
856 | + ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) | |
857 | + ); | |
858 | +} /* zip_has_symlink_attr */ | |
859 | + | |
860 | + | |
861 | +static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime) | |
862 | +{ | |
863 | +#ifdef _WIN32_WCE | |
864 | + /* We have no struct tm and no mktime right now. | |
865 | + FIXME: This should probably be fixed at some point. | |
866 | + */ | |
867 | + return -1; | |
868 | +#else | |
869 | + PHYSFS_uint32 dosdate; | |
870 | + struct tm unixtime; | |
871 | + memset(&unixtime, '\0', sizeof (unixtime)); | |
872 | + | |
873 | + dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF); | |
874 | + dostime &= 0xFFFF; | |
875 | + | |
876 | + /* dissect date */ | |
877 | + unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80; | |
878 | + unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1; | |
879 | + unixtime.tm_mday = ((dosdate ) & 0x1F); | |
880 | + | |
881 | + /* dissect time */ | |
882 | + unixtime.tm_hour = ((dostime >> 11) & 0x1F); | |
883 | + unixtime.tm_min = ((dostime >> 5) & 0x3F); | |
884 | + unixtime.tm_sec = ((dostime << 1) & 0x3E); | |
885 | + | |
886 | + /* let mktime calculate daylight savings time. */ | |
887 | + unixtime.tm_isdst = -1; | |
888 | + | |
889 | + return((PHYSFS_sint64) mktime(&unixtime)); | |
890 | +#endif | |
891 | +} /* zip_dos_time_to_physfs_time */ | |
892 | + | |
893 | + | |
894 | +static int zip_load_entry(void *in, ZIPentry *entry, PHYSFS_uint32 ofs_fixup) | |
895 | +{ | |
896 | + PHYSFS_uint16 fnamelen, extralen, commentlen; | |
897 | + PHYSFS_uint32 external_attr; | |
898 | + PHYSFS_uint16 ui16; | |
899 | + PHYSFS_uint32 ui32; | |
900 | + PHYSFS_sint64 si64; | |
901 | + | |
902 | + /* sanity check with central directory signature... */ | |
903 | + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); | |
904 | + BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0); | |
905 | + | |
906 | + /* Get the pertinent parts of the record... */ | |
907 | + BAIL_IF_MACRO(!readui16(in, &entry->version), NULL, 0); | |
908 | + BAIL_IF_MACRO(!readui16(in, &entry->version_needed), NULL, 0); | |
909 | + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits */ | |
910 | + BAIL_IF_MACRO(!readui16(in, &entry->compression_method), NULL, 0); | |
911 | + BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); | |
912 | + entry->last_mod_time = zip_dos_time_to_physfs_time(ui32); | |
913 | + BAIL_IF_MACRO(!readui32(in, &entry->crc), NULL, 0); | |
914 | + BAIL_IF_MACRO(!readui32(in, &entry->compressed_size), NULL, 0); | |
915 | + BAIL_IF_MACRO(!readui32(in, &entry->uncompressed_size), NULL, 0); | |
916 | + BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0); | |
917 | + BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0); | |
918 | + BAIL_IF_MACRO(!readui16(in, &commentlen), NULL, 0); | |
919 | + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* disk number start */ | |
920 | + BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* internal file attribs */ | |
921 | + BAIL_IF_MACRO(!readui32(in, &external_attr), NULL, 0); | |
922 | + BAIL_IF_MACRO(!readui32(in, &entry->offset), NULL, 0); | |
923 | + entry->offset += ofs_fixup; | |
924 | + | |
925 | + entry->symlink = NULL; /* will be resolved later, if necessary. */ | |
926 | + entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ? | |
927 | + ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; | |
928 | + | |
929 | + entry->name = (char *) allocator.Malloc(fnamelen + 1); | |
930 | + BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0); | |
931 | + if (__PHYSFS_platformRead(in, entry->name, fnamelen, 1) != 1) | |
932 | + goto zip_load_entry_puked; | |
933 | + | |
934 | + entry->name[fnamelen] = '\0'; /* null-terminate the filename. */ | |
935 | + zip_convert_dos_path(entry, entry->name); | |
936 | + | |
937 | + si64 = __PHYSFS_platformTell(in); | |
938 | + if (si64 == -1) | |
939 | + goto zip_load_entry_puked; | |
940 | + | |
941 | + /* seek to the start of the next entry in the central directory... */ | |
942 | + if (!__PHYSFS_platformSeek(in, si64 + extralen + commentlen)) | |
943 | + goto zip_load_entry_puked; | |
944 | + | |
945 | + return(1); /* success. */ | |
946 | + | |
947 | +zip_load_entry_puked: | |
948 | + allocator.Free(entry->name); | |
949 | + return(0); /* failure. */ | |
950 | +} /* zip_load_entry */ | |
951 | + | |
952 | + | |
953 | +static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) | |
954 | +{ | |
955 | + if (one != two) | |