#ifndef XADMASTER_PACKDEV_C
#define XADMASTER_PACKDEV_C

/* Programmheader

	Name:		PackDev.c
	Main:		xadmaster
	Versionstring:	$VER: PackDev.c 1.1 (13.02.1999)
	Author:		SDI
	Distribution:	Freeware
	Description:	PackDev disk archiver client

 1.0   13.06.98 : first version
 1.1   13.02.99 : started again with that client
*/

/* For now SectorLabel information is ignored, as current
xadmaster.library does not support SectorLabels. */

#include <proto/xadmaster.h>
#include <proto/exec.h>
#include <devices/trackdisk.h>
#include "SDI_compiler.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"
#include "xpkstuff.c"

#ifndef XADMASTERFILE
#define PackDev_Client		FirstClient
#define NEXTCLIENT		0
UBYTE version[] = "$VER: PackDev 1.1 (13.02.1999)";
#endif
#define PACKDEV_VERSION		1
#define PACKDEV_REVISION	1

struct PackDevHead {
  UBYTE		pd_Header[4];	/* equals 'PKD\x13' */
  ULONG		pd_BlockNum;	/* Number of blocks */
  ULONG		pd_BlockSize;	/* size of one block */
  ULONG		pd_Reserved;	/* Reserved blocks */
  ULONG		pd_TrackLength;	/* Length of one track*/
  ULONG		pd_xpkBufferSize; /* in byte */
  ULONG		pd_xpkPacker;	/* XPK packer type */
  ULONG pad1;	/* These are fields containing the XPK packer name */
  ULONG pad2;	/* Don't know, why the author used 24bytes instead */
  ULONG pad3;	/* of the required 4. */
  ULONG pad4;	/* The fields are ignored by that client */
  ULONG pad5;
  UWORD		pd_xpkMode;	 /* XPK mode Number 0..100 */
  UWORD		pd_KnownFileSys; /* When all data stored, this is 0, else 1 */
};

struct PackDevHeadOld {
  UBYTE		pd_Header[4];	/* equals 'PKD\x11' */
  ULONG		pd_BlockNum;	/* Number of blocks */
  ULONG		pd_BlockSize;	/* size of one block */
  ULONG		pd_Reserved;	/* Reserved blocks */
  ULONG		pd_TrackLength;	/* Length of one track*/
  ULONG		pd_xpkBufferSize; /* in byte */
  ULONG		pd_xpkPacker;	/* XPK packer type */
  UWORD		pd_xpkMode;	 /* XPK mode Number 0..100 */
  UWORD		pd_KnownFileSys; /* When all data stored, this is 0, else 1 */
};

/* Every block has following structure:
 ULONG size
 ULONG data[...]
 ULONG checksum

Where data are the blocks and additionally the SectorLabels (16 Byte).
NOTE: xadmaster does not support to extract the label data in current version!

Checksum is missing in PackDev11 Version.
*/

#define PKD_XPKPACKED	(1<<0)
#define PKD_OLDMODE	(1<<1)

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

LONG PKDdecrBuf(STRPTR *buf, ULONG *i, struct xadArchiveInfo *ai,
struct xadMasterBase *xadMasterBase, ULONG oldmode)
{
  LONG err, size;
  if(!(err = xadHookAccess(XADAC_READ, 4, &size, ai)))
  {
    if(!(err = xpkDecrunch(buf, i, ai, xadMasterBase)))
    {
      if(!oldmode)
        err = xadHookAccess(XADAC_READ, 4, &size, ai);
    }
  }
  return err;
}

/* maybe there are some errors in that code, not tested yet */
ASM(LONG) PackDev_GetInfo(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  struct PackDevHead h;
  LONG err;
  struct ExecBase * SysBase = xadMasterBase->xmb_SysBase;

  if(!(err = xadHookAccess(XADAC_READ, sizeof(struct PackDevHeadOld), &h, ai)))
  {
    if(h.pd_Header[3] == 0x11 || !(err = xadHookAccess(XADAC_READ,
    sizeof(struct PackDevHead)-sizeof(struct PackDevHeadOld), ((STRPTR) &h) +
    sizeof(struct PackDevHeadOld), ai)))
    {
      struct xadDiskInfo *xdi;
      ULONG blksiz = 0, i, j, dat[10];
      STRPTR buf = 0;

      if(h.pd_Header[3] == 0x11)
      {
        h.pd_KnownFileSys = ((struct PackDevHeadOld *) &h)->pd_KnownFileSys;
/*      h.pd_xpkMode = ((struct PackDevHeadOld *) &h)->pd_xpkMode; */
      }
    
      /* check for password flag */
      if(h.pd_xpkPacker && !err && !(err = xadHookAccess(XADAC_READ, 40, dat, ai))
      && !(err = xadHookAccess(XADAC_INPUTSEEK, -40, 0, ai)))
      {
        if(dat[9] & (1<<25))
          ai->xai_Flags |= XADAIF_CRYPTED;
      }

      if(h.pd_KnownFileSys)
      {
        blksiz = h.pd_BlockNum;
        if(h.pd_xpkPacker)
        {
          j = ai->xai_InPos;
          if(!(err = PKDdecrBuf(&buf, &i, ai, xadMasterBase, h.pd_Header[3] == 0x11)))
            err = xadHookAccess(XADAC_INPUTSEEK, j-ai->xai_InPos, 0, ai);
        }
        else
        {
          if(!(buf = (STRPTR) AllocVec((i = blksiz>>3), MEMF_ANY)))
            err = XADERR_NOMEMORY;
          else
            err = xadHookAccess(XADAC_READ, i, buf, ai);
        }
      }
    
      if(!err)
      {
        if((xdi = (struct xadDiskInfo *) xadAllocObject(XADOBJ_DISKINFO,
        blksiz ? XAD_OBJBLOCKENTRIES : TAG_DONE, blksiz, TAG_DONE)))
        {
          if(ai->xai_Flags & XADAIF_CRYPTED)
            xdi->xdi_Flags |= XADDIF_CRYPTED;
          xdi->xdi_Flags |= XADDIF_NOCYLINDERS|XADDIF_NOLOWCYL|
        	            XADDIF_NOHIGHCYL|XADDIF_NOHEADS|XADDIF_NOCYLSECTORS;
          xdi->xdi_TotalSectors = h.pd_BlockNum;
          xdi->xdi_SectorSize = h.pd_BlockSize;
          xdi->xdi_TrackSectors = h.pd_TrackLength / h.pd_BlockSize;
          xdi->xdi_EntryNumber = 1;
          i = 0;
          if(h.pd_xpkPacker)
            i |= PKD_XPKPACKED;
          if(h.pd_Header[3] == 0x11)
            i |= PKD_OLDMODE;
          xdi->xdi_PrivateInfo = (APTR) i;
          ai->xai_DiskInfo = xdi;

	  /* does nothing if blksiz == 0 */
	  for(i = 0; i < blksiz; ++i)
          {
            if(i < h.pd_Reserved || (buf[i/8] & ((1 << (7-(i%8))))))
              xdi->xdi_BlockInfo[i] |= XADBIF_CLEARED;
          }
        }
        else
          err = XADERR_NOMEMORY;
      }
      if(buf)
        FreeVec(buf);
    }
  }

  return err;
}

