/***************************************************************************
 *                                                                         *
 *   GALFILUZ.C                                                            *
 *                                                                         *
 *   Copyright (c) 1993-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   File Libraries - UnZip Submodule                                      *
 *                                                                         *
 *                                                 - D. Pitchford  9/28/93 *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "gcspsrv.h"
#include "filexfer.h"
#include "reserve.h"
#include "galfilh.h"

#define FILREV "$Revision: 12 $"

struct filhead {         /* 30 bytes+filename needed */
     CHAR headr[4];                /* Should be 0x04034B50                 */
     SHORT versneed;               /* version of pkzip needed to extract   */
     SHORT flags;                  /* general purpose bitflags             */
     SHORT method;                 /* compression method                   */
     SHORT ftime;                  /* file time                            */
     SHORT fdate;                  /* file date                            */
     ULONG crc32;                  /* file crc                             */
     LONG compsz;                  /* compressed size                      */
     LONG normsz;                  /* uncompressed size                    */
     SHORT namesz;                 /* filename size                        */
     SHORT xtrasz;                 /* extra field size                     */
};

IMPORT_VARIABLE(struct flddef) filheadFDA[];

struct dirhead {         /* 46+filename */
     CHAR headr[4];      /* Should be 0x02014B50                      */
     SHORT versmade;     /* version of pkzip that created             */
     SHORT versneed;     /* version of pkzip needed to extract        */
     SHORT flags;        /* general purpose bitflags                  */
     SHORT method;       /* compression method                        */
     SHORT ftime;        /* file time                                 */
     SHORT fdate;        /* file date                                 */
     ULONG crc32;        /* file crc                                */
     LONG compsz;        /* compressed size                           */
     LONG normsz;        /* uncompressed size                         */
     SHORT namesz;       /* filename size                             */
     SHORT xtrasz;       /* extra field size                          */
     SHORT commsz;       /* file comment length                       */
     SHORT dsknst;       /* disk number start                         */
     SHORT infatr;       /* internal file attributes                  */
     LONG extatr;        /* external file attributes                  */
     LONG offshd;        /* offset to local header                    */
};

EXPWGSV(struct flddef) dirheadFDA[];

struct endcdr {          /* end of central directory record           */
     CHAR headr[4];      /* should be 0x06054b50                      */
     SHORT diskno;       /* number of this disk                       */
     SHORT ctrbeg;       /* number of disk with start of central dir  */
     SHORT ctrcnt;       /* number of entries in central dir on disk  */
     SHORT totcnt;       /* total number of central dir entries       */
     ULONG ctrsiz;       /* size of the central directory             */
     LONG ctroff;        /* offset of start of central directory      */
     SHORT commsz;       /* zip file comment length                   */
};

#define CDRLEN   22      /* valid end of central dir size             */

EXPWGSV(struct flddef) endcdrFDA[];

struct archead {
     CHAR signat;        /* Signature byte of 26 (1Ah)                */
     CHAR hdrtyp;        /* Header ID                                 */
     CHAR fname[13];     /* Filename                                  */
     LONG size;          /* Size of compressed file                   */
     SHORT date;         /* File date                                 */
     SHORT time;         /* File time                                 */
     USHORT crc;         /* file CRC-16                               */
     LONG length;        /* uncompressed file length (header ID >1)   */
};

EXPWGSV(struct flddef) archeadFDA[];

struct icehead {
     CHAR hdrtyp[2];     /* header type                               */
     CHAR encod[5];      /* encode type                               */
     LONG size;          /* bytes after compression                   */
     LONG length;        /* bytes originally                          */
     SHORT time;
     SHORT date;
     SHORT attir;        /* file attributes                           */
     CHAR fnlen;         /* length of filename                        */
};

EXPWGSV(struct flddef) iceheadFDA[];

struct zoohead {
     ULONG head;              /* should be 0xFDC4A7DCL                */
     CHAR type;               /* directory type (should be 1)         */
     CHAR meth;               /* packing method                       */
     LONG next;               /* offset to next directory entry       */
     LONG data;               /* offset that this file starts at      */
     SHORT date;
     SHORT time;
     USHORT crc;
     LONG length;             /* bytes originally */
     LONG size;               /* bytes after compression */
     CHAR majver;
     CHAR minver;
     CHAR del;
     CHAR struc;
     LONG comment;            /* offset to comment; zero if none      */
     USHORT cmtsize;          /* length of comment; 0 if none         */
     CHAR fname[13];          /* filename                             */
};

EXPWGSV(struct flddef) zooheadFDA[];

struct sftree_entry {
     CHAR bit_length;
     INT code;
     CHAR value;
};

static
struct sftree {
     struct sftree_entry *tree;
     INT entries;
     INT max_length;
} ltree,lentree,distree;

#define INFLATE2  12               /* Various inflate() "states"           */
#define INFLATE3  13
#define INFLATE4  14
#define INFNOK    15
#define INFOK     16
#define INFLATE5  17
#define INFLATE6  18
#define INFLATE7  19
#define WSIZE  0x800
#define BMAX      16
#define N_MAX    288

                                   /* below find macros for inflating      */
#define NEXTBYTE    (readbyte(&zp.bytebuf), zp.bytebuf)
#define NEEDBITS(n) {while(k<(n)){b|=((ULONG)NEXTBYTE)<<k;k+=8;}}
#define DUMPBITS(n) {b>>=(n);k-=(n);}
#define NEEDBITS2(n) {while(zp.k<(n)) \
     {zp.b|=((ULONG)NEXTBYTE)<<zp.k;zp.k+=8;}}
#define DUMPBITS2(n) {zp.b>>=(n);zp.k-=(n);}
#define NEEDBITS3(n) {while(zp.k4<(n))\
     {zp.b4|=((ULONG)NEXTBYTE)<<zp.k4;zp.k4+=8;}}
#define DUMPBITS3(n) {zp.b4>>=(n);zp.k4-=(n);}
#define NEEDBITS4(n) {while(zp.k3<(n))\
     {zp.b3|=((ULONG)NEXTBYTE)<<zp.k3;zp.k3+=8;}}
#define DUMPBITS4(n) {zp.b3>>=(n);zp.k3-=(n);}
#define NEEDBITS5(n) {while(zp.k5<(n))\
     {zp.b5|=((ULONG)NEXTBYTE)<<zp.k5;zp.k5+=8;}}
#define DUMPBITS5(n) {zp.b5>>=(n);zp.k5-=(n);}

typedef CHAR byte;                 /* code assumes UNSIGNED bytes          */

static USHORT startim;             /* start time, btuTicker(), of unzip    */

struct huff {
     byte e;                       /* number of extra bits or operation    */
     byte b;                       /* number of bits in this code/subcode  */
     union {
          USHORT n;                /* literal, length base, or dist base   */
          struct huff *t;          /* pointer to next level of table       */
     } v;
};

