/*
 *                          DiskSpeed v1.0
 *                                by
 *                           Michael Sinz
 *
 *             Copyright (c) 1989 by MKSoft Development
 *
 *
 * Yes, this is yet another disk speed testing program, but with a few
 * differences.  It was designed to give the most accurate results of the
 * true disk performance in the system.  For this reason many of
 * DiskSpeed's results may look either lower or higher than current disk
 * performance tests.
 *
 * This program was thrown together in a few hours because I needed more
 * accurate and consistant results for disk performance as seen from the
 * application's standpoint.  This program has now served its purpose and
 * I am now giving it to the rest of the Amiga world to play with as long
 * as all of the files remain together in unmodified form.  (That is, the
 * files DiskSpeed, DiskSpeed.info, DiskSpeed.c, DiskSpeed.doc, and
 * MakeFile)
 *
 ******************************************************************************
 *									      *
 *	Reading legal mush can turn your bain into guacamole!		      *
 *									      *
 *		So here is some of that legal mush:			      *
 *									      *
 * Permission is hereby granted to distribute this program's source	      *
 * executable, and documentation for non-comercial purposes, so long as the   *
 * copyright notices are not removed from the sources, executable or	      *
 * documentation.  This program may not be distributed for a profit without   *
 * the express written consent of the author Michael Sinz.		      *
 *									      *
 * This program is not in the public domain.				      *
 *									      *
 * Fred Fish is expressly granted permission to distribute this program's     *
 * source and executable as part of the "Fred Fish freely redistributable     *
 * Amiga software library."						      *
 *									      *
 * Permission is expressly granted for this program and it's source to be     *
 * distributed as part of the Amicus Amiga software disks, and the	      *
 * First Amiga User Group's Hot Mix disks.				      *
 *									      *
 ******************************************************************************
 */

#include	<EXEC/Types.h>
#include	<EXEC/Memory.h>
#include	<Intuition/Intuition.h>
#include	<Libraries/DOS.h>
#include	<Libraries/DOSextens.h>
#include	<Devices/Timer.h>

#include	<PROTO/All.h>

#include	<string.h>

extern	struct	IntuitionBase	*IntuitionBase;
	struct	Library		*TimerBase;

#define	TIMESCALER		256L
#define	SEEK_READ_SIZE		64L
#define	REQ_GAD			7
#define	TEXT_BUFFER_SIZE	1024L

static char FileUnit[8]	="files/s";
static char Text1[8]	="bytes/s";
static char Text2[12]	="32768 byte:";
static char Text3[11]	="4096 byte:";
static char Text4[10]	="512 byte:";
static char Text5[16]	="Raw Write Speed";
static char Text6[15]	="Raw Read Speed";
static char Text7[13]	="Seek & Read:";
static char Text8[16]	="Directory Scan:";
static char Text9[13]	="File Delete:";
static char Text10[13]	="File Create:";
static char Text11[7]	="Drive:";

static char MenuText1[12]	="Version 1.0";
static char MenuText2[17]	="Copyright © 1989";
static char MenuText3[16]	="By Michael Sinz";
static char MenuText4[5]	="Test";
static char MenuText5[5]	="Save";
static char MenuText6[6]	="Print";
static char MenuText7[23]	="MKSoft Disk Speed Test";
static char MenuText8[8]	="Project";
static char MenuText9[5]	="Quit";

static char ErrorText1[28]	="Can't create test directory";
static char ErrorText2[16]	="Bad device name";
static char ErrorText3[24]	="Can't open results file";
static char ErrorText4[19]	="Can't open printer";

static char WindowText[60]	="Disk Speed Test 1.0  Copyright © 1989 by MKSoft Development";
static char WorkingText[12]	=" Working...";

static char PrinterFile[5]	="PRT:";
static char ResultFile[18]	="DiskSpeed.Results";
static char DirectoryName[22]	=" Disk Test Directory ";
static char FileNameRoot[22]	=" -- DiskTest File -- ";

