[gcab] Add LZX decompression: thanks Wine!



commit 79f68f84bbecc02897f55ac577e4c0cecd18e619
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date:   Fri Jan 25 23:57:26 2013 +0100

    Add LZX decompression: thanks Wine!

 libgcab/cabinet.c      |   26 ++-
 libgcab/cabinet.h      |    6 +-
 libgcab/decomp.c       |  519 ++++++++++++++++++++++++++++++++++++++++++++++++
 libgcab/decomp.h       |  164 +++++++++++++++
 libgcab/gcab-cabinet.c |    2 +-
 libgcab/gcab-folder.c  |   28 ++-
 libgcab/gcab-folder.h  |    2 +-
 libgcab/gcab-priv.h    |    2 +-
 8 files changed, 733 insertions(+), 16 deletions(-)
---
diff --git a/libgcab/cabinet.c b/libgcab/cabinet.c
index 31a3cf5..6e8523c 100644
--- a/libgcab/cabinet.c
+++ b/libgcab/cabinet.c
@@ -426,15 +426,17 @@ cdata_finish (cdata_t *cd, GError **error)
 }
 
 G_GNUC_INTERNAL gboolean
-cdata_read (cdata_t *cd, u1 res_data, GCabCompression compression,
+cdata_read (cdata_t *cd, u1 res_data, gint comptype,
             GDataInputStream *in, GCancellable *cancellable, GError **error)
 
 {
     gboolean success = FALSE;
-    int zret = Z_OK;
+    int ret, zret = Z_OK;
+    gint compression = comptype & GCAB_COMPRESSION_MASK;
     gchar *buf = compression == GCAB_COMPRESSION_NONE ? cd->out : cd->in;
 
-    if (compression > GCAB_COMPRESSION_MSZIP) {
+    if (compression > GCAB_COMPRESSION_MSZIP &&
+        compression != GCAB_COMPRESSION_LZX) {
         g_set_error (error, GCAB_ERROR, GCAB_ERROR_FAILED,
                      _("unsupported compression method %d"), compression);
         return FALSE;
@@ -460,6 +462,24 @@ cdata_read (cdata_t *cd, u1 res_data, GCabCompression compression,
         PND (cd, buf, 64);
     }
 
+    if (compression == GCAB_COMPRESSION_LZX) {
+        if (cd->fdi.alloc == NULL) {
+            cd->fdi.alloc = g_malloc;
+            cd->fdi.free = g_free;
+            cd->decomp.fdi = &cd->fdi;
+            cd->decomp.inbuf = cd->in;
+            cd->decomp.outbuf = cd->out;
+
+            ret = LZXfdi_init((comptype >> 8) & 0x1f, &cd->decomp);
+            if (ret < 0)
+                goto end;
+        }
+
+        ret = LZXfdi_decomp (cd->ncbytes, cd->nubytes, &cd->decomp);
+        if (ret < 0)
+            goto end;
+    }
+
     if (compression == GCAB_COMPRESSION_MSZIP) {
         if (cd->in[0] != 'C' || cd->in[1] != 'K')
             goto end;
diff --git a/libgcab/cabinet.h b/libgcab/cabinet.h
index 79c73ec..6bb1f4e 100644
--- a/libgcab/cabinet.h
+++ b/libgcab/cabinet.h
@@ -98,7 +98,11 @@ struct cdata
     guint8 *reserved;
     guint8 in[CAB_INPUTMAX+2];
     guint8 out[CAB_BLOCKMAX];
+    /* using zlib */
     z_stream z;
+    /* using wine decomp.h */
+    FDI_Int fdi;
+    fdi_decomp_state decomp;
 };
 
 gboolean     cheader_write                      (cheader_t *ch,
@@ -136,7 +140,7 @@ gboolean     cdata_write                        (cdata_t *cd,
                                                  GError **error);
 gboolean     cdata_read                         (cdata_t *cd,
                                                  u1 res_data,
-                                                 GCabCompression compression,
+                                                 gint comptype,
                                                  GDataInputStream *in,
                                                  GCancellable *cancellable,
                                                  GError **error);
diff --git a/libgcab/decomp.c b/libgcab/decomp.c
index a3ef69f..43f59de 100644
--- a/libgcab/decomp.c
+++ b/libgcab/decomp.c
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <string.h>
 #include "decomp.h"
 
 #ifndef max
@@ -56,6 +57,7 @@ THOSE_ZIP_CONSTS;
 
 #define CAB(x) (decomp_state->x)
 #define ZIP(x) (decomp_state->methods.zip.x)
+#define LZX(x) (decomp_state->methods.lzx.x)
 
 #define ZIPNEEDBITS(n) {while(k<(n)){cab_LONG c=*(ZIP(inpos)++);\
     b|=((cab_ULONG)c)<<k;k+=8;}}
@@ -629,3 +631,520 @@ int ZIPfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state)
   /* return success */
   return 1;
 }
+
+/*************************************************************************
+ * make_decode_table (internal)
+ *
+ * This function was coded by David Tritscher. It builds a fast huffman
+ * decoding table out of just a canonical huffman code lengths table.
+ *
+ * PARAMS
+ *   nsyms:  total number of symbols in this huffman tree.
+ *   nbits:  any symbols with a code length of nbits or less can be decoded
+ *           in one lookup of the table.
+ *   length: A table to get code lengths from [0 to syms-1]
+ *   table:  The table to fill up with decoded symbols and pointers.
+ *
+ * RETURNS
+ *   OK:    0
+ *   error: 1
+ */
+static int make_decode_table(cab_ULONG nsyms, cab_ULONG nbits,
+                             const cab_UBYTE *length, cab_UWORD *table) {
+  register cab_UWORD sym;
+  register cab_ULONG leaf;
+  register cab_UBYTE bit_num = 1;
+  cab_ULONG fill;
+  cab_ULONG pos         = 0; /* the current position in the decode table */
+  cab_ULONG table_mask  = 1 << nbits;
+  cab_ULONG bit_mask    = table_mask >> 1; /* don't do 0 length codes */
+  cab_ULONG next_symbol = bit_mask; /* base of allocation for long codes */
+
+  /* fill entries for codes short enough for a direct mapping */
+  while (bit_num <= nbits) {
+    for (sym = 0; sym < nsyms; sym++) {
+      if (length[sym] == bit_num) {
+        leaf = pos;
+
+        if((pos += bit_mask) > table_mask) return 1; /* table overrun */
+
+        /* fill all possible lookups of this symbol with the symbol itself */
+        fill = bit_mask;
+        while (fill-- > 0) table[leaf++] = sym;
+      }
+    }
+    bit_mask >>= 1;
+    bit_num++;
+  }
+
+  /* if there are any codes longer than nbits */
+  if (pos != table_mask) {
+    /* clear the remainder of the table */
+    for (sym = pos; sym < table_mask; sym++) table[sym] = 0;
+
+    /* give ourselves room for codes to grow by up to 16 more bits */
+    pos <<= 16;
+    table_mask <<= 16;
+    bit_mask = 1 << 15;
+
+    while (bit_num <= 16) {
+      for (sym = 0; sym < nsyms; sym++) {
+        if (length[sym] == bit_num) {
+          leaf = pos >> 16;
+          for (fill = 0; fill < bit_num - nbits; fill++) {
+            /* if this path hasn't been taken yet, 'allocate' two entries */
+            if (table[leaf] == 0) {
+              table[(next_symbol << 1)] = 0;
+              table[(next_symbol << 1) + 1] = 0;
+              table[leaf] = next_symbol++;
+            }
+            /* follow the path and select either left or right for next bit */
+            leaf = table[leaf] << 1;
+            if ((pos >> (15-fill)) & 1) leaf++;
+          }
+          table[leaf] = sym;
+
+          if ((pos += bit_mask) > table_mask) return 1; /* table overflow */
+        }
+      }
+      bit_mask >>= 1;
+      bit_num++;
+    }
+  }
+
+  /* full table? */
+  if (pos == table_mask) return 0;
+
+  /* either erroneous table, or all elements are 0 - let's find out. */
+  for (sym = 0; sym < nsyms; sym++) if (length[sym]) return 1;
+  return 0;
+}
+
+/************************************************************
+ * fdi_lzx_read_lens (internal)
+ */
+static int fdi_lzx_read_lens(cab_UBYTE *lens, cab_ULONG first, cab_ULONG last, struct lzx_bits *lb,
+                  fdi_decomp_state *decomp_state) {
+  cab_ULONG i,j, x,y;
+  int z;
+
+  register cab_ULONG bitbuf = lb->bb;
+  register int bitsleft = lb->bl;
+  cab_UBYTE *inpos = lb->ip;
+  cab_UWORD *hufftbl;
+
+  for (x = 0; x < 20; x++) {
+    READ_BITS(y, 4);
+    LENTABLE(PRETREE)[x] = y;
+  }
+  BUILD_TABLE(PRETREE);
+
+  for (x = first; x < last; ) {
+    READ_HUFFSYM(PRETREE, z);
+    if (z == 17) {
+      READ_BITS(y, 4); y += 4;
+      while (y--) lens[x++] = 0;
+    }
+    else if (z == 18) {
+      READ_BITS(y, 5); y += 20;
+      while (y--) lens[x++] = 0;
+    }
+    else if (z == 19) {
+      READ_BITS(y, 1); y += 4;
+      READ_HUFFSYM(PRETREE, z);
+      z = lens[x] - z; if (z < 0) z += 17;
+      while (y--) lens[x++] = z;
+    }
+    else {
+      z = lens[x] - z; if (z < 0) z += 17;
+      lens[x++] = z;
+    }
+  }
+
+  lb->bb = bitbuf;
+  lb->bl = bitsleft;
+  lb->ip = inpos;
+  return 0;
+}
+
+/************************************************************
+ * LZXfdi_init (internal)
+ */
+int LZXfdi_init(int window, fdi_decomp_state *decomp_state) {
+  static const cab_UBYTE bits[]  =
+                        { 0,  0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,
+                          7,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
+                         15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+                         17, 17, 17};
+  static const cab_ULONG base[] =
+                {      0,       1,       2,       3,       4,       6,       8,      12,
+                      16,      24,      32,      48,      64,      96,     128,     192,
+                     256,     384,     512,     768,    1024,    1536,    2048,    3072,
+                    4096,    6144,    8192,   12288,   16384,   24576,   32768,   49152,
+                   65536,   98304,  131072,  196608,  262144,  393216,  524288,  655360,
+                  786432,  917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
+                 1835008, 1966080, 2097152};
+  cab_ULONG wndsize = 1 << window;
+  int posn_slots;
+
+  /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
+  /* if a previously allocated window is big enough, keep it     */
+  if (window < 15 || window > 21) return DECR_DATAFORMAT;
+  if (LZX(actual_size) < wndsize) {
+    if (LZX(window)) CAB(fdi)->free(LZX(window));
+    LZX(window) = NULL;
+  }
+  if (!LZX(window)) {
+    if (!(LZX(window) = CAB(fdi)->alloc(wndsize))) return DECR_NOMEMORY;
+    LZX(actual_size) = wndsize;
+  }
+  LZX(window_size) = wndsize;
+
+  /* initialize static tables */
+  memcpy(CAB(extra_bits), bits, sizeof(bits));
+  memcpy(CAB(lzx_position_base), base, sizeof(base));
+
+  /* calculate required position slots */
+  if (window == 20) posn_slots = 42;
+  else if (window == 21) posn_slots = 50;
+  else posn_slots = window << 1;
+
+  /*posn_slots=i=0; while (i < wndsize) i += 1 << CAB(extra_bits)[posn_slots++]; */
+
+  LZX(R0)  =  LZX(R1)  = LZX(R2) = 1;
+  LZX(main_elements)   = LZX_NUM_CHARS + (posn_slots << 3);
+  LZX(header_read)     = 0;
+  LZX(frames_read)     = 0;
+  LZX(block_remaining) = 0;
+  LZX(block_type)      = LZX_BLOCKTYPE_INVALID;
+  LZX(intel_curpos)    = 0;
+  LZX(intel_started)   = 0;
+  LZX(window_posn)     = 0;
+
+  /* initialize tables to 0 (because deltas will be applied to them) */
+  memset(LZX(MAINTREE_len), 0, sizeof(LZX(MAINTREE_len)));
+  memset(LZX(LENGTH_len), 0, sizeof(LZX(LENGTH_len)));
+
+  return DECR_OK;
+}
+
+/*******************************************************
+ * LZXfdi_decomp(internal)
+ */
+int LZXfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state) {
+  cab_UBYTE *inpos  = CAB(inbuf);
+  const cab_UBYTE *endinp = inpos + inlen;
+  cab_UBYTE *window = LZX(window);
+  cab_UBYTE *runsrc, *rundest;
+  cab_UWORD *hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */
+
+  cab_ULONG window_posn = LZX(window_posn);
+  cab_ULONG window_size = LZX(window_size);
+  cab_ULONG R0 = LZX(R0);
+  cab_ULONG R1 = LZX(R1);
+  cab_ULONG R2 = LZX(R2);
+
+  register cab_ULONG bitbuf;
+  register int bitsleft;
+  cab_ULONG match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */
+  struct lzx_bits lb; /* used in READ_LENGTHS macro */
+
+  int togo = outlen, this_run, main_element, aligned_bits;
+  int match_length, copy_length, length_footer, extra, verbatim_bits;
+
+  INIT_BITSTREAM;
+
+  /* read header if necessary */
+  if (!LZX(header_read)) {
+    i = j = 0;
+    READ_BITS(k, 1); if (k) { READ_BITS(i,16); READ_BITS(j,16); }
+    LZX(intel_filesize) = (i << 16) | j; /* or 0 if not encoded */
+    LZX(header_read) = 1;
+  }
+
+  /* main decoding loop */
+  while (togo > 0) {
+    /* last block finished, new block expected */
+    if (LZX(block_remaining) == 0) {
+      if (LZX(block_type) == LZX_BLOCKTYPE_UNCOMPRESSED) {
+        if (LZX(block_length) & 1) inpos++; /* realign bitstream to word */
+        INIT_BITSTREAM;
+      }
+
+      READ_BITS(LZX(block_type), 3);
+      READ_BITS(i, 16);
+      READ_BITS(j, 8);
+      LZX(block_remaining) = LZX(block_length) = (i << 8) | j;
+
+      switch (LZX(block_type)) {
+      case LZX_BLOCKTYPE_ALIGNED:
+        for (i = 0; i < 8; i++) { READ_BITS(j, 3); LENTABLE(ALIGNED)[i] = j; }
+        BUILD_TABLE(ALIGNED);
+        /* rest of aligned header is same as verbatim */
+
+      case LZX_BLOCKTYPE_VERBATIM:
+        READ_LENGTHS(MAINTREE, 0, 256, fdi_lzx_read_lens);
+        READ_LENGTHS(MAINTREE, 256, LZX(main_elements), fdi_lzx_read_lens);
+        BUILD_TABLE(MAINTREE);
+        if (LENTABLE(MAINTREE)[0xE8] != 0) LZX(intel_started) = 1;
+
+        READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS, fdi_lzx_read_lens);
+        BUILD_TABLE(LENGTH);
+        break;
+
+      case LZX_BLOCKTYPE_UNCOMPRESSED:
+        LZX(intel_started) = 1; /* because we can't assume otherwise */
+        ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */
+        if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */
+        R0 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
+        R1 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
+        R2 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
+        break;
+
+      default:
+        return DECR_ILLEGALDATA;
+      }
+    }
+
+    /* buffer exhaustion check */
+    if (inpos > endinp) {
+      /* it's possible to have a file where the next run is less than
+       * 16 bits in size. In this case, the READ_HUFFSYM() macro used
+       * in building the tables will exhaust the buffer, so we should
+       * allow for this, but not allow those accidentally read bits to
+       * be used (so we check that there are at least 16 bits
+       * remaining - in this boundary case they aren't really part of
+       * the compressed data)
+       */
+      if (inpos > (endinp+2) || bitsleft < 16) return DECR_ILLEGALDATA;
+    }
+
+    while ((this_run = LZX(block_remaining)) > 0 && togo > 0) {
+      if (this_run > togo) this_run = togo;
+      togo -= this_run;
+      LZX(block_remaining) -= this_run;
+
+      /* apply 2^x-1 mask */
+      window_posn &= window_size - 1;
+      /* runs can't straddle the window wraparound */
+      if ((window_posn + this_run) > window_size)
+        return DECR_DATAFORMAT;
+
+      switch (LZX(block_type)) {
+
+      case LZX_BLOCKTYPE_VERBATIM:
+        while (this_run > 0) {
+          READ_HUFFSYM(MAINTREE, main_element);
+
+          if (main_element < LZX_NUM_CHARS) {
+            /* literal: 0 to LZX_NUM_CHARS-1 */
+            window[window_posn++] = main_element;
+            this_run--;
+          }
+          else {
+            /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
+            main_element -= LZX_NUM_CHARS;
+
+            match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
+            if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
+              READ_HUFFSYM(LENGTH, length_footer);
+              match_length += length_footer;
+            }
+            match_length += LZX_MIN_MATCH;
+
+            match_offset = main_element >> 3;
+
+            if (match_offset > 2) {
+              /* not repeated offset */
+              if (match_offset != 3) {
+                extra = CAB(extra_bits)[match_offset];
+                READ_BITS(verbatim_bits, extra);
+                match_offset = CAB(lzx_position_base)[match_offset]
+                               - 2 + verbatim_bits;
+              }
+              else {
+                match_offset = 1;
+              }
+
+              /* update repeated offset LRU queue */
+              R2 = R1; R1 = R0; R0 = match_offset;
+            }
+            else if (match_offset == 0) {
+              match_offset = R0;
+            }
+            else if (match_offset == 1) {
+              match_offset = R1;
+              R1 = R0; R0 = match_offset;
+            }
+            else /* match_offset == 2 */ {
+              match_offset = R2;
+              R2 = R0; R0 = match_offset;
+            }
+
+            rundest = window + window_posn;
+            this_run -= match_length;
+
+            /* copy any wrapped around source data */
+            if (window_posn >= match_offset) {
+              /* no wrap */
+              runsrc = rundest - match_offset;
+            } else {
+              runsrc = rundest + (window_size - match_offset);
+              copy_length = match_offset - window_posn;
+              if (copy_length < match_length) {
+                match_length -= copy_length;
+                window_posn += copy_length;
+                while (copy_length-- > 0) *rundest++ = *runsrc++;
+                runsrc = window;
+              }
+            }
+            window_posn += match_length;
+
+            /* copy match data - no worries about destination wraps */
+            while (match_length-- > 0) *rundest++ = *runsrc++;
+          }
+        }
+        break;
+
+      case LZX_BLOCKTYPE_ALIGNED:
+        while (this_run > 0) {
+          READ_HUFFSYM(MAINTREE, main_element);
+
+          if (main_element < LZX_NUM_CHARS) {
+            /* literal: 0 to LZX_NUM_CHARS-1 */
+            window[window_posn++] = main_element;
+            this_run--;
+          }
+          else {
+            /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
+            main_element -= LZX_NUM_CHARS;
+
+            match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
+            if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
+              READ_HUFFSYM(LENGTH, length_footer);
+              match_length += length_footer;
+            }
+            match_length += LZX_MIN_MATCH;
+
+            match_offset = main_element >> 3;
+
+            if (match_offset > 2) {
+              /* not repeated offset */
+              extra = CAB(extra_bits)[match_offset];
+              match_offset = CAB(lzx_position_base)[match_offset] - 2;
+              if (extra > 3) {
+                /* verbatim and aligned bits */
+                extra -= 3;
+                READ_BITS(verbatim_bits, extra);
+                match_offset += (verbatim_bits << 3);
+                READ_HUFFSYM(ALIGNED, aligned_bits);
+                match_offset += aligned_bits;
+              }
+              else if (extra == 3) {
+                /* aligned bits only */
+                READ_HUFFSYM(ALIGNED, aligned_bits);
+                match_offset += aligned_bits;
+              }
+              else if (extra > 0) { /* extra==1, extra==2 */
+                /* verbatim bits only */
+                READ_BITS(verbatim_bits, extra);
+                match_offset += verbatim_bits;
+              }
+              else /* extra == 0 */ {
+                /* ??? */
+                match_offset = 1;
+              }
+
+              /* update repeated offset LRU queue */
+              R2 = R1; R1 = R0; R0 = match_offset;
+            }
+            else if (match_offset == 0) {
+              match_offset = R0;
+            }
+            else if (match_offset == 1) {
+              match_offset = R1;
+              R1 = R0; R0 = match_offset;
+            }
+            else /* match_offset == 2 */ {
+              match_offset = R2;
+              R2 = R0; R0 = match_offset;
+            }
+
+            rundest = window + window_posn;
+            this_run -= match_length;
+
+            /* copy any wrapped around source data */
+            if (window_posn >= match_offset) {
+              /* no wrap */
+              runsrc = rundest - match_offset;
+            } else {
+              runsrc = rundest + (window_size - match_offset);
+              copy_length = match_offset - window_posn;
+              if (copy_length < match_length) {
+                match_length -= copy_length;
+                window_posn += copy_length;
+                while (copy_length-- > 0) *rundest++ = *runsrc++;
+                runsrc = window;
+              }
+            }
+            window_posn += match_length;
+
+            /* copy match data - no worries about destination wraps */
+            while (match_length-- > 0) *rundest++ = *runsrc++;
+          }
+        }
+        break;
+
+      case LZX_BLOCKTYPE_UNCOMPRESSED:
+        if ((inpos + this_run) > endinp) return DECR_ILLEGALDATA;
+        memcpy(window + window_posn, inpos, (size_t) this_run);
+        inpos += this_run; window_posn += this_run;
+        break;
+
+      default:
+        return DECR_ILLEGALDATA; /* might as well */
+      }
+
+    }
+  }
+
+  if (togo != 0) return DECR_ILLEGALDATA;
+  memcpy(CAB(outbuf), window + ((!window_posn) ? window_size : window_posn) -
+    outlen, (size_t) outlen);
+
+  LZX(window_posn) = window_posn;
+  LZX(R0) = R0;
+  LZX(R1) = R1;
+  LZX(R2) = R2;
+
+  /* intel E8 decoding */
+  if ((LZX(frames_read)++ < 32768) && LZX(intel_filesize) != 0) {
+    if (outlen <= 6 || !LZX(intel_started)) {
+      LZX(intel_curpos) += outlen;
+    }
+    else {
+      cab_UBYTE *data    = CAB(outbuf);
+      cab_UBYTE *dataend = data + outlen - 10;
+      cab_LONG curpos    = LZX(intel_curpos);
+      cab_LONG filesize  = LZX(intel_filesize);
+      cab_LONG abs_off, rel_off;
+
+      LZX(intel_curpos) = curpos + outlen;
+
+      while (data < dataend) {
+        if (*data++ != 0xE8) { curpos++; continue; }
+        abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
+        if ((abs_off >= -curpos) && (abs_off < filesize)) {
+          rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
+          data[0] = (cab_UBYTE) rel_off;
+          data[1] = (cab_UBYTE) (rel_off >> 8);
+          data[2] = (cab_UBYTE) (rel_off >> 16);
+          data[3] = (cab_UBYTE) (rel_off >> 24);
+        }
+        data += 4;
+        curpos += 5;
+      }
+    }
+  }
+  return DECR_OK;
+}
diff --git a/libgcab/decomp.h b/libgcab/decomp.h
index de10984..14a9e8d 100644
--- a/libgcab/decomp.h
+++ b/libgcab/decomp.h
@@ -25,6 +25,98 @@
 
 #include <glib.h>
 