static
struct inflate {
     USHORT bytebuf;
     UINT huffs;
     UINT wp;                      /* current position in slide            */
     ULONG bb;                     /* bit buffer                           */
     UINT bk;                      /* bits in bit buffer                   */
     INT lbits;                    /* bits in base lit/len lookup table    */
     INT dbits;                    /* bits in base distance lookup table   */
     ULONG b;                      /* bit buffer                           */
     UINT k;                       /* number of bits in bit buffer         */
     INT e2;                       /* last block flag                      */
     UINT h2;                      /* maximum struct huff's malloc'ed      */
     UINT t3;                      /* block type                           */
     ULONG b3;                     /* bit buffer                           */
     UINT k3;                      /* number of bits in bit buffer         */
     INT i4;                       /* temporary variables                  */
     UINT j4;
     UINT l4;                      /* last length                          */
     UINT m4;                      /* mask for bit lengths table           */
     UINT n4;                      /* number of lengths to get             */
     struct huff *tl4;             /* literal/length code table            */
     struct huff *td4;             /* distance code table                  */
     INT bl4;                      /* lookup bits for tl                   */
     INT bd4;                      /* lookup bits for td                   */
     UINT nb4;                     /* number of bit length codes           */
     UINT nl4;                     /* number of literal/length codes       */
     UINT nd4;                     /* number of distance codes             */
     UINT ll4[286+30];             /* literal/length and distance code len */
     ULONG b4;                     /* bit buffer                           */
     UINT k4;                      /* number of bits in bit buffer         */
     UINT e5;                      /* table entry flag/number of xtra bits */
     UINT n5, d5;                  /* length and index for copy            */
     UINT w5;                      /* current window position              */
     struct huff *t5;              /* pointer to table entry               */
     UINT ml5, md5;                /* masks for bl and bd bits             */
     ULONG b5;                     /* bit buffer                           */
     UINT k5;                      /* number of bits in bit buffer         */
     INT defstate;                 /* current "state" of inflate routine   */
     LONG csize;                   /* bytes-remaining to inflate           */
     LONG cur_zip_bs;
     CHAR *inptr;
} zp;

