/* trackdisk example */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/trackdisk.h>
#include <libraries/dosextens.h>
#include <proto/all.h>
#include <dos.h>
#include <string.h>

#define TRACK_SIZE			((LONG)(NUMSECS * TD_SECTOR))

#define OP_CHECK			1
#define OP_QFMT				2
#define OP_FMT				4
#define OP_INST				8
#define OP_STD				1

int disknumber,operation,ctrlc=0;
char diskname[]="dfx:";
char disklabel[50];
char version[]="$VER: [1;33mMarkDisk v1.2[0m by MaC (28.6.93)";

#include "MarkDiskData.c"

int brk()
{
ctrlc=1;
return(0);
}

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_CLEAR|MEMF_PUBLIC))
	{
	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,sizeof(struct StandardPacket));
	}
}

void Motor_Off(struct IOExtTD *disk)
{
disk->iotd_Req.io_Length=0;
disk->iotd_Req.io_Command=TD_MOTOR;
DoIO((struct IORequest *)disk);
}

void Motor_On(struct IOExtTD *disk)
{
disk->iotd_Req.io_Length=1;
disk->iotd_Req.io_Command=TD_MOTOR;
DoIO((struct IORequest *)disk);
}

SHORT Read_Track(struct IOExtTD *disk,UBYTE *buffer,SHORT track)
{
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) return(FALSE);
return (TRUE);
}

SHORT Read_Block(struct IOExtTD *disk,UBYTE *buffer,SHORT block)
{
disk->iotd_Req.io_Length=TD_SECTOR;
disk->iotd_Req.io_Data=(APTR)buffer;
disk->iotd_Req.io_Command=CMD_READ;
disk->iotd_Req.io_Offset=(ULONG)(TD_SECTOR * block);
DoIO((struct IORequest *)disk);
if (disk->iotd_Req.io_Error) return (FALSE);
return (TRUE);
}

void Format_Track(struct IOExtTD *disk,UBYTE *buffer,SHORT track)
{
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);
}

SHORT Write_Block(struct IOExtTD *disk,UBYTE *buffer,SHORT block)
{
disk->iotd_Req.io_Length=TD_SECTOR;
disk->iotd_Req.io_Data=(APTR)buffer;
disk->iotd_Req.io_Command=CMD_WRITE;
disk->iotd_Req.io_Offset=(ULONG)(TD_SECTOR * block);
DoIO((struct IORequest *)disk);
if (disk->iotd_Req.io_Error) return(FALSE);
return (TRUE);
}

SHORT FindNumTracks(struct IOExtTD *disk)
{
disk->iotd_Req.io_Command=TD_GETNUMTRACKS;
DoIO((struct IORequest *)disk);
return((SHORT)disk->iotd_Req.io_Actual);
}

SHORT Disk_In(struct IOExtTD *disk)
{
disk->iotd_Req.io_Command=TD_CHANGESTATE;
DoIO((struct IORequest *)disk);
if (disk->iotd_Req.io_Actual)
	{
	printf ("Sorry, can't process an empty drive!\n");
	return(FALSE);
	}
else return(TRUE);
}

SHORT Write_Enable(struct IOExtTD *disk)
{
disk->iotd_Req.io_Command=TD_PROTSTATUS;
DoIO((struct IORequest *)disk);
if (disk->iotd_Req.io_Actual)
	{
	printf ("Sorry, disk is write-protected!\n");
	return(FALSE);
	}
else return(TRUE);
}