+#define DECR_ILLEGALDATA -1
+#define DECR_DATAFORMAT -2
+#define DECR_NOMEMORY -3
+#define DECR_OK 1
+
+/* Bitstream reading macros (LZX / intel little-endian byte order)
+ *
+ * INIT_BITSTREAM    should be used first to set up the system
+ * READ_BITS(var,n)  takes N bits from the buffer and puts them in var
+ *
+ * ENSURE_BITS(n)    ensures there are at least N bits in the bit buffer.
+ *                   it can guarantee up to 17 bits (i.e. it can read in
+ *                   16 new bits when there is down to 1 bit in the buffer,
+ *                   and it can read 32 bits when there are 0 bits in the
+ *                   buffer).
+ * PEEK_BITS(n)      extracts (without removing) N bits from the bit buffer
+ * REMOVE_BITS(n)    removes N bits from the bit buffer
+ *
+ * These bit access routines work by using the area beyond the MSB and the
+ * LSB as a free source of zeroes. This avoids having to mask any bits.
+ * So we have to know the bit width of the bitbuffer variable.
+ */
+
+#define INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0)
+
+/* Quantum reads bytes in normal order; LZX is little-endian order */
+#define ENSURE_BITS(n)                                                    \
+  while (bitsleft < (n)) {                                                \
+    bitbuf |= ((inpos[1]<<8)|inpos[0]) << (CAB_ULONG_BITS-16 - bitsleft); \
+    bitsleft += 16; inpos+=2;                                             \
+  }
+
+#define PEEK_BITS(n)   (bitbuf >> (CAB_ULONG_BITS - (n)))
+#define REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n)))
+
+#define READ_BITS(v,n) do {                                             \
+  if (n) {                                                              \
+    ENSURE_BITS(n);                                                     \
+    (v) = PEEK_BITS(n);                                                 \
+    REMOVE_BITS(n);                                                     \
+  }                                                                     \
+  else {                                                                \
+    (v) = 0;                                                            \
+  }                                                                     \
+} while (0)
+
+/* Huffman macros */
+
+#define TABLEBITS(tbl)   (LZX_##tbl##_TABLEBITS)
+#define MAXSYMBOLS(tbl)  (LZX_##tbl##_MAXSYMBOLS)
+#define SYMTABLE(tbl)    (LZX(tbl##_table))
+#define LENTABLE(tbl)    (LZX(tbl##_len))
+
+/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths.
+ * In reality, it just calls make_decode_table() with the appropriate
+ * values - they're all fixed by some #defines anyway, so there's no point
+ * writing each call out in full by hand.
+ */
+#define BUILD_TABLE(tbl)                                                \
+  if (make_decode_table(                                                \
+    MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl)       \
+  )) { return DECR_ILLEGALDATA; }
+
+/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the
+ * bitstream using the stated table and puts it in var.
+ */
+#define READ_HUFFSYM(tbl,var) do {                                      \
+  ENSURE_BITS(16);                                                      \
+  hufftbl = SYMTABLE(tbl);                                              \
+  if ((i = hufftbl[PEEK_BITS(TABLEBITS(tbl))]) >= MAXSYMBOLS(tbl)) {    \
+    j = 1 << (CAB_ULONG_BITS - TABLEBITS(tbl));                         \
+    do {                                                                \
+      j >>= 1; i <<= 1; i |= (bitbuf & j) ? 1 : 0;                      \
+      if (!j) { return DECR_ILLEGALDATA; }                              \
+    } while ((i = hufftbl[i]) >= MAXSYMBOLS(tbl));                      \
+  }                                                                     \
+  j = LENTABLE(tbl)[(var) = i];                                         \
+  REMOVE_BITS(j);                                                       \
+} while (0)
+
+/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
+ * first to last in the given table. The code lengths are stored in their
+ * own special LZX way.
+ */
+#define READ_LENGTHS(tbl,first,last,fn) do { \
+  lb.bb = bitbuf; lb.bl = bitsleft; lb.ip = inpos; \
+  if (fn(LENTABLE(tbl),(first),(last),&lb,decomp_state)) { \
+    return DECR_ILLEGALDATA; \
+  } \
+  bitbuf = lb.bb; bitsleft = lb.bl; inpos = lb.ip; \
+} while (0)
+
 /* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed
  * blocks have zero growth. MSZIP guarantees that it won't grow above
  * uncompressed size by more than 12 bytes. LZX guarantees it won't grow
@@ -38,6 +130,12 @@ typedef guint16       cab_UWORD; /* 16 bits */
 typedef guint32       cab_ULONG; /* 32 bits */
 typedef gint32        cab_LONG;  /* 32 bits */
 
