#ifndef XADMASTER_LZX_C
#define XADMASTER_LZX_C

/* Programmheader

	Name:		LZX.c
	Main:		xadmaster
	Versionstring:	$VER: LZX.c 1.4 (29.08.1999)
	Author:		SDI, David Tritscher
	Distribution:	Freeware
	Description:	LZX file archiver client

 1.0   09.02.99 : first working version
 1.1   13.03.99 : now uses register parameters
 1.2   20.06.99 : removed exec.library calls
 1.3   29.06.99 : now uses master free stuff
 1.4   29.08.99 : uses ConvertE.c instead of own routines and
	uses crc copy function
*/

/* The decrunch routines of this client are based on unlzx sources code made
   by David Tritscher. Thanks guy, was a big help. A made the routines
   reentrant (using a parameter structure) and developed the XAD interface
   functions. */

#include <proto/xadmaster.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include "SDI_compiler.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"
#include "ConvertE.c"

#ifndef XADMASTERFILE
#define LZX_Client		FirstClient
#define NEXTCLIENT		0
UBYTE version[] = "$VER: LZX 1.4 (29.08.1999) Freeware";
#endif
#define LZX_VERSION		1
#define LZX_REVISION		4

/* ---------------------------------------------------------------------- */

#define LZXINFO_DAMAGE_PROTECT 1
#define LZXINFO_FLAG_LOCKED 2

struct LZXInfo_Header
{
  UBYTE ID[3];			/* "LZX" */
  UBYTE Flags;			/* LZXINFO_FLAG_#? */
  UBYTE Unknown[6];		
};

#define LZXHDR_FLAG_MERGED	(1<<0)

#define LZXHDR_PROT_READ	(1<<0)
#define LZXHDR_PROT_WRITE	(1<<1)
#define LZXHDR_PROT_DELETE	(1<<2)
#define LZXHDR_PROT_EXECUTE	(1<<3)
#define LZXHDR_PROT_ARCHIVE	(1<<4)
#define LZXHDR_PROT_HOLD	(1<<5)
#define LZXHDR_PROT_SCRIPT	(1<<6)
#define LZXHDR_PROT_PURE	(1<<7)

#define LZXHDR_TYPE_MSDOS	0
#define LZXHDR_TYPE_WINDOWS	1
#define LZXHDR_TYPE_OS2		2
#define LZXHDR_TYPE_AMIGA	10
#define LZXHDR_TYPE_UNIX	20

#define LZXHDR_PACK_STORE	0
#define LZXHDR_PACK_NORMAL	2
#define LZXHDR_PACK_EOF		32

struct LZXArc_Header
{
  UBYTE Attributes;		/*  0 - LZXHDR_PROT_#? */
  UBYTE pad1;			/*  1 */
  ULONG FileSize;		/*  2 (little endian) */
  ULONG CrSize;			/*  6 (little endian) */
  UBYTE MachineType;		/* 10 - LZXHDR_TYPE_#? */
  UBYTE PackMode;		/* 11 - LZXHDR_PACK_#? */
  UBYTE Flags;			/* 12 - LZXHDR_FLAG_#? */
  UBYTE pad2;			/* 13 */
  UBYTE CommentSize;		/* 14 - length (0-79) */
  UBYTE ExtractVersion;		/* 15 - version needed to extract */
  UBYTE pad3;			/* 16 */
  UBYTE pad4;			/* 17 */
  ULONG Date;			/* 18 - Packed_Date */
  ULONG DataCRC;		/* 22 (little endian) */
  ULONG HeaderCRC;		/* 26 (little endian) */
  UBYTE FilenameSize;		/* 30 - filename length */
}; /* SIZE = 31 */

/* Header CRC includes filename and comment. */

#define LZXHEADERSIZE	31

/* Packed date [4 BYTES, bit 0 is MSB, 31 is LSB]
  bit  0 -  4	Day
       5 -  8	Month	(January is 0)
       9 - 14	Year	(start 1970)
      15 - 19	Hour
      20 - 25	Minute
      26 - 31	Second
*/

struct LZXEntryData {
  ULONG CRC;		/* CRC of uncrunched data */
  ULONG PackMode;	/* CrunchMode */
  ULONG ArchivePos;	/* Position is source file */
  ULONG DataStart;	/* Position in merged buffer */
};

