#define TITLE "Disk Format/Copy"

/* A handful of includes for good luck. */

#include <intuition/intuition.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <devices/trackdisk.h>
#include <libraries/dosextens.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <stdlib.h>
#include <string.h>

/* Dette er de forskjellige globale variabler som dette programmet bruker */

struct StandardPacket *gpacket;		/* packet for å sende forskjellige ting	*/
struct IntuiMessage *message;		/* meldingen som vi arbeider med		*/
struct Gadget *gadad;				/* adresse til gadget fra meldingen		*/
struct Window *window;				/* vårt vindu							*/
struct MsgPort *port;				/* I/O port for dos kommunikasjon		*/
struct IntuitionBase *IntuitionBase;/* vi trenger Intuition					*/
struct GfxBase *GfxBase;			/* og graphics							*/
int havedisk;						/* har vi en full disk i RAM?			*/
int hibuf;							/* det høyeste buffer som vi har		*/
long output;						/* kan vi skrive til stdout?			*/
char *buffers[81];					/* pekere til bufferene våre			*/
struct IOExtTD *diskreq[4];			/* vår I/O request blokker				*/
long diskChangeCount[4];			/* siste gang disketten var byttet		*/
int source = 0;						/* "FRA" disketten						*/
int dest = 1;						/* alle "TIL" diskett stasjonene		*/
int verify = 1;						/* verifiser skriving?					*/
int buffer = 0;						/* bruker vi mye minne?					*/
char namebuf[32];					/* dette buffer holder diskettens navn	*/
char df0[] = "DF0:";				/* bruk bare en kopi av dette navnet	*/
char df1[] = "DF1:";				/* samme								*/
char df2[] = "DF2:";				/* samme								*/
char df3[] = "DF3:";				/* samme								*/
char *df[] = { df0,df1,df2,df3 };	/* for lett adgang til stasjons navnene	*/
char blank[] =   "                    ";	/* brukes for å blanke meldings linjen */
char msg[] = "Disk DFx  is not complete";	/* feil-melding for en diskett	*/
char reading[] = "Reading track xx DFx";	/* melding for lesing			*/
char writing[] = "Writing track xx DFx";	/* melding for skriving			*/
char veriing[] = "Ver'ing track xx DFx";	/* melding for verifisering		*/
char nobuf[] = "! couldn't get buffer";		/* hvis vi ikke har nok minne	*/

/* Bruk altid topaz 80, og la oss definere et intuition buffer for vår
 * generelle skriving av string. */

struct TextAttr myfont = { (STRPTR)"topaz.font",TOPAZ_EIGHTY,0,0 };
struct IntuiText intuitext = { 1,0,JAM2,0,0,&myfont };

/* Noen definisjoner for å sette størrelse på forskjellige ting. Prøv å endre
 * disse for å endre størrelsen på diverse gadgets. Husk størrelsen på string
 * gadgeten hvis du øker størrelsen på fonten. */

#define WIDEGADG 85
#define NARROWGADG 53
#define STRGADG 256
#define GADGHEIGHT 13
#define LINE1 11
#define LINE2 LINE1+12
#define LINE3 LINE2+GADGHEIGHT+1
#define LINE4 LINE3+GADGHEIGHT+1
#define LINE5 LINE4+GADGHEIGHT+1
#define LINE6 LINE5+GADGHEIGHT+1
#define WINDOWHEIGHT LINE6+GADGHEIGHT+2
#define COL1 5
#define LINESTART COL1+NARROWGADG
#define STRSTART COL1+NARROWGADG+29
#define COL2 COL1+NARROWGADG+29
#define LINEEND COL2-1
#define COL3 COL2+NARROWGADG+27
#define COL4 COL3+WIDEGADG+6
#define WINDOWWIDTH COL4+WIDEGADG+5
#define TRACKSIZE (2*512*11)

/* Her er noen makroer som gjør det mye enklere å definere de forskjellige
 * intuition gadget'ene og rammene */

#define makeintuitext(name,string,size) struct IntuiText name={1,0,JAM2,\
	(1+size-8*(sizeof(string)-1))/2,3,&myfont,(UBYTE*)string}
#define makebox(name,tname,width,height,off) short tname[]={0,0,width,0,\
	width,height,0,height,0,0,1,1,width-1,1,width-1,height-1,1,height-1,1,1};\
	struct Border name={off,off,1,0,JAM2,10,tname}
