#ifndef XADMASTER_DMS_C
#define XADMASTER_DMS_C

/* Programmheader

	Name:		DMS.c
	Main:		xadmaster
	Versionstring:	$VER: DMS.c 1.4 (04.03.1999)
	Author:		SDI, Andre R. de la Rocha
	Distribution:	Freeware
	Description:	DMS and related disk/file archiver clients

 1.0   23.12.98 : first version
 1.1   31.01.99 : added unpack stuff
 1.2   04.02.99 : bug fixes, first full working version
 1.3   16.02.99 : bug fixes
 1.4   04.03.99 : added XADFIF_NODATE flag
*/

/* This format seems to have really lots of problems. Append option
does not work correctly, because the crunching algorithm start data
seems to be handled wrong. ENCRYPTION over APPEND borders doe not
work correctly as well. DIZ text sometimes are encrypted, sometimes
are not. Generally all is possible and all may fail. Hopefully there
are not too much archives out there doing such a strange defective
stuff. */

/* This client is lossely based (mainly decrunch stuff) on xDMS
source made by Andre R. de la Rocha. Thanks for your work. The code
has been made reentrant to fit requirements of shared libraries and
also lots of strange files are handled correctly now. Crunch Type
reinitialisation has been fixed as well. This client does not use
DMS header information (except password flag), but always the track
data. This should reduce influence of modified DMS files.

DIZ texts seem to use easy compression (NONE or RLE) always, so they
can be decompressed without decompressiong all the data before.
APPENDED files which use a password, where the normal file does
not use one, will fail. The other way round should work.
*/

/* FMS format:
- pre 2.04 FMS files contain file name in block DMSTRTYPE_FILENAME
- 2.04 format contains additionally information:
  LONGWORD	protection bits
  3 LONGWORDs	DateStamp structure
  UBYTE		size of following text, if bit 7 is set, the text
		is a comment
[ UBYTE		size of filename, when there was a comment]
*/

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

#ifndef XADMASTERFILE
#define DMS_Client		FirstClient
#define NEXTCLIENT		0
UBYTE version[] = "$VER: DMS 1.4 (04.03.1999)";
#endif
#define DMS_VERSION		1
#define DMS_REVISION		4
#define DMSSFX_VERSION		DMS_VERSION
#define DMSSFX_REVISION		DMS_REVISION
#define SDSSFX_VERSION		DMS_VERSION
#define SDSSFX_REVISION		DMS_REVISION

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

#define DMSINFO_NOZERO		(1<<0)
#define DMSINFO_ENCRYPT		(1<<1)
#define DMSINFO_APPENDS		(1<<2)
#define DMSINFO_BANNER		(1<<3)
#define DMSINFO_HIGHDENSITY	(1<<4)
#define DMSINFO_PC		(1<<5)
#define DMSINFO_DMS_DEVICE_FIX	(1<<6)
#define DMSINFO_REGIST_VERSION	(1<<7)
#define DMSINFO_DIZ		(1<<8)
/* InfoFlags seem to be valid for first part. Appended part may have
   other flags! */

#define DMSOSV_AMIGA_AGA	(1<<15)

#define DMSCPU_68000		0
#define DMSCPU_68010		1
#define DMSCPU_68020		2
#define DMSCPU_68030		3
#define DMSCPU_68040		4
#define DMSCPU_68060		5
#define DMSCPU_6086		6
#define DMSCPU_8088		7
#define DMSCPU_80188		8
#define DMSCPU_80186		9
#define DMSCPU_80286		10
#define DMSCPU_80386SX		11
#define DMSCPU_80386		12
#define DMSCPU_80486		13
#define DMSCPU_80586		14

#define DMSFPU_NONE		0
#define DMSFPU_68881		1
#define DMSFPU_68882		2
#define DMSFPU_8087		3
#define DMSFPU_80287SX		4
#define DMSFPU_80387		5

#define DMSMACH_UNKNOWN		0
#define DMSMACH_AMIGA		1
#define DMSMACH_PC		2
#define DMSMACH_ATARI		3
#define DMSMACH_MACINTOSH	4

#define DMSTYPE_UNKNOWN		0
#define DMSTYPE_AOS1OFS		1
#define DMSTYPE_AOS1FFS		2
#define DMSTYPE_AOS3INT		3
#define DMSTYPE_AOS3INTFFS	4
#define DMSTYPE_AOS3DIR		5
#define DMSTYPE_AOS3DIRFFS	6
#define DMSTYPE_FMS		7

#define DMSCOMP_NOCOMP		0
#define DMSCOMP_SIMPLE		1
#define DMSCOMP_QUICK		2
#define DMSCOMP_MEDIUM		3
#define DMSCOMP_DEEP		4
#define DMSCOMP_HEAVY1		5
#define DMSCOMP_HEAVY2		6

#define DMSCFLAG_NOINIT		(1<<0)
#define DMSCFLAG_HEAVY_C	(1<<1)
#define DMSCFLAG_HEAVYRLE	(1<<2)
#define DMSCFLAG_HEAVY2		(1<<3)	/* own flag */

#define DMSTRTYPE_DIZ		80
#define DMSTRTYPE_BANNER	-1
#define DMSTRTYPE_FILENAME	0x03E7
#define DMSTRTYPE_FILESTART	0x03E8

#define DMSPWD_USE	1
#define DMSPWD_NOUSE	2

struct DMSHeader {
  ULONG		DMSID;		/* "DMS!" */
  ULONG		ID;		/* " PRO" or "FILE" */
  ULONG		InfoFlags;	/* DMSINFO_ defines */
  ULONG		Date;		/* UNIX format */
  UWORD		LowTrack;
  UWORD		HighTrack;
  ULONG		PackedSize;
  ULONG		UnpackedSize;
  UWORD		OSVersion;	/* DMSOSV_ defines */
  UWORD		OSRevision;
  UWORD		CPU;		/* DMSCPU_ defines */
  UWORD		FPU;		/* DMSFPU_ defines */
  UWORD		Machine;	/* DMSMACH_ defines */
  UWORD		DiskType2;	/* DMSTYPE_ defines */
  UWORD		CPUSpeed;	/* multiplied (in 0.01 MHz) */
  ULONG		CreationTime;
  UWORD		CreatorVersion;
  UWORD		NeededVersion;
  UWORD		DiskType;	/* DMSTYPE_ defines */
  UWORD		CompMode;	/* DMSCOMP_ defines */
  UWORD		CheckSum;
  /* checksums exclude DMSID and checksum word itself */
};

struct DMSTrack {
  UWORD		TRID;		/* "TR" */
  WORD		TrackNumber;
  UWORD		pad;
  UWORD		CMODE_Packed;
  UWORD		RuntimePacked;
  UWORD		UnpackedSize;
  UBYTE		CFlag;
  UBYTE		CModeTrk;	/* DMSCOMP_ defines */
  UWORD		UncrunchedCRC;
  UWORD		CrunchedCRC;
  UWORD		CheckSum;
};