#define LZXPE(a)	((struct LZXEntryData *) ((a)->xfi_PrivateInfo))
#define LZXDD(a)	((struct LZXDecrData *) ((a)->xai_PrivateClient))
struct LZXDecrData {
  ULONG ArchivePos;	/* The Archive-Pos to detect if it is correct buffer */
  ULONG DataPos;	/* must be lower or equal to current entry or reinit is necessary */

  UBYTE *source;
  UBYTE *destination;
  UBYTE *source_end;
  UBYTE *destination_end;
  UBYTE *pos;

  ULONG decrunch_method;
  ULONG decrunch_length;
  ULONG pack_size;
  ULONG last_offset;
  ULONG control;
  LONG  shift;

  UBYTE offset_len[8];
  UWORD offset_table[128];
  UBYTE huffman20_len[20];
  UWORD huffman20_table[96];
  UBYTE literal_len[768];
  UWORD literal_table[5120];

  UBYTE read_buffer[16384];		/* have a reasonable sized read buffer */
  UBYTE decrunch_buffer[258+65536+258];	/* allow overrun for speed */
};

ASM(BOOL) LZX_RecogData(REG(d0, ULONG size), REG(a0, STRPTR data),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(data[0] == 'L' && data[1] == 'Z' && data[2] == 'X')
    return 1;
  else
    return 0;
}

ASM(LONG) LZX_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG err, num = 1;
  ULONG bufpos = 0;
  struct xadFileInfo *fi = 0, *fi2, *fig = 0; /* fig - first grouped ptr */
  struct LZXArc_Header head;

  if(!(err = xadHookAccess(XADAC_INPUTSEEK, sizeof(struct LZXInfo_Header), 0, ai)))
  {
    while(!err && ai->xai_InPos < ai->xai_InSize)
    {
      if(!(err = xadHookAccess(XADAC_READ, LZXHEADERSIZE, &head, ai)))
      {
	ULONG i, j, k, l, crc;
        i = head.CommentSize;
        j = head.FilenameSize;
        k = EndConvI32(head.HeaderCRC);
        head.HeaderCRC = 0; /* clear for CRC check */

        if(!(fi2 = (struct xadFileInfo *) xadAllocObject(XADOBJ_FILEINFO,
        XAD_OBJNAMESIZE, j+1, i ? XAD_OBJCOMMENTSIZE : TAG_IGNORE, i+1,
        XAD_OBJPRIVINFOSIZE, sizeof(struct LZXEntryData), TAG_DONE)))
          err = XADERR_NOMEMORY;
        else if(!(err = xadHookAccess(XADAC_READ, j, fi2->xfi_FileName, ai)) &&
        (!i || !(err = xadHookAccess(XADAC_READ, i, fi2->xfi_Comment, ai))))
        {
          l = EndConvI32(head.CrSize);

          if(!l || !(err = xadHookAccess(XADAC_INPUTSEEK, l, 0, ai)))
          {
            crc = xadCalcCRC32(XADCRC32_ID1, ~0, LZXHEADERSIZE, (STRPTR) &head);
            crc = xadCalcCRC32(XADCRC32_ID1, crc, j, fi2->xfi_FileName);
            if(i)
              crc = xadCalcCRC32(XADCRC32_ID1, crc, i, fi2->xfi_Comment);

            if(~crc != k)
              err = XADERR_CHECKSUM;
            else
            {
              if(!fig)
              {
                fig = fi2; bufpos = 0;
              }
	      fi2->xfi_Size = EndConvI32(head.FileSize);
	      fi2->xfi_EntryNumber = num++;
	      if(!l && !fi2->xfi_Size && fi2->xfi_FileName[--j] == '/')
	      {
	        fi2->xfi_FileName[j] = 0;
	        fi2->xfi_Flags |= XADFIF_DIRECTORY;
	      }

	      i = head.Attributes;
	      j = 0;

	      if(!(i & LZXHDR_PROT_READ))
	        j |= FIBF_READ;
	      if(!(i & LZXHDR_PROT_WRITE))
	        j |= FIBF_WRITE;
	      if(!(i & LZXHDR_PROT_DELETE))
	        j |= FIBF_DELETE;
	      if(!(i & LZXHDR_PROT_EXECUTE))
	        j |= FIBF_EXECUTE;
	      j |= (i & (LZXHDR_PROT_ARCHIVE|LZXHDR_PROT_SCRIPT));
	      if(i & LZXHDR_PROT_PURE)
	        j |= FIBF_PURE;
	      if(i & LZXHDR_PROT_HOLD)
	        j |= (1<<7);	/* not defined in <dos/dos.h> */
	      fi2->xfi_Protection = j;

	      { /* Make the date */
	        struct xadDate d;
	        j = head.Date;
	        d.xd_Second = j & 63;
	        j >>= 6;
	        d.xd_Minute = j & 63;
	        j >>= 6;
	        d.xd_Hour = j & 31;
	        j >>= 5;
	        d.xd_Year = 1970 + (j & 63);
	        j >>= 6;
	        d.xd_Month = 1 + (j & 15);
	        j >>= 4;
	        d.xd_Day = j;
	        d.xd_Micros = 0;
                xadConvertDates(XAD_DATEXADDATE, &d, XAD_GETDATEXADDATE,
                &fi2->xfi_Date, TAG_DONE);
	      }
	      LZXPE(fi2)->CRC = EndConvI32(head.DataCRC);
	      LZXPE(fi2)->DataStart = bufpos;
	      bufpos += fi2->xfi_Size;
	      if(head.Flags & LZXHDR_FLAG_MERGED)
	      {
	        fi2->xfi_Flags |= XADFIF_GROUPED;
	        if(l)
	        {
	          fi2->xfi_Flags |= XADFIF_ENDOFGROUP;
	          fi2->xfi_GroupCrSize = l;
	        }
	      }
	      else
	        fi2->xfi_CrunchSize = l;
	      
	      if(l)
	      {
	        LZXPE(fi2)->ArchivePos = ai->xai_InPos-l;
	        LZXPE(fi2)->PackMode = head.PackMode;
	        while(fig)
	        {
	          fig->xfi_GroupCrSize = l;
	          LZXPE(fig)->ArchivePos = ai->xai_InPos-l;
	          LZXPE(fig)->PackMode = head.PackMode;
                  fig = fig->xfi_Next;
                }
              }

              if(!fi)
                ai->xai_FileInfo = fi2;
              else
                fi->xfi_Next = fi2;
              fi = fi2;
              fi2 = 0;
            } /* skip crunched data */
          } /* get filename and comment */
          if(fi2)
            xadFreeObjectA(fi2,0);
        } /* xadFileInfo Allocation */
      } /* READ header */
    } /* while loop */
  } /* INPUTSEEK 3 bytes */

  if(err && ai->xai_FileInfo)
  {
    ai->xai_Flags |= XADAIF_FILECORRUPT;
    ai->xai_LastError = err;
    err = 0;
  }

  return err;
}

