#ifndef XADMASTER_STUFFIT_C
#define XADMASTER_STUFFIT_C

/* Programmheader

	Name:		StuffIt.c
	Main:		xadmaster
	Versionstring:	$VER: StuffIt.c 1.7 (30.12.2001)
	Author:		SDI
	Distribution:	GPL
	Description:	StuffIt file archiver client

 1.0   23.08.00 : first version
 1.1   23.10.00 : first complete StuffIt5 scanner, StuffIt5-Exe
 1.2   30.10.00 : now uses xadIO.c for all algorithms
 1.3   14.07.01 : added algorithm 8, uses xadAddEntry funcs
 1.4   12.08.01 : added MacBinary client, added StuffIt fixes
 1.5   19.09.01 : finally added algorithm 13
 1.6   02.10.01 : little fixes, added ID's
 1.7   30.12.01 : added algorithm 14
*/

/* StuffIt file archiver client for XAD.
 * Copyright (C) 2000-2001 Dirk Stöcker <stoecker@epost.de>
 *
 * little based on macutils 2.0b3 macunpack by Dik T. Winter
 * Copyright (C) 1992 Dik T. Winter <dik@cwi.nl>
 *
 * 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.
 */

//#define DEBUG

#include <proto/xadmaster.h>
#include <exec/memory.h>
#include "SDI_compiler.h"
#include "ConvertE.c"
#define XADIOGETBITSHIGH
#define XADIOGETBITSLOW
#define XADIOREADBITSLOW
#include "xadIO.c"
#include "xadCPU.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"

#ifndef XADMASTERFILE
#define SIT_Client		FirstClient
#define NEXTCLIENT		0
#define XADMASTERVERSION	10
UBYTE version[] = "$VER: StuffIt 1.7 (30.12.2001)" CPUTEXT " GPL by Dirk Stöcker";
#endif
#define SIT_VERSION		1
#define SIT_REVISION		7
#define SIT5_VERSION		SIT_VERSION
#define SIT5_REVISION		SIT_REVISION
#define SIT5EXE_VERSION		SIT_VERSION
#define SIT5EXE_REVISION	SIT_REVISION
#define MACBINARY_VERSION	SIT_VERSION
#define MACBINARY_REVISION	SIT_REVISION

#define SITFH_COMPRMETHOD    0 /* UBYTE rsrc fork compression method */
#define SITFH_COMPDMETHOD    1 /* UBYTE data fork compression method */
#define SITFH_FNAMESIZE      2 /* UBYTE filename size */
#define SITFH_FNAME          3 /* UBYTE 83 byte filename */
#define SITFH_FTYPE         66 /* ULONG file type */
#define SITFH_CREATOR       70 /* ULONG file creator */
#define SITFH_FNDRFLAGS     74 /* UWORD Finder flags */
#define SITFH_CREATIONDATE  76 /* ULONG creation date */
#define SITFH_MODDATE       80 /* ULONG modification date */
#define SITFH_RSRCLENGTH    84 /* ULONG decompressed rsrc length */
#define SITFH_DATALENGTH    88 /* ULONG decompressed data length */
#define SITFH_COMPRLENGTH   92 /* ULONG compressed rsrc length */
#define SITFH_COMPDLENGTH   96 /* ULONG compressed data length */
#define SITFH_RSRCCRC      100 /* UWORD crc of rsrc fork */
#define SITFH_DATACRC      102 /* UWORD crc of data fork */ /* 6 reserved bytes */
#define SITFH_HDRCRC       110 /* UWORD crc of file header */
#define SIT_FILEHDRSIZE    112

#define SITAH_SIGNATURE    0 /* ULONG signature = 'SIT!' */
#define SITAH_NUMFILES     4 /* UWORD number of files in archive */
#define SITAH_ARCLENGTH    6 /* ULONG arcLength length of entire archive incl. header */
#define SITAH_SIGNATURE2  10 /* ULONG signature2 = 'rLau' */
#define	SITAH_VERSION     14 /* UBYTE version number */
#define SIT_ARCHDRSIZE    22 /* +7 reserved bytes */

/* compression methods */
#define SITnocomp	0	/* just read each byte and write it to archive */
#define SITrle		1	/* RLE compression */
#define SITlzc		2	/* LZC compression */
#define SIThuffman	3	/* Huffman compression */

#define SITlzah		5	/* LZ with adaptive Huffman */
#define SITfixhuf	6	/* Fixed Huffman table */

#define SITmw		8	/* Miller-Wegman encoding */

#define SITprot		16	/* password protected bit */
#define SITsfolder	32	/* start of folder */
#define SITefolder	33	/* end of folder */

struct SITPrivate {
  UWORD CRC;
  UBYTE Method;
};

#define SITPI(a)	((struct SITPrivate *) ((a)->xfi_PrivateInfo))

/*****************************************************************************/

static UWORD crctable[256] = {
  0x0000,0x1021,0x2042,0x3063,0x4084,0x50A5,0x60C6,0x70E7,0x8108,0x9129,
  0xA14A,0xB16B,0xC18C,0xD1AD,0xE1CE,0xF1EF,0x1231,0x0210,0x3273,0x2252,
  0x52B5,0x4294,0x72F7,0x62D6,0x9339,0x8318,0xB37B,0xA35A,0xD3BD,0xC39C,
  0xF3FF,0xE3DE,0x2462,0x3443,0x0420,0x1401,0x64E6,0x74C7,0x44A4,0x5485,
  0xA56A,0xB54B,0x8528,0x9509,0xE5EE,0xF5CF,0xC5AC,0xD58D,0x3653,0x2672,
  0x1611,0x0630,0x76D7,0x66F6,0x5695,0x46B4,0xB75B,0xA77A,0x9719,0x8738,
  0xF7DF,0xE7FE,0xD79D,0xC7BC,0x48C4,0x58E5,0x6886,0x78A7,0x0840,0x1861,
  0x2802,0x3823,0xC9CC,0xD9ED,0xE98E,0xF9AF,0x8948,0x9969,0xA90A,0xB92B,
  0x5AF5,0x4AD4,0x7AB7,0x6A96,0x1A71,0x0A50,0x3A33,0x2A12,0xDBFD,0xCBDC,
  0xFBBF,0xEB9E,0x9B79,0x8B58,0xBB3B,0xAB1A,0x6CA6,0x7C87,0x4CE4,0x5CC5,
  0x2C22,0x3C03,0x0C60,0x1C41,0xEDAE,0xFD8F,0xCDEC,0xDDCD,0xAD2A,0xBD0B,
  0x8D68,0x9D49,0x7E97,0x6EB6,0x5ED5,0x4EF4,0x3E13,0x2E32,0x1E51,0x0E70,
  0xFF9F,0xEFBE,0xDFDD,0xCFFC,0xBF1B,0xAF3A,0x9F59,0x8F78,0x9188,0x81A9,
  0xB1CA,0xA1EB,0xD10C,0xC12D,0xF14E,0xE16F,0x1080,0x00A1,0x30C2,0x20E3,
  0x5004,0x4025,0x7046,0x6067,0x83B9,0x9398,0xA3FB,0xB3DA,0xC33D,0xD31C,
  0xE37F,0xF35E,0x02B1,0x1290,0x22F3,0x32D2,0x4235,0x5214,0x6277,0x7256,
  0xB5EA,0xA5CB,0x95A8,0x8589,0xF56E,0xE54F,0xD52C,0xC50D,0x34E2,0x24C3,
  0x14A0,0x0481,0x7466,0x6447,0x5424,0x4405,0xA7DB,0xB7FA,0x8799,0x97B8,
  0xE75F,0xF77E,0xC71D,0xD73C,0x26D3,0x36F2,0x0691,0x16B0,0x6657,0x7676,
  0x4615,0x5634,0xD94C,0xC96D,0xF90E,0xE92F,0x99C8,0x89E9,0xB98A,0xA9AB,
  0x5844,0x4865,0x7806,0x6827,0x18C0,0x08E1,0x3882,0x28A3,0xCB7D,0xDB5C,
  0xEB3F,0xFB1E,0x8BF9,0x9BD8,0xABBB,0xBB9A,0x4A75,0x5A54,0x6A37,0x7A16,
  0x0AF1,0x1AD0,0x2AB3,0x3A92,0xFD2E,0xED0F,0xDD6C,0xCD4D,0xBDAA,0xAD8B,
  0x9DE8,0x8DC9,0x7C26,0x6C07,0x5C64,0x4C45,0x3CA2,0x2C83,0x1CE0,0x0CC1,
  0xEF1F,0xFF3E,0xCF5D,0xDF7C,0xAF9B,0xBFBA,0x8FD9,0x9FF8,0x6E17,0x7E36,
  0x4E55,0x5E74,0x2E93,0x3EB2,0x0ED1,0x1EF0
};

static UWORD DoCRC(UBYTE * Mem, LONG Size)
{
  UWORD CRC = 0;

  while(Size--)
    CRC = crctable[((CRC>>8) ^ *(Mem++)) & 0xFF] ^ (CRC<<8);

  return CRC;
}

/*****************************************************************************/

#define STUFFITMAXALGO	15
static const STRPTR sittypes[] = {
"NoComp", "RLE", "LZC", "Huffmann", "4", "LZAH", "FixHuff", "7", "MW",
"9", "10", "11", "12", "TableHuff", "Installer", "15"};

ASM(BOOL) SIT_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *data),
REG(a6, struct xadMasterBase *xadMasterBase))
{ CPUCHECK
  if(EndGetM32(data+10) == 0x724C6175)
  {
    if(EndGetM32(data) == 0x53495421)
      return 1;
    /* Installer archives? */
    if(data[0] == 'S' && data[1] == 'T')
    {
      if(data[2] == 'i')
      {
        if(data[3] == 'n' || (data[3] >= '0' && data[3] <= '9'))
          return 1;
      }
      else if(data[2] >= '0' && data[2] <= '9' && data[3] >= '0' && data[3] <= '9')
        return 1;
    }
  }
  return 0;
}

/*****************************************************************************/

#ifdef DEBUG
#include <proto/intuition.h>
#include <proto/exec.h>
static void UnknownRequest(struct xadMasterBase *xadMasterBase, ULONG num, STRPTR name)
{
  struct IntuitionBase *IntuitionBase;
  struct ExecBase *SysBase = xadMasterBase->xmb_SysBase;
  
  if((IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37)))
  {
    struct EasyStruct easystruct;

    memset(&easystruct, 0, sizeof(struct EasyStruct));

    easystruct.es_StructSize = sizeof(struct EasyStruct);
    easystruct.es_Title = "Required test-file detected";
    easystruct.es_GadgetFormat = "ok";
    if(num)
    {
      easystruct.es_TextFormat = "The Client 'Stuffit' detected unknown algorithm %ld.\n"
      "Please send me this archive file.\n"
      "The filename maybe '%s'";
      EasyRequest(0, &easystruct, 0, num, name ? name : "");
    }
    else
    {
      easystruct.es_TextFormat = "The Client 'Stuffit' detected an unknown archive structure.\n"
      "Please send me this archive file.\n"
      "The filename maybe '%s'";
      EasyRequest(0, &easystruct, 0, name ? name : "");
    }
    CloseLibrary((struct Library *) IntuitionBase);
  }
}
#endif

static void MACcopyname(struct xadFileInfo *dir, STRPTR file, STRPTR dest, ULONG size, ULONG rsrc)
{
  if(dir)
  {
    STRPTR n = dir->xfi_FileName;

    while(*n)
      *(dest++) = *(n++);
    *(dest++) = '/';
  }
  while(size--)
  {
    *(dest++) = *file >= 0x20 && *file <= 0x7E && *file != '/' ? *file : '_';
    ++file;
  }
  if(rsrc)
  {
    *(dest++) = '.';
    *(dest++) = 'r';
    *(dest++) = 's';
    *(dest++) = 'r';
    *(dest++) = 'c';
  }
  *dest = 0;
}

