/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: Formatter.c
 *	Created ..: Thursday 31-May-90 19:49
 *	Revision .: 7
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	20-Sep-90       Olsen           If autostart enabled, displays the
 *	                                name of the disk inserted.
 *	29-Aug-90       Olsen           Write verification uses blitter
 *	04-Aug-90       Olsen           Sector labels are cleared
 *	02-Aug-90       Olsen           Gadget/menu coordination fixed
 *	24-Jul-90       Olsen           Removed bug in formatting routine,
 *	                                added disk validation break,
 *	                                fixed the bootblock checksum
 *	08-Jul-90       Olsen           Sped up the formatting process
 *	08-Jun-90       Olsen           Added the menu
 *	08-Jun-90       Olsen           Fixed some bugs
 *	31-May-90       Olsen           Created this file!
 *
 ****************************************************************************
 *
 *	This file contains the main routines used by Formatter.c
 *
 * $Revision Header ********************************************************/

	/* Prototypes for this module. */

BYTE			FindDevice(char *DevName);
VOID			RefreshGadget(struct Window *Window,struct Gadget *Gadget,BYTE Colour);
VOID			ClearPort(VOID);
VOID			AdjustClass(VOID);
VOID			ConvertString(char *ToString,char *FromString);
ULONG			FixBlockSum(ULONG *Array);
ULONG			FixBootSum(ULONG *Array);
VOID			SetBitmapBlock(ULONG *BitmapData,SHORT Block,BYTE SetFlag);
VOID			ClearBlock(ULONG *Block);
VOID			Inhibit(char *Drive,BYTE Bool);
BYTE			CompareTracks(ULONG *Track1,ULONG *Track2);
BYTE			DiskRead(BYTE Track);
BYTE			DiskWrite(ULONG Offset,BYTE Track);
BYTE			DiskFormat(BYTE Track);
BYTE			Formatter(char *DiskName);
VOID			CentreWindow(struct NewWindow *New,USHORT OffsetX,USHORT OffsetY);
VOID			DecrementUsage(VOID);
VOID			CloseAll(BYTE ReturnCode);
VOID			OpenAll(VOID);
LONG			Strlen(VOID *String);
VOID			Strcpy(VOID *Dest,VOID *Source);
VOID			main(int argc,char **argv);

	/* Assembly language string routines. */

#pragma regcall(Strlen(a0))
#pragma regcall(Strcpy(a0,a1))

	/* Window interaction. */

struct Window		*Window;
struct IntuiMessage	*Massage;
ULONG			 Class,Code;
BYTE			 GadgetID;

	/* Global information port. */

struct FormatterPort	*GlobalPort;

#define PORTNAME	 "Formatter"

	/* Disk interaction. */

struct MsgPort		*DiskPort;
struct IOExtTD		*DiskRequest;
struct MsgPort		*TimePort;
struct timerequest	*TimeRequest;
ULONG			*DiskTrack,*CheckTrack,*RootTrack,*SectorLabel;
BYTE			 DriveUnit = 0;
ULONG			 OffsetTable[NUMCYLS];

	/* Debug flag. */

BYTE			 DebugFlag = FALSE;

	/* Boolean flags. */

#define AutoStart	(FormatGadget[4] . Flags & SELECTED)
#define Verify		(FormatGadget[5] . Flags & SELECTED)
#define Fast		(FormatGadget[6] . Flags & SELECTED)
#define Install		(FormatGadget[7] . Flags & SELECTED)
#define FFS		(FormatMenuItem[6].Flags & CHECKED)

	/* Arp CLI info. */

char *CLI_Template	= "Drive,NAME/K,F=FAST/S,I=INSTALL/S,V=VERIFY/S,A=AUTOSTART/S,FFS/S,DEBUG/S";
char *CLI_Help		= "\nUsage: Formatter [DF0: | DF1: | DF2: | DF3:] [Name]\n                 [FAST] [INSTALL] [VERIFY] [AUTOSTART] [FFS]\n";

	/* Argument vector offsets. */

#define ARG_DRIVE	1
#define ARG_NAME	2
#define ARG_FAST	3
#define ARG_INSTALL	4
#define ARG_VERIFY	5
#define ARG_AUTOSTART	6
#define ARG_FFS		7
#define ARG_DEBUG	8

	/* Drive step delay modification. */

struct FileRequester	*DelayFileReq;
ULONG			 OldSteps[4];

	/* Offset from window top edge. */

LONG TopEdge;

	/* Default font for text rendering. */

struct TextAttr DefaultFont =
{
	(STRPTR)"topaz.font",
	8,
	FS_NORMAL,
	FPF_ROMFONT
};

struct TextAttr BoldFont =
{
	(STRPTR)"topaz.font",
	8,
	FSF_BOLD,
	FPF_ROMFONT
};

	/* A whole lot of intuition & graphics data. */

struct IntuiText InfoText = { 1,0,JAM1,0,25,&DefaultFont,(UBYTE *)"                                        ",NULL };

struct IntuiText FormatIntTxt[10] =
{
	{1,0,0, 21,2,&DefaultFont,(UBYTE *)"DF0:",		NULL},
	{1,0,0, 21,2,&DefaultFont,(UBYTE *)"DF1:",		NULL},
	{1,0,0, 21,2,&DefaultFont,(UBYTE *)"DF2:",		NULL},
	{1,0,0, 21,2,&DefaultFont,(UBYTE *)"DF3:",		NULL},
	{1,0,0, 20,2,&DefaultFont,(UBYTE *)"Auto start",	NULL},
	{1,0,0, 12,2,&DefaultFont,(UBYTE *)"Verify Write",	NULL},
	{1,0,0, 16,2,&DefaultFont,(UBYTE *)"Fast format",	NULL},
	{1,0,0, 12,2,&DefaultFont,(UBYTE *)"Install Disk",	NULL},
	{1,0,0, 12,9,&DefaultFont,(UBYTE *)NULL,		NULL},
	{1,0,0,-48,0,&DefaultFont,(UBYTE *)"Name:",		NULL}
};

UBYTE FormatText[32],UndoFormatText[32];

struct StringInfo FormatStringInfo = { FormatText,UndoFormatText,0,32,32,0,0,0,0,NULL,NULL,NULL };

SHORT FormatBorderData[] =
{
	-1,-1, 74,-1, 74,12,-1,12,-1,-1,
	-2,-1, 75,-1, 75,12,-2,12,-2,-1,
	-1,-1, 74,-1, 74,12,-1,12,-1,-1,
	-2,-1, 75,-1, 75,12,-2,12,-2,-1,
	-1,-1, 74,-1, 74,12,-1,12,-1,-1,
	-2,-1, 75,-1, 75,12,-2,12,-2,-1,
	-1,-1, 74,-1, 74,12,-1,12,-1,-1,
	-2,-1, 75,-1, 75,12,-2,12,-2,-1,
	-1,-1,120,-1,120,12,-1,12,-1,-1,
	-2,-1,121,-1,121,12,-2,12,-2,-1,
	-1,-1,120,-1,120,12,-1,12,-1,-1,
	-2,-1,121,-1,121,12,-2,12,-2,-1,
	-1,-1,120,-1,120,12,-1,12,-1,-1,
	-2,-1,121,-1,121,12,-2,12,-2,-1,
	-1,-1,120,-1,120,12,-1,12,-1,-1,
	-2,-1,121,-1,121,12,-2,12,-2,-1,
	-2,-1, 66,-1, 66,27,-2,27,-2,-1,
	-1,-1, 65,-1, 65,27,-1,27,-1,-1,
	-1,-1,256,-1,256, 8,-1, 8,-1,-1,
	-2,-1,257,-1,257, 8,-2, 8,-2,-1
};

struct Border FormatBorder[] =
{
	{0,0,1,0,0,5,&FormatBorderData[  0],&FormatBorder[ 1]},
	{0,0,1,0,0,5,&FormatBorderData[ 10],NULL},
	{0,0,1,0,0,5,&FormatBorderData[ 20],&FormatBorder[ 3]},
	{0,0,1,0,0,5,&FormatBorderData[ 30],NULL},
	{0,0,1,0,0,5,&FormatBorderData[ 40],&FormatBorder[ 5]},
	{0,0,1,0,0,5,&FormatBorderData[ 50],NULL},
	{0,0,1,0,0,5,&FormatBorderData[ 60],&FormatBorder[ 7]},
	{0,0,1,0,0,5,&FormatBorderData[ 70],NULL},
	{0,0,1,0,0,5,&FormatBorderData[ 80],&FormatBorder[ 9]},
	{0,0,1,0,0,5,&FormatBorderData[ 90],NULL},
	{0,0,1,0,0,5,&FormatBorderData[100],&FormatBorder[11]},
	{0,0,1,0,0,5,&FormatBorderData[110],NULL},
	{0,0,1,0,0,5,&FormatBorderData[120],&FormatBorder[13]},
	{0,0,1,0,0,5,&FormatBorderData[130],NULL},
	{0,0,1,0,0,5,&FormatBorderData[140],&FormatBorder[15]},
	{0,0,1,0,0,5,&FormatBorderData[150],NULL},
	{0,0,1,0,0,5,&FormatBorderData[160],&FormatBorder[17]},
	{0,0,1,0,0,5,&FormatBorderData[170],NULL},
	{0,0,1,0,0,5,&FormatBorderData[180],&FormatBorder[19]},
	{0,0,1,0,0,5,&FormatBorderData[190],NULL}
};

#define IMMEDIATE	(RELVERIFY | GADGIMMEDIATE)
#define TOGGLE		(RELVERIFY | GADGIMMEDIATE | TOGGLESELECT)