#define makengadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
	xpos,ypos,NARROWGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
	(APTR)&narrowbox,NULL,&text,NULL,NULL,ch,NULL}
#define makewgadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
	xpos,ypos,WIDEGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
	(APTR)&widebox,NULL,&text,NULL,NULL,ch,NULL}

/* Nå deklarerer vi alle structurene med makroene som vi nettop har laget.
 * Tekstene defineres først som egene stringer, da de vill bli definert 2
 * ganger av kompilatoren hvis de brukes direkete i makroene. */

char formattxt[]	= "Format";
char againtxt[]		= "Again";
char retrytxt[]		= "Retry";
char quittxt[]		= "Quit";
char verifyontxt[]	= "Verify On ";
char verifyofftxt[]	= "Verify Off";
char bufferontxt[]	= "Buffer On ";
char bufferofftxt[]	= "Buffer Off";
char gotxt[]		= "Go";

makeintuitext(formattext,formattxt,NARROWGADG);
makeintuitext(df0text,df0,NARROWGADG);
makeintuitext(df1text,df1,NARROWGADG);
makeintuitext(df2text,df2,NARROWGADG);
makeintuitext(df3text,df3,NARROWGADG);
makeintuitext(againtext,againtxt,WIDEGADG);
makeintuitext(retrytext,retrytxt,WIDEGADG);
makeintuitext(quittext,quittxt,WIDEGADG);
makeintuitext(verifyontext,verifyontxt,WIDEGADG);
makeintuitext(verifyofftext,verifyofftxt,WIDEGADG);
makeintuitext(bufferontext,bufferontxt,WIDEGADG);
makeintuitext(bufferofftext,bufferofftxt,WIDEGADG);
makeintuitext(gotext,gotxt,WIDEGADG);
makebox(narrowbox,tn1,NARROWGADG-1,GADGHEIGHT-1,0);
makebox(widebox,tn2,WIDEGADG-1,GADGHEIGHT-1,0);
makebox(strbox,tn3,STRGADG-1,GADGHEIGHT-1,-2);
makengadg(formatgadg,NULL,COL1,LINE2,formattext,'f');
makengadg(sdf0gadg,&formatgadg,COL1,LINE3,df0text,'0');
makengadg(sdf1gadg,&sdf0gadg,COL1,LINE4,df1text,'1');
makengadg(sdf2gadg,&sdf1gadg,COL1,LINE5,df2text,'2');
makengadg(sdf3gadg,&sdf2gadg,COL1,LINE6,df3text,'3');
makengadg(ddf0gadg,&sdf3gadg,COL2,LINE3,df0text,'=');
makengadg(ddf1gadg,&ddf0gadg,COL2,LINE4,df1text,'!');
makengadg(ddf2gadg,&ddf1gadg,COL2,LINE5,df2text,'\"');
makengadg(ddf3gadg,&ddf2gadg,COL2,LINE6,df3text,'#');
makewgadg(gogadg,&ddf3gadg,COL3,LINE4,gotext,'g');
makewgadg(retrygadg,&gogadg,COL3,LINE5,retrytext,'r');
makewgadg(verifygadg,&retrygadg,COL3,LINE6,verifyontext,'v');
makewgadg(againgadg,&verifygadg,COL4,LINE4,againtext,'a');
makewgadg(quitgadg,&againgadg,COL4,LINE5,quittext,'q');
makewgadg(buffergadg,&quitgadg,COL4,LINE6,bufferontext,'b');

/* Vi trenger en siste gadget, en string gadget, og dens spesielle info
 * struktur. */

struct StringInfo nameinfo = { (UBYTE*)namebuf,NULL,0,31 };
struct Gadget namegadg = {
	&buffergadg,STRSTART+2,LINE2+2,STRGADG-4,GADGHEIGHT-4,GADGHCOMP,
	STRINGCENTER|RELVERIFY,STRGADGET,(APTR)&strbox,NULL,NULL,NULL,
	(APTR)&nameinfo,'n'
};

/* Til sist vår vindus struktur. */