#define DMSNC 510
#define DMSNPT 30
#define DMSN1 510
#define DMSOFFSET 253

#define DMSF		60		/* lookahead buffer size */
#define DMSTHRESHOLD	2
#define DMSN_BYTE	(256 - DMSTHRESHOLD + DMSF)	/* kinds of characters (character code = 0..DMSN_BYTE-1) */
#define DMST		(DMSN_BYTE * 2 - 1)		/* size of table */
#define DMSR		(DMST - 1)      /* position of root */
#define DMSMAX_FREQ	0x8000      	/* DMSupdates tree when the */

struct DMSData {
  ULONG		bitbuf;
  STRPTR	indata;
  UBYTE		bitcount;
  UBYTE		DidInit;
  UWORD		RTV_Medium;
  UBYTE		Text[32768];
  /* the above are accessed by Medium decruncher and supplied as a short
    data structure from SDS SFX */

  UWORD		UsePwd;
  UWORD		PassCRC;
  UWORD		RTV_Pass; /* RunTimeVariable */
  UWORD		RTV_Quick;
  UWORD		RTV_Deep;
  UWORD		RTV_Heavy;
  UWORD		c_table[4096];
  UWORD		pt_table[256];
  UWORD		lastlen;
  UWORD		np;
  UWORD		left[2*DMSNC - 1];
  UWORD		right[2*DMSNC -1 + 9];
  UWORD		freq[DMST + 1];		/* d->frequency table */
  UWORD		prnt[DMST + DMSN_BYTE]; /* pointers to parent nodes, except for the */
		/* elements [DMST..DMST + DMSN_BYTE - 1] which are used to get */
		/* the positions of leaves corresponding to the codes. */
  UWORD		son[DMST];	/* pointers to child nodes (son[], son[] + 1) */

  UBYTE		DidInitDEEP;
  UBYTE		c_len[DMSNC];
  UBYTE		pt_len[DMSNPT];
};

struct DMSPos {
  ULONG startpos;	/* decrunch start position */
  ULONG firsttrack;	/* first decrunch track */
};

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

static ULONG DMS_mask_bits[]={
	0x000000,0x000001,0x000003,0x000007,0x00000f,0x00001f,
	0x00003f,0x00007f,0x0000ff,0x0001ff,0x0003ff,0x0007ff,
	0x000fff,0x001fff,0x003fff,0x007fff,0x00ffff,0x01ffff,
	0x03ffff,0x07ffff,0x0fffff,0x1fffff,0x3fffff,0x7fffff,
	0xffffff};

static UBYTE DMS_d_code[256] = {
    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,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
    0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
    0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
    0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
    0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
    0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
    0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
    0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
    0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
    0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
    0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
    0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
    0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
    0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
    0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
};

static UBYTE DMS_d_len[256] = {
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};

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

static LONG DMSUnpRLE(STRPTR *res, ULONG size, struct ExecBase *SysBase)
{
  STRPTR out, outstore, outend, in = *res;
  UWORD n;
  UBYTE a,b;
  LONG err = 0;

  if(!(out = outstore = (STRPTR) AllocVec(size+1, MEMF_PUBLIC|MEMF_CLEAR)))
    return XADERR_NOMEMORY;
  
  outend = out+size;

  while(out < outend && !err)
  {
    if((a = *(in++)) != 0x90)
      *(out++) = a;
    else if(!(b = *(in++)))
      *(out++) = a;
    else
    {
      a = *(in++);
      if(b == 0xFF)
      {
        n = *(in++);
        n = ((n<<8) + *(in++));
      }
      else
        n = b;
      if(out+n > outend)
        err = XADERR_ILLEGALDATA;
      else
      {
        memset(out,a,n);
        out += n;
      }
    }
  }

  FreeVec(*res);
  *res = outstore;
  return err;
}

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

#define DMSGETBITS(n) ((UWORD)(d->bitbuf >> (d->bitcount-(n))))
#define DMSDROPBITS(n) {d->bitbuf &= DMS_mask_bits[d->bitcount-=(n)]; \
	while (d->bitcount<16) {d->bitbuf = (d->bitbuf << 8) | \
	*d->indata++;  d->bitcount += 8;}}

static void DMSinitbitbuf(STRPTR in, struct DMSData *d)
{
  d->bitbuf = 0;
  d->bitcount = 0;
  d->indata = in;
  DMSDROPBITS(0);
}

static LONG DMSUnpQUICK(STRPTR *res, ULONG size, struct DMSData *d, struct ExecBase *SysBase)
{
  STRPTR out, outstore, outend, in = *res;
  UWORD i,j;
  LONG err = 0;

  if(!(out = outstore = (STRPTR) AllocVec(size+1, MEMF_PUBLIC|MEMF_CLEAR)))
    return XADERR_NOMEMORY;

  d->DidInit = 0;	/* clear Init flag */

  DMSinitbitbuf(in, d);
  outend = out + size;
  while (out < outend)
  {
    if(DMSGETBITS(1))
    {
      DMSDROPBITS(1);
      *out++ = d->Text[d->RTV_Quick++ & 0xFF] = (UBYTE) DMSGETBITS(8);
      DMSDROPBITS(8);
    }
    else
    {
      DMSDROPBITS(1);
      j = DMSGETBITS(2)+2;
      DMSDROPBITS(2);
      i = d->RTV_Quick - DMSGETBITS(8) - 1;
      DMSDROPBITS(8);
      if(j + out > outend)
        err = XADERR_ILLEGALDATA;
      else
        while(j--)
          *out++ = d->Text[d->RTV_Quick++ & 0xFF] = d->Text[i++ & 0xFF];
    }
  }
  d->RTV_Quick = (d->RTV_Quick+5) & 0xFF;

  FreeVec(*res);
  *res = outstore;
  return err;
}

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

static LONG DMSUnpMEDIUM(STRPTR *res, ULONG size, struct DMSData *d, struct ExecBase *SysBase)
{
  STRPTR out, outstore, outend, in = *res;
  UWORD i,j, c;
  LONG err = 0;
  UBYTE u;

  if(!(out = outstore = (STRPTR) AllocVec(size+1, MEMF_PUBLIC|MEMF_CLEAR)))
    return XADERR_NOMEMORY;

  d->DidInit = 0;	/* clear Init flag */

  DMSinitbitbuf(in, d);
  outend = out + size;

  while(out < outend)
  {
    if(DMSGETBITS(1))
    {
      DMSDROPBITS(1);
      *out++ = d->Text[d->RTV_Medium++ & 0x3FFF] = (UBYTE)DMSGETBITS(8);
      DMSDROPBITS(8);
    }
    else
    {
      DMSDROPBITS(1);
      c = DMSGETBITS(8);
      DMSDROPBITS(8);
      j = (UWORD) (DMS_d_code[c]+3);
      u = DMS_d_len[c];
      c = (UWORD) (((c << u) | DMSGETBITS(u)) & 0xff);
      DMSDROPBITS(u);
      u = DMS_d_len[c];
      c = (UWORD) ((DMS_d_code[c] << 8) | (((c << u) | DMSGETBITS(u)) & 0xff));
      DMSDROPBITS(u);
      i = (UWORD) (d->RTV_Medium - c - 1);

      if(j + out > outend)
        err = XADERR_ILLEGALDATA;
      else
        while(j--)
          *out++ = d->Text[d->RTV_Medium++ & 0x3FFF] = d->Text[i++ & 0x3FFF];		
    }
  }
  d->RTV_Medium = (UWORD)((d->RTV_Medium+66) & 0x3FFF);

  FreeVec(*res);
  *res = outstore;
  return err;
}

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

