/*
 * Copyright (c) 1992-1999 Amiga, Inc.
 * 
 * This example is provided in electronic form by Amiga, Inc. for 
 * use with the "Amiga ROM Kernel Reference Manual: Devices", 3rd Edition, 
 * published by Addison-Wesley (ISBN 0-201-56775-X).
 * 
 * The "Amiga ROM Kernel Reference Manual: Devices" contains additional 
 * information on the correct usage of the techniques and operating system 
 * functions presented in these examples.  The source and executable code 
 * of these examples may only be distributed in free electronic form, via 
 * bulletin board or as part of a fully non-commercial and freely 
 * redistributable diskette.  Both the source and executable code (including 
 * comments) must be included, without modification, in any copy.  This 
 * example may not be published in printed form or distributed with any
 * commercial product.  However, the programming techniques and support
 * routines set forth in these examples may be used in the development
 * of original executable software products for Amiga computers.
 * 
 * All other rights reserved.
 * 
 * This example is provided "as-is" and is subject to change; no
 * warranties are made.  All use is at your own risk. No liability or
 * responsibility is assumed.
 *
 *****************************************************************************
 *
 *
 * Track_Copy.c
 *
 * This program does a track by track copy from one drive to another
 *
 * Compile with SAS C 5.10  LC -cfist -ms -v -L
 *
 * This program will only run from the CLI.  If started from
 * the Workbench, it will just exit...
 *
 * Usage:  Track_Copy  dfx dfy
 */


#include <exec/types.h>
#include <exec/memory.h>
#include <devices/trackdisk.h>
#include <dos/dosextens.h>

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>

#include <stdio.h>
#include <string.h>

#ifdef LATTICE
int CXBRK(void) { return(0); }     /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }  /* really */
#endif

#define	TRACK_SIZE	((LONG)(NUMSECS * TD_SECTOR))
/*
 * Turn the BUSY flag off/on for the drive
 * If onflag is TRUE, the disk will be marked as busy...
 *
 * This is to stop the validator from executing while
 * we are playing with the disks.
 */
VOID disk_busy(UBYTE *drive,LONG onflag)
{
struct StandardPacket *pk;
struct Process        *tsk;

    tsk=(struct Process *)FindTask(NULL);
    if (pk=AllocMem(sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))
    {
        pk->sp_Msg.mn_Node.ln_Name=(UBYTE *)&(pk->sp_Pkt);

        pk->sp_Pkt.dp_Link=&(pk->sp_Msg);
        pk->sp_Pkt.dp_Port=&(tsk->pr_MsgPort);
        pk->sp_Pkt.dp_Type=ACTION_INHIBIT;
        pk->sp_Pkt.dp_Arg1=(onflag ? -1L : 0L);

        PutMsg(DeviceProc(drive),(struct Message *)pk);
        WaitPort(&(tsk->pr_MsgPort));
        GetMsg(&(tsk->pr_MsgPort));
        FreeMem(pk,(long)sizeof(*pk));
    }
}

/*
 * This turns the motor off
 */
VOID Motor_Off(struct IOExtTD *disk)
{
    disk->iotd_Req.io_Length=0;
    disk->iotd_Req.io_Command=TD_MOTOR;
    DoIO((struct IORequest *)disk);
}

/*
 * This turns the motor on
 */
VOID	Motor_On(struct IOExtTD *disk)
{
    disk->iotd_Req.io_Length=1;
    disk->iotd_Req.io_Command=TD_MOTOR;
    DoIO((struct IORequest *)disk);
}

/*
 * This reads a track, reporting any errors...
 */
SHORT Read_Track(struct IOExtTD *disk,UBYTE *buffer,SHORT track)
{
SHORT All_OK=TRUE;

    disk->iotd_Req.io_Length=TRACK_SIZE;
    disk->iotd_Req.io_Data=(APTR)buffer;
    disk->iotd_Req.io_Command=CMD_READ;
    disk->iotd_Req.io_Offset=(ULONG)(TRACK_SIZE * track);
    DoIO((struct IORequest *)disk);
    if (disk->iotd_Req.io_Error)
    {
        All_OK=FALSE;
        printf("Error %u when reading track %d",disk->iotd_Req.io_Error,track);
    }
    return(All_OK);
}

/*
 * This writes a track, reporting any errors...
 */
SHORT Write_Track(struct IOExtTD *disk,UBYTE *buffer,SHORT track)
{
SHORT All_OK=TRUE;

    disk->iotd_Req.io_Length=TRACK_SIZE;
    disk->iotd_Req.io_Data=(APTR)buffer;
    disk->iotd_Req.io_Command=TD_FORMAT;
    disk->iotd_Req.io_Offset=(ULONG)(TRACK_SIZE * track);
    DoIO((struct IORequest *)disk);
    if (disk->iotd_Req.io_Error)
    {
        All_OK=FALSE;
        printf("Error %d when writing track %d",disk->iotd_Req.io_Error,track);
    }
    return(All_OK);
}

/*
 * This function finds the number of TRACKS on the device.
 * NOTE That this is TRACKS and not cylinders.  On a Two-Head
 * drive (such as the standard 3.5" drives) the number of tracks
 * is 160, 80 cylinders, 2-heads.
 */
SHORT FindNumTracks(struct IOExtTD *disk)
{
    disk->iotd_Req.io_Command=TD_GETNUMTRACKS;
    DoIO((struct IORequest *)disk);
    return((SHORT)disk->iotd_Req.io_Actual);
}