struct NewWindow newwindow = {
	200,20,WINDOWWIDTH,WINDOWHEIGHT,0,1,
	CLOSEWINDOW | VANILLAKEY | GADGETDOWN | GADGETUP,
	WINDOWDEPTH | WINDOWCLOSE | WINDOWDRAG | SMART_REFRESH | ACTIVATE,
	&namegadg,NULL,(UBYTE *)TITLE,NULL,NULL,-1,-1,-1,-1,WBENCHSCREEN
};

/* Forhånds deklarering av alle prosedyrer og funksjoner */

void draw(char *,long,long),inhibit(int,long),redrawlines(void);
void cleanup(void),getbuffers(int),freebuffers(void);
void DeleteIO(struct IORequest *),FreeDisk(int),allocdisks(void);
void initdisks(int),motoroff(int),turnon(struct Gadget *);
void turnoff(struct Gadget *),goforit(int),writeop(char *,int,int);
void makeformatdata(int,int),recheck(long *),updaterootblock(int);
void writename(int),main(void);
int disposemsgs(void),toupper(int),abortretry(void),error(char *);
int OpenDisk(int),InitDisk(int),getdata(int,int,int),writedata(int,int,int);
struct IORequest *CreatExtIO(void);
char *to40(char *),getop(void);

/* Denne rutinen tegner en tekst i vinduet på posisjon X,Y */

void draw(s,x,y)
char *s;
long x,y;
{
	intuitext.IText = (UBYTE *)s;
	PrintIText(window->RPort,&intuitext,x,y);
}

/* Denne rutinen gir oss det fine DF0:BUSY icon'et. Dette forhindrer
 * AmigaDOS fra å tulle med diskett-stasjonen mens vi bruker den. */

void inhibit(d,t)
int d;
long t;
{
	struct MsgPort *handler;
	struct StandardPacket *packet = gpacket;

	handler = (struct MsgPort *)DeviceProc(df[d]);
	if (handler == NULL || port == NULL)
		return;
	packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
	packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
	packet->sp_Pkt.dp_Port = port;
	packet->sp_Pkt.dp_Type = ACTION_INHIBIT;
	packet->sp_Pkt.dp_Arg1 = t;
	PutMsg(handler,(struct Message *)packet);
	WaitPort(port);
	GetMsg(port);
}

/* Denne rutinen undersøker den globale meldingen og returnerer med en bokstav
 * som indikerer vilket gadget som var valgt. Den foretar også en ReplyMsg(). */

char getop()
{
	register long class;
	register short code;
	register char op;

	class = message->Class;
	code = message->Code;
	op = ' ';
	gadad = (struct Gadget *)(message->IAddress);
	ReplyMsg((struct Message *)message);
	message = NULL;
	if (class == CLOSEWINDOW) {
		op = 'q';
	} else if (class == GADGETDOWN || class == GADGETUP) {
		op = gadad->GadgetID;
	} else if (class == VANILLAKEY) {
		op = (char)code;
		gadad = NULL;
	}
	return((char)(toupper(op) & 0x7F));
}

/* Vi ønsker ikke at meldinger skal bli liggende i kø, da brukeren kan trykke
 * på forskjellige gadget flere ganger mens vi er opptatt med andre ting. Denne
 * rutinen dropper alle ventende meldinger og returnerer den siste. */

int disposemsgs()
{
	register char op = '\0';

	while (message = (struct IntuiMessage *)GetMsg(window->UserPort))
		op = getop();
	return((int)op & 0x7F);
}

/* Her venter vi på en quit eller retry tast. Vi aksepterer også go, space,
 * og carriage return. */

int abortretry()
{
	register char op;

	disposemsgs();
	for ( ; ; ) {
		while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) == NULL)
			WaitPort(window->UserPort);
		op = getop();
		if (op == 'Q' || op == 'R' || op == ' ' || op == 'G' || op == 10)
			return((int)op & 0x7F);
	}
	return(0);
}

/* Denne rutinen føyer til blanke tegn på slutten av en string intil den er på
 * 40 tegn. Brukes for å skrive til øverste linjen i vinduet. */

static char ibuf[41];

char *to40(s)
register char *s;
{
	register int i = 0;
	register char *p = ibuf;

	while (*s != 0)
		p[i++] = *s++;
	while (i < 40)
		p[i++] = ' ';
	p[i] = '\0';
	return(p);
}

/* Denne feil rutinen tegner en melding på toppen av vinduet og venter på et
 * valg fra brukeren. Etter at brukeren velger et svar, blankes meldingen og
 * svaret returneres. */