/* reconstruction of tree */
static void DMSreconst(struct DMSData *d)
{
  UWORD i, j, k, f, l;

  /* collect leaf nodes in the first half of the table */
  /* and replace the d->freq by (d->freq + 1) / 2. */
  j = 0;
  for (i = 0; i < DMST; i++)
  {
    if(d->son[i] >= DMST)
    {
      d->freq[j] = (UWORD) ((d->freq[i] + 1) / 2);
      d->son[j] = d->son[i];
      j++;
    }
  }
  /* begin constructing tree by connecting d->sons */
  for(i = 0, j = DMSN_BYTE; j < DMST; i += 2, j++)
  {
    k = (UWORD) (i + 1);
    f = d->freq[j] = (UWORD) (d->freq[i] + d->freq[k]);
    for(k = (UWORD)(j - 1); f < d->freq[k]; k--)
      ;
    k++;
    for(l = j; l > k; --l)
    {
      d->freq[l] = d->freq[l-1];
      d->son[l] = d->son[l-1];
    }
    d->freq[k] = f;
    d->son[k] = i;
  }
  /* connect d->prnt */
  for(i = 0; i < DMST; i++)
  {
    if((k = d->son[i]) >= DMST)
      d->prnt[k] = i;
    else
      d->prnt[k] = d->prnt[k + 1] = i;
  }
}

/* increment d->frequency of given code by one, and update tree */
static void DMSupdate(UWORD c, struct DMSData *d)
{
  UWORD i, j, k, l;

  if(d->freq[DMSR] == DMSMAX_FREQ)
    DMSreconst(d);
  c = d->prnt[c + DMST];
  do
  {
    k = ++d->freq[c];

    /* if the order is disturbed, exchange nodes */
    if(k > d->freq[l = (UWORD)(c + 1)])
    {
      while(k > d->freq[++l])
        ;
      l--;
      d->freq[c] = d->freq[l];
      d->freq[l] = k;

      i = d->son[c];
      d->prnt[i] = l;
      if(i < DMST)
        d->prnt[i + 1] = l;

      j = d->son[l];
      d->son[l] = i;

      d->prnt[j] = c;
      if(j < DMST)
        d->prnt[j + 1] = c;
      d->son[c] = j;

      c = l;
    }
  } while((c = d->prnt[c]) != 0); /* repeat up to root */
}

static UWORD DMSDecodeChar(struct DMSData *d)
{
  UWORD c;

  c = d->son[DMSR];

  /* travel from root to leaf, */
  /* choosing the smaller child node (d->son[]) if the read bit is 0, */
  /* the bigger (d->son[]+1) if 1 */
  while(c < DMST)
  {
    c = d->son[c + DMSGETBITS(1)];
    DMSDROPBITS(1);
  }
  c -= DMST;
  DMSupdate(c, d);
  return c;
}

static UWORD DMSDecodePosition(struct DMSData *d)
{
  UWORD i, j, c;

  i = DMSGETBITS(8);
  DMSDROPBITS(8);
  c = (UWORD) (DMS_d_code[i] << 8);
  j = DMS_d_len[i];
  i = (UWORD) (((i << j) | DMSGETBITS(j)) & 0xff);
  DMSDROPBITS(j);

  return (UWORD) (c | i);
}

static LONG DMSUnpDEEP(STRPTR *res, ULONG size, struct DMSData *d, struct ExecBase *SysBase)
{
  STRPTR out, outstore, outend, in = *res;
  UWORD i,j, c;
  LONG err = 0;

  if(!(out = outstore = (STRPTR) AllocVec(size+1, MEMF_PUBLIC|MEMF_CLEAR)))
    return XADERR_NOMEMORY;

  d->DidInitDEEP = d->DidInit = 0;	/* clear Init flag */

  DMSinitbitbuf(in, d);
  outend = out + size;

  while(out < outend)
  {
    c = DMSDecodeChar(d);
    if(c < 256)
      *out++ = d->Text[d->RTV_Deep++ & 0x3FFF] = (UBYTE)c;
    else
    {
      j = (UWORD) (c - 255 + DMSTHRESHOLD);
      i = (UWORD) (d->RTV_Deep - DMSDecodePosition(d) - 1);
      if(j + out > outend)
        err = XADERR_ILLEGALDATA;
      else
        while (j--)
          *out++ = d->Text[d->RTV_Deep++ & 0x3FFF] = d->Text[i++ & 0x3FFF];
    }
  }

  d->RTV_Deep = (UWORD)((d->RTV_Deep+60) & 0x3FFF);

  FreeVec(*res);
  *res = outstore;
  return err;
}

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

struct DMSTableData {
  WORD		c;
  UWORD		n;
  UWORD		tblsiz;
  UWORD		len;
  UWORD		depth;
  UWORD		maxdepth;
  UWORD		avail;
  UWORD		codeword;
  UWORD		bit;
  UWORD	*	tbl;
  UWORD 	TabErr;
  STRPTR	blen;

  UWORD	*	left;  /* copy of data in DMSData */
  UWORD	*	right; /* copy of data in DMSData */
};

static UWORD DMSmktbl(struct DMSTableData *t)
{
  UWORD i=0;

  if(t->TabErr)
    return 0;

  if(t->len == t->depth)
  {
    while(++t->c < t->n)
      if(t->blen[t->c] == t->len)
      {
        i = t->codeword;
        t->codeword += t->bit;
        if(t->codeword > t->tblsiz)
        {
          t->TabErr=1;
          return 0;
        }
        while(i < t->codeword)
          t->tbl[i++] = (UWORD) t->c;
        return (UWORD) t->c;
      }
      t->c = -1;
      t->len++;
      t->bit >>= 1;
  }
  t->depth++;
  if(t->depth < t->maxdepth)
  {
    (void) DMSmktbl(t);
    (void) DMSmktbl(t);
  }
  else if(t->depth > 32)
  {
    t->TabErr = 2;
    return 0;
  }
  else
  {
    if((i = t->avail++) >= 2 * t->n - 1)
    {
      t->TabErr = 3;
      return 0;
    }
    t->left[i] = DMSmktbl(t);
    t->right[i] = DMSmktbl(t);
    if(t->codeword >= t->tblsiz)
    {
      t->TabErr = 4;
      return 0;
    }
    if(t->depth == t->maxdepth)
      t->tbl[t->codeword++] = i;
  }
  t->depth--;
  return i;
}