/* ---------------------------------------------------------------------- */

static const UBYTE LZXtable_one[32] = {
  0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14
};

static const ULONG LZXtable_two[32] = {
  0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,
  1536,2048,3072,4096,6144,8192,12288,16384,24576,32768,49152,
};

static const UWORD LZXmask_bits[16] = {
  0x0000,0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,
  0x00FF,0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,
};

static const UBYTE LZXtable_four[34] = {
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
  0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
};

/* ---------------------------------------------------------------------- */

/* Build a fast huffman decode table from the symbol bit lengths.         */
/* There is an alternate algorithm which is faster but also more complex. */

static LONG LZXmake_decode_table(LONG number_symbols, LONG LZXtable_size,
                      UBYTE *length, unsigned short *table)
{
 register UBYTE bit_num = 0;
 register LONG symbol;
 ULONG leaf; /* could be a register */
 ULONG LZXtable_mask, bit_mask, pos, fill, next_symbol, reverse;
 LONG abort = 0;

 pos = 0; /* consistantly used as the current position in the decode table */

 bit_mask = LZXtable_mask = 1 << LZXtable_size;

 bit_mask >>= 1; /* don't do the first number */
 bit_num++;

 while((!abort) && (bit_num <= LZXtable_size))
 {
  for(symbol = 0; symbol < number_symbols; symbol++)
  {
   if(length[symbol] == bit_num)
   {
    reverse = pos; /* reverse the order of the position's bits */
    leaf = 0;
    fill = LZXtable_size;
    do /* reverse the position */
    {
     leaf = (leaf << 1) + (reverse & 1);
     reverse >>= 1;
    } while(--fill);
    if((pos += bit_mask) > LZXtable_mask)
    {
     abort = 1;
     break; /* we will overrun the table! abort! */
    }
    fill = bit_mask;
    next_symbol = 1 << bit_num;
    do
    {
     table[leaf] = symbol;
     leaf += next_symbol;
    } while(--fill);
   }
  }
  bit_mask >>= 1;
  bit_num++;
 }

 if((!abort) && (pos != LZXtable_mask))
 {
  for(symbol = pos; symbol < LZXtable_mask; symbol++) /* clear the rest of the table */
  {
   reverse = symbol; /* reverse the order of the position's bits */
   leaf = 0;
   fill = LZXtable_size;
   do /* reverse the position */
   {
    leaf = (leaf << 1) + (reverse & 1);
    reverse >>= 1;
   } while(--fill);
   table[leaf] = 0;
  }
  next_symbol = LZXtable_mask >> 1;
  pos <<= 16;
  LZXtable_mask <<= 16;
  bit_mask = 32768;

  while((!abort) && (bit_num <= 16))
  {
   for(symbol = 0; symbol < number_symbols; symbol++)
   {
    if(length[symbol] == bit_num)
    {
     reverse = pos >> 16; /* reverse the order of the position's bits */
     leaf = 0;
     fill = LZXtable_size;
     do /* reverse the position */
     {
      leaf = (leaf << 1) + (reverse & 1);
      reverse >>= 1;
     } while(--fill);
     for(fill = 0; fill < bit_num - LZXtable_size; fill++)
     {
      if(table[leaf] == 0)
      {
       table[(next_symbol << 1)] = 0;
       table[(next_symbol << 1) + 1] = 0;
       table[leaf] = next_symbol++;
      }
      leaf = table[leaf] << 1;
      leaf += (pos >> (15 - fill)) & 1;
     }
     table[leaf] = symbol;
     if((pos += bit_mask) > LZXtable_mask)
     {
      abort = 1;
      break; /* we will overrun the table! abort! */
     }
    }
   }
   bit_mask >>= 1;
   bit_num++;
  }
 }
 if(pos != LZXtable_mask) abort = 1; /* the table is incomplete! */

 return(abort);
}