static char File_Create[12];
static char File_Delete[12];
static char Directory_Scan[12];
static char Seek_And_Read[12];
static char Read_512[12];
static char Read_4096[12];
static char Read_32768[12];
static char Write_512[12];
static char Write_4096[12];
static char Write_32768[12];

static UBYTE Device[32];

static char fontnam[11]	="topaz.font";

static struct TextAttr TOPAZ60	={fontnam,TOPAZ_SIXTY,0,0};
static struct TextAttr TOPAZ80	={fontnam,TOPAZ_EIGHTY,0,0};


static SHORT BorderVectors1[10]	={0,0,230,0,230,40,0,40,0,0};
static SHORT BorderVectors[10]	={0,0,177,0,177,9,0,9,0,0};

static struct Border Border6	={4-56,24-13,1,0,JAM1,5,BorderVectors1,NULL};
static struct Border Border5	={5-56,25-13,2,0,JAM1,5,BorderVectors1,&Border6};
static struct Border Border4	={4-56,67-13,1,0,JAM1,5,BorderVectors1,&Border5};
static struct Border Border3	={5-56,68-13,2,0,JAM1,5,BorderVectors1,&Border4};
static struct Border Border2	={4-56,111-13,1,0,JAM1,5,BorderVectors1,&Border3};
static struct Border Border1	={5-56,112-13,2,0,JAM1,5,BorderVectors1,&Border2};
static struct Border Border	={-1,-1,1,0,JAM1,5,BorderVectors,&Border1};

static struct IntuiText IText25	={1,0,JAM2,48,129,&TOPAZ60,Write_32768,NULL};
static struct IntuiText IText24	={1,0,JAM2,48,120,&TOPAZ60,Write_4096,&IText25};
static struct IntuiText IText23	={1,0,JAM2,48,111,&TOPAZ60,Write_512,&IText24};
static struct IntuiText IText22	={1,0,JAM2,48,85,&TOPAZ60,Read_32768,&IText23};
static struct IntuiText IText21	={1,0,JAM2,48,76,&TOPAZ60,Read_4096,&IText22};
static struct IntuiText IText20	={1,0,JAM2,48,67,&TOPAZ60,Read_512,&IText21};
static struct IntuiText IText19	={1,0,JAM2,78,41,&TOPAZ80,Seek_And_Read,&IText20};
static struct IntuiText IText18	={1,0,JAM2,78,23,&TOPAZ80,Directory_Scan,&IText19};
static struct IntuiText IText17	={1,0,JAM2,78,32,&TOPAZ80,File_Delete,&IText18};
static struct IntuiText IText16	={1,0,JAM2,78,14,&TOPAZ80,File_Create,&IText17};

static struct IntuiText IText15	={3,0,JAM2,120,101,&TOPAZ80,Text1,&IText16};
static struct IntuiText IText14	={3,0,JAM2,120,57,&TOPAZ80,Text1,&IText15};
static struct IntuiText IText13	={1,0,JAM2,-49,129,&TOPAZ80,Text2,&IText14};
static struct IntuiText IText12	={1,0,JAM2,-41,120,&TOPAZ80,Text3,&IText13};
static struct IntuiText IText11	={1,0,JAM2,-33,111,&TOPAZ80,Text4,&IText12};
static struct IntuiText IText10	={1,0,JAM2,-49,101,&TOPAZ60,Text5,&IText11};
static struct IntuiText IText9	={1,0,JAM2,-49,85,&TOPAZ80,Text2,&IText10};
static struct IntuiText IText8	={1,0,JAM2,-41,76,&TOPAZ80,Text3,&IText9};
static struct IntuiText IText7	={1,0,JAM2,-33,67,&TOPAZ80,Text4,&IText8};
static struct IntuiText IText6	={1,0,JAM2,-49,57,&TOPAZ60,Text6,&IText7};
static struct IntuiText IText5	={1,0,JAM2,-50,41,&TOPAZ80,Text7,&IText6};
static struct IntuiText IText4	={1,0,JAM2,-49,23,&TOPAZ80,Text8,&IText5};
static struct IntuiText IText3	={1,0,JAM2,-49,32,&TOPAZ80,Text9,&IText4};
static struct IntuiText IText2	={1,0,JAM2,-49,14,&TOPAZ80,Text10,&IText3};
static struct IntuiText IText1	={1,0,JAM2,-50,0,&TOPAZ80,Text11,&IText2};

