/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: Formatter.c
 *	Created ..: Thursday 31-May-90 19:49
 *	Revision .: 4
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	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;
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

	/* 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[16] =
{
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  About...",			NULL},
	{0,0,0,2,1,&BoldFont   ,(UBYTE *)"_______________________",	NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Auto start",		NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Fast format",		NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Verify write",		NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Install disk",		NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Use FFS",			NULL},
	{0,0,0,2,1,&BoldFont   ,(UBYTE *)"_______________________",	NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Drive DF0:",		NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Drive DF1:",		NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Drive DF2:",		NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Drive DF3:",		NULL},
	{0,0,0,2,1,&BoldFont   ,(UBYTE *)"_______________________",	NULL},
	{0,0,0,2,1,&DefaultFont,NULL,					NULL},
	{0,0,0,2,1,&BoldFont   ,(UBYTE *)"_______________________",	NULL},
	{0,0,0,2,1,&DefaultFont,(UBYTE *)"  Quit",			NULL}
};

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

struct Menu FormatMenu = {NULL,0,0,112,0,257,"Formatter 2.4",&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.4a",			&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 *)"7 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;

	/* 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.4a",
	(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;
	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;
				}
			}
		}
	}
}

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

VOID
AdjustClass()
{
	USHORT	MenuNum = Code;
	BYTE	i;

		/* Truly selected a menu item? */

	if(MenuNum != MENUNULL)
		Class = GADGETUP;
	else
		return;

		/* 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:GadgetID = 8;
					break;

				case 15: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)
{
	SHORT Size = (NUMSECS * NUMHEADS * TD_SECTOR) >> 2;

	while(Size--)
		if(*Track1++ != *Track2++)
			return(FALSE);

	return(TRUE);
}

	/* 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);

		/* 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);

			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);

			/* 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);

		/* 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);

			/* Verification error. */

			return(-43);
		}

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

	return(0);
}

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

BYTE
DiskFormat(BYTE Track)
{
	BYTE Error;

		/* Initialize the request. */

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

	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);

		/* 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. */

				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;

	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;

		/* 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))
			{
					/* 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();

							/* 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(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)
	{
		if(DiskRequest -> iotd_Req . io_Device)
			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()
{
	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);

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

	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;
	}

		/* 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[13] . 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);

		/* 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,323,34);
						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[15] . Flags &= ~ITEMENABLED;

						FormatMenuIntTxt[13] . 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[15] . Flags |= ITEMENABLED;

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

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

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

							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,0);
						}

						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;
				}
			}
		}
	}
}
