From e804e487473eed797e7b9aed61d42629cbfe1ad4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 19 Jun 2019 10:06:54 +0530 Subject: [PATCH] Start work on building calibre on windows --- bypy/init_env.py | 6 +- bypy/windows/XUnzip.cpp | 4379 +++++++++++++++++++++++++++ bypy/windows/XUnzip.h | 382 +++ bypy/windows/__main__.py | 691 +++++ bypy/windows/eject.c | 423 +++ bypy/windows/en-us.xml | 9 + bypy/windows/file_dialogs.cpp | 355 +++ bypy/windows/main.c | 113 + bypy/windows/portable-installer.cpp | 618 ++++ bypy/windows/portable.c | 154 + bypy/windows/site.py | 101 + bypy/windows/template.rc | 42 + bypy/windows/util.c | 448 +++ bypy/windows/wix-template.xml | 206 ++ bypy/windows/wix.py | 137 + setup/build.py | 11 +- setup/build_environment.py | 4 +- setup/parallel_build.py | 7 +- 18 files changed, 8077 insertions(+), 9 deletions(-) create mode 100644 bypy/windows/XUnzip.cpp create mode 100644 bypy/windows/XUnzip.h create mode 100644 bypy/windows/__main__.py create mode 100644 bypy/windows/eject.c create mode 100644 bypy/windows/en-us.xml create mode 100644 bypy/windows/file_dialogs.cpp create mode 100644 bypy/windows/main.c create mode 100644 bypy/windows/portable-installer.cpp create mode 100644 bypy/windows/portable.c create mode 100644 bypy/windows/site.py create mode 100644 bypy/windows/template.rc create mode 100644 bypy/windows/util.c create mode 100644 bypy/windows/wix-template.xml create mode 100644 bypy/windows/wix.py diff --git a/bypy/init_env.py b/bypy/init_env.py index 6f9272105f..786b5808af 100644 --- a/bypy/init_env.py +++ b/bypy/init_env.py @@ -151,9 +151,10 @@ def initialize_constants(): return calibre_constants -def run(*args): +def run(*args, **extra_env): env = os.environ.copy() env.update(worker_env) + env.update(extra_env) env['SW'] = PREFIX env['LD_LIBRARY_PATH'] = LIBDIR env['SIP_BIN'] = os.path.join(PREFIX, 'bin', 'sip') @@ -165,7 +166,8 @@ def build_c_extensions(ext_dir): bdir = os.path.join(build_dir(), 'calibre-extension-objects') if run( PYTHON, 'setup.py', 'build', - '--output-dir', ext_dir, '--build-dir', bdir + '--output-dir', ext_dir, '--build-dir', bdir, + COMPILER_CWD=bdir ) != 0: print('Building of calibre C extensions failed', file=sys.stderr) os.chdir(CALIBRE_DIR) diff --git a/bypy/windows/XUnzip.cpp b/bypy/windows/XUnzip.cpp new file mode 100644 index 0000000000..cc9400306a --- /dev/null +++ b/bypy/windows/XUnzip.cpp @@ -0,0 +1,4379 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + + +#define _USE_32BIT_TIME_T //+++1.2 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include "XUnzip.h" + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(time_t timer) +{ + struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + GetCurrentDirectory(MAX_PATH,rootdir); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { time_t atime = *(time_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { time_t ctime = *(time_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/bypy/windows/XUnzip.h b/bypy/windows/XUnzip.h new file mode 100644 index 0000000000..aa39ca142e --- /dev/null +++ b/bypy/windows/XUnzip.h @@ -0,0 +1,382 @@ +// XUnzip.h Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XUNZIP_H +#define XUNZIP_H + + +#ifndef XZIP_H +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +#endif + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenZip() +// +// Purpose: Open an existing zip archive file +// +// Parameters: z - archive file name if flags is ZIP_FILENAME; for other +// uses see below +// len - for memory (ZIP_MEMORY) should be the buffer size; +// for other uses, should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: HZIP - non-zero if zip archive opened ok, otherwise 0 +// +HZIP OpenZip(void *z, unsigned int len, DWORD flags); +// OpenZip - opens a zip file and returns a handle with which you can +// subsequently examine its contents. You can open a zip file from: +// from a pipe: OpenZip(hpipe_read,0, ZIP_HANDLE); +// from a file (by handle): OpenZip(hfile,0, ZIP_HANDLE); +// from a file (by name): OpenZip("c:\\test.zip",0, ZIP_FILENAME); +// from a memory block: OpenZip(bufstart, buflen, ZIP_MEMORY); +// If the file is opened through a pipe, then items may only be +// accessed in increasing order, and an item may only be unzipped once, +// although GetZipItem can be called immediately before and after unzipping +// it. If it's opened i n any other way, then full random access is possible. +// Note: pipe input is not yet implemented. + + +/////////////////////////////////////////////////////////////////////////////// +// +// GetZipItem() +// +// Purpose: Get information about an item in an open zip archive +// +// Parameters: hz - handle of open zip archive +// index - index number (0 based) of item in zip +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define GetZipItem GetZipItemW +#else +#define GetZipItem GetZipItemA +#endif + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +// GetZipItem - call this to get information about an item in the zip. +// If index is -1 and the file wasn't opened through a pipe, +// then it returns information about the whole zipfile +// (and in particular ze.index returns the number of index items). +// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY) +// See below for notes on what happens when you unzip such an item. +// Note: if you are opening the zip through a pipe, then random access +// is not possible and GetZipItem(-1) fails and you can't discover the number +// of items except by calling GetZipItem on each one of them in turn, +// starting at 0, until eventually the call fails. Also, in the event that +// you are opening through a pipe and the zip was itself created into a pipe, +// then then comp_size and sometimes unc_size as well may not be known until +// after the item has been unzipped. + + +/////////////////////////////////////////////////////////////////////////////// +// +// FindZipItem() +// +// Purpose: Find item by name and return information about it +// +// Parameters: hz - handle of open zip archive +// name - name of file to look for inside zip archive +// ic - TRUE = case insensitive +// index - pointer to index number returned, or -1 +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define FindZipItem FindZipItemW +#else +#define FindZipItem FindZipItemA +#endif + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +// FindZipItem - finds an item by name. ic means 'insensitive to case'. +// It returns the index of the item, and returns information about it. +// If nothing was found, then index is set to -1 and the function returns +// an error code. + + +/////////////////////////////////////////////////////////////////////////////// +// +// UnzipItem() +// +// Purpose: Find item by index and unzip it +// +// Parameters: hz - handle of open zip archive +// index - index number of file to unzip +// dst - target file name of unzipped file +// len - for memory (ZIP_MEMORY. length of buffer; +// otherwise 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +// UnzipItem - given an index to an item, unzips it. You can unzip to: +// to a pipe: UnzipItem(hz,i, hpipe_write,0,ZIP_HANDLE); +// to a file (by handle): UnzipItem(hz,i, hfile,0,ZIP_HANDLE); +// to a file (by name): UnzipItem(hz,i, ze.name,0,ZIP_FILENAME); +// to a memory block: UnzipItem(hz,i, buf,buflen,ZIP_MEMORY); +// In the final case, if the buffer isn't large enough to hold it all, +// then the return code indicates that more is yet to come. If it was +// large enough, and you want to know precisely how big, GetZipItem. +// Note: zip files are normally stored with relative pathnames. If you +// unzip with ZIP_FILENAME a relative pathname then the item gets created +// relative to the current directory - it first ensures that all necessary +// subdirectories have been created. Also, the item may itself be a directory. +// If you unzip a directory with ZIP_FILENAME, then the directory gets created. +// If you unzip it to a handle or a memory block, then nothing gets created +// and it emits 0 bytes. + + +/////////////////////////////////////////////////////////////////////////////// +// +// CloseZip() +// +// Purpose: Close an open zip archive +// +// Parameters: hz - handle to an open zip archive +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + +// e.g. +// +// SetCurrentDirectory("c:\\docs\\stuff"); +// HZIP hz = OpenZip("c:\\stuff.zip",0,ZIP_FILENAME); +// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index; +// for (int i=0; i + +from __future__ import absolute_import, division, print_function, unicode_literals + +import errno +import glob +import os +import re +import shutil +import stat +import subprocess +import sys +import zipfile + +from bypy.constants import ( + PREFIX, SRC as CALIBRE_DIR, SW, is64bit, build_dir, python_major_minor_version +) +from bypy.utils import py_compile, run, walk + +from .wix import create_installer + +iv = globals()['init_env'] +calibre_constants = iv['calibre_constants'] +QT_PREFIX = os.path.join(PREFIX, 'qt') +QT_DLLS, QT_PLUGINS, PYQT_MODULES = iv['QT_DLLS'], iv['QT_PLUGINS'], iv['PYQT_MODULES'] + +APPNAME, VERSION = calibre_constants['appname'], calibre_constants['version'] +WINVER = VERSION + '.0' +machine = 'X64' if is64bit else 'X86' +j, d, a, b = os.path.join, os.path.dirname, os.path.abspath, os.path.basename + +DESCRIPTIONS = { + 'calibre': 'The main calibre program', + 'ebook-viewer': 'The calibre e-book viewer', + 'ebook-edit': 'The calibre e-book editor', + 'lrfviewer': 'Viewer for LRF files', + 'ebook-convert': 'Command line interface to the conversion/news download system', + 'ebook-meta': 'Command line interface for manipulating e-book metadata', + 'calibredb': 'Command line interface to the calibre database', + 'calibre-launcher': 'Utility functions common to all executables', + 'calibre-debug': 'Command line interface for calibre debugging/development', + 'calibre-customize': 'Command line interface to calibre plugin system', + 'calibre-server': 'Standalone calibre content server', + 'calibre-parallel': 'calibre worker process', + 'calibre-smtp': 'Command line interface for sending books via email', + 'calibre-eject': 'Helper program for ejecting connected reader devices', + 'calibre-file-dialog': 'Helper program to show file open/save dialogs', +} + +# https://msdn.microsoft.com/en-us/library/windows/desktop/dn481241(v=vs.85).aspx +SUPPORTED_OS = { + 'vista': '{e2011457-1546-43c5-a5fe-008deee3d3f0}', + 'w7': '{35138b9a-5d96-4fbd-8e2d-a2440225f93a}', + 'w8': '{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}', + 'w81': '{1f676c76-80e1-4239-95bb-83d0f6d0da78}', + 'w10': '{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}', +} + +EXE_MANIFEST = '''\ + + + + + + + + + + + + + + + + + + + +'''.format(**SUPPORTED_OS) + + +def printf(*args, **kw): + print(*args, **kw) + sys.stdout.flush() + + +class Env(object): + + def __init__(self, build_dir): + self.python_base = os.path.join(PREFIX, 'private', 'python') + self.portable_uncompressed_size = 0 + self.src_root = CALIBRE_DIR + self.base = j(build_dir, 'winfrozen') + self.app_base = j(self.base, 'app') + self.rc_template = j(d(a(__file__)), 'template.rc') + self.py_ver = '.'.join(map(str, python_major_minor_version())) + self.lib_dir = j(self.app_base, 'Lib') + self.pylib = j(self.app_base, 'pylib.zip') + self.dll_dir = j(self.app_base, 'DLLs') + self.portable_base = j(d(self.base), 'Calibre Portable') + self.obj_dir = j(build_dir, 'launcher') + self.installer_dir = j(build_dir, 'wix') + self.dist = j(SW, 'dist') + + +def initbase(env): + os.makedirs(env.app_base) + os.mkdir(env.dll_dir) + try: + shutil.rmtree(env.dist) + except EnvironmentError as err: + if err.errno != errno.ENOENT: + raise + os.mkdir(env.dist) + + +def add_plugins(env, ext_dir): + printf('Adding plugins...') + tgt = env.dll_dir + for f in glob.glob(j(ext_dir, '*.pyd')): + shutil.copy2(f, tgt) + + +def freeze(env, ext_dir): + shutil.copy2(j(env.src_root, 'LICENSE'), env.base) + + printf('Adding resources...') + tgt = j(env.app_base, 'resources') + if os.path.exists(tgt): + shutil.rmtree(tgt) + shutil.copytree(j(env.src_root, 'resources'), tgt) + + printf('\tAdding misc binary deps') + + def copybin(x): + shutil.copy2(x, env.dll_dir) + try: + shutil.copy2(x + '.manifest', env.dll_dir) + except EnvironmentError as err: + if err.errno != errno.ENOENT: + raise + + bindir = os.path.join(PREFIX, 'bin') + for x in ('pdftohtml', 'pdfinfo', 'pdftoppm', 'jpegtran-calibre', 'cjpeg-calibre', 'optipng-calibre', 'JXRDecApp-calibre'): + copybin(os.path.join(bindir, x + '.exe')) + for f in glob.glob(os.path.join(bindir, '*.dll')): + if re.search(r'(easylzma|icutest)', f.lower()) is None: + copybin(f) + + copybin(os.path.join(env.python_base, 'python%s.dll' % env.py_ver.replace('.', ''))) + for x in glob.glob(os.path.join(env.python_base, 'DLLs', '*')): # python pyd modules + copybin(x) + for f in walk(os.path.join(env.python_base, 'Lib')): + if f.lower().endswith('.dll') and 'scintilla' not in f.lower(): + copybin(f) + add_plugins(env, ext_dir) + + printf('Adding Qt...') + for x in QT_DLLS: + copybin(os.path.join(QT_PREFIX, 'bin', x + '.dll')) + plugdir = j(QT_PREFIX, 'plugins') + tdir = j(env.app_base, 'qt_plugins') + for d in QT_PLUGINS: + imfd = os.path.join(plugdir, d) + tg = os.path.join(tdir, d) + if os.path.exists(tg): + shutil.rmtree(tg) + shutil.copytree(imfd, tg) + for f in walk(tdir): + if not f.lower().endswith('.dll'): + os.remove(f) + + printf('Adding python...') + + def ignore_lib(root, items): + ans = [] + for x in items: + ext = os.path.splitext(x)[1].lower() + if ext in ('.dll', '.chm', '.htm', '.txt'): + ans.append(x) + return ans + + shutil.copytree(r'%s\Lib' % env.python_base, env.lib_dir, ignore=ignore_lib) + install_site_py(env) + + # Fix win32com + sp_dir = j(env.lib_dir, 'site-packages') + comext = j(sp_dir, 'win32comext') + shutil.copytree(j(comext, 'shell'), j(sp_dir, 'win32com', 'shell')) + shutil.rmtree(comext) + + for pat in ('PyQt5\\uic\\port_v3', ): + x = glob.glob(j(env.lib_dir, 'site-packages', pat))[0] + shutil.rmtree(x) + pyqt = j(env.lib_dir, 'site-packages', 'PyQt5') + for x in {x for x in os.listdir(pyqt) if x.endswith('.pyd')}: + if x.partition('.')[0] not in PYQT_MODULES: + os.remove(j(pyqt, x)) + + printf('Adding calibre sources...') + for x in glob.glob(j(CALIBRE_DIR, 'src', '*')): + if os.path.isdir(x): + if os.path.exists(os.path.join(x, '__init__.py')): + shutil.copytree(x, j(sp_dir, b(x))) + else: + shutil.copy(x, j(sp_dir, b(x))) + + for x in (r'calibre\manual', r'calibre\plugins', 'pythonwin'): + deld = j(sp_dir, x) + if os.path.exists(deld): + shutil.rmtree(deld) + + for x in os.walk(j(sp_dir, 'calibre')): + for f in x[-1]: + if not f.endswith('.py'): + os.remove(j(x[0], f)) + + extract_pyd_modules(env, sp_dir) + + printf('Byte-compiling all python modules...') + for x in ('test', 'lib2to3', 'distutils'): + x = j(env.lib_dir, x) + if os.path.exists(x): + shutil.rmtree(x) + py_compile(env.lib_dir.replace(os.sep, '/')) + + +def embed_manifests(env): + printf('Embedding remaining manifests...') + for manifest in walk(env.base): + dll, ext = os.path.splitext(manifest) + if ext != '.manifest': + continue + res = 2 + if os.path.splitext(dll)[1] == '.exe': + res = 1 + if os.path.exists(dll) and open(manifest, 'rb').read().strip(): + run('mt.exe', '-manifest', manifest, '-outputresource:%s;%d' % (dll, res)) + os.remove(manifest) + + +def extract_pyd_modules(env, site_packages_dir): + printf('\nExtracting .pyd modules from site-packages...') + + def extract_pyd(path, root): + fullname = os.path.relpath(path, root).replace(os.sep, '/').replace('/', '.') + dest = os.path.join(env.dll_dir, fullname) + if os.path.exists(dest): + raise ValueError('Cannot extract %s into DLLs as it already exists' % fullname) + os.rename(path, dest) + bpy = dest[:-1] + if os.path.exists(bpy): + with open(bpy, 'rb') as f: + raw = f.read().strip() + if (not raw.startswith('def __bootstrap__') or not raw.endswith('__bootstrap__()')): + raise ValueError('The file %r has non bootstrap code' % bpy) + for ext in ('', 'c', 'o'): + try: + os.remove(bpy + ext) + except EnvironmentError as err: + if err.errno != errno.ENOENT: + raise + + def find_pyds(base): + for dirpath, dirnames, filenames in os.walk(base): + for fname in filenames: + if fname.lower().endswith('.pyd'): + yield os.path.join(dirpath, fname) + + def process_root(root, base=None): + for path in find_pyds(root): + extract_pyd(path, base or root) + + def absp(x): + return os.path.normcase(os.path.abspath(os.path.join(site_packages_dir, x))) + + roots = set() + for pth in glob.glob(os.path.join(site_packages_dir, '*.pth')): + for line in open(pth, 'rb').readlines(): + line = line.strip() + if line and not line.startswith('#') and os.path.exists(os.path.join(site_packages_dir, line)): + roots.add(absp(line)) + + for x in os.listdir(site_packages_dir): + x = absp(x) + if x in roots: + process_root(x) + elif os.path.isdir(x): + process_root(x, site_packages_dir) + elif x.lower().endswith('.pyd'): + extract_pyd(x, site_packages_dir) + + +def embed_resources(env, module, desc=None, extra_data=None, product_description=None): + icon_base = j(env.src_root, 'icons') + icon_map = {'calibre': 'library', 'ebook-viewer': 'viewer', 'ebook-edit': 'ebook-edit', + 'lrfviewer': 'viewer', 'calibre-portable': 'library'} + file_type = 'DLL' if module.endswith('.dll') else 'APP' + template = open(env.rc_template, 'rb').read() + bname = b(module) + internal_name = os.path.splitext(bname)[0] + icon = icon_map.get(internal_name, 'command-prompt') + if internal_name.startswith('calibre-portable-'): + icon = 'install' + icon = j(icon_base, icon + '.ico') + if desc is None: + defdesc = 'A dynamic link library' if file_type == 'DLL' else \ + 'An executable program' + desc = DESCRIPTIONS.get(internal_name, defdesc) + license = 'GNU GPL v3.0' + + def e(val): + return val.replace('"', r'\"') + if product_description is None: + product_description = APPNAME + ' - E-book management' + rc = template.format( + icon=icon, + file_type=e(file_type), + file_version=e(WINVER.replace('.', ',')), + file_version_str=e(WINVER), + file_description=e(desc), + internal_name=e(internal_name), + original_filename=e(bname), + product_version=e(WINVER.replace('.', ',')), + product_version_str=e(VERSION), + product_name=e(APPNAME), + product_description=e(product_description), + legal_copyright=e(license), + legal_trademarks=e(APPNAME + ' is a registered U.S. trademark number 3,666,525') + ) + if extra_data: + rc += '\nextra extra "%s"' % extra_data + tdir = env.obj_dir + rcf = j(tdir, bname + '.rc') + with open(rcf, 'wb') as f: + f.write(rc) + res = j(tdir, bname + '.res') + run('rc', '/n', '/fo' + res, rcf) + return res + + +def install_site_py(env): + if not os.path.exists(env.lib_dir): + os.makedirs(env.lib_dir) + shutil.copy2(j(d(__file__), 'site.py'), env.lib_dir) + + +def build_portable_installer(env): + zf = a(j(env.dist, 'calibre-portable-%s.zip.lz' % VERSION)) + usz = env.portable_uncompressed_size or os.path.getsize(zf) + + def cc(src, obj): + cflags = '/c /EHsc /MT /W4 /Ox /nologo /D_UNICODE /DUNICODE /DPSAPI_VERSION=1'.split() + cflags.append(r'/I%s\include' % PREFIX) + cflags.append('/DUNCOMPRESSED_SIZE=%d' % usz) + printf('Compiling', obj) + cmd = ['cl.exe'] + cflags + ['/Fo' + obj, src] + run(*cmd) + + base = d(a(__file__)) + src = j(base, 'portable-installer.cpp') + obj = j(env.obj_dir, b(src) + '.obj') + xsrc = j(base, 'XUnzip.cpp') + xobj = j(env.obj_dir, b(xsrc) + '.obj') + cc(src, obj) + cc(xsrc, xobj) + + exe = j(env.dist, 'calibre-portable-installer-%s.exe' % VERSION) + printf('Linking', exe) + manifest = exe + '.manifest' + with open(manifest, 'wb') as f: + f.write(EXE_MANIFEST) + cmd = ['link.exe'] + [ + '/INCREMENTAL:NO', '/MACHINE:' + machine, + '/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:WINDOWS', + '/LIBPATH:' + (PREFIX + r'\lib'), + '/RELEASE', '/MANIFEST:EMBED', '/MANIFESTINPUT:' + manifest, + '/ENTRY:wWinMainCRTStartup', + '/OUT:' + exe, embed_resources( + env, exe, desc='Calibre Portable Installer', extra_data=zf, product_description='Calibre Portable Installer'), + xobj, obj, 'User32.lib', 'Shell32.lib', 'easylzma_s.lib', + 'Ole32.lib', 'Shlwapi.lib', 'Kernel32.lib', 'Psapi.lib'] + run(*cmd) + os.remove(zf) + + +def build_portable(env): + base = env.portable_base + if os.path.exists(base): + shutil.rmtree(base) + os.makedirs(base) + root = d(a(__file__)) + src = j(root, 'portable.c') + obj = j(env.obj_dir, b(src) + '.obj') + cflags = '/c /EHsc /MT /W3 /Ox /nologo /D_UNICODE /DUNICODE'.split() + + printf('Compiling', obj) + cmd = ['cl.exe'] + cflags + ['/Fo' + obj, '/Tc' + src] + run(*cmd) + + exe = j(base, 'calibre-portable.exe') + printf('Linking', exe) + cmd = ['link.exe'] + [ + '/INCREMENTAL:NO', '/MACHINE:' + machine, + '/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:WINDOWS', + '/RELEASE', + '/ENTRY:wWinMainCRTStartup', + '/OUT:' + exe, embed_resources(env, exe, desc='Calibre Portable', product_description='Calibre Portable'), + obj, 'User32.lib'] + run(*cmd) + + printf('Creating portable installer') + shutil.copytree(env.base, j(base, 'Calibre')) + os.mkdir(j(base, 'Calibre Library')) + os.mkdir(j(base, 'Calibre Settings')) + + name = '%s-portable-%s.zip' % (APPNAME, VERSION) + name = j(env.dist, name) + with zipfile.ZipFile(name, 'w', zipfile.ZIP_STORED) as zf: + add_dir_to_zip(zf, base, 'Calibre Portable') + + env.portable_uncompressed_size = os.path.getsize(name) + subprocess.check_call([PREFIX + r'\bin\elzma.exe', '-9', '--lzip', name]) + + +def sign_files(env, files): + args = ['signtool.exe', 'sign', '/a', '/fd', 'sha256', '/td', 'sha256', '/d', + 'calibre - E-book management', '/du', + 'https://calibre-ebook.com', '/tr'] + + def runcmd(cmd): + for timeserver in ('http://sha256timestamp.ws.symantec.com/sha256/timestamp', 'http://timestamp.comodoca.com/rfc3161',): + try: + subprocess.check_call(cmd + [timeserver] + list(files)) + break + except subprocess.CalledProcessError: + print ('Signing failed, retrying with different timestamp server') + else: + raise SystemExit('Signing failed') + + runcmd(args) + + +def sign_installers(env): + printf('Signing installers...') + installers = set() + for f in glob.glob(j(env.dist, '*')): + if f.rpartition('.')[-1].lower() in {'exe', 'msi'}: + installers.add(f) + else: + os.remove(f) + if not installers: + raise ValueError('No installers found') + sign_files(env, installers) + + +def add_dir_to_zip(zf, path, prefix=''): + ''' + Add a directory recursively to the zip file with an optional prefix. + ''' + if prefix: + zi = zipfile.ZipInfo(prefix + '/') + zi.external_attr = 16 + zf.writestr(zi, '') + cwd = os.path.abspath(os.getcwd()) + try: + os.chdir(path) + fp = (prefix + ('/' if prefix else '')).replace('//', '/') + for f in os.listdir('.'): + arcname = fp + f + if os.path.isdir(f): + add_dir_to_zip(zf, f, prefix=arcname) + else: + zf.write(f, arcname) + finally: + os.chdir(cwd) + + +def build_utils(env): + + def build(src, name, subsys='CONSOLE', libs='setupapi.lib'.split()): + printf('Building ' + name) + obj = j(env.obj_dir, (src) + '.obj') + cflags = '/c /EHsc /MD /W3 /Ox /nologo /D_UNICODE'.split() + ftype = '/T' + ('c' if src.endswith('.c') else 'p') + cmd = ['cl.exe'] + cflags + ['/Fo' + obj, ftype + src] + run(*cmd) + exe = j(env.dll_dir, name) + mf = exe + '.manifest' + with open(mf, 'wb') as f: + f.write(EXE_MANIFEST) + cmd = ['link.exe'] + [ + '/MACHINE:' + machine, + '/SUBSYSTEM:' + subsys, '/RELEASE', '/MANIFEST:EMBED', '/MANIFESTINPUT:' + mf, + '/OUT:' + exe] + [embed_resources(env, exe), obj] + libs + run(*cmd) + base = d(a(__file__)) + build(j(base, 'file_dialogs.cpp'), 'calibre-file-dialog.exe', 'WINDOWS', 'Ole32.lib Shell32.lib'.split()) + build(j(base, 'eject.c'), 'calibre-eject.exe') + + +def build_launchers(env, debug=False): + if not os.path.exists(env.obj_dir): + os.makedirs(env.obj_dir) + dflags = (['/Zi'] if debug else []) + dlflags = (['/DEBUG'] if debug else ['/INCREMENTAL:NO']) + base = d(a(__file__)) + sources = [j(base, x) for x in ['util.c', ]] + objects = [j(env.obj_dir, b(x) + '.obj') for x in sources] + cflags = '/c /EHsc /W3 /Ox /nologo /D_UNICODE'.split() + cflags += ['/DPYDLL="python%s.dll"' % env.py_ver.replace('.', ''), '/I%s/include' % env.python_base] + for src, obj in zip(sources, objects): + cmd = ['cl.exe'] + cflags + dflags + ['/MD', '/Fo' + obj, '/Tc' + src] + run(*cmd) + + dll = j(env.obj_dir, 'calibre-launcher.dll') + ver = '.'.join(VERSION.split('.')[:2]) + cmd = ['link.exe', '/DLL', '/VERSION:' + ver, '/LTCG', '/OUT:' + dll, + '/nologo', '/MACHINE:' + machine] + dlflags + objects + \ + [embed_resources(env, dll), + '/LIBPATH:%s/libs' % env.python_base, + 'delayimp.lib', 'user32.lib', 'shell32.lib', + 'python%s.lib' % env.py_ver.replace('.', ''), + '/delayload:python%s.dll' % env.py_ver.replace('.', '')] + printf('Linking calibre-launcher.dll') + run(*cmd) + + src = j(base, 'main.c') + shutil.copy2(dll, env.dll_dir) + basenames, modules, functions = calibre_constants['basenames'], calibre_constants['modules'], calibre_constants['functions'] + for typ in ('console', 'gui', ): + printf('Processing %s launchers' % typ) + subsys = 'WINDOWS' if typ == 'gui' else 'CONSOLE' + for mod, bname, func in zip(modules[typ], basenames[typ], functions[typ]): + cflags = '/c /EHsc /MT /W3 /O1 /nologo /D_UNICODE /DUNICODE /GS-'.split() + if typ == 'gui': + cflags += ['/DGUI_APP='] + + cflags += ['/DMODULE="%s"' % mod, '/DBASENAME="%s"' % bname, + '/DFUNCTION="%s"' % func] + dest = j(env.obj_dir, bname + '.obj') + printf('Compiling', bname) + cmd = ['cl.exe'] + cflags + dflags + ['/Tc' + src, '/Fo' + dest] + run(*cmd) + exe = j(env.base, bname + '.exe') + lib = dll.replace('.dll', '.lib') + u32 = ['user32.lib'] + printf('Linking', bname) + mf = dest + '.manifest' + with open(mf, 'wb') as f: + f.write(EXE_MANIFEST) + cmd = ['link.exe'] + [ + '/MACHINE:' + machine, '/NODEFAULTLIB', '/ENTRY:start_here', + '/LIBPATH:' + env.obj_dir, '/SUBSYSTEM:' + subsys, + '/LIBPATH:%s/libs' % env.python_base, '/RELEASE', + '/MANIFEST:EMBED', '/MANIFESTINPUT:' + mf, + 'user32.lib', 'kernel32.lib', + '/OUT:' + exe] + u32 + dlflags + [embed_resources(env, exe), dest, lib] + run(*cmd) + + +def add_to_zipfile(zf, name, base, zf_names): + abspath = j(base, name) + name = name.replace(os.sep, '/') + if name in zf_names: + raise ValueError('Already added %r to zipfile [%r]' % (name, abspath)) + zinfo = zipfile.ZipInfo(filename=name, date_time=(1980, 1, 1, 0, 0, 0)) + + if os.path.isdir(abspath): + if not os.listdir(abspath): + return + zinfo.external_attr = 0o700 << 16 + zf.writestr(zinfo, '') + for x in os.listdir(abspath): + add_to_zipfile(zf, name + os.sep + x, base, zf_names) + else: + ext = os.path.splitext(name)[1].lower() + if ext in ('.dll',): + raise ValueError('Cannot add %r to zipfile' % abspath) + zinfo.external_attr = 0o600 << 16 + if ext in ('.py', '.pyc', '.pyo'): + with open(abspath, 'rb') as f: + zf.writestr(zinfo, f.read()) + + zf_names.add(name) + + +def archive_lib_dir(env): + printf('Putting all python code into a zip file for performance') + zf_names = set() + with zipfile.ZipFile(env.pylib, 'w', zipfile.ZIP_STORED) as zf: + # Add everything in Lib except site-packages to the zip file + for x in os.listdir(env.lib_dir): + if x == 'site-packages': + continue + add_to_zipfile(zf, x, env.lib_dir, zf_names) + + sp = j(env.lib_dir, 'site-packages') + # Special handling for pywin32 + handled = {'pywin32.pth', 'win32'} + base = j(sp, 'win32', 'lib') + for x in os.listdir(base): + if os.path.splitext(x)[1] not in ('.exe',): + add_to_zipfile(zf, x, base, zf_names) + base = os.path.dirname(base) + for x in os.listdir(base): + if not os.path.isdir(j(base, x)): + if os.path.splitext(x)[1] not in ('.exe',): + add_to_zipfile(zf, x, base, zf_names) + + # We dont want the site.py (if any) from site-packages + handled.add('site.pyo') + + # The rest of site-packages + for x in os.listdir(sp): + if x in handled or x.endswith('.egg-info'): + continue + absp = j(sp, x) + if os.path.isdir(absp): + if not os.listdir(absp): + continue + add_to_zipfile(zf, x, sp, zf_names) + else: + add_to_zipfile(zf, x, sp, zf_names) + + shutil.rmtree(env.lib_dir) + + +def copy_crt(env): + printf('Copying CRT...') + plat = ('x64' if is64bit else 'x86') + for key, val in os.environ.items(): + if 'COMNTOOLS' in key.upper(): + redist_dir = os.path.dirname(os.path.dirname(val.rstrip(os.sep))) + redist_dir = os.path.join(redist_dir, 'VC', 'Redist', 'MSVC') + vc_path = glob.glob(os.path.join(redist_dir, '*', plat, '*.CRT'))[0] + break + else: + raise SystemExit('Could not find Visual Studio redistributable CRT') + + sdk_path = os.path.join( + os.environ['UNIVERSALCRTSDKDIR'], 'Redist', os.environ['WINDOWSSDKVERSION'], + 'ucrt', 'DLLs', plat) + if not os.path.exists(sdk_path): + raise SystemExit('Windows 10 Universal CRT redistributable not found at: %r' % sdk_path) + for dll in glob.glob(os.path.join(sdk_path, '*.dll')): + shutil.copy2(dll, env.dll_dir) + os.chmod(os.path.join(env.dll_dir, b(dll)), stat.S_IRWXU) + for dll in glob.glob(os.path.join(vc_path, '*.dll')): + bname = os.path.basename(dll) + if not bname.startswith('vccorlib') and not bname.startswith('concrt'): + # Those two DLLs are not required vccorlib is for the CORE CLR + # I think concrt is the concurrency runtime for C++ which I believe + # nothing in calibre currently uses + shutil.copy(dll, env.dll_dir) + os.chmod(os.path.join(env.dll_dir, bname), stat.S_IRWXU) + + +def sign_executables(env): + files_to_sign = [] + for path in walk(env.base): + if path.lower().endswith('.exe'): + files_to_sign.append(path) + printf('Signing {} exe files'.format(len(files_to_sign))) + sign_files(env, files_to_sign) + + +def main(): + ext_dir = globals()['ext_dir'] + args = globals()['args'] + run_tests = iv['run_tests'] + env = Env(build_dir()) + initbase(env) + build_launchers(env) + build_utils(env) + freeze(env, ext_dir) + embed_manifests(env) + copy_crt(env) + archive_lib_dir(env) + if not args.skip_tests: + run_tests(os.path.join(env.base, 'calibre-debug.exe'), env.base) + if args.sign_installers: + sign_executables(env) + create_installer(env) + if not is64bit: + build_portable(env) + build_portable_installer(env) + if args.sign_installers: + sign_installers(env) diff --git a/bypy/windows/eject.c b/bypy/windows/eject.c new file mode 100644 index 0000000000..dcff769ad3 --- /dev/null +++ b/bypy/windows/eject.c @@ -0,0 +1,423 @@ +/* + * eject.c + * Copyright (C) 2013 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "Windows.h" +#include +#include +#include +#include +#include +#include + +#define BUFSIZE 4096 +#define LOCK_TIMEOUT 10000 // 10 Seconds +#define LOCK_RETRIES 20 + +#define BOOL2STR(x) ((x) ? L"True" : L"False") + +// Error handling {{{ + +static void show_error(LPCWSTR msg) { + MessageBeep(MB_ICONERROR); + MessageBoxW(NULL, msg, L"Error", MB_OK|MB_ICONERROR); +} + +static void show_detailed_error(LPCWSTR preamble, LPCWSTR msg, int code) { + LPWSTR buf; + buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)* + (wcslen(msg) + wcslen(preamble) + 80)); + + _snwprintf_s(buf, + LocalSize(buf) / sizeof(WCHAR), _TRUNCATE, + L"%s\r\n %s (Error Code: %d)\r\n", + preamble, msg, code); + + show_error(buf); + LocalFree(buf); +} + +static void print_detailed_error(LPCWSTR preamble, LPCWSTR msg, int code) { + fwprintf_s(stderr, L"%s\r\n %s (Error Code: %d)\r\n", preamble, msg, code); + fflush(stderr); +} + +static void show_last_error_crt(LPCWSTR preamble) { + WCHAR buf[BUFSIZE]; + int err = 0; + + _get_errno(&err); + _wcserror_s(buf, BUFSIZE, err); + show_detailed_error(preamble, buf, err); +} + +static void show_last_error(LPCWSTR preamble) { + WCHAR *msg = NULL; + DWORD dw = GetLastError(); + + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, + 0, NULL ); + + show_detailed_error(preamble, msg, (int)dw); +} + +static void print_last_error(LPCWSTR preamble) { + WCHAR *msg = NULL; + DWORD dw = GetLastError(); + + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, + 0, NULL ); + + print_detailed_error(preamble, msg, (int)dw); +} + +// }}} + +static void print_help() { + fwprintf_s(stderr, L"Usage: calibre-eject.exe drive-letter1 [drive-letter2 drive-letter3 ...]"); +} + +static LPWSTR root_path = L"X:\\", device_path = L"X:", volume_access_path = L"\\\\.\\X:"; +static wchar_t dos_device_name[MAX_PATH]; +static UINT drive_type = 0; +static long device_number = -1; +static DEVINST dev_inst = 0, dev_inst_parent = 0; + +// Unmount and eject volumes (drives) {{{ +static HANDLE open_volume(wchar_t drive_letter) { + DWORD access_flags; + + switch(drive_type) { + case DRIVE_REMOVABLE: + access_flags = GENERIC_READ | GENERIC_WRITE; + break; + case DRIVE_CDROM: + access_flags = GENERIC_READ; + break; + default: + fwprintf_s(stderr, L"Cannot eject %c: Drive type is incorrect.\r\n", drive_letter); + fflush(stderr); + return INVALID_HANDLE_VALUE; + } + + return CreateFileW(volume_access_path, access_flags, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); +} + +static BOOL lock_volume(HANDLE volume) { + DWORD bytes_returned; + DWORD sleep_amount = LOCK_TIMEOUT / LOCK_RETRIES; + int try_count; + + // Do this in a loop until a timeout period has expired + for (try_count = 0; try_count < LOCK_RETRIES; try_count++) { + if (DeviceIoControl(volume, + FSCTL_LOCK_VOLUME, + NULL, 0, + NULL, 0, + &bytes_returned, + NULL)) + return TRUE; + + Sleep(sleep_amount); + } + + return FALSE; +} + +static BOOL dismount_volume(HANDLE volume) { + DWORD bytes_returned; + + return DeviceIoControl( volume, + FSCTL_DISMOUNT_VOLUME, + NULL, 0, + NULL, 0, + &bytes_returned, + NULL); +} + +static BOOL disable_prevent_removal_of_volume(HANDLE volume) { + DWORD bytes_returned; + PREVENT_MEDIA_REMOVAL PMRBuffer; + + PMRBuffer.PreventMediaRemoval = FALSE; + + return DeviceIoControl( volume, + IOCTL_STORAGE_MEDIA_REMOVAL, + &PMRBuffer, sizeof(PREVENT_MEDIA_REMOVAL), + NULL, 0, + &bytes_returned, + NULL); +} + +static BOOL auto_eject_volume(HANDLE volume) { + DWORD bytes_returned; + + return DeviceIoControl( volume, + IOCTL_STORAGE_EJECT_MEDIA, + NULL, 0, + NULL, 0, + &bytes_returned, + NULL); +} + +static BOOL unmount_drive(wchar_t drive_letter, BOOL *remove_safely, BOOL *auto_eject) { + // Unmount the drive identified by drive_letter. Code adapted from: + // http://support.microsoft.com/kb/165721 + HANDLE volume; + *remove_safely = FALSE; *auto_eject = FALSE; + + volume = open_volume(drive_letter); + if (volume == INVALID_HANDLE_VALUE) return FALSE; + + // Lock and dismount the volume. + if (lock_volume(volume) && dismount_volume(volume)) { + *remove_safely = TRUE; + + // Set prevent removal to false and eject the volume. + if (disable_prevent_removal_of_volume(volume) && auto_eject_volume(volume)) + *auto_eject = TRUE; + } + CloseHandle(volume); + return TRUE; + +} +// }}} + +// Eject USB device {{{ +static void get_device_number(wchar_t drive_letter) { + HANDLE volume; + DWORD bytes_returned = 0; + STORAGE_DEVICE_NUMBER sdn; + + volume = CreateFileW(volume_access_path, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (volume == INVALID_HANDLE_VALUE) { + print_last_error(L"Failed to open volume while getting device number"); + return; + } + + if (DeviceIoControl(volume, + IOCTL_STORAGE_GET_DEVICE_NUMBER, + NULL, 0, &sdn, sizeof(sdn), + &bytes_returned, NULL)) + device_number = sdn.DeviceNumber; + CloseHandle(volume); +} + +static DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, LPWSTR dos_device_name) { + GUID *guid; + HDEVINFO dev_info; + DWORD index, bytes_returned; + BOOL bRet, is_floppy; + BYTE Buf[1024]; + PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd; + long res; + HANDLE drive; + STORAGE_DEVICE_NUMBER sdn; + SP_DEVICE_INTERFACE_DATA spdid; + SP_DEVINFO_DATA spdd; + DWORD size; + + is_floppy = (wcsstr(dos_device_name, L"\\Floppy") != NULL); // is there a better way? + + switch (drive_type) { + case DRIVE_REMOVABLE: + guid = ( (is_floppy) ? (GUID*)&GUID_DEVINTERFACE_FLOPPY : (GUID*)&GUID_DEVINTERFACE_DISK ); + break; + case DRIVE_FIXED: + guid = (GUID*)&GUID_DEVINTERFACE_DISK; + break; + case DRIVE_CDROM: + guid = (GUID*)&GUID_DEVINTERFACE_CDROM; + break; + default: + fwprintf_s(stderr, L"Invalid drive type at line: %d\r\n", __LINE__); + fflush(stderr); + return 0; + } + + // Get device interface info set handle + // for all devices attached to system + dev_info = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (dev_info == INVALID_HANDLE_VALUE) { + fwprintf_s(stderr, L"Failed to setup class devs at line: %d\r\n", __LINE__); + fflush(stderr); + return 0; + } + + // Retrieve a context structure for a device interface + // of a device information set. + index = 0; + bRet = FALSE; + + pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf; + spdid.cbSize = sizeof(spdid); + + while ( TRUE ) { + bRet = SetupDiEnumDeviceInterfaces(dev_info, NULL, + guid, index, &spdid); + if ( !bRet ) break; + + size = 0; + SetupDiGetDeviceInterfaceDetail(dev_info, + &spdid, NULL, 0, &size, NULL); + + if ( size!=0 && size<=sizeof(Buf) ) { + pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes! + + ZeroMemory((PVOID)&spdd, sizeof(spdd)); + spdd.cbSize = sizeof(spdd); + + res = SetupDiGetDeviceInterfaceDetail(dev_info, &spdid, pspdidd, size, &size, &spdd); + if ( res ) { + drive = CreateFile(pspdidd->DevicePath,0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if ( drive != INVALID_HANDLE_VALUE ) { + bytes_returned = 0; + res = DeviceIoControl(drive, + IOCTL_STORAGE_GET_DEVICE_NUMBER, + NULL, 0, &sdn, sizeof(sdn), + &bytes_returned, NULL); + if ( res ) { + if ( device_number == (long)sdn.DeviceNumber ) { + CloseHandle(drive); + SetupDiDestroyDeviceInfoList(dev_info); + return spdd.DevInst; + } + } + CloseHandle(drive); + } + } + } + index++; + } + + SetupDiDestroyDeviceInfoList(dev_info); + fwprintf_s(stderr, L"Invalid device number at line: %d\r\n", __LINE__); + fflush(stderr); + + return 0; +} + + +static void get_parent_device(wchar_t drive_letter) { + get_device_number(drive_letter); + if (device_number == -1) return; + if (QueryDosDeviceW(device_path, dos_device_name, MAX_PATH) == 0) { + print_last_error(L"Failed to query DOS device name"); + return; + } + + dev_inst = get_dev_inst_by_device_number(device_number, + drive_type, dos_device_name); + if (dev_inst == 0) { + fwprintf_s(stderr, L"Failed to get device by device number"); + fflush(stderr); + return; + } + if (CM_Get_Parent(&dev_inst_parent, dev_inst, 0) != CR_SUCCESS) { + fwprintf_s(stderr, L"Failed to get device parent from CM"); + fflush(stderr); + return; + } +} + +static int eject_device() { + int tries; + CONFIGRET res; + PNP_VETO_TYPE VetoType; + WCHAR VetoNameW[MAX_PATH]; + BOOL success; + + for ( tries = 0; tries < 3; tries++ ) { + VetoNameW[0] = 0; + + res = CM_Request_Device_EjectW(dev_inst_parent, + &VetoType, VetoNameW, MAX_PATH, 0); + + success = (res==CR_SUCCESS && + VetoType==PNP_VetoTypeUnknown); + if ( success ) { + break; + } + + Sleep(500); // required to give the next tries a chance! + } + if (!success) { + fwprintf_s(stderr, L"CM_Request_Device_Eject failed after three tries\r\n"); + fflush(stderr); + } + + return (success) ? 0 : 1; +} + +// }}} + +int wmain(int argc, wchar_t *argv[ ]) { + int i = 0; + wchar_t drive_letter; + BOOL remove_safely, auto_eject; + + // Validate command line arguments + if (argc < 2) { print_help(); return 1; } + for (i = 1; i < argc; i++) { + if (wcsnlen_s(argv[i], 2) != 1) { print_help(); return 1; } + } + + // Unmount all mounted volumes and eject volume media + for (i = 1; i < argc; i++) { + drive_letter = *argv[i]; + root_path[0] = drive_letter; + device_path[0] = drive_letter; + volume_access_path[4] = drive_letter; + drive_type = GetDriveTypeW(root_path); + if (i == 1 && device_number == -1) { + get_parent_device(drive_letter); + } + if (device_number != -1) { + unmount_drive(drive_letter, &remove_safely, &auto_eject); + fwprintf_s(stdout, L"Unmounting: %c: Remove safely: %s Media Ejected: %s\r\n", + drive_letter, BOOL2STR(remove_safely), BOOL2STR(auto_eject)); + fflush(stdout); + } + } + + // Eject the parent USB device + if (device_number == -1) { + fwprintf_s(stderr, L"Cannot eject, failed to get device number\r\n"); + fflush(stderr); + return 1; + } + + if (dev_inst_parent == 0) { + fwprintf_s(stderr, L"Cannot eject, failed to get device parent\r\n"); + fflush(stderr); + return 1; + } + + return eject_device(); +} + + diff --git a/bypy/windows/en-us.xml b/bypy/windows/en-us.xml new file mode 100644 index 0000000000..9c47b99dbe --- /dev/null +++ b/bypy/windows/en-us.xml @@ -0,0 +1,9 @@ + + + Click Advanced to change installation settings. + Computing space requirements, this may take up to five minutes... + Computing space requirements, this may take up to five minutes... + Computing space requirements, this may take up to five minutes... + Please wait while the installer finishes determining your disk space requirements, this may take up to five minutes... + + diff --git a/bypy/windows/file_dialogs.cpp b/bypy/windows/file_dialogs.cpp new file mode 100644 index 0000000000..0d200710be --- /dev/null +++ b/bypy/windows/file_dialogs.cpp @@ -0,0 +1,355 @@ +/* + * file_dialogs.c + * Copyright (C) 2016 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#ifndef _UNICODE +#define _UNICODE +#endif +#include +#include +#include +#include +#include +#include +#include + +#define PRINTERR(x) fprintf(stderr, "%s", x); fflush(stderr); +#define SECRET_SIZE 32 + +void set_dpi_aware() { + // Try SetProcessDpiAwareness first + HINSTANCE sh_core = LoadLibraryW(L"Shcore.dll"); + + if (sh_core) { + enum ProcessDpiAwareness + { + ProcessDpiUnaware = 0, + ProcessSystemDpiAware = 1, + ProcessPerMonitorDpiAware = 2 + }; + + typedef HRESULT (WINAPI* SetProcessDpiAwarenessFuncType)(ProcessDpiAwareness); + SetProcessDpiAwarenessFuncType SetProcessDpiAwarenessFunc = reinterpret_cast(GetProcAddress(sh_core, "SetProcessDpiAwareness")); + + if (SetProcessDpiAwarenessFunc) { + // We only check for E_INVALIDARG because we would get + // E_ACCESSDENIED if the DPI was already set previously + // and S_OK means the call was successful + if (SetProcessDpiAwarenessFunc(ProcessPerMonitorDpiAware) == E_INVALIDARG) { + PRINTERR("Failed to set process DPI awareness using SetProcessDpiAwareness"); + } else { + FreeLibrary(sh_core); + return; + } + } + + FreeLibrary(sh_core); + } + + // Fall back to SetProcessDPIAware if SetProcessDpiAwareness + // is not available on this system + HINSTANCE user32 = LoadLibraryW(L"user32.dll"); + + if (user32) { + typedef BOOL (WINAPI* SetProcessDPIAwareFuncType)(void); + SetProcessDPIAwareFuncType SetProcessDPIAwareFunc = reinterpret_cast(GetProcAddress(user32, "SetProcessDPIAware")); + + if (SetProcessDPIAwareFunc) { + if (!SetProcessDPIAwareFunc()) { + PRINTERR("Failed to set process DPI awareness using SetProcessDPIAware"); + } + } + + FreeLibrary(user32); + } +} + +bool write_bytes(HANDLE pipe, DWORD sz, const char* buf) { + DWORD written = 0; + if (!WriteFile(pipe, buf, sz, &written, NULL)) { + fprintf(stderr, "Failed to write to pipe. GetLastError()=%d\n", GetLastError()); fflush(stderr); return false; + } + if (written != sz) { + fprintf(stderr, "Failed to write to pipe. Incomplete write, leftover bytes: %d", sz - written); fflush(stderr); return false; + } + return true; +} + +bool read_bytes(size_t sz, char* buf, bool allow_incomplete=false) { + char *ptr = buf, *limit = buf + sz; + while(limit > ptr && !feof(stdin) && !ferror(stdin)) { + ptr += fread(ptr, 1, limit - ptr, stdin); + } + if (ferror(stdin)) { PRINTERR("Failed to read from stdin!"); return false; } + if (ptr - buf != sz) { if (!allow_incomplete) PRINTERR("Truncated input!"); return false; } + return true; +} + +bool from_utf8(size_t sz, const char *src, LPWSTR* ans) { + int asz = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, (int)sz, NULL, 0); + if (!asz) { PRINTERR("Failed to get size of UTF-8 string"); return false; } + *ans = (LPWSTR)calloc(asz+1, sizeof(wchar_t)); + if(*ans == NULL) { PRINTERR("Out of memory!"); return false; } + asz = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, (int)sz, *ans, asz); + if (!asz) { PRINTERR("Failed to convert UTF-8 string"); return false; } + return true; +} + +char* to_utf8(LPCWSTR src, int *sz) { + // Convert to a null-terminated UTF-8 encoded bytearray, allocated on the heap + char *ans = NULL; + *sz = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL); + if (!*sz) { PRINTERR("Failed to get size of UTF-16 string"); return NULL; } + ans = (char*)calloc((*sz) + 1, sizeof(char)); + if (ans == NULL) { PRINTERR("Out of memory!"); return NULL; } + *sz = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, ans, *sz, NULL, NULL); + if (!*sz) { PRINTERR("Failed to convert UTF-16 string"); return NULL; } + return ans; +} + +static char* rsbuf = NULL; + +bool read_string(unsigned short sz, LPWSTR* ans) { + memset(rsbuf, 0, 65537); + if (!read_bytes(sz, rsbuf)) return false; + if (!from_utf8(sz, rsbuf, ans)) return false; + return true; +} + +COMDLG_FILTERSPEC *read_file_types(UINT *num_file_types) { + char buf[10] = {0}; + COMDLG_FILTERSPEC *ans = NULL; + + if(!read_bytes(sizeof(unsigned short), buf)) return NULL; + *num_file_types = *((unsigned short*)buf); + if (*num_file_types < 1 || *num_file_types > 500) { PRINTERR("Invalid number of file types"); return NULL; } + ans = (COMDLG_FILTERSPEC*)calloc((*num_file_types) + 1, sizeof(COMDLG_FILTERSPEC)); + if (ans == NULL) { PRINTERR("Out of memory!"); return NULL; } + + for(unsigned short i = 0; i < *num_file_types; i++) { + if(!read_bytes(sizeof(unsigned short), buf)) return NULL; + if(!read_string(*((unsigned short*)buf), (LPWSTR*)&(ans[i].pszName))) return NULL; + if(!read_bytes(sizeof(unsigned short), buf)) return NULL; + if(!read_string(*((unsigned short*)buf), (LPWSTR*)&(ans[i].pszSpec))) return NULL; + } + return ans; +} + +static void print_com_error(HRESULT hr, const char *msg) { + _com_error err(hr); + LPCWSTR emsg = (LPCWSTR) err.ErrorMessage(); + int sz = 0; + const char *buf = to_utf8(emsg, &sz); + if (buf == NULL) { fprintf(stderr, "%s", msg); } + else { fprintf(stderr, "%s: (HRESULT=0x%x) %s\n", msg, hr, buf); } + fflush(stderr); +} + +#define REPORTERR(hr, x) { print_com_error(hr, x); ret = 1; goto error; } +#define CALLCOM(x, err) hr = x; if(FAILED(hr)) REPORTERR(hr, err) + +int show_dialog(HANDLE pipe, char *secret, HWND parent, bool save_dialog, LPWSTR title, LPWSTR folder, LPWSTR filename, LPWSTR save_path, bool multiselect, bool confirm_overwrite, bool only_dirs, bool no_symlinks, COMDLG_FILTERSPEC *file_types, UINT num_file_types, LPWSTR default_extension) { + int ret = 0, name_sz = 0; + IFileDialog *pfd = NULL; + IShellItemArray *items = NULL; + IShellItem *item = NULL, *folder_item = NULL, *save_path_item = NULL; + char *path = NULL; + DWORD options = 0, item_count = 0; + LPWSTR name = NULL; + HRESULT hr = S_OK; + hr = CoInitialize(NULL); + if (FAILED(hr)) { PRINTERR("Failed to initialize COM"); return 1; } + + CALLCOM(CoCreateInstance((save_dialog ? CLSID_FileSaveDialog : CLSID_FileOpenDialog), + NULL, CLSCTX_INPROC_SERVER, (save_dialog ? IID_IFileSaveDialog : IID_IFileOpenDialog), + reinterpret_cast(&pfd)), + "Failed to create COM object for file dialog") + CALLCOM(pfd->GetOptions(&options), "Failed to get options") + options |= FOS_PATHMUSTEXIST; + if (no_symlinks) options |= FOS_NODEREFERENCELINKS; + if (save_dialog) { + options |= FOS_NOREADONLYRETURN; + if (confirm_overwrite) options |= FOS_OVERWRITEPROMPT; + if (save_path != NULL) { + hr = SHCreateItemFromParsingName(save_path, NULL, IID_IShellItem, reinterpret_cast(&save_path_item)); + // Failure to set initial save path is not critical + if (SUCCEEDED(hr)) ((IFileSaveDialog*)pfd)->SetSaveAsItem(save_path_item); + } + } else { + if (multiselect) options |= FOS_ALLOWMULTISELECT; + if (only_dirs) options |= FOS_PICKFOLDERS; + options |= FOS_FILEMUSTEXIST; + } + CALLCOM(pfd->SetOptions(options), "Failed to set options") + if (title != NULL) { CALLCOM(pfd->SetTitle(title), "Failed to set title") } + if (folder != NULL) { + hr = SHCreateItemFromParsingName(folder, NULL, IID_IShellItem, reinterpret_cast(&folder_item)); + // Failure to set initial folder is not critical + if (SUCCEEDED(hr)) pfd->SetFolder(folder_item); + } + if (filename != NULL) pfd->SetFileName(filename); // Failure is not critical + if (!(options & FOS_PICKFOLDERS) && file_types != NULL && num_file_types > 0) { + CALLCOM(pfd->SetFileTypes(num_file_types, file_types), "Failed to set file types") + CALLCOM(pfd->SetFileTypeIndex(1), "Failed to set file type index") + } + if (default_extension != NULL) { + CALLCOM(pfd->SetDefaultExtension(default_extension), "Failed to set default extension") + } + hr = pfd->Show(parent); + if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) goto error; + if (FAILED(hr)) REPORTERR(hr, "Failed to show dialog") + + if (save_dialog) { + CALLCOM(pfd->GetResult(&item), "Failed to get save dialog result"); + CALLCOM(item->GetDisplayName(SIGDN_FILESYSPATH, &name), "Failed to get display name of save dialog result"); + path = to_utf8(name, &name_sz); + CoTaskMemFree(name); name = NULL; + if (path == NULL) return 1; + if (!write_bytes(pipe, SECRET_SIZE+1, secret)) return 1; + if (!write_bytes(pipe, name_sz, path)) return 1; + } else { + CALLCOM(((IFileOpenDialog*)pfd)->GetResults(&items), "Failed to get dialog results"); + CALLCOM(items->GetCount(&item_count), "Failed to get count of results"); + if (item_count > 0) { + if (!write_bytes(pipe, SECRET_SIZE+1, secret)) return 1; + for (DWORD i = 0; i < item_count; i++) { + CALLCOM(items->GetItemAt(i, &item), "Failed to get result item"); + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) { + path = to_utf8(name, &name_sz); + CoTaskMemFree(name); name = NULL; + if (path == NULL) return 1; + if (!write_bytes(pipe, name_sz, path)) return 1; + } + } + } + } + +error: + if(pfd) pfd->Release(); + CoUninitialize(); + return ret; +} +#define READ(x, y) if (!read_bytes((x), (y))) return 1; +#define CHECK_KEY(x) (key_size == sizeof(x) - 1 && memcmp(buf, x, sizeof(x) - 1) == 0) +#define READSTR(x) READ(sizeof(unsigned short), buf); if(!read_string(*((unsigned short*)buf), &x)) return 1; +#define SETBINARY(x) if(_setmode(_fileno(x), _O_BINARY) == -1) { PRINTERR("Failed to set binary mode"); return 1; } +#define READBOOL(x) READ(1, buf); x = !!buf[0]; + +HANDLE open_named_pipe(LPWSTR pipename) { + HANDLE ans = INVALID_HANDLE_VALUE; + while(true) { + ans = CreateFileW(pipename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (ans != INVALID_HANDLE_VALUE) break; + if (GetLastError() != ERROR_PIPE_BUSY) { + fprintf(stderr, "Failed to open pipe. GetLastError()=%d\n", GetLastError()); fflush(stderr); return ans; + } + if (!WaitNamedPipeW(pipename, 20000)) { + fprintf(stderr, "Failed to open pipe. 20 second wait timed out. GetLastError()=%d\n", GetLastError()); fflush(stderr); return ans; + } + } + return ans; +} + +typedef HRESULT (__stdcall *app_uid_func)(PCWSTR app_uid); + +bool set_app_uid(LPWSTR app_uid) { + // Not available on vista so we have to load the function dynamically + bool ok = false; + HINSTANCE dll = LoadLibraryW(L"Shell32.dll"); + if (dll != NULL) { + app_uid_func f = (app_uid_func)GetProcAddress(dll, "SetCurrentProcessExplicitAppUserModelID"); + if (f != NULL) ok = f(app_uid) == S_OK; + FreeLibrary(dll); dll = NULL; + } + return ok; +} + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { + char buf[257] = {0}, secret[SECRET_SIZE + 1] = {0}; + size_t key_size = 0; + HWND parent = NULL; + bool save_dialog = false, multiselect = false, confirm_overwrite = false, only_dirs = false, no_symlinks = false; + unsigned short len = 0; + LPWSTR title = NULL, folder = NULL, filename = NULL, save_path = NULL, echo = NULL, pipename = NULL, default_extension = NULL, app_uid = NULL; + COMDLG_FILTERSPEC *file_types = NULL; + UINT num_file_types = 0; + HANDLE pipe = INVALID_HANDLE_VALUE; + + SETBINARY(stdout); SETBINARY(stdin); SETBINARY(stderr); + // The calibre executables call SetDllDirectory, we unset it here just in + // case it interferes with some idiotic shell extension or the other + SetDllDirectory(NULL); + rsbuf = (char*)calloc(65537, sizeof(char)); + if(rsbuf == NULL) { PRINTERR("Out of memory!"); return 1; } + + while(!feof(stdin)) { + memset(buf, 0, sizeof(buf)); + if(!read_bytes(1, buf, true)) { if (feof(stdin)) break; return 1;} + key_size = (size_t)buf[0]; + READ(key_size, buf); + if CHECK_KEY("HWND") { + READ(sizeof(HWND), buf); +#pragma warning( push ) +#pragma warning( disable : 4312) + if (sizeof(HWND) == 8) parent = (HWND)*((__int64*)buf); + else if (sizeof(HWND) == 4) parent = (HWND)*((__int32*)buf); + else { fprintf(stderr, "Unknown pointer size: %zd", sizeof(HWND)); fflush(stderr); return 1;} +#pragma warning( pop ) + } + + else if CHECK_KEY("PIPENAME") { READSTR(pipename); pipe = open_named_pipe(pipename); if (pipe == INVALID_HANDLE_VALUE) return 1; } + + else if CHECK_KEY("SECRET") { if(!read_bytes(SECRET_SIZE, secret)) return 1; } + + else if CHECK_KEY("APP_UID") { READSTR(app_uid) } + + else if CHECK_KEY("TITLE") { READSTR(title) } + + else if CHECK_KEY("FOLDER") { READSTR(folder) } + + else if CHECK_KEY("FILENAME") { READSTR(filename) } + + else if CHECK_KEY("SAVE_PATH") { READSTR(save_path) } + + else if CHECK_KEY("SAVE_AS") { READBOOL(save_dialog) } + + else if CHECK_KEY("MULTISELECT") { READBOOL(multiselect) } + + else if CHECK_KEY("CONFIRM_OVERWRITE") { READBOOL(confirm_overwrite) } + + else if CHECK_KEY("ONLY_DIRS") { READBOOL(only_dirs) } + + else if CHECK_KEY("NO_SYMLINKS") { READBOOL(no_symlinks) } + + else if CHECK_KEY("FILE_TYPES") { file_types = read_file_types(&num_file_types); if (file_types == NULL) return 1; } + + else if CHECK_KEY("DEFAULT_EXTENSION") { READSTR(default_extension) } + + else if CHECK_KEY("ECHO") { READSTR(echo) } + + else { + PRINTERR("Unknown key"); + return 1; + } + } + + if (pipe == INVALID_HANDLE_VALUE) { PRINTERR("No pipename received"); return 1; } + if (secret == NULL) { PRINTERR("No secret received"); return 1; } + + if (echo != NULL) { + int echo_sz = 0; + char *echo_buf = to_utf8(echo, &echo_sz); + if (!write_bytes(pipe, SECRET_SIZE+1, secret)) return 1; + return write_bytes(pipe, echo_sz, echo_buf) ? 0 : 1; + } + if (app_uid != NULL) { + // dont check return status as failure is not critical + set_app_uid(app_uid); + } + + set_dpi_aware(); + return show_dialog(pipe, secret, parent, save_dialog, title, folder, filename, save_path, multiselect, confirm_overwrite, only_dirs, no_symlinks, file_types, num_file_types, default_extension); +} diff --git a/bypy/windows/main.c b/bypy/windows/main.c new file mode 100644 index 0000000000..6a70d5cd1e --- /dev/null +++ b/bypy/windows/main.c @@ -0,0 +1,113 @@ +/* + * Copyright 2009 Kovid Goyal + */ + +#ifndef UNICODE +#define UNICODE +#endif +#define WINDOWS_LEAN_AND_MEAN +#include +#include + +static size_t mystrlen(const wchar_t *buf) { + size_t ans = 0; + if (FAILED(StringCbLengthW(buf, 500, &ans))) return 0; + return ans; +} + +static int show_error(const wchar_t *preamble, const wchar_t *msg, const int code) { + wchar_t *buf; + buf = (wchar_t*)LocalAlloc(LMEM_ZEROINIT, sizeof(wchar_t)* + (mystrlen(msg) + mystrlen(preamble) + 80)); + if (!buf) { + MessageBox(NULL, preamble, NULL, MB_OK|MB_ICONERROR); + return code; + } + + MessageBeep(MB_ICONERROR); + wsprintf(buf, L"%s\r\n %s (Error Code: %d)\r\n", preamble, msg, code); + MessageBox(NULL, buf, NULL, MB_OK|MB_ICONERROR); + LocalFree(buf); + return code; +} + +static int show_last_error(wchar_t *preamble) { + wchar_t *msg = NULL; + DWORD dw = GetLastError(); + int ret; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, + 0, + NULL ); + + ret = show_error(preamble, msg, (int)dw); + if (msg != NULL) LocalFree(msg); + return ret; +} + +typedef int (__cdecl *ENTRYPROC)(const char*, const char*, const char*, int); + +static ENTRYPROC load_launcher_dll() { + wchar_t buf[MAX_PATH]; // Cannot use a zero initializer for the array as it generates an implicit call to memset() + wchar_t *dll_point = NULL; + int i = 0; + DWORD sz = 0; + HMODULE dll = 0; + ENTRYPROC entrypoint = NULL; + + if ((sz = GetModuleFileNameW(NULL, buf, MAX_PATH)) >= MAX_PATH - 30) { + show_error(L"Installation directory path too long", L"", 1); + return NULL; + } + + while (sz > 0) { + if (buf[sz] == L'\\' || buf[sz] == L'/') { dll_point = buf + sz + 1; break; } + sz--; + } + if (dll_point == NULL) { + show_error(L"Executable path has no path separators", L"", 1); + return NULL; + } + wsprintf(dll_point, L"%s\0\0", L"app\\DLLs"); + if (SetDllDirectoryW(buf) == 0) { + show_last_error(L"Failed to set DLL directory"); + return NULL; + } + // Have to load ucrtbase manually first, otherwise loading fails on systems where the + // Universal CRT is not installed. + if (!LoadLibraryW(L"ucrtbase.dll")) { + show_last_error(L"Unable to find ucrtbase.dll. You should install all Windows updates on your computer to get this file."); + return NULL; + } + if (!(dll = LoadLibraryW(L"calibre-launcher.dll"))) { + show_last_error(L"Failed to load: calibre-launcher.dll"); + return NULL; + } + if (!(entrypoint = (ENTRYPROC) GetProcAddress(dll, "execute_python_entrypoint"))) { + show_last_error(L"Failed to get the calibre-launcher dll entry point"); + return NULL; + } + return entrypoint; +} + +int __stdcall start_here() { + int ret = 0; + ENTRYPROC entrypoint = load_launcher_dll(); + if (entrypoint) { +#ifdef GUI_APP + // This should really be returning the value set in the WM_QUIT message, but I cannot be bothered figuring out how to get that. + entrypoint(BASENAME, MODULE, FUNCTION, 1); +#else + ret = entrypoint(BASENAME, MODULE, FUNCTION, 0); +#endif + } else ret = 1; + ExitProcess(ret); + return ret; +} diff --git a/bypy/windows/portable-installer.cpp b/bypy/windows/portable-installer.cpp new file mode 100644 index 0000000000..618655f663 --- /dev/null +++ b/bypy/windows/portable-installer.cpp @@ -0,0 +1,618 @@ +#ifndef UNICODE +#define UNICODE +#endif + +#ifndef _UNICODE +#define _UNICODE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "XUnzip.h" + +#define BUFSIZE 4096 + +// Error handling {{{ + +static void show_error(LPCWSTR msg) { + MessageBeep(MB_ICONERROR); + MessageBox(NULL, msg, L"Error", MB_OK|MB_ICONERROR); +} + +static void show_detailed_error(LPCWSTR preamble, LPCWSTR msg, int code) { + LPWSTR buf; + buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)* + (wcslen(msg) + wcslen(preamble) + 80)); + + _snwprintf_s(buf, + LocalSize(buf) / sizeof(WCHAR), _TRUNCATE, + L"%s\r\n %s (Error Code: %d)\r\n", + preamble, msg, code); + + show_error(buf); + LocalFree(buf); +} + +static void show_zip_error(LPCWSTR preamble, LPCWSTR msg, ZRESULT code) { + LPWSTR buf; + char msgbuf[1024] = {0}; + + FormatZipMessage(code, msgbuf, 1024); + + buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)* + (wcslen(preamble) + wcslen(msg) + 1100)); + + _snwprintf_s(buf, + LocalSize(buf) / sizeof(WCHAR), _TRUNCATE, + L"%s\r\n %s (Error: %S)\r\n", + preamble, msg, msgbuf); + + show_error(buf); + LocalFree(buf); +} + +static void show_last_error_crt(LPCWSTR preamble) { + WCHAR buf[BUFSIZE]; + int err = 0; + + _get_errno(&err); + _wcserror_s(buf, BUFSIZE, err); + show_detailed_error(preamble, buf, err); +} + +static void show_last_error(LPCWSTR preamble) { + WCHAR *msg = NULL; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, + 0, NULL ); + + show_detailed_error(preamble, msg, (int)dw); +} + +// }}} + +// Load, decompress and extract data {{{ + +static BOOL load_data(LPVOID *data, DWORD *sz) { + HRSRC rsrc; + HGLOBAL h; + + rsrc = FindResourceW(NULL, L"extra", L"extra"); + if (rsrc == NULL) { show_last_error(L"Failed to find portable data in exe"); return false; } + + h = LoadResource(NULL, rsrc); + if (h == NULL) { show_last_error(L"Failed to load portable data from exe"); return false; } + + *data = LockResource(h); + if (*data == NULL) { show_last_error(L"Failed to lock portable data in exe"); return false; } + + *sz = SizeofResource(NULL, rsrc); + if (sz == 0) { show_last_error(L"Failed to get size of portable data in exe"); return false; } + + return true; +} + +static BOOL unzip(HZIP zipf, int nitems, IProgressDialog *pd) { + int i = 0; + ZRESULT res; + ZIPENTRYW ze; + + for (i = 0; i < nitems; i++) { + res = GetZipItem(zipf, i, &ze); + if (res != ZR_OK) { show_zip_error(L"Failed to get zip item", L"", res); return false;} + + res = UnzipItem(zipf, i, ze.name, 0, ZIP_FILENAME); + if (res != ZR_OK) { CloseZip(zipf); show_zip_error(L"Failed to extract zip item (is your disk full?):", ze.name, res); return false;} + + pd->SetLine(2, ze.name, true, NULL); + pd->SetProgress(i, nitems); + } + + CloseZip(zipf); + + return true; +} + +static HANDLE temp_file(LPWSTR name) { + UINT res; + HANDLE h; + + res = GetTempFileNameW(L".", L"portable_data", 0, name); + + if (res == 0) { show_last_error(L"Failed to create temporary file to decompress portable data"); return INVALID_HANDLE_VALUE; } + + h = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { show_last_error(L"Failed to open temp file to decompress portable data"); } + return h; + +} + +struct DataStream +{ + const unsigned char *in_data; + size_t in_len; + + HANDLE out; + IProgressDialog *pd; +}; + +static int +input_callback(void *ctx, void *buf, size_t * size) +{ + size_t rd = 0; + struct DataStream * ds = (struct DataStream *) ctx; + + rd = (ds->in_len < *size) ? ds->in_len : *size; + + if (rd > 0) { + memcpy(buf, (void*) ds->in_data, rd); + ds->in_data += rd; + ds->in_len -= rd; + } + + *size = rd; + + return 0; +} + +static int output_error_shown = 0; + +static size_t +output_callback(void *ctx, const void *buf, size_t size) +{ + struct DataStream * ds = (struct DataStream *) ctx; + DWORD written = 0; + + if (size > 0) { + if (!WriteFile(ds->out, buf, size, &written, NULL)) { + show_last_error(L"Failed to write uncompressed data to temp file"); + output_error_shown = 1; + return 0; + } + written = SetFilePointer(ds->out, 0, NULL, FILE_CURRENT); + ds->pd->SetProgress(written, UNCOMPRESSED_SIZE); + } + + return size; +} + +static BOOL decompress(LPVOID src, DWORD src_sz, HANDLE out, IProgressDialog *pd) { + elzma_decompress_handle h; + struct DataStream ds; + int rc; + + h = elzma_decompress_alloc(); + + if (h == NULL) { show_error(L"Out of memory"); return false; } + + ds.in_data = (unsigned char*)src; + ds.in_len = src_sz; + ds.out = out; + ds.pd = pd; + + rc = elzma_decompress_run(h, input_callback, (void *) &ds, output_callback, + (void *) &ds, ELZMA_lzip); + + if (rc != ELZMA_E_OK) { + if (!output_error_shown) show_detailed_error(L"Failed to decompress portable data", L"", rc); + elzma_decompress_free(&h); + return false; + } + + elzma_decompress_free(&h); + + return true; +} + +static BOOL extract(LPVOID cdata, DWORD csz) { + HANDLE h; + WCHAR tempnam[MAX_PATH+1] = {0}; + BOOL ret = true; + HZIP zipf; + ZIPENTRYW ze; + ZRESULT res; + int nitems; + HRESULT hr; + IProgressDialog *pd = NULL; + + hr = CoCreateInstance(CLSID_ProgressDialog, NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pd)); + + if (FAILED(hr)) { show_error(L"Failed to create progress dialog"); return false; } + pd->SetTitle(L"Extracting Calibre Portable"); + pd->SetLine(1, L"Decompressing data...", true, NULL); + + h = temp_file(tempnam); + if (h == INVALID_HANDLE_VALUE) return false; + + pd->StartProgressDialog(NULL, NULL, PROGDLG_NORMAL | PROGDLG_AUTOTIME | PROGDLG_NOCANCEL, NULL); + if (!decompress(cdata, csz, h, pd)) { ret = false; goto end; } + SetFilePointer(h, 0, NULL, FILE_BEGIN); + zipf = OpenZip(h, 0, ZIP_HANDLE); + if (zipf == 0) { show_last_error(L"Failed to open zipped portable data"); ret = false; goto end; } + + res = GetZipItem(zipf, -1, &ze); + if (res != ZR_OK) { show_zip_error(L"Failed to get count of items in portable data", L"", res); ret = false; goto end;} + nitems = ze.index; + + pd->SetLine(1, L"Copying files...", true, NULL); + if (!unzip(zipf, nitems, pd)) { ret = false; goto end; } +end: + pd->StopProgressDialog(); + pd->Release(); + CloseHandle(h); + DeleteFile(tempnam); + return ret; +} + +// }}} + +// Find calibre portable directory and install/upgrade into it {{{ + +static BOOL directory_exists( LPCWSTR path ) +{ + if( _waccess_s( path, 0 ) == 0 ) + { + struct _stat status; + _wstat( path, &status ); + return (status.st_mode & S_IFDIR) != 0; + } + + return FALSE; +} + +static BOOL file_exists( LPCWSTR path ) +{ + if( _waccess_s( path, 0 ) == 0 ) + { + struct _stat status; + _wstat( path, &status ); + return (status.st_mode & S_IFREG) != 0; + } + + return FALSE; +} + +static LPWSTR get_directory_from_user() { + WCHAR name[MAX_PATH+1] = {0}; + LPWSTR path = NULL; + PIDLIST_ABSOLUTE ret; + + path = (LPWSTR)calloc(2*MAX_PATH, sizeof(WCHAR)); + if (path == NULL) { show_error(L"Out of memory"); return NULL; } + + int image = 0; + BROWSEINFO bi = { NULL, NULL, name, + L"Select the folder where you want to install or update Calibre Portable", + BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN | BIF_USENEWUI, + NULL, NULL, image }; + + ret = SHBrowseForFolder(&bi); + if (ret == NULL) { + return NULL; + } + + if (!SHGetPathFromIDList(ret, path)) { + show_detailed_error(L"The selected folder is not valid: ", name, 0); + return NULL; + } + + return path; + +} + +static bool is_dots(LPCWSTR name) { + return wcscmp(name, L".") == 0 || wcscmp(name, L"..") == 0; +} + +static bool rmtree(LPCWSTR path) { + SHFILEOPSTRUCTW op; + WCHAR buf[4*MAX_PATH + 2] = {0}; + + if (GetFullPathName(path, 4*MAX_PATH, buf, NULL) == 0) return false; + + op.hwnd = NULL; + op.wFunc = FO_DELETE; + op.pFrom = buf; + op.pTo = NULL; + op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMMKDIR; + op.fAnyOperationsAborted = false; + op.hNameMappings = NULL; + op.lpszProgressTitle = NULL; + + return SHFileOperationW(&op) == 0; +} + +static BOOL find_portable_dir(LPCWSTR base, LPWSTR *result, BOOL *existing) { + WCHAR buf[4*MAX_PATH] = {0}; + + _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\calibre-portable.exe", base); + *existing = true; + + if (file_exists(buf)) { + *result = _wcsdup(base); + if (*result == NULL) { show_error(L"Out of memory"); return false; } + return true; + } + + WIN32_FIND_DATA fdFile; + HANDLE hFind = NULL; + _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\*", base); + + if((hFind = FindFirstFileEx(buf, FindExInfoStandard, &fdFile, FindExSearchLimitToDirectories, NULL, 0)) != INVALID_HANDLE_VALUE) { + do { + if(is_dots(fdFile.cFileName)) continue; + + if(fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\%s\\calibre-portable.exe", base, fdFile.cFileName); + if (file_exists(buf)) { + *result = _wcsdup(buf); + if (*result == NULL) { show_error(L"Out of memory"); return false; } + PathRemoveFileSpec(*result); + FindClose(hFind); + return true; + } + } + } while(FindNextFile(hFind, &fdFile)); + FindClose(hFind); + } + + *existing = false; + _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\Calibre Portable", base); + if (!CreateDirectory(buf, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { + show_last_error(L"Failed to create Calibre Portable folder"); + return false; + } + *result = _wcsdup(buf); + if (*result == NULL) { show_error(L"Out of memory"); return false; } + + return true; +} + +static LPWSTR make_unpack_dir() { + WCHAR buf[4*MAX_PATH] = {0}; + LPWSTR ans = NULL; + + if (directory_exists(L"_unpack_calibre_portable")) + rmtree(L"_unpack_calibre_portable"); + + if (!CreateDirectory(L"_unpack_calibre_portable", NULL) && GetLastError() != ERROR_ALREADY_EXISTS) { + show_last_error(L"Failed to create temporary folder to unpack into"); + return ans; + } + + if (!GetFullPathName(L"_unpack_calibre_portable", 4*MAX_PATH, buf, NULL)) { + show_last_error(L"Failed to resolve path"); + return NULL; + } + + ans = _wcsdup(buf); + if (ans == NULL) show_error(L"Out of memory"); + return ans; + +} + +static BOOL move_program() { + if (MoveFileEx(L"Calibre Portable\\calibre-portable.exe", + L"..\\calibre-portable.exe", MOVEFILE_REPLACE_EXISTING) == 0) { + show_last_error(L"Failed to move calibre-portable.exe, make sure calibre is not running"); + return false; + } + + if (directory_exists(L"..\\Calibre")) { + if (!rmtree(L"..\\Calibre")) { + show_error(L"Failed to delete the Calibre program folder. Make sure calibre is not running."); + return false; + } + } + + if (MoveFileEx(L"Calibre Portable\\Calibre", L"..\\Calibre", 0) == 0) { + Sleep(4000); // Sleep and try again + if (MoveFileEx(L"Calibre Portable\\Calibre", L"..\\Calibre", 0) == 0) { + show_last_error(L"Failed to move calibre program folder. This is usually caused by an antivirus program or a file sync program like DropBox. Turn them off temporarily and try again. Underlying error: "); + return false; + } + } + + if (!directory_exists(L"..\\Calibre Library")) { + MoveFileEx(L"Calibre Portable\\Calibre Library", L"..\\Calibre Library", 0); + } + + if (!directory_exists(L"..\\Calibre Settings")) { + MoveFileEx(L"Calibre Portable\\Calibre Settings", L"..\\Calibre Settings", 0); + } + + return true; +} +// }}} + +static BOOL ensure_not_running(LPCWSTR dest) { + DWORD processes[4096], needed, num; + unsigned int i; + WCHAR name[4*MAX_PATH] = L""; + HANDLE h; + DWORD len; + LPWSTR fname = NULL; + + if ( !EnumProcesses( processes, sizeof(processes), &needed ) ) { + return true; + } + num = needed / sizeof(DWORD); + + for (i = 0; i < num; i++) { + if (processes[i] == 0) continue; + h = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, processes[i] ); + if (h != NULL) { + len = GetProcessImageFileNameW(h, name, 4*MAX_PATH); + CloseHandle(h); + if (len != 0) { + name[len] = 0; + fname = PathFindFileName(name); + if (wcscmp(fname, L"calibre.exe") == 0) { + show_error(L"Calibre appears to be running on your computer. Please quit it before trying to install Calibre Portable."); + return false; + } + } + } + } + + return true; +} + +static void launch_calibre() { + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + + if (CreateProcess(_wcsdup(L"calibre-portable.exe"), NULL, + NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP, + NULL, NULL, &si, &pi) + == 0) { + show_last_error(L"Failed to launch calibre portable"); + } + + // Close process and thread handles. + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + +} + +void makedirs(LPWSTR path) { + WCHAR *p = path; + while (*p) { + if ((*p == L'\\' || *p == L'/') && p != path && *(p-1) != L':') { + *p = 0; + CreateDirectory(path, NULL); + *p = L'\\'; + } + p++; + } + CreateDirectory(path, NULL); +} + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) +{ + + LPVOID cdata = NULL; + DWORD csz = 0; + int ret = 1, argc; + HRESULT hr; + LPWSTR tgt = NULL, dest = NULL, *argv, unpack_dir = NULL; + BOOL existing = false, launch = false, automated = false; + WCHAR buf[4*MAX_PATH] = {0}, mb_msg[4*MAX_PATH] = {0}, fdest[4*MAX_PATH] = {0}; + + if (!load_data(&cdata, &csz)) return ret; + + hr = CoInitialize(NULL); + if (FAILED(hr)) { show_error(L"Failed to initialize COM"); return ret; } + + // Get the target directory for installation + argv = CommandLineToArgvW(GetCommandLine(), &argc); + if (argv == NULL) { show_last_error(L"Failed to get command line"); return ret; } + if (argc > 1) { + tgt = argv[1]; + automated = true; + if (!directory_exists(tgt)) { + if (GetFullPathName(tgt, MAX_PATH*4, fdest, NULL) == 0) { + show_last_error(L"Failed to resolve target folder"); + goto end; + } + makedirs(fdest); + } + + } else { + tgt = get_directory_from_user(); + if (tgt == NULL) goto end; + } + + if (!directory_exists(tgt)) { + show_detailed_error(L"The specified directory does not exist: ", + tgt, 1); + goto end; + } + + // Ensure the path to Calibre Portable is not too long + do { + if (!find_portable_dir(tgt, &dest, &existing)) goto end; + + if (GetFullPathName(dest, MAX_PATH*4, fdest, NULL) == 0) { + show_last_error(L"Failed to resolve target folder"); + goto end; + } + free(dest); dest = NULL; + + if (wcslen(fdest) > 58) { + _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, + L"Path to Calibre Portable (%s) too long. Must be less than 59 characters.", fdest); + if (!existing) RemoveDirectory(fdest); + show_error(buf); + tgt = get_directory_from_user(); + if (tgt == NULL) goto end; + } + } while (wcslen(fdest) > 58); + + // Confirm the user wants to upgrade + if (existing && !automated) { + _snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE, + L"An existing install of Calibre Portable was found at %s. Do you want to upgrade it?", + fdest); + if (MessageBox(NULL, mb_msg, + L"Upgrade Calibre Portable?", MB_ICONEXCLAMATION | MB_YESNO | MB_TOPMOST) != IDYES) + goto end; + } + + if (existing) { + if (!ensure_not_running(fdest)) goto end; + } + + // Make a temp dir to unpack into + if (!SetCurrentDirectoryW(fdest)) { show_detailed_error(L"Failed to change to unzip directory: ", fdest, 0); goto end; } + + if ( (unpack_dir = make_unpack_dir()) == NULL ) goto end; + if (!SetCurrentDirectoryW(unpack_dir)) { show_detailed_error(L"Failed to change to unpack directory: ", fdest, 0); goto end; } + + // Extract files + if (!extract(cdata, csz)) goto end; + + // Move files from temp dir to the install dir + if (!move_program()) goto end; + + ret = 0; + if (!automated) { + _snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE, + L"Calibre Portable successfully installed to %s. Launch calibre?", + fdest); + launch = MessageBox(NULL, mb_msg, + L"Success", MB_ICONINFORMATION | MB_YESNO | MB_TOPMOST) == IDYES; + } + +end: + if (unpack_dir != NULL) { SetCurrentDirectoryW(L".."); rmtree(unpack_dir); free(unpack_dir); } + CoUninitialize(); + if (launch) launch_calibre(); + return ret; +} + + diff --git a/bypy/windows/portable.c b/bypy/windows/portable.c new file mode 100644 index 0000000000..06a27cf659 --- /dev/null +++ b/bypy/windows/portable.c @@ -0,0 +1,154 @@ +#ifndef UNICODE +#define UNICODE +#endif + +#ifndef _UNICODE +#define _UNICODE +#endif + + +#include +#include +#include +#include + +#define BUFSIZE 4096 + +void show_error(LPCTSTR msg) { + MessageBeep(MB_ICONERROR); + MessageBox(NULL, msg, _T("Error"), MB_OK|MB_ICONERROR); +} + +void show_detailed_error(LPCTSTR preamble, LPCTSTR msg, int code) { + LPTSTR buf; + buf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)* + (_tcslen(msg) + _tcslen(preamble) + 80)); + + _sntprintf_s(buf, + LocalSize(buf) / sizeof(TCHAR), _TRUNCATE, + _T("%s\r\n %s (Error Code: %d)\r\n"), + preamble, msg, code); + + show_error(buf); + LocalFree(buf); +} + +void show_last_error_crt(LPCTSTR preamble) { + TCHAR buf[BUFSIZE]; + int err = 0; + + _get_errno(&err); + _tcserror_s(buf, BUFSIZE, err); + show_detailed_error(preamble, buf, err); +} + +void show_last_error(LPCTSTR preamble) { + TCHAR *msg = NULL; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&msg, + 0, NULL ); + + show_detailed_error(preamble, msg, (int)dw); +} + + +LPTSTR get_app_dir() { + LPTSTR buf, buf2, buf3; + DWORD sz; + TCHAR drive[4] = _T("\0\0\0"); + errno_t err; + + buf = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR)); + buf2 = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR)); + buf3 = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR)); + + sz = GetModuleFileName(NULL, buf, BUFSIZE); + + if (sz == 0 || sz > BUFSIZE-1) { + show_error(_T("Failed to get path to calibre-portable.exe")); + ExitProcess(1); + } + + err = _tsplitpath_s(buf, drive, 4, buf2, BUFSIZE, NULL, 0, NULL, 0); + + if (err != 0) { + show_last_error_crt(_T("Failed to split path to calibre-portable.exe")); + ExitProcess(1); + } + + _sntprintf_s(buf3, BUFSIZE-1, _TRUNCATE, _T("%s%s"), drive, buf2); + free(buf); free(buf2); + return buf3; +} + +void launch_calibre(LPCTSTR exe, LPCTSTR config_dir) { + DWORD dwFlags=0; + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL fSuccess; + + if (! SetEnvironmentVariable(_T("CALIBRE_CONFIG_DIRECTORY"), config_dir)) { + show_last_error(_T("Failed to set environment variables")); + ExitProcess(1); + } + + if (! SetEnvironmentVariable(_T("CALIBRE_PORTABLE_BUILD"), exe)) { + show_last_error(_T("Failed to set environment variables")); + ExitProcess(1); + } + + dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + fSuccess = CreateProcess(exe, NULL, + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + dwFlags, // Creation flags http://msdn.microsoft.com/en-us/library/ms684863(v=vs.85).aspx + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi // Pointer to PROCESS_INFORMATION structure + ); + + if (fSuccess == 0) { + show_last_error(_T("Failed to launch the calibre program")); + } + + // Close process and thread handles. + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + +} + + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) +{ + LPTSTR app_dir, config_dir, exe; + + app_dir = get_app_dir(); + config_dir = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR)); + exe = (LPTSTR)calloc(BUFSIZE, sizeof(TCHAR)); + + _sntprintf_s(config_dir, BUFSIZE, _TRUNCATE, _T("%sCalibre Settings"), app_dir); + _sntprintf_s(exe, BUFSIZE, _TRUNCATE, _T("%sCalibre\\calibre.exe"), app_dir); + + launch_calibre(exe, config_dir); + + free(app_dir); free(config_dir); free(exe); + + return 0; +} + + diff --git a/bypy/windows/site.py b/bypy/windows/site.py new file mode 100644 index 0000000000..0842489aeb --- /dev/null +++ b/bypy/windows/site.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python2 +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import sys +import os +import imp + +class PydImporter(object): + + __slots__ = ('items', 'description') + + def __init__(self): + self.items = None + self.description = ('.pyd', 'rb', imp.C_EXTENSION) + + def find_module(self, fullname, path=None): + if self.items is None: + dlls_dir = os.path.join(sys.app_dir, 'app', 'DLLs') + items = self.items = {} + for x in os.listdir(dlls_dir): + lx = x.lower() + if lx.endswith(b'.pyd'): + items[lx[:-4]] = os.path.abspath(os.path.join(dlls_dir, x)) + return self if fullname.lower() in self.items else None + + def load_module(self, fullname): + m = sys.modules.get(fullname) + if m is not None: + return m + try: + path = self.items[fullname.lower()] + except KeyError: + raise ImportError('The native code module %s seems to have disappeared from self.items' % fullname) + package, name = fullname.rpartition(b'.')[::2] + m = imp.load_module(fullname, None, path, self.description) # This inserts the module into sys.modules itself + m.__loader__ = self + m.__package__ = package or None + return m + +def abs__file__(): + """Set all module __file__ attribute to an absolute path""" + for m in sys.modules.values(): + if hasattr(m, '__loader__'): + continue # don't mess with a PEP 302-supplied __file__ + try: + m.__file__ = os.path.abspath(m.__file__) + except AttributeError: + continue + +def aliasmbcs(): + import locale, codecs + enc = locale.getdefaultlocale()[1] + if enc.startswith('cp'): # "cp***" ? + try: + codecs.lookup(enc) + except LookupError: + import encodings + encodings._cache[enc] = encodings._unknown + encodings.aliases.aliases[enc] = 'mbcs' + +def add_calibre_vars(): + sys.new_app_layout = 1 + sys.resources_location = os.path.join(sys.app_dir, 'app', 'resources') + sys.extensions_location = os.path.join(sys.app_dir, 'app', 'DLLs') + + dv = os.environ.get('CALIBRE_DEVELOP_FROM', None) + if dv and os.path.exists(dv): + sys.path.insert(0, os.path.abspath(dv)) + +def run_entry_point(): + bname, mod, func = sys.calibre_basename, sys.calibre_module, sys.calibre_function + sys.argv[0] = bname+'.exe' + pmod = __import__(mod, fromlist=[1], level=0) + return getattr(pmod, func)() + +def main(): + sys.frozen = 'windows_exe' + sys.setdefaultencoding('utf-8') + aliasmbcs() + + sys.meta_path.insert(0, PydImporter()) + sys.path_importer_cache.clear() + + import linecache + def fake_getline(filename, lineno, module_globals=None): + return '' + linecache.orig_getline = linecache.getline + linecache.getline = fake_getline + + abs__file__() + + add_calibre_vars() + + # Needed for pywintypes to be able to load its DLL + sys.path.append(os.path.join(sys.app_dir, 'app', 'DLLs')) + + return run_entry_point() diff --git a/bypy/windows/template.rc b/bypy/windows/template.rc new file mode 100644 index 0000000000..bcdc918682 --- /dev/null +++ b/bypy/windows/template.rc @@ -0,0 +1,42 @@ +#include + +#define VER_FILEVERSION {file_version} +#define VER_FILEVERSION_STR "{file_version_str}" + +#define VER_PRODUCTVERSION {product_version} +#define VER_PRODUCTVERSION_STR "{product_version_str}" + +#define VER_DEBUG 0 + +1 VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VER_DEBUG +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_{file_type} +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "calibre-ebook.com" + VALUE "FileDescription", "{file_description}" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "{internal_name}" + VALUE "LegalCopyright", "{legal_copyright}" + VALUE "LegalTrademarks", "{legal_trademarks}" + VALUE "OriginalFilename", "{original_filename}" + VALUE "ProductName", "{product_name}" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +1 ICON "{icon}" diff --git a/bypy/windows/util.c b/bypy/windows/util.c new file mode 100644 index 0000000000..4379e1696c --- /dev/null +++ b/bypy/windows/util.c @@ -0,0 +1,448 @@ +/* + * Copyright 2009 Kovid Goyal + */ + +#define UNICODE + +#define _WIN32_WINNT 0x0502 +#define WINDOWS_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include +#include + + +static int GUI_APP = 0; +static char python_dll[] = PYDLL; + +void set_gui_app(int yes) { GUI_APP = yes; } + +int calibre_show_python_error(const wchar_t *preamble, int code); + +static int _show_error(const wchar_t *preamble, const wchar_t *msg, const int code) { + wchar_t *buf; + char *cbuf; + buf = (wchar_t*)LocalAlloc(LMEM_ZEROINIT, sizeof(wchar_t)* + (wcslen(msg) + wcslen(preamble) + 80)); + + _snwprintf_s(buf, + LocalSize(buf) / sizeof(wchar_t), _TRUNCATE, + L"%s\r\n %s (Error Code: %d)\r\n", + preamble, msg, code); + + if (GUI_APP) { + MessageBeep(MB_ICONERROR); + MessageBox(NULL, buf, NULL, MB_OK|MB_ICONERROR); + } + else { + cbuf = (char*) calloc(10+(wcslen(buf)*4), sizeof(char)); + if (cbuf) { + if (WideCharToMultiByte(CP_UTF8, 0, buf, -1, cbuf, (int)(10+(wcslen(buf)*4)), NULL, NULL) != 0) printf_s(cbuf); + free(cbuf); + } + } + + LocalFree(buf); + return code; +} + + + +int show_last_error_crt(wchar_t *preamble) { + wchar_t buf[1000]; + int err = 0; + + _get_errno(&err); + _wcserror_s(buf, 1000, err); + return _show_error(preamble, buf, err); +} + +int show_last_error(wchar_t *preamble) { + wchar_t *msg = NULL; + DWORD dw = GetLastError(); + int ret; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, + 0, + NULL ); + + ret = _show_error(preamble, msg, (int)dw); + if (msg != NULL) LocalFree(msg); + return ret; +} + +char* get_app_dir() { + char *buf, *buf2, *buf3; + char drive[4] = "\0\0\0"; + DWORD sz; errno_t err; + + buf = (char*)calloc(MAX_PATH, sizeof(char)); + buf2 = (char*)calloc(MAX_PATH, sizeof(char)); + buf3 = (char*)calloc(MAX_PATH, sizeof(char)); + if (!buf || !buf2 || !buf3) ExitProcess(_show_error(L"Out of memory", L"", 1)); + sz = GetModuleFileNameA(NULL, buf, MAX_PATH); + if (sz >= MAX_PATH-1) ExitProcess(_show_error(L"Installation directory path too long", L"", 1)); + err = _splitpath_s(buf, drive, 4, buf2, MAX_PATH, NULL, 0, NULL, 0); + if (err != 0) ExitProcess(show_last_error_crt(L"Failed to find application directory")); + _snprintf_s(buf3, MAX_PATH, _TRUNCATE, "%s%s", drive, buf2); + free(buf); free(buf2); + return buf3; +} + +wchar_t* get_app_dirw() { + wchar_t *buf, *buf2, *buf3; + wchar_t drive[4] = L"\0\0\0"; + DWORD sz; errno_t err; + + buf = (wchar_t*)calloc(MAX_PATH, sizeof(wchar_t)); + buf2 = (wchar_t*)calloc(MAX_PATH, sizeof(wchar_t)); + buf3 = (wchar_t*)calloc(MAX_PATH, sizeof(wchar_t)); + if (!buf || !buf2 || !buf3) ExitProcess(_show_error(L"Out of memory", L"", 1)); + sz = GetModuleFileNameW(NULL, buf, MAX_PATH); + if (sz >= MAX_PATH-1) ExitProcess(_show_error(L"Installation directory path too long", L"", 1)); + err = _wsplitpath_s(buf, drive, 4, buf2, MAX_PATH, NULL, 0, NULL, 0); + if (err != 0) ExitProcess(show_last_error_crt(L"Failed to find application directory")); + _snwprintf_s(buf3, MAX_PATH, _TRUNCATE, L"%s%s", drive, buf2); + free(buf); free(buf2); + return buf3; +} + + +void load_python_dll() { + char *app_dir, *dll_dir, *qt_plugin_dir; + size_t l; + + app_dir = get_app_dir(); + l = strlen(app_dir)+25; + dll_dir = (char*) calloc(l, sizeof(char)); + qt_plugin_dir = (char*) calloc(l, sizeof(char)); + if (!dll_dir || !qt_plugin_dir) ExitProcess(_show_error(L"Out of memory", L"", 1)); + _snprintf_s(dll_dir, l, _TRUNCATE, "%s\\app\\DLLs", app_dir); + _snprintf_s(qt_plugin_dir, l, _TRUNCATE, "%s\\app\\qt_plugins", app_dir); + free(app_dir); + + _putenv_s("QT_PLUGIN_PATH", qt_plugin_dir); + + if (!SetDllDirectoryA(dll_dir)) ExitProcess(show_last_error(L"Failed to set DLL directory.")); + if (FAILED(__HrLoadAllImportsForDll(python_dll))) + ExitProcess(_show_error(L"Failed to delay load the python dll", L"", 1)); +} + +static char program_name[MAX_PATH]; +static char python_home[MAX_PATH]; + +static wchar_t out_of_memory[] = L"Out of memory"; + +void setup_stream(const char *name, const char *errors, UINT cp) { + PyObject *stream; + char *buf = (char *)calloc(100, sizeof(char)); + if (!buf) ExitProcess(_show_error(out_of_memory, L"", 1)); + + if (cp == CP_UTF8) _snprintf_s(buf, 100, _TRUNCATE, "%s", "utf-8"); + else if (cp == CP_UTF7) _snprintf_s(buf, 100, _TRUNCATE, "%s", "utf-7"); + else _snprintf_s(buf, 100, _TRUNCATE, "cp%d", cp); + + stream = PySys_GetObject((char*)name); + + if (!PyFile_SetEncodingAndErrors(stream, buf, (char*)errors)) + ExitProcess(calibre_show_python_error(L"Failed to set stream encoding", 1)); + + free(buf); + +} + +UINT +setup_streams() { + UINT code_page = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + _putenv_s("PYTHONIOENCODING", "UTF-8"); + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stderr), _O_BINARY); + if (!GUI_APP) { // Remove buffering + setvbuf(stdin, NULL, _IONBF, 2); + setvbuf(stdout, NULL, _IONBF, 2); + setvbuf(stderr, NULL, _IONBF, 2); + } + + //printf("input cp: %d output cp: %d\r\n", GetConsoleCP(), GetConsoleOutputCP()); + + setup_stream("stdin", "strict", GetConsoleCP()); + setup_stream("stdout", "strict", CP_UTF8); + setup_stream("stderr", "strict", CP_UTF8); + return code_page; +} + +UINT +initialize_interpreter(const char *basename, const char *module, const char *function) { + DWORD sz; char *buf, *path; HMODULE dll; + int *flag, i, argc; + wchar_t *app_dir, **wargv; + PyObject *argv, *v; + char *dummy_argv[1] = {""}; + + buf = (char*)calloc(MAX_PATH, sizeof(char)); + path = (char*)calloc(MAX_PATH, sizeof(char)); + if (!buf || !path) ExitProcess(_show_error(L"Out of memory", L"", 1)); + + sz = GetModuleFileNameA(NULL, buf, MAX_PATH); + if (sz >= MAX_PATH-1) ExitProcess(_show_error(L"Installation directory path too long", L"", 1)); + + _snprintf_s(program_name, MAX_PATH, _TRUNCATE, "%s", buf); + free(buf); + + buf = get_app_dir(); + buf[strlen(buf)-1] = '\0'; + + _snprintf_s(python_home, MAX_PATH, _TRUNCATE, "%s", buf); + _snprintf_s(path, MAX_PATH, _TRUNCATE, "%s\\app\\pylib.zip", buf); + free(buf); + + + dll = GetModuleHandleA(python_dll); + if (!dll) ExitProcess(show_last_error(L"Failed to get python dll handle")); + flag = (int*)GetProcAddress(dll, "Py_OptimizeFlag"); + if (!flag) ExitProcess(_show_error(L"Failed to get optimize flag", L"", 1)); + *flag = 2; + flag = (int*)GetProcAddress(dll, "Py_NoSiteFlag"); + if (!flag) ExitProcess(_show_error(L"Failed to get no_site flag", L"", 1)); + *flag = 1; + flag = (int*)GetProcAddress(dll, "Py_DontWriteBytecodeFlag"); + if (!flag) ExitProcess(_show_error(L"Failed to get no_bytecode flag", L"", 1)); + *flag = 1; + flag = (int*)GetProcAddress(dll, "Py_IgnoreEnvironmentFlag"); + if (!flag) ExitProcess(_show_error(L"Failed to get ignore_environment flag", L"", 1)); + *flag = 1; + flag = (int*)GetProcAddress(dll, "Py_NoUserSiteDirectory"); + if (!flag) ExitProcess(_show_error(L"Failed to get user_site flag", L"", 1)); + *flag = 1; + flag = (int*)GetProcAddress(dll, "Py_HashRandomizationFlag"); + if (!flag) ExitProcess(_show_error(L"Failed to get hash randomization flag", L"", 1)); + *flag = 1; + flag = (int*)GetProcAddress(dll, "Py_VerboseFlag"); + if (!flag) ExitProcess(_show_error(L"Failed to get verbose flag", L"", 1)); + //*flag = 1; + flag = (int*)GetProcAddress(dll, "Py_DebugFlag"); + if (!flag) ExitProcess(_show_error(L"Failed to get debug flag", L"", 1)); + //*flag = 1; + + Py_SetProgramName(program_name); + Py_SetPythonHome(python_home); + + //printf("Path before Py_Initialize(): %s\r\n\n", Py_GetPath()); + Py_Initialize(); + UINT code_page = setup_streams(); + + PySys_SetArgv(1, dummy_argv); + //printf("Path after Py_Initialize(): %s\r\n\n", Py_GetPath()); + PySys_SetPath(path); + //printf("Path set by me: %s\r\n\n", path); + PySys_SetObject("gui_app", PyBool_FromLong((long)GUI_APP)); + app_dir = get_app_dirw(); + PySys_SetObject("app_dir", PyUnicode_FromWideChar(app_dir, wcslen(app_dir))); + + PySys_SetObject("calibre_basename", PyBytes_FromString(basename)); + PySys_SetObject("calibre_module", PyBytes_FromString(module)); + PySys_SetObject("calibre_function", PyBytes_FromString(function)); + + wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + if (wargv == NULL) ExitProcess(show_last_error(L"Failed to get command line")); + argv = PyList_New(argc); + if (argv == NULL) ExitProcess(_show_error(out_of_memory, L"", 1)); + for (i = 0; i < argc; i++) { + v = PyUnicode_FromWideChar(wargv[i], wcslen(wargv[i])); + if (v == NULL) ExitProcess(_show_error(out_of_memory, L"", 1)); + PyList_SetItem(argv, i, v); + } + PySys_SetObject("argv", argv); + return code_page; +} + + +wchar_t* pyobject_to_wchar(PyObject *o) { + PyUnicodeObject *t; + size_t s; + wchar_t *ans; + + if (!PyUnicode_Check(o)) { + t = (PyUnicodeObject*)PyUnicode_FromEncodedObject(o, NULL, "replace"); + if (t == NULL) return NULL; + } else t = (PyUnicodeObject*)o; + + + s = 2*PyUnicode_GET_SIZE(t) +1; + ans = (wchar_t*)calloc(s, sizeof(wchar_t)); + if (ans == NULL) return NULL; + s = PyUnicode_AsWideChar(t, ans, s-1); + ans[s] = L'\0'; + + return ans; +} + +int pyobject_to_int(PyObject *res) { + int ret; PyObject *tmp; + tmp = PyNumber_Int(res); + if (tmp == NULL) ret = (PyObject_IsTrue(res)) ? 1 : 0; + else ret = (int)PyInt_AS_LONG(tmp); + + return ret; +} + +int handle_sysexit(PyObject *e) { + PyObject *code; + + code = PyObject_GetAttrString(e, "code"); + if (!code) return 0; + if (!PyInt_Check(code)) { + PyObject_Print(code, stderr, Py_PRINT_RAW); + fflush(stderr); + } + return pyobject_to_int(code); +} + +int calibre_show_python_error(const wchar_t *preamble, int code) { + PyObject *exc, *val, *tb, *str, **system_exit; + HMODULE dll; + int ret, issysexit = 0; wchar_t *i; + + if (!PyErr_Occurred()) return code; + dll = GetModuleHandleA(python_dll); + if (!dll) ExitProcess(show_last_error(L"Failed to get python dll handle")); + system_exit = (PyObject**)GetProcAddress(dll, "PyExc_SystemExit"); + issysexit = PyErr_ExceptionMatches(*system_exit); + + + PyErr_Fetch(&exc, &val, &tb); + + if (exc != NULL) { + PyErr_NormalizeException(&exc, &val, &tb); + + if (issysexit) { + return (val) ? handle_sysexit(val) : 0; + } + if (val != NULL) { + str = PyObject_Unicode(val); + if (str == NULL) { + PyErr_Clear(); + str = PyObject_Str(val); + } + i = pyobject_to_wchar(str); + ret = _show_error(preamble, (i==NULL)?out_of_memory:i, code); + if (i) free(i); + if (tb != NULL) { + PyErr_Restore(exc, val, tb); + PyErr_Print(); + } + return ret; + } + } + return _show_error(preamble, L"", code); +} + +void redirect_out_stream(FILE *stream) { + FILE *f = NULL; + errno_t err; + + err = freopen_s(&f, "NUL", "wt", stream); + if (err != 0) { + ExitProcess(show_last_error_crt(L"Failed to redirect stdout/stderr to NUL. This indicates a corrupted Windows install.\r\n You should contact Microsoft for assistance and/or follow the steps described here:\r\n http://bytes.com/topic/net/answers/264804-compile-error-null-device-missing")); + } +} + +static void +null_invalid_parameter_handler( + const wchar_t * expression, + const wchar_t * function, + const wchar_t * file, + unsigned int line, + uintptr_t pReserved +) { + // The python runtime expects various system calls with invalid parameters + // to return errors instead of aborting the program. So get the windows CRT + // to do that. +} + +__declspec(dllexport) int __cdecl +execute_python_entrypoint(const char *basename, const char *module, const char *function, int is_gui_app) { + PyObject *site, *main, *res; + int ret = 0; + // Prevent Windows' idiotic error dialog popups when various win32 api functions fail + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + + if (is_gui_app) { + // Redirect stdout and stderr to NUL so that python does not fail writing to them + redirect_out_stream(stdout); + redirect_out_stream(stderr); + } + set_gui_app(is_gui_app); + // Disable the invalid parameter handler + _set_invalid_parameter_handler(null_invalid_parameter_handler); + + load_python_dll(); + UINT code_page = initialize_interpreter(basename, module, function); + + site = PyImport_ImportModule("site"); + + if (site == NULL) + ret = calibre_show_python_error(L"Failed to import site module", 1); + else { + Py_XINCREF(site); + + main = PyObject_GetAttrString(site, "main"); + if (main == NULL || !PyCallable_Check(main)) + ret = calibre_show_python_error(L"site module has no main function", 1); + else { + Py_XINCREF(main); + res = PyObject_CallObject(main, NULL); + + if (res == NULL) + ret = calibre_show_python_error(L"Python function terminated unexpectedly", 1); + else { + } + } + } + PyErr_Clear(); + Py_Finalize(); + if (code_page != CP_UTF8) SetConsoleOutputCP(code_page); + + //printf("11111 Returning: %d\r\n", ret); + return ret; +} + + +wchar_t* get_temp_filename(const wchar_t *prefix) { + DWORD dwRetVal; + UINT uRetVal; + + wchar_t *szTempName; + wchar_t lpPathBuffer[MAX_PATH]; + szTempName = (wchar_t *)LocalAlloc(LMEM_ZEROINIT, sizeof(wchar_t)*MAX_PATH); + + dwRetVal = GetTempPath(MAX_PATH, lpPathBuffer); + + if (dwRetVal > MAX_PATH || (dwRetVal == 0)) { + ExitProcess(show_last_error(L"Failed to get temp path.")); + } + + uRetVal = GetTempFileName(lpPathBuffer, // directory for tmp files + prefix, // temp file name prefix + 0, // create unique name + szTempName); // buffer for name + + if (uRetVal == 0) { + ExitProcess(show_last_error(L"Failed to get temp file name")); + } + return szTempName; +} diff --git a/bypy/windows/wix-template.xml b/bypy/windows/wix-template.xml new file mode 100644 index 0000000000..c579cd4bcc --- /dev/null +++ b/bypy/windows/wix-template.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {app_components} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + = {minver})]]> + + + + + + NEWPRODUCTFOUND + {fix_wix} + + + + NEWPRODUCTFOUND + {fix_wix} + + + + + + WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed + + + + + + + + + + + + + + + + + diff --git a/bypy/windows/wix.py b/bypy/windows/wix.py new file mode 100644 index 0000000000..5883e7e849 --- /dev/null +++ b/bypy/windows/wix.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python2 +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2016, Kovid Goyal + +from __future__ import (unicode_literals, division, absolute_import, + print_function) +from itertools import count +import os +import shutil + +from pkgs.constants import is64bit +from pkgs.utils import run +from .. import calibre_constants + +WIXP = r'C:\Program Files (x86)\WiX Toolset v3.10' +if is64bit: + UPGRADE_CODE = '5DD881FF-756B-4097-9D82-8C0F11D521EA' +else: + UPGRADE_CODE = 'BEB2A80D-E902-4DAD-ADF9-8BD2DA42CFE1' +MINVERHUMAN = 'Windows Vista SP2' + +CANDLE = WIXP + r'\bin\candle.exe' +LIGHT = WIXP + r'\bin\light.exe' +j, d, a, b = os.path.join, os.path.dirname, os.path.abspath, os.path.basename + + +def create_installer(env): + if os.path.exists(env.installer_dir): + shutil.rmtree(env.installer_dir) + os.makedirs(env.installer_dir) + + template = open(j(d(__file__), 'wix-template.xml'), 'rb').read() + + components, smap = get_components_from_files(env) + wxs = template.format( + app=calibre_constants['appname'], + appfolder='Calibre2' if is64bit else 'Calibre', + version=calibre_constants['version'], + upgrade_code=UPGRADE_CODE, + ProgramFilesFolder='ProgramFiles64Folder' if is64bit else 'ProgramFilesFolder', + x64=' 64bit' if is64bit else '', + minverhuman=MINVERHUMAN, + minver='600', + fix_wix='' if is64bit else '', + compression='high', + app_components=components, + exe_map=smap, + main_icon=j(env.src_root, 'icons', 'library.ico'), + viewer_icon=j(env.src_root, 'icons', 'viewer.ico'), + editor_icon=j(env.src_root, 'icons', 'ebook-edit.ico'), + web_icon=j(env.src_root, 'icons', 'web.ico'), + ) + template = open(j(d(__file__), 'en-us.xml'), 'rb').read() + enus = template.format(app=calibre_constants['appname']) + + enusf = j(env.installer_dir, 'en-us.wxl') + wxsf = j(env.installer_dir, calibre_constants['appname'] + '.wxs') + with open(wxsf, 'wb') as f: + f.write(wxs) + with open(enusf, 'wb') as f: + f.write(enus) + wixobj = j(env.installer_dir, calibre_constants['appname'] + '.wixobj') + arch = 'x64' if is64bit else 'x86' + cmd = [CANDLE, '-nologo', '-arch', arch, '-ext', 'WiXUtilExtension', '-o', wixobj, wxsf] + run(*cmd) + installer = j(env.dist, '%s%s-%s.msi' % ( + calibre_constants['appname'], ('-64bit' if is64bit else ''), calibre_constants['version'])) + license = j(env.src_root, 'LICENSE.rtf') + banner = j(env.src_root, 'icons', 'wix-banner.bmp') + dialog = j(env.src_root, 'icons', 'wix-dialog.bmp') + cmd = [LIGHT, '-nologo', '-ext', 'WixUIExtension', + '-cultures:en-us', '-loc', enusf, wixobj, + '-ext', 'WixUtilExtension', + '-o', installer, + '-dWixUILicenseRtf=' + license, + '-dWixUIBannerBmp=' + banner, + '-dWixUIDialogBmp=' + dialog] + cmd.extend([ + '-sice:ICE60', # No language in dlls warning + '-sice:ICE61', # Allow upgrading with same version number + '-sice:ICE40', # Re-install mode overriden + '-sice:ICE69', # Shortcut components are part of a different feature than the files they point to + ]) + cmd.append('-sval') # Disable all checks since they fail when running under ssh + run(*cmd) + + +def get_components_from_files(env): + + file_idc = count() + file_id_map = {} + + def process_dir(path): + components = [] + for x in os.listdir(path): + f = os.path.join(path, x) + file_id_map[f] = fid = next(file_idc) + + if os.path.isdir(f): + components.append( + '' % + (file_id_map[f], f, x)) + c = process_dir(f) + components.extend(c) + components.append('') + else: + checksum = 'Checksum="yes"' if x.endswith('.exe') else '' + c = [ + ('') % (fid,), + ('') % + (fid, f, x, checksum), + '' + ] + if x.endswith('.exe') and not x.startswith('pdf'): + # Add the executable to app paths so that users can + # launch it from the run dialog even if it is not on + # the path. See http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx + c[-1:-1] = [ + ('' % (x, fid)), + (''.format(x)), + ] + components.append('\n'.join(c)) + return components + + components = process_dir(a(env.base)) + smap = {} + for x in calibre_constants['basenames']['gui']: + smap[x] = 'file_%d' % file_id_map[a(j(env.base, x + '.exe'))] + + return '\t\t\t\t' + '\n\t\t\t\t'.join(components), smap diff --git a/setup/build.py b/setup/build.py index 211bb11986..9c50975b3a 100644 --- a/setup/build.py +++ b/setup/build.py @@ -206,7 +206,8 @@ def init_env(): for p in win_inc: cflags.append('-I'+p) for p in win_lib: - ldflags.append('/LIBPATH:'+p) + if p: + ldflags.append('/LIBPATH:'+p) cflags.append('-I%s'%sysconfig.get_python_inc()) ldflags.append('/LIBPATH:'+os.path.join(sysconfig.PREFIX, 'libs')) linker = msvc.linker @@ -297,7 +298,7 @@ class Build(Command): def lib_dirs_to_ldflags(self, dirs): pref = '/LIBPATH:' if iswindows else '-L' - return [pref+x for x in dirs] + return [pref+x for x in dirs if x] def libraries_to_ldflags(self, dirs): pref = '' if iswindows else '-l' @@ -338,7 +339,11 @@ class Build(Command): self.info('Linking', ext.name) cmd = [linker] if iswindows: - cmd += self.env.ldflags + ext.ldflags + elib + xlib + \ + pre_ld_flags = [] + if ext.name in ('icu', 'matcher'): + # windows has its own ICU libs that dont work + pre_ld_flags = elib + cmd += pre_ld_flags + self.env.ldflags + ext.ldflags + elib + xlib + \ ['/EXPORT:' + init_symbol_name(ext.name)] + objects + ext.extra_objs + ['/OUT:'+dest] else: cmd += objects + ext.extra_objs + ['-o', dest] + self.env.ldflags + ext.ldflags + elib + xlib diff --git a/setup/build_environment.py b/setup/build_environment.py index 33e6e73220..ca9573a8f1 100644 --- a/setup/build_environment.py +++ b/setup/build_environment.py @@ -20,8 +20,8 @@ if iswindows: NMAKE = msvc.find_exe('nmake.exe') RC = msvc.find_exe('rc.exe') MT = msvc.find_exe('mt.exe') - win_inc = os.environ['include'].split(';') - win_lib = os.environ['lib'].split(';') + win_inc = [x for x in os.environ['include'].split(';') if x] + win_lib = [x for x in os.environ['lib'].split(';') if x] QMAKE = 'qmake' for x in ('qmake-qt5', 'qt5-qmake', 'qmake'): diff --git a/setup/parallel_build.py b/setup/parallel_build.py index d2d6057b2f..9af563dbf5 100644 --- a/setup/parallel_build.py +++ b/setup/parallel_build.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera __license__ = 'GPL v3' __copyright__ = '2014, Kovid Goyal ' -import subprocess +import subprocess, os from multiprocessing.dummy import Pool from functools import partial from contextlib import closing @@ -49,8 +49,11 @@ cpu_count = min(16, max(1, cpu_count)) def run_worker(job, decorate=True): cmd, human_text = job human_text = human_text or ' '.join(cmd) + cwd = None + if cmd[0].lower().endswith('cl.exe'): + cwd = os.environ.get('COMPILER_CWD') try: - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=cwd) except Exception as err: return False, human_text, unicode_type(err) stdout, stderr = p.communicate()