void Check(struct IOExtTD *diskreq)
{
ULONG *bblock,*rblock,*mblock;
UBYTE *rbuffer,*wbuffer;
SHORT track;
SHORT All_OK,Rel_Tracks;
SHORT NumTracks;
bblock=AllocMem(TD_SECTOR,MEMF_CHIP|MEMF_PUBLIC);
rblock=AllocMem(TD_SECTOR,MEMF_CHIP|MEMF_PUBLIC);
mblock=AllocMem(TD_SECTOR,MEMF_CHIP|MEMF_PUBLIC);
rbuffer=AllocMem(TRACK_SIZE,MEMF_CHIP|MEMF_PUBLIC);
wbuffer=AllocMem(TRACK_SIZE,MEMF_CHIP|MEMF_PUBLIC);
if (bblock && rblock && mblock && rbuffer && wbuffer)
	{
	int i,offset,bit;
	ULONG checksum=0;
	All_OK=Rel_Tracks=TRUE;
	if (operation & (OP_QFMT|OP_FMT|OP_INST))
		{
		CopyMem(bbdata,bblock,TD_SECTOR);
		CopyMem(rbdata,rblock,TD_SECTOR);
		CopyMem(mbdata,mblock,TD_SECTOR);
		} else
		{
		if (!(All_OK=Read_Block(diskreq,(UBYTE*)mblock,881)))
			{
			operation=0;
			printf("Couldn't read mapblock!\n");
			}
		}
	disk_busy(diskname,TRUE);
	Motor_On(diskreq);
	NumTracks=FindNumTracks(diskreq);
	if (operation & (OP_FMT|OP_CHECK))
		{
		for (track=0;track<NumTracks && !ctrlc && All_OK;track++)
			{
			if (operation & (OP_FMT))
				{
				printf("Formatting track %2d side %1d\r",track/2,track%2);
				Format_Track(diskreq,wbuffer,track);
				}
			if (operation & (OP_CHECK))
				{
				if (!(operation&OP_FMT)) printf("Checking track %2d side %1d\r",track/2,track%2);
				if (!Read_Track(diskreq,rbuffer,track))
					{
					if (track == 0 || track == 40) All_OK=Rel_Tracks=FALSE;
					printf("*** ERROR on track %2d side %1d\n",track/2,track%2);
					for (i=track*NUMSECS;i<(track+1)*NUMSECS;i++)
						{
						offset=1+((i-2)/32);
						bit=((i-2)%32);
						mblock[offset] &= ~(((ULONG)1L)<<bit);
						}
					}
				}
			}
		}
	if ((operation & (OP_FMT|OP_QFMT|OP_CHECK))&&All_OK)
		{
		mblock[0]=checksum=0;
		for (i=0;i<(TD_SECTOR/4);i++) checksum += mblock[i];
		mblock[0]=(ULONG)(0-checksum);
		}
	if ((operation & (OP_FMT|OP_QFMT))&&All_OK)
		{
		rblock[5]=checksum=0;
		if (disklabel[0]!=0)
			{
			strcpy((UBYTE*)&(rblock[(TD_SECTOR/4)-20]),disklabel);
			}
		DateStamp((struct DateStamp *)&(rblock[(TD_SECTOR/4)-7]));
		DateStamp((struct DateStamp *)&(rblock[(TD_SECTOR/4)-23]));
		for (i=0;i<(TD_SECTOR/4);i++) checksum += rblock[i];
		rblock[5]=(ULONG)(0-checksum);
		}
	if (!ctrlc)
		{
		if (All_OK)
			{
			if (operation & (OP_FMT|OP_QFMT|OP_INST)) All_OK &= Write_Block(diskreq,(UBYTE*)bblock,0);
			if (operation & (OP_FMT|OP_QFMT)) All_OK &= Write_Block(diskreq,(UBYTE*)rblock,880);
			if (operation & (OP_FMT|OP_QFMT|OP_CHECK)) All_OK &= Write_Block(diskreq,(UBYTE*)mblock,881);
			printf("[0KProcessing finished.\n");
			} else
			{
			if (!Rel_Tracks) printf("[0KSorry, ERROR on relevant tracks!\n");
			else printf("[0KSorry, processing failed!\n");
			}
		} else printf("[0KUser break!\n");
	Motor_Off(diskreq);
	disk_busy(diskname,FALSE);
	} else printf("No memory for track buffer...\n");
if (rbuffer) FreeMem(rbuffer,TRACK_SIZE);
if (wbuffer) FreeMem(wbuffer,TRACK_SIZE);
if (bblock) FreeMem(bblock,TD_SECTOR);
if (rblock) FreeMem(rblock,TD_SECTOR);
if (mblock) FreeMem(mblock,TD_SECTOR);
}

void Do_OpenDevice (struct IOExtTD *diskreq)
{
if (!OpenDevice(TD_NAME,disknumber,(struct IORequest *)diskreq,0L))
	{
	if (Disk_In(diskreq) && Write_Enable(diskreq)) Check(diskreq);
	CloseDevice((struct IORequest *)diskreq);
	} else printf("Could not acces disk!\n");
}

void main(int ac,char **av)
{
struct IOExtTD *diskreq;
struct MsgPort *diskPort;

printf ("\n%s\n[1m-------------------------------[0m\n",&version[6]);
if (ac>1)
	{
	disknumber=-1;
	if ((av[1][0]=='d'||av[1][0]=='D')&&(av[1][1]=='f'||av[1][1]=='F')&&(av[1][3]==':')&&(av[1][4]==0)) disknumber=(av[1][2]-'0');
	if (disknumber<0||disknumber>3) printf("Please specify drive DFx: !\n");
	else{
		diskname[2]=(char)('0'+disknumber);
		operation=OP_STD;
		if (ac>2)
			{
			char opt;
			opt=(av[2][0]=='-')?av[2][1]:av[2][0];
			if (opt=='f'||opt=='F') operation=OP_FMT|OP_INST|OP_CHECK|OP_QFMT;
			else if (opt=='c'||opt=='C') operation=OP_CHECK;
			else if (opt=='q'||opt=='Q') operation=OP_QFMT|OP_INST;
			else if (opt=='i'||opt=='I') operation=OP_INST;
			else printf("Unknown option ignored!\n");
			}
		if (ac>3)
			{
			strcpy(&disklabel[1],av[3]);
			disklabel[0]=(char)strlen(av[3]);
			}
		onbreak(&brk);
		if (diskPort=CreatePort(NULL,NULL))
			{
			if (diskreq=(struct IOExtTD *)CreateExtIO(diskPort,sizeof(struct IOExtTD)))
				{
				Do_OpenDevice(diskreq);
				DeleteExtIO((struct IORequest *)diskreq);
				} else printf("Out of memory\n");
			DeletePort(diskPort);
			} else printf("Couldn't create diskPort\n");
		}
	} else 
	{
	printf ("[1mUsage   :[0m MarkDisk DFx: [[-]option [diskname]]\n");
	printf ("[1mOptions :[0m          c    Check disk and mark bad blocks (default)\n");
	printf ("                   f    Format & Check disk\n");
	printf ("                   q    Quickformat disk\n");
	printf ("                   i    Install disk\n");
	}
printf("\n");
exit(0);
}