struct Gadget FormatGadget[10] =
{
	{&FormatGadget[1],  6,67, 74,12,0,IMMEDIATE,	BOOLGADGET,	(APTR)&FormatBorder[ 0],NULL,&FormatIntTxt[0],NULL,NULL,			0,NULL},
	{&FormatGadget[2], 87,67, 74,12,0,IMMEDIATE,	BOOLGADGET,	(APTR)&FormatBorder[ 2],NULL,&FormatIntTxt[1],NULL,NULL,			1,NULL},
	{&FormatGadget[3],168,67, 74,12,0,IMMEDIATE,	BOOLGADGET,	(APTR)&FormatBorder[ 4],NULL,&FormatIntTxt[2],NULL,NULL,			2,NULL},
	{&FormatGadget[4],249,67, 74,12,0,IMMEDIATE,	BOOLGADGET,	(APTR)&FormatBorder[ 6],NULL,&FormatIntTxt[3],NULL,NULL,			3,NULL},
	{&FormatGadget[5],  6,37,120,12,0,TOGGLE,	BOOLGADGET,	(APTR)&FormatBorder[ 8],NULL,&FormatIntTxt[4],NULL,NULL,			4,NULL},
	{&FormatGadget[6],  6,52,120,12,0,TOGGLE,	BOOLGADGET,	(APTR)&FormatBorder[10],NULL,&FormatIntTxt[5],NULL,NULL,			5,NULL},
	{&FormatGadget[7],132,37,120,12,0,TOGGLE,	BOOLGADGET,	(APTR)&FormatBorder[12],NULL,&FormatIntTxt[6],NULL,NULL,			6,NULL},
	{&FormatGadget[8],132,52,120,12,0,TOGGLE,	BOOLGADGET,	(APTR)&FormatBorder[14],NULL,&FormatIntTxt[7],NULL,NULL,			7,NULL},
	{&FormatGadget[9],258,37, 65,27,0,IMMEDIATE,	BOOLGADGET,	(APTR)&FormatBorder[16],NULL,&FormatIntTxt[8],NULL,NULL,			8,NULL},
	{NULL,             67,82,256, 8,0,IMMEDIATE,	STRGADGET,	(APTR)&FormatBorder[18],NULL,&FormatIntTxt[9],NULL,(APTR)&FormatStringInfo,	9,NULL}
};

UBYTE *FormatInfoText[2] =
{
	(UBYTE *)"   Start formatting",
	(UBYTE *)"   Stop formatting"
};