static struct IntuiText MText1	={3,1,COMPLEMENT,61,1,&TOPAZ80,MenuText1,NULL};
static struct IntuiText MText2	={3,1,COMPLEMENT,41,1,&TOPAZ80,MenuText2,NULL};
static struct IntuiText MText3	={3,1,COMPLEMENT,45,1,&TOPAZ80,MenuText3,NULL};

static struct IntuiText MText4	={3,1,COMPLEMENT,0,1,&TOPAZ80,MenuText4,NULL};
static struct IntuiText MText5	={3,1,COMPLEMENT,0,1,&TOPAZ80,MenuText5,NULL};
static struct IntuiText MText6	={3,1,COMPLEMENT,0,1,&TOPAZ80,MenuText6,NULL};
static struct IntuiText MText7	={3,1,COMPLEMENT,0,1,&TOPAZ80,MenuText9,NULL};

static struct StringInfo GadgetSInfo	={Device,NULL,0,31,0,0,0,0,0,0,0,0,NULL};

static struct Gadget MyGadget	={NULL,56,13,176,8,NULL,RELVERIFY,STRGADGET,(APTR)&Border,NULL,&IText1,NULL,(APTR)&GadgetSInfo,NULL,NULL};

static struct MenuItem MenuItem3	={NULL,0,20,216,10,ITEMTEXT|ITEMENABLED|HIGHNONE,0,(APTR)&MText1,NULL,NULL,NULL,MENUNULL};
static struct MenuItem MenuItem2	={&MenuItem3,0,10,216,10,ITEMTEXT|ITEMENABLED|HIGHNONE,0,(APTR)&MText2,NULL,NULL,NULL,MENUNULL};
static struct MenuItem MenuItem1	={&MenuItem2,0,0,216,10,ITEMTEXT|ITEMENABLED|HIGHNONE,0,(APTR)&MText3,NULL,NULL,NULL,MENUNULL};

static struct MenuItem MenuItem7	={NULL,0,45,80,10,COMMSEQ|ITEMTEXT|ITEMENABLED|HIGHCOMP,0,(APTR)&MText7,NULL,'Q',NULL,MENUNULL};
static struct MenuItem MenuItem6	={&MenuItem7,0,30,80,10,COMMSEQ|ITEMTEXT|ITEMENABLED|HIGHCOMP,0,(APTR)&MText6,NULL,'P',NULL,MENUNULL};
static struct MenuItem MenuItem5	={&MenuItem6,0,15,80,10,COMMSEQ|ITEMTEXT|ITEMENABLED|HIGHCOMP,0,(APTR)&MText5,NULL,'S',NULL,MENUNULL};
static struct MenuItem MenuItem4	={&MenuItem5,0,0,80,10,COMMSEQ|ITEMTEXT|ITEMENABLED|HIGHCOMP,0,(APTR)&MText4,NULL,'T',NULL,MENUNULL};

static struct Menu Menu2	={NULL,80,0,220,0,MENUENABLED,MenuText7,&MenuItem1};
static struct Menu MyMenu	={&Menu2,0,0,70,0,MENUENABLED,MenuText8,&MenuItem4};

static struct NewWindow NewWindowStructure	={110,11,240,156,0,1,GADGETUP|CLOSEWINDOW|ACTIVEWINDOW|MENUPICK,WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|ACTIVATE|NOCAREREFRESH,&MyGadget,NULL,WindowText,NULL,NULL,0,0,0,0,WBENCHSCREEN};