int error(s)
register char *s;
{
	int op;

	DisplayBeep(NULL);
	if (*s == '!' || !window) {
		if (output) {
			Write(output,s,(long)strlen(s));
			Write(output,"\n",1L);
		}
		cleanup();
	}
	else {
		draw(to40(s),COL1,LINE1+2);
		op = abortretry();
	}
	draw(to40(""),COL1,LINE1+2);
	return(op);
}

/* Hvis brukeren velger en diskett-stasjon, prøver denne rutinen å ta den i
 * bruk. Avhengig av om dette lar seg gjøre, tegnes det linjer mellom stasjonene
 * for å indikere "FRA" og "TIL" stasjon. */

void redrawlines()
{
	register int i,j;

	allocdisks();
	SetAPen(window->RPort,0L);
	RectFill(window->RPort,(long)LINESTART,(long)LINE2,(long)(STRSTART - 1),
			(long)(LINE6 + GADGHEIGHT));
	RectFill(window->RPort,(long)STRSTART,(long)LINE3,(long)LINEEND,
			(long)(LINE6 + GADGHEIGHT));
	SetAPen(window->RPort,1L);
	i = LINE3 + ((GADGHEIGHT + 1) >> 1) + source * (GADGHEIGHT + 1);

	for (j = 0; j < 4; j++)
		if (dest & (1 << j)) {
			Move(window->RPort,(long)LINESTART,(long)i);
			Draw(window->RPort,(long)LINEEND,
				(long)(LINE3 + ((GADGHEIGHT + 1) >> 1) + j * (GADGHEIGHT + 1)));
		}
}

/* Dette er vår avsluttnings rutine. Den frigjør stasjoner, sletter porter,
 * buffere og stenger vinduet og bibliotekene, og til slutt stopper programmet. */

void cleanup()
{
	register int i;

	for (i = 0; i < 4; i++)
		FreeDisk(i);
	if (gpacket)
		FreeMem((char *)gpacket,(long)sizeof(struct StandardPacket));
	if (port)
		DeletePort(port);
	freebuffers();
	if (window)
		CloseWindow(window);
	if (GfxBase)
		CloseLibrary((struct Library *)GfxBase);
	if (IntuitionBase)
		CloseLibrary((struct Library *)IntuitionBase);
	exit(0);
}

/* Denne rutinen prøver å lage m buffere. Vi prøver å la det være ledig minst
 * 32K med minne. Vi setter hibuf til det siste buffer som vi klarte å lage. */

void getbuffers(m)
int m;
{
	register int i;

	if (buffers[80] == NULL && (buffers[80] = (char *)
		AllocMem((long)TRACKSIZE,MEMF_CHIP|MEMF_PUBLIC)) == NULL)
		error(nobuf);
	hibuf = 0;
	for (i = 0; i < m; i++)
		if (buffers[i] == NULL && (AvailMem(MEMF_PUBLIC) < 32000 ||
			(buffers[i] = (char *)AllocMem((long)TRACKSIZE,MEMF_PUBLIC)) == NULL))
			break;
		else
			hibuf = i+1;
	if (hibuf < 1)
		error(nobuf);
	for ( ; i < 80; i++)
		if (buffers[i] != NULL) {
			FreeMem((char *)buffers[i],(long)TRACKSIZE);
			buffers[i] = NULL;
		}
}

/* Og denne rutinen frigjør bufferene. */

void freebuffers()
{
	register int i;

	for (i = 0; i < 81; i++)
		if (buffers[i] != NULL) {
			FreeMem((char *)buffers[i],(long)TRACKSIZE);
			buffers[i] = NULL;
		}
}

/* Vi prøver å lage en I/O request. Vi setter av minne for den, og klargjør noen
 * av portene. */

struct IORequest *CreatExtIO()
{
	register struct IORequest *ioReq;

	if ((ioReq = (struct IORequest *)AllocMem((long)sizeof(struct IOExtTD),
										MEMF_CLEAR | MEMF_PUBLIC)) == NULL)
		return (NULL);
	ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
	ioReq->io_Message.mn_Node.ln_Pri = 0;
	ioReq->io_Message.mn_ReplyPort = port;
	return(ioReq);
}

/* Denne rutinen frigjør en I/O request. */