static UWORD DMSmake_table(UWORD nchar, UBYTE bitlen[], UWORD tablebits, UWORD table[],
struct DMSData *d)
{
  struct DMSTableData t;

  t.left = d->left;
  t.right = d->right;

  t.n = t.avail = nchar;
  t.blen = bitlen;
  t.tbl = table;
  t.tblsiz = (UWORD) (1 << tablebits);
  t.bit = (UWORD) (t.tblsiz / 2);
  t.maxdepth = (UWORD)(tablebits + 1);
  t.depth = t.len = 1;
  t.c = -1;
  t.codeword = 0;
  t.TabErr = 0;
  (void) DMSmktbl(&t);	/* left subtree */
  (void) DMSmktbl(&t);	/* right subtree */
  if(t.TabErr)
    return t.TabErr;
  if(t.codeword != t.tblsiz)
    return 5;
  return 0;
}

static UWORD DMSdecode_c(struct DMSData *d)
{
  UWORD i, j, m;

  j = d->c_table[DMSGETBITS(12)];
  if(j < DMSN1)
  {
    DMSDROPBITS(d->c_len[j]);
  }
  else
  {
    DMSDROPBITS(12);
    i = DMSGETBITS(16);
    m = 0x8000;
    do
    {
      if(i & m)
        j = d->right[j];
      else
        j = d->left[j];
      m >>= 1;
    } while (j >= DMSN1);
    DMSDROPBITS(d->c_len[j] - 12);
  }
  return j;
}

static UWORD DMSdecode_p(struct DMSData *d)
{
  UWORD i, j, m;

  j = d->pt_table[DMSGETBITS(8)];
  if(j < d->np)
  {
    DMSDROPBITS(d->pt_len[j]);
  }
  else
  {
    DMSDROPBITS(8);
    i = DMSGETBITS(16);
    m = 0x8000;
    do
    {
      if(i & m)
        j = d->right[j];
      else
        j = d->left[j];
      m >>= 1;
    } while (j >= d->np);
    DMSDROPBITS(d->pt_len[j] - 8);
  }

  if(j != d->np-1)
  {
    if(j > 0)
    {
      j = (UWORD)(DMSGETBITS(i=(UWORD)(j-1)) | (1 << (j-1)));
      DMSDROPBITS(i);
    }
    d->lastlen=j;
  }

  return d->lastlen;
}

static UWORD DMSread_tree_c(struct DMSData *d)
{
  UWORD i,n;

  n = DMSGETBITS(9);
  DMSDROPBITS(9);
  if(n > 0)
  {
    for(i=0; i<n; i++)
    {
      d->c_len[i] = (UBYTE)DMSGETBITS(5);
      DMSDROPBITS(5);
    }
    for(i=n; i<510; i++)
      d->c_len[i] = 0;
    if(DMSmake_table(510,d->c_len,12,d->c_table, d))
      return 1;
  }
  else
  {
    n = DMSGETBITS(9);
    DMSDROPBITS(9);
    for(i=0; i<510; i++)
      d->c_len[i] = 0;
    for(i=0; i<4096; i++)
      d->c_table[i] = n;
  }
  return 0;
}

static UWORD DMSread_tree_p(struct DMSData *d)
{
  UWORD i,n;

  n = DMSGETBITS(5);
  DMSDROPBITS(5);
  if(n > 0)
  {
    for(i=0; i<n; i++)
    {
      d->pt_len[i] = (UBYTE) DMSGETBITS(4);
      DMSDROPBITS(4);
    }
    for(i=n; i<d->np; i++)
    d->pt_len[i] = 0;
    if(DMSmake_table(d->np,d->pt_len,8,d->pt_table, d))
      return 1;
  }
  else
  {
    n = DMSGETBITS(5);
    DMSDROPBITS(5);
    for(i=0; i<d->np; i++)
      d->pt_len[i] = 0;
    for (i=0; i<256; i++)
      d->pt_table[i] = n;
  }
  return 0;
}

static LONG DMSUnpHEAVY(STRPTR *res, ULONG size, UBYTE flags, struct DMSData *d, struct ExecBase *SysBase)
{
  STRPTR out, outstore, outend, in = *res;
  UWORD i,j, c, bitmask;
  LONG err = 0;

  if(!(out = outstore = (STRPTR) AllocVec(size+1, MEMF_PUBLIC|MEMF_CLEAR)))
    return XADERR_NOMEMORY;

  d->DidInit = 0;	/* clear Init flag */

  /*  Heavy 1 uses a 4Kb dictionary,  Heavy 2 uses 8Kb  */

  if(flags & DMSCFLAG_HEAVY2)
  {
    d->np = 15;
    bitmask = 0x1fff;
  }
  else
  {
    d->np = 14;
    bitmask = 0x0fff;
  }

  DMSinitbitbuf(in, d);
  outend = out + size;


  if(flags & DMSCFLAG_HEAVY_C)
  {
    if(DMSread_tree_c(d))
      return 1;
    if(DMSread_tree_p(d))
      return 2;
  }

  while(out < outend)
  {
    c = DMSdecode_c(d);
    if(c < 256)
      *out++ = d->Text[d->RTV_Heavy++ & bitmask] = (UBYTE)c;
    else
    {
      j = (UWORD) (c - DMSOFFSET);
      i = (UWORD) (d->RTV_Heavy - DMSdecode_p(d) - 1);
      if(j + out > outend)
        err = XADERR_ILLEGALDATA;
      else
        while(j--)
          *out++ = d->Text[d->RTV_Heavy++ & bitmask] = d->Text[i++ & bitmask];
    }
  }

  FreeVec(*res);
  *res = outstore;
  return err;

}

/****************************************************************************/
/*  track 80 is FILEID.DIZ, track 0xffff (-1) is Banner  */
/*  and track 0 with 1024 bytes only is a fake boot block with more advertising */

static LONG testDMSTrack(struct DMSTrack *t, struct xadMasterBase *xadMasterBase)
{
  if(t->TRID != 0x5452)
    return XADERR_ILLEGALDATA;
  if(xadCalcCRC16(XADCRC16_ID1, 0, sizeof(struct DMSTrack)-2,
  (STRPTR)t) != t->CheckSum)
    return XADERR_CHECKSUM;

  return 0;
}

static UWORD CheckSumDMS(STRPTR mem, ULONG size)
{
  UWORD u = 0;

  while(size--)
    u += *mem++;

  return u;
}