static SHORT MyReqVec[10]		={0,0, 0,40, 231,40, 231,0, 0,0};
static struct Border MyReqBorder	={0,0,2,0,JAM1,5,MyReqVec,NULL};
static struct IntuiText MyReqText	={0,1,JAM1,0,16,&TOPAZ80,NULL,NULL};
static struct Gadget MyReqGadget	={NULL,0,0,232,41,GADGHCOMP,RELVERIFY,BOOLGADGET|REQGADGET,(APTR)&MyReqBorder,NULL,&MyReqText,NULL,NULL,REQ_GAD,NULL};

/*
 * This does a requester on the window to tell the user something...
 */
VOID TellUser(struct Window *Window,char *Text)
{
register	struct	Requester	*myReq;
register	struct	IntuiMessage	*msg;
register		SHORT		flag=TRUE;

	if (myReq=AllocMem(sizeof(struct Requester),MEMF_PUBLIC|MEMF_CLEAR))
	{
		myReq->LeftEdge=3;
		myReq->TopEdge=24;
		myReq->Width=232;
		myReq->Height=41;
		myReq->BackFill=1;	/* Pen colour 1 */

		myReq->ReqGadget=&MyReqGadget;

		MyReqText.LeftEdge=116-(4*strlen(Text));
		MyReqText.IText=Text;

		Request(myReq,Window);
		while (flag)
		{
			WaitPort(Window->UserPort);
			while (msg=(struct IntuiMessage *)GetMsg(Window->UserPort))
			{
				if (msg->Class==GADGETUP)
				{
					if (((struct Gadget *)(msg->IAddress))->GadgetID==REQ_GAD) flag=FALSE;
				}
				ReplyMsg((struct Message *)msg);
			}
		}
		EndRequest(myReq,Window);

		FreeMem(myReq,sizeof(struct Requester));
	}
}

/*
 * Returns FALSE if the user clicked on the CLOSE gadget...
 */
SHORT Check_Quit(struct Window *Window)
{
register	struct	IntuiMessage	*msg;
register		SHORT		flag=TRUE;

	while(msg=(struct IntuiMessage *)GetMsg(Window->UserPort))
	{
		if (msg->Class==CLOSEWINDOW) flag=FALSE;
		ReplyMsg((struct Message *)msg);
	}
	return(flag);
}

/*
 * Clears a string entry to spaces...
 */
VOID Clear_Entry(char *String)
{
register	SHORT	t;

	for (t=0;t<11;t++) String[t]=' ';
	String[11]='\0';
}

/*
 * Sets a string entry to the value passed and does a RefreshGadgets()
 * to display it...
 */
VOID Set_Entry(char *String,LONG Value,struct Window *Window)
{
register	SHORT	t;
register	SHORT	tmp;

	t=11;
	while (Value)
	{
		tmp=Value%10;
		Value=Value/10;
		String[--t]=(char)(tmp)+'0';
	}
	if (!String[t]) String[--t]='0';
	RefreshGadgets(&MyGadget,Window,NULL);
}

/*
 * This gets the starting time...
 */
VOID Timer_Start(struct Window *Window)
{
register	struct	timerequest	*Time_Req;

	Time_Req=(struct timerequest *)Window->UserData;

	Time_Req->tr_node.io_Command=TR_GETSYSTIME;
	Time_Req->tr_node.io_Flags=IOF_QUICK;
	DoIO((struct IORequest *)Time_Req);
}

/*
 * This gets the ending time and finds out what the per second speed is...
 * It also does a RefreshGadgets() to display the text...
 */
VOID Timer_Stop(struct Window *Window,char *Text,LONG Number)
{
register	struct	timerequest	*Time_Req;
register		LONG		Value;
		struct	timeval		StartTime;

	Time_Req=(struct timerequest *)Window->UserData;

	StartTime=Time_Req->tr_time;	/* Store start time... */

	Time_Req->tr_node.io_Command=TR_GETSYSTIME;
	Time_Req->tr_node.io_Flags=IOF_QUICK;
	DoIO((struct IORequest *)Time_Req);

	SubTime(&Time_Req->tr_time,&StartTime);

	/* Now calculate Value based on Number and the time... */
	Value=(Number*TIMESCALER) /
		(	(Time_Req->tr_time.tv_secs*TIMESCALER) +
			(Time_Req->tr_time.tv_micro/(1000000L/TIMESCALER))
		);

	Set_Entry(Text,Value,Window);
}

