[prev in list] [next in list] [prev in thread] [next in thread] 

List:       xwt-patches
Subject:    [patches] proposed patch to HEAD: Integration of CABExtract
From:       andrew () xwt ! org
Date:       2003-09-29 19:57:06
[Download RAW message or body]

I know I'm not doing this right yet; I am not sure how to pass the font name yet, and I'd like to eliminate the file I/O and use the baseaddr and cabstream.length parameters that you already had in there, Adam.

need a little guidance here.  :-)

Regards,
Andrew

? .build_libmspack-20030726_mips-unknown-elf
? .compile
? .configure_binutils-2.13.2.1_i686-pc-linux-gnu
? .configure_binutils-2.13.2.1_mips-unknown-elf
? .configure_gcc-3.3_i686-pc-linux-gnu
? .configure_gcc-3.3_mips-unknown-elf
? .configure_jikes-1.18_
? .configure_jpeg-6b_i686-pc-linux-gnu
? .configure_libmspack-20030726_mips-unknown-elf
? .configure_newlib-1.11.0_mips-unknown-elf
? .download_binutils-2.13.2.1
? .download_freetype-2.1.4
? .download_gcc-3.3
? .download_jikes-1.18
? .download_jpeg-6b
? .download_libmspack-20030726
? .download_newlib-1.11.0
? .empty.c
? .install_binutils-2.13.2.1_i686-pc-linux-gnu
? .install_binutils-2.13.2.1_mips-unknown-elf
? .install_freetype-2.1.4_mips-unknown-elf
? .install_gcc-3.3_i686-pc-linux-gnu
? .install_gcc-3.3_mips-unknown-elf
? .install_jikes-1.18_
? .install_jpeg-6b_i686-pc-linux-gnu
? .install_libmspack-20030726_mips-unknown-elf
? .install_newlib-1.11.0_mips-unknown-elf
? .jikes+
? .vendor
? build
? x
? src/org/xwt/translators/fe.c
? upstream/binutils-2.13.2.1
? upstream/install
? upstream/jikes-1.18
? upstream/libmspack-20030726
? upstream/freetype-2.1.4/src
? upstream/gcc-3.3/build-i686-pc-linux-gnu
? upstream/gcc-3.3/build-i686-pc-mingw32
? upstream/gcc-3.3/build-mips-unknown-elf
? upstream/gcc-3.3/src
? upstream/jpeg-6b/build-i686-pc-linux-gnu
? upstream/jpeg-6b/build-i686-pc-mingw32
? upstream/jpeg-6b/src
? upstream/newlib-1.11.0/build-mips-unknown-elf
? upstream/newlib-1.11.0/src
Index: src/org/xwt/translators/MSPack.c
===================================================================
RCS file: /cvs/xwt/src/org/xwt/translators/MSPack.c,v
retrieving revision 1.1
diff -u -d -B -u -d -r1.1 MSPack.c
--- src/org/xwt/translators/MSPack.c	27 Sep 2003 23:43:30 -0000	1.1
+++ src/org/xwt/translators/MSPack.c	29 Sep 2003 19:50:12 -0000
@@ -9,8 +9,1593 @@
 
        FIXME
 