void DeleteIO(ioExt)
struct IORequest *ioExt;
{
	FreeMem((char *)ioExt,(long)sizeof(struct IOExtTD));
}

/* Nå prøver vi å sette opp en diskett stasjon. Først prøver vi å lage en I/O
 * request, og så åpner vi stasjonen. Hvis noe av dette feiler, returnerer vi
 * med 0. Dette kan intreffe hvis en stasjon ikke eksisterer. Etter at dette
 * er gjort, stenger vi stasjonen for AmigaDOS og returnerer 1. */

int OpenDisk(i)
register int i;
{
	register struct IOExtTD **p = diskreq+i;

	if (*p)
		return(1);
	if ((*p = (struct IOExtTD *)CreatExtIO()) &&
		OpenDevice(TD_NAME,(long)i,(struct IORequest *)*p,0L) == 0) {
		inhibit(i,TRUE);
		return(1);
	}
	else {
		if (*p) {
			DeleteIO((struct IORequest *)*p);
			*p = NULL;
		}
		return(0);
	}
}

/* Her frigjøres en diskett-stasjon. Men først må vi sjekke at vi har stengt
 * den! Så stenger vi device'et, frigjør stasjonen, fjerner I/O request, og
 * avslutter. */

void FreeDisk(i)
register int i;
{
	register struct IOExtTD **p = diskreq+i;

	if (*p) {
		CloseDevice((struct IORequest *)*p);
		inhibit(i,FALSE);
		DeleteIO((struct IORequest *)*p);
		*p = NULL;
	}
}

/* Denne rutinen prøver å sette opp alle diskett-stasjonene som vi trenger,
 * basert på variablene source og dest. Hvis en stasjon ikke kan settes opp,
 * fjernes den fra både source og dest. Hvis source er satt opp som df0 og vi
 * ikke klarer å sette opp den, avsluttes programmet med fatal feil. Dette kan
 * faktisk forekomme hvis et annet program bruker df0. */

void allocdisks()
{
	register int i,need;

top:
	need = dest;
	if (source != -1)
		need |= 1 << source;
	for (i = 0; i < 4; i++, need >>= 1) {
		if (need & 1) {
			if (!OpenDisk(i)) {
				dest &= ~(1 << i);
				if (source == i) {
					if (i == 0)
						error("! I couldn't allocate the internal drive");
					source = 0;
					goto top;
				}
			}
		}
		else
			FreeDisk(i);
	}
}

/* Vi kaller denne rutinen hvis vi skal bruke denne stasjonen. Den henter
 * changecount (hvor mange ganger disketten er byttet), slik at lese/skrive
 * rutinen vår virker. */

int InitDisk(d)
register int d;
{
	register struct IOExtTD *p = diskreq[d];
	register int result;

	p->iotd_Req.io_Command = TD_CHANGENUM;
	DoIO((struct IORequest *)p);
	result = (diskChangeCount[d] != p->iotd_Req.io_Actual);
	diskChangeCount[d] = p->iotd_Req.io_Actual;
	return(result);
}

/* Denne rutinen setter opp alle stasjoner som skal brukes, og legger til
 * "FRA" stasjonen automatisk. */

void initdisks(mask)
int mask;
{
	register int d;

	if (source != -1)
		mask |= (1 << source);

	for (d = 0; d < 4; d++)
		if (mask & (1 << d))
			InitDisk(d);
}

/* Stopp diskett motoren, slik at noen kan ta ut og putte inn disketter. */

void motoroff(d)
register int d;
{
	register struct IOExtTD *p = diskreq[d];

	p->iotd_Req.io_Length = 0;
	p->iotd_Req.io_Command = TD_MOTOR;
	DoIO((struct IORequest *)p);
}

/* Denne rutinen aktiviserer spesifisert gadget. */

void turnon(g)
register struct Gadget *g;
{
	if (g->Flags & GADGDISABLED) {
		SetAPen(window->RPort,0L);
		RectFill(window->RPort,(long)g->LeftEdge,(long)g->TopEdge,
							   (long)g->LeftEdge+g->Width-1,
							   (long)g->TopEdge+g->Height-1);
		SetAPen(window->RPort,1L);
		OnGadget(g,window,NULL);
	}
}

/* Denne rutinen slår av spesifisert gadget. */

