text: #include <time.h>
  #include <stdio.h>
  #include <errno.h>
  #include <ctype.h>
  #include <fcntl.h>
  #include <stdlib.h>
  #include <string.h>
  #include <getopt.h>
  #include <dirent.h>
  #include <unistd.h>
  #include <stdarg.h>
  #ifdef __MINGW32__
  #include <windows.h>
  #else
  #include <signal.h>
  #include <pthread.h>
  #include <termios.h>
  #include <sys/ioctl.h>
  #endif
  
  #define WM_TOOL_PATH_MAX                      256
  #define WM_TOOL_ONCE_READ_LEN	              1024
  
  #define WM_TOOL_RUN_IMG_HEADER_LEN            0x100
  
  #define WM_TOOL_SECBOOT_IMG_ADDR             (0x2100)
  #define WM_TOOL_SECBOOT_HEADER_LEN           (0x100)
  #define WM_TOOL_SECBOOT_HEADER_POS           (WM_TOOL_SECBOOT_IMG_ADDR - WM_TOOL_SECBOOT_HEADER_LEN)
  //#define WM_TOOL_SECBOOT_IMG_AREA_TOTAL_LEN    9216//(56 * 1024)
  
  #define WM_TOOL_IMG_HEAD_MAGIC_NO            (0xA0FFFF9F)
  
  #define WM_TOOL_DEFAULT_BAUD_RATE             115200
  
  #define WM_TOOL_DOWNLOAD_TIMEOUT_SEC          (60 * 1)
  
  #define WM_TOOL_USE_1K_XMODEM                  1  /* 1 for use 1k_xmodem 0 for xmodem */
  
  #define WM_TOOL_IMAGE_VERSION_LEN              16
  
  /* Xmodem Frame form: <SOH><blk #><255-blk #><--128 data bytes--><CRC hi><CRC lo> */
  #define XMODEM_SOH 0x01
  #define XMODEM_STX 0x02
  #define XMODEM_EOT 0x04
  #define XMODEM_ACK 0x06
  #define XMODEM_NAK 0x15
  #define XMODEM_CAN 0x18
  #define XMODEM_CRC_CHR 'C'
  #define XMODEM_CRC_SIZE 2      /* Crc_High Byte + Crc_Low Byte */
  #define XMODEM_FRAME_ID_SIZE 2 /* Frame_Id + 255-Frame_Id */
  #define XMODEM_DATA_SIZE_SOH 128   /* for Xmodem protocol */
  #define XMODEM_DATA_SIZE_STX 1024 /* for 1K xmodem protocol */
  
  #if (WM_TOOL_USE_1K_XMODEM)
  #define XMODEM_DATA_SIZE  XMODEM_DATA_SIZE_STX
  #define XMODEM_HEAD XMODEM_STX
  #else
  #define XMODEM_DATA_SIZE XMODEM_DATA_SIZE_SOH
  #define XMODEM_HEAD XMODEM_SOH
  #endif
  
  #ifdef WM_DEBUG
  #define WM_TOOL_DBG_PRINT       wm_tool_printf
  #else
  #define WM_TOOL_DBG_PRINT(...)
  #endif
  
  typedef enum {
      WM_TOOL_DL_ACTION_NONE = 0,
      WM_TOOL_DL_ACTION_AT,
      WM_TOOL_DL_ACTION_RTS
  } wm_tool_dl_action_e;
  
  typedef enum {
      WM_TOOL_DL_ERASE_NONE = 0,
      WM_TOOL_DL_ERASE_ALL
  } wm_tool_dl_erase_e;
  
  typedef enum {
      WM_TOOL_DL_TYPE_IMAGE = 0,
      WM_TOOL_DL_TYPE_FLS
  } wm_tool_dl_type_e;
  
  typedef enum {
      WM_TOOL_LAYOUT_TYPE_1M = 0,
      WM_TOOL_LAYOUT_TYPE_2M = 3
  } wm_tool_layout_type_e;
  
  typedef enum {
      WM_TOOL_ZIP_TYPE_UNCOMPRESS = 0,
      WM_TOOL_ZIP_TYPE_COMPRESS
  } wm_tool_zip_type_e;
  
  typedef enum {
      WM_TOOL_CRC32_REFLECT_OUTPUT = 1,
      WM_TOOL_CRC32_REFLECT_INPUT  = 2
  } wm_tool_crc32_reflect_e;
  
  typedef enum {
      WM_TOOL_SHOW_LOG_NONE = 0,
      WM_TOOL_SHOW_LOG_STR,
      WM_TOOL_SHOW_LOG_HEX
  } wm_tool_show_log_e;
  
  typedef struct {
      unsigned int   magic_no;
      unsigned short img_type;
      unsigned short zip_type;
      unsigned int   run_img_addr;
      unsigned int   run_img_len;
      unsigned int   img_header_addr;
      unsigned int   upgrade_img_addr;
      unsigned int   run_org_checksum;
      unsigned int   upd_no;
      unsigned char  ver[WM_TOOL_IMAGE_VERSION_LEN];
      unsigned int   reserved0;
      unsigned int   reserved1;
      unsigned int   next_boot;	
      unsigned int   hd_checksum;
  } wm_tool_firmware_booter_t;
  
  const static char *wm_tool_version = "1.0.4";
  
  static int wm_tool_show_usage = 0;
  static int wm_tool_list_com   = 0;
  static int wm_tool_show_ver   = 0;
  
  static char wm_tool_serial_path[WM_TOOL_PATH_MAX] = "/dev/ttyS0";
  static unsigned int wm_tool_download_serial_rate  = WM_TOOL_DEFAULT_BAUD_RATE;
  static unsigned int wm_tool_normal_serial_rate    = WM_TOOL_DEFAULT_BAUD_RATE;
  
  static wm_tool_dl_action_e wm_tool_dl_action = WM_TOOL_DL_ACTION_NONE;
  static wm_tool_dl_erase_e  wm_tool_dl_erase  = WM_TOOL_DL_ERASE_NONE;
  static wm_tool_dl_type_e   wm_tool_dl_type   = WM_TOOL_DL_TYPE_IMAGE;
  static char *wm_tool_download_image = NULL;
  
  static char *wm_tool_input_binary = NULL;
  static char *wm_tool_output_image = NULL;
  static char *wm_tool_secboot_image = NULL;
  static unsigned int wm_tool_src_binary_len = 0;
  static unsigned int wm_tool_src_binary_crc = 0;
  static int wm_tool_is_debug = 0;
  static char wm_tool_image_version[WM_TOOL_IMAGE_VERSION_LEN];
  static wm_tool_layout_type_e wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_1M;
  static wm_tool_zip_type_e wm_tool_zip_type = WM_TOOL_ZIP_TYPE_COMPRESS;
  static unsigned int wm_tool_upd_addr = 0x8090000;
  static unsigned int wm_tool_run_addr = 0x8002400;
  
  static unsigned int wm_tool_image_header = 0x8002000;
  static unsigned int wm_tool_next_image_header = 0x0;
  static unsigned int wm_tool_image_upd_no = 0x0;
  
  static unsigned int wm_tool_file_crc = 0xFFFFFFFF;
  
  static wm_tool_show_log_e wm_tool_show_log_type = WM_TOOL_SHOW_LOG_NONE;
  
  #ifdef __MINGW32__
  
  #ifndef CBR_2000000
  #define CBR_2000000 2000000
  #endif
  
  #ifndef CBR_1000000
  #define CBR_1000000 1000000
  #endif
  
  #ifndef CBR_921600
  #define CBR_921600 921600
  #endif
  
  #ifndef CBR_460800
  #define CBR_460800 460800
  #endif
  
  static DWORD wm_tool_uart_block = 0;
  
  static HANDLE wm_tool_uart_handle = NULL;
  
  const static int wm_tool_uart_speed_array[] = {CBR_2000000, CBR_1000000, CBR_921600, CBR_460800, CBR_115200, CBR_38400,
                                                 CBR_19200,   CBR_9600,    CBR_4800,   CBR_2400,   CBR_1200};
  #else /* __MINGW32__ */
  
  #ifndef B2000000
  #define B2000000 2000000
  #endif
  
  #ifndef B1000000
  #define B1000000 1000000
  #endif
  
  #ifndef B921600
  #define B921600 921600
  #endif
  
  #ifndef B460800
  #define B460800 460800
  #endif
  
  static int wm_tool_uart_fd = -1;
  
  static struct termios wm_tool_saved_serial_cfg;
  
  
  const static int wm_tool_uart_speed_array[] = {B2000000, B1000000, B921600, B460800, B115200, B38400,
                                                 B19200,   B9600,    B4800,   B2400,   B1200};
  
  #endif /* __MINGW32__ */
  
  const static int wm_tool_uart_name_array[] = {2000000, 1000000, 921600, 460800, 115200, 38400,
                                                19200,   9600,    4800,   2400,   1200};
  
  const static unsigned char wm_tool_chip_cmd_b115200[]  = {0x21, 0x0a, 0x00, 0x97, 0x4b, 0x31, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00};
  const static unsigned char wm_tool_chip_cmd_b460800[]  = {0x21, 0x0a, 0x00, 0x07, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0x00};
  const static unsigned char wm_tool_chip_cmd_b921600[]  = {0x21, 0x0a, 0x00, 0x5d, 0x50, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x00};
  const static unsigned char wm_tool_chip_cmd_b1000000[] = {0x21, 0x0a, 0x00, 0x5e, 0x3d, 0x31, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00};
  const static unsigned char wm_tool_chip_cmd_b2000000[] = {0x21, 0x0a, 0x00, 0xef, 0x2a, 0x31, 0x00, 0x00, 0x00, 0x80, 0x84, 0x1e, 0x00};
  
  const static unsigned char wm_tool_chip_cmd_get_mac[]  = {0x21, 0x06, 0x00, 0xea, 0x2d, 0x38, 0x00, 0x00, 0x00};
  
  static const unsigned int wm_tool_crc32_tab[] = {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};
  
  static void wm_tool_signal_proc_entry(void);
  static void wm_tool_stdin_to_uart(void);
  
  /* ============================ zlib gzip ================================== */
  
  #define ALLOC(size) zcalloc((voidp)0, 1, size)
  #define TRYFREE(p) {if (p) zcfree((voidp)0, p);}
  
  #define BINARY  0
  #define ASCII   1
  #define UNKNOWN 2
  
  #define STORED_BLOCK 0
  #define STATIC_TREES 1
  #define DYN_TREES    2
  
  #define REP_3_6      16
  #define REPZ_3_10    17
  #define REPZ_11_138  18
  
  #define MAX_BL_BITS 7
  
  #define BASE 65521 /* largest prime smaller than 65536 */
  #define NMAX 5552
  
  #define DO1(buf)  {s1 += *buf++; s2 += s1;}
  #define DO2(buf)  DO1(buf); DO1(buf);
  #define DO4(buf)  DO2(buf); DO2(buf);
  #define DO8(buf)  DO4(buf); DO4(buf);
  #define DO16(buf) DO8(buf); DO8(buf);
  
  #define Z_OK            0
  #define Z_STREAM_END    1
  #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_NULL  0  /* for initializing zalloc, zfree, opaque */
  
  #define Z_BEST_SPEED             1
  #define Z_BEST_COMPRESSION       9
  #define Z_DEFAULT_COMPRESSION  (-1)
  
  #define DEFLATED   8
  
  #ifndef WBITS
  # define WBITS   15 /* 32K window */
  #endif
  
  #ifndef MEM_LEVEL
  # define MEM_LEVEL  8
  #endif
  
  #define Z_BUFSIZE 4096
  
  #define GZ_MAGIC_1 0x1f
  #define GZ_MAGIC_2 0x8b
  
  #ifndef OS_CODE
  #  define OS_CODE  0x03  /* assume Unix */
  #endif
  
  #ifndef TOO_FAR
  #  define TOO_FAR 4096
  #endif
  
  #define Z_NO_FLUSH      0
  #define Z_FINISH        4
  
  #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
  #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
  #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
  #define COMMENT      0x10 /* bit 4 set: file comment present */
  #define RESERVED     0xE0 /* bits 5..7: reserved */
  
  #define LENGTH_CODES 29/* number of length codes, not counting the special END_BLOCK code */
  #define LITERALS  256/* number of literal bytes 0..255 */
  #define L_CODES (LITERALS+1+LENGTH_CODES)/* number of Literal or Length codes, including the END_BLOCK code */
  #define D_CODES   30/* number of distance codes */
  #define BL_CODES  19/* number of codes used to transfer the bit lengths */
  #define HEAP_SIZE (2*L_CODES+1)/* maximum heap size */
  
  #define END_BLOCK 256
  
  #define MAX_BITS 15
  
  #define MAX_MEM_LEVEL 9
  
  #define MIN_MATCH  3
  #define MAX_MATCH  258
  
  #define Z_UNKNOWN  2
  
  #define INIT_STATE    42
  #define BUSY_STATE   113
  #define FINISH_STATE 666
  
  #define NIL 0
  
  #define Z_FILTERED            1
  #define Z_HUFFMAN_ONLY        2
  #define Z_DEFAULT_STRATEGY    0
  
  #define SMALLEST 1
  
  #define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
  
  #define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
  
  #define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err)
  
  #define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
  
  #define put_short(s, w) { \
      put_byte(s, (uch)((w) & 0xff)); \
      put_byte(s, (uch)((ush)(w) >> 8)); \
  }
  
  #define INSERT_STRING(s, str, match_head) \
     (UPDATE_HASH(s, s->ins_h, s->window[(str) + MIN_MATCH-1]), \
      s->prev[(str) & s->w_mask] = match_head = s->head[s->ins_h], \
      s->head[s->ins_h] = (str))
  
  #define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)
  
  #define check_match(s, start, match, length)
  
  #define d_code(dist) \
     ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)])
  
  #define FLUSH_BLOCK_ONLY(s, eof) { \
     ct_flush_block(s, (s->block_start >= 0L ? \
  	       (char*)&s->window[(unsigned)s->block_start] : \
                 (char*)Z_NULL), (long)s->strstart - s->block_start, (eof)); \
     s->block_start = s->strstart; \
     flush_pending(s->strm); \
  }
  
  #define FLUSH_BLOCK(s, eof) { \
     FLUSH_BLOCK_ONLY(s, eof); \
     if (s->strm->avail_out == 0) return 1; \
  }
  
  #define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
  
  #define MAX(a,b) (a >= b ? a : b)
  
  #define Buf_size (8 * 2*sizeof(char))
  
  #define smaller(tree, n, m, depth) \
     (tree[n].Freq < tree[m].Freq || \
     (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
  
  #define pqremove(s, tree, top) \
  {\
      top = s->heap[SMALLEST]; \
      s->heap[SMALLEST] = s->heap[s->heap_len--]; \
      pqdownheap(s, tree, SMALLEST); \
  }
  
  #ifndef local
  #  define local static
  #endif
  
  #ifndef __P
  #define __P(args)  args
  #endif
  
  #define ZALLOC(strm, items, size) \
             (*((strm)->zalloc))((strm)->opaque, (items), (size))
  #define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidp)(addr))
  #define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
  
  #define Assert(cond,msg)
  
  #ifndef Byte
    typedef unsigned char  Byte;  /* 8 bits */
  #endif
  #ifndef uInt
    typedef unsigned int   uInt;  /* may be 16 or 32 bits */
  #endif
  #ifndef uLong
    typedef unsigned long  uLong; /* 32 bits or more */
  #endif
  typedef Byte *voidp;
  typedef voidp gzFile;
  typedef unsigned char  uch;
  typedef unsigned long  ulg;
  typedef unsigned short ush;
  typedef ush Pos;
  typedef unsigned IPos;
  
  typedef voidp (*alloc_func) __P((voidp opaque, uInt items, uInt size));
  typedef void  (*free_func)  __P((voidp opaque, voidp address));
  
  typedef uLong (*check_func) __P((uLong check, Byte *buf, uInt len));
  
  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 */
      voidp      opaque;  /* private data object passed to zalloc and zfree */
  
      Byte     data_type; /* best guess about the data type: ascii or binary */
  
  } z_stream;
  
  typedef struct gz_stream {
      z_stream stream;
      int      z_err;   /* error code for last stream operation */
      int      z_eof;   /* set if end of input file */
      FILE     *file;   /* .gz file */
      Byte     *inbuf;  /* input buffer */
      Byte     *outbuf; /* output buffer */
      uLong    crc;     /* crc32 of uncompressed data */
      char     *msg;    /* error message */
      char     *path;   /* path name for debugging only */
      int      transparent; /* 1 if input file is not a .gz file */
      char     mode;    /* 'w' or 'r' */
  } gz_stream;
  
  /* Data structure describing a single value and its code string. */
  typedef struct ct_data_s {
      union {
          ush  freq;       /* frequency count */
          ush  code;       /* bit string */
      } fc;
      union {
          ush  dad;        /* father node in Huffman tree */
          ush  len;        /* length of bit string */
      } dl;
  } ct_data;
  
  struct static_tree_desc_s {
      ct_data *static_tree;        /* static tree or NULL */
      int     *extra_bits;         /* extra bits for each code or NULL */
      int     extra_base;          /* base index for extra_bits */
      int     elems;               /* max number of elements in the tree */
      int     max_length;          /* max bit length for the codes */
  };
  
  typedef struct static_tree_desc_s  static_tree_desc;
  
  typedef struct tree_desc_s {
      ct_data *dyn_tree;           /* the dynamic tree */
      int     max_code;            /* largest code with non zero frequency */
      static_tree_desc *stat_desc; /* the corresponding static tree */
  } tree_desc;
  
  typedef struct inflate_huft_s inflate_huft;
  struct inflate_huft_s {
    union {
      struct {
        char Exop;        /* number of extra bits or operation */
        char Bits;        /* number of bits in this code or subcode */
      } what;
      Byte *pad;          /* pad structure to a power of 2 (4 bytes for */
    } word;               /*  16-bit, 8 bytes for 32-bit machines) */
    union {
      uInt Base;          /* literal, length base, or distance base */
      inflate_huft *Next; /* pointer to next level of table */
    } more;
  };
  
  /* inflate codes private state */
  struct inflate_codes_state {
  
    /* mode */
    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 */
        BAD}	/* x: got error */
      mode;		/* current inflate_codes mode */
  
    /* mode dependent information */
    uInt len;
    union {
      struct {
        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 */
    inflate_huft *ltree;		/* literal/length/eob tree */
    inflate_huft *dtree;		/* distance tree */
  
  };
  
  
  /* inflate blocks semi-private state */
  struct inflate_blocks_state {
  
    /* mode */
    enum {
        TYPE,	/* get type bits (3, including end bit) */
        LENS,	/* get lengths for stored */
        STORED,	/* processing stored block */
        TABLE,	/* get table lengths */
        BTREE,	/* get bit lengths tree for a dynamic block */
        DTREE,	/* get length, distance trees for a dynamic block */
        CODES,	/* processing fixed or dynamic block */
        DRY,	/* output remaining window bytes */
        DONE,	/* finished last block, done */
        INF_ERROR}/* got a data error--stuck here */
      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_state
        *codes;		/* 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 */
    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 */
  
  };
  
  typedef struct internal_state {
      z_stream *strm;      /* pointer back to this zlib stream */
      int   status;        /* as the name implies */
      Byte *pending_buf;   /* output still pending */
      Byte *pending_out;   /* next pending byte to output to the stream */
      int   pending;       /* nb of bytes in the pending buffer */
      uLong adler;         /* adler32 of uncompressed data */
      int   noheader;      /* suppress zlib header and adler32 */
      Byte  data_type;     /* UNKNOWN, BINARY or ASCII */
      Byte  method;        /* STORED (for zip only) or DEFLATED */
  
      		/* used by deflate.c: */
  
      uInt  w_size;        /* LZ77 window size (32K by default) */
      uInt  w_bits;        /* log2(w_size)  (8..16) */
      uInt  w_mask;        /* w_size - 1 */
  
      Byte *window;
      /* Sliding window. Input bytes are read into the second half of the window,
       * and move to the first half later to keep a dictionary of at least wSize
       * bytes. With this organization, matches are limited to a distance of
       * wSize-MAX_MATCH bytes, but this ensures that IO is always
       * performed with a length multiple of the block size. Also, it limits
       * the window size to 64K, which is quite useful on MSDOS.
       * To do: use the user input buffer as sliding window.
       */
  
      ulg window_size;
      /* Actual size of window: 2*wSize, except when the user input buffer
       * is directly used as sliding window.
       */
  
      Pos *prev;
      /* Link to older string with same hash index. To limit the size of this
       * array to 64K, this link is maintained only for the last 32K strings.
       * An index in this array is thus a window index modulo 32K.
       */
  
      Pos *head; /* Heads of the hash chains or NIL. */
  
      uInt  ins_h;          /* hash index of string to be inserted */
      uInt  hash_size;      /* number of elements in hash table */
      uInt  hash_bits;      /* log2(hash_size) */
      uInt  hash_mask;      /* hash_size-1 */
  
      uInt  hash_shift;
      /* Number of bits by which ins_h must be shifted at each input
       * step. It must be such that after MIN_MATCH steps, the oldest
       * byte no longer takes part in the hash key, that is:
       *   hash_shift * MIN_MATCH >= hash_bits
       */
  
      long block_start;
      /* Window position at the beginning of the current output block. Gets
       * negative when the window is moved backwards.
       */
  
      uInt match_length;           /* length of best match */
      IPos prev_match;             /* previous match */
      int match_available;         /* set if previous match exists */
      uInt strstart;               /* start of string to insert */
      uInt match_start;            /* start of matching string */
      uInt lookahead;              /* number of valid bytes ahead in window */
  
      uInt prev_length;
      /* Length of the best match at previous step. Matches not greater than this
       * are discarded. This is used in the lazy match evaluation.
       */
  
      uInt max_chain_length;
      /* To speed up deflation, hash chains are never searched beyond this
       * length.  A higher limit improves compression ratio but degrades the
       * speed.
       */
  
      uInt max_lazy_match;
      /* Attempt to find a better match only when the current match is strictly
       * smaller than this value. This mechanism is used only for compression
       * levels >= 4.
       */
  #   define max_insert_length  max_lazy_match
      /* Insert new strings in the hash table only if the match length is not
       * greater than this length. This saves time but degrades compression.
       * max_insert_length is used only for compression levels <= 3.
       */
  
      int level;    /* compression level (1..9) */
      int strategy; /* favor or force Huffman coding*/
  
      uInt good_match;
      /* Use a faster search when the previous match is longer than this */
  
       int nice_match; /* Stop searching when current match exceeds this */
  
      		/* used by trees.c: */
  
      ct_data dyn_ltree[HEAP_SIZE];   /* literal and length tree */
      ct_data dyn_dtree[2*D_CODES+1]; /* distance tree */
      ct_data bl_tree[2*BL_CODES+1];  /* Huffman tree for the bit lengths */
  
      tree_desc l_desc;               /* descriptor for literal tree */
      tree_desc d_desc;               /* descriptor for distance tree */
      tree_desc bl_desc;              /* descriptor for bit length tree */
  
      ush bl_count[MAX_BITS+1];
      /* number of codes at each bit length for an optimal tree */
  
      int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
      int heap_len;               /* number of elements in the heap */
      int heap_max;               /* element of largest frequency */
      /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
       * The same heap array is used to build all trees.
       */
  
      uch depth[2*L_CODES+1];
      /* Depth of each subtree used as tie breaker for trees of equal frequency
       */
  
      uch *l_buf;           /* buffer for literals or lengths */
  
      uInt  lit_bufsize;
      /* Size of match buffer for literals/lengths.  There are 4 reasons for
       * limiting lit_bufsize to 64K:
       *   - frequencies can be kept in 16 bit counters
       *   - if compression is not successful for the first block, all input
       *     data is still in the window so we can still emit a stored block even
       *     when input comes from standard input.  (This can also be done for
       *     all blocks if lit_bufsize is not greater than 32K.)
       *   - if compression is not successful for a file smaller than 64K, we can
       *     even emit a stored file instead of a stored block (saving 5 bytes).
       *     This is applicable only for zip (not gzip or zlib).
       *   - creating new Huffman trees less frequently may not provide fast
       *     adaptation to changes in the input data statistics. (Take for
       *     example a binary file with poorly compressible code followed by
       *     a highly compressible string table.) Smaller buffer sizes give
       *     fast adaptation but have of course the overhead of transmitting
       *     trees more frequently.
       *   - I can't count above 4
       */
  
      uInt last_lit;      /* running index in l_buf */
  
      ush *d_buf;
      /* Buffer for distances. To simplify the code, d_buf and l_buf have
       * the same number of elements. To use different lengths, an extra flag
       * array would be necessary.
       */
  
      ulg opt_len;        /* bit length of current block with optimal trees */
      ulg static_len;     /* bit length of current block with static trees */
      ulg compressed_len; /* total bit length of compressed file */
      uInt matches;       /* number of string matches in current block */
  
  #ifdef DEBUG
      ulg bits_sent;      /* bit length of the compressed data */
  #endif
  
      ush bi_buf;
      /* Output buffer. bits are inserted starting at the bottom (least
       * significant bits).
       */
      int bi_valid;
      /* Number of valid bits in bi_buf.  All bits above the last valid bit
       * are always zero.
       */
  
      /* mode */
    enum {
        METHOD,	/* waiting for method byte */
        FLAG,	/* waiting for flag byte */
        //START,	/* make new blocks state */
        BLOCKS,	/* decompressing blocks */
        CHECK4,	/* four check bytes to go */
        CHECK3,	/* three check bytes to go */
        CHECK2,	/* two check bytes to go */
        CHECK1,	/* one check byte to go */
        //DONE,	/* finished check, done */
        //INF_ERROR/* got an error--stay here */
        }
      mode;		/* current inflate mode */
  
    /* mode dependent information */
    union {
      uInt method;	/* if FLAGS, method byte */
      struct inflate_blocks_state
        *blocks;		/* if BLOCKS, current state */
      struct {
        uLong was;		/* computed check value */
        uLong need;		/* stream check value */
      } check;		/* if CHECK, check values to compare */
    } sub;	/* submode */
  
    /* mode independent information */
    int  nowrap;		/* flag for no wrapper */
    uInt wbits;  		/* log2(window size)  (8..15, defaults to 15) */
  } deflate_state;
  
  typedef struct config_s {
     ush good_length; /* reduce lazy search above this match length */
     ush max_lazy;    /* do not perform lazy search above this match length */
     ush nice_length; /* quit search above this match length */
     ush max_chain;
  } config;
  
  char *z_errmsg[] = {
  "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) */
  ""};
  
  local int base_length[LENGTH_CODES];
  local int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
     = {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};
  local uch length_code[MAX_MATCH-MIN_MATCH+1];
  local int base_dist[D_CODES];
  local int extra_dbits[D_CODES] /* extra bits for each distance code */
     = {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};
  local ct_data static_ltree[L_CODES+2];
  local uch dist_code[512];
  local int base_dist[D_CODES];
  local ct_data static_dtree[D_CODES];
  
  local static_tree_desc  static_l_desc =
  {static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
  
  local static_tree_desc  static_d_desc =
  {static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};
  
  local int extra_blbits[BL_CODES]/* extra bits for each bit length code */
     = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
  
  local static_tree_desc  static_bl_desc =
  {(ct_data *)0, extra_blbits, 0,      BL_CODES, MAX_BL_BITS};
  
  local uch bl_order[BL_CODES]
     = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
  
  local config configuration_table[10] = {
  /*      good lazy nice chain */
  /* 0 */ {0,    0,  0,    0},  /* store only */
  /* 1 */ {4,    4,  8,    4},  /* maximum speed, no lazy matches */
  /* 2 */ {4,    5, 16,    8},
  /* 3 */ {4,    6, 32,   32},
  
  /* 4 */ {4,    4, 16,   16},  /* lazy matches */
  /* 5 */ {8,   16, 32,   32},
  /* 6 */ {8,   16, 128, 128},
  /* 7 */ {8,   32, 128, 256},
  /* 8 */ {32, 128, 258, 1024},
  /* 9 */ {32, 258, 258, 4096}}; /* maximum compression */
  
  #define FIXEDH 530	/* number of hufts used by fixed tables */
  local inflate_huft fixed_mem[FIXEDH];
  
  #define next more.Next
  
  #define Freq fc.freq
  #define Code fc.code
  #define Dad  dl.dad
  #define Len  dl.len
  
  local voidp zcalloc (opaque, items, size)
      voidp opaque;
      unsigned items;
      unsigned size;
  {
      return calloc(items, size);
  }
  
  local void  zcfree (opaque, ptr)
      voidp opaque;
      voidp ptr;
  {
      free(ptr);
  }
  
  local uLong crc32(crc, buf, len)
      uLong crc;
      Byte *buf;
      uInt len;
  {
      if (buf == Z_NULL) return 0L;
      crc = crc ^ 0xffffffffL;
      if (len) do {
  	crc = wm_tool_crc32_tab[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
      } while (--len);
      return crc ^ 0xffffffffL;
  }
  
  local void zmemcpy(dest, source, len)
      Byte* dest;
      Byte* source;
      uInt  len;
  {
      if (len == 0) return;
      do {
  	*dest++ = *source++; /* ??? to be unrolled */
      } while (--len != 0);
  }
  
  local void zmemzero(dest, len)
      Byte* dest;
      uInt  len;
  {
      if (len == 0) return;
      do {
  	*dest++ = 0;  /* ??? to be unrolled */
      } while (--len != 0);
  }
  
  local uLong adler32(adler, buf, len)
      uLong adler;
      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) {
  	    DO16(buf);
  	    k -= 16;
  	}
  	if (k != 0) do {
  	    DO1(buf);
  	} while (--k);
  	s1 %= BASE;
  	s2 %= BASE;
      }
      return (s2 << 16) | s1;
  }
  
  local int read_buf(strm, buf, size)
      z_stream *strm;
      char *buf;
      unsigned size;
  {
      unsigned len = strm->avail_in;
  
      if (len > size) len = size;
      if (len == 0) return 0;
  
      strm->avail_in  -= len;
  
      if (!strm->state->noheader) {
  	strm->state->adler = adler32(strm->state->adler, strm->next_in, len);
      }
      zmemcpy((Byte *)buf, strm->next_in, len);
      strm->next_in  += len;
      strm->total_in += len;
  
      return (int)len;
  }
  
  local int inflate_trees_free(t, z)
  inflate_huft *t;	/* table to free */
  z_stream *z;		/* for zfree function */
  /* Free the malloc'ed tables built by huft_build(), which makes a linked
     list of the tables it made, with the links in a dummy first entry of
     each table. */
  {
    register inflate_huft *p, *q;
  
    /* Don't free fixed trees */
    if (t >= fixed_mem && t <= fixed_mem + FIXEDH)
      return Z_OK;
  
    /* Go through linked list, freeing from the malloced (t[-1]) address. */
    p = t;
    while (p != Z_NULL)
    {
      q = (--p)->next;
      ZFREE(z,p);
      p = q;
    }
    return Z_OK;
  }
  
  local void inflate_codes_free(c, z)
  struct inflate_codes_state *c;
  z_stream *z;
  {
    inflate_trees_free(c->dtree, z);
    inflate_trees_free(c->ltree, z);
    ZFREE(z, c);
  }
  
  local int inflate_blocks_free(s, z, c)
  struct inflate_blocks_state *s;
  z_stream *z;
  uLong *c;
  {
    if (s->checkfn != Z_NULL)
      *c = s->check;
    if (s->mode == BTREE || s->mode == DTREE)
      ZFREE(z, s->sub.trees.blens);
    if (s->mode == CODES)
      inflate_codes_free(s->sub.codes, z);
    ZFREE(z, s->window);
    ZFREE(z, s);
    return Z_OK;
  }
  
  local int deflateEnd (strm)
      z_stream *strm;
  {
      if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
  
      TRY_FREE(strm, strm->state->window);
      TRY_FREE(strm, strm->state->prev);
      TRY_FREE(strm, strm->state->head);
      TRY_FREE(strm, strm->state->pending_buf);
  
      ZFREE(strm, strm->state);
      strm->state = Z_NULL;
  
      return Z_OK;
  }
  
  local int inflateEnd(z)
  z_stream *z;
  {
    uLong c;
  
    if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
      return Z_STREAM_ERROR;
    if (z->state->mode == BLOCKS)
      inflate_blocks_free(z->state->sub.blocks, z, &c);
    ZFREE(z, z->state);
    z->state = Z_NULL;
    return Z_OK;
  }
  
  local int destroy (s)
      gz_stream *s;
  {
      int err = Z_OK;
  
      if (!s) return Z_STREAM_ERROR;
  
      TRYFREE(s->inbuf);
      TRYFREE(s->outbuf);
      TRYFREE((voidp)s->path);
      TRYFREE((voidp)s->msg);
  
      if (s->stream.state != NULL) {
         if (s->mode == 'w') {
  	   err = deflateEnd(&(s->stream));
         } else if (s->mode == 'r') {
  	   err = inflateEnd(&(s->stream));
         }
      }
      if (s->file != NULL && fclose(s->file)) {
  	err = Z_ERRNO;
      }
      if (s->z_err < 0) err = s->z_err;
      zcfree((voidp)0, (voidp)s);
      return err;
  }
  
  local void putLong (file, x)
      FILE *file;
      uLong x;
  {
      int n;
      for (n = 0; n < 4; n++) {
  	fputc((int)(x & 0xff), file);
  	x >>= 8;
      }
  }
  
  local uLong getLong (buf)
      Byte *buf;
  {
      uLong x = 0;
      Byte *p = buf+4;
  
      do {
  	x <<= 8;
  	x |= *--p;
      } while (p != buf);
      return x;
  }
  
  local unsigned bi_reverse(code, len)
      unsigned code; /* the value to invert */
      int len;       /* its bit length */
  {
      register unsigned res = 0;
      do {
          res |= code & 1;
          code >>= 1, res <<= 1;
      } while (--len > 0);
      return res >> 1;
  }
  
  local void gen_codes (tree, max_code, bl_count)
      ct_data *tree;             /* the tree to decorate */
      int max_code;              /* largest code with non zero frequency */
      ush bl_count[];            /* number of codes at each bit length */
  {
      ush next_code[MAX_BITS+1]; /* next code value for each bit length */
      ush code = 0;              /* running code value */
      int bits;                  /* bit index */
      int n;                     /* code index */
  
      /* The distribution counts are first used to generate the code values
       * without bit reversal.
       */
      for (bits = 1; bits <= MAX_BITS; bits++) {
          next_code[bits] = code = (code + bl_count[bits-1]) << 1;
      }
      /* Check that the bit counts in bl_count are consistent. The last code
       * must be all ones.
       */
      Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
              "inconsistent bit counts");
  
      for (n = 0;  n <= max_code; n++) {
          int len = tree[n].Len;
          if (len == 0) continue;
          /* Now reverse the bits */
          tree[n].Code = bi_reverse(next_code[len]++, len);
      }
  }
  
  local void ct_static_init()
  {
      int n;        /* iterates over tree elements */
      int bits;     /* bit counter */
      int length;   /* length value */
      int code;     /* code value */
      int dist;     /* distance index */
      ush bl_count[MAX_BITS+1];
      /* number of codes at each bit length for an optimal tree */
  
      /* Initialize the mapping length (0..255) -> length code (0..28) */
      length = 0;
      for (code = 0; code < LENGTH_CODES-1; code++) {
          base_length[code] = length;
          for (n = 0; n < (1<<extra_lbits[code]); n++) {
              length_code[length++] = (uch)code;
          }
      }
      Assert (length == 256, "ct_static_init: length != 256");
      /* Note that the length 255 (match length 258) can be represented
       * in two different ways: code 284 + 5 bits or code 285, so we
       * overwrite length_code[255] to use the best encoding:
       */
      length_code[length-1] = (uch)code;
  
      /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
      dist = 0;
      for (code = 0 ; code < 16; code++) {
          base_dist[code] = dist;
          for (n = 0; n < (1<<extra_dbits[code]); n++) {
              dist_code[dist++] = (uch)code;
          }
      }
      Assert (dist == 256, "ct_static_init: dist != 256");
      dist >>= 7; /* from now on, all distances are divided by 128 */
      for ( ; code < D_CODES; code++) {
          base_dist[code] = dist << 7;
          for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
              dist_code[256 + dist++] = (uch)code;
          }
      }
      Assert (dist == 256, "ct_static_init: 256+dist != 512");
  
      /* Construct the codes of the static literal tree */
      for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
      n = 0;
      while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
      while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
      while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
      while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
      /* Codes 286 and 287 do not exist, but we must include them in the
       * tree construction to get a canonical Huffman tree (longest code
       * all ones)
       */
      gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
  
      /* The static distance tree is trivial: */
      for (n = 0; n < D_CODES; n++) {
          static_dtree[n].Len = 5;
          static_dtree[n].Code = bi_reverse(n, 5);
      }
  }
  
  local void init_block(s)
      deflate_state *s;
  {
      int n; /* iterates over tree elements */
  
      /* Initialize the trees. */
      for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0;
      for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0;
      for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
  
      s->dyn_ltree[END_BLOCK].Freq = 1;
      s->opt_len = s->static_len = 0L;
      s->last_lit = s->matches = 0;
  }
  
  local void ct_init(s)
      deflate_state *s;
  {
      if (static_dtree[0].Len == 0) {
  	ct_static_init();              /* To do: at compile time */
      }
  
      s->compressed_len = 0L;
  
      s->l_desc.dyn_tree = s->dyn_ltree;
      s->l_desc.stat_desc = &static_l_desc;
  
      s->d_desc.dyn_tree = s->dyn_dtree;
      s->d_desc.stat_desc = &static_d_desc;
  
      s->bl_desc.dyn_tree = s->bl_tree;
      s->bl_desc.stat_desc = &static_bl_desc;
  
      s->bi_buf = 0;
      s->bi_valid = 0;
  #ifdef DEBUG
      s->bits_sent = 0L;
  #endif
  
      /* Initialize the first block of the first file: */
      init_block(s);
  }
  
  local void lm_init (s)
      deflate_state *s;
  {
      register unsigned j;
  
      s->window_size = (ulg)2L*s->w_size;
  
  
      /* Initialize the hash table (avoiding 64K overflow for 16 bit systems).
       * prev[] will be initialized on the fly.
       */
      s->head[s->hash_size-1] = NIL;
      zmemzero((unsigned char*)s->head, (unsigned int)(s->hash_size-1)*sizeof(*s->head));
  
      /* Set the default configuration parameters:
       */
      s->max_lazy_match   = configuration_table[s->level].max_lazy;
      s->good_match       = configuration_table[s->level].good_length;
      s->nice_match       = configuration_table[s->level].nice_length;
      s->max_chain_length = configuration_table[s->level].max_chain;
  
      s->strstart = 0;
      s->block_start = 0L;
      s->lookahead = 0;
      s->match_length = MIN_MATCH-1;
      s->match_available = 0;
  #ifdef ASMV
      match_init(); /* initialize the asm code */
  #endif
  
      s->ins_h = 0;
      for (j=0; j<MIN_MATCH-1; j++) UPDATE_HASH(s, s->ins_h, s->window[j]);
      /* If lookahead < MIN_MATCH, ins_h is garbage, but this is
       * not important since only literal bytes will be emitted.
       */
  }
  
  local int deflateReset (strm)
      z_stream *strm;
  {
      deflate_state *s;
  
      if (strm == Z_NULL || strm->state == Z_NULL ||
  	strm->zalloc == Z_NULL || strm->zfree == Z_NULL) return Z_STREAM_ERROR;
  
      strm->total_in = strm->total_out = 0;
      strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
      strm->data_type = Z_UNKNOWN;
  
      s = (deflate_state *)strm->state;
      s->pending = 0;
      s->pending_out = s->pending_buf;
  
      s->status = s->noheader ? BUSY_STATE : INIT_STATE;
      s->adler = 1;
  
      ct_init(s);
      lm_init(s);
  
      return Z_OK;
  }
  
  local int deflateInit2 (strm, level, method, windowBits, memLevel, strategy)
      z_stream *strm;
      int  level;
      int  method;
      int  windowBits;
      int  memLevel;
      int  strategy;
  {
      deflate_state *s;
      int noheader = 0;
  
      if (strm == Z_NULL) return Z_STREAM_ERROR;
  
      strm->msg = Z_NULL;
      if (strm->zalloc == Z_NULL) strm->zalloc = zcalloc;
      if (strm->zfree == Z_NULL) strm->zfree = zcfree;
  
      if (level == Z_DEFAULT_COMPRESSION) level = 6;
  
      if (windowBits < 0) { /* undocumented feature: suppress zlib header */
  	noheader = 1;
  	windowBits = -windowBits;
      }
      if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != DEFLATED ||
  	windowBits < 8 || windowBits > 15 || level < 1 || level > 9) {
  	return Z_STREAM_ERROR;
      }
      s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
      if (s == Z_NULL) return Z_MEM_ERROR;
      strm->state = (struct internal_state *)s;
      s->strm = strm;
  
      s->noheader = noheader;
      s->w_bits = windowBits;
      s->w_size = 1 << s->w_bits;
      s->w_mask = s->w_size - 1;
  
      s->hash_bits = memLevel + 7;
      s->hash_size = 1 << s->hash_bits;
      s->hash_mask = s->hash_size - 1;
      s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
  
      s->window = (Byte*) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
      s->prev   = (Pos*)  ZALLOC(strm, s->w_size, sizeof(Pos));
      s->head   = (Pos*)  ZALLOC(strm, s->hash_size, sizeof(Pos));
  
      s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
  
      s->pending_buf = (uch*) ZALLOC(strm, s->lit_bufsize, 2*sizeof(ush));
  
      if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
  	s->pending_buf == Z_NULL) {
  	strm->msg = z_errmsg[1-Z_MEM_ERROR];
  	deflateEnd (strm);
  	return Z_MEM_ERROR;
      }
      s->d_buf = (ush*) &(s->pending_buf[s->lit_bufsize]);
      s->l_buf = (uch*) &(s->pending_buf[3*s->lit_bufsize]);
      /* We overlay pending_buf and d_buf+l_buf. This works since the average
       * output size for (length,distance) codes is <= 32 bits (worst case
       * is 15+15+13=33).
       */
  
      s->level = level;
      s->strategy = strategy;
      s->method = (Byte)method;
  
      return deflateReset(strm);
  }
  
  local int inflateInit2(z, w)
  z_stream *z;
  int w;
  {
    /* initialize state */
    if (z == Z_NULL)
      return Z_STREAM_ERROR;
    if (z->zalloc == Z_NULL) z->zalloc = zcalloc;
    if (z->zfree == Z_NULL) z->zfree = zcfree;
    z->total_in = z->total_out = 0;
    z->msg = Z_NULL;
    if ((z->state = (struct internal_state *)
         ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
      return Z_MEM_ERROR;
    z->state->mode = METHOD;
  
    /* handle undocumented nowrap option (no zlib header or check) */
    z->state->nowrap = 0;
    if (w < 0)
    {
      w = - w;
      z->state->nowrap = 1;
      z->state->mode = START;
    }
  
    /* set window size */
    if (w < 8 || w > 15)
    {
      inflateEnd(z);
      return Z_STREAM_ERROR;
    }
    z->state->wbits = w;
    return Z_OK;
  }
  
  local void putShortMSB (s, b)
      deflate_state *s;
      uInt b;
  {
      put_byte(s, (Byte)(b >> 8));
      put_byte(s, (Byte)(b & 0xff));
  }
  
  local void flush_pending(strm)
      z_stream *strm;
  {
      unsigned len = strm->state->pending;
  
      if (len > strm->avail_out) len = strm->avail_out;
      if (len == 0) return;
  
      zmemcpy(strm->next_out, strm->state->pending_out, len);
      strm->next_out  += len;
      strm->state->pending_out  += len;
      strm->total_out += len;
      strm->avail_out  -= len;
      strm->state->pending -= len;
      if (strm->state->pending == 0) {
  	strm->state->pending_out = strm->state->pending_buf;
      }
  }
  
  local void fill_window(s)
      deflate_state *s;
  {
      register unsigned n, m;
      unsigned more;    /* Amount of free space at the end of the window. */
  
      do {
  	more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
  
  	/* Deal with !@#$% 64K limit: */
  	if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
  	    more = s->w_size;
          } else if (more == (unsigned)(-1)) {
              /* Very unlikely, but possible on 16 bit machine if strstart == 0
               * and lookahead == 1 (input done one byte at time)
               */
              more--;
  
          /* If the window is almost full and there is insufficient lookahead,
           * move the upper half to the lower one to make room in the upper half.
           */
          } else if (s->strstart >= s->w_size+MAX_DIST(s)) {
  
              /* By the IN assertion, the window is not empty so we can't confuse
               * more == 0 with more == 64K on a 16 bit machine.
               */
              memcpy((char*)s->window, (char*)s->window+s->w_size,
  		   (unsigned)s->w_size);
              s->match_start -= s->w_size;
              s->strstart    -= s->w_size; /* we now have strstart >= MAX_DIST */
  
              s->block_start -= (long) s->w_size;
  
              for (n = 0; n < s->hash_size; n++) {
                  m = s->head[n];
                  s->head[n] = (Pos)(m >= s->w_size ? m-s->w_size : NIL);
              }
              for (n = 0; n < s->w_size; n++) {
                  m = s->prev[n];
                  s->prev[n] = (Pos)(m >= s->w_size ? m-s->w_size : NIL);
                  /* If n is not on any hash chain, prev[n] is garbage but
                   * its value will never be used.
                   */
              }
              more += s->w_size;
          }
          if (s->strm->avail_in == 0) return;
  
          /* If there was no sliding:
           *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
           *    more == window_size - lookahead - strstart
           * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
           * => more >= window_size - 2*WSIZE + 2
           * In the BIG_MEM or MMAP case (not yet supported),
           *   window_size == input_size + MIN_LOOKAHEAD  &&
           *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
           * Otherwise, window_size == 2*WSIZE so more >= 2.
           * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
           */
          Assert(more >= 2, "more < 2");
  
          n = read_buf(s->strm, (char*)s->window + s->strstart + s->lookahead,
  		     more);
  	s->lookahead += n;
  
      } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
  }
  
  /* For 80x86 and 680x0, an optimized version will be provided in match.asm or
   * match.S. The code will be functionally equivalent.
   */
  local int longest_match(s, cur_match)
      deflate_state *s;
      IPos cur_match;                             /* current match */
  {
      unsigned chain_length = s->max_chain_length;/* max hash chain length */
      register Byte *scan = s->window + s->strstart; /* current string */
      register Byte *match;                       /* matched string */
      register int len;                           /* length of current match */
      int best_len = s->prev_length;              /* best match length so far */
      IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
  	s->strstart - (IPos)MAX_DIST(s) : NIL;
      /* Stop when cur_match becomes <= limit. To simplify the code,
       * we prevent matches with the string of window index 0.
       */
  
  #ifdef UNALIGNED_OK
      /* Compare two bytes at a time. Note: this is not always beneficial.
       * Try with and without -DUNALIGNED_OK to check.
       */
      register Byte *strend = s->window + s->strstart + MAX_MATCH - 1;
      register ush scan_start = *(ush*)scan;
      register ush scan_end   = *(ush*)(scan+best_len-1);
  #else
      register Byte *strend = s->window + s->strstart + MAX_MATCH;
      register Byte scan_end1  = scan[best_len-1];
      register Byte scan_end   = scan[best_len];
  #endif
  
      /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
       * It is easy to get rid of this optimization if necessary.
       */
      Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
  
      /* Do not waste too much time if we already have a good match: */
      if (s->prev_length >= s->good_match) {
          chain_length >>= 2;
      }
      Assert(s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
  
      do {
          Assert(cur_match < s->strstart, "no future");
          match = s->window + cur_match;
  
          /* Skip to next match if the match length cannot increase
           * or if the match length is less than 2:
           */
  #if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
          /* This code assumes sizeof(unsigned short) == 2. Do not use
           * UNALIGNED_OK if your compiler uses a different size.
           */
          if (*(ush*)(match+best_len-1) != scan_end ||
              *(ush*)match != scan_start) continue;
  
          /* It is not necessary to compare scan[2] and match[2] since they are
           * always equal when the other bytes match, given that the hash keys
           * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
           * strstart+3, +5, ... up to strstart+257. We check for insufficient
           * lookahead only every 4th comparison; the 128th check will be made
           * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
           * necessary to put more guard bytes at the end of the window, or
           * to check more often for insufficient lookahead.
           */
          scan++, match++;
          do {
          } while (*(ush*)(scan+=2) == *(ush*)(match+=2) &&
                   *(ush*)(scan+=2) == *(ush*)(match+=2) &&
                   *(ush*)(scan+=2) == *(ush*)(match+=2) &&
                   *(ush*)(scan+=2) == *(ush*)(match+=2) &&
                   scan < strend);
          /* The funny "do {}" generates better code on most compilers */
  
          /* Here, scan <= window+strstart+257 */
          Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
          if (*scan == *match) scan++;
  
          len = (MAX_MATCH - 1) - (int)(strend-scan);
          scan = strend - (MAX_MATCH-1);
  
  #else /* UNALIGNED_OK */
  
          if (match[best_len]   != scan_end  ||
              match[best_len-1] != scan_end1 ||
              *match            != *scan     ||
              *++match          != scan[1])      continue;
  
          /* The check at best_len-1 can be removed because it will be made
           * again later. (This heuristic is not always a win.)
           * It is not necessary to compare scan[2] and match[2] since they
           * are always equal when the other bytes match, given that
           * the hash keys are equal and that HASH_BITS >= 8.
           */
          scan += 2, match++;
  
          /* We check for insufficient lookahead only every 8th comparison;
           * the 256th check will be made at strstart+258.
           */
          do {
          } while (*++scan == *++match && *++scan == *++match &&
                   *++scan == *++match && *++scan == *++match &&
                   *++scan == *++match && *++scan == *++match &&
                   *++scan == *++match && *++scan == *++match &&
                   scan < strend);
  
          Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
  
          len = MAX_MATCH - (int)(strend - scan);
          scan = strend - MAX_MATCH;
  
  #endif /* UNALIGNED_OK */
  
          if (len > best_len) {
              s->match_start = cur_match;
              best_len = len;
              if (len >= s->nice_match) break;
  #ifdef UNALIGNED_OK
              scan_end = *(ush*)(scan+best_len-1);
  #else
              scan_end1  = scan[best_len-1];
              scan_end   = scan[best_len];
  #endif
          }
      } while ((cur_match = s->prev[cur_match & s->w_mask]) > limit
               && --chain_length != 0);
  
      return best_len;
  }
  
  local int ct_tally (s, dist, lc)
      deflate_state *s;
      int dist;  /* distance of matched string */
      int lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
  {
      s->d_buf[s->last_lit] = (ush)dist;
      s->l_buf[s->last_lit++] = (uch)lc;
      if (dist == 0) {
          /* lc is the unmatched char */
          s->dyn_ltree[lc].Freq++;
      } else {
  	s->matches++;
          /* Here, lc is the match length - MIN_MATCH */
          dist--;             /* dist = match distance - 1 */
          Assert((ush)dist < (ush)MAX_DIST(s) &&
                 (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
                 (ush)d_code(dist) < (ush)D_CODES,  "ct_tally: bad match");
  
          s->dyn_ltree[length_code[lc]+LITERALS+1].Freq++;
          s->dyn_dtree[d_code(dist)].Freq++;
      }
  
      /* Try to guess if it is profitable to stop the current block here */
      if (s->level > 2 && (s->last_lit & 0xfff) == 0) {
          /* Compute an upper bound for the compressed length */
          ulg out_length = (ulg)s->last_lit*8L;
          ulg in_length = (ulg)s->strstart - s->block_start;
          int dcode;
          for (dcode = 0; dcode < D_CODES; dcode++) {
              out_length += (ulg)s->dyn_dtree[dcode].Freq *
  		(5L+extra_dbits[dcode]);
          }
          out_length >>= 3;
  
          if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
      }
      return (s->last_lit == s->lit_bufsize-1);
      /* We avoid equality with lit_bufsize because of wraparound at 64K
       * on 16 bit machines and because stored blocks are restricted to
       * 64K-1 bytes.
       */
  }
  
  local void set_data_type(s)
      deflate_state *s;
  {
      int n = 0;
      unsigned ascii_freq = 0;
      unsigned bin_freq = 0;
      while (n < 7)        bin_freq += s->dyn_ltree[n++].Freq;
      while (n < 128)    ascii_freq += s->dyn_ltree[n++].Freq;
      while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq;
      s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII);
  }
  
  local void pqdownheap(s, tree, k)
      deflate_state *s;
      ct_data *tree;  /* the tree to restore */
      int k;               /* node to move down */
  {
      int v = s->heap[k];
      int j = k << 1;  /* left son of k */
      while (j <= s->heap_len) {
          /* Set j to the smallest of the two sons: */
          if (j < s->heap_len &&
  	    smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
  	    j++;
  	}
          /* Exit if v is smaller than both sons */
          if (smaller(tree, v, s->heap[j], s->depth)) break;
  
          /* Exchange v with the smallest son */
          s->heap[k] = s->heap[j];  k = j;
  
          /* And continue down the tree, setting j to the left son of k */
          j <<= 1;
      }
      s->heap[k] = v;
  }
  
  local void gen_bitlen(s, desc)
      deflate_state *s;
      tree_desc *desc;    /* the tree descriptor */
  {
      ct_data *tree  = desc->dyn_tree;
      int max_code   = desc->max_code;
      ct_data *stree = desc->stat_desc->static_tree;
      int *extra     = desc->stat_desc->extra_bits;
      int base       = desc->stat_desc->extra_base;
      int max_length = desc->stat_desc->max_length;
      int h;              /* heap index */
      int n, m;           /* iterate over the tree elements */
      int bits;           /* bit length */
      int xbits;          /* extra bits */
      ush f;              /* frequency */
      int overflow = 0;   /* number of elements with bit length too large */
  
      for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
  
      /* In a first pass, compute the optimal bit lengths (which may
       * overflow in the case of the bit length tree).
       */
      tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
  
      for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
          n = s->heap[h];
          bits = tree[tree[n].Dad].Len + 1;
          if (bits > max_length) bits = max_length, overflow++;
          tree[n].Len = (ush)bits;
          /* We overwrite tree[n].Dad which is no longer needed */
  
          if (n > max_code) continue; /* not a leaf node */
  
          s->bl_count[bits]++;
          xbits = 0;
          if (n >= base) xbits = extra[n-base];
          f = tree[n].Freq;
          s->opt_len += (ulg)f * (bits + xbits);
          if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
      }
      if (overflow == 0) return;
  
      /* This happens for example on obj2 and pic of the Calgary corpus */
  
      /* Find the first bit length which could increase: */
      do {
          bits = max_length-1;
          while (s->bl_count[bits] == 0) bits--;
          s->bl_count[bits]--;      /* move one leaf down the tree */
          s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
          s->bl_count[max_length]--;
          /* The brother of the overflow item also moves one step up,
           * but this does not affect bl_count[max_length]
           */
          overflow -= 2;
      } while (overflow > 0);
  
      /* Now recompute all bit lengths, scanning in increasing frequency.
       * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
       * lengths instead of fixing only the wrong ones. This idea is taken
       * from 'ar' written by Haruhiko Okumura.)
       */
      for (bits = max_length; bits != 0; bits--) {
          n = s->bl_count[bits];
          while (n != 0) {
              m = s->heap[--h];
              if (m > max_code) continue;
              if (tree[m].Len != (unsigned) bits) {
  
                  s->opt_len += ((long)bits - (long)tree[m].Len)
  		              *(long)tree[m].Freq;
                  tree[m].Len = (ush)bits;
              }
              n--;
          }
      }
  }
  
  local void build_tree(s, desc)
      deflate_state *s;
      tree_desc *desc; /* the tree descriptor */
  {
      ct_data *tree   = desc->dyn_tree;
      ct_data *stree  = desc->stat_desc->static_tree;
      int elems       = desc->stat_desc->elems;
      int n, m;          /* iterate over heap elements */
      int max_code = -1; /* largest code with non zero frequency */
      int node = elems;  /* next internal node of the tree */
      int new;           /* new node being created */
  
      /* Construct the initial heap, with least frequent element in
       * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
       * heap[0] is not used.
       */
      s->heap_len = 0, s->heap_max = HEAP_SIZE;
  
      for (n = 0; n < elems; n++) {
          if (tree[n].Freq != 0) {
              s->heap[++(s->heap_len)] = max_code = n;
              s->depth[n] = 0;
          } else {
              tree[n].Len = 0;
          }
      }
  
      /* The pkzip format requires that at least one distance code exists,
       * and that at least one bit should be sent even if there is only one
       * possible code. So to avoid special checks later on we force at least
       * two codes of non zero frequency.
       */
      while (s->heap_len < 2) {
          new = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
          tree[new].Freq = 1;
          s->depth[new] = 0;
          s->opt_len--; if (stree) s->static_len -= stree[new].Len;
          /* new is 0 or 1 so it does not have extra bits */
      }
      desc->max_code = max_code;
  
      /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
       * establish sub-heaps of increasing lengths:
       */
      for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
  
      /* Construct the Huffman tree by repeatedly combining the least two
       * frequent nodes.
       */
      do {
          pqremove(s, tree, n);  /* n = node of least frequency */
          m = s->heap[SMALLEST]; /* m = node of next least frequency */
  
          s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
          s->heap[--(s->heap_max)] = m;
  
          /* Create a new node father of n and m */
          tree[node].Freq = tree[n].Freq + tree[m].Freq;
          s->depth[node] = (uch) (MAX(s->depth[n], s->depth[m]) + 1);
          tree[n].Dad = tree[m].Dad = (ush)node;
  #ifdef DUMP_BL_TREE
          if (tree == s->bl_tree) {
              fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
                      node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
          }
  #endif
          /* and insert the new node in the heap */
          s->heap[SMALLEST] = node++;
          pqdownheap(s, tree, SMALLEST);
  
      } while (s->heap_len >= 2);
  
      s->heap[--(s->heap_max)] = s->heap[SMALLEST];
  
      /* At this point, the fields freq and dad are set. We can now
       * generate the bit lengths.
       */
      gen_bitlen(s, (tree_desc *)desc);
  
      /* The field len is now set, we can generate the bit codes */
      gen_codes ((ct_data *)tree, max_code, s->bl_count);
  }
  
  local void scan_tree (s, tree, max_code)
      deflate_state *s;
      ct_data *tree;   /* the tree to be scanned */
      int max_code;    /* and its largest code of non zero frequency */
  {
      int n;                     /* iterates over all tree elements */
      int prevlen = -1;          /* last emitted length */
      int curlen;                /* length of current code */
      int nextlen = tree[0].Len; /* length of next code */
      int count = 0;             /* repeat count of the current code */
      int max_count = 7;         /* max repeat count */
      int min_count = 4;         /* min repeat count */
  
      if (nextlen == 0) max_count = 138, min_count = 3;
      tree[max_code+1].Len = (ush)0xffff; /* guard */
  
      for (n = 0; n <= max_code; n++) {
          curlen = nextlen; nextlen = tree[n+1].Len;
          if (++count < max_count && curlen == nextlen) {
              continue;
          } else if (count < min_count) {
              s->bl_tree[curlen].Freq += count;
          } else if (curlen != 0) {
              if (curlen != prevlen) s->bl_tree[curlen].Freq++;
              s->bl_tree[REP_3_6].Freq++;
          } else if (count <= 10) {
              s->bl_tree[REPZ_3_10].Freq++;
          } else {
              s->bl_tree[REPZ_11_138].Freq++;
          }
          count = 0; prevlen = curlen;
          if (nextlen == 0) {
              max_count = 138, min_count = 3;
          } else if (curlen == nextlen) {
              max_count = 6, min_count = 3;
          } else {
              max_count = 7, min_count = 4;
          }
      }
  }
  
  local int build_bl_tree(s)
      deflate_state *s;
  {
      int max_blindex;  /* index of last bit length code of non zero freq */
  
      /* Determine the bit length frequencies for literal and distance trees */
      scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
      scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
  
      /* Build the bit length tree: */
      build_tree(s, (tree_desc *)(&(s->bl_desc)));
      /* opt_len now includes the length of the tree representations, except
       * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
       */
  
      /* Determine the number of bit length codes to send. The pkzip format
       * requires that at least 4 bit length codes be sent. (appnote.txt says
       * 3 but the actual value used is 4.)
       */
      for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
          if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
      }
      /* Update opt_len to include the bit length tree and counts */
      s->opt_len += 3*(max_blindex+1) + 5+5+4;
  
      return max_blindex;
  }
  
  local void send_bits(s, value, length)
      deflate_state *s;
      int value;  /* value to send */
      int length; /* number of bits */
  {
  #ifdef DEBUG
      Assert(length > 0 && length <= 15, "invalid length");
      s->bits_sent += (ulg)length;
  #endif
      /* If not enough room in bi_buf, use (valid) bits from bi_buf and
       * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
       * unused bits in value.
       */
      if (s->bi_valid > (int)Buf_size - length) {
          s->bi_buf |= (value << s->bi_valid);
          put_short(s, s->bi_buf);
          s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
          s->bi_valid += length - Buf_size;
      } else {
          s->bi_buf |= value << s->bi_valid;
          s->bi_valid += length;
      }
  }
  
  local void bi_windup(s)
      deflate_state *s;
  {
      if (s->bi_valid > 8) {
          put_short(s, s->bi_buf);
      } else if (s->bi_valid > 0) {
          put_byte(s, (Byte)s->bi_buf);
      }
      s->bi_buf = 0;
      s->bi_valid = 0;
  #ifdef DEBUG
      s->bits_sent = (s->bits_sent+7) & ~7;
  #endif
  }
  
  local void copy_block(s, buf, len, header)
      deflate_state *s;
      char     *buf;    /* the input data */
      unsigned len;     /* its length */
      int      header;  /* true if block header must be written */
  {
      bi_windup(s);              /* align on byte boundary */
  
      if (header) {
          put_short(s, (ush)len);
          put_short(s, (ush)~len);
  #ifdef DEBUG
          s->bits_sent += 2*16;
  #endif
      }
  #ifdef DEBUG
      s->bits_sent += (ulg)len<<3;
  #endif
      while (len--) {
  	put_byte(s, *buf++);
      }
  }
  
  local void compress_block(s, ltree, dtree)
      deflate_state *s;
      ct_data *ltree; /* literal tree */
      ct_data *dtree; /* distance tree */
  {
      unsigned dist;      /* distance of matched string */
      int lc;             /* match length or unmatched char (if dist == 0) */
      unsigned lx = 0;    /* running index in l_buf */
      unsigned code;      /* the code to send */
      int extra;          /* number of extra bits to send */
  
      if (s->last_lit != 0) do {
  	dist = s->d_buf[lx];
          lc = s->l_buf[lx++];
          if (dist == 0) {
              send_code(s, lc, ltree); /* send a literal byte */
          } else {
              /* Here, lc is the match length - MIN_MATCH */
              code = length_code[lc];
              send_code(s, code+LITERALS+1, ltree); /* send the length code */
              extra = extra_lbits[code];
              if (extra != 0) {
                  lc -= base_length[code];
                  send_bits(s, lc, extra);       /* send the extra length bits */
              }
  	    dist--; /* dist is now the match distance - 1 */
              code = d_code(dist);
              Assert (code < D_CODES, "bad d_code");
  
              send_code(s, code, dtree);       /* send the distance code */
              extra = extra_dbits[code];
              if (extra != 0) {
                  dist -= base_dist[code];
                  send_bits(s, dist, extra);   /* send the extra distance bits */
              }
          } /* literal or match pair ? */
  
  	/* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
  	Assert(s->pending < s->lit_bufsize + 2*lx, "pendingBuf overflow");
  
      } while (lx < s->last_lit);
  
      send_code(s, END_BLOCK, ltree);
  }
  
  local void send_tree (s, tree, max_code)
      deflate_state *s;
      ct_data *tree; /* the tree to be scanned */
      int max_code;       /* and its largest code of non zero frequency */
  {
      int n;                     /* iterates over all tree elements */
      int prevlen = -1;          /* last emitted length */
      int curlen;                /* length of current code */
      int nextlen = tree[0].Len; /* length of next code */
      int count = 0;             /* repeat count of the current code */
      int max_count = 7;         /* max repeat count */
      int min_count = 4;         /* min repeat count */
  
      /* tree[max_code+1].Len = -1; */  /* guard already set */
      if (nextlen == 0) max_count = 138, min_count = 3;
  
      for (n = 0; n <= max_code; n++) {
          curlen = nextlen; nextlen = tree[n+1].Len;
          if (++count < max_count && curlen == nextlen) {
              continue;
          } else if (count < min_count) {
              do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
  
          } else if (curlen != 0) {
              if (curlen != prevlen) {
                  send_code(s, curlen, s->bl_tree); count--;
              }
              Assert(count >= 3 && count <= 6, " 3_6?");
              send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
  
          } else if (count <= 10) {
              send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
  
          } else {
              send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
          }
          count = 0; prevlen = curlen;
          if (nextlen == 0) {
              max_count = 138, min_count = 3;
          } else if (curlen == nextlen) {
              max_count = 6, min_count = 3;
          } else {
              max_count = 7, min_count = 4;
          }
      }
  }
  
  local void send_all_trees(s, lcodes, dcodes, blcodes)
      deflate_state *s;
      int lcodes, dcodes, blcodes; /* number of codes for each tree */
  {
      int rank;                    /* index in bl_order */
  
      Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
      Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
              "too many codes");
      send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
      send_bits(s, dcodes-1,   5);
      send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */
      for (rank = 0; rank < blcodes; rank++) {
          send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
      }
  
      send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
  
      send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
  }
  
  ulg ct_flush_block(s, buf, stored_len, eof)
      deflate_state *s;
      char *buf;        /* input block, or NULL if too old */
      ulg stored_len;   /* length of input block */
      int eof;          /* true if this is the last block for a file */
  {
      ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
      int max_blindex;  /* index of last bit length code of non zero freq */
  
       /* Check if the file is ascii or binary */
      if (s->data_type == UNKNOWN) set_data_type(s);
  
      /* Construct the literal and distance trees */
      build_tree(s, (tree_desc *)(&(s->l_desc)));
  
  
      build_tree(s, (tree_desc *)(&(s->d_desc)));
  
      /* At this point, opt_len and static_len are the total bit lengths of
       * the compressed block data, excluding the tree representations.
       */
  
      /* Build the bit length tree for the above two trees, and get the index
       * in bl_order of the last bit length code to send.
       */
      max_blindex = build_bl_tree(s);
  
      /* Determine the best encoding. Compute first the block length in bytes */
      opt_lenb = (s->opt_len+3+7)>>3;
      static_lenb = (s->static_len+3+7)>>3;
  
      if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
  
      /* If compression failed and this is the first and last block,
       * and if the .zip file can be seeked (to rewrite the local header),
       * the whole file is transformed into a stored file:
       */
  
      if (stored_len+4 <= opt_lenb && buf != (char*)0) {
          /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
           * Otherwise we can't have processed more than WSIZE input bytes since
           * the last block flush, because compression would have been
           * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
           * transform a block into a stored block.
           */
          send_bits(s, (STORED_BLOCK<<1)+eof, 3);  /* send block type */
          s->compressed_len = (s->compressed_len + 3 + 7) & ~7L;
          s->compressed_len += (stored_len + 4) << 3;
  
          copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
  
      } else if (static_lenb == opt_lenb) {
          send_bits(s, (STATIC_TREES<<1)+eof, 3);
          compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
          s->compressed_len += 3 + s->static_len;
      } else {
          send_bits(s, (DYN_TREES<<1)+eof, 3);
          send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
  		       max_blindex+1);
          compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
          s->compressed_len += 3 + s->opt_len;
      }
      Assert (s->compressed_len == s->bits_sent, "bad compressed size");
      init_block(s);
  
      if (eof) {
          bi_windup(s);
          s->compressed_len += 7;  /* align on byte boundary */
      }
  
      return s->compressed_len >> 3;
  }
  
  local int deflate_fast(s, flush)
      deflate_state *s;
      int flush;
  {
      IPos hash_head; /* head of the hash chain */
      int bflush;     /* set if current block must be flushed */
  
      s->prev_length = MIN_MATCH-1;
  
      for (;;) {
          /* Make sure that we always have enough lookahead, except
           * at the end of the input file. We need MAX_MATCH bytes
           * for the next match, plus MIN_MATCH bytes to insert the
           * string following the next match.
           */
          if (s->lookahead < MIN_LOOKAHEAD) {
  	    fill_window(s);
  	    if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1;
  
  	    if (s->lookahead == 0) break; /* flush the current block */
  	}
  
          /* Insert the string window[strstart .. strstart+2] in the
           * dictionary, and set hash_head to the head of the hash chain:
           */
          INSERT_STRING(s, s->strstart, hash_head);
  
          /* Find the longest match, discarding those <= prev_length.
           * At this point we have always match_length < MIN_MATCH
           */
          if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
              /* To simplify the code, we prevent matches with the string
               * of window index 0 (in particular we have to avoid a match
               * of the string with itself at the start of the input file).
               */
  	    if (s->strategy != Z_HUFFMAN_ONLY) {
  		s->match_length = longest_match (s, hash_head);
  	    }
              /* longest_match() sets match_start */
  
              if (s->match_length > s->lookahead) s->match_length = s->lookahead;
          }
          if (s->match_length >= MIN_MATCH) {
              check_match(s, s->strstart, s->match_start, s->match_length);
  
              bflush = ct_tally(s, s->strstart - s->match_start,
  		 	      s->match_length - MIN_MATCH);
  
              s->lookahead -= s->match_length;
  
              /* Insert new strings in the hash table only if the match length
               * is not too large. This saves time but degrades compression.
               */
              if (s->match_length <= s->max_insert_length) {
                  s->match_length--; /* string at strstart already in hash table */
                  do {
                      s->strstart++;
                      INSERT_STRING(s, s->strstart, hash_head);
                      /* strstart never exceeds WSIZE-MAX_MATCH, so there are
                       * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
                       * these bytes are garbage, but it does not matter since
                       * the next lookahead bytes will be emitted as literals.
                       */
                  } while (--s->match_length != 0);
                  s->strstart++;
              } else {
                  s->strstart += s->match_length;
                  s->match_length = 0;
                  s->ins_h = s->window[s->strstart];
                  UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
  #if MIN_MATCH != 3
                  Call UPDATE_HASH() MIN_MATCH-3 more times
  #endif
              }
          } else {
              /* No match, output a literal byte */
              bflush = ct_tally (s, 0, s->window[s->strstart]);
              s->lookahead--;
              s->strstart++;
          }
          if (bflush) FLUSH_BLOCK(s, 0);
      }
      FLUSH_BLOCK(s, flush == Z_FINISH);
      return 0; /* normal exit */
  }
  
  local int deflate_slow(s, flush)
      deflate_state *s;
      int flush;
  {
      IPos hash_head;          /* head of hash chain */
      int bflush;              /* set if current block must be flushed */
  
      /* Process the input block. */
      for (;;) {
          /* Make sure that we always have enough lookahead, except
           * at the end of the input file. We need MAX_MATCH bytes
           * for the next match, plus MIN_MATCH bytes to insert the
           * string following the next match.
           */
          if (s->lookahead < MIN_LOOKAHEAD) {
  	    fill_window(s);
  	    if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) return 1;
  
  	    if (s->lookahead == 0) break; /* flush the current block */
  	}
  
          /* Insert the string window[strstart .. strstart+2] in the
           * dictionary, and set hash_head to the head of the hash chain:
           */
          INSERT_STRING(s, s->strstart, hash_head);
  
          /* Find the longest match, discarding those <= prev_length.
           */
          s->prev_length = s->match_length, s->prev_match = s->match_start;
          s->match_length = MIN_MATCH-1;
  
          if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
              s->strstart - hash_head <= MAX_DIST(s)) {
              /* To simplify the code, we prevent matches with the string
               * of window index 0 (in particular we have to avoid a match
               * of the string with itself at the start of the input file).
               */
  	    if (s->strategy != Z_HUFFMAN_ONLY) {
  		s->match_length = longest_match (s, hash_head);
  	    }
              /* longest_match() sets match_start */
              if (s->match_length > s->lookahead) s->match_length = s->lookahead;
  
              if (s->match_length <= 5 && (s->strategy == Z_FILTERED ||
  	         (s->match_length == MIN_MATCH &&
  		  s->strstart - s->match_start > TOO_FAR))) {
  
                  /* If prev_match is also MIN_MATCH, match_start is garbage
                   * but we will ignore the current match anyway.
                   */
                  s->match_length = MIN_MATCH-1;
              }
          }
          /* If there was a match at the previous step and the current
           * match is not better, output the previous match:
           */
          if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
  
              check_match(s, s->strstart-1, s->prev_match, s->prev_length);
  
              bflush = ct_tally(s, s->strstart -1 - s->prev_match,
  			      s->prev_length - MIN_MATCH);
  
              /* Insert in hash table all strings up to the end of the match.
               * strstart-1 and strstart are already inserted.
               */
              s->lookahead -= s->prev_length-1;
              s->prev_length -= 2;
              do {
                  s->strstart++;
                  INSERT_STRING(s, s->strstart, hash_head);
                  /* strstart never exceeds WSIZE-MAX_MATCH, so there are
                   * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
                   * these bytes are garbage, but it does not matter since the
                   * next lookahead bytes will always be emitted as literals.
                   */
              } while (--s->prev_length != 0);
              s->match_available = 0;
              s->match_length = MIN_MATCH-1;
              s->strstart++;
  
              if (bflush) FLUSH_BLOCK(s, 0);
  
          } else if (s->match_available) {
              /* If there was no match at the previous position, output a
               * single literal. If there was a match but the current match
               * is longer, truncate the previous match to a single literal.
               */
              if (ct_tally (s, 0, s->window[s->strstart-1])) {
  	        FLUSH_BLOCK_ONLY(s, 0);
              }
              s->strstart++;
              s->lookahead--;
  	    if (s->strm->avail_out == 0) return 1;
          } else {
              /* There is no previous match to compare with, wait for
               * the next step to decide.
               */
              s->match_available = 1;
              s->strstart++;
              s->lookahead--;
          }
      }
      if (s->match_available) ct_tally (s, 0, s->window[s->strstart-1]);
  
      FLUSH_BLOCK(s, flush == Z_FINISH);
      return 0;
  }
  
  local int deflate (strm, flush)
      z_stream *strm;
      int flush;
  {
      if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
  
      if (strm->next_out == Z_NULL || strm->next_in == Z_NULL) {
  	ERR_RETURN(strm, Z_STREAM_ERROR);
      }
      if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
  
      strm->state->strm = strm; /* just in case */
  
      /* Write the zlib header */
      if (strm->state->status == INIT_STATE) {
  
  	uInt header = (DEFLATED + ((strm->state->w_bits-8)<<4)) << 8;
          uInt level_flags = (strm->state->level-1) >> 1;
  
  	if (level_flags > 3) level_flags = 3;
  	header |= (level_flags << 6);
  	header += 31 - (header % 31);
  
  	strm->state->status = BUSY_STATE;
  	putShortMSB(strm->state, header);
      }
  
      /* Flush as much pending output as possible */
      if (strm->state->pending != 0) {
  	flush_pending(strm);
  	if (strm->avail_out == 0) return Z_OK;
      }
  
      /* User must not provide more input after the first FINISH: */
      if (strm->state->status == FINISH_STATE && strm->avail_in != 0) {
  	ERR_RETURN(strm, Z_BUF_ERROR);
      }
  
      /* Start a new block or continue the current one.
       */
      if (strm->avail_in != 0 ||
  	(flush == Z_FINISH && strm->state->status != FINISH_STATE)) {
  
  	if (flush == Z_FINISH) {
  	    strm->state->status = FINISH_STATE;
  	}
          if (strm->state->level <= 3) {
  	    if (deflate_fast(strm->state, flush)) return Z_OK;
  	} else {
  	    if (deflate_slow(strm->state, flush)) return Z_OK;
  	}
      }
      Assert(strm->avail_out > 0, "bug2");
  
      if (flush != Z_FINISH) return Z_OK;
      if (strm->state->noheader) return Z_STREAM_END;
  
      /* Write the zlib trailer (adler32) */
      putShortMSB(strm->state, (uInt)(strm->state->adler >> 16));
      putShortMSB(strm->state, (uInt)(strm->state->adler & 0xffff));
      flush_pending(strm);
      /* If avail_out is zero, the application will call deflate again
       * to flush the rest.
       */
      strm->state->noheader = 1; /* write the trailer only once! */
      return strm->state->pending != 0 ? Z_OK : Z_STREAM_END;
  }
  
  local int gzflush (file, flush)
      gzFile file;
      int flush;
  {
      uInt len;
      int done = 0;
      gz_stream *s = (gz_stream*)file;
  
      if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
  
      s->stream.avail_in = 0; /* should be zero already anyway */
  
      for (;;) {
  	len = Z_BUFSIZE - s->stream.avail_out;
  
  	if (len != 0) {
  	    if (fwrite(s->outbuf, 1, len, s->file) != len) {
  		s->z_err = Z_ERRNO;
  		return Z_ERRNO;
  	    }
  	    s->stream.next_out = s->outbuf;
  	    s->stream.avail_out = Z_BUFSIZE;
  	}
  	if (done) break;
  	s->z_err = deflate(&(s->stream), flush);
  
          /* deflate has finished flushing only when it hasn't used up
           * all the available space in the output buffer:
           */
          done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
  
  	if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
      }
      return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
  }
  
  local gzFile gz_open (path, mode, fd)
      char *path;
      char *mode;
      int  fd;
  {
      int err;
      char *p = mode;
      gz_stream *s = (gz_stream *)ALLOC(sizeof(gz_stream));
  
      if (!s) return Z_NULL;
  
      s->stream.zalloc = (alloc_func)0;
      s->stream.zfree = (free_func)0;
      s->stream.next_in = s->inbuf = Z_NULL;
      s->stream.next_out = s->outbuf = Z_NULL;
      s->stream.avail_in = s->stream.avail_out = 0;
      s->file = NULL;
      s->z_err = Z_OK;
      s->z_eof = 0;
      s->crc = crc32(0L, Z_NULL, 0);
      s->msg = NULL;
      s->transparent = 0;
  
      s->path = (char*)ALLOC(strlen(path)+1);
      if (s->path == NULL) {
  	return destroy(s), (gzFile)Z_NULL;
      }
      strcpy(s->path, path); /* do this early for debugging */
  
      s->mode = '\0';
      do {
  	if (*p == 'r') s->mode = 'r';
  	if (*p == 'w') s->mode = 'w';
      } while (*p++);
      if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
  
      if (s->mode == 'w') {
  	err = deflateInit2(&(s->stream), Z_BEST_COMPRESSION,
  			   DEFLATED, -WBITS, MEM_LEVEL, 0);
  	/* windowBits is passed < 0 to suppress zlib header */
  
  	s->stream.next_out = s->outbuf = ALLOC(Z_BUFSIZE);
  
  	if (err != Z_OK || s->outbuf == Z_NULL) {
  	    return destroy(s), (gzFile)Z_NULL;
  	}
      } else {
  	err = inflateInit2(&(s->stream), -WBITS);
  	s->stream.next_in  = s->inbuf = ALLOC(Z_BUFSIZE);
  
  	if (err != Z_OK || s->inbuf == Z_NULL) {
  	    return destroy(s), (gzFile)Z_NULL;
  	}
      }
      s->stream.avail_out = Z_BUFSIZE;
  
      errno = 0;
      s->file = fd < 0 ? fopen(path, mode) : fdopen(fd, mode);
  
      if (s->file == NULL) {
  	return destroy(s), (gzFile)Z_NULL;
      }
      if (s->mode == 'w') {
  	/* Write a very simple .gz header:
  	 */
  	fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", GZ_MAGIC_1, GZ_MAGIC_2,
  	       DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
      } else {
  	/* Check and skip the header:
           */
  	Byte c1 = 0, c2 = 0;
  	Byte method = 0;
  	Byte flags = 0;
  	Byte xflags = 0;
  	Byte time[4];
  	Byte osCode;
  	int c;
  
  	s->stream.avail_in = fread(s->inbuf, 1, 2, s->file);
  	if (s->stream.avail_in != 2 || s->inbuf[0] != GZ_MAGIC_1
  	    || s->inbuf[1] != GZ_MAGIC_2) {
  	    s->transparent = 1;
  	    return (gzFile)s;
  	}
  	s->stream.avail_in = 0;
  	err = fscanf(s->file,"%c%c%4c%c%c", &method, &flags, time, &xflags, &osCode);
  
  	if (method != DEFLATED || feof(s->file) || (flags & RESERVED) != 0) {
  	    s->z_err = Z_DATA_ERROR;
  	    return (gzFile)s;
  	}
  	if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
  	    long len;
  	    err = fscanf(s->file, "%c%c", &c1, &c2);
  	    len = c1 + ((long)c2<<8);
  	    fseek(s->file, len, SEEK_CUR);
  	}
  	if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
  	    while ((c = getc(s->file)) != 0 && c != EOF) ;
  	}
  	if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
  	    while ((c = getc(s->file)) != 0 && c != EOF) ;
  	}
  	if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
  	    err = fscanf(s->file, "%c%c", &c1, &c2);
  	}
  	if (feof(s->file)) {
  	    s->z_err = Z_DATA_ERROR;
  	}
      }
      return (gzFile)s;
  }
  
  local gzFile gzopen (path, mode)
      char *path;
      char *mode;
  {
      return gz_open (path, mode, -1);
  }
  
  local int gzwrite (file, buf, len)
      gzFile file;
      voidp buf;
      unsigned len;
  {
      gz_stream *s = (gz_stream*)file;
  
      if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
  
      s->stream.next_in = buf;
      s->stream.avail_in = len;
  
      while (s->stream.avail_in != 0) {
  
  	if (s->stream.avail_out == 0) {
  
  	    s->stream.next_out = s->outbuf;
  	    if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
  		s->z_err = Z_ERRNO;
  		break;
  	    }
  	    s->stream.avail_out = Z_BUFSIZE;
  	}
  	s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
  
  	if (s->z_err != Z_OK) break;
      }
      s->crc = crc32(s->crc, buf, len);
  
      return len - s->stream.avail_in;
  }
  
  local int gzclose (file)
      gzFile file;
  {
      uInt n;
      int err;
      gz_stream *s = (gz_stream*)file;
  
      if (s == NULL) return Z_STREAM_ERROR;
  
      if (s->mode == 'w') {
  	err = gzflush (file, Z_FINISH);
  	if (err != Z_OK) return destroy((gz_stream *)file);
  
  	putLong (s->file, s->crc);
  	putLong (s->file, s->stream.total_in);
  
      } else if (s->mode == 'r' && s->z_err == Z_STREAM_END) {
  
  	/* slide CRC and original size if they are at the end of inbuf */
  	if ((n = s->stream.avail_in) < 8  && !s->z_eof) {
  	    Byte *p = s->inbuf;
  	    Byte *q = s->stream.next_in;
  	    while (n--) { *p++ = *q++; };
  
  	    n = s->stream.avail_in;
  	    n += fread(p, 1, 8, s->file);
  	    s->stream.next_in = s->inbuf;
  	}
  	/* check CRC and original size */
  	if (n < 8 ||
  	    getLong(s->stream.next_in) != s->crc ||
  	    getLong(s->stream.next_in + 4) != s->stream.total_out) {
  
  	    s->z_err = Z_DATA_ERROR;
  	}
      }
      return destroy((gz_stream *)file);
  }
  
  /* ========================================================================= */
  
  static int wm_tool_printf(const char *format, ...)
  {
      int ret;
      va_list ap;
  
  	va_start(ap, format);
  	ret = vprintf(format, ap);
  	va_end(ap);
  
  	fflush(stdout);
  
  	return ret;
  }
  
  static unsigned long long wm_tool_crc32_reflect(unsigned long long ref, unsigned char ch)
  {
  	int i;
  	unsigned long long value = 0;
  
  	for ( i = 1; i < ( ch + 1 ); i++ )
  	{
  		if( ref & 1 )
  			value |= 1 << ( ch - i );
  		ref >>= 1;
  	}
  
  	return value;
  }
  
  static unsigned int wm_tool_crc32(unsigned int crc, unsigned char *buffer, int size, wm_tool_crc32_reflect_e mode)
  {
  	int i;
  	unsigned char temp;
  
  	for (i = 0; i < size; i++)
  	{
  		if (mode & WM_TOOL_CRC32_REFLECT_INPUT)
  		{
  			temp = wm_tool_crc32_reflect(buffer[i], 8);
  		}
  		else
  		{
  			temp = buffer[i];
  		}
  		crc = wm_tool_crc32_tab[(crc ^ temp) & 0xff] ^ (crc >> 8);
  	}
  
  	return crc ;
  }
  
  static unsigned int wm_tool_get_crc32(unsigned char *buffer, int size, wm_tool_crc32_reflect_e mode)
  {
  	wm_tool_file_crc = wm_tool_crc32(wm_tool_file_crc, buffer, size, mode);
  	if (mode & WM_TOOL_CRC32_REFLECT_OUTPUT)
  	{
  		wm_tool_file_crc = wm_tool_crc32_reflect(wm_tool_file_crc, 32);
  	}
  	return wm_tool_file_crc;
  }
  
  static unsigned short wm_tool_get_crc16(unsigned char *ptr, unsigned short count)
  {
  	unsigned short crc, i;
  
  	crc = 0;
  
  	while (count--)
  	{
  		crc = crc ^ (int) *ptr++ << 8;
  
  		for (i = 0; i < 8; i++)
  		{
  			if (crc & 0x8000)
  			    crc = crc << 1 ^ 0x1021;
  			else
  			    crc = crc << 1;
  		}
  	}
  
  	return (crc & 0xFFFF);
  }
  
  static int wm_tool_char_to_hex(char ch)
  {
  	int hex;
  
  	hex = -1;
  
  	if ((ch >= '0') && (ch <= '9'))
  	{
  	    hex = ch - '0';
  	}
  	else if ((ch >= 'a') && (ch <= 'f'))
  	{
  	    hex = ch - 'a' + 0xa;
  	}
  	else if ((ch >= 'A') && (ch <= 'F'))
  	{
  	    hex = ch - 'A' + 0xa;
  	}
  
  	return hex;
  }
  
  static int wm_tool_str_to_hex_array(char *str, int cnt, unsigned char array[])
  {
  	int hex;
  	unsigned char tmp;
  	unsigned char *des;
  
  	des = array;
  
  	while (cnt-- > 0)
  	{
  		hex = wm_tool_char_to_hex(*str++);
  
  		if (hex < 0)
  		{
  		    return -1;
  		}
  		else
  		{
  		    tmp = (hex << 4) & 0xf0;
  		}
  
  		hex = wm_tool_char_to_hex(*str++);
  
  		if (hex < 0)
  		{
  		    return -1;
  		}
  		else
  		{
  		    tmp = tmp | (hex & 0x0f);
  		}
  
  		*des++ = (unsigned char) tmp;
  	}
  
  	return ((*str == 0) ? 0 : -1);
  }
  
  static char *wm_tool_strcasestr(const char *str1, const char *str2)
  {
    char *cp = (char *) str1;
    char *s1, *s2;
  
    if (!*str2) return (char *) str1;
  
    while (*cp) {
      s1 = cp;
      s2 = (char *) str2;
  
      while (*s1 && *s2 && !(tolower((int)*s1) - tolower((int)*s2))) s1++, s2++;
      if (!*s2) return cp;
      cp++;
    }
  
    return NULL;
  }
  
  static int wm_tool_get_file_size(const char* filename)
  {
      FILE *fp = fopen(filename, "r");
      if(!fp) return -1;
      fseek(fp,0L,SEEK_END);
      int size = ftell(fp);
      fclose(fp);
      return size;
  }
  
  static char *wm_tool_get_name(const char *name)
  {
      static char sz_name[WM_TOOL_PATH_MAX] = {0};
      char *p = (char *)name;
      char *q = (char *)name;
  
      do
      {
  #ifdef __MINGW32__
          p = strchr(p, '\\');
  #else
          p = strchr(p, '/');
  #endif
          if (p)
          {
              p++;
              q = p;
          }
      } while (p);
  
      strncpy(sz_name, q, WM_TOOL_PATH_MAX - 1);
  
  #ifdef __MINGW32__
      p = wm_tool_strcasestr(sz_name, ".exe");
      if (p)
          *p = '\0';
  #endif
  
      return sz_name;
  }
  
  static void wm_tool_print_usage(const char *name)
  {
      wm_tool_printf("Usage: %s [-h] [-v] [-b] [-o] [-sb] [-ct] [-it]"
                     " [-ua] [-ra] [-ih] [-nh] [-un] [-df] [-vs]"
                     " [-l] [-c] [-ws] [-ds] [-rs] [-eo] [-dl] [-sl]"
                     "\r\n"
                     "\r\n"
                     "WinnerMicro firmware packaging "
                     "and programming "
                     "tool\r\n\r\n"
                     "options:\r\n\r\n"
                     "  -h                    , show usage\r\n"
                     "  -v                    , show version\r\n"
                     "\r\n"
                     "  -b  binary            , original binary file\r\n"
                     "  -o  output_name       , output firmware file\r\n"
                     "                          the default is the same as the original binary file name\r\n"
                     "  -sb second_boot       , second boot file, used to generate fls file\r\n"
                     "  -fc compress_type     , whether the firmware is compressed, default is compressed\r\n"
                     "                          <0 | 1> or <uncompress | compress>\r\n"
                     "  -it image_type        , firmware image layout type, default is 0\r\n"
                     "                          <0 | 1>\r\n"
                     "  -ua update_address    , upgrade storage location (hexadecimal)\r\n"
                     "                          the default is 8090000\r\n"
                     "  -ra run_address       , runtime position (hexadecimal)\r\n"
                     "                          the default is 8002400\r\n"
                     "  -ih image_header      , image header storage location (hexadecimal)\r\n"
                     "                          the default is 8002000\r\n"
                     "  -nh next_image_header , next image header storage location (hexadecimal)\r\n"
                     "                          the default is 0\r\n"
                     "  -un upd_no            , upd no version number (hexadecimal)\r\n"
                     "                          the default is 0\r\n"
                     "  -df                   , generate debug firmware for openocd\r\n"
                     "  -vs version_string    , firmware version string, cannot exceed 16 bytes\r\n"
                     "\r\n"
                     "  -l                    , list the local serial port\r\n"
                     "  -c  serial_name       , connect a serial port\r\n"
  #if defined(__APPLE__) && defined(__MACH__)
                     "                          e.g: tty.usbserial0 tty.usbserial3 tty.usbserial7\r\n"
  #elif defined(__MINGW32__) || defined(__CYGWIN__)
                     "                          e.g: COM0 COM3 COM7\r\n"
  #elif defined(__linux__)
                     "                          e.g: ttyUSB0 ttyUSB3 ttyUSB7\r\n"
  #endif
                     "  -ws baud_rate         , set the serial port speed during normal work, default is 115200\r\n"
                     "                          <1200 - 2000000> or <1M | 2M>\r\n"
                     "  -ds baud_rate         , set the serial port speed when downloading, default is 115200\r\n"
                     "                          <115200 | 460800 | 921600 | 1000000 | 2000000> or <1M | 2M>\r\n"
                     "  -rs reset_action      , set device reset method, default is manual control\r\n"
                     "                          <none | at | rts>\r\n"
                     "                           none - manual control device reset\r\n"
                     "                           at   - use the at command to control the device reset\r\n"
                     "                           rts  - use the serial port rts pin to control the device reset\r\n"
                     "  -eo erase_option      , firmware area erase option\r\n"
                     "                          <all>\r\n"
                     "                           all  - erase all areas\r\n"
                     "  -dl download_firmware , firmware file to be downloaded, default download compressed image\r\n"
                     "  -sl display_format    , display the log information output from the serial port\r\n"
                     "                          <0 | 1> or <str | hex>\r\n"
                     "                           str - string mode display\r\n"
                     "                           hex - hexadecimal format\r\n"
                     , wm_tool_get_name(name));
  
      return;
  }
  
  static void wm_tool_print_version(const char *name)
  {
      wm_tool_printf("%s %s for W806\r\nCopyright (C) 2013 - 2020 WinnerMicro, Inc.\r\n", wm_tool_get_name(name), wm_tool_version);
  
      return;
  }
  
  static int wm_tool_parse_arv(int argc, char *argv[])
  {
      int opt;
      int option_index = 0;
      char *opt_string = "hvlc:b:o:";
      int cnt = 0;
  
      opterr = 1;/* show err info */
  
      struct option long_options[] = {
         {"dl", required_argument, NULL, 'd'},
         {"ws", required_argument, NULL, 'w'},
         {"ds", required_argument, NULL, 's'},
         {"sb", required_argument, NULL, 'S'},
         {"it", required_argument, NULL, 'i'},
         {"fc", required_argument, NULL, 'C'},
         {"ua", required_argument, NULL, 'u'},
         {"ra", required_argument, NULL, 'r'},
         {"ih", required_argument, NULL, 'H'},
         {"nh", required_argument, NULL, 'n'},
         {"un", required_argument, NULL, 'U'},
         {"df", no_argument,       NULL, 'D'},
         {"vs", required_argument, NULL, 'V'},
         {"rs", required_argument, NULL, 'a'},
         {"eo", required_argument, NULL, 'e'},
         {"sl", required_argument, NULL, 'g'},
         {0,    0,                 NULL, 0}
      };
  
      while ( (opt = getopt_long_only(argc, argv, opt_string, long_options, &option_index)) != -1)
      {
          WM_TOOL_DBG_PRINT("%c-%s\r\n", opt, optarg);
  
          switch (opt)
          {
              case '?':
              case 'h':
              {
                  wm_tool_show_usage = 1;
                  break;
              }
              case 'v':
              {
                  wm_tool_show_ver = 1;
                  break;
              }
              case 'l':
              {
                  wm_tool_list_com = 1;
                  break;
              }
              case 'c':
              {
  #if defined(__MINGW32__)
                  strcpy(wm_tool_serial_path, optarg);
  #elif defined(__CYGWIN__)
                  sprintf(wm_tool_serial_path, "/dev/ttyS%d", atoi(optarg + strlen("COM")) - 1);
  #else
                  sprintf(wm_tool_serial_path, "/dev/%s", optarg);
  #endif
                  break;
              }
              case 'w':
              {
                  if ('M' == optarg[1])
                      wm_tool_normal_serial_rate = (optarg[0] - 0x30) * 1000000;
                  else
                      wm_tool_normal_serial_rate = strtol(optarg, NULL, 10);
                  break;
              }
              case 's':
              {
                  if ('M' == optarg[1])
                      wm_tool_download_serial_rate = (optarg[0] - 0x30) * 1000000;
                  else
                      wm_tool_download_serial_rate = strtol(optarg, NULL, 10);
                  break;
              }
              case 'a':
              {
                  if (0 == strncmp(optarg, "none", strlen("none")))
                      wm_tool_dl_action = WM_TOOL_DL_ACTION_NONE;
                  else if (0 == strncmp(optarg, "at", strlen("at")))
                      wm_tool_dl_action = WM_TOOL_DL_ACTION_AT;
                  else if (0 == strncmp(optarg, "rts", strlen("rts")))
                      wm_tool_dl_action = WM_TOOL_DL_ACTION_RTS;
                  else
                      wm_tool_show_usage = 1;
                  break;
              }
              case 'e':
              {
                  if (0 == strncmp(optarg, "all", strlen("all")))
                      wm_tool_dl_erase = WM_TOOL_DL_ERASE_ALL;
                  else
                      wm_tool_show_usage = 1;
                  break;
              }
              case 'd':
              {
                  wm_tool_download_image = strdup(optarg);
                  if (wm_tool_strcasestr(wm_tool_download_image, ".fls"))
                      wm_tool_dl_type = WM_TOOL_DL_TYPE_FLS;
                  break;
              }
              case 'o':
              {
                  wm_tool_output_image = strdup(optarg);
                  break;
              }
              case 'b':
              {
                  wm_tool_input_binary = strdup(optarg);
                  break;
              }
              case 'S':
              {
                  wm_tool_secboot_image = strdup(optarg);
                  break;
              }
              case 'i':
              {
  #if 0
                  if ('0' == optarg[0])
                      wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_1M;
                  else if ('3' == optarg[0])
                      wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_2M;
                  else if (0 == strncmp(optarg, "1M", strlen("1M")))
                      wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_1M;
                  else if (0 == strncmp(optarg, "2M", strlen("2M")))
                      wm_tool_image_type = WM_TOOL_LAYOUT_TYPE_2M;
                  else
  #endif
                  {
                      if (isdigit((int)optarg[0]))
                          wm_tool_image_type = atoi(optarg);//optarg[0] - 0x30;
                      else
                          wm_tool_show_usage = 1;
                  }
                  break;
              }
              case 'C':
              {
                  if ('0' == optarg[0])
                      wm_tool_zip_type = WM_TOOL_ZIP_TYPE_UNCOMPRESS;
                  else if ('1' == optarg[0])
                      wm_tool_zip_type = WM_TOOL_ZIP_TYPE_COMPRESS;
                  else if (0 == strncmp(optarg, "compress", strlen("compress")))
                      wm_tool_zip_type = WM_TOOL_ZIP_TYPE_COMPRESS;
                  else if (0 == strncmp(optarg, "uncompress", strlen("uncompress")))
                      wm_tool_zip_type = WM_TOOL_ZIP_TYPE_UNCOMPRESS;
                  else
                      wm_tool_show_usage = 1;
                  break;
              }
              case 'u':
              {
                  wm_tool_upd_addr = strtol(optarg, NULL, 16);
                  break;
              }
              case 'r':
              {
                  wm_tool_run_addr = strtol(optarg, NULL, 16);
                  break;
              }
              case 'D':
              {
                  wm_tool_is_debug = 1;
                  break;
              }
              case 'V':
              {
                  strncpy(wm_tool_image_version, optarg, WM_TOOL_IMAGE_VERSION_LEN);
                  wm_tool_image_version[WM_TOOL_IMAGE_VERSION_LEN - 1] = '\0';
                  break;
              }
              case 'g':
              {
                  if ('0' == optarg[0])
                      wm_tool_show_log_type = WM_TOOL_SHOW_LOG_STR;
                  else if ('1' == optarg[0])
                      wm_tool_show_log_type = WM_TOOL_SHOW_LOG_HEX;
                  else if (0 == strncmp(optarg, "str", strlen("str")))
                      wm_tool_show_log_type = WM_TOOL_SHOW_LOG_STR;
                  else if (0 == strncmp(optarg, "hex", strlen("hex")))
                      wm_tool_show_log_type = WM_TOOL_SHOW_LOG_HEX;
                  else
                      wm_tool_show_usage = 1;
                  break;
                  break;
              }
              case 'H':
              {
                  wm_tool_image_header = strtol(optarg, NULL, 16);
                  break;
              }
              case 'n':
              {
                  wm_tool_next_image_header = strtol(optarg, NULL, 16);
                  break;
              }
              case 'U':
              {
                  wm_tool_image_upd_no = strtol(optarg, NULL, 16);
                  break;
              }
              default:
              {
                  wm_tool_show_usage = 1;
                  break;
              }
          }
  
          cnt++;
      }
  
      return cnt;
  }
  
  static int wm_tool_pack_image(const char *outfile)
  {
      FILE *fpbin = NULL;
  	FILE *fpimg = NULL;
  	int readlen = 0;
  	int filelen = 0;
  	int patch = 0;
  	wm_tool_firmware_booter_t fbooter;
  	unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1];
  
      fpbin = fopen(wm_tool_input_binary, "rb");
  	if (NULL == fpbin)
  	{
  		wm_tool_printf("can not open input file [%s].\r\n", wm_tool_input_binary);
  		return -2;
  	}
  
  	fpimg = fopen(outfile, "wb+");
  	if (NULL == fpimg)
  	{
  		wm_tool_printf("open img file error: [%s].\r\n", outfile);
  		fclose(fpbin);
  		return -3;
  	}
  
      /* --------deal with upgrade image's CRC begin---- */
      wm_tool_file_crc = 0xFFFFFFFF;
  	while (!feof(fpbin))
  	{
  		memset(buf, 0, sizeof(buf));
  		readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin);
  		if(readlen % 4 != 0)
  		{
  			patch = 4 - readlen%4;
  			readlen += patch;
  		}
  		filelen += readlen;
  		wm_tool_get_crc32((unsigned char*)buf, readlen, 0);
  	}
  	/* --------deal with upgrade image's CRC end---- */
  
  	wm_tool_src_binary_len = filelen;
  	wm_tool_src_binary_crc = wm_tool_file_crc;
  
      memset(&fbooter, 0, sizeof(wm_tool_firmware_booter_t));
      strcpy((char *)fbooter.ver, wm_tool_image_version);
  	fbooter.magic_no = WM_TOOL_IMG_HEAD_MAGIC_NO;
  
      fbooter.run_org_checksum = wm_tool_file_crc;
  	fbooter.img_type         = wm_tool_image_type;
  	fbooter.run_img_len      = filelen;
  	fbooter.run_img_addr     = wm_tool_run_addr;
  	fbooter.zip_type         = WM_TOOL_ZIP_TYPE_UNCOMPRESS;
      fbooter.img_header_addr  = wm_tool_image_header;
      fbooter.upgrade_img_addr = wm_tool_upd_addr;
      fbooter.upd_no           = wm_tool_image_upd_no;
      fbooter.next_boot        = wm_tool_next_image_header;
  
      /* calculate image's header's CRC */
  	wm_tool_file_crc = 0xFFFFFFFF;
  	wm_tool_get_crc32((unsigned char *)&fbooter, sizeof(wm_tool_firmware_booter_t) - 4, 0);
      fbooter.hd_checksum = wm_tool_file_crc;
  
      /* write image's header to output file */
  	fwrite(&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fpimg);
  
  	/* write image to output file */
  	fseek(fpbin, 0, SEEK_SET);
  	while (!feof(fpbin))
  	{
  		readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin);
  		fwrite(buf, 1, readlen, fpimg);
  	}
  
  	/* write dummy data to pad 4byte-aligned */
  	if (patch > 0)
  	{
  		memset(buf, 0, patch);
  		fwrite(buf, 1, patch, fpimg);
  	}
  
  	if (fpbin)
  	{
  		fclose(fpbin);
  	}
  
  	if (fpimg)
  	{
  		fclose(fpimg);
  	}
  
      wm_tool_printf("generate normal image completed.\r\n");
  
      return 0;
  }
  
  static int wm_tool_pack_gz_image(const char *gzbin, const char *outfile)
  {
      FILE *fpbin = NULL;
  	FILE *fpimg = NULL;
  	int readlen = 0;
  	int filelen = 0;
  	int patch = 0;
  	wm_tool_firmware_booter_t fbooter;
  	unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1];
  
      fpbin = fopen(gzbin, "rb");
  	if (NULL == fpbin)
  	{
  		wm_tool_printf("can not open input file [%s].\r\n", gzbin);
  		return -2;
  	}
  
  	fpimg = fopen(outfile, "wb+");
  	if (NULL == fpimg)
  	{
  		wm_tool_printf("create img file error: [%s].\r\n", outfile);
  		fclose(fpbin);
  		return -3;
  	}
  
      /* --------deal with upgrade image's CRC begin---- */
      wm_tool_file_crc = 0xFFFFFFFF;
  	while (!feof(fpbin))
  	{
  		memset(buf, 0, sizeof(buf));
  		readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin);
  		if(readlen % 4 != 0)
  		{
  			patch = 4 - readlen%4;
  			readlen += patch;
  		}
  		filelen += readlen;
  		wm_tool_get_crc32((unsigned char*)buf, readlen, 0);
  	}
  	/* --------deal with upgrade image's CRC end---- */
  
      memset(&fbooter, 0, sizeof(wm_tool_firmware_booter_t));
      strcpy((char *)fbooter.ver, wm_tool_image_version);
  	fbooter.magic_no = WM_TOOL_IMG_HEAD_MAGIC_NO;
  
      fbooter.run_org_checksum = wm_tool_file_crc;
  	fbooter.img_type         = wm_tool_image_type;
  	fbooter.run_img_len      = filelen;
  	fbooter.run_img_addr     = wm_tool_run_addr;
  	fbooter.zip_type         = wm_tool_zip_type;
  	fbooter.img_header_addr  = wm_tool_image_header;
      fbooter.upgrade_img_addr = wm_tool_upd_addr;
      fbooter.upd_no           = wm_tool_image_upd_no;
      fbooter.next_boot        = wm_tool_next_image_header;
  
      /* calculate image's header's CRC */
  	wm_tool_file_crc = 0xFFFFFFFF;
  	wm_tool_get_crc32((unsigned char *)&fbooter, sizeof(wm_tool_firmware_booter_t) - 4, 0);
      fbooter.hd_checksum = wm_tool_file_crc;
  
      /* write image's header to output file */
  	fwrite(&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fpimg);
  
  	/* write image to output file */
  	fseek(fpbin, 0, SEEK_SET);
  	while (!feof(fpbin))
  	{
  		readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpbin);
  		fwrite(buf, 1, readlen, fpimg);
  	}
  
  	/* write dummy data to pad 4byte-aligned */
  	if (patch > 0)
  	{
  		memset(buf, 0, patch);
  		fwrite(buf, 1, patch, fpimg);
  	}
  
  	if (fpbin)
  	{
  		fclose(fpbin);
  	}
  
  	if (fpimg)
  	{
  		fclose(fpimg);
  	}
  
      wm_tool_printf("generate compressed image completed.\r\n");
  
      return 0;
  }
  
  static int wm_tool_pack_dbg_image(const char *image, const char *outfile)
  {
      FILE *fpimg = NULL;
  	FILE *fout = NULL;
  	unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1];
  	int readlen = 0;
  	int i;
  	wm_tool_firmware_booter_t fbooter;
  	int appimg_len = 0;
  	int final_len = 0;
  	int magic_word = 0;
  
  	fpimg = fopen(image, "rb");
  	if (NULL == fpimg)
  	{
  		wm_tool_printf("open img file error: [%s].\r\n", image);
  		return -4;
  	}
  
  	magic_word = 0;
  	readlen = fread(&magic_word, 1, 4, fpimg);
  	if (magic_word != WM_TOOL_IMG_HEAD_MAGIC_NO)
  	{
  		wm_tool_printf("input [%s] file magic error.\n", image);
  		fclose(fpimg);
  		return -5;
  	}
  
  	fout = fopen(outfile, "wb+");
  	if (NULL == fout)
  	{
  		wm_tool_printf("create img file error [%s].\r\n", outfile);
  		fclose(fpimg);
  		return -6;
  	}
  
  	appimg_len = wm_tool_get_file_size(image);
  
  	/* write 0xFF to output file */
  	final_len = WM_TOOL_RUN_IMG_HEADER_LEN + (appimg_len - sizeof(wm_tool_firmware_booter_t));
  	for (i = 0; i < (final_len /WM_TOOL_ONCE_READ_LEN); i++)
  	{
  		memset(buf, 0xff, WM_TOOL_ONCE_READ_LEN);
  		fwrite(buf, 1, WM_TOOL_ONCE_READ_LEN, fout);
  	}
  	memset(buf, 0xff, final_len % WM_TOOL_ONCE_READ_LEN);
  	fwrite(buf, 1, final_len % WM_TOOL_ONCE_READ_LEN, fout);
  
  	memset(&fbooter, 0, sizeof(wm_tool_firmware_booter_t));
  
  	/* write sec img to output file */
  	fseek(fpimg, 0, SEEK_SET);
  	readlen = fread((unsigned char *)&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fpimg);
  
  	fseek(fout, 0, SEEK_SET);
  	fwrite(&fbooter, 1, sizeof(wm_tool_firmware_booter_t), fout);
  	fseek(fout, WM_TOOL_RUN_IMG_HEADER_LEN, SEEK_SET);
  
  	while (!feof(fpimg))
  	{
  		readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpimg);
  		fwrite(buf, 1, readlen, fout);
  	}
  
  	if (fpimg)
  	{
  		fclose(fpimg);
  	}
  
  	if (fout)
  	{
  		fclose(fout);
  	}
  
      wm_tool_printf("generate debug image completed.\r\n");
  
      return 0;
  }
  
  static int wm_tool_pack_fls(const char *image, const char *outfile)
  {
  	FILE *fpsec = NULL;
  	FILE *fpimg = NULL;
  	FILE *fout = NULL;
  	unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1];
  	int readlen = 0;
  	int magic_word = 0;
  
  	fpsec = fopen(wm_tool_secboot_image,"rb");
  	if (NULL == fpsec)
  	{
  		wm_tool_printf("can not open input file [%s].\r\n", wm_tool_secboot_image);
  		return -2;
  	}
  
  	magic_word = 0;
  	readlen = fread(&magic_word, 1, 4, fpsec);
  	if (magic_word != WM_TOOL_IMG_HEAD_MAGIC_NO)
  	{
  		wm_tool_printf("input [%s] file magic error.\r\n", wm_tool_secboot_image);
  		fclose(fpsec);
  		return -3;
  	}
  
  	fpimg = fopen(image, "rb");
  	if (NULL == fpimg)
  	{
  		wm_tool_printf("open img file error [%s].\r\n", image);
  		fclose(fpsec);
  		return -4;
  	}
  
  	magic_word = 0;
  	readlen = fread(&magic_word, 1, 4, fpimg);
  	if (magic_word != WM_TOOL_IMG_HEAD_MAGIC_NO)
  	{
  		wm_tool_printf("input [%s] file magic error.\r\n", image);
  		fclose(fpsec);
  		fclose(fpimg);
  		return -5;
  	}
  
  	fout = fopen(outfile, "wb+");
  	if (NULL == fout)
  	{
  		wm_tool_printf("create img file error [%s].\r\n", outfile);
  		fclose(fpsec);
  		fclose(fpimg);
  		return -6;
  	}
  
  	fseek(fpsec, 0, SEEK_SET);
  
      while (!feof(fpsec))
  	{
  		readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpsec);
  		fwrite(buf, 1, readlen, fout);
  	}
  
      fseek(fpimg, 0, SEEK_SET);
  
      while (!feof(fpimg))
  	{
  		readlen = fread(buf, 1, WM_TOOL_ONCE_READ_LEN, fpimg);
  		fwrite(buf, 1, readlen, fout);
  	}
  
  	if (fpsec)
  	{
  		fclose(fpsec);
  	}
  
  	if (fpimg)
  	{
  		fclose(fpimg);
  	}
  
  	if (fout)
  	{
  		fclose(fout);
  	}
  
      wm_tool_printf("generate flash file completed.\r\n");
  
  	return 0;
  }
  
  static int wm_tool_gzip_bin(const char *binary, const char *gzbin)
  {
      FILE *bfp;
      gzFile gzfp;
      int ret;
      char buf[1024];
  
      bfp = fopen(binary, "rb");
      gzfp = gzopen((char *)gzbin, "wb+");
  
      if (!bfp || !gzfp)
      {
          wm_tool_printf("can not gzip binary.\r\n");
          return -1;
      }
  
      do {
          ret = fread(buf, 1, sizeof(buf), bfp);
          if (ret > 0)
          {
              ret = gzwrite(gzfp, (voidp)buf, ret);
              if (ret <= 0)
              {
                  wm_tool_printf("can not write gzip binary.\r\n");
                  return -2;
              }
          }
      } while (ret > 0);
  
      gzclose(gzfp);
      fclose(bfp);
  
      wm_tool_printf("compress binary completed.\r\n");
  
      return 0;
  }
  
  static int wm_tool_pack_firmware(void)
  {
      int ret;
      char *path;
      char *name;
      char *image;
      char *gzbin;
      char *gzimg;
      char *dbgimg;
      char *fls;
  
      if (!wm_tool_input_binary)
          return 1;
  
      if (!wm_tool_output_image)
      {
          char *r = wm_tool_input_binary;
          char *t = wm_tool_input_binary;
          do
          {
              r = strchr(r, '.');
              if (r)
              {
                  t = r;
                  r++;
              }
          } while (r);
          wm_tool_output_image = malloc(t - wm_tool_input_binary + 1);
          if (!wm_tool_output_image)
              return -1;
          memcpy(wm_tool_output_image, wm_tool_input_binary, t - wm_tool_input_binary);
          wm_tool_output_image[t - wm_tool_input_binary] = '\0';
      }
  
      char *p = wm_tool_output_image;
      char *q = wm_tool_output_image;
  
      do
      {
          p = strchr(p, '/');
          if (p)
          {
              p++;
              q = p;
          }
      } while (p);
  
      /* output file */
      name = strdup(q);
      *q = '\0';
      path = strdup(wm_tool_output_image);
  
      image = malloc(strlen(path) + strlen(name) + strlen(".img") + 1);
      if (!image)
          return -1;
      sprintf(image, "%s%s.img", path, name);
  	if (WM_TOOL_ZIP_TYPE_UNCOMPRESS == wm_tool_zip_type)
  	{
          ret = wm_tool_pack_image(image);
          if (ret)
              return ret;
  	}
  
      if (WM_TOOL_ZIP_TYPE_COMPRESS == wm_tool_zip_type)
      {
          gzbin = malloc(strlen(path) + strlen(name) + strlen("_gz.bin") + 1);
          gzimg = malloc(strlen(path) + strlen(name) + strlen("_gz.img") + 1);
          if (!gzbin || !gzimg)
              return -1;
          sprintf(gzbin, "%s%s.bin.gz", path, name);
          sprintf(gzimg, "%s%s_gz.img", path, name);
  
          ret = wm_tool_gzip_bin(wm_tool_input_binary, gzbin);
          if (ret)
              return ret;
  
          ret = wm_tool_pack_gz_image(gzbin, gzimg);
          free(gzbin);
          free(gzimg);
          if (ret)
              return ret;
      }
  
      if (wm_tool_is_debug)
      {
          dbgimg = malloc(strlen(path) + strlen(name) + strlen("_dbg.img") + 1);
          if (!dbgimg)
              return -1;
          sprintf(dbgimg, "%s%s_dbg.img", path, name);
  
          ret = wm_tool_pack_dbg_image(image, dbgimg);
          free(dbgimg);
          if (ret)
              return ret;
      }
  
      if (wm_tool_secboot_image)
      {
          fls = malloc(strlen(path) + strlen(name) + strlen(".fls") + 1);
          if (!fls)
              return -1;
          sprintf(fls, "%s%s.fls", path, name);
  
          ret = wm_tool_pack_fls(image, fls);
  
          free(fls);
      }
  
      free(image);
  
      return ret;
  }
  
  #ifdef __MINGW32__
  static BOOL wm_tool_signal_proc(DWORD no)
  {
  	switch (no)
  	{
      	case CTRL_C_EVENT:
      		wm_tool_signal_proc_entry();
      		return(FALSE);
      	default:
      		return FALSE;
  	}
  }
  
  static void wm_tool_signal_init(void)
  {
      SetConsoleCtrlHandler((PHANDLER_ROUTINE)wm_tool_signal_proc, TRUE);
  }
  
  static void wm_tool_delay_ms(int ms)
  {
      Sleep(ms);
  
      return;
  }
  
  static int wm_tool_uart_set_rts(int boolflag)
  {
      return EscapeCommFunction(wm_tool_uart_handle,((boolflag) ? SETRTS : CLRRTS)) ? 0 : -1;
  }
  
  static int wm_tool_uart_set_dtr(int boolflag)
  {
      return EscapeCommFunction(wm_tool_uart_handle,((boolflag) ? SETDTR : CLRDTR)) ? 0 : -1;
  }
  
  static int wm_tool_uart_set_timeout(void)
  {
      BOOL ret;
      COMMTIMEOUTS timeout;
  
  	timeout.ReadIntervalTimeout         = MAXDWORD;
  	timeout.ReadTotalTimeoutConstant    = 0;
  	timeout.ReadTotalTimeoutMultiplier  = 0;
  	timeout.WriteTotalTimeoutConstant   = 0;
  	timeout.WriteTotalTimeoutMultiplier = 0;
  
  	ret = SetCommTimeouts(wm_tool_uart_handle, &timeout);
  	if (ret)
  	    ret = SetCommMask(wm_tool_uart_handle, EV_TXEMPTY);
  
      return ret ? 0 : -1;
  }
  
  static void wm_tool_uart_set_block(int bolck)
  {
      if (bolck)
          wm_tool_uart_block = INFINITE;
      else
          wm_tool_uart_block = 0;
  
      return;
  }
  
  static int wm_tool_uart_set_speed(int speed)
  {
      int i;
      int size;
  	DCB cfg;
  
  	size = sizeof(wm_tool_uart_speed_array) / sizeof(int);
  
      for (i= 0; i < size; i++)
      {
          if (speed == wm_tool_uart_name_array[i])
          {
              if (GetCommState(wm_tool_uart_handle, &cfg))
              {
              	cfg.DCBlength		= sizeof(DCB);
              	cfg.BaudRate		= wm_tool_uart_speed_array[i];
              	cfg.fBinary		    = TRUE;
              	cfg.fParity         = FALSE;
              	cfg.fDtrControl	    = DTR_CONTROL_DISABLE;
              	cfg.fDsrSensitivity = FALSE;
              	cfg.fRtsControl	    = RTS_CONTROL_DISABLE;
              	cfg.ByteSize		= 8;
              	cfg.StopBits 		= ONESTOPBIT;
              	cfg.fAbortOnError	= FALSE;
              	cfg.fOutX			= FALSE;
              	cfg.fInX			= FALSE;
              	cfg.fErrorChar      = FALSE;
              	cfg.fNull           = FALSE;
              	cfg.fOutxCtsFlow    = FALSE;
  	            cfg.fOutxDsrFlow    = FALSE;
  	            cfg.Parity          = NOPARITY;
  	            cfg.fTXContinueOnXoff = FALSE;
  
              	return SetCommState(wm_tool_uart_handle, &cfg) ? 0 : -1;
          	}
          	else
          	{
                  return -3;
          	}
          }
      }
  
  	return -2;
  }
  
  static void wm_tool_uart_clear(void)
  {
      PurgeComm(wm_tool_uart_handle, PURGE_RXCLEAR);
  
      return;
  }
  
  static int wm_tool_uart_open(const char *device)
  {
      BOOL ret;
      char name[40];
  
  	sprintf(name,"\\\\.\\%s", device);
  
  	wm_tool_uart_handle = CreateFile(name, GENERIC_WRITE | GENERIC_READ,
                                       0, NULL, OPEN_EXISTING,
                                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                                       NULL);
  
  	if (wm_tool_uart_handle == INVALID_HANDLE_VALUE)
  	{
  		return -1;
  	}
  
      ret = SetupComm(wm_tool_uart_handle, 4096, 4096);
      if (ret)
      {
          ret  = wm_tool_uart_set_speed(WM_TOOL_DEFAULT_BAUD_RATE);
          ret |= wm_tool_uart_set_timeout();
      }
      else
      {
          ret = -1;
      }
  
      return ret;
  }
  
  static int wm_tool_uart_read(void *data, unsigned int size)
  {
      BOOL ret;
      DWORD wait;
  	unsigned long len = 0;
  	COMSTAT state;
  	DWORD error;
      OVERLAPPED event;
  
      do
      {
      	memset(&event, 0, sizeof(OVERLAPPED));
  
      	event.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  
      	ClearCommError(wm_tool_uart_handle, &error, &state);
  
          ret = ReadFile(wm_tool_uart_handle, data, size, &len, &event);
          if (!ret)
          {
              if (ERROR_IO_PENDING == GetLastError())
      		{
      			wait = WaitForSingleObject(event.hEvent, wm_tool_uart_block);
      			if (WAIT_OBJECT_0 == wait)
      			    ret = TRUE;
      		}
          }
      } while (wm_tool_uart_block && !len);
  
  	if (ret)
  	{
          if (len > 0)
              return (int)len;
  	}
  
  	return -1;
  }
  
  static int wm_tool_uart_write(const void *data, unsigned int size)
  {
      BOOL ret;
      DWORD wait;
      OVERLAPPED event;
      COMSTAT state;
  	DWORD error;
      unsigned int snd = 0;
  	unsigned long len = 0;
  
      memset(&event, 0, sizeof(OVERLAPPED));
  
      event.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  
      do
      {
          ClearCommError(wm_tool_uart_handle, &error, &state);
  
          ret = WriteFile(wm_tool_uart_handle, data + snd, size - snd, &len, &event);
          if (!ret)
          {
              if (ERROR_IO_PENDING == GetLastError())
      		{
      			wait = WaitForSingleObject(event.hEvent, INFINITE);
      			if (WAIT_OBJECT_0 == wait)
  			        ret = TRUE;
      		}
          }
  
          if (ret)
      	{
              if ((len > 0) && (snd != size))
              {
                  snd += len;
              }
              else
              {
      	        return size;
      	    }
      	}
      	else
      	{
              break;
      	}
      } while (1);
  
  	return -1;
  }
  
  static void wm_tool_uart_close(void)
  {
  	FlushFileBuffers(wm_tool_uart_handle);
  	CloseHandle(wm_tool_uart_handle);
  	wm_tool_uart_handle = NULL;
  
  	return;
  }
  
  static DWORD WINAPI wm_tool_uart_tx_thread(LPVOID arg)
  {
      wm_tool_stdin_to_uart();
  
      return 0;
  }
  
  static int wm_tool_create_thread(void)
  {
      HANDLE thread_handle;
      thread_handle = CreateThread(0, 0, wm_tool_uart_tx_thread, NULL, 0, NULL);
      CloseHandle(thread_handle);
      return 0;
  }
  
  #else
  
  static void wm_tool_signal_proc(int no)
  {
      wm_tool_signal_proc_entry();
  }
  
  static void wm_tool_signal_init(void)
  {
      signal(SIGINT, wm_tool_signal_proc);
  }
  
  static void wm_tool_delay_ms(int ms)
  {
      usleep(ms * 1000);
  
      return;
  }
  
  static int wm_tool_uart_set_rts(int boolflag)
  {
      int ret;
      int controlbits;
  
      ret = ioctl(wm_tool_uart_fd, TIOCMGET, &controlbits);
      if (ret < 0)
      {
          WM_TOOL_DBG_PRINT("TIOCMGET failed!\r\n");
          return ret;
      }
  
      if (controlbits & TIOCM_RTS)
      {
          WM_TOOL_DBG_PRINT("TIOCM_RTS!, %d!\r\n", boolflag);
      }
      else
      {
          WM_TOOL_DBG_PRINT("~TIOCM_RTS!, %d!\r\n", boolflag);
      }
  
      if (boolflag)
          controlbits |= TIOCM_RTS;
      else
          controlbits &= ~TIOCM_RTS;
  
      ret = ioctl(wm_tool_uart_fd, TIOCMSET, &controlbits);
      if (ret < 0)
      {
          WM_TOOL_DBG_PRINT("TIOCMSET failed!\r\n");
      }
  
      if (ret >= 0)
          ret = 0;
  
      return ret;
  }
  
  static int wm_tool_uart_set_dtr(int boolflag)
  {
      int ret;
      int controlbits;
  
      ret = ioctl(wm_tool_uart_fd, TIOCMGET, &controlbits);
      if (ret < 0)
      {
          WM_TOOL_DBG_PRINT("TIOCMGET failed!\r\n");
          return ret;
      }
  
      if (controlbits & TIOCM_DTR)
      {
          WM_TOOL_DBG_PRINT("TIOCM_DTR, %d!\r\n", boolflag);
      }
      else
      {
          WM_TOOL_DBG_PRINT("~TIOCM_DTR, %d!\r\n", boolflag);
      }
  
      if (boolflag)
          controlbits |= TIOCM_DTR;
      else
          controlbits &= ~TIOCM_DTR;
  
      ret = ioctl(wm_tool_uart_fd, TIOCMSET, &controlbits);
      if (ret < 0)
      {
          WM_TOOL_DBG_PRINT("TIOCMSET failed!\r\n");
      }
  
      if (ret >= 0)
          ret = 0;
  
      return ret;
  }
  
  static void wm_tool_uart_set_block(int bolck)
  {
      int comopt;
  
      comopt = fcntl(wm_tool_uart_fd, F_GETFL, 0);
  
      if (bolck)
          fcntl(wm_tool_uart_fd, F_SETFL, comopt & ~O_NDELAY);
      else
          fcntl(wm_tool_uart_fd, F_SETFL, comopt | O_NDELAY);
  
      return;
  }
  
  static int wm_tool_uart_set_speed(int speed)
  {
  
      int i;
      int size;
      int status = -1;
      struct termios opt;
  
      tcgetattr(wm_tool_uart_fd, &opt);
  
      size = sizeof(wm_tool_uart_speed_array) / sizeof(int);
  
      for (i= 0; i < size; i++)
      {
          if (speed == wm_tool_uart_name_array[i])
          {
              cfsetispeed(&opt, wm_tool_uart_speed_array[i]);
              cfsetospeed(&opt, wm_tool_uart_speed_array[i]);
  
              status = tcsetattr(wm_tool_uart_fd, TCSANOW, &opt);
  
              break;
          }
      }
  
      return status;
  }
  
  static void wm_tool_uart_clear(void)
  {
      tcflush(wm_tool_uart_fd, TCIFLUSH);
  
      return;
  }
  
  static int wm_tool_uart_open(const char *device)
  {
      struct termios tty;
  
      wm_tool_uart_fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
      if (-1 == wm_tool_uart_fd)
      {
  		return -1;
      }
  
      tcgetattr(wm_tool_uart_fd, &wm_tool_saved_serial_cfg);
  
      wm_tool_uart_set_dtr(0);
  
      tcgetattr(wm_tool_uart_fd, &tty);
  
      /* databit 8bit */
      tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
  
      /* set into raw, no echo mode */
      tty.c_iflag =  IGNBRK;
      tty.c_lflag = 0;
      tty.c_oflag = 0;
      tty.c_cflag |= CLOCAL | CREAD;
      tty.c_cflag &= ~CRTSCTS;
      tty.c_cc[VMIN] = 1;
      tty.c_cc[VTIME] = 5;
  
      /* have no software flow control */
      tty.c_iflag &= ~(IXON|IXOFF|IXANY);
  
      /* parity none */
      tty.c_cflag &= ~(PARENB | PARODD);
  
      /* stopbit 1bit */
      tty.c_cflag &= ~CSTOPB;
  
      tcsetattr(wm_tool_uart_fd, TCSANOW, &tty);
  
      return wm_tool_uart_set_speed(WM_TOOL_DEFAULT_BAUD_RATE);
  }
  
  static int wm_tool_uart_read(void *data, unsigned int size)
  {
  	return read(wm_tool_uart_fd, data, size);
  }
  
  static int wm_tool_uart_write(const void *data, unsigned int size)
  {
      int snd = 0;
      int ret = 0;
  
      do
      {
          ret = write(wm_tool_uart_fd, data + snd, size - snd);
          if (ret > 0)
          {
              snd += ret;
  
              if (snd == size)
              {
                  return size;
              }
          }
          else
          {
              break;
          }
      } while (1);
  
  	return -1;
  }
  
  static void wm_tool_uart_close(void)
  {
      tcsetattr(wm_tool_uart_fd, TCSANOW, &wm_tool_saved_serial_cfg);
  
      tcdrain(wm_tool_uart_fd);
      tcflush(wm_tool_uart_fd, TCIOFLUSH);
  
  	close(wm_tool_uart_fd);
      wm_tool_uart_fd = -1;
  
  	return;
  }
  
  static void *wm_tool_uart_tx_thread(void *arg)
  {
      wm_tool_stdin_to_uart();
  
      return NULL;
  }
  
  static int wm_tool_create_thread(void)
  {
      pthread_t thread_id;
      return pthread_create(&thread_id, NULL, wm_tool_uart_tx_thread, NULL);
  }
  #endif
  
  static void wm_tool_signal_proc_entry(void)
  {
      WM_TOOL_DBG_PRINT("signal exit.\r\n");
      exit(0);
  }
  
  static void wm_tool_stdin_to_uart(void)
  {
      int ret;
      char buf[WM_TOOL_ONCE_READ_LEN];
  
      while (1)
      {
          if (fgets(buf, WM_TOOL_ONCE_READ_LEN, stdin))
          {
              ret = wm_tool_uart_write(buf, strlen(buf));
              if (ret <= 0)
              {
                  WM_TOOL_DBG_PRINT("failed to write [%s].\r\n", buf);
              }
          }
      }
  
      return;
  }
  
  static int wm_tool_show_log_from_serial(void)
  {
      int i;
      int ret;
      unsigned int j = 0;
      unsigned char buf[WM_TOOL_ONCE_READ_LEN + 1];
  
      ret = wm_tool_uart_open(wm_tool_serial_path);
      if (ret)
      {
          wm_tool_printf("can not open serial\r\n");
          return ret;
      }
  
      if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_normal_serial_rate)
      {
          ret = wm_tool_uart_set_speed(wm_tool_normal_serial_rate);
          if (ret)
          {
              wm_tool_printf("can not set serial baud rate.\r\n");
              wm_tool_uart_close();
              return ret;
          }
      }
  
      ret = wm_tool_create_thread();
      if (ret)
      {
          wm_tool_printf("can not create thread.\r\n");
          wm_tool_uart_close();
          return ret;
      }
  
      wm_tool_uart_set_block(0);
  
      wm_tool_signal_init();
  
      while (1)
      {
          ret = wm_tool_uart_read(buf, WM_TOOL_ONCE_READ_LEN);
          if (ret > 0)
          {
              if (WM_TOOL_SHOW_LOG_STR == wm_tool_show_log_type)
              {
                  buf[ret] = '\0';
                  wm_tool_printf("%s", (char *)buf);
              }
              else if (WM_TOOL_SHOW_LOG_HEX == wm_tool_show_log_type)
              {
                  for (i = 0; i < ret; i++, j++)
                  {
                      wm_tool_printf("%02X ", buf[i]);
                      if ((j + 1) % 16 == 0)
                      {
                          wm_tool_printf("\r\n");
                      }
                  }
              }
              else
              {
                  break;
              }
          }
          else
          {
              wm_tool_delay_ms(1);
          }
      }
  
      wm_tool_uart_close();
  
      return ret;
  }
  
  static int wm_tool_set_wifi_chip_speed(int speed)
  {
  	int ret;
  
  	if (2000000 == speed)
  	{
  		ret = wm_tool_uart_write(wm_tool_chip_cmd_b2000000, sizeof(wm_tool_chip_cmd_b2000000));
  	}
  	else if (1000000 == speed)
  	{
  		ret = wm_tool_uart_write(wm_tool_chip_cmd_b1000000, sizeof(wm_tool_chip_cmd_b1000000));
  	}
  	else if (921600 == speed)
  	{
  		ret = wm_tool_uart_write(wm_tool_chip_cmd_b921600, sizeof(wm_tool_chip_cmd_b921600));
  	}
  	else if (460800 == speed)
  	{
  		ret = wm_tool_uart_write(wm_tool_chip_cmd_b460800, sizeof(wm_tool_chip_cmd_b460800));
  	}
  	else
  	{
  		ret = wm_tool_uart_write(wm_tool_chip_cmd_b115200, sizeof(wm_tool_chip_cmd_b115200));
  	}
  
  	return ret;
  }
  
  static int wm_tool_send_esc2uart(int ms)
  {
      int i;
  	int err = 0;
      unsigned char esc_key = 27;
  
      for (i = 0; i < (ms / 10); i++)
  	{
          err = wm_tool_uart_write(&esc_key, 1);
  		wm_tool_delay_ms(10);/* 10-50ms */
  	}
  
      return err;
  }
  
  static int wm_tool_erase_image(wm_tool_dl_erase_e type)
  {
      int cnt = 0;
      int ret = -1;
      unsigned char ch;
      unsigned char rom_cmd[] = {0x21, 0x0a, 0x00, 0xc3, 0x35, 0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0xfe, 0x01}; /*2M-8k*/
  
      WM_TOOL_DBG_PRINT("start erase.\r\n");
  
      if (WM_TOOL_DL_ERASE_ALL == type)
      {
          wm_tool_printf("erase flash...\r\n");
          ret = wm_tool_uart_write(rom_cmd, sizeof(rom_cmd));
      }
      if (ret <= 0)
          return -1;
  
      wm_tool_uart_clear();
  
      wm_tool_uart_set_block(1);
  
      do
  	{
  		ret = wm_tool_uart_read(&ch, 1);
  		if (ret > 0)
  	    {
  		    if (('C' == ch) || ('P' == ch))
  		        cnt++;
  		    else
  		        cnt = 0;
  		}
  		else
  		{
  		    wm_tool_printf("erase error, errno = %d.\r\n", errno);
              return -2;
  		}
  	} while (cnt < 3);
  
      wm_tool_uart_set_block(0);
  
      wm_tool_printf("erase finish.\r\n");
  
      return 0;
  }
  
  static int wm_tool_query_mac(void)
  {
      int ret = -1;
      int err;
      int offset = 0;
      char macstr[32] = {0};
      int len = strlen("MAC:AABBCCDDEEFF\n");/* resp format, ROM "Mac:AABBCCDDEEFF\n", SECBOOT "MAC:AABBCCDDEEFF\n" */
      unsigned char macaddr[6] = {0};
  
      wm_tool_uart_clear();
  
      wm_tool_uart_set_block(1);
  
  	err = wm_tool_uart_write(wm_tool_chip_cmd_get_mac, sizeof(wm_tool_chip_cmd_get_mac));
      if (err > 0)
      {
          do
          {
              err = wm_tool_uart_read((unsigned char *)(macstr + offset), sizeof(macstr) - 1 - offset);
              if (err > 0)
              {
                  offset += err;
                  if (offset >= len)
                  {
                      macstr[len - 1] = '\0';/* \n -> 0 */
                      err = wm_tool_str_to_hex_array(macstr + strlen("MAC:"), 6, macaddr);
                      if (!err)
                      {
                          wm_tool_printf("mac %02X-%02X-%02X-%02X-%02X-%02X.\r\n", macaddr[0],
                                         macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
  
                          if (!strncmp(macstr, "Mac:", strlen("Mac:")) && 
                              (WM_TOOL_DL_TYPE_FLS != wm_tool_dl_type) && 
                              !wm_tool_dl_erase)
                          {
                              wm_tool_printf("please download the firmware in .fls format.\r\n");
                          }
                          else
                          {
                              ret = 0;
                          }
                      }
                      break;
                  }
              }
          } while (err > 0);
      }
  
      wm_tool_uart_set_block(0);
  
      return ret;
  }
  
  static int wm_tool_xmodem_download(const char *image)
  {
      FILE *imgfp;
  	unsigned char packet_data[XMODEM_DATA_SIZE];
  	unsigned char frame_data[XMODEM_DATA_SIZE + XMODEM_CRC_SIZE + XMODEM_FRAME_ID_SIZE + 1];
  	int complete, retry_num, pack_counter, read_number, write_number = 0, i;
  	unsigned short crc_value;
  	unsigned char ack_id;
  	int sndlen;
  	int ret = -111;
  
  	imgfp = fopen(image, "rb");
      if (!imgfp)
      {
          wm_tool_printf("can not open image to download.\r\n");
  		return -1;
      }
  
  	sndlen = 0;
  	pack_counter = 0;
  	complete = 0;
  	retry_num = 0;
  
  	wm_tool_printf("start download.\r\n");
  	wm_tool_printf("0%% [");
  
      wm_tool_uart_clear();
  
      wm_tool_uart_set_block(1);
  
  	ack_id = XMODEM_ACK;
  
  	while (!complete)
  	{
  	    WM_TOOL_DBG_PRINT("switch ack_id = %x\r\n", ack_id);
  
          switch(ack_id)
          {
          	case XMODEM_ACK:
          	{
  	            retry_num = 0;
  	            pack_counter++;
  	            read_number = fread(packet_data, sizeof(char), XMODEM_DATA_SIZE, imgfp);
  
  	            WM_TOOL_DBG_PRINT("fread = %d\r\n", read_number);
  
  	            if (read_number > 0)
  	            {
  					sndlen += read_number;
  
  					if (read_number < XMODEM_DATA_SIZE)
  	                {
  	                    WM_TOOL_DBG_PRINT("start filling the last frame!\r\n");
  	                    for ( ; read_number < XMODEM_DATA_SIZE; read_number++)
  	                        packet_data[read_number] = 0x0;
  	                }
  
  	                frame_data[0] = XMODEM_HEAD;
  	                frame_data[1] = (char)pack_counter;
  	                frame_data[2] = (char)(255 - frame_data[1]);
  
  	                for (i = 0; i < XMODEM_DATA_SIZE; i++)
  	                    frame_data[i + 3] = packet_data[i];
  
  	                crc_value = wm_tool_get_crc16(packet_data, XMODEM_DATA_SIZE);
  
  					frame_data[XMODEM_DATA_SIZE + 3]=(unsigned char)(crc_value >> 8);
  	                frame_data[XMODEM_DATA_SIZE + 4]=(unsigned char)(crc_value);
  
  	                write_number = wm_tool_uart_write(frame_data, XMODEM_DATA_SIZE + 5);
  	                if (write_number <= 0)
  	                    wm_tool_printf("write serial error, errno = %d.\r\n", errno);
  
  	                WM_TOOL_DBG_PRINT("waiting for ack, %d, %d ...\r\n", pack_counter, write_number);
  
  	                while ((wm_tool_uart_read(&ack_id, 1)) <= 0);
  
  	                if (ack_id == XMODEM_ACK)
  	                {
  	                	WM_TOOL_DBG_PRINT("Ok!\r\n");
  
  	                	if (sndlen % 10240 == 0)
                          {
      						wm_tool_printf("#");
  						}
  	                }
  					else
  					{
                      	WM_TOOL_DBG_PRINT("error = %x!\r\n", ack_id);
  					}
  	            }
  	            else
  	            {
  	                ack_id = XMODEM_EOT;
  	                complete = 1;
  
  	                WM_TOOL_DBG_PRINT("waiting for complete ack ...\r\n");
  
  	                while (ack_id != XMODEM_ACK)
  	                {
  	                    ack_id = XMODEM_EOT;
  
  	                    write_number = wm_tool_uart_write(&ack_id, 1);
  	                    if (write_number <= 0)
  	                        wm_tool_printf("write serial error, errno = %d.\r\n", errno);
  
  	                    while ((wm_tool_uart_read(&ack_id, 1)) <= 0);
  	                }
  
  	                WM_TOOL_DBG_PRINT("ok\r\n");
  
  	                wm_tool_printf("] 100%%\r\n");
  
  	                wm_tool_printf("download completed.\r\n");
  
  	                ret = 0;
  	            }
  	            break;
              }
              case XMODEM_NAK:
              {
                  if ( retry_num++ > 100)
                  {
                      WM_TOOL_DBG_PRINT("retry too many times, quit!\r\n");
                      wm_tool_printf("download firmware timeout.\r\n");
  
                      complete = 1;
                  }
                  else
                  {
                      write_number = wm_tool_uart_write(frame_data, XMODEM_DATA_SIZE + 5);
                      if (write_number <= 0)
  	                    wm_tool_printf("write serial error, errno = %d.\r\n", errno);
  
                      WM_TOOL_DBG_PRINT("retry for ack, %d, %d ...\r\n", pack_counter, write_number);
  
                      while ((wm_tool_uart_read(&ack_id, 1)) <= 0);
  
                      if (ack_id == XMODEM_ACK)
                      {
                      	WM_TOOL_DBG_PRINT("ok\r\n");
                      }
      				else
      				{
                      	WM_TOOL_DBG_PRINT("error!\r\n");
      				}
                  }
                  break;
              }
              default:
              {
                  WM_TOOL_DBG_PRINT("fatal error!\r\n");
                  WM_TOOL_DBG_PRINT("unknown xmodem protocal [%x].\r\n", ack_id);
                  wm_tool_printf("\r\ndownload failed, please reset and try again.\r\n");
  
                  complete = 1;
                  break;
              }
          }
      }
  
      wm_tool_uart_set_block(0);
  
      fclose(imgfp);
  
      return ret;
  }
  static int wm_tool_download_firmware(void)
  {
      int ret;
      int cnt = 0;
      int note = 1;
      int timeout = 0;
      float timeuse;
      time_t start, end;
      unsigned char ch;
  
      wm_tool_printf("connecting serial...\r\n");
  
      ret = wm_tool_uart_open(wm_tool_serial_path);
      if (ret)
      {
          wm_tool_printf("can not open serial\r\n");
          return ret;
      }
  
      /* In some cases, setting the serial port initialization setting requires a delay. */
      wm_tool_delay_ms(500);
  
      wm_tool_printf("serial connected.\r\n");
  
      if (WM_TOOL_DL_ACTION_AT == wm_tool_dl_action)
      {
          if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_normal_serial_rate)
              wm_tool_uart_set_speed(wm_tool_normal_serial_rate);
  
  #if 0 /* use erase option */
          if (WM_TOOL_DL_TYPE_FLS == wm_tool_dl_type)
          {
              ret = wm_tool_uart_write("AT+&FLSW=8002000,0\r\n", strlen("AT+&FLSW=8002000,0\r\n"));
              if (ret <= 0)
              {
                  wm_tool_printf("destroy secboot failed.\r\n");
                  wm_tool_uart_close();
                  return -3;
              }
              wm_tool_delay_ms(300);
          }
  #endif
  
          ret = wm_tool_uart_write("AT+Z\r\n", strlen("AT+Z\r\n"));
          if (ret <= 0)
          {
              wm_tool_printf("reset error.\r\n");
              wm_tool_uart_close();
              return -4;
          }
  
          if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_normal_serial_rate)
              wm_tool_uart_set_speed(WM_TOOL_DEFAULT_BAUD_RATE);
      }
      else if (WM_TOOL_DL_ACTION_RTS == wm_tool_dl_action)
      {
          ret  = wm_tool_uart_set_dtr(0);
          ret |= wm_tool_uart_set_rts(1);
          wm_tool_delay_ms(50);
          ret |= wm_tool_uart_set_dtr(1);
          ret |= wm_tool_uart_set_rts(0);
          wm_tool_delay_ms(50);
  		ret |= wm_tool_uart_set_dtr(0);
  		if (ret < 0)
          {
              wm_tool_printf("set rts to reboot error.\r\n");
              wm_tool_uart_close();
              return -5;
          }
      }
  
      wm_tool_printf("wait serial sync...");
  
      wm_tool_send_esc2uart(500);/* used for delay */
  
      start = time(NULL);
  
      do
  	{
  		ret = wm_tool_uart_read(&ch, 1);
  
  		WM_TOOL_DBG_PRINT("ret=%d, %x-%c\r\n", ret, ch, ch);
  
  		if (ret > 0)
  	    {
  		    if (('C' == ch) || ('P' == ch))
  		        cnt++;
  		    else
  		        cnt = 0;
  		}
  		else
  		{
              wm_tool_send_esc2uart(30);
  		}
  
  		end = time(NULL);
  		timeuse = difftime(end, start);
  		if (timeuse >= 1)
  		{
  		    wm_tool_printf(".");
  
              timeout++;
              if ((timeout >= (WM_TOOL_DOWNLOAD_TIMEOUT_SEC / 10)) && note)
              {
                  wm_tool_printf("\r\nplease manually reset the device.\r\n");
                  note = 0;
              }
              else if (timeout > WM_TOOL_DOWNLOAD_TIMEOUT_SEC)
              {
                  wm_tool_uart_close();
                  wm_tool_printf("\r\nserial sync timeout.\r\n");
          		return -6;
              }
  
  		    start = time(NULL);
  		}
  	} while (cnt < 3);
  
  	wm_tool_printf("\r\nserial sync sucess.\r\n");
  
  	ret = wm_tool_query_mac();
  	if (ret)
      {
          wm_tool_uart_close();
          wm_tool_printf("download failed.\r\n");
          return ret;
      }
  
  	if (WM_TOOL_DL_ERASE_ALL == wm_tool_dl_erase)
  	{
          ret = wm_tool_erase_image(WM_TOOL_DL_ERASE_ALL);
          if (ret)
          {
              wm_tool_uart_close();
              wm_tool_printf("failed to erase.\r\n");
              return ret;
          }
  	}
  
      if (!wm_tool_download_image && wm_tool_dl_erase)
      {
          return 0;
      }
  
      if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_download_serial_rate)
      {
          ret = wm_tool_set_wifi_chip_speed(wm_tool_download_serial_rate);
      	if (ret > 0)
      	{
      		wm_tool_delay_ms(1 * 1000);
      		wm_tool_uart_set_speed(wm_tool_download_serial_rate);
      	}
  	}
  
      ret = wm_tool_xmodem_download(wm_tool_download_image);
  
      if (WM_TOOL_DEFAULT_BAUD_RATE != wm_tool_download_serial_rate)
      {
          wm_tool_delay_ms(1 * 1000);
  		wm_tool_set_wifi_chip_speed(WM_TOOL_DEFAULT_BAUD_RATE);
  		wm_tool_delay_ms(1 * 1000);
      }
  
      if (!ret)
      {
          if (WM_TOOL_DL_TYPE_FLS == wm_tool_dl_type)
          {
              if (WM_TOOL_DL_ACTION_RTS == wm_tool_dl_action)/* auto reset */
              {
                  wm_tool_uart_set_dtr(0);
                  wm_tool_uart_set_rts(1);
                  wm_tool_delay_ms(50);
                  wm_tool_uart_set_dtr(1);
                  wm_tool_uart_set_rts(0);
                  wm_tool_delay_ms(50);
          		wm_tool_uart_set_dtr(0);
      		}
      		else
      		{
                  wm_tool_printf("please manually reset the device.\r\n");
      		}
          }
      }
  
      wm_tool_uart_close();
  
      return ret;
  }
  
  static void wm_tool_show_local_com(void)
  {
  #if defined(__APPLE__) && defined(__MACH__)
      char *comstr = "tty.usbserial";
  #elif defined(__CYGWIN__)
      int num;
      char *comstr = "ttyS";
  #elif defined(__linux__)
      char *comstr = "ttyUSB";
  #endif
  
  #if defined(__APPLE__) || defined(__MACH__) || defined(__CYGWIN__) || defined(__linux__)
  
      DIR *dir;
  	struct dirent *file;
  
  	dir = opendir("/dev");
  
  	if (dir)
  	{
  		while (NULL != (file = readdir(dir)))
  		{
  
  			if ((0 == strncmp(file->d_name, comstr, strlen(comstr))) && (DT_CHR == file->d_type))
  			{
  #if defined(__CYGWIN__)
                  num = atoi(file->d_name + strlen(comstr));
  	        	wm_tool_printf("COM%d ", num + 1);
  #else
                  wm_tool_printf("%s ", file->d_name);
  #endif
  			}
  		}
  		closedir(dir);
  		wm_tool_printf("\r\n");
  	}
  
  #elif defined(__MINGW32__)
  
      LONG ret;
      HKEY key;
  	DWORD i = 0;
  	DWORD knlen;
      char kname[WM_TOOL_PATH_MAX] = {0};
  	DWORD kvlen;
      char kvalue[WM_TOOL_PATH_MAX] = {0};
      REGSAM kmask = KEY_READ;
  
  #if defined(KEY_WOW64_64KEY)
      BOOL is_64;
  
      if (IsWow64Process(GetCurrentProcess(), &is_64))
      {
          if (is_64)
              kmask |= KEY_WOW64_64KEY;
      }
  #endif
  
      ret = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, NULL, 0, kmask, NULL, &key, NULL);
      if (ret != ERROR_SUCCESS)
      {
          return;
      }
  
  	do{
  	    knlen = sizeof(kname);
  	    kvlen = sizeof(kvalue);
  		ret = RegEnumValue(key, i, kname, &knlen, 0, NULL, (LPBYTE)kvalue, &kvlen);
  		if (ret == ERROR_SUCCESS)
  		{
              if (strcmp(kvalue, "") != 0)
              {
      			wm_tool_printf("%s ", kvalue);
  		    }
  		}
  
  		i++;
  	} while (ret != ERROR_NO_MORE_ITEMS);
  
      RegCloseKey(key);
  
      wm_tool_printf("\r\n");
  
  #else
  
      wm_tool_printf("\r\nunsupported system.\r\n");
  
  #endif
  
      return;
  }
  static void wm_tool_free_res(void)
  {
      if (wm_tool_download_image)
          free(wm_tool_download_image);
  
      if (wm_tool_output_image)
          free(wm_tool_output_image);
  
      if (wm_tool_input_binary)
          free(wm_tool_input_binary);
  
      if (wm_tool_secboot_image)
          free(wm_tool_secboot_image);
  
      return;
  }
  
  int main(int argc, char *argv[])
  {
      int ret = 0;
  
      if (!wm_tool_parse_arv(argc, argv))
      {
          wm_tool_print_usage(argv[0]);
          wm_tool_free_res();
          return 0;
      }
  
      if (wm_tool_show_usage)
      {
          wm_tool_print_usage(argv[0]);
          wm_tool_free_res();
          return 0;
      }
  
      if (wm_tool_show_ver)
      {
          wm_tool_print_version(argv[0]);
          wm_tool_free_res();
          return 0;
      }
  
      if (wm_tool_list_com)
      {
          wm_tool_show_local_com();
          wm_tool_free_res();
          return 0;
      }
  
      if (wm_tool_input_binary)
      {
          ret = wm_tool_pack_firmware();
      }
  
      if (wm_tool_download_image || wm_tool_dl_erase)
      {
          ret = wm_tool_download_firmware();
      }
  
      if (wm_tool_show_log_type)
      {
          ret = wm_tool_show_log_from_serial();
      }
  
      if (ret > 0)
          wm_tool_print_usage(argv[0]);
  
      wm_tool_free_res();
  
      return ret;
  }
  
  