text: /*
   * unzip.c
   * 
   * This is a collection of several routines from gzip-1.0.3 
   * adapted for Linux.
   *
   * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
   * puts by Nick Holloway 1993, better puts by Martin Mares 1995
   * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
   *
   * Adapted for MEMDISK by H. Peter Anvin, April 2003
   */
  
  #include <stdint.h>
  #include "memdisk.h"
  #include "conio.h"
  
  /*
   * gzip declarations
   */
  
  #define OF(args)  args
  #define STATIC static
  
  #define memzero(s, n)     memset ((s), 0, (n))
  
  typedef uint8_t  uch;
  typedef uint16_t ush;
  typedef uint32_t ulg;
  
  #define WSIZE 0x8000	        /* Window size must be at least 32k, */
  				/* and a power of two */
  
  static uch *inbuf;		/* input pointer */
  static uch window[WSIZE];	/* sliding output window buffer */
  
  static unsigned insize;		/* total input bytes read */
  static unsigned inbytes;	/* valid bytes in inbuf */
  static unsigned outcnt;		/* bytes in output buffer */
  
  /* gzip flag byte */
  #define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
  #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
  #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 ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
  #define RESERVED     0xC0 /* bit 6,7:   reserved */
  
  /* Diagnostic functions */
  #ifdef DEBUG
  #  define Assert(cond,msg) {if(!(cond)) error(msg);}
  #  define Trace(x) fprintf x
  #  define Tracev(x) {if (verbose) fprintf x ;}
  #  define Tracevv(x) {if (verbose>1) fprintf x ;}
  #  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
  #  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
  #else
  #  define Assert(cond,msg)
  #  define Trace(x)
  #  define Tracev(x)
  #  define Tracevv(x)
  #  define Tracec(c,x)
  #  define Tracecv(c,x)
  #endif
  
  static int  fill_inbuf(void);
  static void flush_window(void);
  static void error(char *m);
  static void gzip_mark(void **);
  static void gzip_release(void **);
  
  extern ulg crc_32_tab[256];
  
  /* Get byte from input buffer */
  static inline uch get_byte(void)
  {
    if ( inbytes ) {
      uch b = *inbuf++;
      inbytes--;
      return b;
    } else {
      return fill_inbuf();	/* Input buffer underrun */
    }
  }
  
  /* Unget byte from input buffer */
  static inline void unget_byte(void)
  {
    inbytes++;
    inbuf--;
  }
  
  static ulg bytes_out = 0;	/* Number of bytes output */
  static uch *output_data;	/* Output data pointer */
  static ulg output_size;		/* Number of output bytes expected */
  
  static void *malloc(int size);
  static void free(void *where);
  
  static ulg free_mem_ptr, free_mem_end_ptr;
  
  #include "inflate.c"
  
  static void *malloc(int size)
  {
    void *p;
    
    if (size < 0) error("malloc error");
    
    free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */
    
    p = (void *)free_mem_ptr;
    free_mem_ptr += size;
    
    if (free_mem_ptr >= free_mem_end_ptr)
      error("out of memory");
    
    return p;
  }
  
  static void free(void *where)
  {
    /* Don't care */
  }
  
  static void gzip_mark(void **ptr)
  {
    *ptr = (void *) free_mem_ptr;
  }
  
  static void gzip_release(void **ptr)
  {
    free_mem_ptr = (long) *ptr;
  }
   
  /* ===========================================================================
   * Fill the input buffer. This is called only when the buffer is empty
   * and at least one byte is really needed.
   */
  static int fill_inbuf(void)
  {
    /* This should never happen.  We have already pointed the algorithm
       to all the data we have. */
    printf("failed\nDecompression error: ran out of input data\n");
    die();
  }
  
  /* ===========================================================================
   * Write the output window window[0..outcnt-1] and update crc and bytes_out.
   * (Used for the decompressed data only.)
   */
  static void flush_window(void)
  {
      ulg c = crc;         /* temporary variable */
      unsigned n;
      uch *in, *out, ch;
  
      if ( bytes_out+outcnt > output_size )
        error("output buffer overrun");
      
      in = window;
      out = output_data;
      for (n = 0; n < outcnt; n++) {
  	    ch = *out++ = *in++;
  	    c = crc_32_tab[(c ^ ch) & 0xff] ^ (c >> 8);
      }
      crc = c;
      output_data = out;
      bytes_out += (ulg)outcnt;
      outcnt = 0;
  }
  
  static void error(char *x)
  {
    printf("failed\nDecompression error: %s\n", x);
    die();
  }
  
  /* GZIP header */
  struct gzip_header {
    uint16_t magic;
    uint8_t method;
    uint8_t flags;
    uint32_t timestamp;
    uint8_t extra_flags;
    uint8_t os_type;
  } __attribute__ ((packed));
  /* (followed by optional and variable length "extra", "original name",
     and "comment" fields) */
  
  struct gzip_trailer {
    uint32_t crc;
    uint32_t dbytes;
  } __attribute__ ((packed));
  
  /* PKZIP header.  See
   * <http://www.pkware.com/products/enterprise/white_papers/appnote.html>.
   */
  struct pkzip_header {
    uint32_t magic;
    uint16_t version;
    uint16_t flags;
    uint16_t method;
    uint16_t modified_time;
    uint16_t modified_date;
    uint32_t crc;
    uint32_t zbytes;
    uint32_t dbytes;
    uint16_t filename_len;
    uint16_t extra_len;
  } __attribute__ ((packed));
  /* (followed by optional and variable length "filename" and "extra"
     fields) */
  
  /* gzip flag byte */
  #define ASCII_FLAG   0x01 /* bit 0 set: file probably ASCII text */
  #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
  #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 ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
  #define RESERVED     0xC0 /* bit 6,7:   reserved */
  
  /* pkzip flag byte */
  #define PK_ENCRYPTED     0x01  /* bit 0 set: file is encrypted */
  #define PK_DATADESC       0x08  /* bit 3 set: file has trailing "data
                                     descriptor" */
  #define PK_UNSUPPORTED    0xFFF0 /* All other bits must be zero */
  
  
  /* Return 0 if (indata, size) points to a ZIP file, and fill in
     compressed data size, uncompressed data size, CRC, and offset of
     data.
  
     If indata is not a ZIP file, return -1. */
  int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p,
                uint32_t *dbytes_p, uint32_t *orig_crc, uint32_t *offset_p) {
    struct gzip_header *gzh = (struct gzip_header *)indata;
    struct pkzip_header *pkzh = (struct pkzip_header *)indata;
    uint32_t offset;
  
    if (gzh->magic == 0x8b1f) {
      struct gzip_trailer *gzt = indata + size - sizeof (struct gzip_trailer);
      /* We only support method #8, DEFLATED */
      if (gzh->method != 8)  {
        error("gzip file uses invalid method");
        return -1;
      }
      if (gzh->flags & ENCRYPTED) {
        error("gzip file is encrypted; not supported");
        return -1;
      }
      if (gzh->flags & CONTINUATION) {
        error("gzip file is a continuation file; not supported");
        return -1;
      }
      if (gzh->flags & RESERVED) {
        error("gzip file has unsupported flags");
        return -1;
      }
      offset = sizeof (*gzh);
      if (gzh->flags & EXTRA_FIELD) {
        /* Skip extra field */
        unsigned len = *(unsigned *)(indata + offset);
        offset += 2 + len;
      }
      if (gzh->flags & ORIG_NAME) {
        /* Discard the old name */
        uint8_t *p = indata;
        while (p[offset] != 0 && offset < size) {
          offset++;
        }
        offset++;
      }
      
      if (gzh->flags & COMMENT) {
        /* Discard the comment */
        uint8_t *p = indata;
        while (p[offset] != 0 && offset < size) {
          offset++;
        }
        offset++;
      }
  
      if (offset > size) {
        error ("gzip file corrupt");
        return -1;
      }
      *zbytes_p = size - offset - sizeof (struct gzip_trailer);
      *dbytes_p = gzt->dbytes;
      *orig_crc = gzt->crc;
      *offset_p = offset;
      return 0;
    }
    else if (pkzh->magic == 0x04034b50UL) {
      /* Magic number matches pkzip file. */
      
      offset = sizeof (*pkzh);
      if (pkzh->flags & PK_ENCRYPTED) {
        error("pkzip file is encrypted; not supported");
        return -1;
      }
      if (pkzh->flags & PK_DATADESC) {
        error("pkzip file uses data_descriptor field; not supported");
        return -1;
      }
      if (pkzh->flags & PK_UNSUPPORTED) {
        error("pkzip file has unsupported flags");
        return -1;
      }
  
      /* We only support method #8, DEFLATED */
      if (pkzh->method != 8) {
        error("pkzip file uses invalid method");
        return -1;
      }
      /* skip header */
      offset = sizeof (*pkzh);
      /* skip filename */
      offset += pkzh->filename_len;
      /* skip extra field */
      offset += pkzh->extra_len;
  
      if (offset + pkzh->zbytes > size) {
        error ("pkzip file corrupt");
        return -1;
      }
  
      *zbytes_p = pkzh->zbytes;
      *dbytes_p = pkzh->dbytes;
      *orig_crc = pkzh->crc;
      *offset_p = offset;
      return 0;
    }
    else {
      /* Magic number does not match. */
      return -1;
    }
  
    error ("Internal error in check_zip");
    return -1;
  }
  
  /*
   * Decompress the image, trying to flush the end of it as close
   * to end_mem as possible.  Return a pointer to the data block,
   * and change datalen.
   */
  extern void _end;
  
  void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes,
              uint32_t orig_crc, void *target)
  {
    /* Set up the heap; it's the 64K after the bounce buffer */
    free_mem_ptr = (ulg)sys_bounce + 0x10000;
    free_mem_end_ptr = free_mem_ptr + 0x10000;
  
    /* Set up input buffer */
    inbuf  = indata;
    /* Sometimes inflate() looks beyond the end of the compressed data,
       but it always backs up before it is done.  So we give it 4 bytes
       of slack. */
    insize = inbytes = zbytes + 4;
  
    /* Set up output buffer */
    outcnt = 0;
    output_data = target;
    output_size = dbytes;
    bytes_out = 0;
  
    makecrc();
    gunzip();
  
    /* Verify that gunzip() consumed the entire input. */
    if (inbytes != 4)
      error("compressed data length error");
  
    /* Check the uncompressed data length and CRC. */
    if ( bytes_out != dbytes )
      error("uncompressed data length error");
  
    if (orig_crc != CRC_VALUE)
      error("crc error");
  
    puts("ok\n");
  
    return target;
  }
  