+/* number of bits in a ULONG */
+#ifndef CHAR_BIT
+# define CHAR_BIT (8)
+#endif
+#define CAB_ULONG_BITS (sizeof(cab_ULONG) * CHAR_BIT)
+
 /* MSZIP stuff */
 #define ZIPWSIZE 	0x8000  /* window size */
 #define ZIPLBITS	9	/* bits in base literal/length lookup table */
@@ -66,6 +164,65 @@ struct ZIPstate {
     cab_UBYTE *inpos;
 };
 
+/* LZX stuff */
+
+/* some constants defined by the LZX specification */
+#define LZX_MIN_MATCH                (2)
+#define LZX_MAX_MATCH                (257)
+#define LZX_NUM_CHARS                (256)
+#define LZX_BLOCKTYPE_INVALID        (0)   /* also blocktypes 4-7 invalid */
+#define LZX_BLOCKTYPE_VERBATIM       (1)
+#define LZX_BLOCKTYPE_ALIGNED        (2)
+#define LZX_BLOCKTYPE_UNCOMPRESSED   (3)
+#define LZX_PRETREE_NUM_ELEMENTS     (20)
+#define LZX_ALIGNED_NUM_ELEMENTS     (8)   /* aligned offset tree #elements */
+#define LZX_NUM_PRIMARY_LENGTHS      (7)   /* this one missing from spec! */
+#define LZX_NUM_SECONDARY_LENGTHS    (249) /* length tree #elements */
+
+/* LZX huffman defines: tweak tablebits as desired */
+#define LZX_PRETREE_MAXSYMBOLS  (LZX_PRETREE_NUM_ELEMENTS)
+#define LZX_PRETREE_TABLEBITS   (6)
+#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8)
+#define LZX_MAINTREE_TABLEBITS  (12)
+#define LZX_LENGTH_MAXSYMBOLS   (LZX_NUM_SECONDARY_LENGTHS+1)
+#define LZX_LENGTH_TABLEBITS    (12)
+#define LZX_ALIGNED_MAXSYMBOLS  (LZX_ALIGNED_NUM_ELEMENTS)
+#define LZX_ALIGNED_TABLEBITS   (7)
+
+#define LZX_LENTABLE_SAFETY (64) /* we allow length table decoding overruns */
+
+#define LZX_DECLARE_TABLE(tbl) \
+  cab_UWORD tbl##_table[(1<<LZX_##tbl##_TABLEBITS) + (LZX_##tbl##_MAXSYMBOLS<<1)];\
+  cab_UBYTE tbl##_len  [LZX_##tbl##_MAXSYMBOLS + LZX_LENTABLE_SAFETY]
+
+struct LZXstate {
+    cab_UBYTE *window;         /* the actual decoding window              */
+    cab_ULONG window_size;     /* window size (32Kb through 2Mb)          */
+    cab_ULONG actual_size;     /* window size when it was first allocated */
+    cab_ULONG window_posn;     /* current offset within the window        */
+    cab_ULONG R0, R1, R2;      /* for the LRU offset system               */
+    cab_UWORD main_elements;   /* number of main tree elements            */
+    int   header_read;         /* have we started decoding at all yet?    */
+    cab_UWORD block_type;      /* type of this block                      */
+    cab_ULONG block_length;    /* uncompressed length of this block       */
+    cab_ULONG block_remaining; /* uncompressed bytes still left to decode */
+    cab_ULONG frames_read;     /* the number of CFDATA blocks processed   */
+    cab_LONG  intel_filesize;  /* magic header value used for transform   */
+    cab_LONG  intel_curpos;    /* current offset in transform space       */
+    int   intel_started;       /* have we seen any translatable data yet? */
+
+    LZX_DECLARE_TABLE(PRETREE);
+    LZX_DECLARE_TABLE(MAINTREE);
+    LZX_DECLARE_TABLE(LENGTH);
+    LZX_DECLARE_TABLE(ALIGNED);
+};
+
+struct lzx_bits {
+  cab_ULONG bb;
+  int bl;
+  cab_UBYTE *ip;
+};
+
 typedef struct
 {
     gpointer (*alloc) (gsize nbyte);
@@ -76,11 +233,18 @@ typedef struct fdi_cds_fwd {
     FDI_Int *fdi;                    /* the hfdi we are using                 */
     cab_UBYTE *inbuf; /* +2 for lzx bitbuffer overflows!       */
     cab_UBYTE *outbuf;
+
+    cab_ULONG lzx_position_base[51];
+    cab_UBYTE extra_bits[51];
+
     union {
         struct ZIPstate zip;
+        struct LZXstate lzx;
     } methods;
 } fdi_decomp_state;
 
 int ZIPfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state);
