char *stx[]={
  "  Pcopy 1.0   1 April 88 by D.Reisig",
  "  Pcopy 1.0 is PD",
  "  Made in Holland!"
};

#include "pcopy.h"

/*
 *  Since no commandline parameters are processed, we may prevent
 *  a call to exit(), by defining _main() here.
 *
 *  We do a call to Init() which sets up all I/O, check patching
 *  sceentitles, loop copy function and if desired, a call to Quit()
 *  which returns all memory and closes all I/O.
 */

_main()
{
  register struct IntuiMessage *Mesg;
  register int  message;

  if (!Init()){
    SetWindowTitles(WT,-1,stx[0]);
    SetWindowTitles(W1,-1,stx[1]);
    SetWindowTitles(W0,-1,stx[2]);
    ConPutStr(IOHist,"\x9b0 p\n\n\n\n\n\n ");
    while(1){
      MainLoop();
      Mesg=GetMsg(WS->UserPort);
      if (Mesg){
        message=Mesg->Class;
        ReplyMsg(Mesg);
        if (message==CLOSEWINDOW){
          if (!Request("    Pcopy","","stay","away")) break;
        }
      }
    }
  }
  Quit();   /*  return and close everything  */
  return(0);
}   /*  _main  */


/*
 *  See if somthing happend in the command window, check drives and
 *  set "Now in .." windows. Finally check if a start copy condition
 *  became valid.
 */

void MainLoop()
{
  static int d0cnt=-1, d1cnt=-1;

  CheckControl(1);  /*  some user command? */
  if (GetDrive(D0)){    /*  disk is present */
    if (d0cnt!=D0->ChgNum){
      ShDName(W0,D0->VolName);
      d0cnt=D0->ChgNum;
    }
  } else{               /*  disk is absent */
    ShDName(W0,0);
    EnAuto=-1;  /*  enable auto start */
  }
  if (GetDrive(D1)){
    if (d1cnt!=D1->ChgNum){
      ShDName(W1,D1->VolName);
      d1cnt=D1->ChgNum;
    }
  } else{
    ShDName(W1,0);
    EnAuto=-1;
  }
  CheckCopy();
}  /*  MainLoop()  */
 

/*
 *  React upon windowsizing of the history window.
 *  The gadgets are scanned and some minor (mostly intuition-patches)
 *  actions are taken. Did I do it wrong or isn't intuition that good?
 */
 
void CheckControl(flag)
short flag;
{
  static short Excla=0, HistH=HHS;
  
  if (HistH!=WH->Height){
    HistH=WH->Height;
    RefHist();
  }           /* NowCopy tells us which picture presently is displayed */
  if (GAuto.Flags&SELECTED){
    if (NowCopy){
      RemoveGadget(WS,&GCopy);       /* remove manual start */
      GCopy.Flags&=~SELECTED;        /* no surprises when changed back! */
      DrawImage(WS->RPort,&IMBlnk,16,4*GGH);  /* clear area */
      PrintIText(WS->RPort,&ITCCon,16,4*GGH+2);
      AddGadget(WS,&GUnRd,-1);
      AddGadget(WS,&GNDOS,-1);
      RefreshGadgets(&GUnRd,WS,0);
      NowCopy=0;
    }
  } else{
    if (!NowCopy){
      RemoveGadget(WS,&GNDOS);
      RemoveGadget(WS,&GUnRd);
      DrawImage(WS->RPort,&IMBlnk,16,4*GGH);
      AddGadget(WS,&GCopy,-1);
      RefreshGadgets(&GCopy,WS,0);
      NowCopy=1;
    }
  }
  if ((GAuto.Flags&SELECTED)   /* check for dangerous condition */
    &&!(GNDOS.Flags&SELECTED)
    &&!(GUnRd.Flags&SELECTED)){
    if (!Excla){               /* display alarming exclamation */
      DrawImage(WS->RPort,&IMEcla,160,5*GGH);
      Excla=1;
    }
  }else if (Excla){            /* or remove it */
    DrawImage(WS->RPort,&IMEcOf,160,5*GGH);
    Excla=0;
  }
  if (GD0D1.Flags&SELECTED){   /* set colors and arrow according to  */
    ITDr0.FrontPen=PenB;       /* selected direction  */
    ITDr1.FrontPen=PenA;       /* if selected, change state and deselect  */
    GD0D1.GadgetRender=ArrowB; /* again  */
    GD0D1.SelectRender=ArrowA;
    ArrowA=GD0D1.GadgetRender;
    ArrowB=GD0D1.SelectRender;
    PenA=ITDr0.FrontPen;
    PenB=ITDr1.FrontPen;
    GD0D1.Flags&=~SELECTED;    /* if done deselect gadget  */
    RefOneGadget(&GD0D1,WS,0);
  }
                               /* refresh (deghost) window titles */
  if (!flag){                  /* track window with or without depth g's */
    TxT[SHORTTIME]=' ';
    DrawImage(WT->RPort,&IMTTTL,0,0);
    DrawImage(WT->RPort,&IMTTTL,0,9);
    PrintIText(WT->RPort,&ITT,4,1);
  } else{
    TxT[SHORTTIME]='\0';
    PrintIText(WT->RPort,&ITT,4,1);
  }
  PrintIText(WH->RPort,&ITH,4,1);
  PrintIText(W1->RPort,&IT1,4,1);
  PrintIText(W0->RPort,&IT0,4,1);
} /*  CheckControl  */