void turnoff(g)
register struct Gadget *g;
{
	if (!(g->Flags & GADGDISABLED))
		OffGadget(g,window,NULL);
}

/* Vår hoved rutine. Variabelen j inneholder "TIL" stasjonene som skrives nå. */

void goforit(again)
int again;
{
	register int i,j,k,t;
	register int ohbuf;

	turnoff(&gogadg);
	turnoff(&verifygadg);
	turnoff(&buffergadg);
	turnoff(&againgadg);
	turnon(&retrygadg);
	turnoff(&namegadg);
	RefreshGadgets(window->FirstGadget,window,NULL);
	j = dest;
	initdisks(j);
	ohbuf = hibuf;

	/* Hvis vi skal formatere, bruker vi bare ett buffer. Dette for å ungå en
	 * lang pause, som det vil bli hvis vi bygger opp den formaterte diskette i
	 * minnet først (og brukeren vil lure på om maskinen døde). Først sjekker
	 * vi at brukeren ikke prøver å kopiere uten buffer, da han i så tilfelle
	 * må belage seg på å bytte disketter 160 ganger. */

	if (source == -1)
		ohbuf = 1;
	if (ohbuf == 1 && source >= 0 && (dest & (1 << source))) {
		while (error("No buffering?") == 'R');
		goto finishup;
	}
	for (t = 0; t < 80; t += ohbuf) {
		if (! again) {
			for (k = 0; k < ohbuf && t + k < 80; k++) {
				if (disposemsgs() == 'Q')
					goto aborted;
				if (getdata(source,t + k,k) == 0)
					goto finishup;
			}
			if (source != -1) {
				if (dest & (1 << source)) {
					for (i = 0; i < 4; i++)
						if (j & (1 << i))
							motoroff(i);
					do {
						if (error("Enter destination disks") == 'Q')
							goto finishup;
					} while (!InitDisk(source));
					initdisks(j);
				}
			}
			if (ohbuf == 80)
				havedisk = 1;
		}
		for (k = 0; k < ohbuf && t + k < 80; k++) {
			if (disposemsgs() == 'Q')
				goto aborted;
			for (i = 0; i < 4; i++) {
				if (j & (1 << i)) {
					if (writedata(i,t + k,k) == 0) {
						j &= ~(1 << i);
						motoroff(i);
					}
				}
			}
		}
		if (!again) {
			if (source != -1 && (dest & (1 << source)) && t+k < 80) {
				motoroff(source);
				do {
					if (error("Enter source disk") == 'Q')
					goto finishup;
				} while (!InitDisk(source));
			}
		}
	}
	goto finishup;

	/* Under avslutning slår vi av alle motorer, sletter meldinger, og returnerer */

aborted:
	j = 0;
finishup:
	for (i = 0; i < 4; i++)
		if (diskreq[i])
			motoroff(i);
	for (i = 0; i < 4; i++)
		if ((dest-j) & (1<<i)) {
			msg[7] = i + '0';
			error(msg);
		}
	draw(blank,COL3,LINE3+3);
	disposemsgs();
}

/* Denne rutinen skriver meldingene 'reading, writing, ver'ing'. Den fyller inn
 * spor og disk nummer. */

void writeop(s,t,d)
char *s;
int t,d;
{
	register short tt;

	if (d != -1) {
		tt = (short)t;
		s[14] = '0' + tt / 10;
		s[15] = '0' + tt % 10;
		s[19] = '0' + d;
		draw(s,COL3,LINE3+3);
	}
}

/* Denne rutinen henter data fra disk d, spor t, til buffer b. */

int getdata(d,t,b)
register int d,t,b;
{
	register struct IOExtTD *p = diskreq[d];
	register int i = 0;

	if (d == -1) {
		makeformatdata(t,b);
		return(1);
	}
	do {
		writeop(reading,t,d);
		p->iotd_Req.io_Length = TRACKSIZE;
		p->iotd_Req.io_Data = (APTR)buffers[80];
		p->iotd_Req.io_Command = ETD_READ;
		p->iotd_Count = diskChangeCount[d];
		p->iotd_Req.io_Offset = t * (long)TRACKSIZE;
		DoIO((struct IORequest *)p);
		memcpy(buffers[b],buffers[80],TRACKSIZE);
	} while (p->iotd_Req.io_Error != 0 &&
			((i = error("Read Error; Quit/Retry?")) != 'Q'));
	if (t == 40 && i != 'Q')
		writename(b);
	return (i != 'Q');
}

