#define NAME         "xadUnDisk"
#define DISTRIBUTION "(Freeware) "
#define REVISION     "12"
#define DATE	     "02.12.2001"

/* Programmheader

	Name:		xadUnDisk
	Author:		SDI
	Distribution:	Freeware
	Description:	dearchives disk archives
	Compileropts:	-
	Linkeropts:	-gsi -l amiga

 1.0   18.11.98 : first version
 1.1   23.12.98 : added support for FILEDESCRIPT
 1.2   04.02.99 : added file corrupt message, new InfoText system and SAVETEXTS
 1.3   16.02.99 : bug fix in report hook
 1.4   28.02.99 : little bug fix in chunk hook
 1.5   15.09.99 : added insert disk question
 1.6   20.02.00 : added FORMAT keyword and ADF to disk
 1.7   19.03.00 : bug fix
 1.8   12.04.00 : image question only, if XADERR_FILETYPE
 1.9   07.07.00 : added NOASKINSERT keyword
 1.10  21.08.00 : now uses xadGetHookAccess instead of own image copy
 1.11  11.11.00 : fixed Enforcer hit, added USESECTORLABELS
 1.12  02.12.01 : added disk-input support
*/

#include <proto/xadmaster.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <exec/memory.h>
#include <devices/trackdisk.h>
#include <dos/dosasl.h>
#include <dos/filehandler.h>
#include <utility/hooks.h>
#include "SDI_version.h"
#include "SDI_compiler.h"
#define SDI_TO_ANSI
#include "SDI_ASM_STD_protos.h"

struct xadMasterBase *   xadMasterBase = 0;
struct DosLibrary *	 DOSBase = 0;
struct ExecBase *	 SysBase  = 0;

#define PARAM	"FROM/A,TO,LOWCYL/N,HIGHCYL/N,ENTRY/N,PASSWORD,SAVETEXTS/K," \
		"NE=NOEXTERN/S,INFO=LIST/S,SHOWTEXTS/S,OW=OVERWRITE/S,"	\
		"IG=IGNOREGEOMETRY/S,FORMAT/S,DIMG=DISKIMAGE/S,NAI=NOASKINSERT/S," \
		"USL=USESECTORLABELS/S"

struct Args {
  STRPTR from;
  STRPTR to;
  LONG * lowcyl;
  LONG * highcyl;
  LONG * entry;
  STRPTR password;
  STRPTR savetexts;
  ULONG  noextern;
  ULONG  info;
  ULONG  showtexts;
  ULONG  overwrite;
  ULONG  ignoregeometry;
  ULONG  format;
  ULONG  diskimage;
  ULONG  noaskinsert;
  ULONG  usesectorlabels;
};

ASM(ULONG) SAVEDS progrhook(REG(a0, struct Hook *),
  REG(a1, struct xadProgressInfo *));
struct Hook prhook = {{0,0},(ULONG (*)()) progrhook, 0, 0};
void ShowTexts(struct xadTextInfo *ti);
void SaveTexts(struct xadTextInfo *ti, STRPTR name);
LONG WriteDisk(struct Args *);
LONG AskInsertDisk(STRPTR);