/*
 *  Gather all possible info of a drive and return disk presence.
 */

GetDrive(drive)
register DrInfo *drive;
{
  register char *name=(char *)(&CopyBuffer[ROOT_VOLNAME]);
  register int error;

  drive->Req.io_Command=TD_CHANGESTATE;
  DoIO(&drive->Req);                            /* is there a disk? */
  if (drive->Present=~drive->Req.io_Actual){
    drive->Req.io_Command=TD_CHANGENUM;
    DoIO(&drive->Req);                      /* did we check it already? */
    if (drive->ChgNum!=drive->Req.io_Actual){    /* no ..  */
      drive->ChgNum=drive->Req.io_Actual;
      drive->DOSDisk=0;
      drive->DOSBoot=0;
      drive->Readable=0;
      error=ReadSec(drive,0,CopyBuffer);    /*  readable?  */
      if (!error||(error!=TDERR_NoSecHdr)){
        drive->Readable=-1;
        if (!error&&((*CopyBuffer==0x444f5300)||(*CopyBuffer==0x4b49434b))){
          drive->DOSBoot=-1;                /*  DOS\0  or KICK  */
          if (!ReadSec(drive,ROOTSECTOR,CopyBuffer)
          &&!CheckSum(CopyBuffer)&&(*name<VOLNAMESIZE)){
            drive->DOSDisk=-1;              /*  Truly Amiga-DOS  */
            strncpy(drive->VolName,name+1,*name);
            drive->VolName[*name]='\0';
          } else  strcpy(drive->VolName,"\x0eNear DOS");
        } else  strcpy(drive->VolName,"\x0eNon DOS");
      } else  strcpy(drive->VolName,"\x0eUnreadable");
      MotorOff(drive);
    }     /* end of other chgnum */
  } else  drive->Present=0;
  return ((int)drive->Present);
} /*  GetDrive()  */



ReadSec(drive,sector,buffer)
register DrInfo *drive; SHORT sector; char *buffer;
{
  drive->Req.io_Command=CMD_READ;
  drive->Req.io_Data=(APTR)buffer; 
  drive->Req.io_Length=TD_SECTOR;
  drive->Req.io_Offset=sector*TD_SECTOR;
  DoIO(&drive->Req);
  return((int)drive->Req.io_Error);
}


/*
 *  The checksum is OK if we add all longwords in a sector and its
 *  result is zero.
 */

CheckSum(Sector)
register ULONG *Sector;
{
register  ULONG Sum;
register  short i;
  for (Sum=0,i=0;i<TD_SECTOR/4;++i) Sum+=Sector[i];
  return(Sum);
}