/*
 * File create test...
 * This creates a bunch of files in our test directory...
 * The delete test deletes these files...
 */
VOID Do_FileCreate_Test(struct Window *Window)
{
register	char	*t1;
register	char	*t2;
register	BPTR	file;
register	LONG	count=0;

	t1=FileNameRoot+1;
	t2=t1+1;

	Timer_Start(Window);
	for (*t1='A';*t1<'Q';(*t1)++) for (*t2='A';*t2<'Q';(*t2)++)
	{
		if (file=Open(FileNameRoot,MODE_NEWFILE)) Close(file);
		count++;
	}
	Timer_Stop(Window,File_Create,count);
}

/*
 * Directory scan test...  This is done by scanning the directory we
 * just created in the create test... (Twice...)
 */
VOID Do_DirScan_Test(struct Window *Window)
{
register	struct	FileInfoBlock	*FIB;
register		BPTR		lock;
register		LONG		count=0;

	lock=CurrentDir(NULL);	/* Get our directory lock... */
	if (FIB=AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC))
	{
		Timer_Start(Window);
		Examine(lock,FIB);
		while (ExNext(lock,FIB)) count++;
		Examine(lock,FIB);
		while (ExNext(lock,FIB)) count++;
		Timer_Stop(Window,Directory_Scan,count);

		FreeMem(FIB,sizeof(struct FileInfoBlock));
	}
	CurrentDir(lock);	/* Return the locked directory... */
}

/*
 * File delete test...  We delete the files created in the create test...
 */
VOID Do_FileDelete_Test(struct Window *Window)
{
register	char	*t1;
register	char	*t2;
register	LONG	count=0;

	t1=FileNameRoot+1;
	t2=t1+1;

	Timer_Start(Window);
	for (*t1='A';*t1<'Q';(*t1)++) for (*t2='A';*t2<'Q';(*t2)++)
	{
		DeleteFile(FileNameRoot);
		count++;
	}
	Timer_Stop(Window,File_Delete,count);
}

/*
 * This does the seek/read test...
 */
VOID Do_SeekRead_Test(struct Window *Window,char *Buffer,BPTR bigfile)
{
register	short	t;
register	LONG	count=0;
register	LONG	pos;

	Timer_Start(Window);
	for (t=0;t<150;t++)
	{
		Seek(bigfile,0L,OFFSET_BEGINNING);
		Read(bigfile,Buffer,SEEK_READ_SIZE);
		count++;

		pos=Seek(bigfile,-(2*SEEK_READ_SIZE),OFFSET_END);
		Read(bigfile,Buffer,SEEK_READ_SIZE);
		count++;

		Seek(bigfile,-(pos>>1),OFFSET_CURRENT);
		Read(bigfile,Buffer,SEEK_READ_SIZE);
		count++;
	}
	Timer_Stop(Window,Seek_And_Read,count);
}

/*
 * This routine does the write timings...
 * To reduce the number of Seek() commands, the complete file is written
 * multiple times in a loop...
 */
VOID Do_Write_Test(char *Text,LONG Size,struct Window *Window,char *Buffer,BPTR bigfile)
{
register	short	t;
register	short	x;
register	LONG	count=0;
register	LONG	numread;

	numread=Seek(bigfile,0L,OFFSET_END)/Size;

	Timer_Start(Window);
	for (t=0;t<6;t++)
	{
		Seek(bigfile,0L,OFFSET_BEGINNING);
		for (x=0;x<numread;x++) count+=Write(bigfile,Buffer,Size);
	}
	Timer_Stop(Window,Text,count);
}