/* ---------------------------------------------------------------------- */
/* Read and build the decrunch tables. There better be enough data in the */
/* source buffer or it's stuffed. */

static LONG LZX_read_literal_table(struct LZXDecrData *decr)
{
 register ULONG control;
 register LONG shift;
 ULONG temp; /* could be a register */
 ULONG symbol, pos, count, fix, max_symbol;
 UBYTE *source;
 LONG abort = 0;

 source = decr->source;
 control = decr->control;
 shift = decr->shift;

 if(shift < 0) /* fix the control word if necessary */
 {
  shift += 16;
  control += *source++ << (8 + shift);
  control += *source++ << shift;
 }

/* read the decrunch method */

 decr->decrunch_method = control & 7;
 control >>= 3;
 if((shift -= 3) < 0)
 {
  shift += 16;
  control += *source++ << (8 + shift);
  control += *source++ << shift;
 }

/* Read and build the offset huffman table */

 if((!abort) && (decr->decrunch_method == 3))
 {
  for(temp = 0; temp < 8; temp++)
  {
   decr->offset_len[temp] = control & 7;
   control >>= 3;
   if((shift -= 3) < 0)
   {
    shift += 16;
    control += *source++ << (8 + shift);
    control += *source++ << shift;
   }
  }
  abort = LZXmake_decode_table(8, 7, decr->offset_len, decr->offset_table);
 }

/* read decrunch length */

 if(!abort)
 {
  decr->decrunch_length = (control & 255) << 16;
  control >>= 8;
  if((shift -= 8) < 0)
  {
   shift += 16;
   control += *source++ << (8 + shift);
   control += *source++ << shift;
  }
  decr->decrunch_length += (control & 255) << 8;
  control >>= 8;
  if((shift -= 8) < 0)
  {
   shift += 16;
   control += *source++ << (8 + shift);
   control += *source++ << shift;
  }
  decr->decrunch_length += (control & 255);
  control >>= 8;
  if((shift -= 8) < 0)
  {
   shift += 16;
   control += *source++ << (8 + shift);
   control += *source++ << shift;
  }
 }

/* read and build the huffman literal table */

 if((!abort) && (decr->decrunch_method != 1))
 {
  pos = 0;
  fix = 1;
  max_symbol = 256;

  do
  {
   for(temp = 0; temp < 20; temp++)
   {
    decr->huffman20_len[temp] = control & 15;
    control >>= 4;
    if((shift -= 4) < 0)
    {
     shift += 16;
     control += *source++ << (8 + shift);
     control += *source++ << shift;
    }
   }
   abort = LZXmake_decode_table(20, 6, decr->huffman20_len, decr->huffman20_table);

   if(abort) break; /* argh! table is corrupt! */

   do
   {
    if((symbol = decr->huffman20_table[control & 63]) >= 20)
    {
     do /* symbol is longer than 6 bits */
     {
      symbol = decr->huffman20_table[((control >> 6) & 1) + (symbol << 1)];
      if(!shift--)
      {
       shift += 16;
       control += *source++ << 24;
       control += *source++ << 16;
      }
      control >>= 1;
     } while(symbol >= 20);
     temp = 6;
    }
    else
    {
     temp = decr->huffman20_len[symbol];
    }
    control >>= temp;
    if((shift -= temp) < 0)
    {
     shift += 16;
     control += *source++ << (8 + shift);
     control += *source++ << shift;
    }
    switch(symbol)
    {
     case 17:
     case 18:
     {
      if(symbol == 17)
      {
       temp = 4;
       count = 3;
      }
      else /* symbol == 18 */
      {
       temp = 6 - fix;
       count = 19;
      }
      count += (control & LZXmask_bits[temp]) + fix;
      control >>= temp;
      if((shift -= temp) < 0)
      {
       shift += 16;
       control += *source++ << (8 + shift);
       control += *source++ << shift;
      }
      while((pos < max_symbol) && (count--))
       decr->literal_len[pos++] = 0;
      break;
     }
     case 19:
     {
      count = (control & 1) + 3 + fix;
      if(!shift--)
      {
       shift += 16;
       control += *source++ << 24;
       control += *source++ << 16;
      }
      control >>= 1;
      if((symbol = decr->huffman20_table[control & 63]) >= 20)
      {
       do /* symbol is longer than 6 bits */
       {
        symbol = decr->huffman20_table[((control >> 6) & 1) + (symbol << 1)];
        if(!shift--)
        {
         shift += 16;
         control += *source++ << 24;
         control += *source++ << 16;
        }
        control >>= 1;
       } while(symbol >= 20);
       temp = 6;
      }
      else
      {
       temp = decr->huffman20_len[symbol];
      }
      control >>= temp;
      if((shift -= temp) < 0)
      {
       shift += 16;
       control += *source++ << (8 + shift);
       control += *source++ << shift;
      }
      symbol = LZXtable_four[decr->literal_len[pos] + 17 - symbol];
      while((pos < max_symbol) && (count--))
       decr->literal_len[pos++] = symbol;
      break;
     }
     default:
     {
      symbol = LZXtable_four[decr->literal_len[pos] + 17 - symbol];
      decr->literal_len[pos++] = symbol;
      break;
     }
    }
   } while(pos < max_symbol);
   fix--;
   max_symbol += 512;
  } while(max_symbol == 768);

  if(!abort)
   abort = LZXmake_decode_table(768, 12, decr->literal_len, decr->literal_table);
 }

 decr->control = control;
 decr->shift = shift;
 decr->source = source;
 return(abort);
}

