#ifndef XADMASTER_XMASH_C
#define XADMASTER_XMASH_C

/* Programmheader

	Name:		xMash.c
	Main:		xadmaster
	Versionstring:	$VER: xMash.c 1.0 (05.09.1998)
	Author:		SDI
	Distribution:	Freeware
	Description:	xMash disk archiver client

 1.0   05.09.98 : first version
*/

#include <proto/xadmaster.h>
#include "SDI_compiler.h"
#include "xpkstuff.c"

#ifndef XADMASTERFILE
#define xMash_Client		FirstClient
#define NEXTCLIENT		0
UBYTE version[] = "$VER: xMash 1.0 (05.09.1998)";
#endif
#define XMASH_VERSION		1
#define XMASH_REVISION		0

/*
  structure of one xMash chunk:
    UBYTE		xmc_Type;
    UBYTE		xmc_Start
    UBYTE		xmc_Num;
    ULONG		xmc_Size;
*/

#define XMASH_INFOTEXT	0x46
#define XMASH_BANNER	0x42
#define XMASH_ARCHIVE	0x44

struct xMashHead {
  UBYTE	type;
  UBYTE start;
  UBYTE num;
};

ASM(BOOL) xMash_RecogData(REG(d0, ULONG size), REG(a0, STRPTR data),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(data[0] == 'M' && data[1] == 'S' && data[2] == 'H' &&
  (data[3] == XMASH_BANNER || data[3] == XMASH_ARCHIVE || data[3] ==
  XMASH_INFOTEXT))
    return 1;
  else
    return 0;
}

ASM(LONG) xMash_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG err, lowcyl = 80, highcyl = -1;
  ULONG dat[9], start = 3;
  struct xadDiskInfo *xdi;
  struct xMashHead h;

  if(!(xdi = (struct xadDiskInfo *) xadAllocObjectA(XADOBJ_DISKINFO, 0)))
    return XADERR_NOMEMORY;
  ai->xai_DiskInfo = xdi;

  xdi->xdi_EntryNumber = 1;
  xdi->xdi_SectorSize = 512;
  xdi->xdi_Cylinders = 80;
  xdi->xdi_Heads = 2;
  xdi->xdi_TrackSectors = 11;
  xdi->xdi_CylSectors = 22;
  xdi->xdi_TotalSectors = 80 * 22;

  if((err = xadHookAccess(XADAC_INPUTSEEK, 3, 0, ai))) /* skip MSH */
    return err;

  while(ai->xai_InPos < ai->xai_InSize && !err)
  {
    if(!(err = xadHookAccess(XADAC_READ, 3, &h, ai)) &&
    !(err = xadHookAccess(XADAC_READ, 4, dat, ai)))
    {
      switch(h.type)
      {
      case XMASH_INFOTEXT:
        err = xpkDecrunch(&xdi->xdi_InfoText, &xdi->xdi_InfoTextSize, ai,
        xadMasterBase);
        xdi->xdi_Flags |= XADDIF_INFOTEXT;
        start = ai->xai_InPos;
        break;
      case XMASH_BANNER:
        err = xpkDecrunch(&xdi->xdi_Banner, &xdi->xdi_BannerSize, ai,
        xadMasterBase);
        xdi->xdi_Flags |= XADDIF_BANNER;
        start = ai->xai_InPos;
        break;
      case XMASH_ARCHIVE:
        if(!(err = xadHookAccess(XADAC_READ, 36, dat, ai)) &&
        !(err = xadHookAccess(XADAC_INPUTSEEK, dat[1]-28, 0, ai)))
        {
          if(dat[8] & (1<<25))
          { /* check for password flag in every entry */
            ai->xai_Flags |= XADAIF_CRYPTED;
            xdi->xdi_Flags |= XADDIF_CRYPTED;
          }
          h.num = ((h.num+h.start) >> 1)-1;
          h.start >>= 1;
          if(h.start < lowcyl)
            lowcyl = h.start;
          if(h.num > highcyl)
            highcyl = h.num;
        }
        break;
      }
    }
  }

  if(lowcyl <= highcyl)
  {
    xdi->xdi_LowCyl  = lowcyl;
    xdi->xdi_HighCyl = highcyl;
  }
  else
    err = XADERR_INPUT;

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

  return err;
}

ASM(LONG) xMash_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct xMashHead h;
  LONG err = 0, u;
  ULONG size, lowcyl;
  STRPTR a;
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;

  u = ai->xai_InPos;
  lowcyl = ai->xai_LowCyl;

  while(!err && lowcyl <= ai->xai_HighCyl)
  {
    if(!(err = xadHookAccess(XADAC_READ, 3, &h, ai)) &&
    !(err = xadHookAccess(XADAC_READ, 4, &size, ai)))
    {
      LONG endcyl, startcyl, skipbyte;

      startcyl = h.start>>1;
      endcyl = ((h.start+h.num)>>1)-1;

      if(endcyl < lowcyl)
        err = xadHookAccess(XADAC_INPUTSEEK, size, 0, ai);
      else
      {
	ULONG size;
        if(!(err = xpkDecrunch(&a, &size, ai, xadMasterBase)))
        {
	  skipbyte = 0;

          if(startcyl < lowcyl)
            skipbyte = (lowcyl-startcyl)*22*512;
          if(endcyl > ai->xai_HighCyl)
            endcyl = ai->xai_HighCyl;
	  size = (endcyl+1-lowcyl)*22*512;
            
          err = xadHookAccess(XADAC_WRITE, size, a+skipbyte, ai);
          FreeVec(a);
          lowcyl = endcyl+1;
        }
      }
    }
  }

  /* seek back to start */
  if(!err)
    err = xadHookAccess(XADAC_INPUTSEEK, u-ai->xai_InPos, 0, ai);

  return err;
}

ASM(void) xMash_Free(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;
  if(ai->xai_DiskInfo)
  {
    if(ai->xai_DiskInfo->xdi_InfoText)
      FreeVec(ai->xai_DiskInfo->xdi_InfoText);
    if(ai->xai_DiskInfo->xdi_Banner)
      FreeVec(ai->xai_DiskInfo->xdi_Banner);
    xadFreeObjectA(ai->xai_DiskInfo, 0);
    ai->xai_DiskInfo = 0; /* clear the entry */
  }
}

struct xadClient xMash_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 1, XMASH_VERSION, XMASH_REVISION, 3,
XADCF_DISKARCHIVER, XADCID_XMASH, "xMash", (BOOL (*)()) xMash_RecogData,
(LONG (*)()) xMash_GetInfo, (LONG (*)()) xMash_UnArchive,
(void (*)()) xMash_Free};

#endif /* XADASTER_XMASH_C */