ASM(LONG) PackDev_UnArchive(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  ULONG p, i, j, trsec, numsecs = 0;
  LONG err = 0, secsize;
  struct xadDiskInfo *di;
  STRPTR temp;
  struct ExecBase *SysBase = xadMasterBase->xmb_SysBase;
  
  p = ai->xai_InPos;
  di = ai->xai_CurDisk;
  secsize = di->xdi_SectorSize;
  trsec = di->xdi_TrackSectors;

  if(!(temp = AllocVec(di->xdi_SectorSize*di->xdi_TrackSectors, MEMF_ANY)))
    return XADERR_NOMEMORY;

  if(!(((ULONG) di->xdi_PrivateInfo) & PKD_XPKPACKED))
  {
    numsecs = 0;
    for(i = 0; !err && i < di->xdi_TotalSectors; ++i)
    {
      j = (i % trsec)*secsize;
    
      if(di->xdi_BlockInfo && di->xdi_BlockInfo[i])
        memset(temp+j, 0, secsize);
      else
      {
        err = xadHookAccess(XADAC_READ, secsize, temp+j, ai);
        ++numsecs;
      }
      /* skip the sectorlabel and write data */
      if((i % trsec) == (trsec-1) && !err)
      {
        if(!numsecs || !(err = xadHookAccess(XADAC_INPUTSEEK, TD_LABELSIZE*numsecs, 0, ai)))
          err = xadHookAccess(XADAC_WRITE, trsec*secsize, temp, ai);
        numsecs = 0;
      }
    }
  }
  else
  {
    ULONG size;
    LONG pos = 0, ressize;
    STRPTR buf = 0;

    err = PKDdecrBuf(&buf, &size, ai, xadMasterBase,
    (((ULONG) di->xdi_PrivateInfo) & PKD_OLDMODE));

    if(di->xdi_BlockInfo)
      pos += di->xdi_TotalSectors>>3;

    for(i = 0; !err && i < di->xdi_TotalSectors; ++i)
    {
      j = (i % trsec)*secsize;
      
      if(di->xdi_BlockInfo && di->xdi_BlockInfo[i])
        memset(temp+j, 0, secsize);
      else
      {
        ++numsecs;
        if((ressize = size-pos) >= secsize)
        {
          CopyMem(buf+pos, temp+j, secsize);
          pos += secsize;
        }
        else
        {
          if(ressize > 0)
          {
            CopyMem(buf+pos, temp+j, ressize);
            pos += ressize;
          }
          else if(ressize < 0)
            ressize = 0;
          FreeVec(buf);
          buf = 0;
          pos -= size;
          if(!(err = PKDdecrBuf(&buf, &size, ai, xadMasterBase,
          (((ULONG) di->xdi_PrivateInfo) & PKD_OLDMODE))))
          {
            CopyMem(buf+pos, temp+j+ressize, secsize-ressize);
            pos += secsize-ressize;
          }
        }
      }
      /* skip the sectorlabel and write data */
      if((i % trsec) == (trsec-1) && !err)
      {
        pos += TD_LABELSIZE*numsecs;
        err = xadHookAccess(XADAC_WRITE, trsec*secsize, temp, ai);
        numsecs = 0;
      }
    }
    if(buf)
      FreeVec(buf);
  }

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

  FreeVec(temp);

  return err;
}

ASM(void) PackDev_Free(REG(a0, struct xadArchiveInfo *ai),
REG(a6, struct xadMasterBase *xadMasterBase))
{
  if(ai->xai_DiskInfo)
  {
    xadFreeObjectA(ai->xai_DiskInfo, 0);
    ai->xai_DiskInfo = 0; /* clear the entry */
  }
}

struct xadClient PackDev_Client = {
NEXTCLIENT, XADCLIENT_VERSION, 1, PACKDEV_VERSION, PACKDEV_REVISION,
4, XADCF_DISKARCHIVER, XADCID_PACKDEV, "PackDev",
(BOOL (*)()) PackDev_RecogData, (LONG (*)()) PackDev_GetInfo,
(LONG (*)()) PackDev_UnArchive, (void (*)()) PackDev_Free};

#endif /* XADASTER_PACKDEV_C */