/* ---------------------------------------------------------------------- */

/* Fill up the decrunch buffer. Needs lots of overrun for both destination */
/* and source buffers. Most of the time is spent in this routine so it's  */
/* pretty damn optimized. */
static void LZXdecrunch(struct LZXDecrData *decr)
{
 register ULONG control;
 register LONG shift;
 ULONG temp; /* could be a register */
 ULONG symbol, count;
 UBYTE *string, *source, *destination;

 control = decr->control;
 shift = decr->shift;
 source = decr->source;
 destination = decr->destination;

 do
 {
  if((symbol = decr->literal_table[control & 4095]) >= 768)
  {
   control >>= 12;
   if((shift -= 12) < 0)
   {
    shift += 16;
    control += *source++ << (8 + shift);
    control += *source++ << shift;
   }
   do /* literal is longer than 12 bits */
   {
    symbol = decr->literal_table[(control & 1) + (symbol << 1)];
    if(!shift--)
    {
     shift += 16;
     control += *source++ << 24;
     control += *source++ << 16;
    }
    control >>= 1;
   } while(symbol >= 768);
  }
  else
  {
   temp = decr->literal_len[symbol];
   control >>= temp;
   if((shift -= temp) < 0)
   {
    shift += 16;
    control += *source++ << (8 + shift);
    control += *source++ << shift;
   }
  }
  if(symbol < 256)
  {
   *destination++ = symbol;
  }
  else
  {
   symbol -= 256;
   count = LZXtable_two[temp = symbol & 31];
   temp = LZXtable_one[temp];
   if((temp >= 3) && (decr->decrunch_method == 3))
   {
    temp -= 3;
    count += ((control & LZXmask_bits[temp]) << 3);
    control >>= temp;
    if((shift -= temp) < 0)
    {
     shift += 16;
     control += *source++ << (8 + shift);
     control += *source++ << shift;
    }
    count += (temp = decr->offset_table[control & 127]);
    temp = decr->offset_len[temp];
   }
   else
   {
    count += control & LZXmask_bits[temp];
    if(!count) count = decr->last_offset;
   }
   control >>= temp;
   if((shift -= temp) < 0)
   {
    shift += 16;
    control += *source++ << (8 + shift);
    control += *source++ << shift;
   }
   decr->last_offset = count;

   count = LZXtable_two[temp = (symbol >> 5) & 15] + 3;
   temp = LZXtable_one[temp];
   count += (control & LZXmask_bits[temp]);
   control >>= temp;
   if((shift -= temp) < 0)
   {
    shift += 16;
    control += *source++ << (8 + shift);
    control += *source++ << shift;
   }
   string = (decr->decrunch_buffer + decr->last_offset < destination) ?
            destination - decr->last_offset : destination + 65536 - decr->last_offset;
   do
   {
    *destination++ = *string++;
   } while(--count);
  }
 } while((destination < decr->destination_end) && (source < decr->source_end));

 decr->control = control;
 decr->shift = shift;
 decr->source = source;
 decr->destination = destination;
}

