Commit d51ea00a0cc955651804fe445bc84882cce28b53

Authored by dg
0 parents

first import


git-svn-id: http://svn.net-core.org/repos/t-engine4@1 51575b47-30f0-44d4-a5cc-537603b46e54

Too many changes to show.

To preserve performance only 7 of 7+ files are displayed.

  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
... ...
  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
... ...
  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
... ...
  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/")
... ...
  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 +
... ...
  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)