diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -291,6 +291,14 @@ if test x"$enable_iso9660" != xno; then
   FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_ISO9660=1"
 fi
 
+AC_ARG_ENABLE(squashfs,
+  [  --disable-squashfs      disable SQUASHFS support in Stage 2])
+
+if test x"$enable_squashfs" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_SQUASHFS=1"
+fi
+
+
 dnl AC_ARG_ENABLE(tftp,
 dnl [  --enable-tftp           enable TFTP support in Stage 2])
 dnl 
diff --git a/stage2/Makefile.am b/stage2/Makefile.am
--- a/stage2/Makefile.am
+++ b/stage2/Makefile.am
@@ -7,7 +7,8 @@ noinst_HEADERS = apic.h defs.h dir.h dis
         fat.h filesys.h freebsd.h fs.h hercules.h i386-elf.h \
 	imgact_aout.h iso9660.h jfs.h mb_header.h mb_info.h md5.h \
 	nbi.h pc_slice.h serial.h shared.h smp-imps.h term.h \
-	terminfo.h tparm.h nbi.h ufs2.h vstafs.h xfs.h graphics.h
+	terminfo.h tparm.h nbi.h ufs2.h vstafs.h xfs.h graphics.h \
+	squashfs_fs.h squashfs_zlib.h
 EXTRA_DIST = setjmp.S apm.S $(noinst_SCRIPTS)
 
 # For <stage1.h>.
@@ -19,12 +20,13 @@ libgrub_a_SOURCES = boot.c builtins.c ch
 	disk_io.c fsys_ext2fs.c fsys_fat.c fsys_ffs.c fsys_iso9660.c \
 	fsys_jfs.c fsys_minix.c fsys_reiserfs.c fsys_ufs2.c \
 	fsys_vstafs.c fsys_xfs.c gunzip.c md5.c serial.c stage2.c \
-	terminfo.c tparm.c graphics.c
+	terminfo.c tparm.c graphics.c fsys_squashfs.c squashfs_zlib.c
 libgrub_a_CFLAGS = $(GRUB_CFLAGS) -I$(top_srcdir)/lib \
 	-DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \
 	-DFSYS_ISO9660=1 -DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 \
 	-DFSYS_UFS2=1 -DFSYS_VSTAFS=1 -DFSYS_XFS=1 \
-	-DUSE_MD5_PASSWORDS=1 -DSUPPORT_SERIAL=1 -DSUPPORT_HERCULES=1
+	-DUSE_MD5_PASSWORDS=1 -DSUPPORT_SERIAL=1 -DSUPPORT_HERCULES=1 \
+	-DFSYS_SQUASHFS=1
 
 # Stage 2 and Stage 1.5's.
 pkglibdir = $(libdir)/$(PACKAGE)/$(host_cpu)-$(host_vendor)
@@ -35,23 +37,25 @@ pkglib_DATA = stage2 stage2_eltorito e2f
 pkglib_DATA = stage2 stage2_eltorito e2fs_stage1_5 fat_stage1_5 \
 	ffs_stage1_5 iso9660_stage1_5 jfs_stage1_5 minix_stage1_5 \
 	reiserfs_stage1_5 ufs2_stage1_5 vstafs_stage1_5 xfs_stage1_5 \
-	nbgrub pxegrub
+	squashfs_stage1_5 nbgrub pxegrub
 noinst_DATA = pre_stage2 start start_eltorito nbloader pxeloader diskless
 noinst_PROGRAMS = pre_stage2.exec start.exec start_eltorito.exec \
 	e2fs_stage1_5.exec fat_stage1_5.exec ffs_stage1_5.exec \
 	iso9660_stage1_5.exec jfs_stage1_5.exec minix_stage1_5.exec \
 	reiserfs_stage1_5.exec ufs2_stage1_5.exec vstafs_stage1_5.exec \
-	xfs_stage1_5.exec nbloader.exec pxeloader.exec diskless.exec
+	xfs_stage1_5.exec squashfs_stage1_5.exec \
+	nbloader.exec pxeloader.exec diskless.exec
 else
 pkglib_DATA = stage2 stage2_eltorito e2fs_stage1_5 fat_stage1_5 \
 	ffs_stage1_5 iso9660_stage1_5 jfs_stage1_5 minix_stage1_5 \
-	reiserfs_stage1_5 ufs2_stage1_5 vstafs_stage1_5 xfs_stage1_5
+	reiserfs_stage1_5 ufs2_stage1_5 vstafs_stage1_5 xfs_stage1_5 \
+	squashfs_stage1_5
 noinst_DATA = pre_stage2 start start_eltorito
 noinst_PROGRAMS = pre_stage2.exec start.exec start_eltorito.exec \
 	e2fs_stage1_5.exec fat_stage1_5.exec ffs_stage1_5.exec \
 	iso9660_stage1_5.exec jfs_stage1_5.exec minix_stage1_5.exec \
 	reiserfs_stage1_5.exec ufs2_stage1_5.exec vstafs_stage1_5.exec \
-	xfs_stage1_5.exec
+	xfs_stage1_5.exec squashfs_stage1_5.exec
 endif
 MOSTLYCLEANFILES = $(noinst_PROGRAMS)
 
@@ -97,7 +101,7 @@ pre_stage2_exec_SOURCES = asm.S bios.c b
 	fsys_fat.c fsys_ffs.c fsys_iso9660.c fsys_jfs.c fsys_minix.c \
 	fsys_reiserfs.c fsys_ufs2.c fsys_vstafs.c fsys_xfs.c gunzip.c \
 	hercules.c md5.c serial.c smp-imps.c stage2.c terminfo.c tparm.c \
-	graphics.c
+	graphics.c squashfs_zlib.c fsys_squashfs.c
 pre_stage2_exec_CFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS)
 pre_stage2_exec_CCASFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS)
 pre_stage2_exec_LDFLAGS = $(PRE_STAGE2_LINK)