/* ---------------------------------------------------------------------- */

static LONG LZXextract(struct xadArchiveInfo *ai, struct xadMasterBase *xadMasterBase,
ULONG unpack_size, ULONG rescrc)
{
  UBYTE *temp;
  ULONG count, crc = ~0;
  LONG err;
  struct LZXDecrData *decr;

  decr = (struct LZXDecrData *) ai->xai_PrivateClient;

  while(unpack_size > 0)
  {
    if(decr->pos == decr->destination) /* time to fill the buffer? */
    {
      /* check if we have enough data and read some if not */
      if(decr->source >= decr->source_end) /* have we exhausted the current read buffer? */
      {
        temp = decr->read_buffer;
        if((count = temp - decr->source + 16384))
        {
          do /* copy the remaining overrun to the start of the buffer */
          {
            *temp++ = *(decr->source++);
          } while(--count);
        }
        decr->source = decr->read_buffer;
        count = decr->source - temp + 16384;

        if(decr->pack_size < count)
          count = decr->pack_size; /* make sure we don't read too much */

        if((err = xadHookAccess(XADAC_READ, count, temp, ai)))
          return err;
        decr->pack_size -= count;

        temp += count;
        if(decr->source >= temp)
          return XADERR_DECRUNCH; /* argh! no more data! */
      } /* if(decr->source >= decr->source_end) */

    /* check if we need to read the tables */
    if(decr->decrunch_length <= 0)
    {
      if(LZX_read_literal_table(decr))
        return XADERR_DECRUNCH; /* argh! can't make huffman tables! */
    }

    /* unpack some data */
    if(decr->destination >= decr->decrunch_buffer + 258 + 65536)
    {
      if((count = decr->destination - decr->decrunch_buffer - 65536))
      {
        temp = (decr->destination = decr->decrunch_buffer) + 65536;
        do /* copy the overrun to the start of the buffer */
        {
          *(decr->destination++) = *temp++;
        } while(--count);
      }
      decr->pos = decr->destination;
    }
    decr->destination_end = decr->destination + decr->decrunch_length;
    if(decr->destination_end > decr->decrunch_buffer + 258 + 65536)
      decr->destination_end = decr->decrunch_buffer + 258 + 65536;
    temp = decr->destination;

    LZXdecrunch(decr);

    decr->decrunch_length -= (decr->destination - temp);
   }

/* calculate amount of data we can use before we need to fill the buffer again */
   count = decr->destination - decr->pos;
   if(count > unpack_size)
     count = unpack_size; /* take only what we need */

   if(rescrc) /* when no CRC given, then skip writing */
   {
     crc = xadCalcCRC32(XADCRC32_ID1, crc, count, decr->pos);
     if((err = xadHookAccess(XADAC_WRITE, count, decr->pos, ai)))
       return err;
   }
   unpack_size -= count;
   decr->pos += count;
   decr->DataPos += count;
 }

 if(rescrc && ~crc != rescrc)
   return XADERR_CHECKSUM;

 return 0;
}