static LONG SITmakecomment(STRPTR txt, UWORD creatorflags, STRPTR comment, ULONG csize, STRPTR dest)
{
  LONG i, res = 0;

  if(csize)
    res = 15+csize+1;
  else
  {
    for(i = 0; i < 8 && txt[i] >= 0x20 && txt[i] <= 0x7E; ++i)
      ;
    if(i == 8)
      res = 15;
  }
  if(res && dest)
  {
    for(i = 0; i < 4; ++i)
      *(dest++) = txt[i] >= 0x20 && txt[i] <= 0x7E ? txt[i] : '_';
    *(dest++) = '/';
    for(; i < 8; ++i)
      *(dest++) = txt[i] >= 0x20 && txt[i] <= 0x7E ? txt[i] : '_';
    *(dest++) = ' ';
    for(i = 0; i < 4 ; ++i)
    {
      creatorflags &= 0xFFFF; /* security */
      *(dest++) = (creatorflags >= 0xA000 ? 'A'-10 : '0') + (creatorflags >> 12);
      creatorflags <<= 4;
    }
    if(csize)
    {
      *(dest++) = ' ';
      while(csize--)
      {
        *(dest++) = *comment >= 0x20 && *comment <= 0x7E ? *comment : '_';
        ++comment;
      }
    }
    *dest = 0;
  }
  return res;
}

ASM(LONG) SIT_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  UBYTE sithdr[SIT_FILEHDRSIZE];
  struct xadFileInfo *fi, *ldir = 0, *lfi = 0;
  LONG err;
  ULONG nsize, csize, dirsize = 0, fullsize;


  fullsize = ai->xai_InSize;
  if(!(err = xadHookAccess(XADAC_READ, 22, sithdr, ai)))
  {
    if(sithdr[2] == 'i')
    {
      csize = EndGetM32(sithdr+SITAH_ARCLENGTH);
      if(csize < fullsize)
        fullsize = csize;
    }
#ifdef DEBUG
#define MAKESIT(a,b,c,d) ((ULONG) ((a)<<24) | (ULONG) ((b)<<16) | (ULONG) ((c)<<8) | (ULONG) (d))

switch(EndGetM32(sithdr))
{
case MAKESIT('S','I','T','!'):
case MAKESIT('S','T','4','6'):
case MAKESIT('S','T','5','0'):
case MAKESIT('S','T','6','0'):
case MAKESIT('S','T','6','5'):
case MAKESIT('S','T','i','n'):
case MAKESIT('S','T','i','2'):
case MAKESIT('S','T','i','3'):
case MAKESIT('S','T','i','4'):
  break;
default: UnknownRequest(xadMasterBase, 0, ai->xai_InName);
}
#endif
    while(!err && ai->xai_InPos+SIT_FILEHDRSIZE <= fullsize)
    {
      if(!(err = xadHookAccess(XADAC_READ, SIT_FILEHDRSIZE, sithdr, ai)))
      {
        if(EndGetM16(sithdr+SITFH_HDRCRC) == xadCalcCRC16(XADCRC16_ID1, 0, 110, sithdr))
        {
	  for(nsize = 0; nsize < 8; ++nsize)
	  {
	    if(sithdr[SITFH_FTYPE+nsize] < 0x20 || sithdr[SITFH_FTYPE+nsize] > 0x7E)
	      sithdr[SITFH_FTYPE+nsize] = '?';
	  }

          if(sithdr[SITFH_FNAMESIZE] > 83)
            nsize = 83; /* prevent errors */
          else
	    nsize = sithdr[SITFH_FNAMESIZE];
	  if(sithdr[SITFH_COMPRMETHOD] == SITefolder || sithdr[SITFH_COMPDMETHOD] == SITefolder)
	  {
	    if((ldir = (struct xadFileInfo *) ldir->xfi_PrivateInfo))
	      dirsize = strlen(ldir->xfi_FileName)+1;
	    else
	      dirsize = 0;
	  }
	  else if(sithdr[SITFH_COMPRMETHOD] == SITsfolder || sithdr[SITFH_COMPDMETHOD] == SITsfolder)
	  {
	    if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, nsize+1+dirsize, TAG_DONE)))
	    {
	      MACcopyname(ldir, sithdr+SITFH_FNAME, fi->xfi_FileName, nsize, 0);

	      fi->xfi_Flags |= XADFIF_DIRECTORY;
              xadConvertDates(XAD_DATEMAC, EndGetM32(&sithdr[SITFH_MODDATE]), XAD_GETDATEXADDATE,
              &fi->xfi_Date, TAG_DONE);
	      fi->xfi_PrivateInfo = (APTR) ldir;
	      ldir = fi;
	      dirsize = strlen(fi->xfi_FileName)+1;
              err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos, TAG_DONE);
	    }
	    else
	      err = XADERR_NOMEMORY;
	  }
	  else
	  {
	    csize = SITmakecomment(sithdr + SITFH_FTYPE, EndGetM16(&sithdr[SITFH_FNDRFLAGS]), 0, 0, 0);

	    if(EndGetM32(&sithdr[SITFH_RSRCLENGTH]))
	    {
	      if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, nsize+1+5+dirsize,
	      XAD_OBJPRIVINFOSIZE, sizeof(struct SITPrivate), EndGetM32(&sithdr[SITFH_DATALENGTH]) || !csize ?
	      TAG_IGNORE : XAD_OBJCOMMENTSIZE, csize, TAG_DONE)))
	      {
	        MACcopyname(ldir, sithdr+SITFH_FNAME, fi->xfi_FileName, nsize, 1);
	        /* if comment field is zero, nothing is done! */
		SITmakecomment(sithdr + SITFH_FTYPE, EndGetM16(&sithdr[SITFH_FNDRFLAGS]), 0, 0, fi->xfi_Comment);

                fi->xfi_CrunchSize = EndGetM32(&sithdr[SITFH_COMPRLENGTH]);
                fi->xfi_Size = EndGetM32(&sithdr[SITFH_RSRCLENGTH]);

	        fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACRESOURCE|XADFIF_EXTRACTONBUILD;
	        fi->xfi_DataPos = ai->xai_InPos;
	        lfi = fi;

	        SITPI(fi)->CRC = EndGetM16(&sithdr[SITFH_RSRCCRC]);
	        SITPI(fi)->Method = sithdr[SITFH_COMPRMETHOD]&15;

                if(!fi->xfi_Size && !SITPI(fi)->Method)
                  fi->xfi_Size = fi->xfi_CrunchSize;

#ifdef DEBUG
  if(SITPI(fi)->Method != 0 && SITPI(fi)->Method != 2 && SITPI(fi)->Method != 3 &&
  SITPI(fi)->Method != 5 && SITPI(fi)->Method != 8 && SITPI(fi)->Method != 13 && SITPI(fi)->Method != 14
  && SITPI(fi)->Method != 15)
  {
    xadHookAccess(0,SITPI(fi)->Method,0,ai);
    UnknownRequest(xadMasterBase, SITPI(fi)->Method, ai->xai_InName);
  }
#endif
                xadConvertDates(XAD_DATEMAC, EndGetM32(&sithdr[SITFH_MODDATE]), XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);

	        if(SITPI(fi)->Method <= STUFFITMAXALGO)
	          fi->xfi_EntryInfo = sittypes[SITPI(fi)->Method];
	        if(sithdr[SITFH_COMPRMETHOD]&SITprot)
	        {
	          fi->xfi_Flags |= XADFIF_CRYPTED;
	          ai->xai_Flags |= XADAIF_CRYPTED;
	        }

                err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos+fi->xfi_CrunchSize, TAG_DONE);
	      }
	      else
	        err = XADERR_NOMEMORY;
	    }

	    if(!err && (EndGetM32(&sithdr[SITFH_DATALENGTH]) || !EndGetM32(&sithdr[SITFH_RSRCLENGTH])))
	    {
	      if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, nsize+1+dirsize,
	      XAD_OBJPRIVINFOSIZE, sizeof(struct SITPrivate), csize ? XAD_OBJCOMMENTSIZE : TAG_IGNORE, csize,
	      TAG_DONE)))
	      {
	        MACcopyname(ldir, sithdr+SITFH_FNAME, fi->xfi_FileName, nsize, 0);
		SITmakecomment(sithdr + SITFH_FTYPE, EndGetM16(&sithdr[SITFH_FNDRFLAGS]), 0, 0, fi->xfi_Comment);

                fi->xfi_CrunchSize = EndGetM32(&sithdr[SITFH_COMPDLENGTH]);
                fi->xfi_Size = EndGetM32(&sithdr[SITFH_DATALENGTH]);

	        fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACDATA|XADFIF_EXTRACTONBUILD;
	        fi->xfi_DataPos = ai->xai_InPos;

                if(EndGetM32(&sithdr[SITFH_RSRCLENGTH]))
                {
                  fi->xfi_MacFork = lfi;
                  lfi->xfi_MacFork = fi;
                }

	        SITPI(fi)->CRC = EndGetM16(&sithdr[SITFH_DATACRC]);
	        SITPI(fi)->Method = sithdr[SITFH_COMPDMETHOD]&15;

                if(!fi->xfi_Size && !SITPI(fi)->Method)
                  fi->xfi_Size = fi->xfi_CrunchSize;

#ifdef DEBUG
  if(SITPI(fi)->Method != 0 && SITPI(fi)->Method != 2 && SITPI(fi)->Method != 3 &&
  SITPI(fi)->Method != 5 && SITPI(fi)->Method != 8 && SITPI(fi)->Method != 13 && SITPI(fi)->Method != 14
  && SITPI(fi)->Method != 15)
  {
    xadHookAccess(0,SITPI(fi)->Method,0,ai);
    UnknownRequest(xadMasterBase, SITPI(fi)->Method, ai->xai_InName);
  }
#endif
                xadConvertDates(XAD_DATEMAC, EndGetM32(&sithdr[SITFH_MODDATE]), XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);

	        if(SITPI(fi)->Method <= STUFFITMAXALGO)
	          fi->xfi_EntryInfo = sittypes[SITPI(fi)->Method];
	        if(sithdr[SITFH_COMPRMETHOD]&SITprot)
	        {
	          fi->xfi_Flags |= XADFIF_CRYPTED;
	          ai->xai_Flags |= XADAIF_CRYPTED;
	        }

                err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos+fi->xfi_CrunchSize, TAG_DONE);
	      }
	      else
	        err = XADERR_NOMEMORY;
	    }
	  }
        }
        else
          err = XADERR_CHECKSUM;
      }
    }

    if(!err && ai->xai_InPos < fullsize)
      err = XADERR_ILLEGALDATA;
    if(err)
    {
      ai->xai_Flags |= XADAIF_FILECORRUPT;
      ai->xai_LastError = err;
    }
  }

#ifdef DEBUG
  if(err || (ai->xai_Flags & XADAIF_CRYPTED))
    UnknownRequest(xadMasterBase, 0, ai->xai_InName);
#endif

  return (ai->xai_FileInfo ? 0 : err);
}

/*****************************************************************************/

#define	SITESC	0x90	/* repeat packing escape */

static LONG SIT_rle(struct xadInOut *io)
{
  LONG ch, lastch = 0, n;

  while(!(io->xio_Flags & (XADIOF_LASTOUTBYTE|XADIOF_ERROR)))
  {
    if((ch = xadIOGetChar(io)) == SITESC)
    {
      if((n = xadIOGetChar(io) - 1) < 0)
        lastch = xadIOPutChar(io, SITESC);
      else
        while(n--)
	  xadIOPutChar(io, lastch);
    }
    else
      lastch = xadIOPutChar(io, ch);
  }
  return io->xio_Error;
}

/*****************************************************************************/

#define UCOMPMAXCODE(n)	(((ULONG) 1 << (n)) - 1)
#define UCOMPBITS	   16
#define UCOMPSTACKSIZE	 8000
#define UCOMPFIRST	  257		/* first free entry */
#define UCOMPCLEAR	  256		/* table clear output code */
#define UCOMPINIT_BITS	    9		/* initial number of bits/code */
#define UCOMPBIT_MASK	 0x1f
#define UCOMPBLOCK_MASK	 0x80

struct UCompData {
  WORD		clear_flg;
  UWORD		n_bits;			/* number of bits/code */
  UWORD  	maxbits;		/* user settable max # bits/code */
  ULONG 	maxcode;		/* maximum code, given n_bits */
  ULONG 	maxmaxcode;
  LONG 		free_ent;
  LONG		offset;
  LONG		size;
  UWORD *	tab_prefixof;
  STRPTR 	tab_suffixof;
  UBYTE 	stack[UCOMPSTACKSIZE];
  UBYTE		buf[UCOMPBITS];
};