/*
 * This routine does the read timings...
 * To reduce the number of Seek() commands, the complete file is read
 * multiple times in a loop...
 */
VOID Do_Read_Test(char *Text,LONG Size,struct Window *Window,char *Buffer,BPTR bigfile)
{
register	short	t;
register	short	x;
register	LONG	count=0;
register	LONG	numread;

	numread=Seek(bigfile,0L,OFFSET_END)/Size;

	Timer_Start(Window);
	for (t=0;t<12;t++)
	{
		Seek(bigfile,0L,OFFSET_BEGINNING);
		for (x=0;x<numread;x++) count+=Read(bigfile,Buffer,Size);
	}
	Timer_Stop(Window,Text,count);
}

/*
 * This is the master test routine...  It calles the ones above...
 */
VOID Do_Disk_Test(struct Window *Window)
{
register	BPTR	lock;
register	BPTR	mydir;
register	BPTR	bigfile;
register	SHORT	flag=TRUE;
register	BYTE	*Buffer;

	if (lock=Lock(Device,ACCESS_READ))
	{
		lock=CurrentDir(lock);
		if (mydir=CreateDir(DirectoryName))
		{
			mydir=CurrentDir(mydir);

			Clear_Entry(File_Create);
			Clear_Entry(File_Delete);
			Clear_Entry(Directory_Scan);
			Clear_Entry(Seek_And_Read);
			Clear_Entry(Read_512);
			Clear_Entry(Read_4096);
			Clear_Entry(Read_32768);
			Clear_Entry(Write_512);
			Clear_Entry(Write_4096);
			Clear_Entry(Write_32768);
			RefreshGadgets(&MyGadget,Window,NULL);

			if (flag&=Check_Quit(Window))
			{
				Do_FileCreate_Test(Window);
				if (flag&=Check_Quit(Window)) Do_DirScan_Test(Window);
				Do_FileDelete_Test(Window);
			}
			if (flag&=Check_Quit(Window))
			{
				if (bigfile=Open(FileNameRoot,MODE_NEWFILE))
				{
					if (Buffer=AllocMem(32768L,MEMF_PUBLIC))
					{
						for (flag=8;flag--;flag>1) if (Write(bigfile,Buffer,32768L)<32768L) flag=FALSE;

						if (flag&=Check_Quit(Window)) Do_SeekRead_Test(Window,Buffer,bigfile);

						if (flag&=Check_Quit(Window)) Do_Read_Test(Read_512,512L,Window,Buffer,bigfile);
						if (flag&=Check_Quit(Window)) Do_Read_Test(Read_4096,4096L,Window,Buffer,bigfile);
						if (flag&=Check_Quit(Window)) Do_Read_Test(Read_32768,32768L,Window,Buffer,bigfile);

						if (flag&=Check_Quit(Window)) Do_Write_Test(Write_512,512L,Window,Buffer,bigfile);
						if (flag&=Check_Quit(Window)) Do_Write_Test(Write_4096,4096L,Window,Buffer,bigfile);
						if (flag&=Check_Quit(Window)) Do_Write_Test(Write_32768,32768L,Window,Buffer,bigfile);

						FreeMem(Buffer,32768L);
					}
					Close(bigfile);
					DeleteFile(FileNameRoot);
				}
			}

			mydir=CurrentDir(mydir);
			UnLock(mydir);
			DeleteFile(DirectoryName);
		}
		else
		{	/* Tell user he messed up... */
			TellUser(Window,ErrorText1);
		}

		lock=CurrentDir(lock);
		UnLock(lock);
	}
	else
	{	/* Tell user he messed up... */
		TellUser(Window,ErrorText2);
	}
}

/*
 * This define is for the Write_Results routine...
 */
#define	MYCAT(z,y,x)	for (y=x;*y;*z++=*y++)
#define	MYCATNEWLINE(x)	*x++='\n'
#define	MYCATTAB(x)	*x++='\t'
#define	MYCATSPACE(x)	*x++=' '

/*
 * This routine builds a string of the results and then writes it to
 * the file passed...
 */
