#ifndef XADMASTER_XDISK_C
#define XADMASTER_XDISK_C

/* Programmheader

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

 1.0   05.09.98 : first version
*/

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

#ifndef XADMASTERFILE
#define xDisk_Client		FirstClient
#define NEXTCLIENT		0
UBYTE version[] = "$VER: xDisk 1.0 (05.09.1998)";
#endif
#define XDISK_VERSION		1
#define XDISK_REVISION		0

struct xDisk {
  ULONG			xd_Header;	/* equals 'XDS0' */
  struct DateStamp	xd_Date;	/* date of creation */
  ULONG			xd_PackTime;	/* in seconds */
  UWORD			xd_AttnFlags;	/* ExecBase->AttnFlags */
  UWORD			xd_SoftVer;	/* ExecBase->SoftVer */
  ULONG			xd_CylinderSize; /* Size of one cylinder */
  ULONG			xd_NumCylinders; /* Number of cylinders */
  ULONG			xd_InfoTextSize; /* no text == 0 */
  ULONG			xd_LowCyl;	/* lowest crunched cylinder */
  ULONG			xd_HighCyl;	/* highest crunched Cylinder */
};

/* After tha xDisk structure the packed data follows. First the
   XPK-Crunched info-text (when available) and the each cylinder as a
   XPKF-block.
   NOTE: It seems that sometimes xDisk adds xd_InfoTextSize, also when no
   text was added!
*/

ASM(BOOL) xDisk_RecogData(REG(d0, ULONG size), REG(a0, STRPTR data),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(((ULONG *) data)[0] == 0x58445330)
    return 1;
  else
    return 0;
}

ASM(LONG) xDisk_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG err;
  ULONG dat[9];
  ULONG num = 0;
  struct xDisk xd;
  struct xadDiskInfo *xdi;

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

  if((err = xadHookAccess(XADAC_READ, sizeof(struct xDisk), &xd, ai)))
    return err;
  xdi->xdi_EntryNumber = 1;
  xdi->xdi_Cylinders = xd.xd_NumCylinders;
  xdi->xdi_LowCyl = xd.xd_LowCyl;
  xdi->xdi_HighCyl = xd.xd_HighCyl;
  xdi->xdi_SectorSize = 512; /* most devices should use 512 as blocksize */
  xdi->xdi_CylSectors = xd.xd_CylinderSize>>9;
  xdi->xdi_TotalSectors = xd.xd_NumCylinders*xdi->xdi_CylSectors;
  xdi->xdi_Flags = XADDIF_GUESSHEADS|XADDIF_GUESSTRACKSECTORS;
  if(xdi->xdi_CylSectors & 1)
  {
    xdi->xdi_Heads = 1;
    xdi->xdi_TrackSectors = xdi->xdi_CylSectors;
  }
  else
  {
    xdi->xdi_Heads = 2;
    xdi->xdi_TrackSectors = xdi->xdi_CylSectors>>1;
  }

  while(ai->xai_InPos < ai->xai_InSize)
  {
    if((err = xadHookAccess(XADAC_READ, 36, dat, ai)))
      return err;
    if((err = xadHookAccess(XADAC_INPUTSEEK, dat[1]-28, 0, ai)))
      return err;
    ++num;
    if(dat[8] & (1<<25)) /* check for password flag in every entry */
    {
      ai->xai_Flags |= XADAIF_CRYPTED;
      xdi->xdi_Flags |= XADDIF_CRYPTED;
    }
  }
  if((err = xadHookAccess(XADAC_INPUTSEEK, sizeof(struct xDisk) -
  ai->xai_InPos, 0, ai)))
    return err;
  dat[0] = xdi->xdi_HighCyl+1-xdi->xdi_LowCyl;
  if(num == dat[0]+1) /* decrunch infotext and store pointer */
  {
    xdi->xdi_Flags |= XADDIF_INFOTEXT;

    return xpkDecrunch(&xdi->xdi_InfoText, &xdi->xdi_InfoTextSize, ai,
    xadMasterBase);
  }
  else if(num != dat[0])
    return XADERR_ILLEGALDATA;

  return 0;
}

ASM(LONG) xDisk_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  LONG i, err = 0, u;
  struct {
    STRPTR a;
    ULONG  s;
  } dat;
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;

  u = ai->xai_InPos;

  /* skip entries */
  for(i = ai->xai_CurDisk->xdi_LowCyl; !err && i < ai->xai_LowCyl; ++i)
  {
    if(!(err = xadHookAccess(XADAC_READ, 8, &dat, ai)))
      err = xadHookAccess(XADAC_INPUTSEEK, dat.s, 0, ai);
  }

  for(; !err && i <= ai->xai_HighCyl; ++i)
  {
    if(!(err = xpkDecrunch(&dat.a, &dat.s, ai, xadMasterBase)))
    {
      err = xadHookAccess(XADAC_WRITE, dat.s, dat.a, ai);
      FreeVec(dat.a);
    }
  }

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

  return err;
}

ASM(void) xDisk_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);
    xadFreeObjectA(ai->xai_DiskInfo, 0);
    ai->xai_DiskInfo = 0; /* clear the entry */
  }
}

struct xadClient xDisk_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 1, XDISK_VERSION, XDISK_REVISION, 4,
XADCF_DISKARCHIVER, XADCID_XDISK, "xDisk", (BOOL (*)()) xDisk_RecogData,
(LONG (*)()) xDisk_GetInfo, (LONG (*)()) xDisk_UnArchive,
(void (*)()) xDisk_Free};

#endif /* XADASTER_XDISK_C */