/* Read one code from input. If EOF, return -1. */
static LONG UCompgetcode(struct xadInOut *io, struct UCompData *cd)
{
  LONG code, r_off, bits;
  UBYTE *bp = cd->buf;

  if(cd->clear_flg > 0 || cd->offset >= cd->size || cd->free_ent > cd->maxcode)
  {
    /*
     * If the next entry will be too big for the current code
     * size, then we must increase the size.  This implies reading
     * a new buffer full, too.
     */
    if(cd->free_ent > cd->maxcode)
    {
      if(++cd->n_bits == cd->maxbits)
	cd->maxcode = cd->maxmaxcode;	/* won't get any bigger now */
      else
	cd->maxcode = UCOMPMAXCODE(cd->n_bits);
    }
    if(cd->clear_flg > 0)
    {
      cd->maxcode = UCOMPMAXCODE(cd->n_bits = UCOMPINIT_BITS);
      cd->clear_flg = 0;
    }

    /* This reads maximum n_bits characters into buf */
    cd->size = 0;
    while(cd->size < cd->n_bits && !(io->xio_Flags & (XADIOF_LASTINBYTE|XADIOF_ERROR)))
      cd->buf[cd->size++] = xadIOGetChar(io);
    if(cd->size <= 0)
      return -1;

    cd->offset = 0;
    /* Round size down to integral number of codes */
    cd->size = (cd->size << 3) - (cd->n_bits - 1);
  }

  r_off = cd->offset;
  bits = cd->n_bits;

  /* Get to the first byte. */
  bp += (r_off >> 3);
  r_off &= 7;

  /* Get first part (low order bits) */
  code = (*bp++ >> r_off);
  bits -= (8 - r_off);
  r_off = 8 - r_off;			/* now, offset into code word */

  /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
  if(bits >= 8)
  {
    code |= *bp++ << r_off;
    r_off += 8;
    bits -= 8;
  }

  /* high order bits. */
  code |= (*bp & ((1<<bits)-1)) << r_off;
  cd->offset += cd->n_bits;

  return code;
}

/* Decompress. This routine adapts to the codes in the file building the
 * "string" table on-the-fly; requiring no table to be stored in the
 * compressed file.
 */
static LONG UCompDecomp(struct xadInOut *io, UBYTE bitinfo)
{
  LONG err;
  struct UCompData *cd;
  struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;

  if((cd = (struct UCompData *) xadAllocVec(sizeof(struct UCompData), MEMF_CLEAR|MEMF_PUBLIC)))
  {
    LONG finchar, code, oldcode, incode, blockcomp;
    STRPTR stackp, stack;

    stackp = stack = cd->stack;
    cd->maxbits = bitinfo & UCOMPBIT_MASK;
    blockcomp = bitinfo & UCOMPBLOCK_MASK;
    cd->maxmaxcode = 1 << cd->maxbits;
    cd->maxcode = UCOMPMAXCODE(cd->n_bits = UCOMPINIT_BITS);
    cd->free_ent = blockcomp ? UCOMPFIRST : 256;
    cd->clear_flg = cd->offset = cd->size = 0;

    if((cd->tab_prefixof = (UWORD *) xadAllocVec(sizeof(UWORD)*cd->maxmaxcode, MEMF_PUBLIC|MEMF_CLEAR)))
    {
      if((cd->tab_suffixof = (STRPTR) xadAllocVec(cd->maxmaxcode, MEMF_PUBLIC|MEMF_CLEAR)))
      {
        /* Initialize the first 256 entries in the table. */
        for(code = 255; code >= 0; code--)
        {
//        cd->tab_prefixof[code] = 0;
          cd->tab_suffixof[code] = (UBYTE) code;
        }

        if((finchar = oldcode = UCompgetcode(io, cd)) == -1)
          err = XADERR_DECRUNCH;
        else
        {
          xadIOPutChar(io, finchar); /* first code must be 8 bits = UBYTE */

          while((code = UCompgetcode(io, cd)) > -1)
          {
            if((code == UCOMPCLEAR) && blockcomp)
            {
              for(code = 255; code >= 0; code--)
	        cd->tab_prefixof[code] = 0;
              cd->clear_flg = 1;
              cd->free_ent = UCOMPFIRST - 1;
              if((code = UCompgetcode(io, cd)) == -1)
	        break;					/* O, untimely death! */
            }
            incode = code;

            /* Special case for KwKwK string. */
            if(code >= cd->free_ent)
            {
              *stackp++ = finchar;
              code = oldcode;
            }

            /* Generate output characters in reverse order */
            while(code >= 256)
            {
              *stackp++ = cd->tab_suffixof[code];
              code = cd->tab_prefixof[code];
            }
            *stackp++ = finchar = cd->tab_suffixof[code];

            /* And put them out in forward order */
            do
            {
              xadIOPutChar(io, *(--stackp));
            } while(stackp > stack);

            /* Generate the new entry. */
            if((code = cd->free_ent) < cd->maxmaxcode)
            {
              cd->tab_prefixof[code] = (UWORD) oldcode;
              cd->tab_suffixof[code] = finchar;
              cd->free_ent = code+1;
            }
            /* Remember previous code. */
            oldcode = incode;
          }
          err = io->xio_Error;
        }
        xadFreeObjectA(cd->tab_suffixof, 0);
      }
      else
        err = XADERR_NOMEMORY;
      xadFreeObjectA(cd->tab_prefixof, 0);
    }
    else
      err = XADERR_NOMEMORY;
    xadFreeObjectA(cd, 0);
  }
  else
    err = XADERR_NOMEMORY;
 
  return err;
}

/*****************************************************************************/

struct sithufnode {
  struct sithufnode *one;
  struct sithufnode *zero;
  UBYTE              byte;
};

static LONG SIT_huffman(struct xadInOut *io)
{
  struct sithufnode *np, *npb, *nodelist;
  LONG numfreetree = 0; /* number of free np->one nodes */
  struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;

   /* 515 because StuffIt Classic needs more than the needed 511 */
  if((nodelist = (struct sithufnode *) xadAllocVec(sizeof(struct sithufnode)*515, MEMF_ANY|MEMF_CLEAR)))
  {
    npb = nodelist;
    do /* removed recursion, optimized a lot */
    {
      do
      {
        np = npb++;
        if(xadIOGetBitsHigh(io, 1))
        {
          np->byte = xadIOGetBitsHigh(io, 8);
          np->zero = np->one = (struct sithufnode *) -1;
        }
        else
        {
          np->zero = npb;
          ++numfreetree;
        }
      } while(!np->one);
      if(numfreetree--)
      {
        while(np->one)
          --np;
        np->one = npb;
      }
    } while(numfreetree >= 0);

    while(!(io->xio_Flags & (XADIOF_LASTOUTBYTE|XADIOF_ERROR)))
    {
      np = nodelist;
      while(np->one != (struct sithufnode *) -1)
        np = xadIOGetBitsHigh(io, 1) ? np->one : np->zero;
      xadIOPutChar(io, np->byte);
    }

    xadFreeObjectA(nodelist, 0);
  }
  else
    return XADERR_NOMEMORY;

  return io->xio_Error;
}
/*****************************************************************************/

/* Note: compare with LZSS decoding in lharc! */
#define SITLZAH_N	314
#define SITLZAH_T	(2*SITLZAH_N-1)
/*	Huffman table used for first 6 bits of offset:
	#bits	codes
	3	0x000
	4	0x040-0x080
	5	0x100-0x2c0
	6	0x300-0x5c0
	7	0x600-0xbc0
	8	0xc00-0xfc0
*/

static UBYTE SITLZAH_HuffCode[] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
  0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
  0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
  0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
  0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
  0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
  0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
  0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
  0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24,
  0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
  0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,
  0x30, 0x30, 0x30, 0x30, 0x34, 0x34, 0x34, 0x34,
  0x38, 0x38, 0x38, 0x38, 0x3c, 0x3c, 0x3c, 0x3c,
  0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x44, 0x44,
  0x48, 0x48, 0x48, 0x48, 0x4c, 0x4c, 0x4c, 0x4c,
  0x50, 0x50, 0x50, 0x50, 0x54, 0x54, 0x54, 0x54,
  0x58, 0x58, 0x58, 0x58, 0x5c, 0x5c, 0x5c, 0x5c,
  0x60, 0x60, 0x64, 0x64, 0x68, 0x68, 0x6c, 0x6c,
  0x70, 0x70, 0x74, 0x74, 0x78, 0x78, 0x7c, 0x7c,
  0x80, 0x80, 0x84, 0x84, 0x88, 0x88, 0x8c, 0x8c,
  0x90, 0x90, 0x94, 0x94, 0x98, 0x98, 0x9c, 0x9c,
  0xa0, 0xa0, 0xa4, 0xa4, 0xa8, 0xa8, 0xac, 0xac,
  0xb0, 0xb0, 0xb4, 0xb4, 0xb8, 0xb8, 0xbc, 0xbc,
  0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
  0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf4, 0xf8, 0xfc};

static UBYTE SITLZAH_HuffLength[] = {
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8};

struct SITLZAHData {
  UBYTE buf[4096];
  ULONG Frequ[1000];
  ULONG ForwTree[1000];
  ULONG BackTree[1000];
};

static void SITLZAH_move(ULONG *p, ULONG *q, ULONG n)
{
  if(p > q)
  {
    while(n-- > 0)
      *q++ = *p++;
  }
  else
  {
    p += n;
    q += n;
    while(n-- > 0)
      *--q = *--p;
  }
}

static LONG SIT_lzah(struct xadInOut *io)
{
  LONG i, i1, j, k, l, ch, byte, offs, skip;
  ULONG bufptr = 0;
  struct SITLZAHData *dat;
  struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;

  if((dat = (struct SITLZAHData *) xadAllocVec(sizeof(struct SITLZAHData), MEMF_CLEAR|MEMF_PUBLIC)))
  {
    /* init buffer */
    for(i = 0; i < SITLZAH_N; i++)
    {
      dat->Frequ[i] = 1;
      dat->ForwTree[i] = i + SITLZAH_T;
      dat->BackTree[i + SITLZAH_T] = i;
    }
    for(i = 0, j = SITLZAH_N; j < SITLZAH_T; i += 2, j++)
    {
      dat->Frequ[j] = dat->Frequ[i] + dat->Frequ[i + 1];
      dat->ForwTree[j] = i;
      dat->BackTree[i] = j;
      dat->BackTree[i + 1] = j;
    }
    dat->Frequ[SITLZAH_T] = 0xffff;
    dat->BackTree[SITLZAH_T - 1] = 0;

    for(i = 0; i < 4096; i++)
      dat->buf[i] = ' ';

    while(!(io->xio_Flags & (XADIOF_LASTOUTBYTE|XADIOF_ERROR)))
    {
      ch = dat->ForwTree[SITLZAH_T - 1];
      while(ch < SITLZAH_T)
        ch = dat->ForwTree[ch + xadIOGetBitsHigh(io, 1)];
      ch -= SITLZAH_T;
      if(dat->Frequ[SITLZAH_T - 1] >= 0x8000) /* need to reorder */
      {
        j = 0;
        for(i = 0; i < SITLZAH_T; i++)
        {
	  if(dat->ForwTree[i] >= SITLZAH_T)
	  {
	    dat->Frequ[j] = ((dat->Frequ[i] + 1) >> 1);
	    dat->ForwTree[j] = dat->ForwTree[i];
	    j++;
	  }
        }
        j = SITLZAH_N;
        for(i = 0; i < SITLZAH_T; i += 2)
        {
	  k = i + 1;
	  l = dat->Frequ[i] + dat->Frequ[k];
	  dat->Frequ[j] = l;
	  k = j - 1;
	  while(l < dat->Frequ[k])
	    k--;
	  k = k + 1;
	  SITLZAH_move(dat->Frequ + k, dat->Frequ + k + 1, j - k);
	  dat->Frequ[k] = l;
	  SITLZAH_move(dat->ForwTree + k, dat->ForwTree + k + 1, j - k);
	  dat->ForwTree[k] = i;
	  j++;
        }
        for(i = 0; i < SITLZAH_T; i++)
        {
	  k = dat->ForwTree[i];
	  if(k >= SITLZAH_T)
	    dat->BackTree[k] = i;
	  else
	  {
	    dat->BackTree[k] = i;
	    dat->BackTree[k + 1] = i;
	  }
        }
      }

      i = dat->BackTree[ch + SITLZAH_T];
      do
      {
	j = ++dat->Frequ[i];
	i1 = i + 1;
	if(dat->Frequ[i1] < j)
	{
	  while(dat->Frequ[++i1] < j)
	    ;
	  i1--;
	  dat->Frequ[i] = dat->Frequ[i1];
	  dat->Frequ[i1] = j;

	  j = dat->ForwTree[i];
	  dat->BackTree[j] = i1;
	  if(j < SITLZAH_T)
	    dat->BackTree[j + 1] = i1;
	  dat->ForwTree[i] = dat->ForwTree[i1];
	  dat->ForwTree[i1] = j;
	  j = dat->ForwTree[i];
	  dat->BackTree[j] = i;
	  if(j < SITLZAH_T)
	    dat->BackTree[j + 1] = i;
	  i = i1;
	}
	i = dat->BackTree[i];
      } while(i != 0);

      if(ch < 256)
      {
        dat->buf[bufptr++] = xadIOPutChar(io, ch);
        bufptr &= 0xFFF;
      }
      else
      {
        byte = xadIOGetBitsHigh(io, 8);
	skip = SITLZAH_HuffLength[byte] - 2;
	offs = (SITLZAH_HuffCode[byte]<<4) | (((byte << skip)  + xadIOGetBitsHigh(io, skip)) & 0x3f);
        offs = ((bufptr - offs - 1) & 0xfff);
	ch = ch - 253;
	while(ch-- > 0)
	{
          dat->buf[bufptr++] = xadIOPutChar(io, dat->buf[offs++ & 0xfff]);
          bufptr &= 0xFFF;
        }
      }
    }
    xadFreeObjectA(dat, 0);
  }
  else
    return XADERR_NOMEMORY;

  return io->xio_Error;
}