/*
 * This routine allocates the memory for one track and does
 * the copy loop.
 */
VOID Do_Copy(struct IOExtTD *diskreq0,struct IOExtTD *diskreq1)
{
UBYTE *buffer;
SHORT track;
SHORT All_OK;
SHORT NumTracks;

    if (buffer=AllocMem(TRACK_SIZE,MEMF_CHIP|MEMF_PUBLIC))
    {
        printf(" Starting Motors\r");
        Motor_On(diskreq0);
        Motor_On(diskreq1);
        All_OK=TRUE;

        NumTracks=FindNumTracks(diskreq0);

        for (track=0;(track<NumTracks) && All_OK;track++)
        {
            printf(" Reading track %d\r",track);

            if (All_OK=Read_Track(diskreq0,buffer,track))
            {
                printf(" Writing track %d\r",track);

                All_OK=Write_Track(diskreq1,buffer,track);
            }
        }
        if (All_OK) printf(" * Copy complete *");
        printf("\n");
        Motor_Off(diskreq0);
        Motor_Off(diskreq1);
        FreeMem(buffer,TRACK_SIZE);
    }
    else printf("No memory for track buffer...\n");
}

/*
 * Prompts the user to remove one of the disks.
 * Since this program makes an EXACT copy of the disks
 * AmigaDOS would get confused by them so one must be removed
 * before the validator is let loose.  Also, note that the
 * disks may NEVER be in drives on the SAME computer at the
 * SAME time unless one of the disks is renamed.  This is due
 * to a bug in the system.  It would normally be prevented
 * by a diskcopy program that knew the disk format and modified
 * the creation date by one clock-tick such that the disks would
 * be different.
 */
VOID Remove_Disks(VOID)
{
    printf("\nYou *MUST* remove at least one of the disks now.\n");
    printf("\nPress RETURN when ready\n");
    while(getchar()!='\n');
}

/*
 * Prompts the user to insert the disks.
 */
VOID Insert_Disks(char drive1[], char drive2[])
{
    printf("\nPlease insert source disk in %s\n",drive1);
    printf("\n          and destination in %s\n",drive2);
    printf("\nPress RETURN when ready\n");
    while(getchar()!='\n');
}

/*
 * Open the devices and mark them as busy
 */
VOID Do_OpenDevice(struct IOExtTD *diskreq0,struct IOExtTD *diskreq1, long unit[])
{
char drive1[] = "DFx:";  /* String for source drive */
char drive2[] = "DFx:";  /* String for destination drive */

    drive1[2] = unit[0]+ '0';  /* Set drive number for source */

    if (!OpenDevice(TD_NAME,unit[0],(struct IORequest *)diskreq0,0L))
    {


          disk_busy(drive1,TRUE);

          drive2[2] = unit[1]+ '0';  /* Set drive number for destination */

        if (!OpenDevice(TD_NAME,unit[1],(struct IORequest *)diskreq1,0L))
        {


            disk_busy(drive2,TRUE);

            Insert_Disks(drive1,drive2);
            Do_Copy(diskreq0,diskreq1);
            Remove_Disks();

            disk_busy(drive2,FALSE);
            CloseDevice((struct IORequest *)diskreq1);
        }
        else printf("Could not open %s\n",drive2);

        disk_busy(drive1,FALSE);
        CloseDevice((struct IORequest *)diskreq0);
    }
    else printf("Could not open %s\n",drive1);
}


SHORT ParseArgs(int argc, char **argv, long Unit[])

#define OKAY 1

{
int j=1, params = OKAY;
char *position[]={"First","Second"};

if (argc != 3)
    {
    printf("\nYou must specify a source and destination disk\n");
    return(!OKAY);
    }
else if (strcmp(argv[1],argv[2]) == 0)
         {
         printf("\nYou must specify different disks for source and destination\n");
         return(!OKAY);
         }
     else while (params == OKAY && j<3)
               {
               if (strnicmp(argv[j],"df",2)==0)
                   {
                   if (argv[j][2] >= '0' && argv[j][2] <= '3' && argv[j][3] == '\0')
                       {
                       Unit[j-1] = argv[j][2] - 0x30;
                       }
                   else
                       {
                       printf("\n%s parameter is wrong, unit number must be 0-3\n",position[j-1]);
                       params = !OKAY;
                       return(!OKAY);
                       }
                   }
               else
                   {
                   printf("\n%s parameter is wrong, you must specify a floppy device df0 - df3\n",position[j-1]);
                   params=!OKAY;
                   return(!OKAY);
               }
               j++;
               }
return(OKAY);
}

VOID main(int argc,char **argv)
{
struct IOExtTD *diskreq0;
struct IOExtTD *diskreq1;
struct MsgPort *diskPort;
long unit[2];

    if (ParseArgs(argc, argv, unit))       /* Check inputs */
    {
        if (diskPort=CreatePort(NULL,NULL))
        {
            if (diskreq0=(struct IOExtTD *)CreateExtIO(diskPort,
                                                 sizeof(struct IOExtTD)))
            {
                if (diskreq1=(struct IOExtTD *)CreateExtIO(diskPort,
                                                 sizeof(struct IOExtTD)))
                {

                    Do_OpenDevice(diskreq0,diskreq1, unit);
                    DeleteExtIO((struct IORequest *)diskreq1);
                }
                else printf("Out of memory\n");
                DeleteExtIO((struct IORequest *)diskreq0);
            }
            else printf("Out of memory\n");
            DeletePort(diskPort);
        }
        else printf("Could not create diskReq port\n");
    }
}