/*
 *  This routine does the diskcopy. If succesful, a new title is dispayed
 *  in the drive's window and the name is pushed into the history
 *  buffer/window. A short sound signal put out.
 *  If an error occured, a requester is set up to ask a decision from
 *  the user. If aborted, a special message is displayed in the drive's
 *  window, and the routine is left.
 */

DiskCopy(Source,Dest,mod)
register DrInfo *Source, *Dest; short mod;
{
  register int cylinder, byte, error;
  char Errbuf0[64], Errbuf1[64];
  char *name=(char *)(&CopyBuffer[ROOT_VOLNAME]);

  OffGadget(&GD0D1,WS,0);     /*  disable source/dest selection  */
  GCopy.Flags|=GADGDISABLED;  /*  disable manual start  */
  if (NowCopy){               /*  Intuition handles it bad - trick needed */
    DrawImage(WS->RPort,&IMBlnk,16,4*GGH);
    RefOneGadget(&GCopy,WS,0);
  }
  D0->Count=D0->ChgNum;       /*  set up for Extended Command  */
  D1->Count=D1->ChgNum;

  if (mod==WAIT){             /*  Special case, see manual  */
    DisplayBeep(IntuitionBase->ActiveScreen);
    Delay(100);
  }

          /*  This is the cylinder counting loop  */
  for (cylinder=0,byte=0;cylinder<NUMCYLS;++cylinder,byte+=BPCYL){

    DrawImage(WT->RPort,&IMTmOn,cylinder*4,12);  /*  track window  */

ReadSource:
    if (error=DIOCyl(Source,byte,CopyBuffer,ETD_READ)){
      stci_d(stpcpy(Errbuf0,"Source read error # "),error);
      stci_d(stpcpy(Errbuf1,"On cylinder # "),cylinder);
      Beep();
      if (Request(Errbuf0,Errbuf1,"retry","abort")) goto ReadSource;
      else break;
    }

    if (cylinder==ROOTCYL){
      ChangeRoot(CopyBuffer);
      strncpy(Dest->VolName,name+1,*name);
      Dest->VolName[*name]='\0';
      Dest->DOSDisk=-1;
    }

WriteDestination:
    if (error=DIOCyl(Dest,byte,CopyBuffer,ETD_FORMAT)){
      stci_d(stpcpy(Errbuf0,"Destination write error # "),error);
      stci_d(stpcpy(Errbuf1,"On cylinder # "),cylinder);
      Beep();
      if (Request(Errbuf0,Errbuf1,"retry","abort")) goto WriteDestination;
      else break;
    }

    if (GVerf.Flags&SELECTED){
      Dest->Req.io_Command=ETD_CLEAR;
      DoIO(&Dest->Req);
      if (error=DIOCyl(Dest,byte,CompBuffer,ETD_READ)){
        stci_d(stpcpy(Errbuf0,"Destination read error # "),error);
        stci_d(stpcpy(Errbuf1,"On cylinder # "),cylinder);
        Beep();
        if (Request(Errbuf0,Errbuf1,"retry","abort")) goto WriteDestination;
        else break;
      }
  
      if (Compare(CopyBuffer,CompBuffer)){
        stci_d(stpcpy(Errbuf1,"On cylinder # "),cylinder);
        Beep();
        if (Request("Verification error",Errbuf1,"retry","abort"))
          goto WriteDestination;
        else break;
      }
    }
  } /* end of cylinder count loop */ /* copy done */

  DrawImage(WT->RPort,&IMTmOf,0,12);  /*  clear track window  */
  RefreshWindowFrame(WT);             /*  depth gadgets back  */
  if (!error){
    ConPutStr(IOHist,"\n ");
    ConPutStr(IOHist,Dest->VolName);
    EnterHist(Dest->VolName);
    Beep(0);
  } else{
    strcpy(Dest->VolName,"\x0eCopy unsuccesful");
    Dest->DOSDisk=0;
  }
  ShDName(W0,D0->VolName);
  ShDName(W1,D1->VolName);
  MotorOff(Source);
  MotorOff(Dest);
  OnGadget(&GD0D1,WS,0);              /*  enable direction selection  */
  GCopy.Flags&=~GADGDISABLED;
  if (NowCopy){                       /*  that intuition patch again */
    DrawImage(WS->RPort,&IMBlnk,16,4*GGH);
    RefOneGadget(&GCopy,WS,0);
  }
  return(error);
}  /*  DiskCopy  */