+int LZXfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state);
+int LZXfdi_init(int window, fdi_decomp_state *decomp_state);
 
 #endif
diff --git a/libgcab/gcab-cabinet.c b/libgcab/gcab-cabinet.c
index df70a42..d06d648 100644
--- a/libgcab/gcab-cabinet.c
+++ b/libgcab/gcab-cabinet.c
@@ -207,7 +207,7 @@ gcab_cabinet_write (GCabCabinet *self,
     for (l = files; l != NULL; l = l->next)
         sumstr += strlen (GCAB_FILE (l->data)->name) + 1;
 
-    folder.typecomp = cabfolder->compression;
+    folder.typecomp = cabfolder->comptype;
     folder.offsetdata = CFI_START + nfiles * 16 + sumstr;
     folder.ndatab = gcab_folder_get_ndatablocks (cabfolder);
 
diff --git a/libgcab/gcab-folder.c b/libgcab/gcab-folder.c
index 4998167..ac51afa 100644
--- a/libgcab/gcab-folder.c
+++ b/libgcab/gcab-folder.c
@@ -29,6 +29,7 @@ enum {
     PROP_0,
 
     PROP_COMPRESSION,
+    PROP_COMPTYPE,
     PROP_RESERVED
 };
 
@@ -63,8 +64,8 @@ gcab_folder_set_property (GObject *object, guint prop_id, const GValue *value, G
     GCabFolder *self = GCAB_FOLDER (object);
 
     switch (prop_id) {
-    case PROP_COMPRESSION:
-        self->compression = g_value_get_enum (value);
+    case PROP_COMPTYPE:
+        self->comptype = g_value_get_int (value);
         break;
     case PROP_RESERVED:
         if (self->reserved)
@@ -85,7 +86,10 @@ gcab_folder_get_property (GObject *object, guint prop_id, GValue *value, GParamS
 
     switch (prop_id) {
     case PROP_COMPRESSION:
-        g_value_set_enum (value, self->compression);
+        g_value_set_enum (value, self->comptype & GCAB_COMPRESSION_MASK);
+        break;
+    case PROP_COMPTYPE:
+        g_value_set_int (value, self->comptype);
         break;
     case PROP_RESERVED:
         g_value_set_boxed (value, self->reserved);
@@ -108,9 +112,15 @@ gcab_folder_class_init (GCabFolderClass *klass)
     g_object_class_install_property (object_class, PROP_COMPRESSION,
         g_param_spec_enum ("compression", "compression", "compression",
                            GCAB_TYPE_COMPRESSION, 0,
-                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+                           G_PARAM_READABLE |
                            G_PARAM_STATIC_STRINGS));
 
+    g_object_class_install_property (object_class, PROP_COMPTYPE,
+        g_param_spec_int ("comptype", "comptype", "comptype",
+                          0, G_MAXINT, 0,
+                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS));
+
     g_object_class_install_property (object_class, PROP_RESERVED,
          g_param_spec_boxed ("reserved", "Reserved", "Reserved",
                             G_TYPE_BYTE_ARRAY,
@@ -258,7 +268,7 @@ gcab_folder_get_nfiles (GCabFolder *self)
 
 /**
  * gcab_folder_new:
- * @compression: compression to used in this folder
+ * @comptype: compression to used in this folder
  *
  * Creates a new empty Cabinet folder. Use gcab_folder_add_file() to
  * add files to an archive.
@@ -268,10 +278,10 @@ gcab_folder_get_nfiles (GCabFolder *self)
  * Returns: a new #GCabFolder
  **/
 GCabFolder *
-gcab_folder_new (GCabCompression compression)
+gcab_folder_new (gint comptype)
 {
     return g_object_new (GCAB_TYPE_FOLDER,
-                         "compression", compression,
+                         "comptype", comptype,
                          NULL);
 }
 
@@ -279,7 +289,7 @@ G_GNUC_INTERNAL GCabFolder *
 gcab_folder_new_with_cfolder (const cfolder_t *folder, GInputStream *stream)
 {
     GCabFolder *self = g_object_new (GCAB_TYPE_FOLDER,
-                                     "compression", folder->typecomp & GCAB_COMPRESSION_MASK,
+                                     "comptype", folder->typecomp,
                                      NULL);
     self->stream = g_object_ref (stream);
     self->cfolder = *folder;
@@ -378,7 +388,7 @@ gcab_folder_extract (GCabFolder *self,
         do {
             if ((nubytes + cdata.nubytes) <= uoffset) {
                 nubytes += cdata.nubytes;
-                if (!cdata_read (&cdata, res_data, self->compression,
+                if (!cdata_read (&cdata, res_data, self->comptype,
                                  data, cancellable, error))
                     goto end;
                 continue;
diff --git a/libgcab/gcab-folder.h b/libgcab/gcab-folder.h
index 9189ea4..d59ec24 100644
--- a/libgcab/gcab-folder.h
+++ b/libgcab/gcab-folder.h
@@ -64,7 +64,7 @@ typedef enum
 
 GType gcab_folder_get_type (void) G_GNUC_CONST;
 
-GCabFolder *    gcab_folder_new               (GCabCompression compression);
+GCabFolder *    gcab_folder_new               (gint comptype);
 gboolean        gcab_folder_add_file          (GCabFolder *cabfolder,
                                                GCabFile *cabfile,
                                                gboolean recurse,
diff --git a/libgcab/gcab-priv.h b/libgcab/gcab-priv.h
index 3a1810f..2d4d932 100644
--- a/libgcab/gcab-priv.h
+++ b/libgcab/gcab-priv.h
@@ -26,7 +26,7 @@ struct _GCabFolder
 
     GSList *files;
     GHashTable *hash;
-    GCabCompression compression;
+    gint comptype;
     GByteArray *reserved;
     cfolder_t cfolder;
     GInputStream *stream;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]