static void DecryptDMS(STRPTR p, ULONG size, struct DMSData *d)
{
  UWORD t;

  while(size--)
  {
    t = (UWORD) *p;
    *p++ ^= (UBYTE) d->RTV_Pass;
    d->RTV_Pass = (UWORD)((d->RTV_Pass >> 1) + t);
  }
}

static void DMSInitData(struct DMSData *d)
{
  d->RTV_Quick = 251;
  d->RTV_Medium = 0x3FBE;
  d->RTV_Deep = 0x3fc4;

  if(!d->DidInitDEEP)
  {
    UWORD i, j;
    for(i = 0; i < DMSN_BYTE; i++)
    {
      d->freq[i] = 1;
      d->son[i] = (UWORD)(i + DMST);
      d->prnt[i + DMST] = i;
    }
    i = 0;
    j = DMSN_BYTE;
    while(j <= DMSR)
    {
      d->freq[j] = (UWORD) (d->freq[i] + d->freq[i + 1]);
      d->son[j] = i;
      d->prnt[i] = d->prnt[i + 1] = j;
      i += 2;
      j++;
    }
    d->freq[DMST] = 0xffff;
    d->prnt[DMSR] = 0;
  }

  d->DidInit = d->DidInitDEEP = 1;

  memset(d->Text,0,0x3fc8);
}

/* always make buffer 1 byte larger, which is filled with 0! */
static LONG DecrunchDMS(struct DMSTrack *t, struct xadArchiveInfo *ai,
struct xadMasterBase *xadMasterBase, STRPTR *res, struct DMSData *d)
{
  APTR inbuf;
  LONG err;
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;

  *res = 0;

  if(!(inbuf = AllocVec(t->CMODE_Packed+1, MEMF_PUBLIC|MEMF_CLEAR)))
    return XADERR_NOMEMORY;

  if((err = xadHookAccess(XADAC_READ, t->CMODE_Packed, inbuf, ai)))
  {
    FreeVec(inbuf);
    return err;
  }

  *res = inbuf;

  if(d->UsePwd == DMSPWD_USE && d->PassCRC)
    DecryptDMS(inbuf, t->CMODE_Packed, d);

  switch(t->CModeTrk)
  {
  case DMSCOMP_NOCOMP: break;
  case DMSCOMP_SIMPLE: err = DMSUnpRLE(res, t->UnpackedSize, SysBase); break;
  case DMSCOMP_QUICK:
    if(!(err = DMSUnpQUICK(res, t->RuntimePacked, d, SysBase)))
      err = DMSUnpRLE(res, t->UnpackedSize, SysBase);
    break;
  case DMSCOMP_MEDIUM:
    if(!(err = DMSUnpMEDIUM(res, t->RuntimePacked, d, SysBase)))
      err = DMSUnpRLE(res, t->UnpackedSize, SysBase);
    break;
  case DMSCOMP_DEEP:
    if(!(err = DMSUnpDEEP(res, t->RuntimePacked, d, SysBase)))
      err = DMSUnpRLE(res, t->UnpackedSize, SysBase);
    break;
  case DMSCOMP_HEAVY1: case DMSCOMP_HEAVY2:
    if(t->CModeTrk == DMSCOMP_HEAVY1)
      err = DMSUnpHEAVY(res, t->RuntimePacked, t->CFlag & ~DMSCFLAG_HEAVY2, d, SysBase);
    else
      err = DMSUnpHEAVY(res, t->RuntimePacked, t->CFlag | DMSCFLAG_HEAVY2, d, SysBase);
    if(t->CFlag & DMSCFLAG_HEAVYRLE && !err)
      err = DMSUnpRLE(res, t->UnpackedSize, SysBase);
    break;
  default: err = XADERR_DECRUNCH; break;
  }

  if(!err && CheckSumDMS(*res, t->UnpackedSize) != t->UncrunchedCRC)
  {
    /* try again without password */
    if(d->UsePwd == DMSPWD_USE && d->PassCRC && !(err = xadHookAccess(XADAC_INPUTSEEK,
    -t->CMODE_Packed, 0, ai)))
    {
      FreeVec(*res);
      d->UsePwd = DMSPWD_NOUSE;
      return DecrunchDMS(t, ai, xadMasterBase, res, d);
    }
    if(!err)
      err = d->UsePwd ? XADERR_PASSWORD : XADERR_CHECKSUM;
  }

  if(!(t->CFlag & DMSCFLAG_NOINIT) && !(d->DidInit))
    DMSInitData(d);

  if(err)
  {
    if(*res)
    {
      FreeVec(*res);
      *res= 0;
    }
  }

  return err;
}

static struct DMSData *GetDMSData(struct xadMasterBase *xadMasterBase, STRPTR pwd)
{
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;
  struct DMSData *d;
  if((d = (struct DMSData *) AllocVec(sizeof(struct DMSData), MEMF_CLEAR)))
  {
    DMSInitData(d);
    if(pwd)
      d->RTV_Pass = d->PassCRC = xadCalcCRC16(XADCRC16_ID1, 0, strlen(pwd), pwd);
  }
  return d;
}

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

ASM(BOOL) DMS_RecogData(REG(d0, ULONG size), REG(a0, struct DMSHeader *h),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(h->DMSID == 0x444D5321 && xadCalcCRC16(XADCRC16_ID1, 0,
  sizeof(struct DMSHeader)-6, ((STRPTR)h)+4) == h->CheckSum)
    return 1;
  else
    return 0;
}

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

/* Only the crypted information is used form DMS header. All the other
   information is taken from data directly, to allow these lots of
   modified headers. */

static struct xadDiskInfo *DMSOneArc(struct xadMasterBase *xadMasterBase,
struct xadArchiveInfo *ai, LONG *more, LONG crypted, LONG *ret)
{
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;
  struct xadDiskInfo *di;
  struct xadTextInfo *ti = 0, *ti2;
  struct DMSData *d;
  struct DMSTrack t;
  LONG err = 0, stop = 0, tracksize = 0;
  LONG lowcyl = -1, highcyl = -1;
  STRPTR zerotxt = 0;
  ULONG zerosize = 0;

  *more = 0;

