/* This XAD client is (C) 2000 Stuart Caie <kyzer@4u.net>
 *
 * 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.
 */

/* COP is an XPK based disk archiver, written by Armin Sander
 * for Markt & Technik in 1993.
 * Armin Sander's homepage is at http://mindwalker.org/rnd/
 *
 * COP format (all unsigned big-endian four byte values):
 * - header
 * - block
 * - block
 * - ...
 * 
 * COP header:
 * offset  field                value in normal Amiga disk
 * 0       Identification 1     "COP0"
 * 4       Identification 2     0x932AE3DB	
 * 8       XPK packer used      "SHRI", "SQSH", etc or 0 for no compression
 * 12      lowcyl               0
 * 16      highcyl              79
 * 20      reserved             2
 * 24      sectorsize           512
 * 28      surfaces             2
 * 32      sectors per cylinder 11
 * 36      interleave           0
 * 40      cylinders            80
 * 44      SIZEOF
 * 
 * COP block type 1: no compression (XPK packer = 0x000000000)
 * - ULONG block_size
 * - UBYTE raw_data[block_size]
 * 
 * COP block type 2: XPK packed
 * - ULONG block_size
 * - ULONG packed_data_size (== block_size - 8)
 * - ULONG unpacked_data_size
 * - UBYTE xpk_packed_data[packed_data_size]
 * 
 * COP block type 3: XPK packed, but XPK failed to compress the block
 * - ULONG block_size
 * - ULONG packed_data_size            (== block_size - 8)
 * - ULONG unpacked_data_size          (== packed_data_size)
 * - UBYTE raw_data[packed_data_size]
 * 
 * $VER: COP.c 1.2 (09.12.2000)
 */

#include <libraries/xadmaster.h>
#include <proto/xadmaster.h>
#include <string.h>

#include "ConvertE.c"
#include "SDI_compiler.h"
#include "xadXPK.c"

#ifndef XADMASTERFILE
#define COP_Client		FirstClient
#define NEXTCLIENT		0
UBYTE version[] = "$VER: COP 1.2 (09.12.2000)";
#endif
#define COP_VERSION     1
#define COP_REVISION    2

#define XADBASE REG(a6, struct xadMasterBase *xadMasterBase)

/* work-doing macros */
#define SKIP(offset) if ((err = xadHookAccess(XADAC_INPUTSEEK, \
  (ULONG)(offset), NULL, ai))) goto exit_handler
#define SEEK(offset) SKIP((offset) - ai->xai_InPos)
#define READ(buffer,length) if ((err = xadHookAccess(XADAC_READ, \
  (ULONG)(length), (APTR)(buffer), ai))) goto exit_handler
#define WRITE(buffer,length) if ((err = xadHookAccess(XADAC_WRITE, \
  (ULONG)(length), (APTR)(buffer), ai))) goto exit_handler
#define COPY(length) if ((err = xadHookAccess(XADAC_COPY, \
  (ULONG)(length), NULL, ai))) goto exit_handler
#define READLONG(var) do { READ(&buffer,4); (var)=EndGetM32(buffer); } while(0)

#define ERROR(error) do { err = XADERR_##error; goto exit_handler; } while(0)
#define FREE(obj) xadFreeObjectA((APTR)(obj),NULL)


ASM(BOOL) COP_RecogData(REG(d0, ULONG size), REG(a0, UBYTE *d), XADBASE) {
  return (BOOL) (d[0]=='C'  && d[1]=='O'  && d[2]=='P'  && d[3]=='0' &&
                 d[4]==0x93 && d[5]==0x2A && d[6]==0xE3 && d[7]==0xDB) ? 1:0;
}

ASM(LONG) COP_GetInfo(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
  struct xadDiskInfo *xdi;
  UBYTE buffer[44];
  LONG err;

  /* it's so simple here, I won't use the normal ALLOCOBJ/READ macros */
  xdi = (struct xadDiskInfo *) xadAllocObjectA(XADOBJ_DISKINFO, NULL);
  if (!(ai->xai_DiskInfo = xdi)) return XADERR_NOMEMORY;
  if ((err = xadHookAccess(XADAC_READ, 44, (APTR)&buffer, ai))) return err;

  xdi->xdi_EntryNumber  = 1;
  xdi->xdi_TrackSectors = EndGetM32(&buffer[32]);
  xdi->xdi_Heads        = EndGetM32(&buffer[28]);
  xdi->xdi_LowCyl       = EndGetM32(&buffer[12]);
  xdi->xdi_HighCyl      = EndGetM32(&buffer[16]);
  xdi->xdi_SectorSize   = EndGetM32(&buffer[24]);
  xdi->xdi_Cylinders    = EndGetM32(&buffer[40]);
  xdi->xdi_CylSectors   = xdi->xdi_TrackSectors * xdi->xdi_Heads;
  xdi->xdi_TotalSectors = xdi->xdi_CylSectors * xdi->xdi_Cylinders;
  return XADERR_OK;
}

ASM(LONG) COP_UnArchive(REG(a0, struct xadArchiveInfo *ai), XADBASE) {
  struct xadDiskInfo *di = ai->xai_CurDisk;
  UBYTE buffer[4], *decrbuf = NULL;
  ULONG cylsize, start, length, offset=0, foffset=44;
  ULONG crlen, blklen, outlen, xpk_mode;
  LONG err = XADERR_OK;

  cylsize = di->xdi_CylSectors * di->xdi_SectorSize;
  start  = (ai->xai_LowCyl - di->xdi_LowCyl) * cylsize;
  length = (ai->xai_HighCyl + 1 - ai->xai_LowCyl) * cylsize;

  SEEK(8); READLONG(xpk_mode);
  
  while (length) {
    /* go to next header and read in unpacked block length */
    SEEK(foffset);
    READLONG(blklen);
    foffset = ai->xai_InPos + blklen;
    if (xpk_mode) { READLONG(crlen); READLONG(blklen); }

    /* should we be writing data from this block? */
    if (start < (offset + blklen)) {
      /* calculate amount required to be output */
      length -= (outlen = (blklen > length) ? length : blklen);

      /* read (and decrunch) the block */
      if (xpk_mode && (crlen != blklen)) {
        if ((err = xpkDecrunch(&decrbuf, &crlen, ai, xadMasterBase)))
          goto exit_handler;
        WRITE(decrbuf, outlen);
        FREE(decrbuf); decrbuf = NULL;
      }
      else {
        COPY(outlen);
      }
    }
    offset += blklen;
  }

exit_handler:
  if (decrbuf) FREE(decrbuf);
  return err;
}

const struct xadClient COP_Client = {
  NEXTCLIENT, XADCLIENT_VERSION, 4, COP_VERSION, COP_REVISION,
  44, XADCF_DISKARCHIVER|XADCF_FREEDISKINFO,
  0, "COP",
  (BOOL (*)()) COP_RecogData,
  (LONG (*)()) COP_GetInfo,
  (LONG (*)()) COP_UnArchive,
  (void (*)()) NULL
};