/* writedata er det motsatte av foregående rutine. Hvis verify er på verifiseres
 * i tillegg sporet. */

int writedata(d,t,b)
register int d,t,b;
{
	register struct IOExtTD *p = diskreq[d];
	register int i = 0;

top:
	if (t == 40)
		updaterootblock(b);
	do {
		writeop(writing,t,d);
		memcpy(buffers[80],buffers[b],TRACKSIZE);
		p->iotd_Req.io_Length = TRACKSIZE;
		p->iotd_Req.io_Data = (APTR)buffers[80];
		p->iotd_Req.io_Command = TD_FORMAT;
		p->iotd_Count = diskChangeCount[d];
		p->iotd_Req.io_Offset = t * (long)TRACKSIZE;
		DoIO((struct IORequest *)p);
	} while (p->iotd_Req.io_Error != 0 &&
			((i = error("Write Error; Quit/Retry?")) != 'Q'));
	if (i == 'Q')
		return(0);
	if (verify) {
		writeop(veriing,t,d);
		p->iotd_Req.io_Length = TRACKSIZE;
		p->iotd_Req.io_Data = (APTR)buffers[80];
		p->iotd_Req.io_Command = ETD_READ;
		p->iotd_Count = diskChangeCount[d];
		p->iotd_Req.io_Offset = t * (long)TRACKSIZE;
		DoIO((struct IORequest *)p);
		if ((p->iotd_Req.io_Error != 0 ||
			memcmp(buffers[80],buffers[b],TRACKSIZE)) &&
			((i = error("Verify Error; Quit/Retry?")) != 'Q')) {
			if (t == 0)
				InitDisk(d);
			goto top;
		}
	}
	return (i != 'Q');
}

/* Denne rutinen setter opp et spor for formattering. Hvis dette er spor 40
 * må noen tilleggs data lages for å opprette katalog og bitmap. */

void makeformatdata(t,b)
int t,b;
{
	register long *p;
	register long cs;
	register long i;
	register unsigned char *q;

	p = (long *)buffers[b];
	cs = ((long)'D' << 24) |
		 ((long)'O' << 16) |
		 ((long)'S' << 8) +
		 (((long)t & 48) << 16);
	for (i = 0; i < (TRACKSIZE >> 2); i++)
		*p++ = cs + (i & 0x0CFF);
	if (t != 40)
		return;
	memset(buffers[b],0,1024);
	p = (long *)buffers[b];
	p[0]  = 2;
	p[3]  = 0x48;
	p[78] = 1;
	p[79] = 0x371;
	q = (unsigned char *)(p + 108);
	*q++ = strlen(namebuf);
	strcpy(q,namebuf);
	p[127] = 1;
	p += 128;
	for (i = 1; i < 55; i++)
		p[i] = 0xffffffff;
	p[0] = 0xc000c037;
	p[28] = 0xffff3fff;
	p[55] = 0x3fffffff;
}

/* Denne rutinen kalkulerer sjekksummen for en blokk og oppdaterer den i ord 5. */

void recheck(w)
register long *w;
{
	register int i;
	register long cs;

	cs = 0;
	for (i = 0; i < 128; i++)
		cs += w[i];
	w[5] -= cs;
}

/* Sett datostempel for create og modify, og oppdater sjekksum. */

void updaterootblock(b)
register int b;
{
	DateStamp((long *)(buffers[b] + 420));
	DateStamp((long *)(buffers[b] + 484));
	recheck((long *)buffers[b]);
}

/* Skriv volumnavnet fra spor 40 til string gadget'en. */

void writename(b)
int b;
{
	register int i;

	i = buffers[b][432];
	RemoveGadget(window,&namegadg);
	if (i > 31)
		i = 31;
	memcpy((char *)namebuf,(char *)&buffers[b][433],i);
	namebuf[i] = 0;
	AddGadget(window,&namegadg,-1L);
	RefreshGadgets(&namegadg,window,NULL);
}

/* Og til slutt vår main rutine!  Dette var forferdelig langt, ikke sant? */