/*****************************************************************************/

struct SITMWData {
  UWORD dict[16385];
  UWORD stack[16384];
};

static void SITMW_out(struct xadInOut *io, struct SITMWData *dat, LONG ptr)
{
  UWORD stack_ptr = 1;

  dat->stack[0] = ptr;
  while(stack_ptr)
  {
    ptr = dat->stack[--stack_ptr];
    while(ptr >= 256)
    {
      dat->stack[stack_ptr++] = dat->dict[ptr];
      ptr = dat->dict[ptr - 1];
    }
    xadIOPutChar(io, (UBYTE) ptr);
  }
}

static LONG SIT_mw(struct xadInOut *io)
{
  struct SITMWData *dat;
  struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;

  if((dat = (struct SITMWData *) xadAllocVec(sizeof(struct SITMWData), MEMF_CLEAR|MEMF_PUBLIC)))
  {
    LONG ptr, max, max1, bits;

    while(!(io->xio_Flags & (XADIOF_LASTOUTBYTE|XADIOF_ERROR)))
    {
      max = 256;
      max1 = max << 1;
      bits = 9;
      ptr = xadIOGetBitsLow(io, bits);
      if(ptr < max)
      {
        dat->dict[255] = ptr;
        SITMW_out(io, dat, ptr);
        while(!(io->xio_Flags & (XADIOF_LASTOUTBYTE|XADIOF_ERROR)) &&
        (ptr = xadIOGetBitsLow(io, bits)) < max)
        {
          dat->dict[max++] = ptr;
	  if(max == max1)
	  {
	    max1 <<= 1;
	    bits++;
	  }
	  SITMW_out(io, dat, ptr);
        }
      }
      if(ptr > max)
        break;
    }

    xadFreeObjectA(dat, 0);
  }
  else
    return XADERR_NOMEMORY;

  return io->xio_Error;
}

/*****************************************************************************/

struct SIT13Buffer {
  UWORD data;
  BYTE  bits;
};

struct SIT13Store {
  WORD  freq;
  UWORD d1;
  UWORD d2;
};

struct SIT13Data {
  UWORD		     MaxBits;
  struct SIT13Store  Buffer4[0xE08];
  struct SIT13Buffer Buffer1[0x1000];
  struct SIT13Buffer Buffer2[0x1000];
  struct SIT13Buffer Buffer3[0x1000];
  struct SIT13Buffer Buffer3b[0x1000];
  struct SIT13Buffer Buffer5[0x141];
  UBYTE		     TextBuf[658];
  UBYTE		     Window[0x10000];
};