  if((di = (struct xadDiskInfo *) xadAllocObject(XADOBJ_DISKINFO,
  XAD_OBJPRIVINFOSIZE, sizeof(struct DMSPos), TAG_DONE)))
  {
    struct DMSPos *dpos;

    dpos = (struct DMSPos *) (di->xdi_PrivateInfo);
    if((d = GetDMSData(xadMasterBase, ai->xai_Password)))
    {
      if(crypted)
        d->UsePwd = DMSPWD_USE;

      dpos->startpos = ai->xai_InPos;

      while(!stop && !err)
      {
        if(xadHookAccess(XADAC_READ, sizeof(struct DMSTrack), &t, ai))
          stop = 1;
        else
        {
          if(testDMSTrack(&t, xadMasterBase))
	    stop = 2;	/* stop > 1 means seek back one Track */
          else
          {
            if(zerotxt)
            {
              while(zerosize && !zerotxt[zerosize-1])
                --zerosize;
              if(t.TrackNumber != 1 && zerosize && zerosize <= 2048) /* was a information text */
              {
		highcyl = lowcyl = -1; /* reset these two */
                if((ti2 = (struct xadTextInfo *) xadAllocObjectA(XADOBJ_TEXTINFO, 0)))
                {
	          if(ti)
		    ti->xti_Next = ti2;
		  else
		    di->xdi_TextInfo = ti2;
		  ti = ti2;
		  if(!(ti2->xti_Text = (STRPTR) AllocVec(zerosize+1, MEMF_PUBLIC|MEMF_CLEAR)))
		    err = XADERR_NOMEMORY;
		  else
		  {
		    ti2->xti_Size = zerosize;
		    CopyMem(zerotxt, ti->xti_Text, zerosize);
		  }
	        }
	        else
	          err = XADERR_NOMEMORY;
              }
              FreeVec(zerotxt);
              zerotxt = 0;
            }
            /* Normally only -1 is allowed as banner, but I found at least
               one file using -2. */
            if(t.TrackNumber < 0 || t.TrackNumber == DMSTRTYPE_DIZ ||
            (!t.TrackNumber && t.UnpackedSize == 1024))
            {
              if((ti2 = (struct xadTextInfo *) xadAllocObjectA(XADOBJ_TEXTINFO, 0)))
              {
                if(t.TrackNumber == DMSTRTYPE_DIZ)
                  ti2->xti_Flags |= XADTIF_FILEDIZ;
		if(!DecrunchDMS(&t, ai, xadMasterBase, &ti2->xti_Text, d))
		  ti2->xti_Size = t.UnpackedSize;
		else if(crypted && !d->PassCRC)
		  ti2->xti_Flags |= XADTIF_CRYPTED;
		if(ti)
		  ti->xti_Next = ti2;
		else
		  di->xdi_TextInfo = ti2;
		ti = ti2;
	      }
	      else
	        err = XADERR_NOMEMORY;
	    }
	    else
	    {
	      if(highcyl == -1)
	      {
	        highcyl = lowcyl = t.TrackNumber;
	        tracksize = t.UnpackedSize;
		dpos->firsttrack = ai->xai_InPos-sizeof(struct DMSTrack);
		if(!t.TrackNumber)
		{
		  zerosize = t.UnpackedSize;
		  if((DecrunchDMS(&t, ai, xadMasterBase, &zerotxt, d)))
		    zerotxt = 0;	/* ignore it */
		  continue;
		}
	      }
	      else if(t.TrackNumber != highcyl+1 || tracksize != t.UnpackedSize)
	      {
	        stop = 3; break;
	      }
	      else
	        ++highcyl;

              if(d->UsePwd)
              {
	        STRPTR a;
	        if(!(a = AllocMem(t.CMODE_Packed, MEMF_ANY)))
	          err = XADERR_NOMEMORY;
	        else
	        {
	          if(!(err = xadHookAccess(XADAC_READ, t.CMODE_Packed, a, ai)))
	            DecryptDMS(a, t.CMODE_Packed, d);	/* to get a valid pwd pointer */
	          FreeMem(a, t.CMODE_Packed);
	        }
              }
              else
                err = xadHookAccess(XADAC_INPUTSEEK, t.CMODE_Packed, 0, ai);
            }
          } /* testDMSTrack */
        } /* xadHookAccess */
      } /* while */

      if(zerotxt) /* in case there is only one zero track and nothing else */
      {
        FreeVec(zerotxt);
        zerotxt = 0;
      }

      if(stop > 1)
        err = xadHookAccess(XADAC_INPUTSEEK, -sizeof(struct DMSTrack), &t, ai);

      if(!tracksize || lowcyl == -1 || highcyl == -1 || tracksize % (2*512))
        err = XADERR_ILLEGALDATA;
      else
      {
	if(crypted & DMSINFO_ENCRYPT)
	  di->xdi_Flags = XADDIF_CRYPTED;

	tracksize /= (2*512);

	di->xdi_LowCyl = lowcyl;
	di->xdi_HighCyl = highcyl;
	di->xdi_SectorSize = 512;
	di->xdi_Heads = 2;
	di->xdi_Cylinders = 80;
        switch(tracksize)
        {
        case 18: case 9:
          di->xdi_EntryInfo = "MSDOS disk"; /* no break! */
	case 22: case 11:
	  di->xdi_TrackSectors = tracksize;
	  break;
        default: err = XADERR_ILLEGALDATA; break;
	}
	di->xdi_CylSectors = 2 * di->xdi_TrackSectors;
	di->xdi_TotalSectors = 80 * di->xdi_CylSectors;
      }

      FreeVec(d);
    } /* GetDMSData */
    else
      err = XADERR_NOMEMORY;

    if(err)
    {
      for(ti = di->xdi_TextInfo; ti; ti = ti2)
      {
        ti2 = ti->xti_Next;
        if(ti->xti_Text)
          FreeVec(ti->xti_Text);
        xadFreeObjectA(ti, 0);
      }
      xadFreeObjectA(di, 0);
      di = 0;
    }
  } /* xadAllocObjectA */
  else
    err = XADERR_NOMEMORY;

  *ret = err;
  if(stop == 3 && !err)
    *more = 1;

  return di;
}