static
UINT border[]={16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
static
USHORT cplens[]={3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,
     43,51,59,67,83,99,115,131,163,195,227,258,0,0};
static
USHORT cplext[]={0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,
     4,4,5,5,5,5,0,99,99};
static
USHORT cpdist[]={1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
     257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577};
static
USHORT cpdext[]={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};
static
USHORT mask_bits[]={0x0000,0x0001,0x0003,0x0007,0x000f,0x001f,0x003f,
     0x007f,0x00ff,0x01ff,0x03ff,0x07ff,0x0fff,0x1fff,0x3fff,0x7fff,0xffff};

static
byte slide[WSIZE];

static
INT output_index,
    output_avail,
    input_avail,
    input_index,
    zip_eof,
    bits_left;
USHORT *prefix_of;                 /* utility array for unshrinking        */

static
LONG compressed,
       input_offset,
       output_offset;

static
CHAR *input_buf,                   /* a buffer                             */
      *output_buf,                 /* another buffer                       */
      *stack,                      /* utility array for unshrinking        */
      *suffix_of;                  /* utility array for unshrinking        */

                                   /* static functions                     */
static INT dizftyp(CHAR *);
static INT fend(CHAR *buf,LONG *pos,INT *state,LONG filen);
static INT huff_build(UINT *,UINT,UINT,USHORT *,USHORT *,struct huff **,INT *);
static INT readbits(INT);
static SHORT readbyte(USHORT *);
static INT revbits(INT);
static INT rvtree(struct sftree *);
static INT undefl(FILE *,struct filhead *);
static INT unimpl(FILE *,struct filhead *);
static INT unshrink(FILE *);
static VOID huff_free(struct huff *);
static VOID inflate(VOID);
static VOID flush(UINT);
static VOID loadst(struct sftree *,INT);
static VOID outb(INT);
static VOID outbyte(CHAR);
static VOID zipext(INT,CHAR *,struct filhead *);
static GBOOL initfixd(VOID);

INT
dizread(                           /* CYCLE calls this, read some more     */
CHAR *b)                           /*   buffer, must be 2049 bytes         */
{
     struct dirhead dhdr;
     struct filhead fhdr;
     INT go=1,x,loop;
     LONG y;
     CHAR fin[13];

     switch (flo->state) {
     case 0:                       /* read initial ID                      */
          setmem(b,2049,0);
          if (fread(b,1,52,flo->fsrc) != 52) {
               go=0;
          }
          else {
               x=dizftyp(b);
               switch (x) {
               case 2:
               case 4:
                    if ((b[2] != 0x07) || (b[3] != 0x08)) {
                         break;
                    }
               default:
               case 0:
                    go=0;
                    break;
               case 1:
               case 3:
                    break;
               }
               if (go) {
                    flo->state=1;  /* look through whole thing             */
               }
          }
          break;
     case 1:
          if (fseek(flo->fsrc,flo->pos,0) != 0) {
               go=0;
               break;
          }
          if ((INT)fread(b,1,128,flo->fsrc) <= 0) {
               go=0;
               break;
          }
          x=b[2]+b[3]*256;
          switch (x) {
          case 0x01EF:
               flo->pos=0x31F0L;
               break;
          case 0x0201:
               cvtData(b,&dhdr,sizeof(struct dirhead),dirheadFDA,
                       CVTPACKED,CVTSERVER,CHAN_NUL);
               flo->pos+=(0x2e)+dhdr.namesz+dhdr.xtrasz+dhdr.commsz;
               break;
          case 0x0403:
               setmem(fin,13,0);
               cvtData(b,&fhdr,sizeof(struct filhead),filheadFDA,
                       CVTPACKED,CVTSERVER,CHAN_NUL);
               if (fhdr.namesz < 13) {
                    for (loop=0 ; loop < fhdr.namesz ; loop++) {
                         fin[loop]=b[(0x1e)+loop];
                    }
                    if (sameas(fin,"file_id.diz")) {
                         if (fhdr.normsz < 1000) {
                              flo->pos+=fhdr.namesz+fhdr.xtrasz+0x1e;
                              zipext(3,b,&fhdr);
                              flo->pos+=fhdr.compsz;
                              break;
                         }
                    }
                    else if (sameas(fin,"desc.sdi")) {
                         if (fhdr.normsz < 100) {
                              flo->pos+=fhdr.namesz+fhdr.xtrasz+0x1e;
                              zipext(2,b,&fhdr);
                              flo->pos+=fhdr.compsz;
                              break;
                         }
                    }
               }
               flo->pos+=fhdr.namesz+fhdr.xtrasz+fhdr.compsz+0x1e;
               break;
          case 0x0605:
               go=0;               /* end normally                         */
               break;
          default:
               go=0;
               break;
          }
          break;
     case 2:
          if (fseek(flo->fsrc,flo->pos,0) != 0) {
               go=0;
               break;
          }
          y=flo->filen;
          if (y > 2048L) {
               y=2048L;
          }
          if (fread(b,1,(INT)y,flo->fsrc) != (INT)y) {
               go=0;
               break;
          }
          go=fend(b,&flo->pos,&flo->state,flo->filen);
          break;
     default:
          go=0;
          break;
     }
     if (!go) {
          fclose(flo->fsrc);
          flo->fsrc=NULL;
     }
     return(go);
}

VOID
init_galfiluz(VOID)                /* initialize library variables         */
{
     suffix_of=(CHAR *)alcmem(8192);
     stack=(CHAR *)alcmem(8192);
     prefix_of=(USHORT *)alcmem(8192*sizeof(USHORT));
     output_buf=(CHAR *)alcmem(1025);
     input_buf=(CHAR *)alcmem(512);
     ltree.tree=(struct sftree_entry *)alcmem(256*sizeof(struct sftree_entry));
     lentree.tree=(struct sftree_entry *)alcmem(64*sizeof(struct sftree_entry));
     distree.tree=(struct sftree_entry *)alcmem(64*sizeof(struct sftree_entry));
     dclvda(2049);
}

INT
openzip(VOID)                      /* open .ZIP file, determine if valid   */
{
     struct ffblk fb;

     if (fndfile(&fb,flo->destpath,0)) {
          flo->filen=fb.ff_fsize;
          flo->fsrc=fopen(flo->destpath,FOPRB);
          flo->pos=0l;
          if (flo->fsrc != NULL) {
               flo->state=0;
               return(1);
          }
     }
     return(0);
}

static VOID
zipext(                            /* extract compressed desc file         */
INT name,                          /* 2 = userdir(2)    3 = userdir(3)     */
CHAR *buf,
struct filhead *f)
{
     INT ok=0,n;
     FILE *outfil;

     if (fseek(flo->fsrc,flo->pos,0) != 0) {
          return;
     }
     outfil=fopen(userdir(name),FOPWB);
     if (outfil == NULL) {
          return;
     }
     startim=btuTicker();
     compressed=f->compsz;
     switch (f->method) {
     case 0: /* stored */
          n=(INT)f->compsz;
          if (fread(buf,1,n,flo->fsrc) == n) {
               fwrite(buf,n,1,outfil);
               ok=1;
          }
          break;
     case 1: /* shrunk */
          ok=unshrink(outfil);
          break;
     case 6: /* imploded */
          ok=unimpl(outfil,f);
          break;
     case 8: /* deflated */
          ok=undefl(outfil,f);
          break;
     default:
          break;
     }
     if (outfil != NULL) {
          fclose(outfil);
     }
     if (!ok) {
          unlink(userdir(name));
     }
}

static INT
unshrink(                          /* [unshrink]  initializer              */
FILE *out)
{
     INT code_bits;
     INT stack_index;
     USHORT final_char;
     USHORT last_code;
     USHORT i,this_code,code,prefix,got;
     USHORT first_free;

     bits_left=0;
     input_index=0;
     input_avail=0;
     input_offset=0L;
     zip_eof=0;
     code_bits=9;
     first_free=257;
     for (i=0 ; i < 8192 ; i++) {
          prefix_of[i]=0xffff;
     }
     for (i=0 ; i < 256 ; i++) {
          prefix_of[i]=0;
          suffix_of[i]=i;
     }
     last_code=readbits(code_bits);
     if (zip_eof) {
          return(0);
     }
     final_char=last_code;
     fprintf(out,"%c",(CHAR)final_char);
     stack_index=0;
     while (!zip_eof) {
          do {
               if (btuTicker()-startim > 5) {
                    return(0);
               }
               code=readbits(code_bits);
               if (zip_eof) {
                    break;
               }
               if (code == 256) {
                    code=readbits(code_bits);
                    switch (code) {
                    case 1:
                         code_bits++;
                         break;
                    case 2:
                         for (code=257 ; code < first_free ; code++) {
                              prefix_of[code]|=0x8000;
                         }
                         for (code=257 ; code < first_free ; code++) {
                              prefix=prefix_of[code]&0x7fff;
                              if (prefix >= 257) {
                                   prefix_of[prefix]&=0x7fff;
                              }
                         }
                         for (code=257 ; code < first_free ; code++) {
                              if (prefix_of[code]&0x8000) {
                                   prefix_of[code]=-1;
                              }
                         }
                         first_free=257;
                         while ((first_free < (1<<13)) &&
                    (prefix_of[first_free] != 0xffff)) {
                              first_free++;
                         }
                         break;
                    }
                    got=1;
               }
               else {
                    got=0;
               }
          } while (got);
          this_code=code;
          if (prefix_of[code] == 0xffff) {
               stack[stack_index++]=final_char;
               code=last_code;
          }
          while (code >= 257) {
               if (btuTicker()-startim > 5) {
                    return(0);
               }
               stack[stack_index++]=suffix_of[code];
               code=prefix_of[code];
          }
          final_char=suffix_of[code];
          stack[stack_index++]=final_char;
          while (stack_index) {
               fprintf(out,"%c",stack[--stack_index]);
          }
          code=first_free;
          if (code < (1<<13)) {
               prefix_of[code]=last_code;
               suffix_of[code]=final_char;
               while ((first_free < (1<<13)) &&
                  (prefix_of[first_free] != 0xffff)) {
                    first_free++;
               }
          }
          last_code=this_code;
     }
     return(1);
}

static INT
unimpl(                            /* [explode]   initializer              */
FILE *out,
struct filhead *f)
{
     INT distance,length,ldb,ltreep,mml,back_index,i;

     bits_left=0;
     input_index=0;
     input_avail=0;
     input_offset=0L;
     zip_eof=0;
     output_index=0;
     output_avail=0;
     output_offset=0L;
     if (f->flags&2) {
          ldb=7;
     }
     else {
          ldb=6;
     }
     if (f->flags&4) {
          mml=3;
          loadst(&ltree,256);
          ltreep=1;
     }
     else {
          mml=2;
          ltreep=0;
     }
     loadst(&lentree,64);
     loadst(&distree,64);
     while (!zip_eof) {
          if (btuTicker()-startim > 5) {
               return(0);
          }
          if (readbits(1)) {
               if (ltreep) {
                    outbyte(rvtree(&ltree));
                    if (zip_eof) {
                         break;
                    }
               }
               else {
                    outbyte(readbits(8));
                    if (zip_eof) {
                         break;
                    }
               }
          }
          else {
               if (zip_eof) {
                    break;
               }
               distance=readbits(ldb);
               distance+=(rvtree(&distree)<<ldb);
               if (zip_eof) {
                    break;
               }
               length=rvtree(&lentree)+mml;
               if (zip_eof) {
                    break;
               }
               if (length == (63+mml)) {
                    length+=readbits(8);
               }
               back_index=output_index-(distance+1);
               if (back_index < 0) {
                    back_index+=1025;
               }
               while (length) {
                    if (btuTicker()-startim > 5) {
                         return(0);
                    }
                    outbyte(output_buf[back_index]);
                    back_index=(back_index+1)%1025;
                    length--;
               }
          }
     }
     i=0;
     while (output_avail) {
          fprintf(out,"%c",output_buf[i++]);
          output_avail--;
     }
     return(1);
}

static VOID
outbyte(                           /* [unshrink, explode]   "output byte"  */
CHAR b)
{
     output_buf[output_index]=b;
     output_index=(output_index+1)%1025;
     output_avail++;
     output_offset++;
     if (output_avail > 1025-10) {
          zip_eof=1;
     }
}

static INT
readbits(                          /* [unshrink, explode]   "read bits"    */
INT b)
{
     register SHORT x;
     register SHORT mask;
     INT n;

     x=0;
     mask=1;
     while (b) {
          if (!bits_left) {
               input_index++;
               if (input_index >= input_avail) {
                    n=512;
                    if (compressed-input_offset < n) {
                         n=(INT)(compressed-input_offset);
                    }
                    input_avail=fread(input_buf,1,n,flo->fsrc);
                    if (input_avail == 0 || input_avail == -1) {
                         zip_eof=1;
                         input_avail=0;
                         return(0);
                    }
                    input_offset+=input_avail;
                    input_index=0;
               }
               bits_left=8;
          }
          if (input_buf[input_index]&1) {
               x|=mask;
          }
          input_buf[input_index] >>= 1;
          bits_left--;
          mask<<=1;
          b--;
     }
     return(x);
}

static VOID
loadst(                            /* [explode]   "load sf tree"           */
struct sftree *t,
INT tree_size)
{
     INT ctbs,i,num,len,code,codeinc,lbitlen;
     CHAR swapped,x,y;
     struct sftree_entry temp;

     t->entries=tree_size;
     ctbs=readbits(8)+1;
     i=0;
     t->max_length=0;
     while (ctbs) {
          len=readbits(4)+1;
          num=readbits(4)+1;
          while (num != 0) {
               if (len > t->max_length) {
                    t->max_length=len;
               }
               t->tree[i].bit_length=len;
               t->tree[i].value=i;
               i++;
               num--;
          }
          ctbs--;
     }
     do {
          swapped=0;
          for (i=0; i < t->entries-1; i++) {
               x=t->tree[i].bit_length;
               y=t->tree[i+1].bit_length;
               if (x > y ||
                  (x == y && t->tree[i].value > t->tree[i+1].value)) {
                    temp=t->tree[i];
                    t->tree[i]=t->tree[i+1];
                    t->tree[i+1]=temp;
                    swapped=1;
               }
          }
     } while (swapped);
     code=0;
     codeinc=0;
     lbitlen=0;
     i=t->entries-1;
     while (i >= 0) {
          code+=codeinc;
          if (t->tree[i].bit_length != lbitlen) {
               lbitlen=t->tree[i].bit_length;
               codeinc=1<<(16-lbitlen);
          }
          t->tree[i].code=revbits(code);
          i--;
     }
}

static INT
revbits(INT x)                     /* [explode]   "reverse bits"           */
{
     register USHORT in,out;
     INT b;

     in=x;
     out=0;
     for (b=0; b < 16; b++) {
          out<<=1;
          if (in&1) {
               out|=1;
          }
          in>>=1;
     }
     return(out);
}

static INT
rvtree(                            /* [explode]   "read via tree"          */
struct sftree *t)
{
     USHORT code,bits,index;

     code=bits=index=0;
     for (;;) {
          code|=readbits(1)<<bits;
          bits++;
          while (t->tree[index].bit_length < bits) {
               index++;
               if (index >= t->entries) {
                    zip_eof=1;
                    return(0);
               }
          }
          while (t->tree[index].bit_length == bits) {
               if (t->tree[index].code == code) {
                    return(t->tree[index].value);
               }
               index++;
               if (index >= t->entries) {
                    zip_eof=1;
                    return(0);
               }
          }
     }
}

static INT
undefl(                            /* [inflate] initializer                */
FILE *out,
struct filhead *f)
{
     INT i;

     output_index=0;
     output_avail=0;
     input_avail=0;
     output_offset=0L;
     zp.lbits=9;
     zp.dbits=6;
     zp.wp=0;
     zp.bk=0;
     zp.bb=0;
     zp.h2=0;
     zp.huffs=0;
     zp.defstate=INFLATE2;
     zp.csize=f->compsz;
     while ((zp.defstate != INFOK) && (zp.defstate != INFNOK)) {
          if (btuTicker()-startim > 5) {
               return(0);
          }
          inflate();
     }
     if (zp.defstate == INFNOK) {
          return(0);
     }
     i=0;
     while (output_avail) {
          fprintf(out,"%c",output_buf[i++]);
          output_avail--;
     }
     return(1);
}

static VOID
inflate(VOID)                      /* Routine to INFLATE deflated .ZIPs    */
{
     switch (zp.defstate) {
     case INFLATE2:
          zp.b3=zp.bb;
          zp.k3=zp.bk;
          NEEDBITS4(1)
          zp.e2=(INT)zp.b3&1;
          DUMPBITS4(1)
          NEEDBITS4(2)
          zp.t3=(UINT)zp.b3&3;
          DUMPBITS4(2)
          zp.bb=zp.b3;
          zp.bk=zp.k3;
          switch (zp.t3) {
          case 2:
               zp.defstate=INFLATE3;
               break;
          case 1:
               if (initfixd()) {
                    zp.defstate=INFLATE4;
                    break;
               }
          default:
               zp.defstate=INFNOK;
          }
          return;
     case INFLATE3:
          zp.b4=zp.bb;
          zp.k4=zp.bk;
          NEEDBITS3(5)
          zp.nl4=257+((UINT)zp.b4&0x1f);
          DUMPBITS3(5)
          NEEDBITS3(5)
          zp.nd4=1+((UINT)zp.b4&0x1f);
          DUMPBITS3(5)
          NEEDBITS3(4)
          zp.nb4=4+((UINT)zp.b4&0xf);
          DUMPBITS3(4)
          if (zp.nl4 > 286 || zp.nd4 > 30) {
               zp.defstate=INFNOK;
               return;
          }
          for (zp.j4=0 ; zp.j4 < zp.nb4 ; zp.j4++) {
               NEEDBITS3(3)
               zp.ll4[border[zp.j4]]=(UINT)zp.b4&7;
               DUMPBITS3(3)
          }
          for ( ; zp.j4 < 19 ; zp.j4++) {
               zp.ll4[border[zp.j4]]=0;
          }
          zp.bl4=7;
          if ((zp.i4=huff_build(zp.ll4,19,19,NULL,NULL,
              &zp.tl4,&zp.bl4)) != 0) {
               if (zp.i4 == 1) {
                    huff_free(zp.tl4);
               }
               zp.defstate=INFNOK;
               return;
          }
          zp.n4=zp.nl4+zp.nd4;
          zp.m4=mask_bits[zp.bl4];
          zp.i4=zp.l4=0;
          while ((UINT)zp.i4 < zp.n4) {
               NEEDBITS3((UINT)zp.bl4)
               zp.j4=(zp.td4=zp.tl4+((UINT)zp.b4&zp.m4))->b;
               DUMPBITS3(zp.j4)
               zp.j4=zp.td4->v.n;
               if (zp.j4 < 16) {
                    zp.ll4[zp.i4++]=zp.l4=zp.j4;
               }
               else if (zp.j4 == 16) {
                    NEEDBITS3(2)
                    zp.j4=3+((UINT)zp.b4&3);
                    DUMPBITS3(2)
                    if ((UINT)zp.i4+zp.j4 > zp.n4) {
                         zp.defstate=INFNOK;
                         return;
                    }
                    while (zp.j4--)
                         zp.ll4[zp.i4++]=zp.l4;
               }
               else if (zp.j4 == 17) {
                    NEEDBITS3(3)
                    zp.j4=3+((UINT)zp.b4&7);
                    DUMPBITS3(3)
                    if ((UINT)zp.i4+zp.j4 > zp.n4) {
                         zp.defstate=INFNOK;
                         return;
                    }
                    while (zp.j4--) {
                         zp.ll4[zp.i4++]=0;
                    }
                    zp.l4=0;
               }
               else {
                    NEEDBITS3(7)
                    zp.j4=11+((UINT)zp.b4&0x7f);
                    DUMPBITS3(7)
                    if ((UINT)zp.i4+zp.j4 > zp.n4) {
                         zp.defstate=INFNOK;
                         return;
                    }
                    while (zp.j4--) {
                         zp.ll4[zp.i4++]=0;
                    }
                    zp.l4=0;
               }
          }
          huff_free(zp.tl4);
          zp.bb=zp.b4;
          zp.bk=zp.k4;
          zp.bl4=zp.lbits;
          if ((zp.i4=huff_build(zp.ll4,zp.nl4,257,cplens,cplext,
               &zp.tl4,&zp.bl4)) != 0) {
               if (zp.i4 == 1) {
                    huff_free(zp.tl4);
               }
               zp.defstate=INFNOK;
               return;
          }
          zp.bd4=zp.dbits;
          if ((zp.i4=huff_build(zp.ll4+zp.nl4,zp.nd4,0,
              cpdist,cpdext,&zp.td4,&zp.bd4)) != 0) {
               if (zp.i4 == 1) {
                    huff_free(zp.td4);
               }
               huff_free(zp.tl4);
               zp.defstate=INFNOK;
               return;
          }
          zp.defstate=INFLATE4;
          return;
     case INFLATE4:
          zp.b5=zp.bb;
          zp.k5=zp.bk;
          zp.w5=zp.wp;
          zp.ml5=mask_bits[zp.bl4];
          zp.md5=mask_bits[zp.bd4];
          zp.defstate=INFLATE5;
          return;
     case INFLATE5:
          NEEDBITS5((UINT)zp.bl4)
          if ((zp.e5=(zp.t5=zp.tl4+
              ((UINT)zp.b5&zp.ml5))->e) > 16) {
               do {
                    if (zp.e5 == 99) {
                         zp.defstate=INFNOK;
                    }
                    DUMPBITS5(zp.t5->b)
                    zp.e5-=16;
                    NEEDBITS5(zp.e5)
               } while ((zp.e5=(zp.t5=zp.t5->v.t+
                 ((UINT)zp.b5&mask_bits[zp.e5]))->e) > 16);
          }
          DUMPBITS5(zp.t5->b)
          if (zp.e5 == 16) {
               slide[zp.w5++]=(CHAR)zp.t5->v.n;
               if (zp.w5 == WSIZE) {
                    flush(zp.w5);
                    zp.w5=0;
               }
          }
          else {
               /* exit if end of block */
               if (zp.e5 == 15) {
                    zp.defstate=INFLATE6;
                    return;
               }
               NEEDBITS5(zp.e5)
               zp.n5=zp.t5->v.n+((UINT)zp.b5&mask_bits[zp.e5]);
               DUMPBITS5(zp.e5);
               NEEDBITS5((UINT)zp.bd4)
               if ((zp.e5=(zp.t5=zp.td4+
                   ((UINT)zp.b5&zp.md5))->e) > 16) {
                    do {
                         if (zp.e5 == 99) {
                              zp.defstate=INFNOK;
                         }
                         DUMPBITS5(zp.t5->b)
                         zp.e5-=16;
                         NEEDBITS5(zp.e5)
                    } while ((zp.e5=(zp.t5=zp.t5->v.t+
                       ((UINT)zp.b5&mask_bits[zp.e5]))->e)
                        > 16);
               }
               DUMPBITS5(zp.t5->b)
               NEEDBITS5(zp.e5)
               zp.d5=zp.w5-zp.t5->v.n-((UINT)zp.b5&mask_bits[zp.e5]);
               DUMPBITS5(zp.e5)
               zp.defstate=INFLATE7;
               return;
          }
          return;
     case INFLATE6:
          zp.wp=zp.w5;
          zp.bb=zp.b5;
          zp.bk=zp.k5;
          huff_free(zp.tl4);
          huff_free(zp.td4);
          if (zp.huffs > zp.h2) {
               zp.h2=zp.huffs;
          }
          if (!zp.e2) {
               zp.huffs=0;
               zp.defstate=INFLATE2;
               return;
          }
          flush(zp.wp);
          zp.defstate=INFOK;
          return;
     case INFLATE7:
          zp.n5-=(zp.e5=(zp.e5=WSIZE-((zp.d5&=WSIZE-1)
           > zp.w5 ? zp.d5 : zp.w5)) > zp.n5 ? zp.n5 :
            zp.e5);
          if (zp.w5-zp.d5 >= zp.e5) {
               memcpy(slide+zp.w5,slide+zp.d5,zp.e5);
               zp.w5+=zp.e5;
               zp.d5+=zp.e5;
          }
          else {
               do {
                    slide[zp.w5++]=slide[zp.d5++];
               } while (--zp.e5);
          }
          if (zp.w5 == WSIZE) {
               flush(zp.w5);
               zp.w5=0;
          }
          if (!zp.n5) { /* 3 */
               zp.defstate=INFLATE5;
          }
          return;
     }
}

static GBOOL
initfixd(VOID)                     /* init inflate of fixed code block     */
{
     INT i;

     for (i=0 ; i < 144 ; i++) {
          zp.ll4[i]=8;
     }
     for (; i < 256 ; i++) {
          zp.ll4[i]=9;
     }
     for (; i < 280 ; i++) {
          zp.ll4[i]=7;
     }
     for (; i < 288 ; i++) {
          zp.ll4[i]=8;
     }
     zp.bl4=7;
     if ((i=huff_build(zp.ll4,288,257,cplens,cplext,&zp.tl4,&zp.bl4)) != 0) {
          return(0);
     }
     for (i=0 ; i < 30 ; i++) {
          zp.ll4[i]=5;
     }
     zp.bd4=5;
     if ((i=huff_build(zp.ll4,30,0,cpdist,cpdext,&zp.td4,&zp.bd4)) > 1) {
          huff_free(zp.tl4);
          return(0);
     }
     return(1);
}

static INT                         /* build Huffman tables                 */
huff_build(UINT *b,
           UINT n,
           UINT s,
           USHORT *d,
           USHORT *e,
           struct huff **t,
           INT *m)
{
     UINT a;                       /* counter for codes of length k        */
     UINT c[BMAX+1];               /* bit length count table               */
     UINT f;                       /* i repeats in table every f entries   */
     INT g;                        /* maximum code length                  */
     INT h;                        /* table level                          */
     UINT i;                       /* counter, current code                */
     UINT j;                       /* counter                              */
     INT k;                        /* number of bits in current code       */
     INT l;                        /* bits per table (returned in m)       */
     UINT *p;                      /* pointer into c[], b[], or v[]        */
     struct huff *q;               /* points to current table              */
     struct huff r;                /* table entry for structure assignment */
     struct huff *u[BMAX];         /* table stack                          */
     UINT v[N_MAX];                /* values in order of bit length        */
     INT w;                        /* bits before this table == (l * h)    */
     UINT x[BMAX+1];               /* bit offsets, then code stack         */
     UINT *xp;                     /* pointer into x                       */
     INT y;                        /* number of dummy codes added          */
     UINT z;                       /* number of entries in current table   */

     memset(c,0,sizeof(c));
     p=b;
     i=n;
     do {
          c[*p++]++;
     } while (--i);
     if (c[0] == n) {
          *t=(struct huff *)NULL;
          *m=0;
          return(0);
     }
     l=*m;
     for (j=1 ; j <= BMAX ; j++) {
          if (c[j]) {
               break;
          }
     }
     k=j;
     if ((UINT)l < j) {
          l=j;
     }
     for (i=BMAX ; i ; i--) {
          if (c[i]) {
               break;
          }
     }
     g=i;
     if ((UINT)l > i) {
          l=i;
     }
     *m=l;
     for (y=1<<j ; j < i ; j++,y<<=1) {
          if ((y-=c[j]) < 0) {
               return(2);
          }
     }
     if ((y-=c[i]) < 0) {
          return(2);
     }
     c[i]+=y;
     x[1]=j=0;
     p=c+1;
     xp=x+2;
     while (--i) {
          *xp++=(j+=*p++);
     }
     p=b;
     i=0;
     do {
          if ((j=*p++) != 0) {
               v[x[j]++]=i;
          }
     } while (++i < n);
     x[0]=i=0;
     p=v;
     h=-1;
     w=-l;
     u[0]=(struct huff *)NULL;
     q=(struct huff *)NULL;
     z=0;
     for ( ; k <= g ; k++) {       /* 1                                    */
          a=c[k];
          while (a--) {            /* 2                                    */
               while (k > w+l) {   /* 3                                    */
                    h++;
                    w+=l;
                    z=(z=g-w) > (UINT)l ? l : z;
                    if ((f=1 << (j=k-w)) > a+1) {
                         f-=a+1;
                         xp=c+k;
                         while (++j < z) {
                              if ((f<<=1) <= *++xp) {
                                   break;
                              }
                              f-=*xp;
                         }
                    }
                    z=1<<j;
                    if ((q=(struct huff *)malloc((z+1)*
                        sizeof(struct huff))) == (struct huff *)NULL) {
                         if (h) {
                              huff_free(u[0]);
                         }
                         return(3);
                    }
                    zp.huffs+=z+1;
                    *t=q+1;
                    *(t=&(q->v.t))=(struct huff *)NULL;
                    u[h]=++q;
                    if (h) {
                         x[h]=i;
                         r.b=(CHAR)l;
                         r.e=(CHAR)(16 + j);
                         r.v.t=q;
                         j=i>>(w-l);
                         u[h-1][j]=r;
                    }
               }
               r.b=(CHAR)(k-w);
               if (p >= v+n) {
                    r.e=99;
               }
               else if (*p < s) {
                    r.e=(CHAR)(*p < 256 ? 16 : 15);
                    r.v.n=*p++;
               }
               else {
                    r.e=(CHAR)e[*p-s];
                    r.v.n=d[*p++-s];
               }
               f=1 << (k-w);
               for (j=i>>w ; j < z ; j+=f) {
                    q[j]=r;
               }
               for (j=1<<(k-1) ; i&j ; j>>=1) {
                    i^=j;
               }
               i^=j;
               while ((i&((1<<w)-1)) != x[h]) {
                    h--;
                    w-=l;
               }
          }
     }
     return(y != 0 && g != 1);
}

static VOID                        /* "Release" Huffman tables             */
huff_free(struct huff *t)
{
     struct huff *p, *q;

     p=t;
     while (p != (struct huff *)NULL) {
          q=(--p)->v.t;
          free(p);
          p=q;
     }
}

static VOID                        /* Output inflated byte                 */
outb(INT intc)
{
     if (output_avail == (1025-10)) {
          return;
     }
     output_buf[output_avail++]=(byte)intc;
}

static VOID                        /* Flush out "w" inflated bytes         */
flush(UINT w)
{
     UINT n;

     for (n=0 ; n < w ; n++) {
          outb((INT)slide[n]);
     }
}

static SHORT                       /* Read in next byte from .ZIP file     */
readbyte(
USHORT *x)
{
     if (zp.csize-- <= 0) {
          return(0);
     }
     if (input_avail == 0) {
          if ((input_avail=(INT)fread((CHAR *)input_buf,1,512,flo->fsrc)) == 0
            || input_avail == -1) {
               return(0);
          }
          /* buffer ALWAYS starts on a block boundary:  */
          zp.cur_zip_bs+=512;
          zp.inptr=input_buf;
     }
     *x=*zp.inptr;
     zp.inptr++;
     input_avail--;
     return(8);
}

static INT
dizftyp(                           /* determine what kind of zipfile       */
CHAR *recbuf)
{
     if (recbuf[0] == 'P'
      && recbuf[1] == 'K'
      && ((recbuf[2] == 0x03
      && recbuf[3] == 0x04)
      || (recbuf[2] == 0x07
      && recbuf[3] == 0x08))) {
          return(recbuf[4] < 0x14 ? 1 : 2);
     }
     if (recbuf[0] == 'M'
      && recbuf[1] == 'Z') {
          if (recbuf[2] == 0xEF
            && recbuf[3] == 0x01
            && recbuf[0x32] == 'P'
            && recbuf[0x33] == 'K') {
               return(3);
          }
          if ((recbuf[2] == 0xBA
            && recbuf[3] == 0x01)
            || (recbuf[2] == 0xF4
            && recbuf[3] == 0x01
            && recbuf[0x1E] == 'P'
            && recbuf[0x1F] == 'K'
            && recbuf[0x20] == 'L')) {
               return(4);
          }
     }
     return(0);
}

static INT
fend(                              /* find end of central dir record       */
CHAR *buf,
LONG *pos,
INT *state,
LONG filen)
{
     CHAR *ptr;
     INT cnt;
     LONG oldpos;
     INT byts;
     struct endcdr endcdr;

     oldpos=*pos;
     byts=filen > 2048L ? 2048 : (INT)filen;
     cnt=byts-CDRLEN;
     ptr=&buf[cnt];
     while (cnt >= 0) {
          if (*ptr == 'P'
            && *(ptr+1) == 'K'
            && *(ptr+2) == 5
            && *(ptr+3) == 6) {
               cvtData(&buf[cnt],&endcdr,sizeof(struct endcdr),endcdrFDA,
                       CVTPACKED,CVTSERVER,CHAN_NUL);
               *pos=endcdr.ctroff;
               *state=1;
               return(1);
          }
          ptr--;
          cnt--;
     }
     if (oldpos != 0L) {
          *pos-=(LONG)(2048-CDRLEN);
          if (*pos < 0L) {
               *pos=0L;
          }
     }
     else {
          return(0);
     }
     return(1);
}

/* zip file viewing submodule for c/s mode begins here                     */

extern INT zafilid(CHAR *);        /* identify zip file type, FTFVIEW      */

struct zvdat *vewscb;

/*--- Archive file id's, returned by zafilid() ---*/
#define UNKFILE 0                  /* Unknown file format                  */
#define ARCFILE 1                  /* ARC                                  */
#define ZIPFILE 2                  /* ZIP                                  */
#define ICEFILE 3                  /* ICE or LZH                           */
#define ZOOFILE 4                  /* ZOO                                  */
#define ZSEFILE 5                  /* Zip Self-Extracting                  */
#define PSEFILE 6                  /* Pak Self-Extracting                  */
#define ZP2FILE 7                  /* ZIP V2                               */
#define ZS2FILE 8                  /* ZIP V2 Self-Extracting               */

#define NUMVFILE 9                 /* number of viewable file types        */

EXPWGSV(CHAR*) arcdsc[];           /* Array of file descriptions, indexed by file id */

#define VIDCHUNK 52   /* View file ID, chunk of file necessary to identify */

static VOID arpli(CHAR *fname,SHORT fnleng,LONG length,CHAR *meth,
            LONG size,SHORT date,SHORT time,ULONG crc,SHORT cflag);
static INT zpprfr(VOID);
static CHAR *zipmth(struct dirhead *dhdr);
static INT arprfr(VOID);
static INT icprfr(VOID);
static INT zoprfr(VOID);
static VOID z2fend(VOID);

/*--- View transmitter states ---*/
#define VEWOPN    1                               /* open file for transmit */
#define VEWFID    2                            /* check type, output header */
#define VEWFIL    3                             /* output info on each file */
#define VEWEND    4                                              /* wind up */
#define VEWFND    5           /* searching ZIP2 file for end of central dir */


/*--- ARC/ZIP/etc display structures ---*/

#define VEWCHUNK 128                /* amount of data to read for each file */
#define ZP2CHUNK 2048               /* data to read to check for ZIP2 file  */

                         /* Deflate compression flavors for PKZIP V2  */
#define NORCMP   0x0000  /*   Normal compression (-en)                */
#define MAXCMP   0x0002  /*   Maximum compression (-ex)               */
#define FSTCMP   0x0004  /*   Fast compression (-ef)                  */
#define SFSCMP   (MAXCMP+FSTCMP)   /* Super Fast compression (-es)    */

                         /* ZIP compression methods                   */
#define DEFLAT      8    /*   This file is Deflated                   */

static
CHAR *zoomth[2]={"Stored  ","LevZimp "};

static
CHAR *methds[]={"Stored  ","Shrunk  ","Reduce1 ","Reduce2 ","Reduce3 ",
                 "Reduce4 ","Implode ","--------","Deflat"};

static
CHAR *arcmth[10]={"ENDOFARC","Stored  ","Stored  ","Packed  ","Squeezed",
                   "crunched","crunched","crunched","Crunched","Squashed"};

static VOID
arpli(                                                  /* report on 1 file */
CHAR *fname,                     /* name of file (may not be 0-terminated) */
SHORT fnleng,           /* max length of file name (not incl 0-term if any) */
LONG length,                                  /* bytes before compression */
CHAR *meth,                                       /* method of compression */
LONG size,                                     /* bytes after compression */
SHORT date,                                        /* DOS-style date & time */
SHORT time,
ULONG crc,                                                         /* CRC */
SHORT cflag)                                            /* 1=comments 0=not */
{
     INT ratio;     /* percentage of reduction (e.g. 75% ==> 1/4 orig size) */

     if (length != 0) {
          ratio=100-(INT)((100*size)/length);
     }             /* note: LHARC program actually displays 100%-this ratio */
     else {
          ratio=0;
     }
     sprintf(rsptmp,
             "%ld\t%s\t%ld\t%d\t%f\t%8.8lx\t%-12.*s\t%d",
             length,meth,size,ratio,d2vdat(date,time),crc,
             min(fnleng,79),fname,cflag);
}

static INT
zpprfr(VOID)                               /* output one PKZIP record entry */
{                                     /* returns 1=done -1=error 0=continue */
     struct dirhead dhdr;
     struct filhead fhdr;
     INT hdr;
     INT rval=0;

     vewscb=&flo->cszview;
     hdr=vdatmp[2]+vdatmp[3]*256;
     switch (hdr) {
     case 0x01EF:                   /* Self-Extracting Header               */
          vewscb->pos=0x31F0L;
          break;
     case 0x0201:                   /* Directory record                     */
          cvtData(vdatmp,&dhdr,sizeof(struct dirhead),dirheadFDA,
                  CVTPACKED,CVTSERVER,CHAN_NUL);
          arpli(vdatmp+0x2E,
                dhdr.namesz,
                dhdr.normsz,
                zipmth(&dhdr),
                dhdr.compsz,
                dhdr.fdate,
                dhdr.ftime,
                dhdr.crc32,
                dhdr.commsz);
          vewscb->pos+=dhdr.namesz+dhdr.xtrasz+dhdr.commsz+0x2e;
          break;
     case 0x0403:                   /* File header record                   */
          cvtData(vdatmp,&fhdr,sizeof(struct filhead),filheadFDA,
                  CVTPACKED,CVTSERVER,CHAN_NUL);
          vewscb->pos+=fhdr.namesz+fhdr.xtrasz+fhdr.compsz+0x1e;
          break;
     case 0x0605:                   /* End of Central Dir Record            */
          rval=1;
          break;
     default:
          rval=-1;
          break;
     }
     return(rval);
}

static CHAR *
zipmth(                                /* returns zip compression method  */
struct dirhead *dhdr)
{
     static CHAR mthbuf[10];

     strcpy(mthbuf,methds[dhdr->method]);
     if (dhdr->method == DEFLAT) {      /* PKZIP V2x deflate method */
          if ((dhdr->flags&SFSCMP) == SFSCMP) {
               strcat(mthbuf,"S");
          }
          else if (dhdr->flags&FSTCMP) {
               strcat(mthbuf,"F");
          }
          else if (dhdr->flags&MAXCMP) {
               strcat(mthbuf,"X");
          }
          else {
               strcat(mthbuf,"N");
          }
     }
     return(mthbuf);
}

static INT
arprfr(VOID)                                 /* output one ARC record entry */
{                                     /* returns 1=done -1=error 0=continue */
     struct archead ahdr;

     vewscb=&flo->cszview;
     if (vewscb->pos == 0L && vdatmp[0] == 'M' && vdatmp[1] == 'Z') {
          vewscb->pos=0x3352L;
          return(0);
     }
     if (*vdatmp != 0x1a) {
          return(-1);
     }
     cvtData(vdatmp,&ahdr,sizeof(struct archead),archeadFDA,CVTPACKED,CVTSERVER,
             CHAN_NUL);
     if (ahdr.hdrtyp == 0) {
          return(1);
     }
     arpli(ahdr.fname,
           12,
           ahdr.length,
           arcmth[(SHORT)ahdr.hdrtyp],
           ahdr.size,
           ahdr.date,
           ahdr.time,
           (LONG)ahdr.crc,
           0);
     vewscb->pos+=0x1d+ahdr.size;
     return(0);
}

static INT
icprfr(VOID)                                 /* output one ICE record entry */
{                                     /* returns 1=done -1=error 0=continue */
     struct icehead ihdr;
     ULONG crc;
     CHAR mbf[10];

     if (vdatmp[2] != '-' && vdatmp[6] != '-') {
          return(-1);
     }
     cvtData(vdatmp,&ihdr,sizeof(struct icehead),iceheadFDA,CVTPACKED,CVTSERVER,
             CHAN_NUL);
     if (ihdr.hdrtyp[0] == 0) {
          return(1);
     }
     setmem(mbf,10,'\0');
     strncpy(mbf,ihdr.encod,5);
     crc=vdatmp[22+ihdr.fnlen]+256*vdatmp[23+ihdr.fnlen];
     arpli(vdatmp+22,
           min(ihdr.fnlen,18),
           ihdr.length,
           mbf,
           ihdr.size,
           ihdr.date,
           ihdr.time,
           crc,
           0);
     vewscb->pos+=ihdr.fnlen+ihdr.size+(ihdr.encod[3] == '5' ? 0x1B : 0x18);
     return(0);
}

static INT
zoprfr(VOID)                                 /* output one ZOO record entry */
{                                     /* returns 1=done -1=error 0=continue */
     struct zoohead ohdr;

     if (vewscb->pos == 0L) {            /* Skip header portion             */
          vewscb->pos=20L;
          return(0);
     }
     if (vewscb->pos == 20L) {           /* Directory header                */
          vewscb->pos=*(LONG *)(vdatmp+4);
          return(0);
     }
     cvtData(vdatmp,&ohdr,sizeof(struct zoohead),zooheadFDA,CVTPACKED,CVTSERVER,
             CHAN_NUL);
     if (ohdr.head != 0xFDC4A7DCL) {     /* Look for                   */
          return(-1);
     }
     if (ohdr.length == 0
      && ohdr.size == 0
      && ohdr.date == 0
      && ohdr.time == 0
      && ohdr.meth == 0
      && ohdr.crc == 0) {
          return(1);
     }
     arpli(ohdr.fname,
           12,
           ohdr.length,
           zoomth[(SHORT)ohdr.meth],
           ohdr.size,
           ohdr.date,
           ohdr.time,
           (ULONG)ohdr.crc,
           (INT)ohdr.comment);
     vewscb->pos=ohdr.next;
     return(0);
}

INT
vewctn(VOID)                                            /* Continue Viewing */
{
     struct fllib *libptr;
     struct ffblk fb;
     INT rc;
     CHAR *s;

     vewscb=&flo->cszview;
     switch (vewscb->state) {
     case VEWOPN:
          if ((libptr=libfind(vewscb->libname)) == NULL) {
               return(0);
          }
          s=spr("%s"SLS"%s",libpath(libptr),vewscb->filname);
          if (fndfile(&fb,s,0)
           && (vewscb->fp=fopen(s,FOPRB)) != NULL) {
               vewscb->size=fb.ff_fsize;
               vewscb->state=VEWFID;
          }
          else {
               return(0);
          }
          break;
     case VEWFID:
          if (fread(vdatmp,1,VIDCHUNK,vewscb->fp) < VIDCHUNK
           || (vewscb->filid=zafilid(vdatmp)) == UNKFILE) {
               vewscb->state=VEWEND;
          }
          else {
               if ((vewscb->filid == ZP2FILE || vewscb->filid != ZS2FILE)
             && vdatmp[2] == 0x07 && vdatmp[3] == 0x08) {
                    vewscb->state=VEWEND;
                    break;
               }
               vewscb->pos=0L;
               if (vewscb->filid != ZP2FILE && vewscb->filid != ZS2FILE) {
                    vewscb->state=VEWFIL;
               }
               else {
                    if (vewscb->size > (LONG)ZP2CHUNK) {
                         vewscb->pos=vewscb->size-(LONG)ZP2CHUNK;
                    }
                    vewscb->state=VEWFND;
               }
          }
          break;
     case VEWFIL:
          fseek(vewscb->fp,vewscb->pos,0);
          if ((INT)fread(vdatmp,1,VEWCHUNK,vewscb->fp) <= 0) {
               vewscb->state=VEWEND;
               break;
          }
          switch(vewscb->filid) {
          case ARCFILE:
          case PSEFILE:
               rc=arprfr();
               break;
          case ZIPFILE:
          case ZSEFILE:
          case ZP2FILE:
          case ZS2FILE:
               rc=zpprfr();
               break;
          case ICEFILE:
               rc=icprfr();
               break;
          case ZOOFILE:
               rc=zoprfr();
               break;
          default:
               rc=-1;
               break;
          }
          if (rc == -1 || rc == 1) {
               vewscb->state=VEWEND;
          }
          break;
     case VEWFND:
          fseek(vewscb->fp,vewscb->pos,0);
          if ((INT)fread(vdatmp,1,
                vewscb->size > (LONG)ZP2CHUNK ? ZP2CHUNK
                : (INT)vewscb->size,
                vewscb->fp) <= 0) {
               vewscb->state=VEWEND;
               break;
          }
          z2fend();
          break;
     case VEWEND:
          fclose(vewscb->fp);
          vewscb->state=0;
          return(0);
     }
     return(1);
}

static VOID
z2fend(VOID)                      /* find end of central dir record in ZIP2 */
{
     INT state=0;

     if (!fend(vdatmp,&vewscb->pos,&state,vewscb->size)) {
          vewscb->state=VEWEND;
     }
     else if (state != 0) {
          vewscb->state=VEWFIL;
     }
}