struct IntuiText FormatMenuIntTxt[17] =
{
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   About...",			NULL},
	{0,0,0,0,1,&BoldFont,	(UBYTE *)"________________________",	NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Auto start",		NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Fast format",		NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Verify write",		NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Install disk",		NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Use FFS",			NULL},
	{0,0,0,0,1,&BoldFont,	(UBYTE *)"________________________",	NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Drive DF0:",		NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Drive DF1:",		NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Drive DF2:",		NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Drive DF3:",		NULL},
	{0,0,0,0,1,&BoldFont,	(UBYTE *)"________________________",	NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Modify stepdelay",		NULL},
	{0,0,0,0,1,&DefaultFont,NULL,					NULL},
	{0,0,0,0,1,&BoldFont,	(UBYTE *)"________________________",	NULL},
	{0,0,0,0,1,&DefaultFont,(UBYTE *)"   Quit",			NULL}
};

struct MenuItem FormatMenuItem[17] =
{
	{&FormatMenuItem[ 1],	0,  0,192,10, 86,0,(APTR)&FormatMenuIntTxt[ 0],NULL,63,NULL,NULL},
	{&FormatMenuItem[ 2],	0,  3,192,10,210,0,(APTR)&FormatMenuIntTxt[ 1],NULL, 0,NULL,NULL},
	{&FormatMenuItem[ 3],	0, 13,192,10, 95,0,(APTR)&FormatMenuIntTxt[ 2],NULL,65,NULL,NULL},
	{&FormatMenuItem[ 4],	0, 23,192,10, 95,0,(APTR)&FormatMenuIntTxt[ 3],NULL,70,NULL,NULL},
	{&FormatMenuItem[ 5],	0, 33,192,10, 95,0,(APTR)&FormatMenuIntTxt[ 4],NULL,86,NULL,NULL},
	{&FormatMenuItem[ 6],	0, 43,192,10, 95,0,(APTR)&FormatMenuIntTxt[ 5],NULL,73,NULL,NULL},
	{&FormatMenuItem[ 7],	0, 53,192,10, 95,0,(APTR)&FormatMenuIntTxt[ 6],NULL,70,NULL,NULL},
	{&FormatMenuItem[ 8],	0, 56,192,10,210,0,(APTR)&FormatMenuIntTxt[ 7],NULL, 0,NULL,NULL},
	{&FormatMenuItem[ 9],	0, 66,192,10, 95,0,(APTR)&FormatMenuIntTxt[ 8],NULL,49,NULL,NULL},
	{&FormatMenuItem[10],	0, 76,192,10, 95,0,(APTR)&FormatMenuIntTxt[ 9],NULL,50,NULL,NULL},
	{&FormatMenuItem[11],	0, 86,192,10, 95,0,(APTR)&FormatMenuIntTxt[10],NULL,51,NULL,NULL},
	{&FormatMenuItem[12],	0, 96,192,10, 95,0,(APTR)&FormatMenuIntTxt[11],NULL,52,NULL,NULL},
	{&FormatMenuItem[13],	0, 99,192,10,210,0,(APTR)&FormatMenuIntTxt[12],NULL, 0,NULL,NULL},
	{&FormatMenuItem[14],	0,109,192,10, 86,0,(APTR)&FormatMenuIntTxt[13],NULL,77,NULL,NULL},
	{&FormatMenuItem[15],	0,119,192,10, 86,0,(APTR)&FormatMenuIntTxt[14],NULL,83,NULL,NULL},
	{&FormatMenuItem[16],	0,122,192,10,210,0,(APTR)&FormatMenuIntTxt[15],NULL, 0,NULL,NULL},
	{NULL,			0,132,192,10, 86,0,(APTR)&FormatMenuIntTxt[16],NULL,81,NULL,NULL}
};

struct Menu FormatMenu = {NULL,0,0,112,0,257,"Formatter 2.7",&FormatMenuItem[0]};

struct IntuiText ReqIntTxt[9] =
{
	{2,0,0,  3, 3,&DefaultFont,(UBYTE *)"Copy me, I wish to travel!",	NULL},
	{2,0,0, 10, 5,&BoldFont,   (UBYTE *)"Formatter 2.7",			&ReqIntTxt[2]},
	{3,0,0,127, 5,&DefaultFont,(UBYTE *)"© Copyright 1990 by MXM",		&ReqIntTxt[3]},
	{0,0,0,127,17,&DefaultFont,(UBYTE *)"Olaf Barthel",			&ReqIntTxt[4]},
	{0,0,0,127,25,&DefaultFont,(UBYTE *)"Brabeckstrasse 35",		&ReqIntTxt[5]},
	{0,0,0,127,33,&DefaultFont,(UBYTE *)"D-3000 Hannover 71",		&ReqIntTxt[6]},
	{2,0,0, 67,17,&DefaultFont,(UBYTE *)"Author",				&ReqIntTxt[7]},
	{2,0,0, 11,45,&DefaultFont,(UBYTE *)"Shareware fee",			&ReqIntTxt[8]},
	{0,0,0,127,45,&DefaultFont,(UBYTE *)"10$ US or DM 15,-",			NULL}
};

SHORT ReqBorderData[] =
{
	-1,-1,214,-1,214,14,-1,14,-1,-1,
	-2,-1,215,-1,215,14,-2,14,-2,-1,
	 0, 0,311, 0,311,75, 0,75, 0, 0,
	 0, 0,311, 0,311,75, 0,75, 0, 0
};

struct Border ReqBorder[] =
{
	{0,0,2,0,0,5,&ReqBorderData[ 0],&ReqBorder[1]},
	{0,0,2,0,0,5,&ReqBorderData[10],NULL},
	{4,2,0,0,0,5,&ReqBorderData[20],&ReqBorder[3]},
	{5,2,0,0,0,5,&ReqBorderData[30],NULL}
};

struct Gadget ReqGadget = {NULL,53,58,214,14,0,3,4097,(APTR)&ReqBorder[0],NULL,&ReqIntTxt[0],NULL,NULL,0,NULL};

struct Requester Req;

struct IntuiText DelayIntTxt[9] =
{
	{2,1,0,-91, 0,&DefaultFont,(UBYTE *)"Delay DF0:",				NULL},
	{2,1,0,-91, 0,&DefaultFont,(UBYTE *)"Delay DF1:",				NULL},
	{2,1,0,-91, 0,&DefaultFont,(UBYTE *)"Delay DF2:",				NULL},
	{2,1,0,-91, 0,&DefaultFont,(UBYTE *)"Delay DF3:",				NULL},
	{2,1,0,  8, 5,&DefaultFont,(UBYTE *)"Load settings",				NULL},
	{2,1,0,  8, 5,&DefaultFont,(UBYTE *)"Save settings",				NULL},
	{2,1,0, 36, 3,&DefaultFont,(UBYTE *)"Accept",					NULL},
	{2,1,0, 36, 3,&DefaultFont,(UBYTE *)"Cancel",					NULL},
	{2,1,0, 13,50,&DefaultFont,(UBYTE *)"The default drive step delay is 3000.",	NULL}
};

UBYTE DelayString0[11];
UBYTE UndoDelayString0[11];
UBYTE DelayString1[11];
UBYTE UndoDelayString1[11];
UBYTE DelayString2[11];
UBYTE UndoDelayString2[11];
UBYTE DelayString3[11];
UBYTE UndoDelayString3[11];

struct StringInfo DelayStringInfo[4] =
{
	{DelayString0,UndoDelayString0,1,11,11,0,0,0,0,NULL,NULL,NULL},
	{DelayString1,UndoDelayString1,1,11,11,0,0,0,0,NULL,NULL,NULL},
	{DelayString2,UndoDelayString2,1,11,11,0,0,0,0,NULL,NULL,NULL},
	{DelayString3,UndoDelayString3,1,11,11,0,0,0,0,NULL,NULL,NULL}
};

SHORT DelayBorderData[] =
{
	-1,-1, 88,-1, 88, 8,-1, 8,-1,-1,
	-2,-1, 89,-1, 89, 8,-2, 8,-2,-1,
	-1,-1, 88,-1, 88, 8,-1, 8,-1,-1,
	-2,-1, 89,-1, 89, 8,-2, 8,-2,-1,
	-1,-1, 88,-1, 88, 8,-1, 8,-1,-1,
	-2,-1, 89,-1, 89, 8,-2, 8,-2,-1,
	-1,-1, 88,-1, 88, 8,-1, 8,-1,-1,
	-2,-1, 89,-1, 89, 8,-2, 8,-2,-1,
	-1,-1,120,-1,120,19,-1,19,-1,-1,
	-2,-1,121,-1,121,19,-2,19,-2,-1,
	 1, 1,118, 1,118,17, 1,17, 1, 1,
	 2, 1,117, 1,117,17, 2,17, 2, 1,
	-1,-1,120,-1,120,19,-1,19,-1,-1,
	-2,-1,121,-1,121,19,-2,19,-2,-1,
	 1, 1,118, 1,118,17, 1,17, 1, 1,
	 2, 1,117, 1,117,17, 2,17, 2, 1,
	-1,-1,120,-1,120,14,-1,14,-1,-1,
	-2,-1,121,-1,121,14,-2,14,-2,-1,
	 1, 1,118, 1,118,12, 1,12, 1, 1,
	 2, 1,117, 1,117,12, 2,12, 2, 1,
	-1,-1,120,-1,120,14,-1,14,-1,-1,
	-2,-1,121,-1,121,14,-2,14,-2,-1,
	 1, 1,118, 1,118,12, 1,12, 1, 1,
	 2, 1,117, 1,117,12, 2,12, 2, 1,
	 0, 0,318, 0,318,77, 0,77, 0, 1,
	 0, 0,318, 0,318,77, 0,77, 0, 1
};

struct Border DelayBorder[26] =
{
	{0,0,2,0,0,5,&DelayBorderData[  0],&DelayBorder[ 1]},
	{0,0,2,0,0,5,&DelayBorderData[ 10],NULL},
	{0,0,2,0,0,5,&DelayBorderData[ 20],&DelayBorder[ 3]},
	{0,0,2,0,0,5,&DelayBorderData[ 30],NULL},
	{0,0,2,0,0,5,&DelayBorderData[ 40],&DelayBorder[ 5]},
	{0,0,2,0,0,5,&DelayBorderData[ 50],NULL},
	{0,0,2,0,0,5,&DelayBorderData[ 60],&DelayBorder[ 7]},
	{0,0,2,0,0,5,&DelayBorderData[ 70],NULL},
	{0,0,2,0,0,5,&DelayBorderData[ 80],&DelayBorder[ 9]},
	{0,0,2,0,0,5,&DelayBorderData[ 90],&DelayBorder[10]},
	{0,0,2,0,0,5,&DelayBorderData[100],&DelayBorder[11]},
	{0,0,2,0,0,5,&DelayBorderData[110],NULL},
	{0,0,2,0,0,5,&DelayBorderData[120],&DelayBorder[13]},
	{0,0,2,0,0,5,&DelayBorderData[130],&DelayBorder[14]},
	{0,0,2,0,0,5,&DelayBorderData[140],&DelayBorder[15]},
	{0,0,2,0,0,5,&DelayBorderData[150],NULL},
	{0,0,2,0,0,5,&DelayBorderData[160],&DelayBorder[17]},
	{0,0,2,0,0,5,&DelayBorderData[170],&DelayBorder[18]},
	{0,0,2,0,0,5,&DelayBorderData[180],&DelayBorder[19]},
	{0,0,2,0,0,5,&DelayBorderData[190],NULL},
	{0,0,2,0,0,5,&DelayBorderData[200],&DelayBorder[21]},
	{0,0,2,0,0,5,&DelayBorderData[210],&DelayBorder[22]},
	{0,0,2,0,0,5,&DelayBorderData[220],&DelayBorder[23]},
	{0,0,2,0,0,5,&DelayBorderData[230],NULL},
	{2,1,2,0,0,5,&DelayBorderData[240],&DelayBorder[25]},
	{3,1,2,0,0,5,&DelayBorderData[250],NULL}
};

struct Gadget DelayGadgets[8] =
{
	{&DelayGadgets[1],	101, 5, 88, 8,0,3075,4100,(APTR)&DelayBorder[ 0],NULL,(APTR)&DelayIntTxt[0],NULL,(APTR)&DelayStringInfo[0],	0,NULL},
	{&DelayGadgets[2],	101,16, 88, 8,0,3075,4100,(APTR)&DelayBorder[ 2],NULL,(APTR)&DelayIntTxt[1],NULL,(APTR)&DelayStringInfo[1],	1,NULL},
	{&DelayGadgets[3],	101,27, 88, 8,0,3075,4100,(APTR)&DelayBorder[ 4],NULL,(APTR)&DelayIntTxt[2],NULL,(APTR)&DelayStringInfo[2],	2,NULL},
	{&DelayGadgets[4],	101,38, 88, 8,0,3075,4100,(APTR)&DelayBorder[ 6],NULL,(APTR)&DelayIntTxt[3],NULL,(APTR)&DelayStringInfo[3],	3,NULL},
	{&DelayGadgets[5],	195, 5,120,19,0,   3,4097,(APTR)&DelayBorder[ 8],NULL,(APTR)&DelayIntTxt[4],NULL,NULL,				4,NULL},
	{&DelayGadgets[6],	195,27,120,19,0,   3,4097,(APTR)&DelayBorder[12],NULL,(APTR)&DelayIntTxt[5],NULL,NULL,				5,NULL},
	{&DelayGadgets[7],	 12,61,120,14,0,   3,4097,(APTR)&DelayBorder[16],NULL,(APTR)&DelayIntTxt[6],NULL,NULL,				6,NULL},
	{NULL,			195,61,120,14,0,   3,4097,(APTR)&DelayBorder[20],NULL,(APTR)&DelayIntTxt[7],NULL,NULL,				7,NULL}
};

struct Requester DelayReq;

	/* A very new window (well, sort of). */

struct NewWindow NewWindow =
{
	0,0,
	330,93,
	-1,-1,
	CLOSEWINDOW | GADGETUP | MOUSEBUTTONS | MENUPICK | VANILLAKEY,
	WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | ACTIVATE | RMBTRAP,
	(struct Gadget *)&FormatGadget[0],
	(struct Image *)NULL,
	(STRPTR)"Formatter 2.7",
	(struct Screen *)NULL,
	(struct BitMap *)NULL,
	0,0,
	0,0,
	WBENCHSCREEN
};

	/* The track indicator (run dump). */

USHORT PanelMap[220] =
{
	0x78F0,0x0000,0x0030,0xF000,0x0000,0x78F0,0x0000,0x0078,
	0xF000,0x0000,0xC0F0,0x0000,0x00FC,0xF000,0x0000,0x78F0,
	0x0000,0x00FC,0xF000,0x0000,0xCD98,0x0000,0x0071,0x9800,
	0x0000,0xCD98,0x0000,0x00CD,0x9800,0x0000,0xD998,0x0000,
	0x00C1,0x9800,0x0000,0xC198,0x0000,0x000D,0x9800,0x0000,
	0xCD98,0x0000,0x0031,0x9800,0x0000,0x1998,0x0000,0x0019,
	0x9800,0x0000,0xD998,0x0000,0x00F9,0x9800,0x0000,0xF998,
	0x0000,0x0019,0x9800,0x0000,0xCD98,0x0000,0x0031,0x9800,
	0x0000,0x3198,0x0000,0x00CD,0x9800,0x0000,0xFD98,0x0000,
	0x000D,0x9800,0x0000,0xCD98,0x0000,0x0031,0x9800,0x0000,
	0x78F0,0x0000,0x0078,0xF000,0x0000,0xFCF0,0x0000,0x0078,
	0xF000,0x0000,0x18F0,0x0000,0x00F8,0xF000,0x0000,0x78F0,
	0x0000,0x0030,0xF000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0xC000,0x0000,0x00C0,0x0000,0x0000,0xC000,0x0000,0x00C0,
	0x0000,0x0000,0xC000,0x0000,0x00C0,0x0000,0x0000,0xC000,
	0x0000,0x00C0,0x0000,0x0000,0xC000,0x0000,0x00C0,0x0000,
	0x0000,0xC000,0x0000,0x00C0,0x0000,0x0000,0xC000,0x0000,
	0x00C0,0x0000,0x0000,0xC000,0x0000,0x00C0,0x0000,0x0000,
	0xC000,0x0C00,0x00C0,0x000C,0x0000,0xC000,0x0C00,0x00C0,
	0x000C,0x0000,0xC000,0x0C00,0x00C0,0x000C,0x0000,0xC000,
	0x0C00,0x00C0,0x000C,0x0000,0xC000,0x0C00,0x00C0,0x000C,
	0x0000,0xC000,0x0C00,0x00C0,0x000C,0x0000,0xC000,0x0C00,
	0x00C0,0x000C,0x0000,0xC000,0x0C00,0x00C0,0x000C,0x0000,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0xFFFF,0xFFFF,0xFFFF,0xFFFF
};

	/* The track indicator (the image structure). */

struct Image PanelImage =
{
	0,0,
	320,11,1,
	(USHORT *)&PanelMap[0],
	0x01,0x00,
	(struct Image *)NULL
};

	/* A rectangular shape. */

struct Image Box =
{
	0,0,4,12,2,
	(USHORT *)NULL,
	0,0x03,
	(struct Image *)NULL
};

	/* A standard disk bootblock (as used by the install command, only the
	 * DOS disk ID and the checksum are missing).
	 */

ULONG Standard[13] =
{
	0x00000000,0x00000000,0x00000370,0x43FA0018,0x4EAEFFA0,0x4A80670A,
	0x20402068,0x00167000,0x4E7570FF,0x60FA646F,0x732E6C69,0x62726172,
	0x79000000
};

	/* Some embedded assembly language code. */

#asm
	PUBLIC	_Strlen
	PUBLIC	_Strcpy

;	Simple strlen routine.

_Strlen:
	MOVEQ	#0,D0		; Reset length counter
1$	TST.B	(A0)+		; Check for end
	BEQ.S	2$		; Found it
	ADDQ	#1,D0		; Increment length counter
	BRA.S	1$		; Test again
2$	RTS

;	Simple strcpy routine

_Strcpy:
	TST.B	(A1)		; Check source byte
	BEQ.S	1$		; Found the end?
	MOVE.B	(A1)+,(A0)+	; Copy the byte over
	BRA.S	_Strcpy		; Test again
1$	MOVE.B	#0,(A0)		; Null termination
	RTS
#endasm

	/* FindDevice():
	 *
	 *	Tries to discover a device name in the linked list
	 *	of device nodes to be found in DosLibrary.
	 */

BYTE
FindDevice(char *DevName)
{
	extern struct DosLibrary	*DOSBase;
	char				*Pointer,Name[257];
	register struct DeviceNode	*DevInfo;
	struct RootNode			*RootNode = (struct RootNode *)DOSBase -> dl_Root;
	SHORT				 i;

		/* Knock the rest of the system out. */

	Forbid();

		/* The starting point. */

	DevInfo = (struct DeviceNode *)BADDR(((struct DosInfo *)BADDR(RootNode -> rn_Info)) -> di_DevInfo);

		/* As long as it's valid. */

	while(DevInfo)
	{
			/* Is it a device and does it have a file handler task attached? */

		if(DevInfo -> dn_Type == DLT_DEVICE && DevInfo -> dn_Task)
		{
				/* Convert the name. */

			Pointer = (char *)BADDR(DevInfo -> dn_Name);

			for(i = 0 ; i < Pointer[0] ; i++)
				Name[i] = Pointer[i + 1];

			Name[Pointer[0]    ] = ':';
			Name[Pointer[0] + 1] = 0;

				/* Compare the names. */

			if(!Strcmp(Name,DevName))
			{
				Permit();
				return(TRUE);
			}
		}

			/* Check next entry. */

		DevInfo = (struct DeviceNode *)BADDR(DevInfo -> dn_Next);
	}

	Permit();

	return(FALSE);
}

	/* RefreshGadget():
	 *
	 *	Clears the background of a gadget and refreshes it.
	 */

VOID
RefreshGadget(struct Window *Window,struct Gadget *Gadget,BYTE Colour)
{
	struct Gadget	*NextGadget = Gadget -> NextGadget;
	BYTE		 FgPen = Window -> RPort -> FgPen;

		/* Clear the background. */

	SetAPen(Window -> RPort,Colour);
	RectFill(Window -> RPort,Gadget -> LeftEdge,Gadget -> TopEdge,Gadget -> LeftEdge + Gadget -> Width - 1,Gadget -> TopEdge + Gadget -> Height - 1);
	SetAPen(Window -> RPort,FgPen);

		/* Refresh a single gadget. */

	Gadget -> NextGadget = NULL;
	RefreshGadgets(Gadget,Window,NULL);
	Gadget -> NextGadget = NextGadget;
}

	/* ClearPort():
	 *
	 *	Removes all pending messages from the Window UserPort.
	 */

VOID
ClearPort()
{
	while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		ReplyMsg(Massage);

	Class	= NULL;
	Code	= NULL;
}

	/* PerformRequest():
	 *
	 *	Display the information requester in the
	 *	formatter window.
	 */

VOID
PerformRequest()
{
	InitRequester(&Req);

		/* I don't know how it happened, but the main
		 * window is an odd number of rows wide, which
		 * makes it somehow difficult to cover the
		 * window properly with the requester.
		 */

	Req . LeftEdge	= 4;
	Req . TopEdge	= 11 + TopEdge;
	Req . Width	= 324;
	Req . Height	= 80;
	Req . BackFill	= 1;
	Req . Flags	= NOISYREQ | SIMPLEREQ;
	Req . ReqGadget	= &ReqGadget;
	Req . ReqBorder	= &ReqBorder[2];
	Req . ReqText	= &ReqIntTxt[1];

		/* Now, this gets really boring. */

	if(Request(&Req,Window))
	{
		FOREVER
		{
			WaitPort(Window -> UserPort);

			while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
			{
				Class = Massage -> Class;

				if(Class == GADGETUP || Class == VANILLAKEY)
				{
					EndRequest(&Req,Window);
					return;
				}
			}
		}
	}
}

	/* SetStepDelays():
	 *
	 *	Adjusts the step delay for all four disk drives.
	 */

VOID
SetStepDelays(ULONG *DelayTable)
{
	struct MsgPort		*DelayPort;
	struct IOExtTD		*TDDelayRequest;
	struct TDU_PublicUnit	*PublicUnit;
	SHORT			 i;

	if(DelayPort = (struct MsgPort *)CreatePort(NULL,0))
	{
		if(TDDelayRequest = (struct IOExtTD *)CreateExtIO(DelayPort,sizeof(struct IOExtTD)))
		{
			Forbid();

			for(i = 0 ; i < 4 ; i++)
			{
				if(DelayTable[i])
				{
					if(!OpenDevice(TD_NAME,i,TDDelayRequest,0))
					{
						PublicUnit = (struct TDU_PublicUnit *)TDDelayRequest -> iotd_Req . io_Unit;

						PublicUnit -> tdu_StepDelay = DelayTable[i];

						CloseDevice(TDDelayRequest);
					}
				}
			}

			Permit();

			for(i = 0 ; i < 4 ; i++)
				DelayStringInfo[i] . LongInt = DelayTable[i];

			SPrintf((char *)DelayString0,"%ld",DelayStringInfo[0] . LongInt);
			SPrintf((char *)DelayString1,"%ld",DelayStringInfo[1] . LongInt);
			SPrintf((char *)DelayString2,"%ld",DelayStringInfo[2] . LongInt);
			SPrintf((char *)DelayString3,"%ld",DelayStringInfo[3] . LongInt);

			Strcpy(UndoDelayString0,DelayString0);
			Strcpy(UndoDelayString1,DelayString1);
			Strcpy(UndoDelayString2,DelayString2);
			Strcpy(UndoDelayString3,DelayString3);

			DeleteExtIO(TDDelayRequest);
		}

		DeletePort(DelayPort);
	}
}

	/* PerformDelayRequest():
	 *
	 *	This requester helps the user to adjust the step
	 *	delay of all four disk drives.
	 */

VOID
PerformDelayRequest()
{
	SHORT	i;
	USHORT	ID;
	ULONG	DelayTable[4],OldDelayTable[4];

	for(i = 0 ; i < 4 ; i++)
		OldDelayTable[i] = DelayStringInfo[i] . LongInt;

	InitRequester(&DelayReq);

		/* I don't know how it happened, but the main
		 * window is an odd number of rows wide, which
		 * makes it somehow difficult to cover the
		 * window properly with the requester.
		 */

	DelayReq . LeftEdge		= 4;
	DelayReq . TopEdge		= 11 + TopEdge;
	DelayReq . Width		= 324;
	DelayReq . Height		= 80;
	DelayReq . BackFill		= 1;
	DelayReq . Flags		= SIMPLEREQ;
	DelayReq . ReqGadget		= &DelayGadgets[0];
	DelayReq . ReqBorder		= &DelayBorder[24];
	DelayReq . ReqText		= &DelayIntTxt[8];

		/* Now, this gets really boring. */

	if(Request(&DelayReq,Window))
	{
		FOREVER
		{
			WaitPort(Window -> UserPort);

			while(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
			{
				Class	= Massage -> Class;
				ID	= ((struct Gadget *)Massage -> IAddress) -> GadgetID;

				if(Class == GADGETUP)
				{
					switch(ID)
					{
						/* Drives. */

						case 0:
						case 1:
						case 2:
						case 3:	break;

						/* Load settings. */

						case 4:	if(!DelayFileReq)
							{
								if(DelayFileReq = ArpAllocFreq())
								{
									Strcpy(DelayFileReq -> fr_File,"Formatter_Steps");
									Strcpy(DelayFileReq -> fr_Dir,"S:");
								}
							}

							if(DelayFileReq)
							{
								DelayFileReq -> fr_Hail = "Load settings";

								while(FileRequest(DelayFileReq))
								{
									BPTR	Handle;
									char	Name[DSIZE * 5 + FCHARS];
									ULONG	TempDelay[4];

									Strcpy(Name,DelayFileReq -> fr_Dir);
									TackOn(Name,DelayFileReq -> fr_File);

									if(Handle = Open(Name,MODE_OLDFILE))
									{
										if(Read(Handle,TempDelay,4 * sizeof(ULONG)) == 4 * sizeof(ULONG))
										{
											for(i = 0 ; i < 4 ; i++)
												DelayStringInfo[i] . LongInt = TempDelay[i];

											SPrintf((char *)DelayString0,"%ld",DelayStringInfo[0] . LongInt);
											SPrintf((char *)DelayString1,"%ld",DelayStringInfo[1] . LongInt);
											SPrintf((char *)DelayString2,"%ld",DelayStringInfo[2] . LongInt);
											SPrintf((char *)DelayString3,"%ld",DelayStringInfo[3] . LongInt);

											Strcpy(UndoDelayString0,DelayString0);
											Strcpy(UndoDelayString1,DelayString1);
											Strcpy(UndoDelayString2,DelayString2);
											Strcpy(UndoDelayString3,DelayString3);

											RefreshGadgets(&DelayGadgets[0],Window,&DelayReq);

											Close(Handle);

											break;
										}

										Close(Handle);
									}

									DisplayBeep(Window -> WScreen);
								}
							}
									
							break;

						/* Save settings. */

						case 5:	if(!DelayFileReq)
							{
								if(DelayFileReq = ArpAllocFreq())
								{
									Strcpy(DelayFileReq -> fr_File,"Formatter_Steps");
									Strcpy(DelayFileReq -> fr_Dir,"S:");
								}
							}

							if(DelayFileReq)
							{
								ULONG	TempDelay[4];

								DelayFileReq -> fr_Hail = "Save settings";

								for(i = 0 ; i < 4 ; i++)
									TempDelay[i] = DelayStringInfo[i] . LongInt;

								while(FileRequest(DelayFileReq))
								{
									BPTR	Handle;
									char	Name[DSIZE * 5 + FCHARS];

									Strcpy(Name,DelayFileReq -> fr_Dir);
									TackOn(Name,DelayFileReq -> fr_File);

									if(Handle = Open(Name,MODE_NEWFILE))
									{
										if(Write(Handle,TempDelay,4 * sizeof(ULONG)) == 4 * sizeof(ULONG))
										{
											Close(Handle);

											break;
										}

										Close(Handle);
									}

									DisplayBeep(Window -> WScreen);
								}
							}
									
							break;

						/* Accept. */

						case 6:	for(i = 0 ; i < 4 ; i++)
								DelayTable[i] = DelayStringInfo[i] . LongInt;

							SetStepDelays(DelayTable);

							EndRequest(&DelayReq,Window);
							return;

						/* Cancel. */

						case 7:	EndRequest(&DelayReq,Window);

							SetStepDelays(OldDelayTable);

							return;
					}
				}
			}
		}
	}
}

	/* AdjustClass():
	 *
	 *	Formatter is controlled both by menu and by gadgets;
	 *	the following routine links both interfaces.
	 */

VOID
AdjustClass()
{
	USHORT MenuNum = Code;

		/* Truly selected a menu item? */

	if(MenuNum != MENUNULL)
	{
		BYTE i;

		Class = GADGETUP;

			/* Get the menu item behind the Intuition menu number. */

		while(MenuNum != MENUNULL)
		{
			if(MENUNUM(MenuNum) == 0)
			{
				switch(ITEMNUM(MenuNum))
				{
					case 0:	PerformRequest();
						Class = NULL;
						break;

					case 2:	FormatGadget[4] . Flags ^= SELECTED;
						RefreshGadget(Window,&FormatGadget[4],0);
						Class = NULL;
						break;

					case 3:	FormatGadget[6] . Flags ^= SELECTED;
						RefreshGadget(Window,&FormatGadget[6],0);
						Class = NULL;
						break;

					case 4:	FormatGadget[5] . Flags ^= SELECTED;
						RefreshGadget(Window,&FormatGadget[5],0);
						Class = NULL;
						break;

					case 5:	FormatGadget[7] . Flags ^= SELECTED;
						RefreshGadget(Window,&FormatGadget[7],0);
						Class = NULL;
						break;

					case 8:
					case 9:
					case 10:
					case 11:for(i = 0 ; i < 4 ; i++)
							if((FormatMenuItem[8 + i] . Flags & ITEMENABLED) && 8 + i != ITEMNUM(MenuNum))
								FormatMenuItem[8 + i] . Flags &= ~CHECKED;

						GadgetID = ITEMNUM(MenuNum) - 8;
						break;

					case 13:PerformDelayRequest();
						Class = NULL;
						break;

					case 14:GadgetID = 8;
						break;

					case 16:Class = CLOSEWINDOW;
						break;

					default:Class = NULL;
						break;
				}
			}

			MenuNum = ((struct MenuItem *)ItemAddress(&FormatMenu,MenuNum)) -> NextSelect;
		}
	}
}

	/* ConvertString():
	 *
	 *	Turn a 'C' string into a BCPL string.
	 */

VOID
ConvertString(char *ToString,char *FromString)
{
	*ToString++ = Strlen(FromString);

	while(*FromString)
		*ToString++ = *FromString++;
}

	/* FixBlockSum():
	 *
	 *	Calculate the checksum for a DOS disk block.
	 *	The checksum itself is the complement of the sum
	 *	of all the longwords contained in a track.
	 */

ULONG
FixBlockSum(ULONG *Array)
{
	ULONG	Sum = 0;
	UBYTE	i = 128;

	while(i--)
		Sum += Array[i];

	return(-Sum);
}

	/* FixBootSum(ULONG *Array):
	 *
	 *	Calculates the checksum for a disk bootblock, which
	 *	unfortunately uses an algorithm different from
	 *	FixBlockSum.
	 */

ULONG
FixBootSum(ULONG *Array)
{
	ULONG	Sum = 0,LastSum;
	UBYTE	i = 128;

	while(i--)
	{
		LastSum = Sum;

		if((Sum += Array[i]) < LastSum)
			Sum++;
	}

	return(~Sum);
}

	/* SetBitmapBlock():
	 *
	 *	Marks a block in the disk bitmap (bit by bit).
	 *	A standard bitmap consists of fifty-five longwords
	 *	in which each bit represents a disk block. If a
	 *	bit is set, a block is unused. A cleared bit indicates
	 *	an allocated block.
	 */

VOID
SetBitmapBlock(ULONG *BitmapData,SHORT Block,BYTE SetFlag)
{
	if(SetFlag)
		BitmapData[Block / 32] &= ~(1 << (Block % 32));
	else
		BitmapData[Block / 32] |=  (1 << (Block % 32));
}

	/* ClearBlock():
	 *
	 *	Set the contents of disk block to zero. It takes less
	 *	time to clear the longwords contained in a disk track
	 *	than to clear the single bytes.
	 */

VOID
ClearBlock(ULONG *Block)
{
	UBYTE Offset = 128;

	while(Offset--)
		*Block++ = 0;
}

	/* Inhibit():
	 *
	 *	Toggle the activity of the disk validator. We send
	 *	a DOS packet to the file handler task telling it
	 *	to disable or to enable the disk validator.
	 */

VOID
Inhibit(char *Drive,BYTE Bool)
{
	struct MsgPort	*Handler;
	LONG		 Args[7];

	if(Bool)
		Args[0] = DOSTRUE;
	else
		Args[0] = DOSFALSE;

	if(Handler = DeviceProc(Drive))
		SendPacket(ACTION_INHIBIT,Args,Handler);
}

	/* CompareTracks():
	 *
	 *	Check if two disk blocks are equal.
	 */

BYTE
CompareTracks(ULONG *Track1,ULONG *Track2)
{
	BYTE Result;

		/* Allocate the blitter for private use. */

	OwnBlitter();
	WaitBlit();

		/* Set the blitter data addresses. We will use
		 * sources A & C.
		 */

	custom . bltapt		= (APTR)Track1;
	custom . bltcpt		= (APTR)Track2;

		/* The blitter modulo values are not important
		 * in this example.
		 */

	custom . bltamod	= 0;
	custom . bltcmod	= 0;

		/* Set the first and last word mask values. */

	custom . bltafwm	= 0xFFFF;
	custom . bltalwm	= 0xFFFF;

		/* Set the blitter control registers.
		 * We will work with sources A & C. The
		 * minterm A_XOR_C will cause the blitter to
		 * generate zeroes if both sources are
		 * equal.
		 */

	custom . bltcon0	= SRCA | SRCC | A_XOR_C;
	custom . bltcon1	= 0;

		/* This last assignment starts the blitter.
		 * The size of a disk track is 11264 bytes,
		 * in other words, 64 x 176 bytes.
		 */

/*	custom . bltsize	= (((176 & VSIZEMASK) << 6 ) | ((64 >> 1) & HSIZEMASK));*/
	custom . bltsize	= 0x2C20;

	WaitBlit();

		/* If both sources are equal zeroes will have
		 * been generated, i.e. the blitter zero
		 * flag will be set.
		 */

	if(custom . dmaconr & DMAF_BLTNZERO)
		Result = TRUE;
	else
		Result = FALSE;

	DisownBlitter();

	return(Result);
}

	/* DiskRead():
	 *
	 *	Read a track from disk and compare it with the
	 *	data in the buffer.
	 */

BYTE
DiskRead(BYTE Track)
{
	SHORT	Position = 4 + Box . Width * Track;
	BYTE	Error;

		/* Initialize the request. */

	DiskRequest -> iotd_Req . io_Offset = OffsetTable[Track];

		/* Start the request. */

	BeginIO(DiskRequest);

		/* Move up one track. */

	Box . PlaneOnOff = 3;
	DrawImage(Window -> RPort,&Box,Position,23 + TopEdge);

		/* News in the mail? */

	if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
	{
		Class		= Massage -> Class;
		Code		= Massage -> Code;
		GadgetID	= ((struct Gadget *)Massage -> IAddress) -> GadgetID;

		ReplyMsg(Massage);

		if(Class == MENUPICK)
			AdjustClass();

		if(Class == GADGETUP)
		{
				/* Stop formatting? */

			if(GadgetID == 8)
			{
				/* Wait for request to be replied. */

				WaitIO(DiskRequest);

				/* Format aborted. */

				return(-42);
			}

			if(GadgetID == 4)
				FormatMenuItem[2] . Flags ^= CHECKED;

			if(GadgetID == 6)
				FormatMenuItem[3] . Flags ^= CHECKED;

			if(GadgetID == 5)
				FormatMenuItem[4] . Flags ^= CHECKED;

			if(GadgetID == 7)
				FormatMenuItem[5] . Flags ^= CHECKED;
		}
	}

		/* Trouble? */

	if(Error = WaitIO(DiskRequest))
		return(Error);

		/* Are both buffers equal? */

	if(Track == 40)
	{
		if(!CompareTracks(DiskTrack,RootTrack))
		{
			Box . PlaneOnOff = 2;
			DrawImage(Window -> RPort,&Box,Position,23 + TopEdge);

			if(DebugFlag)
			{
				char HelpName[20];
				BPTR Help;

				SPrintf(HelpName,"RAM:SRCTRACK%02ld",Track);

				if(Help = Open(HelpName,MODE_NEWFILE))
				{
					Write(Help,RootTrack,NUMSECS * NUMHEADS * TD_SECTOR);
					Close(Help);
				}

				SPrintf(HelpName,"RAM:DSTTRACK%02ld",Track);

				if(Help = Open(HelpName,MODE_NEWFILE))
				{
					Write(Help,DiskTrack,NUMSECS * NUMHEADS * TD_SECTOR);
					Close(Help);
				}
			}

			/* Verification error. */

			return(-43);
		}
	}
	else
	{
		if(!CompareTracks(DiskTrack,CheckTrack))
		{
			Box . PlaneOnOff = 2;
			DrawImage(Window -> RPort,&Box,Position,23 + TopEdge);

			/* Verification error. */

			if(DebugFlag)
			{
				char HelpName[20];
				BPTR Help;

				SPrintf(HelpName,"RAM:SRCTRACK%02ld",Track);

				if(Help = Open(HelpName,MODE_NEWFILE))
				{
					Write(Help,CheckTrack,NUMSECS * NUMHEADS * TD_SECTOR);
					Close(Help);
				}

				SPrintf(HelpName,"RAM:DSTTRACK%02ld",Track);

				if(Help = Open(HelpName,MODE_NEWFILE))
				{
					Write(Help,DiskTrack,NUMSECS * NUMHEADS * TD_SECTOR);
					Close(Help);
				}
			}

			return(-43);
		}
	}

	return(0);
}

	/* DiskWrite():
	 *
	 *	Write two blocks to disk.
	 */

BYTE
DiskWrite(ULONG Offset,BYTE Track)
{
	SHORT	 Position = 4 + Box . Width * Track;
	ULONG	*OriginalTrack = DiskRequest -> iotd_Req . io_Data;
	BYTE	 Error;

		/* Initialize the request. */

	DiskRequest -> iotd_Req . io_Command	= ETD_WRITE;
	DiskRequest -> iotd_Req . io_Length	= TD_SECTOR * 2;
	DiskRequest -> iotd_Req . io_Offset	= Offset;

		/* Start the request. */

	BeginIO(DiskRequest);

		/* Move up one track. */

	Box . PlaneOnOff = 1;
	DrawImage(Window -> RPort,&Box,Position,23 + TopEdge);

		/* News in the mail? */

	if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
	{
		Class		= Massage -> Class;
		Code		= Massage -> Code;
		GadgetID	= ((struct Gadget *)Massage -> IAddress) -> GadgetID;

		ReplyMsg(Massage);

		if(Class == MENUPICK)
			AdjustClass();

		if(Class == GADGETUP)
		{
				/* Stop formatting? */

			if(GadgetID == 8)
			{
				/* Wait for request to be replied. */

				WaitIO(DiskRequest);

				/* Format aborted. */

				return(-42);
			}

			if(GadgetID == 4)
				FormatMenuItem[2] . Flags ^= CHECKED;

			if(GadgetID == 6)
				FormatMenuItem[3] . Flags ^= CHECKED;

			if(GadgetID == 5)
				FormatMenuItem[4] . Flags ^= CHECKED;

			if(GadgetID == 7)
				FormatMenuItem[5] . Flags ^= CHECKED;
		}
	}

		/* Trouble? */

	if(Error = WaitIO(DiskRequest))
		return(Error);

		/* Verify the data? */

	if(Verify)
	{
			/* Make sure the buffer has been written. */

		DiskRequest -> iotd_Req . io_Command = ETD_UPDATE;
		DoIO(DiskRequest);

			/* Reread the buffer. */

		DiskRequest -> iotd_Req . io_Command	= ETD_READ;
		DiskRequest -> iotd_Req . io_Data	= (APTR)CheckTrack;

		if(Error = DoIO(DiskRequest))
			return(Error);

			/* Are both buffers equal? */

		if(!CompareTracks(OriginalTrack,CheckTrack))
		{
			Box . PlaneOnOff = 2;
			DrawImage(Window -> RPort,&Box,Position,23 + TopEdge);

			/* Verification error. */

			return(-43);
		}

		Box . PlaneOnOff = 3;
		DrawImage(Window -> RPort,&Box,Position,23 + TopEdge);
	}

	return(0);
}

	/* DiskFormat():
	 *
	 *	Perform a disk write access.
	 */

BYTE
DiskFormat(BYTE Track)
{
	BYTE Error;

		/* Initialize the request. */

	DiskRequest -> iotd_Req . io_Offset = OffsetTable[Track];

		/* Install the sector label information. */

	DiskRequest -> iotd_SecLabel = (ULONG)SectorLabel;

	if(Track == 40)
		DiskRequest -> iotd_Req . io_Data = (APTR)RootTrack;

		/* Start the request. */

	BeginIO(DiskRequest);

		/* Move up one track. */

	Box . PlaneOnOff = 1;
	DrawImage(Window -> RPort,&Box,4 + Box . Width * Track,23 + TopEdge);

		/* News in the mail? */

	if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
	{
		Class		= Massage -> Class;
		Code		= Massage -> Code;
		GadgetID	= ((struct Gadget *)Massage -> IAddress) -> GadgetID;

		ReplyMsg(Massage);

		if(Class == MENUPICK)
			AdjustClass();

		if(Class == GADGETUP)
		{
				/* Stop formatting? */

			if(GadgetID == 8)
			{
				/* Wait for request to be replied. */

				WaitIO(DiskRequest);

				if(Track == 40)
					DiskRequest -> iotd_Req . io_Data = (APTR)DiskTrack;

				/* Format aborted. */

				DiskRequest -> iotd_SecLabel = NULL;

				return(-42);
			}

			if(GadgetID == 4)
				FormatMenuItem[2] . Flags ^= CHECKED;

			if(GadgetID == 6)
				FormatMenuItem[3] . Flags ^= CHECKED;

			if(GadgetID == 5)
				FormatMenuItem[4] . Flags ^= CHECKED;

			if(GadgetID == 7)
				FormatMenuItem[5] . Flags ^= CHECKED;
		}
	}

	Error = WaitIO(DiskRequest);

	if(Track == 40)
		DiskRequest -> iotd_Req . io_Data = (APTR)DiskTrack;

	DiskRequest -> iotd_SecLabel = NULL;

	return(Error);
}

	/* Formatter():
	 *
	 *	Format a disk drive, 3½" hardwired to the Amiga.
	 */

BYTE
Formatter(char *DiskName)
{
	struct DosDirectory	*RootBlock;
	struct DosBitmap	*DosBitmap;
	BYTE			 Error,GeneralError = FALSE;
	SHORT			 i,Count = 0;

		/* Allocate both auxilary buffers. */

	if(CheckTrack = (ULONG *)AllocMem(NUMSECS * NUMHEADS * TD_SECTOR,MEMF_CHIP | MEMF_CLEAR))
	{
		if(DiskTrack = (ULONG *)AllocMem(NUMSECS * NUMHEADS * TD_SECTOR,MEMF_CHIP | MEMF_CLEAR))
		{
			if(RootTrack = (ULONG *)AllocMem(NUMSECS * NUMHEADS * TD_SECTOR,MEMF_CHIP | MEMF_CLEAR))
			{
				if(SectorLabel = (ULONG *)AllocMem(NUMSECS * NUMHEADS * TD_LABELSIZE,MEMF_CHIP | MEMF_CLEAR))
				{
						/* It's a DOS disk. */

					if(FFS)
						CheckTrack[0] = DiskTrack[0] = ID_FFS_DISK;
					else
						CheckTrack[0] = DiskTrack[0] = ID_DOS_DISK;

						/* Tie them to the buffer. */

					RootBlock	= (struct DosDirectory *)RootTrack;
					DosBitmap	= (struct DosBitmap *)((ULONG)RootTrack + TD_SECTOR);

						/* Clear the root block. */

					ClearBlock((ULONG *)RootBlock);

						/* Initialize the defaults. */

					RootBlock -> PrimaryType	= T_SHORT;
					RootBlock -> SecondaryType	= ST_ROOT;
					RootBlock -> HashTableSize	= 128 - 56;
					RootBlock -> BitmapFlag		= DOSFALSE;
					RootBlock -> BitmapPointers[0]	= (NUMTRACKS / 2) * NUMSECS + 1;

						/* Set the date stamps. */

					DateStamp(&RootBlock -> LastRootChange);
					DateStamp(&RootBlock -> LastDiskChange);
					DateStamp(&RootBlock -> CreationDate);

						/* Copy the name string over. */

					ConvertString(RootBlock -> DiskName,DiskName);

						/* Calculate the block checksum. */

					RootBlock -> CheckSum = FixBlockSum((ULONG *)RootBlock);

						/* Clear the disk bitmap. */

					ClearBlock((ULONG *)DosBitmap);

						/* Mark all bitmap blocks as unused. */

					for(i = 0 ; i < 55 ; i++)
						DosBitmap -> BitmapData[i] = ~0L;

						/* Mark two blocks as used (880 -> Root block, 881 -> Bitmap).
						 * Note that the first two blocks by default are reserved
						 * and, not being part of the bitmap itself, have to
						 * be skipped.
						 */

					SetBitmapBlock(DosBitmap -> BitmapData,880 - 2,TRUE);
					SetBitmapBlock(DosBitmap -> BitmapData,881 - 2,TRUE);

						/* Calculate the block checksum. */

					DosBitmap -> CheckSum = FixBlockSum((ULONG *)DosBitmap);

						/* Is there a disk in the drive? */

					DiskRequest -> iotd_Req . io_Command = TD_CHANGESTATE;
					DoIO(DiskRequest);

					if(!DiskRequest -> iotd_Req . io_Actual)
					{
							/* Is the disk write enabled? */

						DiskRequest -> iotd_Req . io_Command = TD_PROTSTATUS;
						DoIO(DiskRequest);

						if(!DiskRequest -> iotd_Req . io_Actual)
						{
								/* Get the disk change number. */

							DiskRequest -> iotd_Req . io_Command = TD_CHANGENUM;
							DoIO(DiskRequest);

								/* Set the standard flags. */

							DiskRequest -> iotd_Req . io_Flags	= 0;
							DiskRequest -> iotd_Count		= DiskRequest -> iotd_Req . io_Actual;

								/* Remove all messages. */

							ClearPort();

							if(AutoStart)
							{
								DiskRequest -> iotd_Req . io_Command	= ETD_READ;
								DiskRequest -> iotd_Req . io_Data	= (APTR)DiskTrack;
								DiskRequest -> iotd_Req . io_Length	= NUMSECS * NUMHEADS * TD_SECTOR;
								DiskRequest -> iotd_Req . io_Offset	= OffsetTable[40];

								if(!DoIO(DiskRequest))
								{
									if(!FixBlockSum((ULONG *)DiskTrack))
									{
										CopyMem(&((struct DosDirectory *)DiskTrack) -> DiskName[1],InfoText . IText,32);

										InfoText . IText[((struct DosDirectory *)DiskTrack) -> DiskName[0]] = 0;

										InfoText . LeftEdge = 4 + (320 - 8 * Strlen(InfoText . IText)) / 2;

										InfoText . FrontPen = 3;
										PrintIText(Window -> RPort,&InfoText,0,TopEdge);
										InfoText . FrontPen = 1;

										DisplayBeep(Window -> WScreen);

										FOREVER
										{
											if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
											{
												Class		= Massage -> Class;
												Code		= Massage -> Code;
												GadgetID	= ((struct Gadget *)Massage -> IAddress) -> GadgetID;

												ReplyMsg(Massage);

												if(Class == MENUPICK)
													AdjustClass();

												if(Class == GADGETUP)
												{
														/* Stop formatting? */

													if(GadgetID == 8)
													{
														Error = -42;

														goto WriteError;
													}
												}
											}

											if((Count++) == 6)
												break;

											Delay(TICKS_PER_SECOND >> 1);
										}

										SetAPen(Window -> RPort,0);
										RectFill(Window -> RPort,4,23 + TopEdge,323,34 + TopEdge);
										SetAPen(Window -> RPort,1);
									}
								}

								CopyMemQuick(CheckTrack,DiskTrack,NUMSECS * NUMHEADS * TD_SECTOR);
							}

								/* Are we to format the whole disk? */

							if(!Fast)
							{
								DiskRequest -> iotd_Req . io_Command	= ETD_FORMAT;
								DiskRequest -> iotd_Req . io_Data	= (APTR)DiskTrack;
								DiskRequest -> iotd_Req . io_Length	= NUMSECS * NUMHEADS * TD_SECTOR;

								for(i = 0 ; i < NUMCYLS ; i++)
									if(Error = DiskFormat(i))
										break;

								if(Verify && !Error)
								{
									DiskRequest -> iotd_Req . io_Command = ETD_READ;

									for(i = NUMCYLS - 1 ; i >= 0 && Verify ; i--)
										if(Error = DiskRead(i))
											break;
								}
							}

							if(!Error)
							{
								if(Install || Fast)
								{
									ClearBlock(DiskTrack);

									if(FFS)
										DiskTrack[0] = ID_FFS_DISK;
									else
										DiskTrack[0] = ID_DOS_DISK;

										/* Take the standard boot block? */

									if(Install)
									{
										CopyMemQuick(Standard,DiskTrack,sizeof(Standard));

										if(FFS)
											DiskTrack[0] = ID_FFS_DISK;
										else
											DiskTrack[0] = ID_DOS_DISK;

										DiskTrack[1] = FixBootSum(DiskTrack);
									}

									DiskRequest -> iotd_Req . io_Data = (APTR)DiskTrack;

									if(Error = DiskWrite(0,0))
									{
										i = 0;
										goto WriteError;
									}

									if(Fast)
									{
										DiskRequest -> iotd_Req . io_Data = (APTR)RootTrack;

										if(Error = DiskWrite((NUMTRACKS / 2) * NUMSECS * TD_SECTOR,40))
										{
											i = 40;
											goto WriteError;
										}
									}
								}
							}
							else
							{
WriteError:							if(Error > -42)
									SPrintf((char *)InfoText . IText,"Write error Nº%ld, track %ld!",Error,i);
								else
								{
									if(Error == -42)
										Strcpy((char *)InfoText . IText,"*** Format aborted ***");
									else
										SPrintf((char *)InfoText . IText,"Verification error on track %ld!",i);
								}

								GeneralError = TRUE;
							}

							DiskRequest -> iotd_Req . io_Command = ETD_UPDATE;
							DoIO(DiskRequest);

							DiskRequest -> iotd_Req . io_Command	= TD_MOTOR;
							DiskRequest -> iotd_Req . io_Length	= 0;

							DoIO(DiskRequest);
						}
						else
						{
							Strcpy(InfoText . IText,"Disk ist write protected!");
							GeneralError = TRUE;
						}
					}
					else
					{
						Strcpy(InfoText . IText,"No disk in drive!");
						GeneralError = TRUE;
					}

					FreeMem(SectorLabel,NUMSECS * NUMHEADS * TD_LABELSIZE);
				}

				FreeMem(RootTrack,NUMSECS * NUMHEADS * TD_SECTOR);
			}

			FreeMem(DiskTrack,NUMSECS * NUMHEADS * TD_SECTOR);
		}
		else
		{
			Strcpy(InfoText . IText,"Out of memory!");
			GeneralError = TRUE;
		}

		FreeMem(CheckTrack,NUMSECS * NUMHEADS * TD_SECTOR);
	}
	else
	{
		Strcpy(InfoText . IText,"Out of memory!");
		GeneralError = TRUE;
	}

	ClearPort();

	return(GeneralError);
}

	/* CentreWindow():
	 *
	 *	Centre a new window under the mouse pointer. I didn't
	 *	want to use the req.library function for this purpose
	 *	and wrote my own special creation.
	 */

VOID
CentreWindow(struct NewWindow *New,USHORT OffsetX,USHORT OffsetY)
{
	struct Screen PublicScreen;

		/* Get the screen data. */

	if(GetScreenData(&PublicScreen,sizeof(struct Screen),New -> Type,New -> Screen))
	{
			/* Put it under the mouse pointer. */

		New -> LeftEdge	= PublicScreen . MouseX - OffsetX;
		New -> TopEdge	= PublicScreen . MouseY - OffsetY;

			/* Adjust the x position. */

		while(New -> LeftEdge + New -> Width > PublicScreen . Width)
			New -> LeftEdge--;

		while(New -> LeftEdge < 0)
			New -> LeftEdge++;

			/* Adjust the y position. */

		while(New -> TopEdge + New -> Height > PublicScreen . Height)
			New -> TopEdge--;

		while(New -> TopEdge < 0)
			New -> TopEdge++;
	}
}

	/* DecrementUsage():
	 *
	 *	Decrement the number of currently running Formatters
	 *	and remove the the MsgPort if the current Formatter is
	 *	the last one to terminate.
	 */

VOID
DecrementUsage()
{
	Forbid();

	if(!(--GlobalPort -> NumPrgs))
	{
		RemPort(&GlobalPort -> Port);
		FreeMem(GlobalPort,sizeof(struct FormatterPort));
	}

	Permit();
}

	/* CloseAll():
	 *
	 *	Closes devices and locks the shop.
	 */

VOID
CloseAll(BYTE ReturnCode)
{
	if(TimeRequest)
	{
		if(TimeRequest -> tr_node . io_Device)
			CloseDevice(TimeRequest);

		DeleteExtIO(TimeRequest);
	}

	if(TimePort)
		DeletePort(TimePort);

	if(DiskRequest)
	{
		struct TDU_PublicUnit	*PublicUnit;
		SHORT			 i;

		if(DiskRequest -> iotd_Req . io_Device)
			CloseDevice(DiskRequest);

		for(i = 0 ; i < 4 ; i++)
		{
			if(OldSteps[i])
			{
				if(!OpenDevice(TD_NAME,i,DiskRequest,0))
				{
					PublicUnit = (struct TDU_PublicUnit *)DiskRequest -> iotd_Req . io_Unit;

					PublicUnit -> tdu_StepDelay = OldSteps[i];

					CloseDevice(DiskRequest);
				}
			}
		}

		DeleteExtIO(DiskRequest);
	}

	if(DiskPort)
		DeletePort(DiskPort);

	if(Window)
	{
		ClearMenuStrip(Window);
		CloseWindow(Window);
	}

	exit(ReturnCode);
}

	/* OpenAll():
	 *
	 *	Opens the devices we need and also the window.
	 */

VOID
OpenAll()
{
	struct TDU_PublicUnit	*PublicUnit;
	SHORT			 i;

	if(!(TimePort = CreatePort(NULL,0)))
		CloseAll(RETURN_FAIL + 0);

	if(!(TimeRequest = (struct timerequest *)CreateExtIO(TimePort,sizeof(struct timerequest))))
		CloseAll(RETURN_FAIL + 1);

	if(OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,0))
		CloseAll(RETURN_FAIL + 2);

	if(!(DiskPort = CreatePort(NULL,0)))
		CloseAll(RETURN_FAIL + 3);

	if(!(DiskRequest = (struct IOExtTD *)CreateExtIO(DiskPort,sizeof(struct IOExtTD))))
		CloseAll(RETURN_FAIL + 4);

	Forbid();

	for(i = 0 ; i < 4 ; i++)
	{
		if(!OpenDevice(TD_NAME,i,DiskRequest,0))
		{
			PublicUnit = (struct TDU_PublicUnit *)DiskRequest -> iotd_Req . io_Unit;

			OldSteps[i] = PublicUnit -> tdu_StepDelay;

			CloseDevice(DiskRequest);
		}
		else
		{
			OldSteps[i] = 0;

			DelayGadgets[i] . Flags |= GADGDISABLED;
		}
	}

	Permit();

	SetStepDelays(OldSteps);

	if(OpenDevice(TD_NAME,DriveUnit,DiskRequest,0))
		CloseAll(RETURN_FAIL + 5);

	TopEdge = GfxBase -> DefaultFont -> tf_YSize - 8;

	for(i = 0 ; i < 10 ; i++)
		FormatGadget[i] . TopEdge += TopEdge;

	NewWindow . Height += TopEdge;

	CentreWindow(&NewWindow,FormatGadget[8] . LeftEdge + (FormatGadget[8] . Width >> 1),FormatGadget[8] . TopEdge + (FormatGadget[8] . Height >> 1));

	if(!(Window = OpenWindow(&NewWindow)))
		CloseAll(RETURN_FAIL + 6);
}

	/* main():
	 *
	 *	That's where all the trouble starts.
	 */

VOID
main(int argc,char **argv)
{
	static char	*Drive = "DFx:";

	BYTE		 Error,OldUnit,DiskInDrive;
	ULONG		 Time1,Time2,Dummy;
	SHORT		 i;

		/* Try to find the global MsgPort. */

	Forbid();

	if(!(GlobalPort = (struct FormatterPort *)FindPort(PORTNAME)))
	{
			/* Didn't find it, create it now. */

		if(GlobalPort = (struct FormatterPort *)AllocMem(sizeof(struct FormatterPort),MEMF_PUBLIC | MEMF_CLEAR))
		{
			GlobalPort -> Port . mp_Node . ln_Name	= PORTNAME;
			GlobalPort -> Port . mp_Node . ln_Type	= NT_MSGPORT;
			GlobalPort -> Port . mp_Node . ln_Pri	= 1;
			GlobalPort -> Port . mp_Flags		= PA_IGNORE;

			AddPort(&GlobalPort -> Port);
		}
	}

		/* Increment the user count. */

	if(GlobalPort)
		GlobalPort -> NumPrgs++;

	Permit();

		/* Didn't create the port. */

	if(!GlobalPort)
	{
		if(argc)
			Printf("Formatter: Unable to create global MsgPort.\a\n");

		exit(RETURN_FAIL);
	}

		/* Do we expect CLI arguments? */

	if(argc > 1)
	{
		char TestString[5];

			/* Enable debug? */

		if(argv[ARG_DEBUG])
			DebugFlag = TRUE;

			/* Which disk drive? */

		if(argv[ARG_DRIVE])
		{
				/* Does it match 'DFx:'? */

			if(Strlen(argv[ARG_DRIVE]) != 4)
			{
				DecrementUsage();

				Puts(CLI_Help);
				exit(RETURN_WARN);
			}

			Strcpy(TestString,argv[ARG_DRIVE]);

			TestString[2] = 'x';

			if(Strcmp(Drive,TestString))
			{
				DecrementUsage();

				Puts(CLI_Help);
				exit(RETURN_WARN);
			}

				/* Can we find the device? */

			if(!FindDevice(argv[ARG_DRIVE]))
			{
				DecrementUsage();

				Printf("Formatter: Unable to find device '%s'.\a\n",argv[ARG_DRIVE]);
				exit(RETURN_FAIL);
			}

			DriveUnit = argv[ARG_DRIVE][2] - '0';
		}

			/* Enable Auto-start? */

		if(argv[ARG_AUTOSTART])
		{
			FormatGadget[4] . Flags |= SELECTED;
			FormatMenuItem[2] . Flags |= CHECKED;
		}

			/* Enable verification? */

		if(argv[ARG_VERIFY])
		{
			FormatGadget[5] . Flags |= SELECTED;
			FormatMenuItem[4] . Flags |= CHECKED;
		}

			/* Enable fast format? */

		if(argv[ARG_FAST])
		{
			FormatGadget[6] . Flags |= SELECTED;
			FormatMenuItem[3] . Flags |= CHECKED;
		}

			/* Enable disk installation? */

		if(argv[ARG_INSTALL])
		{
			FormatGadget[7] . Flags |= SELECTED;
			FormatMenuItem[5] . Flags |= CHECKED;
		}

		if(argv[ARG_FFS])
			FormatMenuItem[6] . Flags |= CHECKED;
	}

	Forbid();

		/* Is the desired drive already occupied? */

	if(argc && argv[ARG_DRIVE])
	{
		if(GlobalPort -> DriveInUse[DriveUnit])
		{
			DecrementUsage();

			Permit();

			Printf("Formatter: A different Formatter task is currently using device '%s'.\a\n",argv[ARG_DRIVE]);
			exit(RETURN_FAIL);
		}	
		else
			GlobalPort -> DriveInUse[DriveUnit] = TRUE;
	}
	else
	{
			/* Try to find the first disk drive
			 * currently not in use.
			 */

		DriveUnit = -1;

		for(i = 0 ; i < 4 ; i++)
		{
			if(!GlobalPort -> DriveInUse[i])
			{
				DriveUnit = i;
				break;
			}
		}

			/* Didn't find an unused drive. */

		if(DriveUnit == -1)
		{
			DecrementUsage();

			Permit();

			DisplayBeep(NULL);

			exit(RETURN_FAIL);
		}
		else
			GlobalPort -> DriveInUse[DriveUnit] = TRUE;
	}

	Permit();

		/* Take which disk name? */

	if(argc)
		Strcpy(FormatText,(argv[ARG_NAME] ? argv[ARG_NAME] : "Blank"));
	else
		Strcpy(FormatText,"Blank");

		/* Set the start gadget text. */

	FormatIntTxt[8] . IText = (UBYTE *)"Start";
	FormatMenuIntTxt[14] . IText = FormatInfoText[0];

		/* Open the resources. */

	OpenAll();

		/* The timer request gets initialized. */

	TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
	TimeRequest -> tr_time . tv_micro	= MILLION / 2;

		/* Draw the track indicator. */

	DrawImage(Window -> RPort,&PanelImage,4,11 + TopEdge);

		/* Mark the device gadgets. */

	for(i = 0 ; i < 4 ; i++)
	{
		Drive[2] = '0' + i;

		if(!FindDevice(Drive))
		{
			FormatGadget[i] . Flags |= GADGDISABLED;
			FormatMenuItem[8 + i] . Flags &= ~ITEMENABLED;
		}
		else
			FormatGadget[i] . Flags &= ~GADGDISABLED;

		if(i != DriveUnit)
			RefreshGadget(Window,&FormatGadget[i],0);
		else
		{
			RefreshGadget(Window,&FormatGadget[i],3);
			FormatMenuItem[8 + i] . Flags |= CHECKED;
		}
	}

		/* Build the track offset table. */

	for(i = 0 ; i < NUMCYLS ; i++)
		OffsetTable[i] = i * (NUMSECS * NUMHEADS * TD_SECTOR);

		/* Inhibit DFx:. */

	Drive[2] = '0' + DriveUnit;
	Inhibit(Drive,TRUE);

		/* Is there a disk in the drive? */

	DiskRequest -> iotd_Req . io_Command = TD_CHANGESTATE;
	DoIO(DiskRequest);

	DiskInDrive = DiskRequest -> iotd_Req . io_Actual;

		/* Enable the menu. */

	SetMenuStrip(Window,&FormatMenu);

	Window -> Flags &= ~RMBTRAP;

		/* Go into loop. */

	FOREVER
	{
			/* Wait half a second. */

		DoIO(TimeRequest);

			/* Auto-start enabled? */

		if(AutoStart)
		{
				/* Has a disk been inserted? */

			DiskRequest -> iotd_Req . io_Command = TD_CHANGESTATE;
			DoIO(DiskRequest);

			if(!DiskRequest -> iotd_Req . io_Actual && (DiskRequest -> iotd_Req . io_Actual != DiskInDrive))
			{
				DiskInDrive = DiskRequest -> iotd_Req . io_Actual;

				GadgetID = 8;

				Class = GADGETUP;

				goto CheckClass;
			}

			DiskInDrive = DiskRequest -> iotd_Req . io_Actual;
		}

			/* News in the mail? */

		if(Massage = (struct IntuiMessage *)GetMsg(Window -> UserPort))
		{
			Class		= Massage -> Class;
			Code		= Massage -> Code;
			GadgetID	= ((struct Gadget *)Massage -> IAddress) -> GadgetID;

			ReplyMsg(Massage);

				/* Picked a menu item? */

			if(Class == MENUPICK)
				AdjustClass();

				/* Close the window. */

			if(Class == CLOSEWINDOW)
			{
				Drive[2] = '0' + DriveUnit;
				Inhibit(Drive,FALSE);

				GlobalPort -> DriveInUse[DriveUnit] = FALSE;

				DecrementUsage();

				CloseAll(RETURN_OK);
			}

			Error = 0;

				/* Check the gadgets. */

CheckClass:		if(Class == GADGETUP)
			{
				switch(GadgetID)
				{
					case 0:
					case 1:
					case 2:
					case 3:	OldUnit = DriveUnit;

						Drive[2] = '0' + GadgetID;

						if(!FindDevice(Drive))
							break;

						if(GadgetID != DriveUnit)
						{
							Forbid();

							if(GlobalPort -> DriveInUse[GadgetID])
							{
								Permit();

								DisplayBeep(Window -> WScreen);
								break;
							}

							GlobalPort -> DriveInUse[GadgetID] = TRUE;
							GlobalPort -> DriveInUse[OldUnit]  = FALSE;

							Permit();

							Drive[2] = '0' + DriveUnit;

							Inhibit(Drive,FALSE);

							if(!Error)
								CloseDevice(DiskRequest);

							DriveUnit = GadgetID;

							for(i = 0 ; i < 4 ; i++)
							{
								if(i != DriveUnit)
									RefreshGadget(Window,&FormatGadget[i],0);
								else
									RefreshGadget(Window,&FormatGadget[i],3);

								if((FormatMenuItem[8 + i] . Flags & ITEMENABLED) && i == DriveUnit)
									FormatMenuItem[8 + i] . Flags |= CHECKED;
								else
									FormatMenuItem[8 + i] . Flags &= ~CHECKED;
							}

							Drive[2] = '0' + DriveUnit;
							Inhibit(Drive,TRUE);

							if(Error = OpenDevice(TD_NAME,DriveUnit,DiskRequest,0))
							{
								GadgetID = OldUnit;
								goto CheckClass;
							}
						}

						break;

					case 4:	FormatMenuItem[2] . Flags ^= CHECKED;
						break;

					case 5:	FormatMenuItem[4] . Flags ^= CHECKED;
						break;

					case 6:	FormatMenuItem[3] . Flags ^= CHECKED;
						break;

					case 7:	FormatMenuItem[5] . Flags ^= CHECKED;
						break;

					case 8:	DiskRequest -> iotd_Req . io_Command = TD_CHANGESTATE;
						DoIO(DiskRequest);

						DiskInDrive = DiskRequest -> iotd_Req . io_Actual;

						FormatIntTxt[8] . IText = (UBYTE *)"Stop!";
						RefreshGadget(Window,&FormatGadget[8],3);

						for(i = 0 ; i < 4 ; i++)
							FormatGadget[i] . Flags |= GADGDISABLED;

						SetAPen(Window -> RPort,0);
						RectFill(Window -> RPort,4,23 + TopEdge,323,34 + TopEdge);
						SetAPen(Window -> RPort,1);

						FormatGadget[4] . Flags |= GADGDISABLED;
						RefreshGadget(Window,&FormatGadget[4],0);
						FormatGadget[6] . Flags |= GADGDISABLED;
						RefreshGadget(Window,&FormatGadget[6],0);
						FormatGadget[9] . Flags |= GADGDISABLED;
						RefreshGadget(Window,&FormatGadget[9],0);

						FormatMenuItem[ 0] . Flags &= ~ITEMENABLED;
						FormatMenuItem[ 2] . Flags &= ~ITEMENABLED;
						FormatMenuItem[ 3] . Flags &= ~ITEMENABLED;
						FormatMenuItem[ 6] . Flags &= ~ITEMENABLED;
						FormatMenuItem[16] . Flags &= ~ITEMENABLED;
						FormatMenuItem[13] . Flags &= ~ITEMENABLED;

						FormatMenuIntTxt[14] . IText = FormatInfoText[1];

						for(i = 0 ; i < 4 ; i++)
							FormatMenuItem[8 + i] . Flags &= ~ITEMENABLED;

						CurrentTime(&Time1,&Dummy);

						Error = Formatter((char *)FormatText);

						CurrentTime(&Time2,&Dummy);

						if(!Error)
						{
							Drive[2] = '0' + DriveUnit;

							Inhibit(Drive,FALSE);
							Delay(3 * TICKS_PER_SECOND);
							Inhibit(Drive,TRUE);
						}

						FormatGadget[4] . Flags &= ~GADGDISABLED;
						RefreshGadget(Window,&FormatGadget[4],0);
						FormatGadget[6] . Flags &= ~GADGDISABLED;
						RefreshGadget(Window,&FormatGadget[6],0);
						FormatGadget[9] . Flags &= ~GADGDISABLED;
						RefreshGadget(Window,&FormatGadget[9],0);

						for(i = 0 ; i < 4 ; i++)
						{
							Drive[2] = '0' + i;

							if(FindDevice(Drive))
								FormatMenuItem[8 + i] . Flags |= ITEMENABLED;
						}

						FormatMenuItem[ 0] . Flags |= ITEMENABLED;
						FormatMenuItem[ 2] . Flags |= ITEMENABLED;
						FormatMenuItem[ 3] . Flags |= ITEMENABLED;
						FormatMenuItem[ 6] . Flags |= ITEMENABLED;
						FormatMenuItem[16] . Flags |= ITEMENABLED;
						FormatMenuItem[13] . Flags |= ITEMENABLED;

						FormatMenuIntTxt[14] . IText = FormatInfoText[0];

						SetAPen(Window -> RPort,0);
						RectFill(Window -> RPort,4,23 + TopEdge,323,34 + TopEdge);
						SetAPen(Window -> RPort,1);

						if(Error)
						{
							InfoText . LeftEdge = 4 + (320 - 8 * Strlen(InfoText . IText)) / 2;
							PrintIText(Window -> RPort,&InfoText,0,TopEdge);

							DisplayBeep(Window -> WScreen);
						}
						else
						{
							SPrintf((char *)InfoText . IText,"Formatting time %ld:%02ld",abs(Time2 - Time1) / 60,abs(Time2 - Time1) % 60);

							InfoText . LeftEdge = 4 + (320 - 8 * Strlen(InfoText . IText)) / 2;
							PrintIText(Window -> RPort,&InfoText,0,TopEdge);
						}

						for(i = 0 ; i < 4 ; i++)
						{
							Drive[2] = '0' + i;

							if(!FindDevice(Drive))
								FormatGadget[i] . Flags |= GADGDISABLED;
							else
								FormatGadget[i] . Flags &= ~GADGDISABLED;

							if(i != DriveUnit)
								RefreshGadget(Window,&FormatGadget[i],0);
							else
								RefreshGadget(Window,&FormatGadget[i],3);
						}

						FormatIntTxt[8] . IText = (UBYTE *)"Start";
						RefreshGadget(Window,&FormatGadget[8],0);

						break;

					default:break;
				}
			}
		}
	}
}