ASM(LONG) DMS_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;
  LONG err = 0;
  struct DMSHeader h;
  struct DMSTrack t = {0};
  struct xadFileInfo *fi = 0, *fi2;
  struct xadDiskInfo *di = 0, *di2;
  ULONG num = 1, i, j;

  /* appended stuff is treated as own archive, as there may be gaps and double
     parts due to the chaotic file format of DMS. */

  while(!err && !xadHookAccess(XADAC_READ, sizeof(struct DMSHeader), &h, ai))
  {
    if(!testDMSTrack((struct DMSTrack *) &h, xadMasterBase))
      err = xadHookAccess(XADAC_INPUTSEEK, sizeof(struct DMSTrack)+
      sizeof(struct DMSHeader)+((struct DMSTrack *)&h)->CMODE_Packed, 0, ai);
    else
    {
      if(!DMS_RecogData(sizeof(struct DMSHeader), &h, xadMasterBase))
        return 0;

      if(h.InfoFlags & DMSINFO_ENCRYPT)
        ai->xai_Flags |= XADAIF_CRYPTED;
      if(h.DiskType2 == DMSTYPE_FMS || h.DiskType == DMSTYPE_FMS)
      {
        if(!(err = xadHookAccess(XADAC_READ, sizeof(struct DMSTrack), &t, ai)) &&
        !(err = testDMSTrack(&t, xadMasterBase)) && t.TrackNumber == DMSTRTYPE_FILENAME)
        {
          if(!(fi2 = (struct xadFileInfo *) xadAllocObject(XADOBJ_FILEINFO,
          XAD_OBJNAMESIZE, t.UnpackedSize+1, TAG_DONE)))
            return XADERR_NOMEMORY;
          if(!(err = xadHookAccess(XADAC_READ, t.UnpackedSize, fi2->xfi_FileName, ai)))
          {
            i = ai->xai_InPos;
            j = 0;
            while(j < h.UnpackedSize && !err)
            {
              if(!(err = xadHookAccess(XADAC_READ, sizeof(struct DMSTrack), &t, ai)) &&
              !(err = testDMSTrack(&t, xadMasterBase)))
              {
                if(!(err = xadHookAccess(XADAC_INPUTSEEK, t.CMODE_Packed, 0, ai)))
                  j += t.UnpackedSize;
              }
            }
            if(!err)
            {
	      if(h.InfoFlags & DMSINFO_ENCRYPT)
	        fi2->xfi_Flags = XADFIF_CRYPTED;
              h.Date -= 7*60*60;
	      if(!fi2->xfi_FileName[0]) /* This is a 2.04 FMS files */
	      {
	        struct FMSInfo {
	          LONG prot;
	          struct DateStamp d;
	          } inf;

		CopyMem(fi2->xfi_FileName, &inf, sizeof(struct FMSInfo));
		fi2->xfi_FileName += sizeof(struct FMSInfo);
	      
	        fi2->xfi_Protection = inf.prot;
                err = xadConvertDates(XAD_DATEDATESTAMP, &inf.d,
                XAD_GETDATEXADDATE, &fi2->xfi_Date, TAG_DONE);
	        if(fi2->xfi_FileName[0] & 0x80)
	        {
	          fi2->xfi_Comment = fi2->xfi_FileName+1;
	          fi2->xfi_FileName += (fi2->xfi_FileName[0]&0x7F)+1;
	        }
	        *(fi2->xfi_FileName++) = 0; /* clear size (make C pointer) */
	      }
	      else
                err = xadConvertDates(XAD_DATEUNIX, h.Date,
                XAD_GETDATEXADDATE, &fi2->xfi_Date, XAD_MAKELOCALDATE, 1, TAG_DONE);
              fi2->xfi_PrivateInfo = (APTR) i;
              fi2->xfi_EntryNumber = num++;
              fi2->xfi_Size = h.UnpackedSize;
              fi2->xfi_CrunchSize = h.PackedSize;
              if(fi)
                fi->xfi_Next = fi2;
              else
                ai->xai_FileInfo = fi2;
              fi = fi2;
              fi2 = 0;
            }
          }
          if(fi2)
            xadFreeObjectA(fi2, 0);
        }
      }
      else /* is a disk archive */
      {
	LONG more = 1;
	
	while(more)
	{
	  if((di2 = DMSOneArc(xadMasterBase, ai, &more, h.InfoFlags & DMSINFO_ENCRYPT, &err)))
	  {
	    di2->xdi_EntryNumber = num++;
            if(di)
              di->xdi_Next = di2;
            else
              ai->xai_DiskInfo = di2;
            di = di2;
	  }
	}
      } /* DISK ARCHIVE */
    } /* found correct track */
    if(err)
      ai->xai_Flags |= XADAIF_FILECORRUPT;
  } /* while */

  return 0;
}

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

ASM(LONG) DMS_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;
  struct DMSTrack t;
  LONG i;
  LONG err = 0;
  STRPTR pwd = ai->xai_Password;
  struct DMSData *d;

  if((ai->xai_CurFile && !(ai->xai_CurFile->xfi_Flags & XADFIF_CRYPTED)) ||
  !(ai->xai_CurDisk->xdi_Flags & XADDIF_CRYPTED))
    pwd = 0;
  else if(!pwd)
    return XADERR_PASSWORD;

  if(!(d = GetDMSData(xadMasterBase, pwd)))
    return XADERR_NOMEMORY;  

  if(ai->xai_CurFile)
  {
    struct xadFileInfo *fi;

    fi = ai->xai_CurFile;

    if(fi->xfi_Flags & XADFIF_CRYPTED)
      d->UsePwd = DMSPWD_USE;

    if(ai->xai_InPos != (ULONG) fi->xfi_PrivateInfo)
      err = xadHookAccess(XADAC_INPUTSEEK, ((ULONG) fi->xfi_PrivateInfo) - ai->xai_InPos, 0, ai);

    for(i = DMSTRTYPE_FILESTART; !err && ai->xai_OutSize < fi->xfi_Size; ++i)
    {
      if(!(err = xadHookAccess(XADAC_READ, sizeof(struct DMSTrack), &t, ai)) &&
      !(err = testDMSTrack(&t, xadMasterBase)))
      {
        STRPTR res;

        if(i != t.TrackNumber)
          err = XADERR_ILLEGALDATA;
        else if(!(err = DecrunchDMS(&t, ai, xadMasterBase, &res, d)))
        {
          err = xadHookAccess(XADAC_WRITE, t.UnpackedSize, res, ai);
          FreeVec(res);
        }
      }
    }
  }
  else
  {
    struct xadDiskInfo *di;
    struct DMSPos *dpos;

    di = ai->xai_CurDisk;
    dpos = (struct DMSPos *) (di->xdi_PrivateInfo);

    if(di->xdi_Flags & XADDIF_CRYPTED)
      d->UsePwd = DMSPWD_USE;

    if(ai->xai_InPos != dpos->startpos)
      err = xadHookAccess(XADAC_INPUTSEEK, dpos->startpos - ai->xai_InPos, 0, ai);

    for(i = ai->xai_LowCyl; !err && i <= ai->xai_HighCyl;)
    {
      if(!(err = xadHookAccess(XADAC_READ, sizeof(struct DMSTrack), &t, ai)) &&
      !(err = testDMSTrack(&t, xadMasterBase)))
      {
        STRPTR res;

        if(!(err = DecrunchDMS(&t, ai, xadMasterBase, &res, d)))
        {
          if(ai->xai_InPos > dpos->firsttrack) /* skip unusable parts */
          {
	    if(i == t.TrackNumber)
	    {
              err = xadHookAccess(XADAC_WRITE, t.UnpackedSize, res, ai); ++i;
            }
            else if(t.TrackNumber > i)
              err = XADERR_ILLEGALDATA;
          }
          FreeVec(res);
        }
      }
    }
  }

  FreeVec(d);
  return err;
}

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

ASM(void) DMS_Free(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;
  struct xadFileInfo *fi, *fi2;
  struct xadDiskInfo *di, *di2;
  struct xadTextInfo *ti, *ti2;

  for(fi = ai->xai_FileInfo; fi; fi = fi2)
  {
    fi2 = fi->xfi_Next;
    xadFreeObjectA(fi, 0);
  }
  ai->xai_FileInfo = 0;

  for(di = ai->xai_DiskInfo; di; di = di2)
  {
    di2 = di->xdi_Next;
    
    for(ti = di->xdi_TextInfo; ti; ti = ti2)
    {
      ti2 = ti->xti_Next;
      if(ti->xti_Text)
        FreeVec(ti->xti_Text);
      xadFreeObjectA(ti, 0);
    }
    xadFreeObjectA(di, 0);
  }
  ai->xai_DiskInfo = 0;
}

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