/*
 *  Read or write one cylinder.
 */

DIOCyl(drive,byte,buffer,cmd)
register  DrInfo *drive; ULONG byte; char *buffer; int cmd;
{
  drive->Req.io_Command=cmd;
  drive->Req.io_Data=(APTR)buffer; 
  drive->Req.io_Length=BPCYL;
  drive->Req.io_Offset=byte;
  SendIO(&drive->Req);
  CheckControl(0);       /* see if things did change (without track dg's) */
  WaitIO(&drive->Req);
  return((int)drive->Req.io_Error);
}


Compare(a,b)
register  ULONG *a, *b;
{
  register int i;
  for (i=0;i<(BPCYL/4);++i) if (*(a++)!=*(b++)) return(25); /* or 42 iyl */
  return(0);
}


void MotorOff(drive)
register DrInfo *drive;
{
  drive->Req.io_Command=TD_MOTOR;
  drive->Req.io_Length=0;
  DoIO(&drive->Req);
}


/*
 *  Set present time and date in the creation and last changed field
 *  If the bitmap is -1, it is set to +1. A new checksum is calculated.
 */

void ChangeRoot(sector)
register ULONG *sector;
{
register  ULONG *ptr, sum, i;

  DateStamp(&sector[ROOT_ALTDAYS]);
  sector[ROOT_CRTDAYS]=sector[ROOT_ALTDAYS];
  sector[ROOT_CRTMINS]=sector[ROOT_ALTMINS];
  sector[ROOT_CRTTICKS]=sector[ROOT_ALTTICKS];
  if (sector[ROOT_BMFFLAG]==-1) sector[ROOT_BMFFLAG]=1;

  for (ptr=sector,sector[ROOT_CHKSUM]=0,sum=0,i=0;i<LPSECTOR;++i)
    sum+=*(ptr++);
  sector[ROOT_CHKSUM]=0-sum;
}	/*  ChangeRoot()  */    


/*
 *  First see to get two drives. Then init (alloc) the DrInfo-structures,
 *  set drive names on their places (Now in.. windows and direction g's),
 *  Call OpenUser(), which opens the windows and get data buffers.
 */

Init(){
  register short error=0;

  if ((IntuitionBase=(struct IntuitionBase *)
      OpenLibrary("intuition.library",0))==NULL)  return(-1);

  if (!AskDrives()){
    if (!(D0=OpenDrive(DriveName[0],DriveNr[0])))  ++error;
    if (!(D1=OpenDrive(DriveName[1],DriveNr[1])))  ++error;
  } else ++error;
  if (error){
    Request("Pcopy did not get","two drives","OK","OK");
    return(-1);
  }
  ITDr0.IText=D0->DevName;
  strcpy(&Tx0[9],D0->DevName);
  Tx0[13]=' ';
  ITDr1.IText=D1->DevName;
  strcpy(&Tx1[9],D1->DevName);
  Tx1[13]=' ';
  error=OpenUser();
  if (!(CopyBuffer=(ULONG *)AllocMem(BPCYL,MEMF_CHIP))) ++error;
  if (!(CompBuffer=(ULONG *)AllocMem(BPCYL,MEMF_CHIP))) ++error;
  if (!(Hist=(char *)AllocMem(MHIST*MVOLNAME,PUBCLR))) ++error;
  if (error)  Request("Pcopy did not get","enough memory","OK","OK");
  return(error);
}	/*  Init()  */


/* 
 *  Give it all back.
 */