static const UBYTE SIT13Bits[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
static const UWORD SIT13Info[37] = {
  0x5D8, 0x058, 0x040, 0x0C0, 0x000, 0x078, 0x02B, 0x014,
  0x00C, 0x01C, 0x01B, 0x00B, 0x010, 0x020, 0x038, 0x018,
  0x0D8, 0xBD8, 0x180, 0x680, 0x380, 0xF80, 0x780, 0x480,
  0x080, 0x280, 0x3D8, 0xFD8, 0x7D8, 0x9D8, 0x1D8, 0x004,
  0x001, 0x002, 0x007, 0x003, 0x008
};
static const UWORD SIT13InfoBits[37] = {
  11,  8,  8,  8,  8,  7,  6,  5,  5,  5,  5,  6,  5,  6,  7,  7,
   9, 12, 10, 11, 11, 12, 12, 11, 11, 11, 12, 12, 12, 12, 12,  5,
   2,  2,  3,  4,  5
};
static const UWORD SIT13StaticPos[5] = {0, 330, 661, 991, 1323};
static const UBYTE SIT13StaticBits[5] = {11, 13, 14, 11, 11};
static const UBYTE SIT13Static[1655] = {
  0xB8,0x98,0x78,0x77,0x75,0x97,0x76,0x87,0x77,0x77,0x77,0x78,0x67,0x87,0x68,0x67,0x3B,0x77,0x78,0x67,
  0x77,0x77,0x77,0x59,0x76,0x87,0x77,0x77,0x77,0x77,0x77,0x77,0x76,0x87,0x67,0x87,0x77,0x77,0x75,0x88,
  0x59,0x75,0x79,0x77,0x78,0x68,0x77,0x67,0x73,0xB6,0x65,0xB6,0x76,0x97,0x67,0x47,0x9A,0x2A,0x4A,0x87,
  0x77,0x78,0x67,0x86,0x78,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,0x77,0x77,0x77,
  0x68,0x77,0x77,0x77,0x67,0x87,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x77,0x68,0x77,0x77,0x77,
  0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,0x77,0x67,0x87,
  0x68,0x77,0x77,0x77,0x68,0x77,0x68,0x63,0x86,0x7A,0x87,0x77,0x77,0x87,0x76,0x87,0x77,0x77,0x77,0x77,
  0x77,0x77,0x77,0x77,0x77,0x76,0x86,0x77,0x86,0x86,0x86,0x86,0x87,0x76,0x86,0x87,0x67,0x74,0xA7,0x86,
  0x36,0x88,0x78,0x76,0x87,0x76,0x96,0x87,0x77,0x84,0xA6,0x86,0x87,0x76,0x92,0xB5,0x94,0xA6,0x96,0x85,
  0x78,0x75,0x96,0x86,0x86,0x75,0xA7,0x67,0x87,0x85,0x87,0x85,0x95,0x77,0x77,0x85,0xA3,0xA7,0x93,0x87,
  0x86,0x94,0x85,0xA8,0x67,0x85,0xA5,0x95,0x86,0x68,0x67,0x77,0x96,0x78,0x75,0x86,0x77,0xA5,0x67,0x87,
  0x85,0xA6,0x75,0x96,0x85,0x87,0x95,0x95,0x87,0x86,0x94,0xA5,0x86,0x85,0x87,0x86,0x86,0x86,0x86,0x77,
  0x67,0x76,0x66,0x9A,0x75,0xA5,0x94,0x97,0x76,0x96,0x76,0x95,0x86,0x77,0x86,0x87,0x75,0xA5,0x96,0x85,
  0x86,0x96,0x86,0x86,0x85,0x96,0x86,0x76,0x95,0x86,0x95,0x95,0x95,0x87,0x76,0x87,0x76,0x96,0x85,0x78,
  0x75,0xA6,0x85,0x86,0x95,0x86,0x95,0x86,0x45,0x69,0x78,0x77,0x87,0x67,0x69,0x58,0x79,0x68,0x78,0x87,
  0x78,0x66,0x88,0x68,0x68,0x77,0x76,0x87,0x68,0x68,0x69,0x58,0x5A,0x4B,0x76,0x88,0x69,0x67,0xA7,0x70,
  0x9F,0x90,0xA4,0x84,0x77,0x77,0x77,0x89,0x17,0x77,0x7B,0xA7,0x86,0x87,0x77,0x68,0x68,0x69,0x67,0x78,
  0x77,0x78,0x76,0x87,0x77,0x76,0x73,0xB6,0x87,0x96,0x66,0x87,0x76,0x85,0x87,0x78,0x77,0x77,0x86,0x77,
  0x86,0x78,0x66,0x76,0x77,0x87,0x86,0x78,0x76,0x76,0x86,0xA5,0x67,0x97,0x77,0x87,0x87,0x76,0x66,0x59,
  0x67,0x59,0x77,0x6A,0x65,0x86,0x78,0x94,0x77,0x88,0x77,0x78,0x86,0x86,0x76,0x88,0x76,0x87,0x67,0x87,
  0x77,0x77,0x76,0x87,0x86,0x77,0x77,0x77,0x86,0x86,0x76,0x96,0x77,0x77,0x76,0x78,0x86,0x86,0x86,0x95,
  0x86,0x96,0x85,0x95,0x86,0x87,0x75,0x88,0x77,0x87,0x57,0x78,0x76,0x86,0x76,0x96,0x86,0x87,0x76,0x87,
  0x86,0x76,0x77,0x86,0x78,0x78,0x57,0x87,0x86,0x76,0x85,0xA5,0x87,0x76,0x86,0x86,0x85,0x86,0x53,0x98,
  0x78,0x78,0x77,0x87,0x79,0x67,0x79,0x85,0x87,0x69,0x67,0x68,0x78,0x69,0x68,0x69,0x58,0x87,0x66,0x97,
  0x68,0x68,0x76,0x85,0x78,0x87,0x67,0x97,0x67,0x74,0xA2,0x28,0x77,0x78,0x77,0x77,0x78,0x68,0x67,0x78,
  0x77,0x78,0x68,0x68,0x77,0x59,0x67,0x5A,0x68,0x68,0x68,0x68,0x68,0x68,0x67,0x77,0x78,0x68,0x68,0x78,
  0x59,0x58,0x76,0x77,0x68,0x78,0x68,0x59,0x69,0x58,0x68,0x68,0x67,0x78,0x77,0x78,0x69,0x58,0x68,0x57,
  0x78,0x67,0x78,0x76,0x88,0x58,0x67,0x7A,0x46,0x88,0x77,0x78,0x68,0x68,0x66,0x78,0x78,0x68,0x68,0x59,
  0x68,0x69,0x68,0x59,0x67,0x78,0x59,0x58,0x69,0x59,0x67,0x68,0x67,0x69,0x69,0x57,0x79,0x68,0x59,0x59,
  0x59,0x68,0x68,0x68,0x58,0x78,0x67,0x59,0x68,0x78,0x59,0x58,0x78,0x58,0x76,0x78,0x68,0x68,0x68,0x69,
  0x59,0x67,0x68,0x69,0x59,0x59,0x58,0x69,0x59,0x59,0x58,0x5A,0x58,0x68,0x68,0x59,0x58,0x68,0x66,0x47,
  0x88,0x77,0x87,0x77,0x87,0x76,0x87,0x87,0x87,0x77,0x77,0x87,0x67,0x96,0x78,0x76,0x87,0x68,0x77,0x77,
  0x76,0x86,0x96,0x86,0x88,0x77,0x85,0x86,0x8B,0x76,0x0A,0xF9,0x07,0x38,0x57,0x67,0x77,0x78,0x77,0x91,
  0x77,0xD7,0x77,0x7A,0x67,0x3C,0x68,0x68,0x77,0x68,0x78,0x59,0x77,0x68,0x77,0x68,0x76,0x77,0x69,0x68,
  0x68,0x68,0x68,0x67,0x68,0x68,0x77,0x87,0x77,0x67,0x78,0x68,0x67,0x58,0x78,0x68,0x77,0x68,0x78,0x67,
  0x68,0x68,0x67,0x78,0x77,0x77,0x87,0x77,0x76,0x67,0x86,0x85,0x87,0x86,0x97,0x58,0x67,0x79,0x57,0x77,
  0x87,0x77,0x87,0x77,0x76,0x59,0x78,0x77,0x77,0x68,0x77,0x77,0x76,0x78,0x77,0x77,0x77,0x76,0x87,0x77,
  0x77,0x68,0x77,0x77,0x77,0x67,0x78,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x76,0x68,0x87,0x77,
  0x77,0x77,0x77,0x68,0x77,0x68,0x77,0x77,0x77,0x77,0x77,0x77,0x76,0x78,0x77,0x77,0x76,0x87,0x77,0x77,
  0x67,0x78,0x77,0x77,0x76,0x78,0x67,0x68,0x68,0x29,0x77,0x88,0x78,0x78,0x77,0x68,0x77,0x77,0x77,0x77,
  0x77,0x77,0x77,0x77,0x4A,0x77,0x4A,0x74,0x77,0x77,0x68,0xA4,0x7A,0x47,0x76,0x86,0x78,0x76,0x7A,0x4A,
  0x83,0xB2,0x87,0x77,0x87,0x76,0x96,0x86,0x96,0x76,0x78,0x87,0x77,0x85,0x87,0x85,0x96,0x65,0xB5,0x95,
  0x96,0x77,0x77,0x86,0x76,0x86,0x86,0x87,0x86,0x86,0x76,0x96,0x96,0x57,0x77,0x85,0x97,0x85,0x86,0xA5,
  0x86,0x85,0x87,0x77,0x68,0x78,0x77,0x95,0x86,0x75,0x87,0x76,0x86,0x79,0x68,0x84,0x96,0x76,0xB3,0x87,
  0x77,0x68,0x86,0xA5,0x77,0x56,0xB6,0x68,0x85,0x93,0xB6,0x95,0x95,0x85,0x95,0xA5,0x95,0x95,0x69,0x85,
  0x95,0x85,0x86,0x86,0x97,0x84,0x85,0xB6,0x84,0xA5,0x95,0xA4,0x95,0x95,0x95,0x68,0x95,0x66,0xA6,0x95,
  0x95,0x95,0x86,0x93,0xB5,0x86,0x77,0x94,0x96,0x95,0x96,0x85,0x68,0x94,0x87,0x95,0x86,0x86,0x93,0xB4,
  0xA3,0xB3,0xA6,0x86,0x85,0x85,0x96,0x76,0x86,0x64,0x69,0x78,0x68,0x78,0x78,0x77,0x67,0x79,0x68,0x79,
  0x59,0x56,0x87,0x98,0x68,0x78,0x76,0x88,0x68,0x68,0x67,0x76,0x87,0x68,0x78,0x76,0x78,0x77,0x78,0xA6,
  0x80,0xAF,0x81,0x38,0x47,0x67,0x77,0x78,0x77,0x89,0x07,0x79,0xB7,0x87,0x86,0x86,0x87,0x86,0x87,0x76,
  0x78,0x77,0x87,0x66,0x96,0x86,0x86,0x74,0xA6,0x87,0x86,0x77,0x86,0x77,0x76,0x77,0x77,0x87,0x77,0x77,
  0x77,0x77,0x87,0x65,0x78,0x77,0x78,0x75,0x88,0x85,0x76,0x87,0x95,0x77,0x86,0x87,0x86,0x96,0x85,0x76,
  0x69,0x67,0x59,0x77,0x6A,0x65,0x86,0x78,0x94,0x77,0x88,0x77,0x78,0x85,0x96,0x65,0x98,0x77,0x87,0x67,
  0x86,0x77,0x87,0x66,0x87,0x86,0x86,0x86,0x77,0x86,0x86,0x76,0x87,0x86,0x77,0x76,0x87,0x77,0x86,0x86,
  0x86,0x87,0x76,0x95,0x86,0x86,0x87,0x65,0x97,0x86,0x87,0x76,0x86,0x86,0x87,0x75,0x88,0x76,0x87,0x76,
  0x87,0x76,0x77,0x77,0x86,0x78,0x76,0x76,0x96,0x78,0x76,0x77,0x86,0x77,0x77,0x76,0x96,0x75,0x95,0x56,
  0x87,0x87,0x87,0x78,0x88,0x67,0x87,0x87,0x58,0x87,0x77,0x87,0x77,0x76,0x87,0x96,0x59,0x88,0x37,0x89,
  0x69,0x69,0x84,0x96,0x67,0x77,0x57,0x4B,0x58,0xB7,0x80,0x8E,0x0D,0x78,0x87,0x77,0x87,0x68,0x79,0x49,
  0x76,0x78,0x77,0x5A,0x67,0x69,0x68,0x68,0x68,0x4A,0x68,0x69,0x67,0x69,0x59,0x58,0x68,0x67,0x69,0x77,
  0x77,0x69,0x68,0x68,0x66,0x68,0x87,0x68,0x77,0x5A,0x68,0x67,0x68,0x68,0x67,0x78,0x78,0x67,0x6A,0x59,
  0x67,0x57,0x95,0x78,0x77,0x86,0x88,0x57,0x77,0x68,0x67,0x79,0x76,0x76,0x98,0x68,0x75,0x68,0x88,0x58,
  0x87,0x5A,0x57,0x79,0x67,0x59,0x78,0x49,0x58,0x77,0x79,0x49,0x68,0x59,0x77,0x68,0x78,0x48,0x79,0x67,
  0x68,0x59,0x68,0x68,0x59,0x75,0x6A,0x68,0x76,0x4C,0x67,0x77,0x78,0x59,0x69,0x56,0x96,0x68,0x68,0x68,
  0x77,0x69,0x67,0x68,0x67,0x78,0x69,0x68,0x58,0x59,0x68,0x68,0x69,0x49,0x77,0x59,0x67,0x69,0x67,0x68,
  0x65,0x48,0x77,0x87,0x86,0x96,0x88,0x75,0x87,0x96,0x87,0x95,0x87,0x77,0x68,0x86,0x77,0x77,0x96,0x68,
  0x86,0x77,0x85,0x5A,0x81,0xD5,0x95,0x68,0x99,0x74,0x98,0x77,0x09,0xF9,0x0A,0x5A,0x66,0x58,0x77,0x87,
  0x91,0x77,0x77,0xE9,0x77,0x77,0x77,0x76,0x87,0x75,0x97,0x77,0x77,0x77,0x78,0x68,0x68,0x68,0x67,0x3B,
  0x59,0x77,0x77,0x57,0x79,0x57,0x86,0x87,0x67,0x97,0x77,0x57,0x79,0x77,0x77,0x75,0x95,0x77,0x79,0x75,
  0x97,0x57,0x77,0x79,0x58,0x69,0x77,0x77,0x77,0x77,0x77,0x75,0x86,0x77,0x87,0x58,0x95,0x78,0x65,0x8A,
  0x39,0x58,0x87,0x96,0x87,0x77,0x77,0x77,0x86,0x87,0x76,0x78,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,
  0x77,0x68,0x77,0x68,0x77,0x67,0x86,0x77,0x78,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,0x68,
  0x77,0x68,0x77,0x67,0x78,0x77,0x77,0x68,0x68,0x76,0x87,0x68,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,
  0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x68,0x68,0x68,0x76,0x38,0x97,0x67,0x79,0x77,0x77,0x77,0x77,0x77,
  0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x78,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,
  0x72,0xC5,0x86,0x86,0x98,0x77,0x86,0x78,0x1C,0x85,0x2E,0x77,0x77,0x77,0x87,0x86,0x76,0x86,0x86,0xA0,
  0xBD,0x49,0x97,0x66,0x48,0x88,0x48,0x68,0x86,0x78,0x77,0x77,0x78,0x66,0xA6,0x87,0x83,0x85,0x88,0x78,
  0x66,0xA7,0x56,0x87,0x6A,0x46,0x89,0x76,0xA7,0x76,0x87,0x74,0xA2,0x86,0x77,0x79,0x66,0xB6,0x48,0x67,
  0x8A,0x36,0x88,0x77,0xA5,0xA5,0xB1,0xE9,0x39,0x78,0x78,0x75,0x87,0x77,0x77,0x77,0x68,0x58,0x79,0x69,
  0x4A,0x59,0x29,0x6A,0x3C,0x3B,0x46,0x78,0x75,0x89,0x76,0x89,0x4A,0x56,0x88,0x3B,0x66,0x88,0x68,0x87,
  0x57,0x97,0x38,0x87,0x56,0xB7,0x84,0x88,0x67,0x57,0x95,0xA8,0x59,0x77,0x68,0x4A,0x49,0x69,0x57,0x6A,
  0x59,0x58,0x67,0x87,0x5A,0x75,0x78,0x69,0x56,0x97,0x77,0x73,0x08,0x78,0x78,0x77,0x87,0x78,0x77,0x78,
  0x77,0x77,0x87,0x78,0x68,0x77,0x77,0x87,0x78,0x76,0x86,0x97,0x58,0x77,0x78,0x58,0x78,0x77,0x68,0x78,
  0x75,0x95,0xB7,0x70,0x8F,0x80,0xA6,0x87,0x65,0x66,0x78,0x7A,0x17,0x77,0x70,
};

static void SIT13_Func1(struct SIT13Data *s, struct SIT13Buffer *buf, ULONG info, UWORD bits, UWORD num)
{
  ULONG i, j;

  if(bits <= 12)
  {
    for(i = 0; i < (1<<12); i += (1<<bits))
    {
      buf[info+i].data = num;
      buf[info+i].bits = bits;
    }
  }
  else
  {
    j = bits-12;

    if(buf[info & 0xFFF].bits != 0x1F)
    {
      buf[info & 0xFFF].bits = 0x1F;
      buf[info & 0xFFF].data = s->MaxBits++;
    }
    bits = buf[info & 0xFFF].data;
    info >>= 12;

    while(j--)
    {
      UWORD *a;

      a = info & 1 ? &s->Buffer4[bits].d2 : &s->Buffer4[bits].d1;
      if(!*a)
        *a = s->MaxBits++;
      bits = *a;
      info >>= 1;
    }
    s->Buffer4[bits].freq = num;
  }
}

static void SIT13_SortTree(struct SIT13Data *s, struct SIT13Buffer *buf, struct SIT13Buffer *buf2)
{
  UWORD td;
  BYTE tb;

  struct SIT13Buffer *a, *b;

  while(buf2-1 > buf)
  {
    a = buf;
    b = buf2;

    for(;;)
    {
      while(++a < buf2)
      {
        tb = a->bits - buf->bits;
        if(tb > 0 || (!tb && (a->data >= buf->data)))
          break;
      }
      while(--b > buf)
      {
        tb = b->bits - buf->bits;
        if(tb < 0 || (!tb && (b->data <= buf->data)))
          break;
      }
      if(b < a)
        break;
      else
      {
        tb = a->bits;
        td = a->data;
        a->bits = b->bits;
        a->data = b->data;
        b->bits = tb;
        b->data = td;
      }
    }
    if(b == buf)
      ++buf;
    else
    {
      tb = buf->bits;
      td = buf->data;
      buf->bits = b->bits;
      buf->data = b->data;
      b->bits = tb;
      b->data = td;
      if(buf2-b-1 > b-buf)
      {
        SIT13_SortTree(s, buf, b);
        buf = b+1;
      }
      else
      {
        SIT13_SortTree(s, b+1, buf2);
        buf2 = b;
      }
    }
  }
}

static void SIT13_Func2(struct SIT13Data *s, struct SIT13Buffer *buf, UWORD bits, struct SIT13Buffer *buf2)
{
  LONG i, j, k, l, m, n;

  SIT13_SortTree(s, buf2, buf2 + bits);

  l = k = j = 0;
  for(i = 0; i < bits; ++i)
  {
    l += k;
    m = buf2[i].bits;
    if(m != j)
    {
      if((j = m) == -1)
        k = 0;
      else
        k = 1 << (32-j);
    }
    if(j > 0)
    {
      for(n = m = 0; n < 8*4; n += 4)
        m += SIT13Bits[(l>>n)&0xF]<<(7*4-n);
      SIT13_Func1(s, buf, m, j, buf2[i].data);
    }
  }
}

static void SIT13_CreateStaticTree(struct SIT13Data *s, struct SIT13Buffer *buf, UWORD bits, UBYTE *bitsbuf)
{
  ULONG i;

  for(i = 0; i < bits; ++i)
  {
    s->Buffer5[i].data = i;
    s->Buffer5[i].bits = bitsbuf[i];
  }
  SIT13_Func2(s, buf, bits, s->Buffer5);
}

static void SIT13InitInfo(struct SIT13Data *s, UBYTE id)
{
  LONG i;
  UBYTE k, l = 0, *a, *b;

  a = s->TextBuf;
  b = (UBYTE *) SIT13Static+SIT13StaticPos[id-1];
  id &= 1;

  for(i = 658; i; --i)
  {
    k = id ? *b >> 4 : *(b++) & 0xF; id ^=1;

    if(!k)
    {
      l -= id ? *b >> 4 : *(b++) & 0xF; id ^= 1;
    }
    else
    {
      if(k == 15)
      {
        l += id ? *b >> 4 : *(b++) & 0xF; id ^= 1;
      }
      else
        l += k-7;
    }
    *(a++) = l;
  }
}

static void SIT13_Extract(struct SIT13Data *s, struct xadInOut *io)
{
  ULONG wpos = 0, j, k, l, size;
  struct SIT13Buffer *buf = s->Buffer3;

  while(!io->xio_Error)
  {
    k = xadIOReadBitsLow(io, 12);
    if((j = buf[k].bits) <= 12)
    {
      l = buf[k].data;
      xadIODropBitsLow(io, j);
    }
    else
    {
      xadIODropBitsLow(io, 12);

      j = buf[k].data;
      while(s->Buffer4[j].freq == -1)
        j = xadIOGetBitsLow(io, 1) ? s->Buffer4[j].d2 : s->Buffer4[j].d1;
      l = s->Buffer4[j].freq;
    }
    if(l < 0x100)
    {
      s->Window[wpos++] = xadIOPutChar(io, l);
      wpos &= 0xFFFF;
      buf = s->Buffer3;
    }
    else
    {
      buf = s->Buffer3b;
      if(l < 0x13E)
        size = l - 0x100 + 3;
      else
      {
        if(l == 0x13E)
          size = xadIOGetBitsLow(io, 10);
	else
	{
	  if(l == 0x140)
	    return;
	  size = xadIOGetBitsLow(io, 15);
        }
        size += 65;
      }
      j = xadIOReadBitsLow(io, 12);
      k = s->Buffer2[j].bits;
      if(k <= 12)
      {
        l = s->Buffer2[j].data;
        xadIODropBitsLow(io, k);
      }
      else
      {
        xadIODropBitsLow(io, 12);
        j = s->Buffer2[j].data;
        while(s->Buffer4[j].freq == -1)
          j = xadIOGetBitsLow(io, 1) ? s->Buffer4[j].d2 : s->Buffer4[j].d1;
	l = s->Buffer4[j].freq;
      }
      k = 0;
      if(l--)
        k = (1 << l) | xadIOGetBitsLow(io, l);
      l = wpos+0x10000-(k+1);
      while(size--)
      {
        l &= 0xFFFF;
        s->Window[wpos++] = xadIOPutChar(io, s->Window[l++]);
        wpos &= 0xFFFF;
      }
    } /* l >= 0x100 */
  }
}

static void SIT13_CreateTree(struct SIT13Data *s, struct xadInOut *io, struct SIT13Buffer *buf, UWORD num)
{
  struct SIT13Buffer *b;
  ULONG i;
  UWORD data;
  BYTE bi = 0;

  for(i = 0; i < num; ++i)
  {
    b = &s->Buffer1[xadIOReadBitsLow(io, 12)];
    data = b->data;
    xadIODropBitsLow(io, b->bits);

    switch(data-0x1F)
    {
    case 0: bi = -1; break;
    case 1: ++bi; break;
    case 2: --bi; break;
    case 3:
      if(xadIOGetBitsLow(io, 1))
        s->Buffer5[i++].bits = bi;
      break;
    case 4:
      data = xadIOGetBitsLow(io, 3)+2;
      while(data--)
        s->Buffer5[i++].bits = bi;
      break;
    case 5:
      data = xadIOGetBitsLow(io, 6)+10;
      while(data--)
        s->Buffer5[i++].bits = bi;
      break;
    default: bi = data+1; break;
    }
    s->Buffer5[i].bits = bi;
  }
  for(i = 0; i < num; ++i)
    s->Buffer5[i].data = i;
  SIT13_Func2(s, buf, num, s->Buffer5);
}

static LONG SIT_13(struct xadInOut *io)
{
  ULONG i, j;
  struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;
  struct SIT13Data *s;

  if((s = xadAllocVec(sizeof(struct SIT13Data), MEMF_CLEAR)))
  {
    s->MaxBits = 1;
    for(i = 0; i < 37; ++i)
      SIT13_Func1(s, s->Buffer1, SIT13Info[i], SIT13InfoBits[i], i);
    for(i = 1; i < 0x704; ++i)
    {
      /* s->Buffer4[i].d1 = s->Buffer4[i].d2 = 0; */
      s->Buffer4[i].freq = -1;
    }

    j = xadIOGetChar(io);
    i = j>>4;
    if(i > 5)
      io->xio_Error = XADERR_ILLEGALDATA;
    else if(i)
    {
      SIT13InitInfo(s, i--);
      SIT13_CreateStaticTree(s, s->Buffer3, 0x141, s->TextBuf);
      SIT13_CreateStaticTree(s, s->Buffer3b, 0x141, s->TextBuf+0x141);
      SIT13_CreateStaticTree(s, s->Buffer2, SIT13StaticBits[i], s->TextBuf+0x282);
    }
    else
    {
      SIT13_CreateTree(s, io, s->Buffer3, 0x141);
      if(j&8)
        xadCopyMem(s->Buffer3, s->Buffer3b, 0x1000*sizeof(struct SIT13Buffer));
      else
	SIT13_CreateTree(s, io, s->Buffer3b, 0x141);
      j = (j&7)+10;
      SIT13_CreateTree(s, io, s->Buffer2, j);
    }
    if(!io->xio_Error)
      SIT13_Extract(s, io);
    xadFreeObjectA(s, 0);
  }
  return io->xio_Error;
}

/*****************************************************************************/

struct SIT14Data {
  struct xadInOut *io;
  UBYTE code[308];
  UBYTE codecopy[308];
  UWORD freq[308];
  ULONG buff[308];

  UBYTE var1[52];
  UWORD var2[52];
  UWORD var3[75*2];

  UBYTE var4[76];
  ULONG var5[75];
  UBYTE var6[1024];
  UWORD var7[308*2];
  UBYTE var8[0x4000];

  UBYTE Window[0x40000];
};

static void SIT14_Update(UWORD first, UWORD last, UBYTE *code, UWORD *freq)
{
  UWORD i, j;

  while(last-first > 1)
  {
    i = first;
    j = last;

    do
    {
      while(++i < last && code[first] > code[i])
        ;
      while(--j > first && code[first] < code[j])
        ;
      if(j > i)
      {
        UWORD t;
        t = code[i]; code[i] = code[j]; code[j] = t;
        t = freq[i]; freq[i] = freq[j]; freq[j] = t;
      }
    } while(j > i);

    if(first != j)
    {
      {
        UWORD t;
        t = code[first]; code[first] = code[j]; code[j] = t;
        t = freq[first]; freq[first] = freq[j]; freq[j] = t;
      }

      i = j+1;
      if(last-i <= j-first)
      {
        SIT14_Update(i, last, code, freq);
        last = j;
      }
      else
      {
        SIT14_Update(first, j, code, freq);
        first = i;
      }
    }
    else
      ++first;
  }
}

static void SIT14_ReadTree(struct SIT14Data *dat, UWORD codesize, UWORD *result)
{
  ULONG size, i, j, k, l, m, n, o;

  k = xadIOGetBitsLow(dat->io, 1);
  j = xadIOGetBitsLow(dat->io, 2)+2;
  o = xadIOGetBitsLow(dat->io, 3)+1;
  size = 1<<j;
  m = size-1;
  k = k ? m-1 : -1;
  if(xadIOGetBitsLow(dat->io, 2)&1) /* skip 1 bit! */
  {
    /* requirements for this call: dat->buff[32], dat->code[32], dat->freq[32*2] */
    SIT14_ReadTree(dat, size, dat->freq);
    for(i = 0; i < codesize; )
    {
      l = 0;
      do
      {
        l = dat->freq[l + xadIOGetBitsLow(dat->io, 1)];
	n = size<<1;
      } while(n > l);
      l -= n;
      if(k != l)
      {
        if(l == m)
        {
          l = 0;
          do
          {
            l = dat->freq[l + xadIOGetBitsLow(dat->io, 1)];
            n = size<<1;
          } while(n > l);
          l += 3-n;
          while(l--)
          {
            dat->code[i] = dat->code[i-1];
            ++i;
          }
        }
        else
          dat->code[i++] = l+o;
      }
      else
        dat->code[i++] = 0;
    }
  }
  else
  {
    for(i = 0; i < codesize; )
    {
      l = xadIOGetBitsLow(dat->io, j);
      if(k != l)
      {
        if(l == m)
        {
          l = xadIOGetBitsLow(dat->io, j)+3;
          while(l--)
          {
            dat->code[i] = dat->code[i-1];
            ++i;
	  }
        }
        else
          dat->code[i++] = l+o;
      }
      else
        dat->code[i++] = 0;
    }
  }

  for(i = 0; i < codesize; ++i)
  {
    dat->codecopy[i] = dat->code[i];
    dat->freq[i] = i;
  }
  SIT14_Update(0, codesize, dat->codecopy, dat->freq);

  for(i = 0; i < codesize && !dat->codecopy[i]; ++i)
    ; /* find first nonempty */
  for(j = 0; i < codesize; ++i, ++j)
  {
    if(i)
      j <<= (dat->codecopy[i] - dat->codecopy[i-1]);

    k = dat->codecopy[i]; m = 0;
    for(l = j; k--; l >>= 1)
      m = (m << 1) | (l&1);

    dat->buff[dat->freq[i]] = m;
  }

  for(i = 0; i < codesize*2; ++i)
    result[i] = 0;

  j = 2;
  for(i = 0; i < codesize; ++i)
  {
    l = 0;
    m = dat->buff[i];

    for(k = 0; k < dat->code[i]; ++k)
    {
      l += (m&1);
      if(dat->code[i]-1 <= k)
        result[l] = codesize*2+i;
      else
      {
        if(!result[l])
        {
          result[l] = j; j += 2;
        }
        l = result[l];
      }
      m >>= 1;
    }
  }
  xadIOByteBoundary(dat->io);
}

static LONG SIT_14(struct xadInOut *io)
{
  ULONG i, j, k, l, m, n;
  struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;
  struct SIT14Data *dat;

  if((dat = (struct SIT14Data *) xadAllocVec(sizeof(struct SIT14Data), MEMF_ANY|MEMF_CLEAR)))
  {
    dat->io = io;

    /* initialization */
    for(i = k = 0; i < 52; ++i)
    {
      dat->var2[i] = k;
      k += (1<<(dat->var1[i] = ((i >= 4) ? ((i-4)>>2) : 0)));
    }
    for(i = 0; i < 4; ++i)
      dat->var8[i] = i;
    for(m = 1, l = 4; i < 0x4000; m <<= 1) /* i is 4 */
    {
      for(n = l+4; l < n; ++l)
      {
        for(j = 0; j < m; ++j)
          dat->var8[i++] = l;
      }
    }
    for(i = 0, k = 1; i < 75; ++i)
    {
      dat->var5[i] = k;
      k += (1<<(dat->var4[i] = (i >= 3 ? ((i-3)>>2) : 0)));
    }
    for(i = 0; i < 4; ++i)
      dat->var6[i] = i-1;
    for(m = 1, l = 3; i < 0x400; m <<= 1) /* i is 4 */
    {
      for(n = l+4; l < n; ++l)
      {
        for(j = 0; j < m; ++j)
          dat->var6[i++] = l;
      }
    }

    m = xadIOGetBitsLow(io, 16); /* number of blocks */
    j = 0; /* window position */
    while(m-- && !(io->xio_Flags & (XADIOF_ERROR|XADIOF_LASTOUTBYTE)))
    {
      /* these functions do not support access > 24 bit */
      xadIOGetBitsLow(io, 16); /* skip crunched block size */
      xadIOGetBitsLow(io, 16);
      n = xadIOGetBitsLow(io, 16); /* number of uncrunched bytes */
      n |= xadIOGetBitsLow(io, 16)<<16;
      SIT14_ReadTree(dat, 308, dat->var7);
      SIT14_ReadTree(dat, 75, dat->var3);

      while(n && !(io->xio_Flags & (XADIOF_ERROR|XADIOF_LASTOUTBYTE)))
      {
        for(i = 0; i < 616;)
          i = dat->var7[i + xadIOGetBitsLow(io, 1)];
        i -= 616;
        if(i < 0x100)
        {
          dat->Window[j++] = xadIOPutChar(io, i);
          j &= 0x3FFFF;
          --n;
        }
        else
        {
          i -= 0x100;
          k = dat->var2[i]+4;
          i = dat->var1[i];
          if(i)
            k += xadIOGetBitsLow(io, i);
          for(i = 0; i < 150;)
            i = dat->var3[i + xadIOGetBitsLow(io, 1)];
          i -= 150;
          l = dat->var5[i];
          i = dat->var4[i];
          if(i)
            l += xadIOGetBitsLow(io, i);
          n -= k;
          l = j+0x40000-l;
          while(k--)
          {
            l &= 0x3FFFF;
            dat->Window[j++] = xadIOPutChar(io, dat->Window[l++]);
            j &= 0x3FFFF;
          }
        }
      }
      xadIOByteBoundary(io);
    }
    xadFreeObjectA(dat, 0);
  }
  return io->xio_Error;
}

/*****************************************************************************/

ASM(LONG) SIT_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct xadFileInfo *fi;
  struct xadInOut *io;
  LONG err;

  fi = ai->xai_CurFile;

  if((io = xadIOAlloc(XADIOF_ALLOCINBUFFER|XADIOF_ALLOCOUTBUFFER|XADIOF_NOCRC32, ai, xadMasterBase)))
  {
    io->xio_InSize = fi->xfi_CrunchSize;
    io->xio_OutSize = fi->xfi_Size;

    switch(SITPI(fi)->Method)
    {
    case SITnocomp:
      while(!(io->xio_Flags & (XADIOF_LASTOUTBYTE|XADIOF_ERROR)))
        xadIOPutChar(io, xadIOGetChar(io));
      err = io->xio_Error;
      break;
    case SITrle: err = SIT_rle(io); break;
    case SITlzc: err = UCompDecomp(io, 14|UCOMPBLOCK_MASK); break;
    case SIThuffman: err = SIT_huffman(io); break;
    case SITlzah: err = SIT_lzah(io); break;
    case SITmw: err = SIT_mw(io); break;
    case 13: io->xio_Flags |= XADIOF_NOINENDERR; err = SIT_13(io); break;
    case 14: err = SIT_14(io); break;
    default:
#ifdef DEBUG
      {
        UBYTE data[4];
        data[0] = 'S'; data[1] = 'I'; data[2] = 'T'; data[3] = SITPI(fi)->Method;
        xadHookAccess(XADAC_WRITE, 4, data, ai);
        xadHookAccess(XADAC_WRITE, 4, &fi->xfi_Size, ai);
        xadHookAccess(XADAC_WRITE, 4, &fi->xfi_CrunchSize, ai);
        xadHookAccess(XADAC_WRITE, 2, &(SITPI(fi)->CRC), ai);
        xadHookAccess(XADAC_COPY, fi->xfi_CrunchSize, 0, ai);
      }
#endif
      err = XADERR_DATAFORMAT;
    }
    if(!err)
      err = xadIOWriteBuf(io);
    if(!err && io->xio_CRC16 != SITPI(fi)->CRC)
      err = XADERR_CHECKSUM;
    xadFreeObjectA(io, 0);
  }
  else
    err = XADERR_NOMEMORY;

  return err;
}