ULONG start(void)
{
  ULONG ret = RETURN_FAIL;
  struct DosLibrary *dosbase;

  SysBase = (*((struct ExecBase **) 4));
  { /* test for WB and reply startup-message */
    struct Process *task;
    if(!(task = (struct Process *) FindTask(0))->pr_CLI)
    {
      WaitPort(&task->pr_MsgPort);
      Forbid();
      ReplyMsg(GetMsg(&task->pr_MsgPort));
      return RETURN_FAIL;
    }
  }

  if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  {
    LONG err = 0;
    struct xadMasterBase *xadmasterbase;

    DOSBase = dosbase;
    if((xadmasterbase = (struct xadMasterBase *) OpenLibrary("xadmaster.library", 11)))
    {
      LONG def = 1;
      struct Args args;
      struct RDArgs *rda;
      
      memset(&args, 0 , sizeof(struct Args));
      args.entry = &def;

      xadMasterBase = xadmasterbase;
      if((rda = ReadArgs(PARAM, (LONG *) &args, 0)))
      {
	if(args.to || args.info)
	{
	  struct xadArchiveInfo *ai;
	
          if(args.from[strlen(args.from)-1] == ':')
          {
	    UBYTE r;

	    ret = 0;
            Printf("Device input. Press <I> to read and write disks as image: ");
            Flush(Output());
            SetMode(Input(), TRUE);
            r = FGetC(Input());
            SetMode(Input(), FALSE);
            if(r == 'i' || r == 'I')
	      err = WriteDisk(&args);
	    else
              Printf("\n");
          }
	  else if(args.diskimage)
	  {
	    ret = 0;
            err = WriteDisk(&args);
	  }
	  else if((ai = (struct xadArchiveInfo *)
	  xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0)))
	  {
	    if(!(err = xadGetInfo(ai, XAD_INFILENAME, args.from,
	    XAD_NOEXTERN, args.noextern, args.password ? XAD_PASSWORD :
	    TAG_IGNORE, args.password, TAG_DONE)))
	    {
	      if(ai->xai_Flags & XADAIF_FILECORRUPT)
	        Printf("!!! The archive file has some corrupt data. !!!\n");
	      if(args.info)
	      {
	        struct xadDiskInfo *xdi;
	        Printf("ArchiverName:   %s\n", ai->xai_Client->xc_ArchiverName);

	        xdi = ai->xai_DiskInfo;
	        while(xdi)
	        {
	          LONG a;

		  if(xdi->xdi_EntryNumber != 1 || xdi->xdi_Next)
	            Printf("\nEntry:          %ld\n", xdi->xdi_EntryNumber);
	          Printf("EntryInfo:      %s\n", xdi->xdi_EntryInfo ? xdi->xdi_EntryInfo : "<none>");
	          Printf("SectorSize:     %ld\n", xdi->xdi_SectorSize);
	          Printf("Sectors:        %ld\n", xdi->xdi_TotalSectors);
	          Printf("Cylinders:      %ld\n", xdi->xdi_Cylinders);
	          Printf("CylSectors:     %ld\n", xdi->xdi_CylSectors);
	          Printf("Heads:          %ld\n", xdi->xdi_Heads);
	          Printf("TrackSectors:   %ld\n", xdi->xdi_TrackSectors);
	          Printf("LowCyl:         %ld\n", xdi->xdi_LowCyl);
	          Printf("HighCyl:        %ld\n", xdi->xdi_HighCyl);
	          a = xdi->xdi_Flags & (XADDIF_CRYPTED|XADDIF_SECTORLABELS);
	          if(a)
	          {
	            Printf("Flags:          ");
	            if(a & XADDIF_CRYPTED)
	            {
	              a ^= XADDIF_CRYPTED;
	              Printf("encrypted%s", a ? ", " : "\n");
	            }
	            if(a & XADDIF_SECTORLABELS)
	            {
	              a ^= XADDIF_SECTORLABELS;
	              Printf("has SectorLabels%s", a ? ", " : "\n");
	            }
	          }
	          if(xdi->xdi_TextInfo)
	          {
	            STRPTR a;
	            struct xadTextInfo *ti;

	            for(ti = xdi->xdi_TextInfo; ti; ti = ti->xti_Next)
	            {
		      a = "TextInfo";
		      if(ti->xti_Flags & XADTIF_BANNER)
		        a = "Banner";
		      else if(ti->xti_Flags & XADTIF_FILEDIZ)
		        a = "DIZ-Text";

		      if(ti->xti_Size && ti->xti_Text)
	                Printf("There is a %s with size %ld.\n", a, ti->xti_Size);
	              else if(ti->xti_Flags & XADTIF_CRYPTED)
	                Printf("There is a crypted %s.\n", a);
	              else
	                Printf("There is an empty %s.\n", a);
	            }
	            if(args.showtexts)
	              ShowTexts(xdi->xdi_TextInfo);
	            if(args.savetexts)
	              SaveTexts(xdi->xdi_TextInfo, args.savetexts);
	          }
	          xdi = xdi->xdi_Next;
	        }
	        ret = 0;
	      }
	      else
	      {
		struct xadDeviceInfo *dvi = 0;

	        if(args.to[strlen(args.to)-1] == ':' && stricmp(args.to, "NIL:"))
	        {
	          if((dvi = (struct xadDeviceInfo *)
	          xadAllocObjectA(XADOBJ_DEVICEINFO, 0)))
	          {
	            args.to[strlen(args.to)-1] = 0; /* strip ':' */
	            dvi->xdi_DOSName = args.to;
	          }
	          else
	            err = XADERR_NOMEMORY;
	        }
	        if(args.showtexts || args.savetexts)
	        {
	          struct xadDiskInfo *xdi = ai->xai_DiskInfo;

	          while(xdi && xdi->xdi_EntryNumber < *args.entry)
	            xdi = xdi->xdi_Next;
	          if(xdi && xdi->xdi_TextInfo)
	          {
	            if(args.showtexts)
	              ShowTexts(xdi->xdi_TextInfo);
	            if(args.savetexts)
	              SaveTexts(xdi->xdi_TextInfo, args.savetexts);
	          }
	        }
		if(dvi && !args.noaskinsert)
		  err = AskInsertDisk(args.to);

	        if(!err && !(err = xadDiskUnArc(ai, dvi ? XAD_OUTDEVICE :
	        XAD_OUTFILENAME, dvi ? (ULONG) dvi : (ULONG) args.to,
	        XAD_ENTRYNUMBER, *args.entry, args.lowcyl ?
	        XAD_LOWCYLINDER : TAG_IGNORE, args.lowcyl ? *args.lowcyl :
	        0, args.highcyl ? XAD_HIGHCYLINDER : TAG_IGNORE,
	        args.highcyl ? *args.highcyl : 0, XAD_OVERWRITE,
	        args.overwrite, XAD_IGNOREGEOMETRY, args.ignoregeometry,
	        XAD_FORMAT, args.format, XAD_VERIFY, TRUE, XAD_USESECTORLABELS,
	        args.usesectorlabels, XAD_PROGRESSHOOK, &prhook, TAG_DONE)))
	          ret = 0;
	        if(dvi)
	          xadFreeObjectA(dvi, 0);
	      }
	      xadFreeInfo(ai);
	    } /* xadGetInfo */
	    else if(err == XADERR_FILETYPE)
	    {
	      UBYTE r;

	      ret = 0;
              Printf("Unknown type. Press <I> to write it to disks as image: ");
              Flush(Output());
              SetMode(Input(), TRUE);
              r = FGetC(Input());
              SetMode(Input(), FALSE);
              if(r == 'i' || r == 'I')
	        err = WriteDisk(&args);
	      else
                Printf("\n");
	    }

	    xadFreeObjectA(ai, 0);
          } /* xadAllocObject */
        }
        else
          SetIoErr(ERROR_REQUIRED_ARG_MISSING);

        FreeArgs(rda);
      } /* ReadArgs */

      if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
        SetIoErr(ERROR_BREAK);

      if(err)
	Printf("An error occured: %s\n", xadGetErrorText(err));
      else if(ret)
        PrintFault(IoErr(), 0);

      CloseLibrary((struct Library *) xadmasterbase);
    } /* OpenLibrary xadmaster */
    else
      Printf("Could not open xadmaster.library\n");
    CloseLibrary((struct Library *) dosbase);
  } /* OpenLibrary dos */
  return ret;
}