VOID Write_Results(BPTR TheFile)
{
register	char	*t;
register	char	*h;
register	char	*Buffer;

	if (t=Buffer=AllocMem(TEXT_BUFFER_SIZE,MEMF_PUBLIC|MEMF_CLEAR))
	{
		
/* Title and device name */
		MYCATNEWLINE(t);
		MYCAT(t,h,WindowText);
		MYCATNEWLINE(t);
		MYCATNEWLINE(t);
		MYCAT(t,h,Text11);
		MYCATTAB(t);
		MYCAT(t,h,Device);
		MYCATNEWLINE(t);
		MYCATNEWLINE(t);

/* File Create */
		MYCAT(t,h,Text10);
		MYCATTAB(t);
		MYCAT(t,h,File_Create);
		MYCATSPACE(t);
		MYCAT(t,h,FileUnit);
		MYCATNEWLINE(t);

/* File Delete */
		MYCAT(t,h,Text9);
		MYCATTAB(t);
		MYCAT(t,h,File_Delete);
		MYCATSPACE(t);
		MYCAT(t,h,FileUnit);
		MYCATNEWLINE(t);

		MYCATNEWLINE(t);

/* Directory Scan */
		MYCAT(t,h,Text8);
		MYCATTAB(t);
		MYCAT(t,h,Directory_Scan);
		MYCATSPACE(t);
		MYCAT(t,h,FileUnit);
		MYCATNEWLINE(t);

		MYCATNEWLINE(t);

/* Seek and Read */
		MYCAT(t,h,Text7);
		MYCATTAB(t);
		MYCAT(t,h,Seek_And_Read);
		MYCATNEWLINE(t);
		MYCATNEWLINE(t);

/* Read 512 */
		MYCAT(t,h,Text6);
		MYCATTAB(t);
		MYCAT(t,h,Text4);
		MYCATTAB(t);
		MYCAT(t,h,Read_512);
		MYCATSPACE(t);
		MYCAT(t,h,Text1);
		MYCATNEWLINE(t);

/* Read 4096 */
		MYCAT(t,h,Text6);
		MYCATTAB(t);
		MYCAT(t,h,Text3);
		MYCATTAB(t);
		MYCAT(t,h,Read_4096);
		MYCATSPACE(t);
		MYCAT(t,h,Text1);
		MYCATNEWLINE(t);

/* Read 32768 */
		MYCAT(t,h,Text6);
		MYCATTAB(t);
		MYCAT(t,h,Text2);
		MYCATTAB(t);
		MYCAT(t,h,Read_32768);
		MYCATSPACE(t);
		MYCAT(t,h,Text1);
		MYCATNEWLINE(t);

		MYCATNEWLINE(t);

/* Write 512 */
		MYCAT(t,h,Text5);
		MYCATTAB(t);
		MYCAT(t,h,Text4);
		MYCATTAB(t);
		MYCAT(t,h,Write_512);
		MYCATSPACE(t);
		MYCAT(t,h,Text1);
		MYCATNEWLINE(t);

/* Write 4096 */
		MYCAT(t,h,Text5);
		MYCATTAB(t);
		MYCAT(t,h,Text3);
		MYCATTAB(t);
		MYCAT(t,h,Write_4096);
		MYCATSPACE(t);
		MYCAT(t,h,Text1);
		MYCATNEWLINE(t);

/* Write 32768 */
		MYCAT(t,h,Text5);
		MYCATTAB(t);
		MYCAT(t,h,Text2);
		MYCATTAB(t);
		MYCAT(t,h,Write_32768);
		MYCATSPACE(t);
		MYCAT(t,h,Text1);
		MYCATNEWLINE(t);

/* End of buffer... */
		MYCATNEWLINE(t);
		MYCATNEWLINE(t);

/* Now, all we have to do is write the sucker... */
		Write(TheFile,Buffer,t-Buffer);

		FreeMem(Buffer,TEXT_BUFFER_SIZE);
	}
}