/*****************************************************************************/

ASM(BOOL) SIT5_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *data),
REG(a6, struct xadMasterBase *xadMasterBase))
{ CPUCHECK
  {
    STRPTR a = "StuffIt (c)1997-1998 Aladdin Systems, Inc., http://www.aladdinsys.com/StuffIt/\x0d\x0a";

    while(*a && *a == *data)
    {
      ++a; ++data;
    }
    if(!*a)
      return 1;
  }
  return 0;
}

/*****************************************************************************/

#define SIT5_ID	0xA5A5A5A5

/* header format 20 byte
  UBYTE[80] header text
  ULONG     ???
  ULONG     total archive size
  ULONG     offset of first entry
  ULONG     ???
  ULONG     ???
*/

#define SIT5AH_ARCSIZE		84
#define SIT5AH_FIRSTENTRY	88

/* archive block entry				directory:
 0  ULONG     id = SIT5_ID			<--
 4  UBYTE     version				<--
 5  UBYTE     ???
 6  UWORD     header size			<--
 8  UBYTE     ??? (system ID?)
 9  UBYTE     type				<--
10  ULONG     creation date			<--
14  ULONG     modification date			<--
18  ULONG     offset of previous entry		<--
22  ULONG     offset of next entry		<--
26  ULONG     offset of directory entry		<--
30  UWORD     filename size			<--
32  UWORD     header crc			<--
34  ULONG     data file size			offset of first entry
38  ULONG     data crunched size		size of complete directory
42  UWORD     data old crc16 (not with algo 15)
44  UWORD     ???
46  UBYTE     data algorithm
	        none    ==  0
	        fastest == 13
	        max     == 15
47  UBYTE     password data len			number of files
48  UBYTE[..] password information
48+pwdlen            UBYTE[..] filename		<--
48+pwdlen+namelen    UWORD     commentsize
48+pwdlen+namelen+2  UWORD     ????
48+pwdlen+namelen+4  UBYTE[..] comment

  second block:
 0  UWORD     ??? (resource exists?)
 2  UWORD     ???
 4  ULONG     file type
 8  ULONG     file creator
12  UWORD     finder flags
14  UWORD     ???
16  ULONG     ??? (macintosh date variable - version 3)
20  ULONG     ???
24  ULONG     ???
28  ULONG     ???

32  ULONG     ??? (version 3 misses this one and following?)

36  ULONG     rsrc file size
40  ULONG     rsrc crunched size
44  UWORD     rsrc old crc16 (not with algo 15)
46  UWORD     ???
48  UBYTE     rsrc algorithm

  followed by resource fork data
  followed by data fork data

  ! The header crc is CRC16 of header size with crc field cleared !
*/

#define SIT5FH_ID		 0
#define SIT5FH_VERSION		 4
#define SIT5FH_HEADERSIZE	 6
#define SIT5FH_FLAGS		 9
#define SIT5FH_CREATIONDATE	10
#define SIT5FH_MODDATE		14
#define SIT5FH_PREVFILE		18
#define SIT5FH_NEXTFILE		22
#define SIT5FH_DIRECTORY	26
#define SIT5FH_FILENAMESIZE	30
#define SIT5FH_HEADERCRC	32
#define SIT5FH_DATASIZE		34
#define SIT5FH_DATACRUNCHSIZE	38
#define SIT5FH_DATACRC16	42
#define SIT5FH_DATAALGORITHM	46
#define SIT5FH_PASSWORDSIZE	47
#define SIT5FH_FILENAME		48

#define SIT5FH_FILETYPE		 4
#define SIT5FH_FILECREATOR	 8
#define SIT5FH_FINDERFLAGS	12
#define SIT5FH_RSRCSIZE		36
#define SIT5FH_RSRCCRUNCHSIZE	40
#define SIT5FH_RSRCCRC16	44
#define SIT5FH_RSRCALGORITHM	48

#define SIT5FLAGS_DIRECTORY	0x40
#define SIT5FLAGS_CRYPTED	0x20
#define SIT5FLAGS_RSRC_FORK	0x10

ASM(LONG) SIT5_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG err, i, j;
  ULONG dirsize = 0, nsize, csize, csize2;
  STRPTR buffer;
  struct xadFileInfo *fi, *ldir = 0, *lfi = 0;

  if((buffer = (STRPTR) xadAllocVec(2048, MEMF_PUBLIC)))
  {
    if(!(err = xadHookAccess(XADAC_READ, 100, buffer, ai)))
    {
      i = EndGetM32(buffer + SIT5AH_FIRSTENTRY)-100;
      if(!i || !(err = xadHookAccess(XADAC_INPUTSEEK, i, 0, ai)))
      {
        while(!err && ai->xai_InPos < ai->xai_InSize)
        {
          if(!(err = xadHookAccess(XADAC_READ, 48, buffer, ai)))
          {
	    if((buffer[SIT5FH_FLAGS] & SIT5FLAGS_DIRECTORY) && EndGetM32(buffer+SIT5FH_DATASIZE) == 0xFFFFFFFF)
	    {
	      if((ldir = (struct xadFileInfo *) ldir->xfi_PrivateInfo))
	        dirsize = strlen(ldir->xfi_FileName)+1;
	      else
	        dirsize = 0;
	    }
	    else
	    {
	      i = EndGetM16(buffer+SIT5FH_HEADERSIZE);
	      j = buffer[SIT5FH_VERSION] == 1 ? 36 : 32;
              if(EndGetM32(buffer+SIT5FH_ID) != SIT5_ID || i + j > 2048-14)
                err = XADERR_DATAFORMAT;
              else if(!(err = xadHookAccess(XADAC_READ, i+j-48, buffer+48, ai)))
              {
                if(!buffer[i+1] || !(err = xadHookAccess(XADAC_READ, 14, buffer+i+j, ai)))
                {
		  nsize = EndGetM16(buffer+SIT5FH_FILENAMESIZE);
		  if(buffer[SIT5FH_FLAGS] & SIT5FLAGS_DIRECTORY)
		  {
		    if(i > SIT5FH_FILENAME + nsize)
		      csize = EndGetM16(buffer + SIT5FH_FILENAME + nsize);
                    else
                      csize = 0;

	            if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, nsize+1+dirsize, csize ?
	            XAD_OBJCOMMENTSIZE : TAG_IGNORE, csize + 1, TAG_DONE)))
	            {
	              MACcopyname(ldir, buffer+SIT5FH_FILENAME, fi->xfi_FileName, nsize, 0);
	              if(csize)
                        xadCopyMem(buffer+SIT5FH_FILENAME+nsize+4, fi->xfi_Comment, csize);

                      xadConvertDates(XAD_DATEMAC, EndGetM32(buffer+SIT5FH_MODDATE), XAD_GETDATEXADDATE,
                      &fi->xfi_Date, TAG_DONE);

	              fi->xfi_Flags |= XADFIF_DIRECTORY;
	              fi->xfi_PrivateInfo = (APTR) ldir;
	              ldir = fi;
	              dirsize = strlen(fi->xfi_FileName)+1;

                      err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos, TAG_DONE);
	            }
	            else
	              err = XADERR_NOMEMORY;
		  }
		  else
		  {
		    if(i > SIT5FH_FILENAME + buffer[SIT5FH_PASSWORDSIZE] + nsize)
		      csize = EndGetM16(buffer + SIT5FH_FILENAME + buffer[SIT5FH_PASSWORDSIZE] + nsize);
                    else
                      csize = 0;

                    csize2 = SITmakecomment(buffer+i+SIT5FH_FILETYPE, EndGetM16(buffer+i+SIT5FH_FINDERFLAGS),
                    buffer+SIT5FH_FILENAME+buffer[SIT5FH_PASSWORDSIZE]+nsize+4, csize, 0);
		    if(buffer[i+1]) /* rsrc tree */
		    {
	              if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, nsize+1+dirsize+5,
	              XAD_OBJPRIVINFOSIZE, sizeof(struct SITPrivate), csize2 && !EndGetM32(buffer+SIT5FH_DATASIZE) ?
	              XAD_OBJCOMMENTSIZE : TAG_IGNORE, csize2, TAG_DONE)))
	              {
	                MACcopyname(ldir, buffer+SIT5FH_FILENAME+buffer[SIT5FH_PASSWORDSIZE], fi->xfi_FileName, nsize, 1);
	                /* if comment field is zero, nothing is done! */
                        SITmakecomment(buffer+i+SIT5FH_FILETYPE, EndGetM16(buffer+i+SIT5FH_FINDERFLAGS),
                        buffer+SIT5FH_FILENAME+buffer[SIT5FH_PASSWORDSIZE]+nsize+4, csize, fi->xfi_Comment);

                        xadConvertDates(XAD_DATEMAC, EndGetM32(buffer+SIT5FH_MODDATE), XAD_GETDATEXADDATE,
                        &fi->xfi_Date, TAG_DONE);

			if(buffer[SIT5FH_FLAGS] & SIT5FLAGS_CRYPTED)
			{
			  fi->xfi_Flags |= XADFIF_CRYPTED;
			  ai->xai_Flags |= XADAIF_CRYPTED;
			}

                        fi->xfi_CrunchSize = EndGetM32(buffer+i+SIT5FH_RSRCCRUNCHSIZE);
                        fi->xfi_Size = EndGetM32(buffer+i+SIT5FH_RSRCSIZE);

	                fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACRESOURCE|XADFIF_EXTRACTONBUILD;
	                fi->xfi_DataPos = ai->xai_InPos;
	                lfi = fi;

	                SITPI(fi)->CRC = EndGetM16(buffer+i+SIT5FH_RSRCCRC16);
	                SITPI(fi)->Method = buffer[i+SIT5FH_RSRCALGORITHM];
#ifdef DEBUG
  if(SITPI(fi)->Method != 0 && SITPI(fi)->Method != 13 && SITPI(fi)->Method != 15)
  {
    xadHookAccess(0,SITPI(fi)->Method,0,ai);
    UnknownRequest(xadMasterBase, SITPI(fi)->Method, ai->xai_InName);
  }
#endif
	                if(SITPI(fi)->Method <= STUFFITMAXALGO)
	                  fi->xfi_EntryInfo = sittypes[SITPI(fi)->Method];

                        err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos+fi->xfi_CrunchSize, TAG_DONE);
	              }
	              else
	                err = XADERR_NOMEMORY;
	            }

		    if(!err && (EndGetM32(buffer+SIT5FH_DATASIZE) || !buffer[i+1]))
		    {
	              if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, nsize+1+dirsize,
	              XAD_OBJPRIVINFOSIZE, sizeof(struct SITPrivate), csize2 ? XAD_OBJCOMMENTSIZE : TAG_IGNORE,
	              csize2, TAG_DONE)))
	              {
	                MACcopyname(ldir, buffer+SIT5FH_FILENAME+buffer[SIT5FH_PASSWORDSIZE], fi->xfi_FileName, nsize, 0);
                        SITmakecomment(buffer+i+SIT5FH_FILETYPE, EndGetM16(buffer+i+SIT5FH_FINDERFLAGS),
                        buffer+SIT5FH_FILENAME+buffer[SIT5FH_PASSWORDSIZE]+nsize+4, csize, fi->xfi_Comment);
	                if(buffer[i+1])
	                {
                          fi->xfi_MacFork = lfi;
                          lfi->xfi_MacFork = fi;
	                }

                        xadConvertDates(XAD_DATEMAC, EndGetM32(buffer+SIT5FH_MODDATE), XAD_GETDATEXADDATE,
                        &fi->xfi_Date, TAG_DONE);

			if(buffer[SIT5FH_FLAGS] & SIT5FLAGS_CRYPTED)
			{
			  fi->xfi_Flags |= XADFIF_CRYPTED;
			  ai->xai_Flags |= XADAIF_CRYPTED;
			}

                        fi->xfi_CrunchSize = EndGetM32(buffer+SIT5FH_DATACRUNCHSIZE);
                        fi->xfi_Size = EndGetM32(buffer+SIT5FH_DATASIZE);

	                fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACDATA|XADFIF_EXTRACTONBUILD;
	                fi->xfi_DataPos = ai->xai_InPos;

	                SITPI(fi)->CRC = EndGetM16(buffer+SIT5FH_DATACRC16);
	                SITPI(fi)->Method = buffer[SIT5FH_DATAALGORITHM];
#ifdef DEBUG
  if(SITPI(fi)->Method != 0 && SITPI(fi)->Method != 13 && SITPI(fi)->Method != 15)
  {
    xadHookAccess(0,SITPI(fi)->Method,0,ai);
    UnknownRequest(xadMasterBase, SITPI(fi)->Method, ai->xai_InName);
  }
#endif
	                if(SITPI(fi)->Method <= STUFFITMAXALGO)
	                  fi->xfi_EntryInfo = sittypes[SITPI(fi)->Method];

                        err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos+fi->xfi_CrunchSize, TAG_DONE);
	              }
	              else
	                err = XADERR_NOMEMORY;
	            }
	          }
	        }
              }
            }
          }
        }
      }
    }
    xadFreeObjectA(buffer,0);
  }
  else
    err = XADERR_NOMEMORY;

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