/* ---------------------------------------------------------------------- */

ASM(LONG) LZX_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct xadFileInfo *fi;
  struct LZXDecrData *decr = 0;
  LONG ret = 0, i;
  ULONG crc = ~0;

  fi = ai->xai_CurFile;
  if(!ai->xai_PrivateClient || LZXDD(ai)->ArchivePos != LZXPE(fi)->ArchivePos
  || LZXDD(ai)->DataPos > LZXPE(fi)->DataStart)
  {
    if(ai->xai_PrivateClient) /* free the unneeded client */
    {
      xadFreeObjectA(ai->xai_PrivateClient, 0);
      ai->xai_PrivateClient = 0;
    }
    if((i = LZXPE(fi)->ArchivePos - ai->xai_InPos))
    {
      if((ret = xadHookAccess(XADAC_INPUTSEEK, i, 0, ai)))
        return ret;
    }
  }

  switch(LZXPE(fi)->PackMode)
  {
  case LZXHDR_PACK_STORE:
    if(!(ret = xadHookTagAccess(XADAC_COPY, fi->xfi_Size, 0, ai, XAD_GETCRC32, &crc, TAG_DONE)) && ~crc != LZXPE(fi)->CRC)
      ret = XADERR_CHECKSUM;
    break;
  case LZXHDR_PACK_NORMAL:
    if(!ai->xai_PrivateClient && !(decr = (struct LZXDecrData *)
    xadAllocVec(sizeof(struct LZXDecrData), MEMF_PUBLIC|MEMF_CLEAR)))
      ret = XADERR_NOMEMORY;
    else
    {
      if(decr)
      {
        decr->ArchivePos = LZXPE(fi)->ArchivePos;
        decr->DataPos = 0;
        decr->shift = -16;
        decr->last_offset = 1;
        decr->source_end = (decr->source = decr->read_buffer + 16384) - 1024;
        decr->pos = decr->destination_end = decr->destination = decr->decrunch_buffer + 258 + 65536;
        decr->pack_size = fi->xfi_Flags & XADFIF_GROUPED ?
        fi->xfi_GroupCrSize : fi->xfi_CrunchSize;
        ai->xai_PrivateClient = decr;
      }

      if((i = LZXPE(fi)->DataStart - LZXDD(ai)->DataPos))
	ret = LZXextract(ai, xadMasterBase, i, 0);
	
      if(!ret)
	ret = LZXextract(ai, xadMasterBase, fi->xfi_Size, LZXPE(fi)->CRC);

      /* free no longer needed temporary buffer and stuff structure */
      if(ret || !(fi->xfi_Flags & XADFIF_GROUPED) || (fi->xfi_Flags & XADFIF_ENDOFGROUP))
      {
        xadFreeObjectA(ai->xai_PrivateClient, 0);
        ai->xai_PrivateClient = 0;
      }
    }
    break;
  default: ret = XADERR_DECRUNCH; break;
  }

  return ret;
}

ASM(void) LZX_Free(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(ai->xai_PrivateClient) /* decrunch buffer */
  {
    xadFreeObjectA(ai->xai_PrivateClient, 0);
    ai->xai_PrivateClient = 0;
  }
}

const struct xadClient LZX_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 3, LZX_VERSION, LZX_REVISION,
10, XADCF_FILEARCHIVER|XADCF_FREEFILEINFO, XADCID_LZX, "LZX",
(BOOL (*)()) LZX_RecogData, (LONG (*)()) LZX_GetInfo,
(LONG (*)()) LZX_UnArchive, (void (*)()) LZX_Free};

#endif /* XADASTER_LZX_C */