void main()
{
	register char op;

	if (verify)
		verifygadg.GadgetText = &verifyontext;
	else
		verifygadg.GadgetText = &verifyofftext;
	if (buffer) {
		buffergadg.GadgetText = &bufferontext;
		hibuf = 80;
	}
	else {
		buffergadg.GadgetText = &bufferofftext;
		hibuf = 1;
	}

	/* Og nå får vi prøve å åpne diverse ting!  Først intuition, så graphics,
	 * og til slutt vinduet og I/O portene. Hvis noen av disse feiler, avsluttes
	 * programmet. */

	if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",33L)) != NULL &&
		(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L)) != NULL &&
		(window = OpenWindow(&newwindow)) != NULL &&
		(port = CreatePort(0L,0L)) &&
		(gpacket = (struct StandardPacket *)AllocMem(
			(long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))) {

		/* Vi tegner linjer mellom stasjonene som er valget og går så inn i
		 * hoved løkken. Vi venter så på en melding fra intuition. Når denne
		 * kommer brukes en case instruksjon for å finne ut vilken melding
		 * det var. */

		redrawlines();
		getbuffers(hibuf);
		for ( ; ; ) {
			if (havedisk)
				turnon(&againgadg);
			else
				turnoff(&againgadg);
			turnoff(&retrygadg);
			turnon(&gogadg);
			turnon(&verifygadg);
			turnon(&buffergadg);
			if (source == -1)
				turnon(&namegadg);
			else
				turnoff(&namegadg);
			RefreshGadgets(window->FirstGadget,window,NULL);
			while ((message = (struct IntuiMessage *)GetMsg(window->UserPort)) == NULL)
				WaitPort(window->UserPort);
			op = getop();
			switch(op) {

				/* Shift-0,1,2,3 veksler "TIL" stasjonene, mens 0,1,2,3 veksler
				 * "FRA" stasjonene og fortsetter. */

				case '=':
					dest ^= 1;
					goto redrawem;
				case '!':
					dest ^= 2;
					goto redrawem;
				case '\"':
					dest ^= 4;
					goto redrawem;
				case '#':
				case '\xA3':				/* pund tegn */
					dest ^= 8;
					goto redrawem;
				case '0':
				case '1':
				case '2':
				case '3':
					source = (int)(op - '0');
redrawem :
					redrawlines();
					break;

				/* Hvis verify er valgt, veksler vi verify flagen og oppdaterer
				 * gadget. */

				case 'V':
					verify = ! verify;
					RemoveGadget(window,&verifygadg);
					if (verify)
						verifygadg.GadgetText = &verifyontext;
					else
						verifygadg.GadgetText = &verifyofftext;
					AddGadget(window,&verifygadg,-1L);
					RefreshGadgets(&verifygadg,window,NULL);
					break;

				/* Buffer behandles på samme måte */

				case 'B':
					buffer = ! buffer;
					RemoveGadget(window,&buffergadg);
					if (buffer) {
						buffergadg.GadgetText = &bufferontext;
						getbuffers(80);
					}
					else {
						buffergadg.GadgetText = &bufferofftext;
						getbuffers(1);
					}
					havedisk = 0;
					AddGadget(window,&buffergadg,-1L);
					RefreshGadgets(&buffergadg,window,NULL);
					break;

				/* Format tegner om linjen til "FRA" stasjonen og aktiviserer
				 * volum navn gadget */

				case 'F':
					source = -1;
					havedisk = 0;
					goto redrawem;

				/* Navn gadget, hvis dette er valgt fra tastaturet aktiviseres
				 * gadget'en. */

				case 'N':
					if (gadad == NULL && source == -1)
						ActivateGadget(&namegadg,window,NULL);
					break;

				/* 'Go' gadget'en, enter, linje skift eller space starter kopiering */

				case 'G':
				case 10:
				case 32:
				case 13:
					goforit(0);
					break;

				/* 'Again' starter skrivin hvis den er aktiv. */

				case 'A':
					if (! havedisk)
						while (error("No disk in memory") == 'R');
					else
						goforit(1);
					break;

				/* 'Retry' ignoreres her */

				case 'R':

				/* 'Quit' hånteres på slutten av løkken. */

				case 'Q':

				default:
					break;
			}

			/* Hvis siste valg var 'Quit', avslutter vi. */

			if (op == 'Q')
				break;
		}
	}
	else
		error("! couldn't open window");

	/* Frigjør minne og avslutt */

	cleanup();
}