ASM(ULONG) SAVEDS progrhook(REG(a0, struct Hook *hook),
REG(a1, struct xadProgressInfo *pi))
{
  ULONG ret = 0;

  switch(pi->xpi_Mode)
  {
  case XADPMODE_ASK:
    {
      UBYTE r;
      if(pi->xpi_Status & XADPIF_OVERWRITE)
      {
        Printf("File already exists, overwrite? (Y|S|\033[1mN\033[0m): ");
        Flush(Output());
        SetMode(Input(), TRUE);
        r = FGetC(Input());
        if(r == 'Y' || r == 'y')
          ret |= XADPIF_OVERWRITE;
        else if(r == 'S' || r == 's')
          ret |= XADPIF_SKIP;
        SetMode(Input(), FALSE);
      }
      if(pi->xpi_Status & XADPIF_IGNOREGEOMETRY)
      {
        Printf("\r\033[KDrive geometry not correct, ignore? (Y|S|\033[1mN\033[0m): ");
        Flush(Output());
        SetMode(Input(), TRUE);
        r = FGetC(Input());
        if(r == 'Y' || r == 'y')
          ret |= XADPIF_IGNOREGEOMETRY;
        else if(r == 'S' || r == 's')
          ret |= XADPIF_SKIP;
        SetMode(Input(), FALSE);
      }
    }
    break;
  case XADPMODE_PROGRESS:
    {
      if(!pi->xpi_DiskInfo)
        Printf("\r\033[KWrote %ld bytes", pi->xpi_CurrentSize);
      else if(pi->xpi_DiskInfo->xdi_Flags & (XADDIF_NOCYLINDERS|XADDIF_NOCYLSECTORS))
      {
        Printf("\r\033[KWrote %ld of %ld bytes (%ld/%ld sectors)",
        pi->xpi_CurrentSize, pi->xpi_DiskInfo->xdi_TotalSectors*
        pi->xpi_DiskInfo->xdi_SectorSize, pi->xpi_CurrentSize/
        pi->xpi_DiskInfo->xdi_SectorSize, pi->xpi_DiskInfo->xdi_TotalSectors);
      }
      else
      {
        ULONG numcyl, fullsize, curcyl, i;

        i = pi->xpi_DiskInfo->xdi_CylSectors *
            pi->xpi_DiskInfo->xdi_SectorSize;
        numcyl = pi->xpi_HighCyl+1-pi->xpi_LowCyl;
        fullsize = numcyl * i;
        curcyl = pi->xpi_CurrentSize/i;

        Printf("\r\033[KWrote %ld of %ld bytes (%ld/%ld cylinders)",
        pi->xpi_CurrentSize, fullsize, curcyl, numcyl);
      }
      Flush(Output());
    }
    break;
  case XADPMODE_END: 
    if(pi->xpi_DiskInfo && (pi->xpi_DiskInfo->xdi_Flags & (XADDIF_NOCYLINDERS|XADDIF_NOCYLSECTORS)))
      Printf("\r\033[KWrote %ld bytes (%ld sectors)\n",
      pi->xpi_CurrentSize, pi->xpi_DiskInfo->xdi_TotalSectors);
    else
      Printf("\r\033[KWrote %ld bytes (%ld cylinders)\n",
      pi->xpi_CurrentSize, pi->xpi_HighCyl+1-pi->xpi_LowCyl);
    break;
  case XADPMODE_ERROR: Printf("\r\033[K");
    break;
  }

  if(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)) /* clear ok flag */
    ret |= XADPIF_OK;

  return ret;
}