void Quit()
{
  CloseUser();
  if (D0)  CloseDrive(D0);
  if (D1)  CloseDrive(D1);
  if (Hist)        FreeMem(Hist,MHIST*MVOLNAME);
  if (CopyBuffer)  FreeMem(CopyBuffer,BPCYL);
  if (CompBuffer)  FreeMem(CompBuffer,BPCYL);
  if (IntuitionBase) CloseLibrary(IntuitionBase);
}


/*
 *  Allocate space for the structure, inhibit DOS, setup trackdisk port
 *  and open trackdisk.
 */

DrInfo *OpenDrive(DrName,Unit)
char *DrName; int Unit;
{
  register  DrInfo *NewDr=0;

  if (NewDr=(DrInfo *)AllocMem(sizeof(DrInfo),PUBCLR)){
    NewDr->DevName=DrName;
    NewDr->DOSInhbt=dos_packet(DeviceProc(DrName),ACTION_INHIBIT,DOSTRUE);
    NewDr->Req.io_Message.mn_Node.ln_Type=NT_MESSAGE;
    NewDr->Req.io_Message.mn_Node.ln_Pri=0;
    if (NewDr->Req.io_Message.mn_ReplyPort=CreatePort(0,0)){
      if (!(NewDr->Device=OpenDevice(TD_NAME,Unit,&NewDr->Req,1))){
      }
    }
  }
  return(NewDr);
}	/*  OpenDrive()  */


/*
 *  Close trackdisk device and get rid of memory and ports involved.
 */

void CloseDrive(drive)
register  DrInfo *drive;
{
  if (!drive->Device){
    MotorOff(drive);
    CloseDevice(&drive->Req);
  }
  if (drive->Req.io_Message.mn_ReplyPort)
    DeletePort(drive->Req.io_Message.mn_ReplyPort);
  FreeMem(drive,sizeof(DrInfo));
  dos_packet(DeviceProc(drive->DevName),ACTION_INHIBIT,DOSFALSE);
}   /*  CloseDrive  */


/*
 *  Check if a start-copy condition occured. If so call the copy routine.
 */

void CheckCopy()
{
  register DrInfo *Source, *Dest; register short DoCopy;
  if (D0->Present&&D1->Present){  /* determine direction  */
    if (ITDr1.FrontPen==DESTCOLOR){
      Source=D0;
      Dest=D1;
    } else{
      Source=D1;
      Dest=D0;
    }
    DoCopy=0;  /* three values: FALSE/WAIT/NOWAIT  */
    if ((GAuto.Flags&SELECTED)&&EnAuto){
      if (GUnRd.Flags&SELECTED){
        if (!Dest->Readable) DoCopy=NOWAIT;
      } else if (GNDOS.Flags&SELECTED){
        if (!Dest->DOSBoot) DoCopy=NOWAIT;
      } else  DoCopy=(Dest->DOSDisk)?WAIT:NOWAIT;
    } else if (GCopy.Flags&SELECTED){
      GCopy.Flags&=~SELECTED;
      DrawImage(WS->RPort,&IMBlnk,16,4*GGH);
      RefOneGadget(&GCopy,WS,0);
      DoCopy=NOWAIT;
    }
    if (DoCopy)  DiskCopy(Source,Dest,DoCopy);
    EnAuto=0;
  }
}


/*
 *  Show disk name in Now in ..  window, or clear it
 */

void ShDName(W,text)
struct Window *W; register char *text;
{
static struct IntuiText ITDiskName={ 1,0,JAM2,0,0,0,0,0 };
static struct Image IMDBlank={ 0,0,WD0-4,8,0,0,0,0,0 };
  DrawImage(W->RPort,&IMDBlank,2,11);
  if (text){
    if (*text=='\x0e'){
      ITDiskName.FrontPen=2;
      ++text;
    } else  ITDiskName.FrontPen=1;
    ITDiskName.IText=text;
    PrintIText(W->RPort,&ITDiskName,12,11);
  }
}


/*  end of pcopy.c  */