+ fontextract 0.1 - a program to extract a single ttf from the ms core fonts 
+
+ * (C) 2003 Andrew Kohlsmith <akohlsmith@mixdown.ca>
+ *
+ * really it's just a hacked up cabextract-0.6 
+ * (C) 2000-2002 Stuart Caie <kyzer@4u.net>
+ *
+ * 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.
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <time.h>
+#include <utime.h>
+
+extern char *_user_info;
+
+typedef unsigned char  UBYTE; /* 8 bits exactly    */
+typedef unsigned short UWORD; /* 16 bits (or more) */
+typedef unsigned int   ULONG; /* 32 bits (or more) */
+typedef   signed int    LONG; /* 32 bits (or more) */
+
+/* number of bits in a ULONG */
+#ifndef CHAR_BIT
+# define CHAR_BIT (8)
+#endif
+#define ULONG_BITS (sizeof(ULONG) * CHAR_BIT)
+
+/* endian-neutral reading of little-endian data */
+#define EndGetI32(a)  ((((a)[3])<<24)|(((a)[2])<<16)|(((a)[1])<<8)|((a)[0]))
+#define EndGetI16(a)  ((((a)[1])<<8)|((a)[0]))
+
+/* maximum number of cabinets any one folder can be split across */
+#define CAB_SPLITMAX (10)
+
+struct cabinet {
+  struct cabinet *next;                /* for making a list of cabinets  */
+  char  *filename;                     /* input name of cabinet          */
+  FILE  *fh;                           /* open file handle or NULL       */
+  off_t filelen;                       /* length of cabinet file         */
+  off_t blocks_off;                    /* offset to data blocks in file  */
+  struct cabinet *prevcab, *nextcab;   /* multipart cabinet chains       */
+  char *prevname, *nextname;           /* and their filenames            */
+  char *previnfo, *nextinfo;           /* and their visible names        */
+  struct folder *folders;              /* first folder in this cabinet   */
+  struct file *files;                  /* first file in this cabinet     */
+  UBYTE block_resv;                    /* reserved space in datablocks   */
+  UBYTE flags;                         /* header flags                   */
+};
+
+struct folder {
+  struct folder *next;
+  struct cabinet *cab[CAB_SPLITMAX];   /* cabinet(s) this folder spans   */
+  off_t offset[CAB_SPLITMAX];          /* offset to data blocks          */
+  UWORD comp_type;                     /* compression format/window size */
+  ULONG comp_size;                     /* compressed size of folder      */
+  UBYTE num_splits;                    /* number of split blocks + 1     */
+  UWORD num_blocks;                    /* total number of blocks         */
+  struct file *contfile;               /* the first split file           */
+};
+
+struct file {
+  struct file *next;                   /* next file in sequence          */
+  struct folder *folder;               /* folder that contains this file */
+  char *filename;                      /* output name of file            */
+  FILE *fh;                            /* open file handle or NULL       */
+  ULONG length;                        /* uncompressed length of file    */
+  ULONG offset;                        /* uncompressed offset in folder  */
+  UWORD index;                         /* magic index number of folder   */
+  UWORD time, date, attribs;           /* MS-DOS time/date/attributes    */
+};
+
+
+/* structure offsets */
+#define cfhead_Signature         (0x00)
+#define cfhead_CabinetSize       (0x08)
+#define cfhead_FileOffset        (0x10)
+#define cfhead_MinorVersion      (0x18)
+#define cfhead_MajorVersion      (0x19)
+#define cfhead_NumFolders        (0x1A)
+#define cfhead_NumFiles          (0x1C)
+#define cfhead_Flags             (0x1E)
+#define cfhead_SetID             (0x20)
+#define cfhead_CabinetIndex      (0x22)
+#define cfhead_SIZEOF            (0x24)
+#define cfheadext_HeaderReserved (0x00)
+#define cfheadext_FolderReserved (0x02)
+#define cfheadext_DataReserved   (0x03)
+#define cfheadext_SIZEOF         (0x04)
+#define cffold_DataOffset        (0x00)
+#define cffold_NumBlocks         (0x04)
+#define cffold_CompType          (0x06)
+#define cffold_SIZEOF            (0x08)
+#define cffile_UncompressedSize  (0x00)
+#define cffile_FolderOffset      (0x04)
+#define cffile_FolderIndex       (0x08)
+#define cffile_Date              (0x0A)
+#define cffile_Time              (0x0C)
+#define cffile_Attribs           (0x0E)
+#define cffile_SIZEOF            (0x10)
+#define cfdata_CheckSum          (0x00)
+#define cfdata_CompressedSize    (0x04)
+#define cfdata_UncompressedSize  (0x06)
+#define cfdata_SIZEOF            (0x08)
+
+/* flags */
+#define cffoldCOMPTYPE_MASK            (0x000f)
+#define cffoldCOMPTYPE_NONE            (0x0000)
+#define cffoldCOMPTYPE_MSZIP           (0x0001)
+#define cffoldCOMPTYPE_QUANTUM         (0x0002)
+#define cffoldCOMPTYPE_LZX             (0x0003)
+#define cfheadPREV_CABINET             (0x0001)
+#define cfheadNEXT_CABINET             (0x0002)
+#define cfheadRESERVE_PRESENT          (0x0004)
+#define cffileCONTINUED_FROM_PREV      (0xFFFD)
+#define cffileCONTINUED_TO_NEXT        (0xFFFE)
+#define cffileCONTINUED_PREV_AND_NEXT  (0xFFFF)
+#define cffile_A_RDONLY                (0x01)
+#define cffile_A_HIDDEN                (0x02)
+#define cffile_A_SYSTEM                (0x04)
+#define cffile_A_ARCH                  (0x20)
+#define cffile_A_EXEC                  (0x40)
+#define cffile_A_NAME_IS_UTF           (0x80)
+
+
+/*--------------------------------------------------------------------------*/
+/* our archiver information / state */
+
+/* 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) \
+  UWORD tbl##_table[(1<<LZX_##tbl##_TABLEBITS) + (LZX_##tbl##_MAXSYMBOLS<<1)];\
+  UBYTE tbl##_len  [LZX_##tbl##_MAXSYMBOLS + LZX_LENTABLE_SAFETY]
+
+struct LZXstate {
+    UBYTE *window;         /* the actual decoding window              */
+    ULONG window_size;     /* window size (32Kb through 2Mb)          */
+    ULONG actual_size;     /* window size when it was first allocated */
+    ULONG window_posn;     /* current offset within the window        */
+    ULONG R0, R1, R2;      /* for the LRU offset system               */
+    UWORD main_elements;   /* number of main tree elements            */
+    int   header_read;     /* have we started decoding at all yet?    */
+    UWORD block_type;      /* type of this block                      */
+    ULONG block_length;    /* uncompressed length of this block       */
+    ULONG block_remaining; /* uncompressed bytes still left to decode */
+    ULONG frames_read;     /* the number of CFDATA blocks processed   */
+    LONG  intel_filesize;  /* magic header value used for transform   */
+    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);
+};
+
+
+/* generic stuff */
+#define CAB(x) (decomp_state.x)
+#define ZIP(x) (decomp_state.methods.zip.x)
+#define QTM(x) (decomp_state.methods.qtm.x)
+#define LZX(x) (decomp_state.methods.lzx.x)
+#define DECR_OK           (0)
+#define DECR_DATAFORMAT   (1)
+#define DECR_ILLEGALDATA  (2)
+#define DECR_NOMEMORY     (3)
+#define DECR_CHECKSUM     (4)
+#define DECR_INPUT        (5)
+#define DECR_OUTPUT       (6)
+
+/* 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
+ * more than 6144 bytes.
+ */
+#define CAB_BLOCKMAX (32768)
+#define CAB_INPUTMAX (CAB_BLOCKMAX+6144)
+
+struct {
+  struct folder *current; /* current folder we're extracting from  */
+  ULONG offset;           /* uncompressed offset within folder     */
+  UBYTE *outpos;          /* (high level) start of data to use up  */
+  UWORD outlen;           /* (high level) amount of data to use up */
+  UWORD split;            /* at which split in current folder?     */
+  int (*decompress)(int, int); /* the chosen compression func      */
+  UBYTE inbuf[CAB_INPUTMAX+2]; /* +2 for lzx bitbuffer overflows!  */
+  UBYTE outbuf[CAB_BLOCKMAX];
+  union {
+//    struct ZIPstate zip;
+//    struct QTMstate qtm;
+    struct LZXstate lzx;
+  } methods;
+} decomp_state;
+
+/* LZX decruncher */
+
+/* Microsoft's LZX document and their implementation of the
+ * com.ms.util.cab Java package do not concur.
+ *
+ * In the LZX document, there is a table showing the correlation between
+ * window size and the number of position slots. It states that the 1MB
+ * window = 40 slots and the 2MB window = 42 slots. In the implementation,
+ * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
+ * first slot whose position base is equal to or more than the required
+ * window size'. This would explain why other tables in the document refer
+ * to 50 slots rather than 42.
+ *
+ * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
+ * is not defined in the specification.
+ *
+ * The LZX document does not state the uncompressed block has an
+ * uncompressed length field. Where does this length field come from, so
+ * we can know how large the block is? The implementation has it as the 24
+ * bits following after the 3 blocktype bits, before the alignment
+ * padding.
+ *
+ * The LZX document states that aligned offset blocks have their aligned
+ * offset huffman tree AFTER the main and length trees. The implementation
+ * suggests that the aligned offset tree is BEFORE the main and length
+ * trees.
+ *
+ * The LZX document decoding algorithm states that, in an aligned offset
+ * block, if an extra_bits value is 1, 2 or 3, then that number of bits
+ * should be read and the result added to the match offset. This is
+ * correct for 1 and 2, but not 3, where just a huffman symbol (using the
+ * aligned tree) should be read.
+ *
+ * Regarding the E8 preprocessing, the LZX document states 'No translation
+ * may be performed on the last 6 bytes of the input block'. This is
+ * correct.  However, the pseudocode provided checks for the *E8 leader*
+ * up to the last 6 bytes. If the leader appears between -10 and -7 bytes
+ * from the end, this would cause the next four bytes to be modified, at
+ * least one of which would be in the last 6 bytes, which is not allowed
+ * according to the spec.
+ *
+ * The specification states that the huffman trees must always contain at
+ * least one element. However, many CAB files contain blocks where the
+ * length tree is completely empty (because there are no matches), and
+ * this is expected to succeed.
+ */
+
+
+/* LZX uses what it calls 'position slots' to represent match offsets.
+ * What this means is that a small 'position slot' number and a small
+ * offset from that slot are encoded instead of one large offset for
+ * every match.
+ * - lzx_position_base is an index to the position slot bases
+ * - lzx_extra_bits states how many bits of offset-from-base data is needed.
+ */
+static ULONG lzx_position_base[51];
+static UBYTE extra_bits[51];
+
+int LZXinit(int window) {
+  ULONG wndsize = 1 << window;
+  int i, j, 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)) free(LZX(window));
+    LZX(window) = NULL;
+  }
+  if (!LZX(window)) {
+    if (!(LZX(window) = malloc(wndsize))) return DECR_NOMEMORY;
+    LZX(actual_size) = wndsize;
+  }
+  LZX(window_size) = wndsize;
+
+  /* initialise static tables */
+  for (i=0, j=0; i <= 50; i += 2) {
+    extra_bits[i] = extra_bits[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */
+    if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */
+  }
+  for (i=0, j=0; i <= 50; i++) {
+    lzx_position_base[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */
+    j += 1 << extra_bits[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */
+  }
+
+  /* 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 << 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;
+
+  /* initialise tables to 0 (because deltas will be applied to them) */
+  for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0;
+  for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   LZX(LENGTH_len)[i]   = 0;
+
+  return DECR_OK;
+}
+
+/* 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]) << (ULONG_BITS-16 - bitsleft);	\
+    bitsleft += 16; inpos+=2;						\
+  }
+
+#define PEEK_BITS(n)   (bitbuf >> (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 << (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) do { \
+  lb.bb = bitbuf; lb.bl = bitsleft; lb.ip = inpos; \
+  if (lzx_read_lens(LENTABLE(tbl),(first),(last),&lb)) { \
+    return DECR_ILLEGALDATA; \
+  } \
+  bitbuf = lb.bb; bitsleft = lb.bl; inpos = lb.ip; \
+} while (0)
+
+
+/* make_decode_table(nsyms, nbits, length[], table[])
+ *
+ * This function was coded by David Tritscher. It builds a fast huffman
+ * decoding table out of just a canonical huffman code lengths table.
+ *
+ * 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 0 for OK or 1 for error
+ */
+
+int make_decode_table(ULONG nsyms, ULONG nbits, UBYTE *length, UWORD *table) {
+  register UWORD sym;
+  register ULONG leaf;
+  register UBYTE bit_num = 1;
+  ULONG fill;
+  ULONG pos         = 0; /* the current position in the decode table */
+  ULONG table_mask  = 1 << nbits;
+  ULONG bit_mask    = table_mask >> 1; /* don't do 0 length codes */
+  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;
+}
+
+struct lzx_bits {
+  ULONG bb;
+  int bl;
+  UBYTE *ip;
+};
+
+int lzx_read_lens(UBYTE *lens, ULONG first, ULONG last, struct lzx_bits *lb) {
+  ULONG i,j, x,y;
+  int z;
+
+  register ULONG bitbuf = lb->bb;
+  register int bitsleft = lb->bl;
+  UBYTE *inpos = lb->ip;
+  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;
+}
+
+int LZXdecompress(int inlen, int outlen) {
+  UBYTE *inpos  = CAB(inbuf);
+  UBYTE *endinp = inpos + inlen;
+  UBYTE *window = LZX(window);
+  UBYTE *runsrc, *rundest;
+  UWORD *hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */
+
+  ULONG window_posn = LZX(window_posn);
+  ULONG window_size = LZX(window_size);
+  ULONG R0 = LZX(R0);
+  ULONG R1 = LZX(R1);
+  ULONG R2 = LZX(R2);
+
+  register ULONG bitbuf;
+  register int bitsleft;
+  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);
+        READ_LENGTHS(MAINTREE, 256, LZX(main_elements));
+        BUILD_TABLE(MAINTREE);
+        if (LENTABLE(MAINTREE)[0xE8] != 0) LZX(intel_started) = 1;
+
+        READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS);
+        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 = extra_bits[match_offset];
+                READ_BITS(verbatim_bits, extra);
+                match_offset = 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 = extra_bits[match_offset];
+              match_offset = 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 {
+      UBYTE *data    = CAB(outbuf);
+      UBYTE *dataend = data + outlen - 10;
+      LONG curpos    = LZX(intel_curpos);
+      LONG filesize  = LZX(intel_filesize);
+      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] = (UBYTE) rel_off;
+          data[1] = (UBYTE) (rel_off >> 8);
+          data[2] = (UBYTE) (rel_off >> 16);
+          data[3] = (UBYTE) (rel_off >> 24);
+        }
+        data += 4;
+        curpos += 5;
+      }
+    }
+  }
+  return DECR_OK;
+}
+
+
+
+
+/* all the file IO is abstracted into these routines:
+ * cabinet_(open|close|read|seek|skip|getoffset)
+ * file_(open|close|write)
+ */
+
+/* opens a file for output, returns success */
+int file_open(struct file *fi) {
+  int ok = 0;
+
+  fi->fh = fopen("fontfile.ttf", "wb");
+  if (fi->fh) ok = 1;
+
+  if (!ok) {
+    perror(fi->filename);
+  }
+
+  return ok;
+}
+
+/* closes a completed file, updates protections and timestamp */
+void file_close(struct file *fi) {
+  if (fi->fh) {
+    fclose(fi->fh);
+  }
+  fi->fh = NULL;
+}
+
+int file_write(struct file *fi, UBYTE *buf, size_t length) {
+  if (fwrite((void *)buf, 1, length, fi->fh) != length) {
+    perror(fi->filename);
+    return 0;
+  }
+  return 1;
+}
+
+
+/* don't actuall close the fd since it's stdin.  :-) */
+void cabinet_close(struct cabinet *cab) {
+  cab->fh = NULL;
+}
+
+void cabinet_seek(struct cabinet *cab, off_t offset) {
+  if (fseek(cab->fh, offset, SEEK_SET) < 0) {
+    perror(cab->filename);
+  }
+}
+
+void cabinet_skip(struct cabinet *cab, off_t distance) {
+  if (fseek(cab->fh, distance, SEEK_CUR) < 0) {
+    perror(cab->filename);
+  }
+}
+
+off_t cabinet_getoffset(struct cabinet *cab) {
+  return ftell(cab->fh);
+}
+
+/* read data from a cabinet, returns success */
+int cabinet_read(struct cabinet *cab, UBYTE *buf, size_t length) {
+  size_t avail = (size_t) (cab->filelen - cabinet_getoffset(cab));
+  if (length > avail) {
+    fprintf(stderr, "%s: WARNING; cabinet is truncated\n", cab->filename);
+    length = avail;
+  }
+  if (fread((void *)buf, 1, length, cab->fh) != length) {
+    perror(cab->filename);
+    return 0;
+  }
+  return 1;
+}
+
+/* try to open a cabinet file, returns success */
+/* doesn't actually do much since stdin is already open */
+int cabinet_open(struct cabinet *cab) {
+  /* seek to end of file */
+  fseek(stdin, 0, SEEK_END);
+
+  /* get length of file */
+  cab->filelen = ftell(stdin);
+
+  /* return to the start of the file */
+  fseek(stdin, 0, SEEK_SET);
+
+  cab->fh = stdin;
+  return 1;
+}
+
+/* allocate and read an aribitrarily long string from the cabinet */
+char *cabinet_read_string(struct cabinet *cab) {
+  off_t len=256, base = cabinet_getoffset(cab), maxlen = cab->filelen - base;
+  int ok = 0, i;
+  UBYTE *buf = NULL;
+  do {
+    if (len > maxlen) len = maxlen;
+    if (!(buf = realloc(buf, (size_t) len))) break;
+    if (!cabinet_read(cab, buf, (size_t) len)) break;
+
+    /* search for a null terminator in what we've just read */
+    for (i=0; i < len; i++) {
+      if (!buf[i]) {ok=1; break;}
+    }
+
+    if (!ok) {
+      if (len == maxlen) {
+        fprintf(stderr, "%s: WARNING; cabinet is truncated\n", cab->filename);
+        break;
+      }
+      len += 256;
+      cabinet_seek(cab, base);
+    }
+  } while (!ok);
+
+  if (!ok) {
+    if (buf) free(buf); else fprintf(stderr, "out of memory!\n");
+    return NULL;
+  }
+
+  /* otherwise, set the stream to just after the string and return */
+  cabinet_seek(cab, base + ((off_t) strlen((char *) buf)) + 1);
+  return (char *) buf;
+}
+
+/* reads the header and all folder and file entries in this cabinet */
+int cabinet_read_entries(struct cabinet *cab) {
+  int num_folders, num_files, header_resv, folder_resv = 0, i;
+  struct folder *fol, *linkfol = NULL;
+  struct file *file, *linkfile = NULL;
+  off_t base_offset;
+  UBYTE buf[64];
+
+  /* read in the CFHEADER */
+  base_offset = cabinet_getoffset(cab);
+  if (!cabinet_read(cab, buf, cfhead_SIZEOF)) {
+    return 0;
+  }
+  
+  /* check basic MSCF signature */
+  if (EndGetI32(buf+cfhead_Signature) != 0x4643534d) {
+    fprintf(stderr, "%s: not a Microsoft cabinet file\n", cab->filename);
+    return 0;
+  }
+
+  /* get the number of folders */
+  num_folders = EndGetI16(buf+cfhead_NumFolders);
+  if (num_folders == 0) {
+    fprintf(stderr, "%s: no folders in cabinet\n", cab->filename);
+    return 0;
+  }
+
+  /* get the number of files */
+  num_files = EndGetI16(buf+cfhead_NumFiles);
+  if (num_files == 0) {
+    fprintf(stderr, "%s: no files in cabinet\n", cab->filename);
+    return 0;
+  }
+
+  /* just check the header revision */
+  if ((buf[cfhead_MajorVersion] > 1) ||
+      (buf[cfhead_MajorVersion] == 1 && buf[cfhead_MinorVersion] > 3))
+  {
+    fprintf(stderr, "%s: WARNING; cabinet format version > 1.3\n",
+	    cab->filename);
+  }
+
+  /* read the reserved-sizes part of header, if present */
+  cab->flags = EndGetI16(buf+cfhead_Flags);
+  if (cab->flags & cfheadRESERVE_PRESENT) {
+    if (!cabinet_read(cab, buf, cfheadext_SIZEOF)) return 0;
+    header_resv     = EndGetI16(buf+cfheadext_HeaderReserved);
+    folder_resv     = buf[cfheadext_FolderReserved];
+    cab->block_resv = buf[cfheadext_DataReserved];
+
+    if (header_resv > 60000) {
+      fprintf(stderr, "%s: WARNING; header reserved space > 60000\n",
+	      cab->filename);
+    }
+
+    /* skip the reserved header */
+    if (header_resv) fseek(cab->fh, (off_t) header_resv, SEEK_CUR);
+  }
+
+  if (cab->flags & cfheadPREV_CABINET) {
+    cab->prevname = cabinet_read_string(cab);
+    if (!cab->prevname) return 0;
+    cab->previnfo = cabinet_read_string(cab);
+  }
+
+  if (cab->flags & cfheadNEXT_CABINET) {
+    cab->nextname = cabinet_read_string(cab);
+    if (!cab->nextname) return 0;
+    cab->nextinfo = cabinet_read_string(cab);
+  }
+
+  /* read folders */
+  for (i = 0; i < num_folders; i++) {
+    if (!cabinet_read(cab, buf, cffold_SIZEOF)) return 0;
+    if (folder_resv) cabinet_skip(cab, folder_resv);
+
+    fol = (struct folder *) calloc(1, sizeof(struct folder));
+    if (!fol) { fprintf(stderr, "out of memory!\n"); return 0; }
+
+    fol->cab[0]     = cab;
+    fol->offset[0]  = base_offset + (off_t) EndGetI32(buf+cffold_DataOffset);
+    fol->num_blocks = EndGetI16(buf+cffold_NumBlocks);
+    fol->comp_type  = EndGetI16(buf+cffold_CompType);
+
+    if (!linkfol) cab->folders = fol; else linkfol->next = fol;
+    linkfol = fol;
+  }
+
+  /* read files */
+  for (i = 0; i < num_files; i++) {
+    if (!cabinet_read(cab, buf, cffile_SIZEOF)) return 0;
+    file = (struct file *) calloc(1, sizeof(struct file));
+    if (!file) { fprintf(stderr, "out of memory!\n"); return 0; }
+      
+    file->length   = EndGetI32(buf+cffile_UncompressedSize);
+    file->offset   = EndGetI32(buf+cffile_FolderOffset);
+    file->index    = EndGetI16(buf+cffile_FolderIndex);
+    file->time     = EndGetI16(buf+cffile_Time);
+    file->date     = EndGetI16(buf+cffile_Date);
+    file->attribs  = EndGetI16(buf+cffile_Attribs);
+    file->filename = cabinet_read_string(cab);
+    if (!file->filename) return 0;
+    if (!linkfile) cab->files = file; else linkfile->next = file;
+    linkfile = file;
+  }
+  return 1;
+}
+
+
+/* this does the tricky job of running through every file in the cabinet,
+ * including spanning cabinets, and working out which file is in which
+ * folder in which cabinet. It also throws out the duplicate file entries
+ * that appear in spanning cabinets. There is memory leakage here because
+ * those entries are not freed. See the XAD CAB client for an
+ * implementation of this that correctly frees the discarded file entries.
+ */
+struct file *process_files(struct cabinet *basecab) {
+  struct cabinet *cab;
+  struct file *outfi = NULL, *linkfi = NULL, *nextfi, *fi, *cfi;
+  struct folder *fol, *firstfol, *lastfol = NULL, *predfol;
+  int i, mergeok;
+
+  for (cab = basecab; cab; cab = cab->nextcab) {
+    /* firstfol = first folder in this cabinet */
+    /* lastfol  = last folder in this cabinet */
+    /* predfol  = last folder in previous cabinet (or NULL if first cabinet) */
+    predfol = lastfol;
+    firstfol = cab->folders;
+    for (lastfol = firstfol; lastfol->next;) lastfol = lastfol->next;
+    mergeok = 1;
+
+    for (fi = cab->files; fi; fi = nextfi) {
+      i = fi->index;
+      nextfi = fi->next;
+
+      if (i < cffileCONTINUED_FROM_PREV) {
+        for (fol = firstfol; fol && i--; ) fol = fol->next;
+        fi->folder = fol; /* NULL if an invalid folder index */
+      }
+      else {
+        /* folder merging */
+        if (i == cffileCONTINUED_TO_NEXT
+        ||  i == cffileCONTINUED_PREV_AND_NEXT) {
+          if (cab->nextcab && !lastfol->contfile) lastfol->contfile = fi;
+        }
+
+        if (i == cffileCONTINUED_FROM_PREV
+        ||  i == cffileCONTINUED_PREV_AND_NEXT) {
+          /* these files are to be continued in yet another
+           * cabinet, don't merge them in just yet */
+          if (i == cffileCONTINUED_PREV_AND_NEXT) mergeok = 0;
+
+          /* only merge once per cabinet */
+          if (predfol) {
+            if ((cfi = predfol->contfile)
+            && (cfi->offset == fi->offset)
+            && (cfi->length == fi->length)
+            && (strcmp(cfi->filename, fi->filename) == 0)
+            && (predfol->comp_type == firstfol->comp_type)) {
+              /* increase the number of splits */
+              if ((i = ++(predfol->num_splits)) > CAB_SPLITMAX) {
+                mergeok = 0;
+                fprintf(stderr, "%s: internal error, increase CAB_SPLITMAX\n",
+                  basecab->filename);
+              }
+              else {
+                /* copy information across from the merged folder */
+                predfol->offset[i] = firstfol->offset[0];
+                predfol->cab[i]    = firstfol->cab[0];
+                predfol->next      = firstfol->next;
+                predfol->contfile  = firstfol->contfile;
+
+                if (firstfol == lastfol) lastfol = predfol;
+                firstfol = predfol;
+                predfol = NULL; /* don't merge again within this cabinet */
+              }
+            }
+            else {
+              /* if the folders won't merge, don't add their files */
+              mergeok = 0;
+            }
+          }
+
+          if (mergeok) fi->folder = firstfol;
+        }
+      }
+
+      if (fi->folder) {
+        if (linkfi) linkfi->next = fi; else outfi = fi;
+        linkfi = fi;
+      }
+    } /* for (fi= .. */
+  } /* for (cab= ...*/
+
+  return outfi;
+}
+
+/* validates and reads file entries from a cabinet at offset [offset] in
+ * file [name]. Returns a cabinet structure if successful, or NULL
+ * otherwise.
+ */
+struct cabinet *load_cab_offset(char *name, off_t offset) {
+  struct cabinet *cab = (struct cabinet *) calloc(1, sizeof(struct cabinet));
+  int ok;
+  if (!cab) return NULL;
+
+  cab->filename = name;
+  if ((ok = cabinet_open(cab))) {
+    cabinet_seek(cab, offset);
+    ok = cabinet_read_entries(cab);
+    cabinet_close(cab);
+  }
+
+  if (ok) return cab;
+  free(cab);
+  return NULL;
+}
+
+/* Searches a file for embedded cabinets (also succeeds on just normal
+ * cabinet files). The first result of this search will be returned, and
+ * the remaining results will be chained to it via the cab->next structure
+ * member.
+ */
+#define SEARCH_SIZE (32*1024)
+UBYTE search_buf[SEARCH_SIZE];
+
+struct cabinet *find_cabs_in_file() {
+  struct cabinet *cab, *cab2, *firstcab = NULL, *linkcab = NULL;
+  UBYTE *pstart = &search_buf[0], *pend, *p;
+  ULONG offset, caboff, cablen, foffset, filelen;
+  size_t length;
+  int state = 0, found = 0, ok = 0;
+
+char *name = "standard input";
+
+  /* open the file and search for cabinet headers */
+  if ((cab = (struct cabinet *) calloc(1, sizeof(struct cabinet)))) {
+    cab->filename = name;
+    if (cabinet_open(cab)) {
+      filelen = (ULONG) cab->filelen;
+      for (offset = 0; offset < filelen; offset += length) {
+	/* search length is either the full length of the search buffer,
+	 * or the amount of data remaining to the end of the file,
+	 * whichever is less.
+	 */
+	length = filelen - offset;
+	if (length > SEARCH_SIZE) length = SEARCH_SIZE;
+
+	/* fill the search buffer with data from disk */
+	if (!cabinet_read(cab, search_buf, length)) break;
+
+	/* read through the entire buffer. */
+	p = pstart;
+	pend = &search_buf[length];
+	while (p < pend) {
+	  switch (state) {
+	  /* starting state */
+	  case 0:
+	    /* we spend most of our time in this while loop, looking for
+	     * a leading 'M' of the 'MSCF' signature
+	     */
+	    while (*p++ != 0x4D && p < pend);
+	    if (p < pend) state = 1; /* if we found tht 'M', advance state */
+	    break;
+
+	  /* verify that the next 3 bytes are 'S', 'C' and 'F' */
+	  case 1: state = (*p++ == 0x53) ? 2 : 0; break;
+	  case 2: state = (*p++ == 0x43) ? 3 : 0; break;
+	  case 3: state = (*p++ == 0x46) ? 4 : 0; break;
+
+	  /* we don't care about bytes 4-7 */
+	  /* bytes 8-11 are the overall length of the cabinet */
+	  case 8:  cablen  = *p++;       state++; break;
+	  case 9:  cablen |= *p++ << 8;  state++; break;
+	  case 10: cablen |= *p++ << 16; state++; break;
+	  case 11: cablen |= *p++ << 24; state++; break;
+
+	  /* we don't care about bytes 12-15 */
+	  /* bytes 16-19 are the offset within the cabinet of the filedata */
+	  case 16: foffset  = *p++;       state++; break;
+	  case 17: foffset |= *p++ << 8;  state++; break;
+	  case 18: foffset |= *p++ << 16; state++; break;
+	  case 19: foffset |= *p++ << 24;
+	    /* now we have recieved 20 bytes of potential cab header. */
+	    /* work out the offset in the file of this potential cabinet */
+	    caboff = offset + (p-pstart) - 20;
+
+	    /* check that the files offset is less than the alleged length
+	     * of the cabinet, and that the offset + the alleged length are
+	     * 'roughly' within the end of overall file length
+	     */
+	    if ((foffset < cablen) &&
+		((caboff + foffset) < (filelen + 32)) &&
+		((caboff + cablen) < (filelen + 32)) )
+	    {
+	      /* found a potential result - try loading it */
+	      found++;
+	      cab2 = load_cab_offset(name, (off_t) caboff);
+	      if (cab2) {
+		/* success */
+		ok++;
+
+		/* cause the search to restart after this cab's data. */
+		offset = caboff + cablen;
+		if (offset < cab->filelen) cabinet_seek(cab, offset);
+		length = 0;
+		p = pend;
+
+		/* link the cab into the list */
+		if (linkcab == NULL) firstcab = cab2;
+		else linkcab->next = cab2;
+		linkcab = cab2;
+	      }
+	    }
+	    state = 0;
+	    break;
+	  default:
+	    p++, state++; break;
+	  }
+	}
+      }
+      cabinet_close(cab);
+    }
+    free(cab);
+  }
+
+  /* if there were cabinets that were found but are not ok, point this out */
+  if (found > ok) {
+    fprintf(stderr, "%s: WARNING; found %d bad cabinets\n", name, found-ok);
+  }
+
+  /* if no cabinets were found, let the user know */
+  if (!firstcab) {
+    fprintf(stderr, "%s: not a Microsoft cabinet file.\n", name);
+  }
+  return firstcab;
+}
+
+/* UTF translates two-byte unicode characters into 1, 2 or 3 bytes.
+ * %000000000xxxxxxx -> %0xxxxxxx
+ * %00000xxxxxyyyyyy -> %110xxxxx %10yyyyyy
+ * %xxxxyyyyyyzzzzzz -> %1110xxxx %10yyyyyy %10zzzzzz
+ *
+ * Therefore, the inverse is as follows:
+ * First char:
+ *  0x00 - 0x7F = one byte char
+ *  0x80 - 0xBF = invalid
+ *  0xC0 - 0xDF = 2 byte char (next char only 0x80-0xBF is valid)
+ *  0xE0 - 0xEF = 3 byte char (next 2 chars only 0x80-0xBF is valid)
+ *  0xF0 - 0xFF = invalid
+ */
+
+ULONG checksum(UBYTE *data, UWORD bytes, ULONG csum) {
+  int len;
+  ULONG ul = 0;
+
+  for (len = bytes >> 2; len--; data += 4) {
+    csum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24));
+  }
+
+  switch (bytes & 3) {
+  case 3: ul |= *data++ << 16;
+  case 2: ul |= *data++ <<  8;
+  case 1: ul |= *data;
+  }
+  csum ^= ul;
+
+  return csum;
+}
+
+int decompress(struct file *fi, int savemode) {
+  ULONG bytes = savemode ? fi->length : fi->offset - CAB(offset);
+  struct cabinet *cab = CAB(current)->cab[CAB(split)];
+  UBYTE buf[cfdata_SIZEOF], *data;
+  UWORD inlen, len, outlen, cando;
+  ULONG cksum;
+  LONG err;
+
+  while (bytes > 0) {
+    /* cando = the max number of bytes we can do */
+    cando = CAB(outlen);
+    if (cando > bytes) cando = bytes;
+
+    /* if cando != 0 */
+    if (cando && savemode) file_write(fi, CAB(outpos), cando);
+
+    CAB(outpos) += cando;
+    CAB(outlen) -= cando;
+    bytes -= cando; if (!bytes) break;
+
+    /* we only get here if we emptied the output buffer */
+
+    /* read data header + data */
+    inlen = outlen = 0;
+    while (outlen == 0) {
+      /* read the block header, skip the reserved part */
+      if (!cabinet_read(cab, buf, cfdata_SIZEOF)) return DECR_INPUT;
+      cabinet_skip(cab, cab->block_resv);
+
+      /* we shouldn't get blocks over CAB_INPUTMAX in size */
+      data = CAB(inbuf) + inlen;
+      len = EndGetI16(buf+cfdata_CompressedSize);
+      inlen += len;
+      if (inlen > CAB_INPUTMAX) return DECR_INPUT;
+      if (!cabinet_read(cab, data, len)) return DECR_INPUT;
+
+      /* clear two bytes after read-in data */
+      data[len+1] = data[len+2] = 0;
+
+      /* perform checksum test on the block (if one is stored) */
+      cksum = EndGetI32(buf+cfdata_CheckSum);
+      if (cksum && cksum != checksum(buf+4, 4, checksum(data, len, 0))) {
+	/* checksum is wrong */
+	  return DECR_CHECKSUM;
+      }
+
+      /* outlen=0 means this block was part of a split block */
+      outlen = EndGetI16(buf+cfdata_UncompressedSize);
+      if (outlen == 0) {
+        cabinet_close(cab);
+        cab = CAB(current)->cab[++CAB(split)];
+        if (!cabinet_open(cab)) return DECR_INPUT;
+        cabinet_seek(cab, CAB(current)->offset[CAB(split)]);
+      }
+    }
+
+    /* decompress block */
+    if ((err = CAB(decompress)(inlen, outlen))) {
+	return err;
+    }
+
+    CAB(outlen) = outlen;
+    CAB(outpos) = CAB(outbuf);
+  }
+
+  return DECR_OK;
+}
+
+
+void extract_file(struct file *fi) {
+  struct folder *fol = fi->folder, *oldfol = CAB(current);
+  LONG err = DECR_OK;
+
+  /* is a change of folder needed? do we need to reset the current folder? */
+  if (fol != oldfol || fi->offset < CAB(offset)) {
+    UWORD comptype = fol->comp_type;
+    int ct1 = comptype & cffoldCOMPTYPE_MASK;
+    int ct2 = oldfol ? (oldfol->comp_type & cffoldCOMPTYPE_MASK) : 0;
+
+    /* if the archiver has changed, call the old archiver's free() function */
+    if (ct1 != ct2) {
+      switch (ct2) {
+      case cffoldCOMPTYPE_LZX:
+        if (LZX(window)) {
+	  free(LZX(window));
+	  LZX(window) = NULL;
+	}
+	break;
+      case cffoldCOMPTYPE_QUANTUM:
+        fprintf(stderr, "Fatal: Quantum compression scheme free() called, should never happen!\n");
+        exit(1);
+	break;
+      }
+    }
+
+    switch (ct1) {
+    case cffoldCOMPTYPE_LZX:
+      CAB(decompress) = LZXdecompress;
+      err = LZXinit((comptype >> 8) & 0x1f);
+      break;
+
+    case cffoldCOMPTYPE_NONE:
+    case cffoldCOMPTYPE_MSZIP:
+    case cffoldCOMPTYPE_QUANTUM:
+    default:
+      err = DECR_DATAFORMAT;
+    }
+    if (err) goto exit_handler;
+
+    /* initialisation OK, set current folder and reset offset */
+    if (oldfol) cabinet_close(oldfol->cab[CAB(split)]);
+    if (!cabinet_open(fol->cab[0])) goto exit_handler;
+    cabinet_seek(fol->cab[0], fol->offset[0]);
+    CAB(current) = fol;
+    CAB(offset) = 0;
+    CAB(outlen) = 0; /* discard existing block */
+    CAB(split)  = 0;
+  }
+
+  if (fi->offset > CAB(offset)) {
+    /* decode bytes and send them to /dev/null */
+    if ((err = decompress(fi, 0))) goto exit_handler;
+    CAB(offset) = fi->offset;
+  }
+  if (!file_open(fi)) return;
+  err = decompress(fi, 1);
+  if (err) CAB(current) = NULL; else CAB(offset) += fi->length;
+  file_close(fi);
+
+exit_handler:
+  if (err) {
+    char *errmsg, *cabname;
+    switch (err) {
+    case DECR_NOMEMORY:
+      errmsg = "out of memory!\n"; break;
+    case DECR_ILLEGALDATA:
+      errmsg = "%s: illegal or corrupt data\n"; break;
+    case DECR_DATAFORMAT:
+      errmsg = "%s: unsupported data format\n"; break;
+    case DECR_CHECKSUM:
+      errmsg = "%s: checksum error\n"; break;
+    case DECR_INPUT:
+      errmsg = "%s: input error\n"; break;
+    case DECR_OUTPUT:
+      errmsg = "%s: output error\n"; break;
+    default:
+      errmsg = "%s: unknown error (BUG)\n";
+    }
+
+    if (CAB(current)) {
+      cabname = CAB(current)->cab[CAB(split)]->filename;
+    }
+    else {
+      cabname = fi->folder->cab[0]->filename;
+    }
+
+    fprintf(stderr, errmsg, cabname);
+  }
+}
+
+int extract_file_from_cab(char *filename) {
+  int found=0;
+  struct cabinet *basecab, *cab, *cab1, *cab2;
+  struct file *filelist, *fi;
+
+  /* load the file requested */
+  basecab = find_cabs_in_file();
+  if (!basecab) return 1;
+
+  /* iterate over all cabinets found in that file */
+  for (cab = basecab; cab; cab=cab->next) {
+
+    /* bi-directionally load any spanning cabinets -- backwards */
+    for (cab1 = cab; cab1->flags & cfheadPREV_CABINET; cab1 = cab1->prevcab) {
+      cab1->prevcab->nextcab = cab1;
+    }
+
+    /* bi-directionally load any spanning cabinets -- forwards */
+    for (cab2 = cab; cab2->flags & cfheadNEXT_CABINET; cab2 = cab2->nextcab) {
+      cab2->nextcab->prevcab = cab2;
+    }
+
+    filelist = process_files(cab1);
+    CAB(current) = NULL;
+
+    for (fi = filelist; fi; fi = fi->next) {
+      if(strcmp(fi->filename, filename) == 0)
+        {
+	extract_file(fi);
+        found = 1;
+        }
+    }
+  }
+
+ return found;
+}
+
+
+/*
+vm.setUserInfo(0, baseAddr);
+vm.setUserInfo(1, cabstream.length);
+vm.setUserInfo(2, theFontName);
 */
 
 int main(int argc, char** argv) {
-  // FIXME: do some cool stuff here
+   return extract_file_from_cab(_user_info[2]);
 }
+
Index: src/org/xwt/translators/MSPack.java
===================================================================
RCS file: /cvs/xwt/src/org/xwt/translators/MSPack.java,v
retrieving revision 1.1
diff -u -d -B -u -d -r1.1 MSPack.java
--- src/org/xwt/translators/MSPack.java	27 Sep 2003 23:43:30 -0000	1.1
+++ src/org/xwt/translators/MSPack.java	29 Sep 2003 19:50:12 -0000
@@ -29,6 +29,7 @@
             vm.copyout(cabstream, baseAddr, cabstream.length);
             vm.setUserInfo(0, baseAddr);
             vm.setUserInfo(1, cabstream.length);
+//            vm.setUserInfo(2, theFontName);
             
             vm.execute();
             // FIXME: do more stuff here


_______________________________________________
patches mailing list
patches@lists.xwt.org
http://lists.xwt.org/listinfo/patches
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic