#ifndef XADMASTER_STUFFIT_C
#define XADMASTER_STUFFIT_C

/* Programmheader

        Name:           StuffIt.c
        Main:           xadmaster
        Versionstring:  $VER: StuffIt.c 1.11 (21.07.2002)
        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
 1.8   19.01.02 : added PackIt archiver
 1.9   24.03.02 : added character conversion
 1.10  24.06.02 : added new version 12 charset stuff
 1.11  21.07.02 : added algorithm 15
*/

/* StuffIt file archiver client for XAD.
 * Copyright (C) 2000-2002 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>
 *
 * algorithm 15 is based on the work of  Matthew T. Russotto
 * Copyright (C) 2002 Matthew T. Russotto <russotto@speakeasy.net>
 * http://www.speakeasy.org/~russotto/arseniccomp.html
 *
 * 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        12
UBYTE version[] = "$VER: StuffIt 1.11 (21.07.2002)" CPUTEXT " GPL by Dirk Stöcker";
#endif
#define SIT_VERSION             1
#define SIT_REVISION            11
#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 PACKIT_VERSION          SIT_VERSION
#define PACKIT_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", "Arsenic"};

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 STRPTR MACname(struct xadMasterBase *xadMasterBase, struct xadFileInfo *dir, STRPTR file, ULONG size, ULONG rsrc)
{
  return xadConvertName(CHARSET_HOST,
  XAD_XADSTRING, dir ? dir->xfi_FileName : 0,
  XAD_CHARACTERSET, CHARSET_MACOS,
  XAD_STRINGSIZE, size,
  XAD_CSTRING, file,
  XAD_CHARACTERSET, CHARSET_ISO_8859_1,          /* the .rsrc ending */
  XAD_ADDPATHSEPERATOR, FALSE,
  rsrc ? XAD_CSTRING : TAG_IGNORE, ".rsrc",
  TAG_DONE);
}

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, 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)
          {
            ldir = (struct xadFileInfo *) ldir->xfi_PrivateInfo;
          }
          else if(sithdr[SITFH_COMPRMETHOD] == SITsfolder || sithdr[SITFH_COMPDMETHOD] == SITsfolder)
          {
            if((fi = xadAllocObjectA(XADOBJ_FILEINFO, 0)))
            {
              if((fi->xfi_FileName = MACname(xadMasterBase, ldir, sithdr+SITFH_FNAME, nsize, 0)))
              {
                fi->xfi_Flags |= XADFIF_DIRECTORY|XADFIF_XADSTRFILENAME;
                xadConvertDates(XAD_DATEMAC, EndGetM32(&sithdr[SITFH_MODDATE]), XAD_GETDATEXADDATE,
                &fi->xfi_Date, TAG_DONE);
                fi->xfi_PrivateInfo = (APTR) ldir;
                ldir = fi;
                err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos, TAG_DONE);
              }
              else
              {
                xadFreeObjectA(fi, 0);
                err = XADERR_NOMEMORY;
              }
            }
            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_OBJPRIVINFOSIZE, sizeof(struct SITPrivate),
              EndGetM32(&sithdr[SITFH_DATALENGTH]) || !csize ?
              TAG_IGNORE : XAD_OBJCOMMENTSIZE, csize, TAG_DONE)))
              {
                if((fi->xfi_FileName = MACname(xadMasterBase, ldir, sithdr+SITFH_FNAME, 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|XADFIF_XADSTRFILENAME;
                  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
                {
                  xadFreeObjectA(fi, 0);
                  err = XADERR_NOMEMORY;
                }
              }
              else
                err = XADERR_NOMEMORY;
            }

            if(!err && (EndGetM32(&sithdr[SITFH_DATALENGTH]) || !EndGetM32(&sithdr[SITFH_RSRCLENGTH])))
            {
              if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJPRIVINFOSIZE, sizeof(struct SITPrivate),
              csize ? XAD_OBJCOMMENTSIZE : TAG_IGNORE, csize, TAG_DONE)))
              {
                if((fi->xfi_FileName = MACname(xadMasterBase, ldir, sithdr+SITFH_FNAME, 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|XADFIF_XADSTRFILENAME;
                  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_COMPDMETHOD]&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
                {
                  xadFreeObjectA(fi, 0);
                  err = XADERR_NOMEMORY;
                }
              }
              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 = 0;
  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, stackend;

    stackp = stack = cd->stack;
    stackend = stack+UCOMPSTACKSIZE;
    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(stackp < stackend && code >= 256)
            {
              *stackp++ = cd->tab_suffixof[code];
              code = cd->tab_prefixof[code];
            }
            if(stackp >= stackend)
            {
              err = XADERR_ILLEGALDATA;
              break;
            }
            *(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;
          }
          if(!err)
            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;
}

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

static UWORD SIT_rndtable[] = {
 0xee,  0x56,  0xf8,  0xc3,  0x9d,  0x9f,  0xae,  0x2c,
 0xad,  0xcd,  0x24,  0x9d,  0xa6, 0x101,  0x18,  0xb9,
 0xa1,  0x82,  0x75,  0xe9,  0x9f,  0x55,  0x66,  0x6a,
 0x86,  0x71,  0xdc,  0x84,  0x56,  0x96,  0x56,  0xa1,
 0x84,  0x78,  0xb7,  0x32,  0x6a,   0x3,  0xe3,   0x2,
 0x11, 0x101,   0x8,  0x44,  0x83, 0x100,  0x43,  0xe3,
 0x1c,  0xf0,  0x86,  0x6a,  0x6b,   0xf,   0x3,  0x2d,
 0x86,  0x17,  0x7b,  0x10,  0xf6,  0x80,  0x78,  0x7a,
 0xa1,  0xe1,  0xef,  0x8c,  0xf6,  0x87,  0x4b,  0xa7,
 0xe2,  0x77,  0xfa,  0xb8,  0x81,  0xee,  0x77,  0xc0,
 0x9d,  0x29,  0x20,  0x27,  0x71,  0x12,  0xe0,  0x6b,
 0xd1,  0x7c,   0xa,  0x89,  0x7d,  0x87,  0xc4, 0x101,
 0xc1,  0x31,  0xaf,  0x38,   0x3,  0x68,  0x1b,  0x76,
 0x79,  0x3f,  0xdb,  0xc7,  0x1b,  0x36,  0x7b,  0xe2,
 0x63,  0x81,  0xee,   0xc,  0x63,  0x8b,  0x78,  0x38,
 0x97,  0x9b,  0xd7,  0x8f,  0xdd,  0xf2,  0xa3,  0x77,
 0x8c,  0xc3,  0x39,  0x20,  0xb3,  0x12,  0x11,   0xe,
 0x17,  0x42,  0x80,  0x2c,  0xc4,  0x92,  0x59,  0xc8,
 0xdb,  0x40,  0x76,  0x64,  0xb4,  0x55,  0x1a,  0x9e,
 0xfe,  0x5f,   0x6,  0x3c,  0x41,  0xef,  0xd4,  0xaa,
 0x98,  0x29,  0xcd,  0x1f,   0x2,  0xa8,  0x87,  0xd2,
 0xa0,  0x93,  0x98,  0xef,   0xc,  0x43,  0xed,  0x9d,
 0xc2,  0xeb,  0x81,  0xe9,  0x64,  0x23,  0x68,  0x1e,
 0x25,  0x57,  0xde,  0x9a,  0xcf,  0x7f,  0xe5,  0xba,
 0x41,  0xea,  0xea,  0x36,  0x1a,  0x28,  0x79,  0x20,
 0x5e,  0x18,  0x4e,  0x7c,  0x8e,  0x58,  0x7a,  0xef,
 0x91,   0x2,  0x93,  0xbb,  0x56,  0xa1,  0x49,  0x1b,
 0x79,  0x92,  0xf3,  0x58,  0x4f,  0x52,  0x9c,   0x2,
 0x77,  0xaf,  0x2a,  0x8f,  0x49,  0xd0,  0x99,  0x4d,
 0x98, 0x101,  0x60,  0x93, 0x100,  0x75,  0x31,  0xce,
 0x49,  0x20,  0x56,  0x57,  0xe2,  0xf5,  0x26,  0x2b,
 0x8a,  0xbf,  0xde,  0xd0,  0x83,  0x34,  0xf4,  0x17
};

struct SIT_modelsym
{
  UWORD sym;
  ULONG cumfreq;
};

struct SIT_model
{
  LONG                increment;
  LONG                maxfreq;
  LONG                entries;
  ULONG               tabloc[256];
  struct SIT_modelsym *syms;
};

struct SIT_ArsenicData
{
  struct xadInOut *io;

  UWORD  csumaccum;
  UBYTE *window;
  UBYTE *windowpos;
  UBYTE *windowe;
  LONG   windowsize;
  LONG   tsize;
  ULONG  One;
  ULONG  Half;
  ULONG  Range;
  ULONG  Code;
  LONG   lastarithbits; /* init 0 */

  /* SIT_dounmntf function private */
  LONG   inited;        /* init 0 */
  UBYTE  moveme[256];

  /* the private SIT_Arsenic function stuff */
  struct SIT_model initial_model;
  struct SIT_model selmodel;
  struct SIT_model mtfmodel[7];
  struct SIT_modelsym initial_syms[2+1];
  struct SIT_modelsym sel_syms[11+1];
  struct SIT_modelsym mtf0_syms[2+1];
  struct SIT_modelsym mtf1_syms[4+1];
  struct SIT_modelsym mtf2_syms[8+1];
  struct SIT_modelsym mtf3_syms[0x10+1];
  struct SIT_modelsym mtf4_syms[0x20+1];
  struct SIT_modelsym mtf5_syms[0x40+1];
  struct SIT_modelsym mtf6_syms[0x80+1];

  /* private for SIT_unblocksort */
  ULONG counts[256];
  ULONG cumcounts[256];
};

static void SIT_update_model(struct SIT_model *mymod, LONG symindex)
{
  LONG i;

  for (i = 0; i < symindex; i++)
    mymod->syms[i].cumfreq += mymod->increment;
  if(mymod->syms[0].cumfreq > mymod->maxfreq)
  {
    for(i = 0; i < mymod->entries ; i++)
    {
      /* no -1, want to include the 0 entry */
      /* this converts cumfreqs LONGo frequencies, then shifts right */
      mymod->syms[i].cumfreq -= mymod->syms[i+1].cumfreq;
      mymod->syms[i].cumfreq++; /* avoid losing things entirely */
      mymod->syms[i].cumfreq >>= 1;
    }
    /* then convert frequencies back to cumfreq */
    for(i = mymod->entries - 1; i >= 0; i--)
      mymod->syms[i].cumfreq += mymod->syms[i+1].cumfreq;
  }
}

static void SIT_getcode(struct SIT_ArsenicData *sa,
ULONG symhigh, ULONG symlow, ULONG symtot) /* aka remove symbol */
{
  ULONG lowincr;
  ULONG renorm_factor;

  renorm_factor = sa->Range/symtot;
  lowincr = renorm_factor * symlow;
  sa->Code -= lowincr;
  if(symhigh == symtot)
    sa->Range -= lowincr;
  else
    sa->Range = (symhigh - symlow) * renorm_factor;
  
  sa->lastarithbits = 0;
  while(sa->Range <= sa->Half)
  {
    sa->Range <<= 1;
    sa->Code = (sa->Code << 1) | xadIOGetBitsHigh(sa->io, 1);
    sa->lastarithbits++;
  }
}

static LONG SIT_getsym(struct SIT_ArsenicData *sa, struct SIT_model *model)
{
  LONG freq;
  LONG i;
  LONG sym;

  /* getfreq */
  freq = sa->Code/(sa->Range/model->syms[0].cumfreq);
  for(i = 1; i < model->entries; i++)
  {
    if(model->syms[i].cumfreq <= freq)
      break;
  }
  sym = model->syms[i-1].sym;
  SIT_getcode(sa, model->syms[i-1].cumfreq, model->syms[i].cumfreq, model->syms[0].cumfreq);
  SIT_update_model(model, i);
  return sym;
}

static void SIT_reinit_model(struct SIT_model *mymod)
{
  LONG cumfreq = mymod->entries * mymod->increment;
  LONG i;

  for(i = 0; i <= mymod->entries; i++)
  {
    /* <= sets last frequency to 0; there isn't really a symbol for that
       last one  */
    mymod->syms[i].cumfreq = cumfreq;
    cumfreq -= mymod->increment;
  }
}

static void SIT_init_model(struct SIT_model *newmod, struct SIT_modelsym *sym,
LONG entries, LONG start, LONG increment, LONG maxfreq)
{
  LONG i;

  newmod->syms = sym;
  newmod->increment = increment;
  newmod->maxfreq = maxfreq;
  newmod->entries = entries;
  /* memset(newmod->tabloc, 0, sizeof(newmod->tabloc)); */
  for(i = 0; i < entries; i++)
  {
    newmod->tabloc[(entries - i - 1) + start] = i;
    newmod->syms[i].sym = (entries - i - 1) + start;
  }
  SIT_reinit_model(newmod);
}

static ULONG SIT_arith_getbits(struct SIT_ArsenicData *sa, struct SIT_model *model, LONG nbits)
{
  /* the model is assumed to be a binary one */
  ULONG addme = 1;
  ULONG accum = 0;
  while(nbits--)
  {
    if(SIT_getsym(sa, model)) 
      accum += addme;
    addme += addme;
  }
  return accum;
}

static LONG SIT_dounmtf(struct SIT_ArsenicData *sa, LONG sym)
{
  LONG i;
  LONG result;

  if(sym == -1 || !sa->inited)
  {
    for(i = 0; i < 256; i++)
      sa->moveme[i] = i;
    sa->inited = 1;
  }
  if(sym == -1)
    return 0;
  result = sa->moveme[sym];
  for(i = sym; i > 0 ; i-- )
    sa->moveme[i] = sa->moveme[i-1];
  
  sa->moveme[0] = result;
  return result;
}

static LONG SIT_unblocksort(struct SIT_ArsenicData *sa, UBYTE *block,
ULONG blocklen, ULONG last_index, UBYTE *outblock)
{
  ULONG i, j;
  ULONG *xform;
  UBYTE *blockptr;
  ULONG cum;
  struct xadMasterBase *xadMasterBase = sa->io->xio_xadMasterBase;

  memset(sa->counts, 0, sizeof(sa->counts));
  if((xform = xadAllocVec(sizeof(ULONG)*blocklen, MEMF_ANY)))
  {
    blockptr = block;
    for(i = 0; i < blocklen; i++)
      sa->counts[*blockptr++]++;
  
    cum = 0;
    for(i = 0; i < 256; i++)
    {
      sa->cumcounts[i] = cum;
      cum += sa->counts[i];
      sa->counts[i] = 0;
    }

    blockptr = block;
    for(i = 0; i < blocklen; i++)
    {
      xform[sa->cumcounts[*blockptr] + sa->counts[*blockptr]] = i;
      sa->counts[*blockptr++]++;
    }

    blockptr = outblock;
    for(i = 0, j = xform[last_index]; i < blocklen; i++, j = xform[j])
    {
      *blockptr++ = block[j];
//      block[j] = 0xa5; /* for debugging */
    }
    xadFreeObjectA(xform, 0);
  }
  else
    return XADERR_NOMEMORY;
  return 0;
}

static void SIT_write_and_unrle_and_unrnd(struct xadInOut *io, UBYTE *block, ULONG blocklen, WORD rnd)
{
  LONG count = 0;
  LONG last = 0;
  UBYTE *blockptr = block;
  ULONG i;
  ULONG j;
  LONG ch;
  LONG rndindex;
  LONG rndcount;

  rndindex = 0;
  rndcount = SIT_rndtable[rndindex];
  for(i = 0; i < blocklen; i++)
  {
    ch = *blockptr++;
    if(rnd && (rndcount == 0))
    {
      ch ^= 1;
      rndindex++;
      if (rndindex == sizeof(SIT_rndtable)/sizeof(SIT_rndtable[0]))
	rndindex = 0;
      rndcount = SIT_rndtable[rndindex];
    }
    rndcount--;

    if(count == 4)
    {
      for(j = 0; j < ch; j++)
	xadIOPutChar(io, last);
      count = 0;
    }
    else
    {
      xadIOPutChar(io, ch);
      if(ch != last)
      {
	count = 0;
	last = ch;
      }
      count++;
    }
  }
}

static LONG SIT_Arsenic(struct xadInOut *io)
{
  LONG err = 0;
  struct SIT_ArsenicData *sa;
  struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;

  io->xio_Flags &= ~(XADIOF_NOCRC32);
  io->xio_Flags |= XADIOF_NOCRC16;
  io->xio_CRC32 = ~0;

  if((sa = (struct SIT_ArsenicData *) xadAllocVec(sizeof(struct SIT_ArsenicData), MEMF_ANY|MEMF_CLEAR)))
  {
    LONG i, sym, sel;
    WORD blockbits;
    ULONG w, blocksize;
    LONG stopme, nchars; /* 32 bits */
    LONG repeatstate, repeatcount;
    LONG primary_index; /* 32 bits */
    LONG eob, rnd;
    UBYTE *block, *blockptr, *unsortedblock;

    sa->io = io;
    sa->Range = sa->One = 1<<25;
    sa->Half = 1<<24;
    sa->Code = xadIOGetBitsHigh(io, 26);

    SIT_init_model(&sa->initial_model, sa->initial_syms, 2, 0, 1, 256);
    SIT_init_model(&sa->selmodel, sa->sel_syms, 11, 0, 8, 1024);
    /* selector model: 11 selections, starting at 0, 8 increment, 1024 maxfreq */

    SIT_init_model(&sa->mtfmodel[0], sa->mtf0_syms, 2, 2, 8, 1024);
    /* model 3: 2 symbols, starting at 2, 8 increment, 1024 maxfreq */
    SIT_init_model(&sa->mtfmodel[1], sa->mtf1_syms, 4, 4, 4, 1024);
    /* model 4: 4 symbols, starting at 4, 4 increment, 1024 maxfreq */
    SIT_init_model(&sa->mtfmodel[2], sa->mtf2_syms, 8, 8, 4, 1024);
    /* model 5: 8 symbols, starting at 8, 4 increment, 1024 maxfreq */
    SIT_init_model(&sa->mtfmodel[3], sa->mtf3_syms, 0x10, 0x10, 4, 1024);
    /* model 6: $10 symbols, starting at $10, 4 increment, 1024 maxfreq */
    SIT_init_model(&sa->mtfmodel[4], sa->mtf4_syms, 0x20, 0x20, 2, 1024);
    /* model 7: $20 symbols, starting at $20, 2 increment, 1024 maxfreq */
    SIT_init_model(&sa->mtfmodel[5], sa->mtf5_syms, 0x40, 0x40, 2, 1024);
    /* model 8: $40 symbols, starting at $40, 2 increment, 1024 maxfreq */
    SIT_init_model(&sa->mtfmodel[6], sa->mtf6_syms, 0x80, 0x80, 1, 1024);
    /* model 9: $80 symbols, starting at $80, 1 increment, 1024 maxfreq */
    if(SIT_arith_getbits(sa, &sa->initial_model, 8) != 0x41 ||
    SIT_arith_getbits(sa, &sa->initial_model, 8) != 0x73)
      err = XADERR_ILLEGALDATA;
    w = SIT_arith_getbits(sa, &sa->initial_model, 4);
    blockbits = w + 9;
    blocksize = 1<<blockbits;
    if(!err)
    {
      if((block = xadAllocVec(blocksize, MEMF_ANY)))
      {
        if((unsortedblock = xadAllocVec(blocksize, MEMF_ANY)))
        {
          eob = SIT_getsym(sa, &sa->initial_model);
          while(!eob && !err)
          {
            rnd = SIT_getsym(sa, &sa->initial_model);
            primary_index = SIT_arith_getbits(sa, &sa->initial_model, blockbits);
            nchars = stopme = repeatstate = repeatcount = 0;
            blockptr = block;
            while(!stopme)
            {
              sel = SIT_getsym(sa, &sa->selmodel);
              switch(sel)
              {
              case 0:
	        sym = -1;
                if(!repeatstate)
                  repeatstate = repeatcount = 1;
                else
                {
                  repeatstate += repeatstate;
                  repeatcount += repeatstate;
                }
                break;
              case 1:
                if(!repeatstate)
                {
                  repeatstate = 1;
                  repeatcount = 2;
                }
                else
                {
                  repeatstate += repeatstate;
                  repeatcount += repeatstate;
                  repeatcount += repeatstate;
                }
                sym = -1;
                break;
              case 2:
                sym = 1;
                break;
              case 10:
                stopme = 1;
                sym = 0;
                break;
              default: 
                if((sel > 9) || (sel < 3))
                { /* this basically can't happen */
                  err = XADERR_ILLEGALDATA;
                  stopme = 1;
                  sym = 0;
                }
                else
                  sym = SIT_getsym(sa, &sa->mtfmodel[sel-3]);
                break;
              }
              if(repeatstate && (sym >= 0))
              {
                nchars += repeatcount;
                repeatstate = 0;
                memset(blockptr, SIT_dounmtf(sa, 0), repeatcount);
                blockptr += repeatcount;
                repeatcount = 0;
              }
              if(!stopme && !repeatstate)
              {
                sym = SIT_dounmtf(sa, sym);
                *blockptr++ = sym;
                nchars++;
              }
              if(nchars > blocksize)
              {
                err = XADERR_ILLEGALDATA;
                stopme = 1;
              }
            }
            if(err)
              break;
            if((err = SIT_unblocksort(sa, block, nchars, primary_index, unsortedblock)))
              break;
            SIT_write_and_unrle_and_unrnd(io, unsortedblock, nchars, rnd);
            eob = SIT_getsym(sa, &sa->initial_model);
            SIT_reinit_model(&sa->selmodel);
            for(i = 0; i < 7; i ++)
              SIT_reinit_model(&sa->mtfmodel[i]);
            SIT_dounmtf(sa, -1);
          }
          if(!err)
          {
            err = xadIOWriteBuf(io);
            if(!err && SIT_arith_getbits(sa, &sa->initial_model, 32) != ~io->xio_CRC32)
              err = XADERR_CHECKSUM;
          }
          xadFreeObjectA(unsortedblock, 0);
        }
        else
          err = XADERR_NOMEMORY;
        xadFreeObjectA(block, 0);
      }
      else
        err = XADERR_NOMEMORY;
    } /* if(!err) */
    xadFreeObjectA(sa, 0);
  }
  else
    err = XADERR_NOMEMORY;

  return err;
}

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

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;
    case 15: err = SIT_Arsenic(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(SITPI(fi)->Method != 15)
    {
      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-\xFF\xFF\xFF\xFF Aladdin Systems, Inc., http://www.aladdinsys.com/StuffIt/\x0d\x0a";

    while(*a && (*a == *data || *a == 0xFF))
    {
      ++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 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)
            {
              ldir = (struct xadFileInfo *) ldir->xfi_PrivateInfo;
            }
            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, csize ?
                    XAD_OBJCOMMENTSIZE : TAG_IGNORE, csize + 1, TAG_DONE)))
                    {
                      if((fi->xfi_FileName = MACname(xadMasterBase, ldir, buffer+SIT5FH_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|XADFIF_XADSTRFILENAME;
                        fi->xfi_PrivateInfo = (APTR) ldir;
                        ldir = fi;

                        err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos, TAG_DONE);
                      }
                      else
                      {
                        xadFreeObjectA(fi, 0);
                        err = XADERR_NOMEMORY;
                      }
                    }
                    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_OBJPRIVINFOSIZE,
                      sizeof(struct SITPrivate), csize2 && !EndGetM32(buffer+SIT5FH_DATASIZE) ?
                      XAD_OBJCOMMENTSIZE : TAG_IGNORE, csize2, TAG_DONE)))
                      {
                        if((fi->xfi_FileName = MACname(xadMasterBase, ldir, buffer+SIT5FH_FILENAME+buffer[SIT5FH_PASSWORDSIZE], 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|XADFIF_XADSTRFILENAME;
                          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
                        {
                          xadFreeObjectA(fi, 0);
                          err = XADERR_NOMEMORY;
                        }
                      }
                      else
                        err = XADERR_NOMEMORY;
                    }

                    if(!err && (EndGetM32(buffer+SIT5FH_DATASIZE) || !buffer[i+1]))
                    {
                      if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJPRIVINFOSIZE,
                      sizeof(struct SITPrivate), csize2 ? XAD_OBJCOMMENTSIZE : TAG_IGNORE,
                      csize2, TAG_DONE)))
                      {
                        if((fi->xfi_FileName = MACname(xadMasterBase, ldir, buffer+SIT5FH_FILENAME+buffer[SIT5FH_PASSWORDSIZE], 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|XADFIF_XADSTRFILENAME;
                          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
                        {
                          xadFreeObjectA(fi, 0);
                          err = XADERR_NOMEMORY;
                        }
                      }
                      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] <= 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)
            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;
  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 */
        {
          ldir = (struct xadFileInfo *) ldir->xfi_PrivateInfo;
        }
        else if((fi = xadAllocObjectA(XADOBJ_FILEINFO, 0)))
        {
          if((fi->xfi_FileName = MACname(xadMasterBase, ldir, header+2, header[1], 0)))
          {
            fi->xfi_Flags |= XADFIF_DIRECTORY|XADFIF_XADSTRFILENAME;
            xadConvertDates(XAD_DATEMAC, EndGetM32(header+95), XAD_GETDATEXADDATE, &fi->xfi_Date, TAG_DONE);
            fi->xfi_PrivateInfo = (APTR) ldir;
            ldir = fi;
            err = xadAddFileEntry(fi, ai, XAD_SETINPOS, ai->xai_InPos, TAG_DONE);
          }
          else
          {
            xadFreeObjectA(fi, 0);
            err = XADERR_NOMEMORY;
          }
        }
        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, csize ? XAD_OBJCOMMENTSIZE
          : TAG_IGNORE, csize, TAG_DONE)))
          {
            if((fi->xfi_FileName = MACname(xadMasterBase, ldir, header+2, 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|XADFIF_XADSTRFILENAME;
              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
            {
              xadFreeObjectA(fi, 0);
              err = XADERR_NOMEMORY;
            }
          }
          else
            err = XADERR_NOMEMORY;
        }
        if(!err && rsize) /* resource fork */
        {
          if((fi = xadAllocObject(XADOBJ_FILEINFO, dsize || !csize ? TAG_IGNORE
          : XAD_OBJCOMMENTSIZE, csize, TAG_DONE)))
          {
            if((fi->xfi_FileName = MACname(xadMasterBase, ldir, header+2, 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|XADFIF_XADSTRFILENAME;
              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
            {
              xadFreeObjectA(fi, 0);
              err = XADERR_NOMEMORY;
            }
          }
          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);
}

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

ASM(BOOL) PackIt_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *data),
REG(a6, struct xadMasterBase *xadMasterBase))
{ CPUCHECK
  if(data[0] == 'P' && data[1] == 'M' && data[2] == 'a' &&
  (data[3] == 'g' || (data[3] >= '0' && data[3] <= '9')))
    return 1;

  return 0;
}

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

#define PIT_NLEN       0
#define PIT_NAME       1
#define PIT_TYPE      64
#define PIT_AUTH      68
#define PIT_FLAG      72
#define PIT_LOCK      74
#define PIT_DLEN      76
#define PIT_RLEN      80
#define PIT_CTIME     84
#define PIT_MTIME     88
#define PIT_HDRCRC    92
#define PIT_HDRBYTES  94

struct PITPrivate {
  ULONG SkipHead;
  ULONG SkipEnd;
  UBYTE Method;
};

#define PITPI(a)        ((struct PITPrivate *) ((a)->xfi_PrivateInfo))

/* This functions extracts the first header bytes form the data file
you selected */
static UBYTE PackIt_Put(struct xadInOut *io, UBYTE data)
{
  ULONG a;

  a = (ULONG) io->xio_PutFuncPrivate;
  if(!io->xio_OutSize && !a)
  {
    io->xio_Error = XADERR_DECRUNCH;
    io->xio_Flags |= XADIOF_ERROR;
  }
  else
  {
    if(io->xio_OutSize)
    {
      io->xio_OutBuffer[io->xio_OutBufferPos++] = data;
      if(!--io->xio_OutSize)
      {
        if(DoCRC(io->xio_OutBuffer, PIT_HDRBYTES-2)
        != EndGetM16(io->xio_OutBuffer+PIT_HDRCRC))
        {
          io->xio_Error = XADERR_CHECKSUM;
          io->xio_Flags |= XADIOF_ERROR;
        }
        else
        {
          a = EndGetM32(io->xio_OutBuffer+PIT_RLEN)
             +EndGetM32(io->xio_OutBuffer+PIT_DLEN)+2;
        }
      }
    }
    else
    {
      if(!--a)
        io->xio_Flags |= XADIOF_LASTOUTBYTE;
    }

    io->xio_PutFuncPrivate = (APTR) a;
  }

  return data;
}

ASM(LONG) PackIt_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct xadFileInfo *fi, *lfi;
  LONG err = 0;
  ULONG rsize, dsize, csize, crsize = 0, pos;
  STRPTR entryinfo;
  UBYTE header[PIT_HDRBYTES], type[4];

  while(!err && ai->xai_InPos + 4 <= ai->xai_InSize)
  {
    if(!(err = xadHookAccess(XADAC_READ, 4, type, ai)))
    {
      entryinfo = header; /* to silent compiler warnings */
      pos = ai->xai_InPos;
      if(type[0] == 'P' && type[1] == 'E' && type[2] == 'n' && type[3] == 'd')
        break;
      else if(type[0] != 'P' || type[1] != 'M' || type[2] != 'a')
        err = XADERR_DATAFORMAT;
      else if(type[3] == '4')
      {
        struct xadInOut *io;

        if((io = xadIOAlloc(XADIOF_ALLOCINBUFFER|XADIOF_NOCRC32|XADIOF_NOCRC16, ai, xadMasterBase)))
        {
          io->xio_PutFunc = PackIt_Put;
          io->xio_InSize = ai->xai_InSize-ai->xai_InPos;
          io->xio_OutSize = PIT_HDRBYTES;
          io->xio_OutBuffer = header;
          entryinfo = "Huffmann";

          err = SIT_huffman(io);
          crsize = (ai->xai_InSize-io->xio_InSize)-pos;
          xadFreeObjectA(io, 0);
        }
        else
          err = XADERR_NOMEMORY;
      }
      else if(type[3] == 'g')
      {
        if(!(err = xadHookAccess(XADAC_READ, PIT_HDRBYTES, header, ai)))
        {
          if(DoCRC(header, PIT_HDRBYTES-2) != EndGetM16(header+PIT_HDRCRC))
            err = XADERR_CHECKSUM;
          else
          {
            entryinfo = "NoComp";
            crsize = PIT_HDRBYTES+EndGetM32(header+PIT_RLEN)+EndGetM32(header+PIT_DLEN)+2;
          }
        }
      }
      else
        err = XADERR_DATAFORMAT;

      if(!err)
      {
        csize = SITmakecomment(header+PIT_TYPE, EndGetM16(header+PIT_FLAG), 0, 0, 0);
        rsize = EndGetM32(header+PIT_RLEN);
        dsize = EndGetM32(header+PIT_DLEN);
        lfi = 0;

        if(dsize) /* data fork */
        {
          if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJPRIVINFOSIZE,
          sizeof(struct PITPrivate), csize ? XAD_OBJCOMMENTSIZE : TAG_IGNORE,
          csize, TAG_DONE)))
          {
            if((fi->xfi_FileName = MACname(xadMasterBase, 0, header+PIT_NAME, header[PIT_NLEN], 0)))
            {
              /* if comment field is zero, nothing is done! */
              SITmakecomment(header+PIT_TYPE, EndGetM16(header+PIT_FLAG), 0, 0, fi->xfi_Comment);

              fi->xfi_Size = dsize;
              fi->xfi_CrunchSize = crsize;
              if(rsize)
              {
                fi->xfi_Flags |= XADFIF_GROUPED;
                fi->xfi_GroupCrSize = crsize;
              }
              lfi = fi;

              fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACDATA|XADFIF_EXTRACTONBUILD|XADFIF_XADSTRFILENAME;
              fi->xfi_DataPos = pos;

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

              PITPI(fi)->Method   = type[3];
              PITPI(fi)->SkipHead = PIT_HDRBYTES;
              PITPI(fi)->SkipEnd = rsize+2;
              fi->xfi_EntryInfo = entryinfo;

              err = xadAddFileEntry(fi, ai, XAD_SETINPOS, pos+crsize, TAG_DONE);
            }
            else
            {
              xadFreeObjectA(fi, 0);
              err = XADERR_NOMEMORY;
            }
          }
          else
            err = XADERR_NOMEMORY;
        }
        if(!err && rsize) /* resource fork */
        {
          if((fi = xadAllocObject(XADOBJ_FILEINFO, XAD_OBJPRIVINFOSIZE, sizeof(struct PITPrivate),
          dsize || !csize ? TAG_IGNORE : XAD_OBJCOMMENTSIZE, csize, TAG_DONE)))
          {
            if((fi->xfi_FileName = MACname(xadMasterBase, 0, header+PIT_NAME, header[PIT_NLEN], 1)))
            {
              /* if comment field is zero, nothing is done! */
              SITmakecomment(header+PIT_TYPE, EndGetM16(header+PIT_FLAG), 0, 0, fi->xfi_Comment);

              fi->xfi_Size = rsize;
              fi->xfi_CrunchSize = crsize;
              fi->xfi_Flags |= XADFIF_SEEKDATAPOS|XADFIF_MACRESOURCE|XADFIF_EXTRACTONBUILD|XADFIF_XADSTRFILENAME;
              fi->xfi_DataPos = pos;

              PITPI(fi)->Method   = type[3];
              PITPI(fi)->SkipHead = PIT_HDRBYTES+dsize;
              PITPI(fi)->SkipEnd = 2;
              fi->xfi_EntryInfo = entryinfo;

              if((fi->xfi_MacFork = lfi))
              {
                lfi->xfi_MacFork = fi;
                fi->xfi_GroupCrSize = crsize;
                fi->xfi_Flags |= XADFIF_ENDOFGROUP|XADFIF_GROUPED;
              }

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

              err = xadAddFileEntry(fi, ai, XAD_SETINPOS, pos+crsize, TAG_DONE);
            }
            else
            {
              xadFreeObjectA(fi, 0);
              err = XADERR_NOMEMORY;
            }
          }
          else
            err = XADERR_NOMEMORY;
        }
      }
    }
  }

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

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

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

struct PackItOutPrivate {
  ULONG Header;
  ULONG SkipStart;
  ULONG Data;
  ULONG SkipEnd;
  ULONG CRC;
  UWORD CRCValue;
};

static void PackIt_Out(struct xadInOut *io, ULONG size)
{
  struct PackItOutPrivate *p;
  ULONG bufpos = 0, cursize, i;
  UWORD crc;

  p = (struct PackItOutPrivate *) io->xio_OutFuncPrivate;
  if(p->Header)
  {
    if((cursize = p->Header) > size)
      cursize = size;
    size -= cursize;
    bufpos += cursize;
    p->Header -= cursize;
  }
  if(size)
  {
    if((cursize = p->SkipStart+p->Data+p->SkipEnd) > size)
      cursize = size;

    crc = io->xio_CRC16;
    for(i = 0; i < cursize; ++i)
      crc = crctable[((crc>>8) ^ io->xio_OutBuffer[bufpos+i]) & 0xFF] ^ (crc<<8);
    io->xio_CRC16 = crc;

    if(p->SkipStart)
    {
      if((cursize = p->SkipStart) > size)
        cursize = size;
      size -= cursize;
      bufpos += cursize;
      p->SkipStart -= cursize;
    }
    if(size)
    {
      if(p->Data)
      {
        struct xadMasterBase *xadMasterBase = io->xio_xadMasterBase;
        if((cursize = p->Data) > size)
          cursize = size;
        if((io->xio_Error = xadHookAccess(XADAC_WRITE, cursize, io->xio_OutBuffer
        + bufpos, io->xio_ArchiveInfo)))
          io->xio_Flags |= XADIOF_ERROR;
        size -= cursize;
        bufpos += cursize;
        p->Data -= cursize;
      }
      if(p->SkipEnd && size)
      {
        if((cursize = p->SkipEnd) > size)
          cursize = size;
        size -= cursize;
        bufpos += cursize;
        p->SkipEnd -= cursize;
      }
    }
  }
  /* if there is size left, this must be CRC */
  while(size && p->CRC)
  {
    p->CRCValue = (p->CRCValue<<8) | io->xio_OutBuffer[bufpos++];
    --p->CRC;
    --size;
  }
  if(!p->CRC && io->xio_CRC16 != p->CRCValue && !io->xio_Error)
  {
    io->xio_Error = XADERR_CHECKSUM;
    io->xio_Flags |= XADIOF_ERROR;
  }
}

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

  fi = ai->xai_CurFile;

  if((io = xadIOAlloc(XADIOF_ALLOCINBUFFER|XADIOF_ALLOCOUTBUFFER|
  XADIOF_COMPLETEOUTFUNC|XADIOF_NOCRC32|XADIOF_NOCRC16, ai, xadMasterBase)))
  {
    p.Header = PIT_HDRBYTES;
    p.SkipStart = PITPI(fi)->SkipHead-p.Header;
    p.Data = fi->xfi_Size;
    p.SkipEnd = PITPI(fi)->SkipEnd-2;
    p.CRC = 2;
    p.CRCValue = 0;

    io->xio_OutFunc = PackIt_Out;
    io->xio_OutFuncPrivate = &p;
    io->xio_InSize = fi->xfi_CrunchSize;
    io->xio_OutSize = p.Header+p.SkipStart+p.Data+p.SkipEnd+p.CRC;

    switch(PITPI(fi)->Method)
    {
    case 'g':
      while(!(io->xio_Flags & (XADIOF_LASTOUTBYTE|XADIOF_ERROR)))
        xadIOPutChar(io, xadIOGetChar(io));
      err = io->xio_Error;
      break;
    case '4':
      err = SIT_huffman(io);
      break;
    default:
      err = XADERR_DATAFORMAT;
    }
    if(!err)
      err = xadIOWriteBuf(io);
    xadFreeObjectA(io, 0);
  }
  else
    err = XADERR_NOMEMORY;

  return err;
}

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

const struct xadClient PackIt_Client = {
NEXTCLIENT, XADCLIENT_VERSION, XADMASTERVERSION, PACKIT_VERSION, PACKIT_REVISION,
4, XADCF_FILEARCHIVER|XADCF_FREEFILEINFO|XADCF_FREEXADSTRINGS, XADCID_PACKIT, "PackIt",
(BOOL (*)()) PackIt_RecogData, (LONG (*)()) PackIt_GetInfo,
(LONG (*)()) PackIt_UnArchive, 0};

const struct xadClient MacBinary_Client = {
&PackIt_Client, XADCLIENT_VERSION, XADMASTERVERSION, MACBINARY_VERSION, MACBINARY_REVISION,
128, XADCF_FILEARCHIVER|XADCF_FREEFILEINFO|XADCF_FREEXADSTRINGS, 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|XADCF_FREEXADSTRINGS, 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|XADCF_FREEXADSTRINGS, 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|XADCF_FREEXADSTRINGS, XADCID_SIT, "StuffIt",
(BOOL (*)()) SIT_RecogData, (LONG (*)()) SIT_GetInfo,
(LONG (*)()) SIT_UnArchive, 0};

#endif /* XADMASTER_STUFFIT_C */
