/*
* 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"
#undef DEBUG /* Means something different for this file */
/*
* 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 **);
static 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 */
(void)where;
}
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. */
die("failed\nDecompression error: ran out of input data\n");
}
/* ===========================================================================
* 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)
{
die("failed\nDecompression error: %s\n", x);
}
/* 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;
static char heap[65536];
void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes,
uint32_t orig_crc, void *target)
{
/* Set up the heap; it is simply a chunk of bss memory */
free_mem_ptr = (size_t)heap;
free_mem_end_ptr = (size_t)heap + sizeof heap;
/* 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;
}