void ShowTexts(struct xadTextInfo *ti)
{
  ULONG i = 1, j;
  BPTR fh;
  STRPTR a;

  fh = Output();

  while(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C) && ti)
  {
    if(ti->xti_Size && ti->xti_Text)
    {
      Printf("»»»» TEXTINFO %ld ««««\n", i);
      a = ti->xti_Text;
      for(j = 0; !(SetSignal(0L,0L) & SIGBREAKF_CTRL_C) && j < ti->xti_Size; ++j)
      {
        if(isprint(*a) || *a == '\n' || *a == '\t' || *a == '\033')
          FPutC(fh, *a);
        else
          FPutC(fh, '.');
        ++a;
      }
      if(*(--a) != '\n')
        FPutC(fh, '\n');
    }
    ti = ti->xti_Next;
    ++i;
  }
}

void SaveTexts(struct xadTextInfo *ti, STRPTR name)
{
  UBYTE namebuf[256];
  ULONG i = 1;
  BPTR fh;
  LONG err = 0;

  while(!(SetSignal(0L,0L) & SIGBREAKF_CTRL_C) && ti && !err)
  {
    if(ti->xti_Size && ti->xti_Text)
    {
      sprintf(namebuf, "%s.%ld", name, i);
      if((fh = Open(namebuf, MODE_NEWFILE)))
      {
        if(Write(fh, ti->xti_Text, ti->xti_Size) != ti->xti_Size)
          ++err;
        Close(fh);
      }
      else
        ++err;
    }
    ti = ti->xti_Next;
    ++i;
  }
  if(err)
    Printf("Failed to save information texts.\n");
}

