private import stream; private class zlib { const char[] libraryName = "libz.dll"; static HANDLE library; const char[] ZLIB_VERSION = "1.1.4"; const uint Z_OK = 0; const uint Z_STREAM_END = 1; const uint Z_NEED_DICT = 2; const uint Z_ERRNO = -1; const uint Z_STREAM_ERROR = -2; const uint Z_DATA_ERROR = -3; const uint Z_MEM_ERROR = -4; const uint Z_BUF_ERROR = -5; const uint Z_VERSION_ERROR = -6; const uint Z_NO_FLUSH = 0; const uint Z_PARTIAL_FLUSH = 1; const uint Z_SYNC_FLUSH = 2; const uint Z_FULL_FLUSH = 3; const uint Z_FINISH = 4; extern (C) typedef void* function (void* opaque, uint items, uint size) alloc_func; extern (C) typedef void function (void* opaque, void* address) free_func; struct z_stream { ubyte* next_in; /* next input byte */ uint avail_in; /* number of bytes available at next_in */ uint total_in; /* total nb of input bytes read so far */ ubyte* next_out; /* next output byte should be put there */ uint avail_out; /* remaining free space at next_out */ uint total_out; /* total nb of bytes output so far */ char* msg; /* last error message, NULL if no error */ void* state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ free_func zfree; /* used to free the internal state */ void* opaque; /* private data object passed to zalloc and zfree */ int data_type; /* best guess about the data type: ascii or binary */ uint adler; /* adler32 value of the uncompressed data */ uint reserved; /* reserved for future use */ } alias z_stream* z_streamp; static extern (C) char* function () zlibVersion; static extern (C) int function (z_streamp stream, int level, char* ver, int streamSize) deflateInit_; static extern (C) int function (z_streamp stream) deflateEnd; static extern (C) int function (z_streamp stream) deflate; static extern (C) int function (z_streamp stream, char* ver, int streamSize) inflateInit_; static extern (C) int function (z_streamp stream, int flush) inflate; static extern (C) int function (z_streamp stream) inflateEnd; static extern (C) int function (z_streamp strm, int windowBits, char* ver, int stream_size) inflateInit2_; static int deflateInit (z_streamp stream, int level) { return deflateInit_ (stream, level, ZLIB_VERSION, z_stream.size); } static int inflateInit (z_streamp stream) { return inflateInit_ (stream, ZLIB_VERSION, z_stream.size); } static int inflateInit2 (z_streamp stream, int windowBits) { return inflateInit2_ (stream, windowBits, ZLIB_VERSION, z_stream.size); } static void init () { if (library != (HANDLE) 0) return; library = LoadLibraryA (toStringz (libraryName)); if (library == (HANDLE) 0) throw new Error ("Couldn't load \"" ~ libraryName ~ "\"."); load ((void**) &deflateInit_, "deflateInit_"); load ((void**) &deflateEnd, "deflateEnd"); load ((void**) &deflate, "deflate"); load ((void**) &inflateInit_, "inflateInit_"); load ((void**) &inflate, "inflate"); load ((void**) &inflateEnd, "inflateEnd"); load ((void**) &zlibVersion, "zlibVersion"); load ((void**) &inflateInit2_, "inflateInit2_"); } static void load (void** data, char[] name) { *data = GetProcAddress (library, toStringz (name)); if (*data === null) throw new Error ("Couldn't find symbol \"" ~ name ~ "\" in library \"" ~ libraryName ~ "\"."); } } /** A utility class for reading zipfiles. Form stolen from Python. */ class brZipFile { /** A file in the zip. */ private class File { char[] filename; uint size; uint comsize; ulong position; int method; } import path, string; char[] source; private File[char[]] files; const uint localFileHeaderSignature = 0x04034b50; const uint endOfCentralDirectorySignature = 0x06054b50; const uint centralDirectorySignature = 0x02014b50; /** Read from file (opened with brStream). */ this (char[] filename) { this.source = filename.dup; brStream stream = new brStream (filename); assert (stream.igetui () == localFileHeaderSignature); stream.seekEnd (-22); uint fileSize = stream.position () + 22; if (stream.igetui () != endOfCentralDirectorySignature) throw new Error ("Zip file has a comment or is not a zip."); stream.seekCur (6); /* Skip some schloft. */ ushort fileCount = stream.igetus (); uint cdSize = stream.igetui (); uint cdOffset = stream.igetui (); stream.position (cdOffset); uint concat = (fileSize - 22 - cdSize) - cdOffset; for (int c; c < fileCount; c ++) { uint sig = stream.igetui (); ushort verMadeBy = stream.igetus (); ushort verNeeded = stream.igetus (); ushort flags = stream.igetus (); ushort method = stream.igetus (); ushort modFileTime = stream.igetus (); ushort modFileDate = stream.igetus (); uint crc32 = stream.igetui (); uint comsize = stream.igetui (); uint size = stream.igetui (); ushort filenameLength = stream.igetus (); ushort extraFieldLength = stream.igetus (); ushort fileCommentLength = stream.igetus (); ushort diskNumberStart = stream.igetus (); ushort internalFileAttributes = stream.igetus (); uint externalFileAttributes = stream.igetui (); uint localHeaderOffset = stream.igetui () + concat; assert (sig == centralDirectorySignature); if (flags & 1) throw new Error ("Zip file is encrypted."); if (flags & 8) throw new Error ("Unsupported zip file complication."); if (method != 0 && method != 8) throw new Error (fmt ("Compressed zip file support is limited (method %d).", method)); File file = new File; file.filename = new char[filenameLength]; stream.read ((ubyte[]) file.filename); file.filename = string.tolower (string.replace (file.filename, "\\", "/")); file.size = size; file.position = localHeaderOffset; file.method = method; file.comsize = comsize; files [file.filename] = file; stream.seekCur (extraFieldLength + fileCommentLength); } stream.close (); files.rehash; } /** Find out whether this file exists in the zip file. */ bit exists (char[] filename) { if (filename in files) return true; if (string.tolower (filename) in files) return true; return false; } /** Open the file. */ Stream open (char[] filename) { File file; brStream base = new brStream (source); filename = string.replace (filename, "\\", "/"); if (filename in files) file = files [filename]; else file = files [string.tolower (filename)]; base.position (file.position); if (base.igetui () != localFileHeaderSignature) throw new Error ("Corrupt zip file has a bad local file header signature."); base.seekCur (22); uint skip = base.igetus (); skip += base.igetus (); base.seekCur (skip); if (file.method == 0) { Stream stream = new SliceStream (base.base, base.position (), base.position () + file.comsize); ((SliceStream) stream).nestClose = true; return stream; } if (file.method == 8) { zlib.z_stream zstream; ubyte[] indata = new ubyte [file.comsize + 1]; ubyte[] outdata = new ubyte [file.size]; base.read (indata [0 .. indata.length - 1]); base.close (); indata [indata.length - 1] = "Z"; zlib.init (); zstream.next_in = indata; zstream.avail_in = indata.length; zstream.next_out = outdata; zstream.avail_out = outdata.length; int result = zlib.inflateInit2 (&zstream, -15); ZipStream.zcheckex (&zstream, result); try while (ZipStream.zcheckex (&zstream, zlib.inflate (&zstream, zlib.Z_FINISH)) != zlib.Z_STREAM_END) continue; finally zlib.inflateEnd (&zstream); delete indata; return new MemoryStream (outdata); } throw new Error ("Unsupported compression method"); } import dig; /** List files within a directory. */ Control.SearchFile[] listdir (char[] path) { path = string.replace (path, "\\", "/"); if (path.length && path [0] == "/") path = path [1 .. path.length]; if (path.length && path [path.length - 1] == "/") path = path [0 .. path.length - 1]; Control.SearchFile[] list; File[] values = files.values; outer: for (int c; c < values.length; c ++) { File file = values [c]; if (file.filename.length < path.length || file.filename [0 .. path.length] != path || file.filename [path.length] != "/" || file.filename [path.length .. file.filename.length] == "/") continue; char[] filename = file.filename [path.length + 1 .. file.filename.length]; int split; if (!filename.length) continue; if ((split = string.find (filename, (char) "/")) != -1) filename = filename [0 .. split]; for (int c; c < list.length; c ++) if (list [c].name == filename) continue outer; Control.SearchFile item = new Control.SearchFile; item.name = filename; item.full = file.filename [0 .. path.length + 1 + filename.length]; item.ext = getExt (filename); item.alternate = filename; item.directory = (split != -1); item.size = file.size; list ~= item; } return list; } }