#ifdef DEBUG
  if(err || (ai->xai_Flags & XADAIF_CRYPTED))
    UnknownRequest(xadMasterBase, 0, ai->xai_InName);
#endif

  return (ai->xai_FileInfo ? 0 : err);
}

/*****************************************************************************/

ASM(BOOL) SIT5EXE_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *data),
REG(a6, struct xadMasterBase *xadMasterBase))
{ CPUCHECK
  if(data[0] == 'M' && data[1] == 'Z' && EndGetM32(data+4100) == 0x4203e853)
    return 1;
  return 0;
}

/*****************************************************************************/

ASM(LONG) SIT5EXE_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG err;

  if(!(err = xadHookAccess(XADAC_INPUTSEEK, 0x1A000, 0, ai)))
    err = SIT5_GetInfo(ai, xadMasterBase);
  return err;
}

/*****************************************************************************/

ASM(BOOL) MacBinary_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *data),
REG(a6, struct xadMasterBase *xadMasterBase))
{ CPUCHECK
  if(data[0] <= 1 && data[1] >= 1 && data[1] && data[1] <= 63 && !data[74] && !data[82])
  {
    if(DoCRC(data, 124) == EndGetM16(data+124))
    {
      if(data[102] == 'm' && data[103] == 'B' && data[104] == 'I' &&
      data[105] == 'N')
        return 3; /* is MacBinaryIII */
      return 2; /* MacBinaryII */
    }
    else if(!data[0]) /* test for MacBinary type I */
    {
      if(EndGetM32(data+83) <= 0xFFFFFF && EndGetM32(data+87) <= 0xFFFFFF)
      {
        LONG i;
        for(i = 65; i < 65+8; ++i) /* check finder flags */
        {
          if(data[i] < 0x20 || data[i] > 0x7E)
            return 0;
        }
        for(i = 0; i < data[1]; ++i) /* there can be no zero in name! */
        {
          if(!data[i+2])
            return 0;
        }
        return 1;
      }
    }
  }

  return 0;
}

/*****************************************************************************/

ASM(LONG) MacBinary_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct xadFileInfo *fi, *lfi, *ldir = 0;
  LONG err = 0, type, rsize, dsize, csize, i, dirsize = 0;
  UBYTE header[128];

  while(!err && ai->xai_InPos + 128 <= ai->xai_InSize)
  {
    if(!(err = xadHookAccess(XADAC_READ, 128, header, ai)))
    {
      type = MacBinary_RecogData(128, header, xadMasterBase);

#ifdef DEBUG
  if(header[0] == 1 || (ai->xai_InPos != 128 && type))
    UnknownRequest(xadMasterBase, 0, ai->xai_InName);
#endif

      if(!type)
        err = XADERR_DATAFORMAT;
      else if(header[0] == 1 && header[65] == 'f' && header[66] == 'o' && header[67] == 'l' &&
      header[68] == 'd')
      {
        if(!header[1] && ldir) /* stop block????? - no clear standard description */
        {
	  if((ldir = (struct xadFileInfo *) ldir->xfi_PrivateInfo))
	    dirsize = strlen(ldir->xfi_FileName)+1;
          else
	    dirsize = 0;
        }
	else if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, header[1]+1+dirsize, TAG_DONE)))
        {
	  MACcopyname(ldir, header+2, fi->xfi_FileName, header[1], 0);

	  fi->xfi_Flags |= XADFIF_DIRECTORY;
          xadConvertDates(XAD_DATEMAC, EndGetM32(header+95), XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);
	  fi->xfi_PrivateInfo = (APTR) ldir;
	  ldir = fi;
	  dirsize = strlen(fi->xfi_FileName)+1;
          err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos, TAG_DONE);
        }
        else
          err = XADERR_NOMEMORY;
      }
      else
      {
        csize = SITmakecomment(header+65, (header[73]<<8)+(type>1 ? header[101] : 0), 0, 0, 0);
        rsize = EndGetM32(header+87);
        dsize = EndGetM32(header+83);
	lfi = 0;

        if(dsize) /* data fork */
        {
	  if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, header[1]+1+dirsize,
          csize ? XAD_OBJCOMMENTSIZE : TAG_IGNORE, csize, TAG_DONE)))
	  {
	    MACcopyname(ldir, header+2, fi->xfi_FileName, header[1], 0);
	    /* if comment field is zero, nothing is done! */
	    SITmakecomment(header+65, (header[73]<<8)+(type>1 ? header[101] : 0), 0, 0, fi->xfi_Comment);

            fi->xfi_CrunchSize = fi->xfi_Size = dsize;
            lfi = fi;

	    fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACDATA|XADFIF_EXTRACTONBUILD;
	    fi->xfi_DataPos = ai->xai_InPos;

            xadConvertDates(XAD_DATEMAC, EndGetM32(header+95), XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);

            i = (fi->xfi_CrunchSize+127)&(~127);
            if(ai->xai_InSize-ai->xai_InPos == fi->xfi_CrunchSize)
             i = fi->xfi_CrunchSize;
            err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos+i, TAG_DONE);
	  }
	  else
	    err = XADERR_NOMEMORY;
        }
        if(!err && rsize) /* resource fork */
        {
	  if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJNAMESIZE, header[1]+1+5+dirsize,
          dsize || !csize ? TAG_IGNORE : XAD_OBJCOMMENTSIZE, csize, TAG_DONE)))
          {
	    MACcopyname(ldir, header+2, fi->xfi_FileName, header[1], 1);
	    /* if comment field is zero, nothing is done! */
	    SITmakecomment(header+65, (header[73]<<8)+(type>1 ? header[101] : 0), 0, 0, fi->xfi_Comment);

            fi->xfi_CrunchSize = fi->xfi_Size = rsize;

	    fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACRESOURCE|XADFIF_EXTRACTONBUILD;
	    fi->xfi_DataPos = ai->xai_InPos;

            if((fi->xfi_MacFork = lfi))
              lfi->xfi_MacFork = fi;

            xadConvertDates(XAD_DATEMAC, EndGetM32(header+95), XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);

            i = (fi->xfi_CrunchSize+127)&(~127);
            if(ai->xai_InSize-ai->xai_InPos == fi->xfi_CrunchSize)
             i = fi->xfi_CrunchSize;
            err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos+i, TAG_DONE);
	  }
	  else
	    err = XADERR_NOMEMORY;
        }
        if(!err && type > 1 && (csize = EndGetM16(header+99)))
        {
          err = xadHookAccess(XADAC_INPUTSEEK, (csize+127)&(~127), 0, ai);
        }
      }
    }
  }

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

  return (ai->xai_FileInfo ? 0 : err);
}

/*****************************************************************************/

ASM(LONG) MacBinary_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  return xadHookAccess(XADAC_COPY, ai->xai_CurFile->xfi_Size, 0, ai);
}

/*****************************************************************************/

const struct xadClient MacBinary_Client = {
NEXTCLIENT, XADCLIENT_VERSION, XADMASTERVERSION, MACBINARY_VERSION, MACBINARY_REVISION,
128, XADCF_FILEARCHIVER|XADCF_FREEFILEINFO, XADCID_MACBINARY, "MacBinary",
(BOOL (*)()) MacBinary_RecogData, (LONG (*)()) MacBinary_GetInfo,
(LONG (*)()) MacBinary_UnArchive, 0};

const struct xadClient SIT5EXE_Client = {
&MacBinary_Client, XADCLIENT_VERSION, XADMASTERVERSION, SIT5EXE_VERSION, SIT5EXE_REVISION,
8192, XADCF_FILEARCHIVER|XADCF_FREEFILEINFO, XADCID_SIT5EXE, "StuffIt 5 MS-EXE",
(BOOL (*)()) SIT5EXE_RecogData, (LONG (*)()) SIT5EXE_GetInfo,
(LONG (*)()) SIT_UnArchive, 0};

const struct xadClient SIT5_Client = {
&SIT5EXE_Client, XADCLIENT_VERSION, XADMASTERVERSION, SIT5_VERSION, SIT5_REVISION,
100, XADCF_FILEARCHIVER|XADCF_FREEFILEINFO, XADCID_SIT5, "StuffIt 5",
(BOOL (*)()) SIT5_RecogData, (LONG (*)()) SIT5_GetInfo,
(LONG (*)()) SIT_UnArchive, 0};

const struct xadClient SIT_Client = {
&SIT5_Client, XADCLIENT_VERSION, XADMASTERVERSION, SIT_VERSION, SIT_REVISION,
22, XADCF_FILEARCHIVER|XADCF_FREEFILEINFO, XADCID_SIT, "StuffIt",
(BOOL (*)()) SIT_RecogData, (LONG (*)()) SIT_GetInfo,
(LONG (*)()) SIT_UnArchive, 0};

#endif /* XADMASTER_STUFFIT_C */