ASM(BOOL) DMSSFX_RecogData(REG(d0, ULONG size), REG(a0, ULONG *data),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(*data == HUNK_HEADER)
  {
    if(data[5] == 0x1605 && data[6] == 0x1C24 && data[16] == 0x303C05CD &&
    data[17] == 0x421B51C8 && data[18] == 0xFFFC47F9)
      return 1;
    if(data[10] == 0x24E2 && data[9] == 0x60000006 && data[11] == 0x48E77EFE &&
    data[12] == 0x24482400 && data[16] == 0x3B61425B)
      return 1; /* defective 2.04 SFX arcs */
  }
  return 0;
}

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

ASM(LONG) DMSSFX_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG err;
  LONG data[3];

  if(!(err = xadHookAccess(XADAC_READ, 12, data, ai)))
  {
    if(!(err = xadHookAccess(XADAC_INPUTSEEK, data[2] == 2 ? 0x537C-12 : 0x58C4-12, 0, ai)))
      err = DMS_GetInfo(ai, xadMasterBase);
  }

  return err;
}

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

ASM(BOOL) SDSSFX_RecogData(REG(d0, ULONG size), REG(a0, ULONG *data),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(*data == HUNK_HEADER)
  {
    if(data[15] == 0xFDD823C0 && data[17] == 0x2C404EAE &&
    data[19] == 0x405A && data[20] == 0x207C0000 && data[21] == 0x020C7600)
      return 1;
  }
  return 0;
}

struct SDSSFXData {
  UBYTE Name[20];
  ULONG CrSize;
  ULONG Size;
  UWORD CheckSum;
};

ASM(LONG) SDSSFX_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG err, i, j, num = 1;
  struct SDSSFXData sd;
  struct xadFileInfo *fi = 0, *fi2;

  if((err = xadHookAccess(XADAC_INPUTSEEK, 0x41C, 0, ai)))
    return err;
  while(!err)
  {
    j = ai->xai_InPos;
    if(!(err = xadHookAccess(XADAC_READ, sizeof(struct SDSSFXData), &sd, ai)))
    {
      if(!(i = strlen(sd.Name)))
        break; /* last entry */
      if(!(err = xadHookAccess(XADAC_INPUTSEEK, sd.CrSize, 0, ai)))
      {
        if((fi2 = (struct xadFileInfo *) xadAllocObject(XADOBJ_FILEINFO,
        XAD_OBJNAMESIZE, i, TAG_DONE)))
        {
          fi2->xfi_PrivateInfo = (APTR) j; /* file position */
          fi2->xfi_Size = sd.Size;
          fi2->xfi_EntryNumber = num++;
          fi2->xfi_CrunchSize = sd.CrSize;
	  fi2->xfi_Flags = XADFIF_NODATE;
          for(j = 0; j < i; ++j)
            fi2->xfi_FileName[j] = sd.Name[j];
          err = xadConvertDates(XAD_DATECURRENTTIME, 1, XAD_GETDATEXADDATE,
          &fi2->xfi_Date, TAG_DONE);
          if(fi)
            fi->xfi_Next = fi2;
          else
            ai->xai_FileInfo = fi2;
          fi = fi2;
        }
        else
          err = XADERR_NOMEMORY;
      }
    }
  }
  if(err)
    ai->xai_Flags |= XADAIF_FILECORRUPT;

  return fi ? 0 : XADERR_ILLEGALDATA;
}

struct DMSDataShort {
  ULONG		bitbuf;
  STRPTR	indata;
  UBYTE		bitcount;
  UBYTE		DidInit;
  UWORD		RTV_Medium;
  UBYTE		Text[32768];
};

ASM(LONG) SDSSFX_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;
  LONG err;
  struct SDSSFXData sd;

  if(ai->xai_InPos != (ULONG) ai->xai_CurFile->xfi_PrivateInfo)
  {
    if((err = xadHookAccess(XADAC_INPUTSEEK, ((ULONG) ai->xai_CurFile->xfi_PrivateInfo) -
    ai->xai_InPos, 0, ai)))
      return err;
  }
  if(!(err = xadHookAccess(XADAC_READ, sizeof(struct SDSSFXData), &sd, ai)))
  {
    struct DMSData *d;
    
    if((d = (struct DMSData *) AllocMem(sizeof(struct DMSDataShort), MEMF_PUBLIC|MEMF_CLEAR)))
    {
      STRPTR buf;

      if((buf = (STRPTR) AllocVec(sd.CrSize, MEMF_PUBLIC)))
      {
        d->RTV_Medium = 0x3FBE;

        if(!(err = xadHookAccess(XADAC_READ, sd.CrSize, buf, ai)))
        {
          if(!(err = DMSUnpMEDIUM(&buf, sd.Size, d, SysBase)))
          {
            if(CheckSumDMS(buf, sd.Size) == sd.CheckSum)
              err = xadHookAccess(XADAC_WRITE, sd.Size, buf, ai);
            else
              err = XADERR_CHECKSUM;
          }
        }
        FreeVec(buf);
      }
      else
        err = XADERR_NOMEMORY;
    }
    else
      err = XADERR_NOMEMORY;
  }

  return err;
}

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

struct xadClient SDSSFX_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 1, SDSSFX_VERSION, SDSSFX_REVISION, 100,
XADCF_FILEARCHIVER, XADCID_SDSSFX, "SDS Software SFX",
(BOOL (*)()) SDSSFX_RecogData, (LONG (*)()) SDSSFX_GetInfo, (LONG (*)())
SDSSFX_UnArchive, (void (*)()) DMS_Free};

struct xadClient DMSSFX_Client = {
&SDSSFX_Client, XADCLIENT_VERSION, 1, DMSSFX_VERSION, DMSSFX_REVISION, 100,
XADCF_DISKARCHIVER|XADCF_FILEARCHIVER, XADCID_DMSSFX, "DMS SFX",
(BOOL (*)()) DMSSFX_RecogData, (LONG (*)()) DMSSFX_GetInfo, (LONG (*)())
DMS_UnArchive, (void (*)()) DMS_Free};

struct xadClient DMS_Client = {
&DMSSFX_Client, XADCLIENT_VERSION, 1, DMS_VERSION, DMS_REVISION, sizeof(struct DMSHeader),
XADCF_DISKARCHIVER|XADCF_FILEARCHIVER, XADCID_DMS, "DMS",
(BOOL (*)()) DMS_RecogData, (LONG (*)()) DMS_GetInfo, (LONG (*)())
DMS_UnArchive, (void (*)()) DMS_Free};

#endif /* XADASTER_DMS_C */