@@ -232,6 +236,16 @@ iso9660_stage1_5_exec_CCASFLAGS = $(STAG
 iso9660_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_ISO9660=1 \
 	-DNO_BLOCK_FILES=1
 iso9660_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For squashfs_stage1_5 target.
+squashfs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \
+	disk_io.c stage1_5.c fsys_squashfs.c squashfs_zlib.c bios.c
+squashfs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_SQUASHFS=1 \
+	-DNO_BLOCK_FILES=1 -Wall
+squashfs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_SQUASHFS=1 \
+	-DNO_BLOCK_FILES=1
+squashfs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
 
 # For diskless target.
 diskless_exec_SOURCES = $(pre_stage2_exec_SOURCES)
diff --git a/stage2/disk_io.c b/stage2/disk_io.c
--- a/stage2/disk_io.c
+++ b/stage2/disk_io.c
@@ -78,6 +78,9 @@ struct fsys_entry fsys_table[NUM_FSYS + 
 # ifdef FSYS_ISO9660
   {"iso9660", iso9660_mount, iso9660_read, iso9660_dir, 0, 0},
 # endif
+# ifdef FSYS_SQUASHFS
+  {"squashfs", squashfs_mount, squashfs_read, squashfs_dir, 0, 0},
+# endif
   /* XX FFS should come last as it's superblock is commonly crossing tracks
      on floppies from track 1 to 2, while others only use 1.  */
 # ifdef FSYS_FFS
diff --git a/stage2/filesys.h b/stage2/filesys.h
--- a/stage2/filesys.h
+++ b/stage2/filesys.h
@@ -17,6 +17,9 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+
+#ifndef FILESYS_H
+#define FILESYS_H
 
 #include "pc_slice.h"
 
@@ -124,11 +127,20 @@ int iso9660_dir (char *dirname);
 #define FSYS_ISO9660_NUM 0
 #endif
 
+#ifdef FSYS_SQUASHFS
+#define FSYS_SQUASHFS_NUM 1
+int squashfs_mount (void);
+int squashfs_read (char *buf, int len);
+int squashfs_dir (char *dirname);
+#else
+#define FSYS_SQUASHFS_NUM 0
+#endif
+
 #ifndef NUM_FSYS
 #define NUM_FSYS	\
   (FSYS_FFS_NUM + FSYS_FAT_NUM + FSYS_EXT2FS_NUM + FSYS_MINIX_NUM	\
    + FSYS_REISERFS_NUM + FSYS_VSTAFS_NUM + FSYS_JFS_NUM + FSYS_XFS_NUM	\
-   + FSYS_TFTP_NUM + FSYS_ISO9660_NUM + FSYS_UFS2_NUM)
+   + FSYS_TFTP_NUM + FSYS_ISO9660_NUM + FSYS_UFS2_NUM + FSYS_SQUASHFS_NUM)
 #endif
 
 /* defines for the block filesystem info area */
@@ -163,3 +175,6 @@ extern int print_possibilities;
 
 extern int fsmax;
 extern struct fsys_entry fsys_table[NUM_FSYS + 1];
+
+#endif
+
diff --git a/stage2/fsys_squashfs.c b/stage2/fsys_squashfs.c
new file mode 100644
--- /dev/null
+++ b/stage2/fsys_squashfs.c
@@ -0,0 +1,1108 @@
+/*
+ *  Squashfs filesystem backend for GRUB (GRand Unified Bootloader)
+ *
+ *  Copyright (C) 2006  Luc Saillard  <luc@saillard.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifdef FSYS_SQUASHFS
+
+#include "shared.h"
+#include "filesys.h"
+#include "squashfs_fs.h"
+#include "squashfs_zlib.h"
+
+#define SUPERBLOCK ((struct squashfs_super_block *) (FSYS_BUF))
+#define INODE_DATA ((union squashfs_inode_header *)\
+    			((int)SUPERBLOCK + (((sizeof(struct squashfs_super_block)>>5)+1)*32)))
+/* 
+ * We need to allocate two buffers of SQUASHFS_FILE_MAX_SIZE.
+ * One will be used to lad the compressed data (that can be as large as
+ * SQUASHFS_FILE_MAX_SIZE) and one that store the uncompressed data.  Our
+ * current gunzip implementation doesn't support uncompress in place.
+ *
+ * Force a value in the array, to force gcc to allocate the buffer into the
+ * data section, and not in bss section. Else grub will not allocate the
+ * memory.
+ */
+static unsigned char cbuf_data[SQUASHFS_FILE_MAX_SIZE] = {1};
+static unsigned char file_data[SQUASHFS_FILE_MAX_SIZE] = {1};
+
+#define CBUF_DATA ((unsigned char *)cbuf_data)
+#define FILE_DATA ((unsigned char *)file_data)
+
+/* Use to cache the block data that is in FILE_DATA */
+static int squashfs_old_block = -1;
+
+
+#undef SQUASHFS_TRACE
+
+#ifdef SQUASHFS_TRACE
+#define TRACE(s, args...) \
+  do { printf("squashfs: <%s> "s, __PRETTY_FUNCTION__, ## args); } while(0)
+static const char *get_type(int type);
+#else
+#define TRACE(s, args...)
+#endif
+
+static void dump_memory(const void *buffer, int len);
+static void inode_print(union squashfs_inode_header *inode);
+static int inode_read(unsigned int inode_block, unsigned int inode_offset);
+
+
+/*
+ * Read a raw block at @address, length @len and put data in @output_data
+ *
+ * @arg address:
+ * @arg len:
+ * @arg output_data:
+ *
+ * @return the number of bytes read
+ */
+static int read_bytes(long long address, unsigned int len, void *output_data)
+{
+  unsigned int block_number, offset;
+  int ret;
+
+  block_number = address >> SECTOR_BITS;
+  offset = address - (block_number);
+
+  TRACE("reading from position 0x%x, bytes %d\n", (int)address, len);
+  disk_read_func = disk_read_hook;
+  ret = devread(0, (int)address, len, output_data);
+  disk_read_func = NULL;
+  return ret;
+}
+
+
+/*
+ * Read a block located at @start and uncompress it into @output_data
+ *
+ * @arg start: block to read in the filesystem
+ * @arg compressed_size: store in this pointer, the size of the compressed
+ *                       block. So you can find the next compressed block using
+ *                       @start+@compressed_size.
+ * @arg output_data: must an array of at least SQUASHFS_METADATA_SIZE which is the
+ *                   maximum size.
+ *
+ * @return the size of the decompressed block. If an error occur, 0 is returned.
+ */
+static int read_block(long long start, int *compressed_size, void *output_data)
+{
+  unsigned short int c_byte;
+  int offset = 2;
+
+  if (! read_bytes(start, sizeof(c_byte), &c_byte))
+   {
+     TRACE("read_block: Failed to read c_byte\n");
+     return 0;
+   }
+
+  TRACE("read_block: block @0x%x, %d %s bytes\n",
+        (int)start,
+	SQUASHFS_COMPRESSED_SIZE(c_byte),
+	SQUASHFS_COMPRESSED(c_byte) ? "compressed\0" : "uncompressed\0");
+
+  if (SQUASHFS_CHECK_DATA(SUPERBLOCK->flags))
+    offset = 3;
+
+  if (SQUASHFS_COMPRESSED(c_byte))
+   {
+     unsigned int bytes = SQUASHFS_METADATA_SIZE;
+     int res;
+
+     c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+     if (! read_bytes(start + offset, c_byte, CBUF_DATA))
+      {
+	TRACE("Failed to read at offset %x, size %d\n", (int)(start + offset), c_byte);
+	return 0;
+      }
+
+     res = squashfs_uncompress(output_data, &bytes, CBUF_DATA, c_byte);
+     dump_memory(output_data, 48);
+
+     if (res != Z_OK)
+      {
+	if (res == Z_MEM_ERROR)
+	  TRACE("zlib::uncompress failed, not enough memory\n");
+	else if (res == Z_BUF_ERROR)
+	  TRACE("zlib::uncompress failed, not enough room in output buffer\n");
+	else
+	  TRACE("zlib::uncompress failed, unknown error %d\n", res);
+	return 0;
+      }
+
+     if (compressed_size)
+       *compressed_size = offset + c_byte;
+     return bytes;
+   }
+  else
+   {
+     c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+
+     if (! read_bytes(start + offset, c_byte, output_data))
+      {
+	TRACE("Failed to read at offset %x, size %d\n", (int)(start + offset), c_byte);
+	return 0;
+      }
+
+     if (compressed_size)
+       *compressed_size = offset + c_byte;
+
+     return c_byte;
+   }
+}
+
+/*
+ * Read a data block located at @start and uncompress it into @block.
+ * The size of a data block is known in advance and is large of SQUASHFS_FILE_MAX_SIZE.
+ *
+ * @arg start: block to read in the filesystem
+ * @arg size: size of the block. The block can be compressed so it will be
+ *            uncompressed automatically.
+ * @arg output_data: must an array of at least SQUASHFS_METADATA_SIZE which is the
+ *             maximum size.
+ * @return the size of the decompressed block. If an error occur, 0 is returned.
+ */
+static int read_data_block(long long start, unsigned int size, void *output_data)
+{
+  int res;
+  unsigned int bytes = SUPERBLOCK->block_size;
+  int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
+
+  TRACE("block @0x%x, %d %s bytes\n",
+        (int)start,
+	SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte),
+	SQUASHFS_COMPRESSED_BLOCK(c_byte) ? "compressed" : "uncompressed");
+
+  if (SQUASHFS_COMPRESSED_BLOCK(size))
+   {
+     if (! read_bytes(start, c_byte, CBUF_DATA))
+       return 0;
+
+     res = squashfs_uncompress(output_data, &bytes, CBUF_DATA, c_byte);
+     dump_memory(CBUF_DATA, 48);
+
+     if (res != Z_OK)
+      {
+	if (res == Z_MEM_ERROR)
+	  TRACE("zlib::uncompress failed, not enough memory\n");
+	else if (res == Z_BUF_ERROR)
+	  TRACE("zlib::uncompress failed, not enough room in output buffer\n");
+	else
+	  TRACE("zlib::uncompress failed, unknown error %d\n", res);
+	return 0;
+      }
+
+     return bytes;
+   }
+  else
+   {
+     if (! read_bytes(start, c_byte, output_data))
+       return 0;
+
+     return c_byte;
+   }
+}
+
+
+/*
+ * Parse one directory header, and the return the corresponding inode for entry
+ * named @entry_name.
+ *
+ * @param dir_header: struct of a dir_header + a list of dir_entry
+ * @param entry_name: entry to find (ended by a nul character)
+ *
+ * @param result_inode_block: if the entry is present, return in this variable,
+ *                            the inode_block number in the inode_table
+ * @param result_inode_offset: if the entry is present, return in this variable,
+ *                            the offset position in the block_inode_table.
+ *
+ * @return 0 if the entry is found, (@result_inode_offset and @result_inode_offset is valid)
+ *        >0 the number of bytes read to parse the directory header, so this go to the next
+ *           directory_header.
+ *
+ * a directory is represented by a list of squashfs_dir_entry, and a squashfs_dir_header.
+ *
+ *   .-----------------------------------.
+ *   | squashfs_dir_header               |
+ *   |   --> count (number of dir_entry) |
+ *   |-----------------------------------|
+ *   |  squashfs_dir_entry(0)            |
+ *   |  ---------------------            |
+ *   |  name of squashfs_dir_entry(0)    |
+ *   |-----------------------------------|
+ *   |  squashfs_dir_entry(...)          |
+ *   |  -----------------------          |
+ *   |  name of squashfs_dir_entry(...)  |
+ *   |-----------------------------------|
+ *   |  squashfs_dir_entry(n)            |
+ *   |  ---------------------            |
+ *   |  name of squashfs_dir_entry(n)    |
+ *   |-----------------------------------|
+  */
+static int directory_lookup_1(const struct squashfs_dir_header *dir_header,
+    			      const char   *entry_name,
+			      unsigned int *result_inode_block,
+			      unsigned int *result_inode_offset)
+{
+  const unsigned char *data;
+  const struct squashfs_dir_entry *dir_entry;
+  int dir_count, i, offset;
+#ifdef SQUASHFS_TRACE
+  char temp_name[SQUASHFS_NAME_LEN+1];
+#endif
+
+  data = (const unsigned char *)dir_header;
+  offset = sizeof(struct squashfs_dir_header);
+  dir_count = dir_header->count + 1;
+  TRACE("Searching for %s in this directory (entries:%d inode:%d)\n",
+        entry_name, dir_count, dir_header->inode_number);
+
+  while (dir_count--)
+   {
+     dir_entry = (const struct squashfs_dir_entry *)(data + offset);
+     offset += sizeof(struct squashfs_dir_entry);
+
+#ifdef SQUASHFS_TRACE
+     memcpy(temp_name, data+offset, dir_entry->size+1);
+     temp_name[dir_entry->size+1] = 0;
+
+     TRACE("directory entry [%s]: offset:%d  type:%s  size:%d  inode_number:%x\n",
+	   temp_name, dir_entry->offset, get_type(dir_entry->type), dir_entry->size+1,
+	   dir_entry->inode_number);
+#endif
+
+     /* Do a strcmp between the current entry and the entry_name */
+     for (i=0; i<dir_entry->size+1; i++)
+      {
+	if (data[offset+i] != entry_name[i])
+	  break;
+      }
+     if (i == dir_entry->size+1 && entry_name[i] == 0)
+      {
+	*result_inode_block = dir_header->start_block;
+	*result_inode_offset = dir_entry->offset;
+	return 0;
+      }
+
+     offset += dir_entry->size+1;
+   }
+
+  return offset;
+}
+
+/*
+ * Lookup for entry @entry_name in the directory located at @@@inode_block:@inode_offset
+ * If the entry is present, return the inode block of the entry into
+ * @result_inode_block:@result_inode_offset.
+ *
+ * @param inode_block: inode block location of the directory
+ * @param inode_offset: inode offset location of the direcotry
+ * @param dir_size: the directory size. We need this inforamtion because a
+ *                  directory can be composed of a list of directory_header
+ * @param entryname: entry to find (ended by a nul character)
+ * @param result_inode_block: if the entry is present, return in this variable,
+ *                            the inode_block number in the inode_table
+ * @param result_inode_offset: if the entry is present, return in this variable,
+ *                            the offset position in the block_inode_table.
+ *
+ * @result: 0 if the entry was not found
+ *          1 if the entry is found, and fill @result_inode_block, @result_inode_offset
+ *
+ *
+ *  , @inode_block
+ *  |
+ *  |  .............
+ *  |  .............
+ *  |  ...       ...         ____ .------------------------.
+ *  \> .-----------.      __/     | directory_header(0)    | }
+ *     |           | ____/        |   ....                 |  }
+ *     |------------/             | directory_header(xxx)  |   }
+ *  -> |                          |   ...                  |    } dir_size
+ *  |  |------------\____         | directory_header(n)    |   }
+ *  |  |           |     \__      |   ...                  |  }
+ *  |  .           .        \___  |________________________| }
+ *  |  .___________.
+ *  |  ...       ...
+ *  |  .............
+ *  |  .............
+ *  |
+ *  \-- @inode_offset
+ *
+ */
+static int directory_lookup(unsigned int  inode_block,
+                            unsigned int  inode_offset,
+			    unsigned int  dir_size,
+			    const char   *entryname,
+			    unsigned int *result_inode_block,
+			    unsigned int *result_inode_offset)
+{
+  int offset = 0, res, compressed_size, bytes;
+  long long start = SUPERBLOCK->directory_table_start;
+  long long end = SUPERBLOCK->fragment_table_start;
+
+  TRACE("start=0x%x  end=0x%x (len=%d/0x%x)\n", (int)start, (int)end, (int)(end-start));
+
+  while (start < end)
+   {
+     TRACE("reading block 0x%x (offset=0x%x)\n", (int)start, offset);
+     res = read_block(start, &compressed_size, FILE_DATA);
+     if (res == 0)
+      {
+	TRACE("failed to read block\n");
+	return 0;
+      }
+
+     if (inode_block == offset)
+       break;
+     start += compressed_size;
+     offset += compressed_size;
+   }
+
+  if (inode_block != offset)
+   {
+     TRACE("directory block (0x%x:0x%x) was not found\n", inode_block, inode_offset);
+     return 0;
+   }
+
+  TRACE("inode block found at @0x%x\n", (int)start);
+
+  /* A block can be composed of several directory header */
+  bytes = 0;
+  while (bytes < dir_size)
+   {
+     struct squashfs_dir_header *dir_header;
+
+     dir_header = (struct squashfs_dir_header *)
+        		(FILE_DATA+inode_offset+bytes);
+     res = directory_lookup_1(dir_header,
+			      entryname,
+			      result_inode_block,
+			      result_inode_offset);
+     if (res == 0)
+       return 1;
+     bytes += res;
+   }
+  TRACE("entry %s not found in current directory\n", entryname);
+  return 0;
+}
+
+/*
+ * Search in this inode for entry named @entryname. If the entry was found
+ * @result_inode_block and @result_inode_offset is filled.
+ *
+ * INODE_DATA is modified
+ *
+ * If the inode is not a directory, then return 0.
+ *
+ * @param inode_block: inode block location of the directory
+ * @param inode_offset: inode offset location of the direcotry
+ * @param entryname: entry to find (ended by a nul character)
+ * @param result_inode_block: if the entry is present, return in this variable,
+ *                            the inode_block number in the inode_table
+ * @param result_inode_offset: if the entry is present, return in this variable,
+ *                            the offset position in the block_inode_table.
+ *
+ * @return 0 the entry was not found or an error occured
+ *         1 the entry is found, and @result_inode_block, @result_inode_offset is filled
+ *
+ */
+static int squashfs_lookup_directory(int inode_block,
+    				     int inode_offset,
+    				     const char *entryname,
+				     int *result_inode_block,
+				     int *result_inode_offset)
+{
+  int dir_start_block, dir_offset, dir_size;
+  unsigned int entry_start_block = 0, entry_offset = 0;
+
+  TRACE("Lookup in inode %d:%d for %s\n", inode_block, inode_offset, entryname);
+
+  if (! inode_read(inode_block, inode_offset))
+    return 0;
+
+  inode_print(INODE_DATA);
+
+  /* We only support type dir */
+  switch (INODE_DATA->base.inode_type)
+   {
+    case SQUASHFS_DIR_TYPE:
+      dir_start_block = INODE_DATA->dir.start_block;
+      dir_offset = INODE_DATA->dir.offset;
+      dir_size = INODE_DATA->dir.file_size - 3;
+      break;
+
+    case SQUASHFS_LDIR_TYPE:
+      dir_start_block = INODE_DATA->ldir.start_block;
+      dir_offset = INODE_DATA->ldir.offset;
+      dir_size = INODE_DATA->ldir.file_size - 3;
+      break;
+
+    default:
+      TRACE("This inode is not a directory\n");
+      errnum = ERR_BAD_FILETYPE;
+      return 0;
+   }
+
+  if (dir_size > SQUASHFS_METADATA_SIZE)
+   {
+     TRACE("Dir size is too large for our algorithm 0x23I29\n");
+     return 0;
+   }
+
+  /* Get the current directory header */
+  if (! directory_lookup(dir_start_block, dir_offset, dir_size, entryname, &entry_start_block, &entry_offset))
+   {
+     errnum = ERR_FILE_NOT_FOUND;
+     return 0;
+   }
+
+  TRACE("Found %s located at %x:%x\n", entryname, entry_start_block, entry_offset);
+
+  *result_inode_block = entry_start_block;
+  *result_inode_offset = entry_offset;
+
+  return 1;
+}
+
+
+/*
+ * Read the given inode (inode_block:inode_offset) and write the inode data
+ * into INODE_DATA.
+ *
+ * Description of the Squashfs inode table
+ *
+ * r---------- @inode_table_start
+ * |
+ * |
+ * |
+ * |
+ * -> .-----------------.                   array of squashfs_inode_entry
+ *    | inode block 1   |                    .---------------------------.
+ *    |_________________|                   /| @0                        |
+ *    | inode block 2   |                  / |---------------------------|
+ *    |_________________|                 /  |                           |
+ *    |                 |                /   |                           |
+ *    |                 |               /    |---------------------------|
+ *    |                 |@inode_block  /     | @inode_offset             |
+ *    |_________________|/____________/      |---------------------------|
+ *    |                 |                    |                           |
+ *    |                 |                    |                           |
+ *    |_________________|_____________       |                           |
+ *    |                 |             \      |                           |
+ *    |                 |              \     |                           |
+ *    |                 |               \----|___________________________|
+ *    |_________________|
+ *  
+ * an inode block is compressed, so the size length of the block is not known
+ * in advance, but an inode block always contains SQUASHFS_METADATA_SIZE length
+ * bytes.
+ *
+ * So we need to uncompressed all inode block to known the offset of the next
+ * block.
+ *
+ * Each inode doesn't have the same size, so we can't known in advance (without
+ * looking the type of the inode) which data to copy. So we copy all data until
+ * the end of the block.
+ *
+ */
+static int inode_read(unsigned int inode_block, unsigned int inode_offset)
+{
+  int offset = 0, res, compressed_size;
+  long long start = SUPERBLOCK->inode_table_start;
+  long long end = SUPERBLOCK->directory_table_start;
+
+  TRACE("start=0x%x  end=0x%x (len=%d/0x%x)   inode_wanted:%d:%d (0x%x:0x%x)\n",
+        (int)start, (int)end, (int)(end-start), (int)(end-start),
+	inode_block, inode_offset, inode_block, inode_offset);
+
+  while (start < end)
+   {
+     TRACE("reading block 0x%x (offset=0x%x  inode=0x%x:0x%x)\n", (int)start, offset, inode_block, inode_offset);
+     res = read_block(start, &compressed_size, INODE_DATA);
+     if (res == 0)
+      {
+	TRACE("uncompress_directory_table: failed to read block\n");
+	return 0;
+      }
+
+     if (inode_block == offset)
+      {
+	TRACE("Inode %d found @0x%x\n", (int)start);
+	if (inode_offset)
+	  memmove(INODE_DATA, (unsigned char *)INODE_DATA+inode_offset, SQUASHFS_METADATA_SIZE-inode_offset);
+	return 1;
+      }
+
+     start += compressed_size;
+     offset += compressed_size;
+   }
+  TRACE("Inode %d not found\n");
+  return 0;
+}
+
+/*
+ * Return the data block for the current @fragment_index.
+ *
+ * This function read each time the fragment_table so this can be slow to read
+ * all fragment_table in severall calls.
+ *
+ * @param fragment_index: the fragment data block.
+ * @param fragment_data: where to ouput the data. Need to be SQUASHFS_FILE_MAX_SIZE long.
+ *
+ * @return 0 if an error occured, or the fragment_block was not found
+ *        >0 the size of the fragment_data block
+ *
+ *
+ * Description of the Squashfs fragments table
+ *
+ * r---------- @fragment_table_start
+ * |
+ * |
+ * |
+ * |
+ * -> .-----------.     fragment_table(xxx)
+ *    |           |   -> .-------------.
+ *    |___________|__/   |_____________|
+ * -> | (xxx/yyy) | /    |             |       fragment_data(nnn)
+ * |  |___________|/     |_____________|       .-----------------.
+ * |  |           |   -->|   (nnn)      ====>  |                 |
+ * |  |           |   |  |-------------|       |                 |
+ * |  |           |   |  |             |       |                 |
+ * |  |           |   |  |             |       |                 |
+ * |  |___________|   |  |_____________|       |                 |
+ * |                  |                        |-----------------|
+ * |                  |
+ * fragment_index     @offset: yyy
+ *
+ *
+ * fragment_table(xxx) is compressed and contains severall @squashfs_fragment_entry.
+ * fragment_data(nnn) can be compressed and is shared between severall files.
+ *
+ */
+static int fragment_read(unsigned int fragment_index, void *fragment_data)
+{
+  int offset, i, indexes;
+  long long fragment_address;
+  struct squashfs_fragment_entry *fragment_entry;
+  unsigned char *fragments_table = FILE_DATA;
+
+  indexes = SQUASHFS_FRAGMENT_INDEXES(SUPERBLOCK->fragments);
+
+  TRACE("Reading fragment %d/%d  (%d fragments table that start @0x%x)\n",
+      INODE_DATA->reg.fragment, SUPERBLOCK->fragments,
+      indexes,
+      (int)SUPERBLOCK->fragment_table_start);
+
+  for (i=0; i<indexes; i++)
+   {
+     long long current_fragment_location;
+     int length;
+
+     fragment_address = SUPERBLOCK->fragment_table_start + i*sizeof(long long);
+     read_bytes(fragment_address, sizeof(long long), &current_fragment_location);
+
+     TRACE("Block fragment %d is located at @%x\n", i, (int)current_fragment_location);
+
+     length = read_block(current_fragment_location, NULL, fragments_table);
+     if (length == 0)
+       return 0;
+
+     TRACE("Read fragment block %d, length=%d\n", i, length);
+
+     if (SQUASHFS_FRAGMENT_INDEX(fragment_index) == i)
+       break;
+   }
+
+  if (i == indexes)
+   {
+     TRACE("Fragment %d not found\n", fragment_index);
+     return 0;
+   }
+
+  offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment_index);
+
+  fragment_entry = (struct squashfs_fragment_entry *)(fragments_table + offset);
+  TRACE("fragment %d: start_block=0x%x size=%d pending=%d\n",
+        fragment_index, (int)fragment_entry->start_block,
+	fragment_entry->size, fragment_entry->pending);
+
+
+  i = read_data_block(fragment_entry->start_block, fragment_entry->size, fragment_data);
+  if (i == 0)
+   {
+     TRACE("failed to read fragment %d\n", fragment_index);
+     return i;
+   }
+  return i;
+}
+
+/*
+ *
+ * Read one block from a inode file.
+ *
+ * The block_number is position in the list block.
+ * This function is not designed to be speedup, but accurate, and allocate the least memory.
+ *
+ * @arg file_inode: global inode header
+ * @arg SUPERBLOCK: description of the superblock
+ */
+static int squashfs_read_file_one_block(int block_number)
+{
+  long long start;
+  int fragment_size;
+  int blocks;
+  int i;
+
+  if (INODE_DATA->reg.fragment == SQUASHFS_INVALID_FRAG)
+   {
+     fragment_size = 0;
+     blocks = (INODE_DATA->reg.file_size + SUPERBLOCK->block_size - 1) >> SUPERBLOCK->block_log;
+   }
+  else
+   {
+     fragment_size = INODE_DATA->reg.file_size % SUPERBLOCK->block_size;
+     blocks = INODE_DATA->reg.file_size >> SUPERBLOCK->block_log;
+   }
+
+  TRACE("block_number=%d  fragment_size=%d  blocks=%d\n", block_number, fragment_size, blocks);
+
+  /* Only decompress block when the block_number is lesser than the total number of blocks */
+  if (block_number < blocks)
+   {
+     /*
+      * List of blocks to read is after the inode. When copying data, we have also
+      * copied a part of this data
+      */
+     start = INODE_DATA->reg.start_block;
+     for (i=0; i<blocks; i++)
+      {
+	int bytes;
+	unsigned int *c_block_list = (unsigned int *)((struct squashfs_reg_inode_header *)INODE_DATA+1);
+
+	if (i == block_number)
+	 {
+	   TRACE("Reading block %d\n", i);
+	   bytes = read_data_block(start, c_block_list[i], FILE_DATA);
+	   if (bytes == 0)
+	    {
+	      TRACE("failed to read data block at 0x%x\n", (int)start);
+	      return 0;
+	    }
+	   TRACE("Data block:\n");
+	   dump_memory(FILE_DATA, 48);
+	   TRACE("read %d bytes\n", bytes);
+	   return bytes;
+	 }
+	start += SQUASHFS_COMPRESSED_SIZE_BLOCK(c_block_list[i]);
+      }
+   }
+
+  if (fragment_size)
+   {
+     int bytes;
+
+     bytes = fragment_read(INODE_DATA->reg.fragment, FILE_DATA);
+     if (bytes == 0)
+       return 0;
+     /* data begins at FILE_DATA+INODE_DATA->reg.offset */
+     if (INODE_DATA->reg.offset)
+       memmove(FILE_DATA, FILE_DATA+INODE_DATA->reg.offset, INODE_DATA->reg.file_size);
+     TRACE("Data block:\n");
+     dump_memory(FILE_DATA, 48);
+     return INODE_DATA->reg.file_size;
+   }
+
+  return 0;
+}
+
+
+/*
+ *
+ *
+ */
+int
+squashfs_mount (void)
+{
+  TRACE("squashfs_mount()\n");
+
+  /* Check partition type for harddisk */
+  if (((current_drive & 0x80) || (current_slice != 0))
+      && current_slice != PC_SLICE_TYPE_EXT2FS
+      && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS)))
+    return 0;
+
+  /* Read bpb */
+  if (! devread(SQUASHFS_START, 0, sizeof(struct squashfs_super_block), (char *) SUPERBLOCK))
+    return 0;
+
+  if (SUPERBLOCK->s_magic != SQUASHFS_MAGIC)
+   {
+     if (SUPERBLOCK->s_magic != SQUASHFS_MAGIC_SWAP)
+       return 0;
+
+     TRACE("Reading a different endian SQUASHFS filesystem\n");
+     TRACE("Not supported\n");
+     errnum = ERR_FSYS_MOUNT;
+     return 0;
+   }
+
+  /* Check the MAJOR & MINOR versions */
+  if (   SUPERBLOCK->s_major != SQUASHFS_MAJOR
+      || SUPERBLOCK->s_minor > SQUASHFS_MINOR)
+   {
+     TRACE("Major/Minor mismatch, filesystem is (%d:%d)\n",
+	 SUPERBLOCK->s_major, SUPERBLOCK->s_minor);
+     printf("I only support Squashfs 3.0 filesystems!\n");
+     errnum = ERR_FSYS_MOUNT;
+     return 0;
+   }
+
+  if (SUPERBLOCK->block_size > SQUASHFS_FILE_MAX_SIZE)
+   {
+     TRACE("Bad squashfs partition, block size is greater than SQUASHFS_FILE_MAX_SIZE\n");
+     errnum = ERR_FSYS_MOUNT;
+     return 0;
+   }
+
+  TRACE("Found a SQUASHFS partition\n");
+  TRACE("\tInodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(SUPERBLOCK->flags) ? "un" : "");
+  TRACE("\tData is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(SUPERBLOCK->flags) ? "un" : "");
+  TRACE("\tFragments are %scompressed\n", SQUASHFS_UNCOMPRESSED_FRAGMENTS(SUPERBLOCK->flags) ? "un" : "");
+  TRACE("\tCheck data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(SUPERBLOCK->flags) ? "" : "not");
+  TRACE("\tFragments are %s present in the filesystem\n", SQUASHFS_NO_FRAGMENTS(SUPERBLOCK->flags) ? "not" : "");
+  TRACE("\tAlways_use_fragments option is %s specified\n", SQUASHFS_ALWAYS_FRAGMENTS(SUPERBLOCK->flags) ? "" : "not");
+  TRACE("\tDuplicates are %s removed\n", SQUASHFS_DUPLICATES(SUPERBLOCK->flags) ? "" : "not");
+  TRACE("\tFilesystem size %d Kbytes (%d Mbytes)\n", (unsigned long long)SUPERBLOCK->bytes_used >> 10, (unsigned long long)SUPERBLOCK->bytes_used >> 20);
+  TRACE("\tBlock size %d\n", SUPERBLOCK->block_size);
+  TRACE("\tNumber of fragments %d\n", SUPERBLOCK->fragments);
+  TRACE("\tNumber of inodes %d\n", SUPERBLOCK->inodes);
+  TRACE("\tNumber of uids %d\n", SUPERBLOCK->no_uids);
+  TRACE("\tNumber of gids %d\n", SUPERBLOCK->no_guids);
+  TRACE("SUPERBLOCK->inode_table_start 0x%x\n", SUPERBLOCK->inode_table_start);
+  TRACE("SUPERBLOCK->directory_table_start 0x%x\n", SUPERBLOCK->directory_table_start);
+  TRACE("SUPERBLOCK->uid_start 0x%x\n", SUPERBLOCK->uid_start);
+  TRACE("SUPERBLOCK->fragment_table_start 0x%x\n\n", SUPERBLOCK->fragment_table_start);
+
+  return 1;
+}
+
+/*
+ *
+ *
+ */
+int
+squashfs_dir (char *dirname)
+{
+  char *filename;
+  int d_inode_start_block;
+  int d_inode_offset;
+  int res;
+  int found_last_part = 0;
+
+  TRACE("squashfs_dir(%s)\n", dirname);
+
+  d_inode_start_block = SQUASHFS_INODE_BLK(SUPERBLOCK->root_inode);
+  d_inode_offset = SQUASHFS_INODE_OFFSET(SUPERBLOCK->root_inode);
+
+  while (found_last_part == 0)
+   {
+     /* Skip all concatened / */
+     while (*dirname == '/')
+       dirname++;
+
+     /* Keep only the filename */
+     filename = dirname;
+     while (*dirname && *dirname != '/' && *dirname != ' ')
+       dirname++;
+
+     /*
+      * No more / ? so this is the last part of the path, leave the while at the
+      * end. Grub give use the full command line kernel /xxxx toto=azeaze ....
+      * So stop after the first space too.
+      */
+     if (*dirname == 0 || *dirname == ' ')
+      {
+	*dirname = 0;
+	found_last_part = 1;
+      }
+     else
+      {
+	/* filename point to the current entry, make then terminated by \0 */
+	*dirname++ = 0;
+      }
+
+     res = squashfs_lookup_directory(d_inode_start_block, d_inode_offset,
+				     filename,
+				     &d_inode_start_block, &d_inode_offset);
+     if (res == 0)
+      {
+	TRACE("Path %s component not found\n", filename);
+	return 0;
+      }
+   }
+
+  TRACE("Nearly finish we just look for %s\n", filename);
+  TRACE("inode for %s is %d:%d\n", filename, d_inode_start_block, d_inode_offset);
+
+  if (! inode_read(d_inode_start_block, d_inode_offset))
+    return 0;
+
+  inode_print(INODE_DATA);
+
+  switch (INODE_DATA->base.inode_type)
+   {
+    case SQUASHFS_FILE_TYPE:
+      filemax = INODE_DATA->reg.file_size;
+      break;
+
+    case SQUASHFS_LREG_TYPE:
+      filemax = INODE_DATA->lreg.file_size;
+      break;
+
+    default:
+      errnum = ERR_BAD_FILETYPE;
+      return 0;
+   }
+
+  filepos = 0;
+  squashfs_old_block = -1;
+
+  TRACE("Size of %s is %d\n", filename, filemax);
+  return 1;
+}
+
+
+/*
+ *
+ *
+ */
+int
+squashfs_read(char *buf, int len)
+{
+  int bytes, size, block, ret, offset;
+
+  TRACE("buf=0x%x, len=%d, position=%d\n", (unsigned long)buf, len, filepos);
+
+  bytes = 0;
+  while (len > 0)
+   {
+     /* Calculate the block number to read for the current position */
+     block = filepos >> SUPERBLOCK->block_log;
+     offset = filepos % SUPERBLOCK->block_size;
+
+     if (block != squashfs_old_block)
+      {
+	ret = squashfs_read_file_one_block(block);
+	if (ret == 0)
+	  return 0;
+	squashfs_old_block = block;
+      }
+
+     size = SUPERBLOCK->block_size - offset;
+     if (size > len)
+       size = len;
+
+     TRACE("Copying into buffer at @0x%x, %d bytes, position=%d (offset=%d)\n", (unsigned long) buf, size, filepos, offset);
+     memmove(buf, FILE_DATA+offset, size);
+     dump_memory(buf, size);
+
+     filepos += size;
+     len -= size;
+     bytes += size;
+     buf += size;
+   }
+
+  return bytes;
+}
+
+
+
+/*
+ *
+ *
+ *
+ *
+ *
+ */
+
+#ifdef SQUASHFS_TRACE
+
+static int __isalnum(int c)
+{
+  if (c>=0x20 && c<=0x7e)
+    return 1;
+  else
+    return 0;
+}
+
+static char tohex(char c)
+{
+  return c>=10?c-10+'a':c+'0';
+}
+
+static unsigned int fmt_xlong(char *dest, unsigned long i, int precision)
+{
+  register unsigned long len,tmp;
+  /* first count the number of bytes needed */
+  for (len=1, tmp=i; tmp>15; ++len)
+    tmp>>=4;
+
+  if (precision)
+   {
+     int x = 0;
+     for (x=0; x<(precision-len); x++)
+       *dest++='0';
+   }
+
+  tmp = i;
+  dest+=len;
+  *dest = 0;
+  while (1)
+   {
+     *--dest = tohex(tmp&15);
+     if (!(tmp>>=4)) break;
+   }
+  return len;
+}
+
+static void print_fmt_xlong(int i, int precision)
+{
+  char temp[48];
+  fmt_xlong(temp, i, precision);
+  printf("%s", temp);
+}
+
+static void dump_memory(const void *data, int len)
+{
+  int i, address, count;
+  const unsigned char *buffer = data;
+
+  return;
+  address = 0;
+  while (address < len )
+   {
+     print_fmt_xlong(address, 8);
+     for(count=i=0; address+i < len && i<16; count++,i++)
+      {
+	printf(" ");
+	print_fmt_xlong(buffer[address+i], 2);
+      }
+     for(;count<=16;count++)
+       printf("   ");
+     for(i=0; address < len && i<16; i++,address++)
+       printf("%c", __isalnum(buffer[address]) ? buffer[address] : '.');
+     printf("\n");
+   }
+}
+
+static const char *get_type(int type)
+{
+  switch (type)
+   {
+    case SQUASHFS_DIR_TYPE:
+      return "directory";
+    case SQUASHFS_FILE_TYPE:
+      return "file";
+    case SQUASHFS_SYMLINK_TYPE:
+      return "symlink";
+    case SQUASHFS_BLKDEV_TYPE:
+      return "block device";
+    case SQUASHFS_CHRDEV_TYPE:
+      return "char device";
+    case SQUASHFS_FIFO_TYPE:
+      return "fifo";
+    case SQUASHFS_SOCKET_TYPE:
+      return "socket";
+    case SQUASHFS_LDIR_TYPE:
+      return "ldir";
+    case SQUASHFS_LREG_TYPE:
+      return "lreg";
+    default:
+      return "unknown";
+   }
+}
+
+static void print_inode_directory(struct squashfs_dir_inode_header *inode)
+{
+  TRACE("inode DIR: inode_number=%d nlink=%d file_size=%d start_block=%d\n",
+        inode->inode_number,
+	inode->nlink,
+	inode->file_size,
+	inode->start_block);
+
+  dump_memory(inode, sizeof(struct squashfs_dir_inode_header));
+}
+
+static void print_inode_file(struct squashfs_reg_inode_header *inode)
+{
+  TRACE("inode FILE: inode_number=%d mode=%d uid=%d gid=%d file_size=%d ",
+        inode->inode_number,
+	inode->mode,
+	inode->uid,
+	inode->guid,
+	inode->file_size
+      );
+
+  if (inode->fragment == SQUASHFS_INVALID_FRAG)
+   {
+     TRACE("fragment_bytes=0 location=%d:%d blocks=%d\n",
+	   inode->start_block, inode->offset,
+	   (inode->file_size + SUPERBLOCK->block_size - 1) >> SUPERBLOCK->block_log);
+   }
+  else
+   {
+     TRACE("fragment_bytes=%d location=%d:%d blocks=%d\n",
+	   inode->file_size % SUPERBLOCK->block_size,
+	   (int)inode->start_block, inode->offset,
+	   inode->file_size >> SUPERBLOCK->block_log);
+   }
+}
+
+
+
+static void inode_print(union squashfs_inode_header *inode)
+{
+  switch (inode->base.inode_type)
+   {
+    case SQUASHFS_DIR_TYPE:
+      print_inode_directory(&inode->dir);
+      break;
+    case SQUASHFS_FILE_TYPE:
+      print_inode_file(&inode->reg);
+    default:
+      TRACE("inode %s\n", get_type(inode->base.inode_type));
+      break;
+   }
+}
+
+#else
+
+static void inode_print(union squashfs_inode_header *inode)
+{
+}
+
+static void dump_memory(const void *data, int len)
+{
+}
+
+#endif
+
+
+#endif
+
diff --git a/stage2/gunzip.c b/stage2/gunzip.c
--- a/stage2/gunzip.c
+++ b/stage2/gunzip.c
@@ -277,7 +277,7 @@ gunzip_test_header (void)
    *  is a compressed file, and simply mark it as such.
    */
   if (no_decompression
-      || grub_read (buf, 10) != 10
+      || grub_read ((char *)buf, 10) != 10
       || ((*((unsigned short *) buf) != GZIP_HDR_LE)
 	  && (*((unsigned short *) buf) != OLD_GZIP_HDR_LE)))
     {
@@ -293,7 +293,7 @@ gunzip_test_header (void)
   if (buf[2] != DEFLATED
       || (buf[3] & UNSUPP_FLAGS)
       || ((buf[3] & EXTRA_FIELD)
-	  && (grub_read (buf, 2) != 2
+	  && (grub_read ((char *)buf, 2) != 2
 	      || bad_field (*((unsigned short *) buf))))
       || ((buf[3] & ORIG_NAME) && bad_field (-1))
       || ((buf[3] & COMMENT) && bad_field (-1)))
@@ -308,7 +308,7 @@ gunzip_test_header (void)
   
   filepos = filemax - 8;
   
-  if (grub_read (buf, 8) != 8)
+  if (grub_read ((char *)buf, 8) != 8)
     {
       if (! errnum)
 	errnum = ERR_BAD_GZIP_HEADER;
@@ -494,7 +494,7 @@ get_byte (void)
   if (filepos == gzip_data_offset || bufloc == INBUFSIZ)
     {
       bufloc = 0;
-      grub_read (inbuf, INBUFSIZ);
+      grub_read ((char *)inbuf, INBUFSIZ);
     }
 
   return inbuf[bufloc++];
diff --git a/stage2/shared.h b/stage2/shared.h
--- a/stage2/shared.h
+++ b/stage2/shared.h
@@ -212,6 +212,7 @@ extern char *grub_scratch_mem;
 #define STAGE2_ID_XFS_STAGE1_5		8
 #define STAGE2_ID_ISO9660_STAGE1_5	9
 #define STAGE2_ID_UFS2_STAGE1_5		10
+#define STAGE2_ID_SQUASHFS_STAGE1_5	11
 
 #ifndef STAGE1_5
 # define STAGE2_ID	STAGE2_ID_STAGE2
@@ -236,6 +237,8 @@ extern char *grub_scratch_mem;
 #  define STAGE2_ID	STAGE2_ID_ISO9660_STAGE1_5
 # elif defined(FSYS_UFS2)
 #  define STAGE2_ID	STAGE2_ID_UFS2_STAGE1_5
+# elif defined(FSYS_SQUASHFS)
+#  define STAGE2_ID	STAGE2_ID_SQUASHFS_STAGE1_5
 # else
 #  error "unknown Stage 2"
 # endif
diff --git a/stage2/squashfs_fs.h b/stage2/squashfs_fs.h
new file mode 100644
--- /dev/null
+++ b/stage2/squashfs_fs.h
@@ -0,0 +1,877 @@
+#ifndef SQUASHFS_FS
+#define SQUASHFS_FS
+
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006
+ * Phillip Lougher <phillip@lougher.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs.h
+ */
+
+#define SQUASHFS_MAJOR			3
+#define SQUASHFS_MINOR			0
+#define SQUASHFS_MAGIC			0x73717368
+#define SQUASHFS_MAGIC_SWAP		0x68737173
+#define SQUASHFS_START			0
+
+/* size of metadata (inode and directory) blocks */
+#define SQUASHFS_METADATA_SIZE		8192
+#define SQUASHFS_METADATA_LOG		13
+
+/* default size of data blocks */
+#define SQUASHFS_FILE_SIZE		65536
+#define SQUASHFS_FILE_LOG		16
+
+#define SQUASHFS_FILE_MAX_SIZE		65536
+
+/* Max number of uids and gids */
+#define SQUASHFS_UIDS			256
+#define SQUASHFS_GUIDS			255
+
+/* Max length of filename (not 255) */
+#define SQUASHFS_NAME_LEN		256
+
+#define SQUASHFS_INVALID		((long long) 0xffffffffffff)
+#define SQUASHFS_INVALID_FRAG		((unsigned int) 0xffffffff)
+#define SQUASHFS_INVALID_BLK		((long long) -1)
+#define SQUASHFS_USED_BLK		((long long) -2)
+
+/* Filesystem flags */
+#define SQUASHFS_NOI			0
+#define SQUASHFS_NOD			1
+#define SQUASHFS_CHECK			2
+#define SQUASHFS_NOF			3
+#define SQUASHFS_NO_FRAG		4
+#define SQUASHFS_ALWAYS_FRAG		5
+#define SQUASHFS_DUPLICATE		6
+
+#define SQUASHFS_BIT(flag, bit)		((flag >> bit) & 1)
+
+#define SQUASHFS_UNCOMPRESSED_INODES(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_NOI)
+
+#define SQUASHFS_UNCOMPRESSED_DATA(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_NOD)
+
+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_NOF)
+
+#define SQUASHFS_NO_FRAGMENTS(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_NO_FRAG)
+
+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \
+						SQUASHFS_ALWAYS_FRAG)
+
+#define SQUASHFS_DUPLICATES(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_DUPLICATE)
+
+#define SQUASHFS_CHECK_DATA(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_CHECK)
+
+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
+		duplicate_checking)	(noi | (nod << 1) | (check_data << 2) \
+		| (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
+		(duplicate_checking << 6))
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE		1
+#define SQUASHFS_FILE_TYPE		2
+#define SQUASHFS_SYMLINK_TYPE		3
+#define SQUASHFS_BLKDEV_TYPE		4
+#define SQUASHFS_CHRDEV_TYPE		5
+#define SQUASHFS_FIFO_TYPE		6
+#define SQUASHFS_SOCKET_TYPE		7
+#define SQUASHFS_LDIR_TYPE		8
+#define SQUASHFS_LREG_TYPE		9
+
+/* 1.0 filesystem type definitions */
+#define SQUASHFS_TYPES			5
+#define SQUASHFS_IPC_TYPE		0
+
+/* Flag whether block is compressed or uncompressed, bit is set if block is
+ * uncompressed */
+#define SQUASHFS_COMPRESSED_BIT		(1 << 15)
+
+#define SQUASHFS_COMPRESSED_SIZE(B)	(((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
+		(B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
+
+#define SQUASHFS_COMPRESSED(B)		(!((B) & SQUASHFS_COMPRESSED_BIT))
+
+#define SQUASHFS_COMPRESSED_BIT_BLOCK		(1 << 24)
+
+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)	(((B) & \
+	~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
+	~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
+
+#define SQUASHFS_COMPRESSED_BLOCK(B)	(!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
+
+/*
+ * Inode number ops.  Inodes consist of a compressed block number, and an
+ * uncompressed  offset within that block
+ */
+#define SQUASHFS_INODE_BLK(a)		((unsigned int) ((a) >> 16))
+
+#define SQUASHFS_INODE_OFFSET(a)	((unsigned int) ((a) & 0xffff))
+
+#define SQUASHFS_MKINODE(A, B)		((squashfs_inode_t)(((squashfs_inode_t) (A)\
+					<< 16) + (B)))
+
+/* Compute 32 bit VFS inode number from squashfs inode number */
+#define SQUASHFS_MK_VFS_INODE(a, b)	((unsigned int) (((a) << 8) + \
+					((b) >> 2) + 1))
+/* XXX */
+
+/* Translate between VFS mode and squashfs mode */
+#define SQUASHFS_MODE(a)		((a) & 0xfff)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES(A)	(A * sizeof(struct squashfs_fragment_entry))
+
+#define SQUASHFS_FRAGMENT_INDEX(A)	(SQUASHFS_FRAGMENT_BYTES(A) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)	(SQUASHFS_FRAGMENT_BYTES(A) % \
+						SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES(A)	((SQUASHFS_FRAGMENT_BYTES(A) + \
+					SQUASHFS_METADATA_SIZE - 1) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)	(SQUASHFS_FRAGMENT_INDEXES(A) *\
+						sizeof(long long))
+
+#define SQUASHFS_CACHED_FRAGMENTS	3
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS		8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG	64
+
+#define SQUASHFS_MAX_FILE_SIZE		((long long) 1 << \
+					(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+#define SQUASHFS_MARKER_BYTE		0xff
+
+/*
+ * definitions for structures on disk
+ */
+
+typedef long long		squashfs_block_t;
+typedef long long		squashfs_inode_t;
+
+struct squashfs_super_block {
+	unsigned int		s_magic;
+	unsigned int		inodes;
+	unsigned int		bytes_used_2;
+	unsigned int		uid_start_2;
+	unsigned int		guid_start_2;
+	unsigned int		inode_table_start_2;
+	unsigned int		directory_table_start_2;
+	unsigned int		s_major:16;
+	unsigned int		s_minor:16;
+	unsigned int		block_size_1:16;
+	unsigned int		block_log:16;
+	unsigned int		flags:8;
+	unsigned int		no_uids:8;
+	unsigned int		no_guids:8;
+	unsigned int		mkfs_time /* time of filesystem creation */;
+	squashfs_inode_t	root_inode;
+	unsigned int		block_size;
+	unsigned int		fragments;
+	unsigned int		fragment_table_start_2;
+	long long		bytes_used;
+	long long		uid_start;
+	long long		guid_start;
+	long long		inode_table_start;
+	long long		directory_table_start;
+	long long		fragment_table_start;
+	long long		unused;
+} __attribute__ ((packed));
+
+struct squashfs_dir_index {
+	unsigned int		index;
+	unsigned int		start_block;
+	unsigned char		size;
+	unsigned char		name[0];
+} __attribute__ ((packed));
+
+#define SQUASHFS_BASE_INODE_HEADER		\
+	unsigned int		inode_type:4;	\
+	unsigned int		mode:12;	\
+	unsigned int		uid:8;		\
+	unsigned int		guid:8;		\
+	unsigned int		mtime;		\
+	unsigned int 		inode_number;
+
+struct squashfs_base_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned short		rdev;
+} __attribute__ ((packed));
+	
+struct squashfs_symlink_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned short		symlink_size;
+	char			symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	squashfs_block_t	start_block;
+	unsigned int		fragment;
+	unsigned int		offset;
+	unsigned int		file_size;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_lreg_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	squashfs_block_t	start_block;
+	unsigned int		fragment;
+	unsigned int		offset;
+	long long		file_size;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned int		file_size:19;
+	unsigned int		offset:13;
+	unsigned int		start_block;
+	unsigned int		parent_inode;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header {
+	SQUASHFS_BASE_INODE_HEADER;
+	unsigned int		nlink;
+	unsigned int		file_size:27;
+	unsigned int		offset:13;
+	unsigned int		start_block;
+	unsigned int		i_count:16;
+	unsigned int		parent_inode;
+	struct squashfs_dir_index	index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header {
+	struct squashfs_base_inode_header	base;
+	struct squashfs_dev_inode_header	dev;
+	struct squashfs_symlink_inode_header	symlink;
+	struct squashfs_reg_inode_header	reg;
+	struct squashfs_lreg_inode_header	lreg;
+	struct squashfs_dir_inode_header	dir;
+	struct squashfs_ldir_inode_header	ldir;
+	struct squashfs_ipc_inode_header	ipc;
+};
+	
+struct squashfs_dir_entry {
+	unsigned int		offset:13;
+	unsigned int		type:3;
+	unsigned int		size:8;
+	int			inode_number:16;
+	char			name[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_header {
+	unsigned int		count:8;
+	unsigned int		start_block;
+	unsigned int		inode_number;
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry {
+	long long		start_block;
+	unsigned int		size;
+	unsigned int		pending;
+} __attribute__ ((packed));
+
+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
+extern int squashfs_uncompress_init(void);
+extern int squashfs_uncompress_exit(void);
+
+/*
+ * macros to convert each packed bitfield structure from little endian to big
+ * endian and vice versa.  These are needed when creating or using a filesystem
+ * on a machine with different byte ordering to the target architecture.
+ *
+ */
+
+#define SQUASHFS_SWAP_START \
+	int bits;\
+	int b_pos;\
+	unsigned long long val;\
+	unsigned char *s;\
+	unsigned char *d;
+
+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
+	SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
+	SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
+	SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
+	SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
+	SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
+	SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
+	SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
+	SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
+	SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
+	SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
+	SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
+	SQUASHFS_SWAP((s)->flags, d, 288, 8);\
+	SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
+	SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
+	SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
+	SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
+	SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
+	SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
+	SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
+	SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
+	SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
+	SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
+	SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
+	SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
+	SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
+	SQUASHFS_SWAP((s)->unused, d, 888, 64);\
+}
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+	SQUASHFS_MEMSET(s, d, n);\
+	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+	SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+	SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+	SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+	SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+	SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_ipc_inode_header))\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_dev_inode_header)); \
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_symlink_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_reg_inode_header));\
+	SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
+	SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
+	SQUASHFS_SWAP((s)->offset, d, 192, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
+}
+
+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_lreg_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
+	SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
+	SQUASHFS_SWAP((s)->offset, d, 224, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_dir_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
+	SQUASHFS_SWAP((s)->offset, d, 147, 13);\
+	SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
+	SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+			sizeof(struct squashfs_ldir_inode_header));\
+	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
+	SQUASHFS_SWAP((s)->offset, d, 155, 13);\
+	SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
+	SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
+	SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
+	SQUASHFS_SWAP((s)->index, d, 0, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
+	SQUASHFS_SWAP((s)->size, d, 64, 8);\
+}
+
+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
+	SQUASHFS_SWAP((s)->count, d, 0, 8);\
+	SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
+	SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
+	SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+	SQUASHFS_SWAP((s)->type, d, 13, 3);\
+	SQUASHFS_SWAP((s)->size, d, 16, 8);\
+	SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
+	SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
+	SQUASHFS_SWAP((s)->size, d, 64, 32);\
+}
+
+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * 2);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			16)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
+}
+
+#define SQUASHFS_SWAP_INTS(s, d, n) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * 4);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			32)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
+}
+
+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * 8);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			64)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
+}
+
+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
+	int entry;\
+	int bit_position;\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, n * bits / 8);\
+	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+			bits)\
+		SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+
+struct squashfs_base_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned int		type:4;
+	unsigned int		offset:4;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned short		rdev;
+} __attribute__ ((packed));
+	
+struct squashfs_symlink_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned short		symlink_size;
+	char			symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned int		mtime;
+	unsigned int		start_block;
+	unsigned int		file_size:32;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_1 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:4; /* index into uid table */
+	unsigned int		guid:4; /* index into guid table */
+	unsigned int		file_size:19;
+	unsigned int		offset:13;
+	unsigned int		mtime;
+	unsigned int		start_block:24;
+} __attribute__  ((packed));
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
+	SQUASHFS_MEMSET(s, d, n);\
+	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+	SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+	SQUASHFS_SWAP((s)->uid, d, 16, 4);\
+	SQUASHFS_SWAP((s)->guid, d, 20, 4);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_ipc_inode_header_1));\
+	SQUASHFS_SWAP((s)->type, d, 24, 4);\
+	SQUASHFS_SWAP((s)->offset, d, 28, 4);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_dev_inode_header_1));\
+	SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_symlink_inode_header_1));\
+	SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_reg_inode_header_1));\
+	SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 88, SQUASHFS_MAX_FILE_SIZE_LOG);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+			sizeof(struct squashfs_dir_inode_header_1));\
+	SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
+	SQUASHFS_SWAP((s)->offset, d, 43, 13);\
+	SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
+}
+
+#endif
+
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+
+struct squashfs_dir_index_2 {
+	unsigned int		index:27;
+	unsigned int		start_block:29;
+	unsigned char		size;
+	unsigned char		name[0];
+} __attribute__ ((packed));
+
+struct squashfs_base_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned short		rdev;
+} __attribute__ ((packed));
+	
+struct squashfs_symlink_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned short		symlink_size;
+	char			symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned int		mtime;
+	unsigned int		start_block;
+	unsigned int		fragment;
+	unsigned int		offset;
+	unsigned int		file_size:32;
+	unsigned short		block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned int		file_size:19;
+	unsigned int		offset:13;
+	unsigned int		mtime;
+	unsigned int		start_block:24;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header_2 {
+	unsigned int		inode_type:4;
+	unsigned int		mode:12; /* protection */
+	unsigned int		uid:8; /* index into uid table */
+	unsigned int		guid:8; /* index into guid table */
+	unsigned int		file_size:27;
+	unsigned int		offset:13;
+	unsigned int		mtime;
+	unsigned int		start_block:24;
+	unsigned int		i_count:16;
+	struct squashfs_dir_index_2	index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header_2 {
+	struct squashfs_base_inode_header_2	base;
+	struct squashfs_dev_inode_header_2	dev;
+	struct squashfs_symlink_inode_header_2	symlink;
+	struct squashfs_reg_inode_header_2	reg;
+	struct squashfs_dir_inode_header_2	dir;
+	struct squashfs_ldir_inode_header_2	ldir;
+	struct squashfs_ipc_inode_header_2	ipc;
+};
+	
+struct squashfs_dir_header_2 {
+	unsigned int		count:8;
+	unsigned int		start_block:24;
+} __attribute__ ((packed));
+
+struct squashfs_dir_entry_2 {
+	unsigned int		offset:13;
+	unsigned int		type:3;
+	unsigned int		size:8;
+	char			name[0];
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry_2 {
+	unsigned int		start_block;
+	unsigned int		size;
+} __attribute__ ((packed));
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+	SQUASHFS_MEMSET(s, d, n);\
+	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+	SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+	SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+	SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
+	SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_dev_inode_header_2)); \
+	SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_symlink_inode_header_2));\
+	SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_reg_inode_header_2));\
+	SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
+	SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
+	SQUASHFS_SWAP((s)->offset, d, 128, 32);\
+	SQUASHFS_SWAP((s)->file_size, d, 160, SQUASHFS_MAX_FILE_SIZE_LOG);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_dir_inode_header_2));\
+	SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
+	SQUASHFS_SWAP((s)->offset, d, 51, 13);\
+	SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+			sizeof(struct squashfs_ldir_inode_header_2));\
+	SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
+	SQUASHFS_SWAP((s)->offset, d, 59, 13);\
+	SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
+	SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
+	SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
+	SQUASHFS_SWAP((s)->index, d, 0, 27);\
+	SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
+	SQUASHFS_SWAP((s)->size, d, 56, 8);\
+}
+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
+	SQUASHFS_SWAP((s)->count, d, 0, 8);\
+	SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
+	SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+	SQUASHFS_SWAP((s)->type, d, 13, 3);\
+	SQUASHFS_SWAP((s)->size, d, 16, 8);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
+	SQUASHFS_SWAP_START\
+	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
+	SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
+	SQUASHFS_SWAP((s)->size, d, 32, 32);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES_2(A)	(A * sizeof(struct squashfs_fragment_entry_2))
+
+#define SQUASHFS_FRAGMENT_INDEX_2(A)	(SQUASHFS_FRAGMENT_BYTES_2(A) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)	(SQUASHFS_FRAGMENT_BYTES_2(A) % \
+						SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES_2(A)	((SQUASHFS_FRAGMENT_BYTES_2(A) + \
+					SQUASHFS_METADATA_SIZE - 1) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)	(SQUASHFS_FRAGMENT_INDEXES_2(A) *\
+						sizeof(int))
+
+#endif
+
+#ifdef __KERNEL__
+
+/*
+ * macros used to swap each structure entry, taking into account
+ * bitfields and different bitfield placing conventions on differing
+ * architectures
+ */
+
+#include <asm/byteorder.h>
+
+#ifdef __BIG_ENDIAN
+	/* convert from little endian to big endian */
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+		tbits, b_pos)
+#else
+	/* convert from big endian to little endian */ 
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+		tbits, 64 - tbits - b_pos)
+#endif
+
+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
+	b_pos = pos % 8;\
+	val = 0;\
+	s = (unsigned char *)p + (pos / 8);\
+	d = ((unsigned char *) &val) + 7;\
+	for(bits = 0; bits < (tbits + b_pos); bits += 8) \
+		*d-- = *s++;\
+	value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
+}
+
+#define SQUASHFS_MEMSET(s, d, n)	memset(s, 0, n);
+
+#endif
+#endif
diff --git a/stage2/squashfs_zlib.c b/stage2/squashfs_zlib.c
new file mode 100644
--- /dev/null
+++ b/stage2/squashfs_zlib.c
@@ -0,0 +1,1679 @@
+
+#include "shared.h"
+#include "squashfs_zlib.h"
+
+
+
+
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+   Based on testing to date,
+   Pre-increment preferred for:
+   - PowerPC G3 (Adler)
+   - MIPS R5000 (Randers-Pehrson)
+   Post-increment preferred for:
+   - none
+   No measurable difference:
+   - Pentium III (Anderson)
+   - M68060 (Nikl)
+ */
+#ifdef POSTINC
+#  define OFF 0
+#  define PUP(a) *(a)++
+#else
+#  define OFF 1
+#  define PUP(a) *++(a)
+#endif
+
+/*
+   Decode literal, length, and distance codes and write out the resulting
+   literal and match bytes until either not enough input or output is
+   available, an end-of-block is encountered, or a data error is encountered.
+   When large enough input and output buffers are supplied to inflate(), for
+   example, a 16K input buffer and a 64K output buffer, more than 95% of the
+   inflate execution time is spent in this routine.
+
+   Entry assumptions:
+
+        state->mode == LEN
+        strm->avail_in >= 6
+        strm->avail_out >= 258
+        start >= strm->avail_out
+        state->bits < 8
+
+   On return, state->mode is one of:
+
+        LEN -- ran out of enough output space or enough available input
+        TYPE -- reached end of block code, inflate() to interpret next block
+        BAD -- error in block data
+
+   Notes:
+
+    - The maximum input bits used by a length/distance pair is 15 bits for the
+      length code, 5 bits for the length extra, 15 bits for the distance code,
+      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
+      Therefore if strm->avail_in >= 6, then there is enough input to avoid
+      checking for available input while decoding.
+
+    - The maximum bytes that a single length/distance pair can output is 258
+      bytes, which is the maximum length that can be coded.  inflate_fast()
+      requires strm->avail_out >= 258 for each loop to avoid checking for
+      output space.
+
+    - @start:	inflate()'s starting value for strm->avail_out
+ */
+static void inflate_fast(z_streamp strm, unsigned start)
+{
+    struct inflate_state *state;
+    unsigned char *in;      /* local strm->next_in */
+    unsigned char *last;    /* while in < last, enough input available */
+    unsigned char *out;     /* local strm->next_out */
+    unsigned char *beg;     /* inflate()'s initial strm->next_out */
+    unsigned char *end;     /* while out < end, enough space available */
+#ifdef INFLATE_STRICT
+    unsigned dmax;              /* maximum distance from zlib header */
+#endif
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned write;             /* window write index */
+    unsigned char *window;  /* allocated sliding window, if wsize != 0 */
+    unsigned long hold;         /* local strm->hold */
+    unsigned bits;              /* local strm->bits */
+    code const *lcode;      /* local strm->lencode */
+    code const *dcode;      /* local strm->distcode */
+    unsigned lmask;             /* mask for first level of length codes */
+    unsigned dmask;             /* mask for first level of distance codes */
+    code this;                  /* retrieved table entry */
+    unsigned op;                /* code bits, operation, extra bits, or */
+                                /*  window position, window bytes to copy */
+    unsigned len;               /* match length, unused bytes */
+    unsigned dist;              /* match distance */
+    unsigned char *from;    /* where to copy match from */
+
+    /* copy state to local variables */
+    state = (struct inflate_state *)strm->state;
+    in = strm->next_in - OFF;
+    last = in + (strm->avail_in - 5);
+    out = strm->next_out - OFF;
+    beg = out - (start - strm->avail_out);
+    end = out + (strm->avail_out - 257);
+#ifdef INFLATE_STRICT
+    dmax = state->dmax;
+#endif
+    wsize = state->wsize;
+    whave = state->whave;
+    write = state->write;
+    window = state->window;
+    hold = state->hold;
+    bits = state->bits;
+    lcode = state->lencode;
+    dcode = state->distcode;
+    lmask = (1U << state->lenbits) - 1;
+    dmask = (1U << state->distbits) - 1;
+
+    /* decode literals and length/distances until end-of-block or not enough
+       input data or output space */
+    do {
+        if (bits < 15) {
+            hold += (unsigned long)(PUP(in)) << bits;
+            bits += 8;
+            hold += (unsigned long)(PUP(in)) << bits;
+            bits += 8;
+        }
+        this = lcode[hold & lmask];
+      dolen:
+        op = (unsigned)(this.bits);
+        hold >>= op;
+        bits -= op;
+        op = (unsigned)(this.op);
+        if (op == 0) {                          /* literal */
+            PUP(out) = (unsigned char)(this.val);
+        }
+        else if (op & 16) {                     /* length base */
+            len = (unsigned)(this.val);
+            op &= 15;                           /* number of extra bits */
+            if (op) {
+                if (bits < op) {
+                    hold += (unsigned long)(PUP(in)) << bits;
+                    bits += 8;
+                }
+                len += (unsigned)hold & ((1U << op) - 1);
+                hold >>= op;
+                bits -= op;
+            }
+            if (bits < 15) {
+                hold += (unsigned long)(PUP(in)) << bits;
+                bits += 8;
+                hold += (unsigned long)(PUP(in)) << bits;
+                bits += 8;
+            }
+            this = dcode[hold & dmask];
+          dodist:
+            op = (unsigned)(this.bits);
+            hold >>= op;
+            bits -= op;
+            op = (unsigned)(this.op);
+            if (op & 16) {                      /* distance base */
+                dist = (unsigned)(this.val);
+                op &= 15;                       /* number of extra bits */
+                if (bits < op) {
+                    hold += (unsigned long)(PUP(in)) << bits;
+                    bits += 8;
+                    if (bits < op) {
+                        hold += (unsigned long)(PUP(in)) << bits;
+                        bits += 8;
+                    }
+                }
+                dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+                if (dist > dmax) {
+                    strm->msg = (char *)"invalid distance too far back";
+                    state->mode = BAD;
+                    break;
+                }
+#endif
+                hold >>= op;
+                bits -= op;
+                op = (unsigned)(out - beg);     /* max distance in output */
+                if (dist > op) {                /* see if copy from window */
+                    op = dist - op;             /* distance back in window */
+                    if (op > whave) {
+                        strm->msg = (char *)"invalid distance too far back";
+                        state->mode = BAD;
+                        break;
+                    }
+                    from = window - OFF;
+                    if (write == 0) {           /* very common case */
+                        from += wsize - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    else if (write < op) {      /* wrap around window */
+                        from += wsize + write - op;
+                        op -= write;
+                        if (op < len) {         /* some from end of window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = window - OFF;
+                            if (write < len) {  /* some from start of window */
+                                op = write;
+                                len -= op;
+                                do {
+                                    PUP(out) = PUP(from);
+                                } while (--op);
+                                from = out - dist;      /* rest from output */
+                            }
+                        }
+                    }
+                    else {                      /* contiguous in window */
+                        from += write - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    while (len > 2) {
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        len -= 3;
+                    }
+                    if (len) {
+                        PUP(out) = PUP(from);
+                        if (len > 1)
+                            PUP(out) = PUP(from);
+                    }
+                }
+                else {
+                    from = out - dist;          /* copy direct from output */
+                    do {                        /* minimum length is three */
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        len -= 3;
+                    } while (len > 2);
+                    if (len) {
+                        PUP(out) = PUP(from);
+                        if (len > 1)
+                            PUP(out) = PUP(from);
+                    }
+                }
+            }
+            else if ((op & 64) == 0) {          /* 2nd level distance code */
+                this = dcode[this.val + (hold & ((1U << op) - 1))];
+                goto dodist;
+            }
+            else {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+        }
+        else if ((op & 64) == 0) {              /* 2nd level length code */
+            this = lcode[this.val + (hold & ((1U << op) - 1))];
+            goto dolen;
+        }
+        else if (op & 32) {                     /* end-of-block */
+            state->mode = TYPE;
+            break;
+        }
+        else {
+            strm->msg = (char *)"invalid literal/length code";
+            state->mode = BAD;
+            break;
+        }
+    } while (in < last && out < end);
+
+    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+    len = bits >> 3;
+    in -= len;
+    bits -= len << 3;
+    hold &= (1U << bits) - 1;
+
+    /* update state and return */
+    strm->next_in = in + OFF;
+    strm->next_out = out + OFF;
+    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+    strm->avail_out = (unsigned)(out < end ?
+                                 257 + (end - out) : 257 - (out - end));
+    state->hold = hold;
+    state->bits = bits;
+    return;
+}
+
+/*
+   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+   - Using bit fields for code structure
+   - Different op definition to avoid & for extra bits (do & for table bits)
+   - Three separate decoding do-loops for direct, window, and write == 0
+   - Special case for distance > 1 copies to do overlapped load and store copy
+   - Explicit branch predictions (based on measured branch probabilities)
+   - Deferring match copy and interspersed it with decoding subsequent codes
+   - Swapping literal/length else
+   - Swapping window/direct else
+   - Larger unrolled copy loops (three is about right)
+   - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
+
+
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+
+#define MAXBITS 15
+
+/*
+   Build a set of tables to decode the provided canonical Huffman code.
+   The code lengths are lens[0..codes-1].  The result starts at *table,
+   whose indices are 0..2^bits-1.  work is a writable array of at least
+   lens shorts, which is used as a work area.  type is the type of code
+   to be generated, CODES, LENS, or DISTS.  On return, zero is success,
+   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table
+   on return points to the next available entry's address.  bits is the
+   requested root table index bits, and on return it is the actual root
+   table index bits.  It will differ if the request is greater than the
+   longest code or if it is less than the shortest code.
+ */
+int zlib_inflate_table(codetype type, unsigned short *lens, unsigned codes,
+			code **table, unsigned *bits, unsigned short *work)
+{
+    unsigned len;               /* a code's length in bits */
+    unsigned sym;               /* index of code symbols */
+    unsigned min, max;          /* minimum and maximum code lengths */
+    unsigned root;              /* number of index bits for root table */
+    unsigned curr;              /* number of index bits for current table */
+    unsigned drop;              /* code bits to drop for sub-table */
+    int left;                   /* number of prefix codes available */
+    unsigned used;              /* code entries in table used */
+    unsigned huff;              /* Huffman code */
+    unsigned incr;              /* for incrementing code, index */
+    unsigned fill;              /* index for replicating entries */
+    unsigned low;               /* low bits for current root entry */
+    unsigned mask;              /* mask for low root bits */
+    code this;                  /* table entry for duplication */
+    code *next;             /* next available space in table */
+    const unsigned short *base;     /* base value table to use */
+    const unsigned short *extra;    /* extra bits table to use */
+    int end;                    /* use base and extra for symbol > end */
+    unsigned short count[MAXBITS+1];    /* number of codes of each length */
+    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */
+    static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+    static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+        16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196};
+    static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577, 0, 0};
+    static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+        16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+        23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+        28, 28, 29, 29, 64, 64};
+
+    /*
+       Process a set of code lengths to create a canonical Huffman code.  The
+       code lengths are lens[0..codes-1].  Each length corresponds to the
+       symbols 0..codes-1.  The Huffman code is generated by first sorting the
+       symbols by length from short to long, and retaining the symbol order
+       for codes with equal lengths.  Then the code starts with all zero bits
+       for the first code of the shortest length, and the codes are integer
+       increments for the same length, and zeros are appended as the length
+       increases.  For the deflate format, these bits are stored backwards
+       from their more natural integer increment ordering, and so when the
+       decoding tables are built in the large loop below, the integer codes
+       are incremented backwards.
+
+       This routine assumes, but does not check, that all of the entries in
+       lens[] are in the range 0..MAXBITS.  The caller must assure this.
+       1..MAXBITS is interpreted as that code length.  zero means that that
+       symbol does not occur in this code.
+
+       The codes are sorted by computing a count of codes for each length,
+       creating from that a table of starting indices for each length in the
+       sorted table, and then entering the symbols in order in the sorted
+       table.  The sorted table is work[], with that space being provided by
+       the caller.
+
+       The length counts are used for other purposes as well, i.e. finding
+       the minimum and maximum length codes, determining if there are any
+       codes at all, checking for a valid set of lengths, and looking ahead
+       at length counts to determine sub-table sizes when building the
+       decoding tables.
+     */
+
+    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+    for (len = 0; len <= MAXBITS; len++)
+        count[len] = 0;
+    for (sym = 0; sym < codes; sym++)
+        count[lens[sym]]++;
+
+    /* bound code lengths, force root to be within code lengths */
+    root = *bits;
+    for (max = MAXBITS; max >= 1; max--)
+        if (count[max] != 0) break;
+    if (root > max) root = max;
+    if (max == 0) {                     /* no symbols to code at all */
+        this.op = (unsigned char)64;    /* invalid code marker */
+        this.bits = (unsigned char)1;
+        this.val = (unsigned short)0;
+        *(*table)++ = this;             /* make a table to force an error */
+        *(*table)++ = this;
+        *bits = 1;
+        return 0;     /* no symbols, but wait for decoding to report error */
+    }
+    for (min = 1; min <= MAXBITS; min++)
+        if (count[min] != 0) break;
+    if (root < min) root = min;
+
+    /* check for an over-subscribed or incomplete set of lengths */
+    left = 1;
+    for (len = 1; len <= MAXBITS; len++) {
+        left <<= 1;
+        left -= count[len];
+        if (left < 0) return -1;        /* over-subscribed */
+    }
+    if (left > 0 && (type == CODES || max != 1))
+        return -1;                      /* incomplete set */
+
+    /* generate offsets into symbol table for each length for sorting */
+    offs[1] = 0;
+    for (len = 1; len < MAXBITS; len++)
+        offs[len + 1] = offs[len] + count[len];
+
+    /* sort symbols by length, by symbol order within each length */
+    for (sym = 0; sym < codes; sym++)
+        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+    /*
+       Create and fill in decoding tables.  In this loop, the table being
+       filled is at next and has curr index bits.  The code being used is huff
+       with length len.  That code is converted to an index by dropping drop
+       bits off of the bottom.  For codes where len is less than drop + curr,
+       those top drop + curr - len bits are incremented through all values to
+       fill the table with replicated entries.
+
+       root is the number of index bits for the root table.  When len exceeds
+       root, sub-tables are created pointed to by the root entry with an index
+       of the low root bits of huff.  This is saved in low to check for when a
+       new sub-table should be started.  drop is zero when the root table is
+       being filled, and drop is root when sub-tables are being filled.
+
+       When a new sub-table is needed, it is necessary to look ahead in the
+       code lengths to determine what size sub-table is needed.  The length
+       counts are used for this, and so count[] is decremented as codes are
+       entered in the tables.
+
+       used keeps track of how many table entries have been allocated from the
+       provided *table space.  It is checked when a LENS table is being made
+       against the space in *table, ENOUGH, minus the maximum space needed by
+       the worst case distance code, MAXD.  This should never happen, but the
+       sufficiency of ENOUGH has not been proven exhaustively, hence the check.
+       This assumes that when type == LENS, bits == 9.
+
+       sym increments through all symbols, and the loop terminates when
+       all codes of length max, i.e. all codes, have been processed.  This
+       routine permits incomplete codes, so another loop after this one fills
+       in the rest of the decoding tables with invalid code markers.
+     */
+
+    /* set up for code type */
+    switch (type) {
+    case CODES:
+        base = extra = work;    /* dummy value--not used */
+        end = 19;
+        break;
+    case LENS:
+        base = lbase;
+        base -= 257;
+        extra = lext;
+        extra -= 257;
+        end = 256;
+        break;
+    default:            /* DISTS */
+        base = dbase;
+        extra = dext;
+        end = -1;
+    }
+
+    /* initialize state for loop */
+    huff = 0;                   /* starting code */
+    sym = 0;                    /* starting code symbol */
+    len = min;                  /* starting code length */
+    next = *table;              /* current table to fill in */
+    curr = root;                /* current table index bits */
+    drop = 0;                   /* current bits to drop from code for index */
+    low = (unsigned)(-1);       /* trigger new sub-table when len > root */
+    used = 1U << root;          /* use root table entries */
+    mask = used - 1;            /* mask for comparing low */
+
+    /* check available table space */
+    if (type == LENS && used >= ENOUGH - MAXD)
+        return 1;
+
+    /* process all codes and make table entries */
+    for (;;) {
+        /* create table entry */
+        this.bits = (unsigned char)(len - drop);
+        if ((int)(work[sym]) < end) {
+            this.op = (unsigned char)0;
+            this.val = work[sym];
+        }
+        else if ((int)(work[sym]) > end) {
+            this.op = (unsigned char)(extra[work[sym]]);
+            this.val = base[work[sym]];
+        }
+        else {
+            this.op = (unsigned char)(32 + 64);         /* end of block */
+            this.val = 0;
+        }
+
+        /* replicate for those indices with low len bits equal to huff */
+        incr = 1U << (len - drop);
+        fill = 1U << curr;
+        min = fill;                 /* save offset to next table */
+        do {
+            fill -= incr;
+            next[(huff >> drop) + fill] = this;
+        } while (fill != 0);
+
+        /* backwards increment the len-bit code huff */
+        incr = 1U << (len - 1);
+        while (huff & incr)
+            incr >>= 1;
+        if (incr != 0) {
+            huff &= incr - 1;
+            huff += incr;
+        }
+        else
+            huff = 0;
+
+        /* go to next symbol, update count, len */
+        sym++;
+        if (--(count[len]) == 0) {
+            if (len == max) break;
+            len = lens[work[sym]];
+        }
+
+        /* create new sub-table if needed */
+        if (len > root && (huff & mask) != low) {
+            /* if first time, transition to sub-tables */
+            if (drop == 0)
+                drop = root;
+
+            /* increment past last table */
+            next += min;            /* here min is 1 << curr */
+
+            /* determine length of next table */
+            curr = len - drop;
+            left = (int)(1 << curr);
+            while (curr + drop < max) {
+                left -= count[curr + drop];
+                if (left <= 0) break;
+                curr++;
+                left <<= 1;
+            }
+
+            /* check for enough space */
+            used += 1U << curr;
+            if (type == LENS && used >= ENOUGH - MAXD)
+                return 1;
+
+            /* point entry in root table to sub-table */
+            low = huff & mask;
+            (*table)[low].op = (unsigned char)curr;
+            (*table)[low].bits = (unsigned char)root;
+            (*table)[low].val = (unsigned short)(next - *table);
+        }
+    }
+
+    /*
+       Fill in rest of table for incomplete codes.  This loop is similar to the
+       loop above in incrementing huff for table indices.  It is assumed that
+       len is equal to curr + drop, so there is no loop needed to increment
+       through high index bits.  When the current sub-table is filled, the loop
+       drops back to the root table to fill in any remaining entries there.
+     */
+    this.op = (unsigned char)64;                /* invalid code marker */
+    this.bits = (unsigned char)(len - drop);
+    this.val = (unsigned short)0;
+    while (huff != 0) {
+        /* when done with sub-table, drop back to root table */
+        if (drop != 0 && (huff & mask) != low) {
+            drop = 0;
+            len = root;
+            next = *table;
+            this.bits = (unsigned char)len;
+        }
+
+        /* put invalid code marker in table */
+        next[huff >> drop] = this;
+
+        /* backwards increment the len-bit code huff */
+        incr = 1U << (len - 1);
+        while (huff & incr)
+            incr >>= 1;
+        if (incr != 0) {
+            huff &= incr - 1;
+            huff += incr;
+        }
+        else
+            huff = 0;
+    }
+
+    /* set return parameters */
+    *table += used;
+    *bits = root;
+    return 0;
+}
+
+
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Based on zlib 1.2.3 but modified for the Linux Kernel by
+ * Richard Purdie <richard@openedhand.com>
+ *
+ * Changes mainly for static instead of dynamic memory allocation
+ *
+ */
+
+int zlib_inflate_workspacesize(void)
+{
+    return sizeof(struct inflate_workspace);
+}
+
+int zlib_inflateReset(z_streamp strm)
+{
+    struct inflate_state *state;
+
+    if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state *)strm->state;
+    strm->total_in = strm->total_out = state->total = 0;
+    strm->msg = NULL;
+    strm->adler = 1;        /* to support ill-conceived Java test suite */
+    state->mode = HEAD;
+    state->last = 0;
+    state->havedict = 0;
+    state->dmax = 32768U;
+    state->hold = 0;
+    state->bits = 0;
+    state->lencode = state->distcode = state->next = state->codes;
+
+    /* Initialise Window */
+    state->wsize = 1U << state->wbits;
+    state->write = 0;
+    state->whave = 0;
+
+    return Z_OK;
+}
+
+#if 0
+int zlib_inflatePrime(z_streamp strm, int bits, int value)
+{
+    struct inflate_state *state;
+
+    if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state *)strm->state;
+    if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR;
+    value &= (1L << bits) - 1;
+    state->hold += value << state->bits;
+    state->bits += bits;
+    return Z_OK;
+}
+#endif
+
+int zlib_inflateInit2(z_streamp strm, int windowBits)
+{
+    struct inflate_state *state;
+
+    if (strm == NULL) return Z_STREAM_ERROR;
+    strm->msg = NULL;                 /* in case we return an error */
+
+    state = &WS(strm)->inflate_state;
+    strm->state = (struct internal_state *)state;
+
+    if (windowBits < 0) {
+        state->wrap = 0;
+        windowBits = -windowBits;
+    }
+    else {
+        state->wrap = (windowBits >> 4) + 1;
+    }
+    if (windowBits < 8 || windowBits > 15) {
+        return Z_STREAM_ERROR;
+    }
+    state->wbits = (unsigned)windowBits;
+    state->window = &WS(strm)->working_window[0];
+
+    return zlib_inflateReset(strm);
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  This returns fixed tables from inffixed.h.
+ */
+static void zlib_fixedtables(struct inflate_state *state)
+{
+    /* inffixed.h -- table for decoding fixed codes
+     * Generated automatically by makefixed().
+     */
+
+    /* WARNING: this file should *not* be used by applications. It
+       is part of the implementation of the compression library and
+       is subject to change. Applications should only use zlib.h.
+     */
+
+    static const code lenfix[512] = {
+        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+        {0,9,255}
+    };
+
+    static const code distfix[32] = {
+        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+        {22,5,193},{64,5,0}
+    };
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+
+/*
+   Update the window with the last wsize (normally 32K) bytes written before
+   returning. This is only called when a window is already in use, or when
+   output has been written during this inflate call, but the end of the deflate
+   stream has not been reached yet. It is also called to window dictionary data
+   when a dictionary is loaded.
+
+   Providing output buffers larger than 32K to inflate() should provide a speed
+   advantage, since only the last 32K of output is copied to the sliding window
+   upon return from inflate(), and since all distances after the first 32K of
+   output will fall in the output data, making match copies simpler and faster.
+   The advantage may be dependent on the size of the processor's data caches.
+ */
+static void zlib_updatewindow(z_streamp strm, unsigned out)
+{
+    struct inflate_state *state;
+    unsigned copy, dist;
+
+    state = (struct inflate_state *)strm->state;
+
+    /* copy state->wsize or less output bytes into the circular window */
+    copy = out - strm->avail_out;
+    if (copy >= state->wsize) {
+        memcpy(state->window, strm->next_out - state->wsize, state->wsize);
+        state->write = 0;
+        state->whave = state->wsize;
+    }
+    else {
+        dist = state->wsize - state->write;
+        if (dist > copy) dist = copy;
+        memcpy(state->window + state->write, strm->next_out - copy, dist);
+        copy -= dist;
+        if (copy) {
+            memcpy(state->window, strm->next_out - copy, copy);
+            state->write = copy;
+            state->whave = state->wsize;
+        }
+        else {
+            state->write += dist;
+            if (state->write == state->wsize) state->write = 0;
+            if (state->whave < state->wsize) state->whave += dist;
+        }
+    }
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+/*
+   Returns true if inflate is currently at the end of a block generated by
+   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+   implementation to provide an additional safety check. PPP uses
+   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+   block. When decompressing, PPP checks that at the end of input packet,
+   inflate is waiting for these length bytes.
+ */
+static int zlib_inflateSyncPacket(z_streamp strm)
+{
+    struct inflate_state *state;
+
+    if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state *)strm->state;
+
+    if (state->mode == STORED && state->bits == 0) {
+	state->mode = TYPE;
+        return Z_OK;
+    }
+    return Z_DATA_ERROR;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#define UPDATE(check, buf, len) zlib_adler32(check, buf, len)
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+   if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        if (have == 0) goto inf_leave; \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+    ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+     (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+   inflate() uses a state machine to process as much input data and generate as
+   much output data as possible before returning.  The state machine is
+   structured roughly as follows:
+
+    for (;;) switch (state) {
+    ...
+    case STATEn:
+        if (not enough input data or output space to make progress)
+            return;
+        ... make progress ...
+        state = STATEm;
+        break;
+    ...
+    }
+
+   so when inflate() is called again, the same case is attempted again, and
+   if the appropriate resources are provided, the machine proceeds to the
+   next state.  The NEEDBITS() macro is usually the way the state evaluates
+   whether it can proceed or should return.  NEEDBITS() does the return if
+   the requested bits are not available.  The typical use of the BITS macros
+   is:
+
+        NEEDBITS(n);
+        ... do something with BITS(n) ...
+        DROPBITS(n);
+
+   where NEEDBITS(n) either returns from inflate() if there isn't enough
+   input left to load n bits into the accumulator, or it continues.  BITS(n)
+   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops
+   the low n bits off the accumulator.  INITBITS() clears the accumulator
+   and sets the number of available bits to zero.  BYTEBITS() discards just
+   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()
+   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+   if there is no input available.  The decoding of variable length codes uses
+   PULLBYTE() directly in order to pull just enough bytes to decode the next
+   code, and no more.
+
+   Some states loop until they get enough input, making sure that enough
+   state information is maintained to continue the loop where it left off
+   if NEEDBITS() returns in the loop.  For example, want, need, and keep
+   would all have to actually be part of the saved state in case NEEDBITS()
+   returns:
+
+    case STATEw:
+        while (want < need) {
+            NEEDBITS(n);
+            keep[want++] = BITS(n);
+            DROPBITS(n);
+        }
+        state = STATEx;
+    case STATEx:
+
+   As shown above, if the next state is also the next case, then the break
+   is omitted.
+
+   A state may also return if there is not enough output space available to
+   complete that state.  Those states are copying stored data, writing a
+   literal byte, and copying a matching string.
+
+   When returning, a "goto inf_leave" is used to update the total counters,
+   update the check value, and determine whether any progress has been made
+   during that inflate() call in order to return the proper return code.
+   Progress is defined as a change in either strm->avail_in or strm->avail_out.
+   When there is a window, goto inf_leave will update the window with the last
+   output written.  If a goto inf_leave occurs in the middle of decompression
+   and there is no window currently, goto inf_leave will create one and copy
+   output to the window for the next call of inflate().
+
+   In this implementation, the flush parameter of inflate() only affects the
+   return code (per zlib.h).  inflate() always writes as much as possible to
+   strm->next_out, given the space available and the provided input--the effect
+   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers
+   the allocation of and copying into a sliding window until necessary, which
+   provides the effect documented in zlib.h for Z_FINISH when the entire input
+   stream available.  So the only thing the flush parameter actually does is:
+   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it
+   will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int zlib_inflate(z_streamp strm, int flush)
+{
+    struct inflate_state *state;
+    unsigned char *next;    /* next input */
+    unsigned char *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned in, out;           /* save starting available input and output */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char *from;    /* where to copy match bytes from */
+    code this;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    /* Do not check for strm->next_out == NULL here as ppc zImage
+       inflates to strm->next_out = 0 */
+
+    if (strm == NULL || strm->state == NULL ||
+        (strm->next_in == NULL && strm->avail_in != 0))
+        return Z_STREAM_ERROR;
+
+    state = (struct inflate_state *)strm->state;
+
+    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */
+    LOAD();
+    in = have;
+    out = left;
+    ret = Z_OK;
+    for (;;)
+        switch (state->mode) {
+        case HEAD:
+            if (state->wrap == 0) {
+                state->mode = TYPEDO;
+                break;
+            }
+            NEEDBITS(16);
+            if (
+                ((BITS(8) << 8) + (hold >> 8)) % 31) {
+                strm->msg = (char *)"incorrect header check";
+                state->mode = BAD;
+                break;
+            }
+            if (BITS(4) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            DROPBITS(4);
+            len = BITS(4) + 8;
+            if (len > state->wbits) {
+                strm->msg = (char *)"invalid window size";
+                state->mode = BAD;
+                break;
+            }
+            state->dmax = 1U << len;
+            strm->adler = state->check = zlib_adler32(0L, NULL, 0);
+            state->mode = hold & 0x200 ? DICTID : TYPE;
+            INITBITS();
+            break;
+        case DICTID:
+            NEEDBITS(32);
+            strm->adler = state->check = REVERSE(hold);
+            INITBITS();
+            state->mode = DICT;
+        case DICT:
+            if (state->havedict == 0) {
+                RESTORE();
+                return Z_NEED_DICT;
+            }
+            strm->adler = state->check = zlib_adler32(0L, NULL, 0);
+            state->mode = TYPE;
+        case TYPE:
+            if (flush == Z_BLOCK) goto inf_leave;
+        case TYPEDO:
+            if (state->last) {
+                BYTEBITS();
+                state->mode = CHECK;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                zlib_fixedtables(state);
+                state->mode = LEN;              /* decode codes */
+                break;
+            case 2:                             /* dynamic block */
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+        case STORED:
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            INITBITS();
+            state->mode = COPY;
+        case COPY:
+            copy = state->length;
+            if (copy) {
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                if (copy == 0) goto inf_leave;
+                memcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+                break;
+            }
+            state->mode = TYPE;
+            break;
+        case TABLE:
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            state->have = 0;
+            state->mode = LENLENS;
+        case LENLENS:
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (code const *)(state->next);
+            state->lenbits = 7;
+            ret = zlib_inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->have = 0;
+            state->mode = CODELENS;
+        case CODELENS:
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    this = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (this.val < 16) {
+                    NEEDBITS(this.bits);
+                    DROPBITS(this.bits);
+                    state->lens[state->have++] = this.val;
+                }
+                else {
+                    if (this.val == 16) {
+                        NEEDBITS(this.bits + 2);
+                        DROPBITS(this.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = state->lens[state->have - 1];
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (this.val == 17) {
+                        NEEDBITS(this.bits + 3);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(this.bits + 7);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* handle error breaks in while */
+            if (state->mode == BAD) break;
+
+            /* build code tables */
+            state->next = state->codes;
+            state->lencode = (code const *)(state->next);
+            state->lenbits = 9;
+            ret = zlib_inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (code const *)(state->next);
+            state->distbits = 6;
+            ret = zlib_inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            state->mode = LEN;
+        case LEN:
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                inflate_fast(strm, out);
+                LOAD();
+                break;
+            }
+            for (;;) {
+                this = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (this.op && (this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            state->length = (unsigned)this.val;
+            if ((int)(this.op) == 0) {
+                state->mode = LIT;
+                break;
+            }
+            if (this.op & 32) {
+                state->mode = TYPE;
+                break;
+            }
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+            state->extra = (unsigned)(this.op) & 15;
+            state->mode = LENEXT;
+        case LENEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            state->mode = DIST;
+        case DIST:
+            for (;;) {
+                this = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)this.val;
+            state->extra = (unsigned)(this.op) & 15;
+            state->mode = DISTEXT;
+        case DISTEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+#ifdef INFLATE_STRICT
+            if (state->offset > state->dmax) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            if (state->offset > state->whave + out - left) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+            state->mode = MATCH;
+        case MATCH:
+            if (left == 0) goto inf_leave;
+            copy = out - left;
+            if (state->offset > copy) {         /* copy from window */
+                copy = state->offset - copy;
+                if (copy > state->write) {
+                    copy -= state->write;
+                    from = state->window + (state->wsize - copy);
+                }
+                else
+                    from = state->window + (state->write - copy);
+                if (copy > state->length) copy = state->length;
+            }
+            else {                              /* copy from output */
+                from = put - state->offset;
+                copy = state->length;
+            }
+            if (copy > left) copy = left;
+            left -= copy;
+            state->length -= copy;
+            do {
+                *put++ = *from++;
+            } while (--copy);
+            if (state->length == 0) state->mode = LEN;
+            break;
+        case LIT:
+            if (left == 0) goto inf_leave;
+            *put++ = (unsigned char)(state->length);
+            left--;
+            state->mode = LEN;
+            break;
+        case CHECK:
+            if (state->wrap) {
+                NEEDBITS(32);
+                out -= left;
+                strm->total_out += out;
+                state->total += out;
+                if (out)
+                    strm->adler = state->check =
+                        UPDATE(state->check, put - out, out);
+                out = left;
+                if ((
+                     REVERSE(hold)) != state->check) {
+                    strm->msg = (char *)"incorrect data check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+            }
+            state->mode = DONE;
+        case DONE:
+            ret = Z_STREAM_END;
+            goto inf_leave;
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+        case MEM:
+            return Z_MEM_ERROR;
+        case SYNC:
+        default:
+            return Z_STREAM_ERROR;
+        }
+
+    /*
+       Return from inflate(), updating the total counts and the check value.
+       If there was no progress during the inflate() call, return a buffer
+       error.  Call zlib_updatewindow() to create and/or update the window state.
+     */
+  inf_leave:
+    RESTORE();
+    if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+        zlib_updatewindow(strm, out);
+
+    in -= strm->avail_in;
+    out -= strm->avail_out;
+    strm->total_in += in;
+    strm->total_out += out;
+    state->total += out;
+    if (state->wrap && out)
+        strm->adler = state->check =
+            UPDATE(state->check, strm->next_out - out, out);
+
+    strm->data_type = state->bits + (state->last ? 64 : 0) +
+                      (state->mode == TYPE ? 128 : 0);
+    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+        ret = Z_BUF_ERROR;
+
+    if (flush == Z_PACKET_FLUSH && ret == Z_OK &&
+            (strm->avail_out != 0 || strm->avail_in == 0))
+		return zlib_inflateSyncPacket(strm);
+    return ret;
+}
+
+int zlib_inflateEnd(z_streamp strm)
+{
+    if (strm == NULL || strm->state == NULL)
+        return Z_STREAM_ERROR;
+    return Z_OK;
+}
+
+#if 0
+int zlib_inflateSetDictionary(z_streamp strm, const Byte *dictionary,
+        uInt dictLength)
+{
+    struct inflate_state *state;
+    unsigned long id;
+
+    /* check state */
+    if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state *)strm->state;
+    if (state->wrap != 0 && state->mode != DICT)
+        return Z_STREAM_ERROR;
+
+    /* check for correct dictionary id */
+    if (state->mode == DICT) {
+        id = zlib_adler32(0L, NULL, 0);
+        id = zlib_adler32(id, dictionary, dictLength);
+        if (id != state->check)
+            return Z_DATA_ERROR;
+    }
+
+    /* copy dictionary to window */
+    zlib_updatewindow(strm, strm->avail_out);
+
+    if (dictLength > state->wsize) {
+        memcpy(state->window, dictionary + dictLength - state->wsize,
+                state->wsize);
+        state->whave = state->wsize;
+    }
+    else {
+        memcpy(state->window + state->wsize - dictLength, dictionary,
+                dictLength);
+        state->whave = dictLength;
+    }
+    state->havedict = 1;
+    return Z_OK;
+}
+#endif
+
+#if 0
+/*
+   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found
+   or when out of input.  When called, *have is the number of pattern bytes
+   found in order so far, in 0..3.  On return *have is updated to the new
+   state.  If on return *have equals four, then the pattern was found 