LONG WriteDisk(struct Args *args)
{
  LONG err = 0;
  struct xadArchiveInfo *ai;
  struct xadDeviceInfo *dvi = 0, *indvi = 0;

  if((ai = (struct xadArchiveInfo *) xadAllocObjectA(XADOBJ_ARCHIVEINFO, 0)))
  {
    if(args->from[strlen(args->from)-1] == ':')
    {
      if((indvi = (struct xadDeviceInfo *) xadAllocObjectA(XADOBJ_DEVICEINFO, 0)))
      {
        args->from[strlen(args->from)-1] = 0; /* strip ':' */
        indvi->xdi_DOSName = args->from;
      }
      else
        err = XADERR_NOMEMORY;
    }
    if(args->to[strlen(args->to)-1] == ':' && stricmp(args->to, "NIL:"))
    {
      if((dvi = (struct xadDeviceInfo *) xadAllocObjectA(XADOBJ_DEVICEINFO, 0)))
      {
        args->to[strlen(args->to)-1] = 0; /* strip ':' */
        dvi->xdi_DOSName = args->to;
      }
      else
        err = XADERR_NOMEMORY;
    }

    if(dvi && !args->noaskinsert)
      err = AskInsertDisk(args->to);

    if(!err)
    {
      struct TagItem ti[3];
      
      if(indvi) {ti[0].ti_Tag = XAD_INDEVICE; ti[0].ti_Data = (ULONG) indvi;}
      else      {ti[0].ti_Tag = XAD_INFILENAME; ti[0].ti_Data = (ULONG) args->from;}
      if(dvi)   {ti[1].ti_Tag = XAD_OUTDEVICE; ti[1].ti_Data = (ULONG) dvi;}
      else      {ti[1].ti_Tag = XAD_OUTFILENAME; ti[1].ti_Data = (ULONG) args->to;}
      ti[2].ti_Tag = TAG_DONE;
      
      if(!(err = xadGetHookAccess(ai, XAD_OVERWRITE, args->overwrite,
      XAD_IGNOREGEOMETRY, args->ignoregeometry, XAD_FORMAT, args->format, XAD_VERIFY,
      TRUE, XAD_PROGRESSHOOK, &prhook, TAG_MORE, ti)))
      {
        err = xadHookAccess(XADAC_COPY, ai->xai_InSize, 0, ai);
        xadFreeHookAccess(ai, err ? XAD_WASERROR : TAG_DONE, err, TAG_DONE);
      }
      if(dvi)
        xadFreeObjectA(dvi, 0);
      if(indvi)
        xadFreeObjectA(indvi, 0);
    }
    else
      err = XADERR_NOMEMORY;
    xadFreeObjectA(ai,0);
  }
  else
    err = XADERR_NOMEMORY;

  return err;
}

LONG AskInsertDisk(STRPTR name)
{
  UBYTE r;
  STRPTR b;
		  
  for(b = name; *b; ++b)
    *b = toupper(*b);

  Printf("\r\033[KInsert disk into %s: and press <ENTER> (any other key to abort): ", name);
  Flush(Output());
  SetMode(Input(), TRUE);
  r = FGetC(Input());
  SetMode(Input(), FALSE);
  if(r != '\r' && r != '\n')
  {
    Printf("\n");
    return XADERR_BREAK;
  }
  return 0;
}