/*
 * This function will open the file, append to the end of it the
 * test results and close it...
 */
VOID Save_To_File(struct Window *Window)
{
register	BPTR	TheFile;


	TheFile=Open(ResultFile,MODE_OLDFILE);
	if (!TheFile) TheFile=Open(ResultFile,MODE_NEWFILE);
	if (TheFile)
	{
		Seek(TheFile,0L,OFFSET_END);
		Write_Results(TheFile);
		Close(TheFile);
	}
	else
	{	/* Tell the user the file did not open... */
		TellUser(Window,ErrorText3);
	}
}

/*
 * This function will open the printer, append to the end of it the
 * test results and close it...
 */
VOID Save_To_Printer(struct Window *Window)
{
register	BPTR	TheFile;

	if (TheFile=Open(PrinterFile,MODE_NEWFILE))
	{
		Seek(TheFile,0L,OFFSET_END);
		Write_Results(TheFile);
		Close(TheFile);
	}
	else
	{	/* Tell the user the file did not open... */
		TellUser(Window,ErrorText4);
	}
}

VOID main(VOID)
{
register	struct	Window		*Window;
register	struct	IntuiMessage	*msg;
register	struct	timerequest	*Time_Req;
register		SHORT		QuitFlag=0;
register		USHORT		thismenu;
register	struct	Process		*pr;
register		APTR		Old_WindowPtr;

	pr=(struct Process *)FindTask(NULL);
	Old_WindowPtr=pr->pr_WindowPtr;
	pr->pr_WindowPtr=(APTR)(-1);

	if (Time_Req=AllocMem(sizeof(struct timerequest),MEMF_PUBLIC|MEMF_CLEAR))
	{
		if (IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",33L))
		{
			if (!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)Time_Req,NULL))
			{
				Time_Req->tr_node.io_Message.mn_ReplyPort=&(pr->pr_MsgPort);
				TimerBase=(struct Library *)Time_Req->tr_node.io_Device;
				if (Window=OpenWindow(&NewWindowStructure))
				{
					SetWindowTitles(Window,WindowText,WindowText);
					Window->UserData=(BYTE *)Time_Req;
					SetMenuStrip(Window,&MyMenu);
					while (!QuitFlag)
					{
						WaitPort(Window->UserPort);
						while (msg=(struct IntuiMessage *)GetMsg(Window->UserPort))
						{
							if (msg->Class==CLOSEWINDOW) QuitFlag=TRUE;
							else if (msg->Class==ACTIVEWINDOW) ActivateGadget(&MyGadget,Window,NULL);
							else if (msg->Class==MENUPICK)
							{
								OffGadget(&MyGadget,Window,NULL);
								ClearMenuStrip(Window);
								SetWindowTitles(Window,WorkingText,WindowText);
								thismenu=msg->Code;
								while (MENUNULL!=thismenu)
								{
									if (0==MENUNUM(thismenu))
									{
										switch (ITEMNUM(thismenu))
										{
										case 0:	/* Do disk test */
											Do_Disk_Test(Window);
											break;
										case 1:	/* Save result file */
											Save_To_File(Window);
											break;
										case 2:	/* Print result file */
											Save_To_Printer(Window);
											break;
										case 3:	/* Quit... */
											QuitFlag=TRUE;
											break;
										}
									}
									thismenu=ItemAddress(&MyMenu,(long)thismenu)->NextSelect;
								}
								SetWindowTitles(Window,WindowText,WindowText);
								SetMenuStrip(Window,&MyMenu);
								OnGadget(&MyGadget,Window,NULL);
							}
							ReplyMsg((struct Message *)msg);
						}
					}
					ClearMenuStrip(Window);
					CloseWindow(Window);
				}
				CloseDevice((struct IORequest *)Time_Req);
			}
			CloseLibrary((struct Library *)IntuitionBase);
		}
		FreeMem(Time_Req,sizeof(struct timerequest));
	}
	pr->pr_WindowPtr=Old_WindowPtr;
}
