
#include <exec/types.h>
#include <exec/memory.h>
#include <devices/trackdisk.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <workbench/icon.h>

/* Diskspezifische Daten */

#define NUMHEADS	2
#define BLOCKSIZE	TD_SECTOR
#define SIZE		(BLOCKSIZE/4)
#define NUM_ENTRIES	(SIZE-51-5)
#define NUMBLOCKS	(NUMCYLS * NUMHEADS * NUMSECS)
#define ROOTBLOCK	(NUMBLOCKS / 2)
#define NUMLONGS	(NUMBLOCKS / 32)
#define SECS_PER_TRACK	(NUMSECS * NUMHEADS)

/* Diskblockparameter */

#define TYPE		0
#define HEADER_KEY	1
#define HIGHEST_SEQ	2
#define	SEQ_NUM		2
#define BLOCK_COUNT	2
#define	DATA_SIZE	3
#define	FIRST_DATA	4
#define	NEXT_DATA	4
#define CHECKSUM	5
#define DIR_HASHTAB	6
#define FH_ENDLIST	6
#define FH_BLOCKLIST	(SIZE-51)
#define BITMAPINDEX	(SIZE-49)
#define NAME		(SIZE-20)
#define CREATE_TICKS	(SIZE-5)
#define HASHCHAIN	(SIZE-4)
#define	PARENT		(SIZE-3)
#define EXTENSION	(SIZE-2)
#define SECONDARY_TYPE	(SIZE-1)
#define	BITMAP_CHECKSUM	0
#define NAMEOFFSET	NAME*4+1
#define NAMELENGTH	NAME*4

/*	Diskblock-Typ Definitionen 	*/

#define T_SHORT		2
#define T_DATA		8
#define T_LIST		16
#define ST_ROOT		1
#define ST_DIR		2
#define ST_FILE		-3

#define EMPTY		(unsigned long*) 0
#define BLOCKED		(unsigned long*) 0xFFFFFFFF

#define SOURCE		0
#define DESTINATION	1

/*	verwendete Betriebssytemroutinen : exec.library		*/

extern void		*AllocMem();
extern void		CloseLibrary();
extern struct IOExtTD	*CreateExtIO();
extern struct MsgPort	*CreatePort();
extern void		DeleteExtIO();
extern void		DeletePort();
extern void		DoIO();	
extern void		FreeMem();
extern long		OpenDevice();
extern struct Library	*OpenLibrary();
extern void		SendIO();
extern void		WaitIO();	

/*	verwendete Betriebssytemroutinen : icon.library		*/

extern struct DiskObject	GetDiskObjekt();
extern char*			FindToolType();
extern void			FreeDiskObject();

/*	verwendete Betriebssytemroutinen : dos.library		*/

extern struct MsgPort		*DeviceProc();

/*	Unsere Workbenchmessage	(von Aztec vordefiniert)	*/

extern struct WBStartup		*WBenchMsg;

/*	Globale Variablen	*/

struct Library		*IconBase;
struct IOExtTD		*diskreq0,*diskreq1,*diskreq2;
struct MsgPort		*diskport;
struct DiskObject	*dop;

unsigned long	diskchangecount0,diskchangecount1;
unsigned long	dirstack   [NUMBLOCKS];
unsigned long	sectormap  [NUMBLOCKS];
unsigned long	*buffermap [NUMBLOCKS];
unsigned long	*readmap   [NUMBLOCKS];
unsigned long	filestack  [NUMBLOCKS/2];
unsigned short	trackmap   [NUMTRACKS];
unsigned short	dirstackptr = 0,filestackptr = 0,smptr = ROOTBLOCK;
unsigned short	wbflag = 0, formatflag = 1, fastloadflag = 1;
unsigned long	seccount, rootoff = 2;
unsigned long	*trackbuffer;
unsigned long	*oldbuffer = NULL;
unsigned long	sourcedrive = 0 ,destinationdrive = 1;
char		sourcedevice[5] = "DF0:";
char		destdevice[5] = "DF1:";

/*	Interne Funktionen	*/

unsigned long	popdir(), popfile(), getfreesector();
unsigned long	*allocbuffer();
unsigned long 	*GetSector();
short		is_special();


main(argc,argv)
register int	argc;
register char	*argv[];
{
	register short int 	drivetype,i;
	register char		*str;

	drivetype = SOURCE;
	if (argc) {

		/* Aufruf vom CLI */

		for (i=1; i < argc; i++) {
			if (scmp(argv[i],"NOFORMAT",8)) {
				formatflag = 0;
			} else if (scmp(argv[i],"FASTDIR",7)) {
				fastloadflag = 0;
			} else if (scmp(argv[i],"FROM",4)) {
				drivetype = SOURCE;
			} else if (scmp(argv[i],"TO",2)) {
				drivetype = DESTINATION;
			} else if (scmp(argv[i],"DF",2)) {
				if (drivetype == SOURCE) {
					sourcedrive = (ULONG) atoi(argv[i]+2);
					drivetype = DESTINATION;
				} else {
					destinationdrive = (ULONG) atoi(argv[i]+2);
				}
			} else {
				printf("Usage   : %s [FROM] drive [TO] drive [NOFORMAT] [FASTDIR]\n",argv[0]);
				printf("Defaults: FROM df0: TO df1:\n");
				exit(0);
			}
		}
	} else {

		/* Aufruf von Workbench */

		wbflag = 1;

		if (!(IconBase = OpenLibrary(ICONNAME,0L))) {
			printf("Where is the Icon-Library ?\n");
			error();
		}
		if (!(dop = GetDiskObject(WBenchMsg -> sm_ArgList -> wa_Name))) {
			printf("Couldn't find Disk-Object!\n");
			error();
		}
		if (str = FindToolType(dop -> do_ToolTypes, "NOFORMAT")) {
			if (scmp(str,"ON",2)) {
				formatflag = 0;
			}
		}
		if (str = FindToolType(dop -> do_ToolTypes, "FASTDIR")) {
			if (scmp(str,"ON",2)) {
				fastloadflag = 0;
			}
		}
		if (str = FindToolType(dop -> do_ToolTypes, "FROM")) {
			if (scmp(str,"DF",2)) {
				sourcedrive = (ULONG) atoi(str+2);
			}
		}
		if (str = FindToolType(dop -> do_ToolTypes, "TO")) {
			if (scmp(str,"DF",2)) {
				destinationdrive = (ULONG) atoi(str+2);
			}
		}
		FreeDiskObject(dop);		
	}

	sourcedevice[2] = (char) ('0' + (char) sourcedrive);
	destdevice[2] = (char) ('0' + (char) destinationdrive);
	if (sourcedrive == destinationdrive) {
		printf("Can't copy from df%1ld: to df%1ld: !\n");
		error();
	}
	
	openall ();

	printf("\n [3;4;33m FastDisk V1.5 [0;1;31m -  Written 1987 by Torsten Stolpmann [0m\n\n");
	printf(" Please insert SOURCE      Disk in Drive df%1ld:\n",sourcedrive);
	printf(" Please insert DESTINATION Disk in Drive df%1ld:\n",destinationdrive);
	printf(" Press RETURN to continue\n");
	getchar();

	checkdisks();
	init ();
	travel (ROOTBLOCK);
	moveblock ();
	sortfiles ();
	makefiles ();
	makedirs ();
	makeroot ();
	writeheaders ();
	writeblank();
	writeboot ();
	closeall ();
}

travel(head)
unsigned long	head;
{
	register unsigned long	*headbuffer,*workbuffer,*auxbuffer;
	register unsigned long	sector,hc;
	register unsigned short	i,count;

	/*	Rekursives Durchlaufen des Directory-Trees in Zwischenordnung	*/

	headbuffer = buffermap[head];
	for (i=DIR_HASHTAB; i<=FH_BLOCKLIST; i++) {
		sector=headbuffer[i];
		if (sector) {
			workbuffer = buffermap[sector] = GetSector(sector);
			if ((workbuffer[TYPE] == T_SHORT) &&
			    (workbuffer[SECONDARY_TYPE] == ST_DIR)) {
				if (fastloadflag) {
					sectormap[sector] = ROOTBLOCK+2+dirstackptr;
				} else {
					sectormap[sector] = smptr++;
				}
				pushdir(sector);
				travel(sector);
			}
			else {
				sectormap[sector] = smptr++;
				pushfile(sector);

					/*	Existieren File List Blocks ?	*/

				auxbuffer = buffermap[sector];
				count = workbuffer[HIGHEST_SEQ];
				while (auxbuffer[EXTENSION]) {
					sector = auxbuffer[EXTENSION];
					auxbuffer = buffermap[sector] = GetSector(sector);
					count += auxbuffer[BLOCK_COUNT];
					sectormap[sector] = smptr++;
				}

				/*	Ist es ein .info-File?	*/

				if (is_special(workbuffer)) {
					sectormap[workbuffer[FIRST_DATA]] = smptr;
					smptr += count;
				}
			}

			/* Weitere Header-Blocks in der Hashchain ?	 */

			hc=workbuffer[HASHCHAIN];
			while (hc) {
				workbuffer = buffermap[hc] = GetSector(hc);
				if ((workbuffer[TYPE] == T_SHORT) &&
				    (workbuffer[SECONDARY_TYPE] == ST_DIR)) {
					sectormap[hc] = ROOTBLOCK+2+dirstackptr;
					if (fastloadflag) {
						sectormap[hc] = ROOTBLOCK+2+dirstackptr;
					} else {
						sectormap[hc] = smptr++;
					}
					pushdir(hc);
					travel(hc);
				}
				else {
					sectormap[hc]=smptr++;
					pushfile(hc);

					/*	Existieren File List Blocks ?	*/

					auxbuffer = buffermap[hc];
					count = workbuffer[HIGHEST_SEQ];
					while (auxbuffer[EXTENSION]) {
						sector = auxbuffer[EXTENSION];
						auxbuffer = buffermap[sector] = GetSector(sector);
						count += auxbuffer[BLOCK_COUNT];
						sectormap[sector] = smptr++;
					}

					/*	Ist es ein .info-File ?		*/

					if (is_special(workbuffer)) {
						sectormap[workbuffer[FIRST_DATA]] = smptr;
						smptr += count;
					}
				}
				hc=workbuffer[HASHCHAIN];
			}
		}
	}
}

moveblock()
{
	register unsigned long	sector,dest;
	register unsigned short diff;

	/*	Zentriert die Header-Blocks auf der Diskette	*/

	if ((diff = (smptr-1-ROOTBLOCK)/SECS_PER_TRACK) > 2) {
		diff = (((diff/2)+1) * SECS_PER_TRACK) - 2;
	} else {
		diff = 0;
	}
	if (fastloadflag) {
		rootoff += dirstackptr;
		diff -= dirstackptr;
	}

	for (sector=2; sector < NUMBLOCKS; ++sector) {
		dest = sectormap[sector];
		if ((dest)
		 && (dest != ROOTBLOCK)
		 && (dest != ROOTBLOCK+1)
		 && !((fastloadflag) &&
		      (buffermap[sector][SECONDARY_TYPE] == ST_DIR))) {
			if ((dest >= ROOTBLOCK+2) && (dest-diff < ROOTBLOCK+rootoff)) {
				sectormap[sector] -= (ULONG) (diff+rootoff);
			} else {
				sectormap[sector] -= (ULONG) diff;					}
		}
	}
	seccount = (ULONG) ROOTBLOCK - diff;
}

sortfiles()
{
	register short int	file,count,top;
	register unsigned long	temp;

	/* Erst die .info-Files kopieren */

	top = filestackptr-1;
	for (file = 0; file < top;file++) {
		if (is_special(buffermap[filestack[file]])) {
			temp = filestack[file];
			for (count=file; count < top; count++) {
				filestack[count] = filestack[count+1];
			}
			filestack[top--] = temp;
			file--;
		}
	}
}

short scmp(s1,s2,len)
register char	*s1, *s2;
register short	len;
{
	register short	i;

	/* strcmp() ohne Beachtung von Upper-/Lower-Case */

	for (i=0; i <len; i++) {
		if (toupper(*(s1++)) != *(s2++)) {
			return (0);
		}
	}
	return (1);
}

short is_special(buffer)
register unsigned char	*buffer;
{

	/* Diese Files werden direkt hinter ihrem Headerblock abgelegt */

	if (fastloadflag) {
		return (1);
	}

	if (scmp(buffer+NAMEOFFSET+buffer[NAMELENGTH]-5,".INFO",5)) {
		return (1);
	}

	if (scmp(buffer+NAMEOFFSET,"SYSTEM-CONFIGURATION",20)) {
		return (1);
	}

	if (scmp(buffer+NAMEOFFSET,"STARTUP-SEQUENCE",16)) {
		return (1);
	}
	return (0);
}

unsigned long getfreesector()
{

	/* Liefert naechsten freier Sector auf der Diskette */

	if (seccount == 2) {
		return (seccount = NUMBLOCKS-1);
	} else {
		return (--seccount);
	}
}

makefiles()
{
	register unsigned long	file;

	/* kopiert alle File-Datenblocks auf die Zieldiskette.
	   Die neuen Sectorpositionen werden in den Headerblocks korrigiert. */

	while (file = popfile()) {
		dofile(buffermap[file]);
	}
}

dofile(headbuffer)
register unsigned long	*headbuffer;
{
	register unsigned long	*workbuffer;
	register unsigned long	sector,next_sector,more_to_come;

	if (headbuffer[FIRST_DATA]) {
		workbuffer = GetSector(headbuffer[FIRST_DATA]);
		if (is_special(headbuffer)) {
			sector = sectormap[headbuffer[FIRST_DATA]];
			if (sector == ROOTBLOCK) {
				sector += rootoff;
			}
		} else {
			sector = getfreesector();
		}
		sectormap[headbuffer[FIRST_DATA]] = sector;
		do {
			workbuffer[HEADER_KEY]=sectormap[headbuffer[HEADER_KEY]];
			more_to_come = workbuffer[NEXT_DATA];
			if (more_to_come) {
				if (is_special(headbuffer)) {
					next_sector = sector + 1;
					if (next_sector == ROOTBLOCK) {
						next_sector += rootoff;
					}
				} else {
					next_sector = getfreesector();
				}
				workbuffer[NEXT_DATA] = next_sector;
			}
			DoCheckSum(workbuffer,CHECKSUM);
			PutSector(sector,workbuffer);
			if (more_to_come) {
				workbuffer = GetSector(more_to_come);
				sector = sectormap[more_to_come] = next_sector;
			}
		} while (more_to_come);
	}
		/*	Aenderungen in File Header & File List Blocks	*/

	do {
		more_to_come = headbuffer[EXTENSION];
		sector = FH_BLOCKLIST;
		while (sector >= FH_ENDLIST) {
			headbuffer[sector--]=sectormap[headbuffer[sector]];
		}
		headbuffer[PARENT]     = sectormap[headbuffer[PARENT]];
		headbuffer[HEADER_KEY] = sectormap[headbuffer[HEADER_KEY]];
		headbuffer[FIRST_DATA] = sectormap[headbuffer[FIRST_DATA]];
		headbuffer[HASHCHAIN]  = sectormap[headbuffer[HASHCHAIN]];
		headbuffer[EXTENSION]  = sectormap[headbuffer[EXTENSION]];
		DoCheckSum(headbuffer,CHECKSUM);
		if (more_to_come) {
			headbuffer = buffermap[more_to_come];
		}
	} while (more_to_come);
}

makedirs()
{
	register unsigned long	*headbuffer, sector, dir;

	/* Aenderungen in den Directory-Header Blocks */

	while (dir = popdir()) {
		headbuffer = buffermap[dir];
		for ( sector = FH_BLOCKLIST;
		      sector >= FH_ENDLIST;
		      --sector) {
			if (headbuffer[sector]) {
				headbuffer[sector]=sectormap[headbuffer[sector]];
			}
		}
		headbuffer[PARENT]     = sectormap[headbuffer[PARENT]];
		headbuffer[HEADER_KEY] = sectormap[headbuffer[HEADER_KEY]];
		headbuffer[HASHCHAIN]  = sectormap[headbuffer[HASHCHAIN]];
		headbuffer[EXTENSION]  = sectormap[headbuffer[EXTENSION]];
		DoCheckSum(headbuffer,CHECKSUM);
	}
}		

makeroot()
{
	register unsigned long	*headbuffer,*workbuffer;
	register unsigned long	sector;
	register unsigned long	bmsector,mask;
	register unsigned short	offset,count;

	workbuffer = allocbuffer();
	headbuffer = buffermap[ROOTBLOCK];

	/* Aenderungen im Rootblock */

	bmsector = sectormap[headbuffer[BITMAPINDEX]];
	for ( sector = FH_BLOCKLIST; sector >= FH_ENDLIST; --sector) {
		if (headbuffer[sector]) {
			headbuffer[sector]=sectormap[headbuffer[sector]];
		}
	}
	headbuffer[BITMAPINDEX] = bmsector;
	headbuffer[CREATE_TICKS] += 1;	/* Wichtig ! Hiermit wird gesichert, */
					/* dass AmigaDOS Quell- und Zieldisk */
					/* unterscheiden kann. */
	DoCheckSum(headbuffer,CHECKSUM);

	/* Neuen Bit-Map Sector erstellen */

	for (count=1; count <= NUMLONGS; ++count) {
		workbuffer[count] = 0xFFFFFFFF;
	}

	for (count = 2; count < NUMBLOCKS; ++count) {
		sector = sectormap[count];
		if (sector) {
			offset = 1 + ((sector-2)/32);
			mask   = (ULONG) 1 << ((sector-2) & 0x1f);
			workbuffer[offset] -= (ULONG) mask;
		}
	}
	DoCheckSum(workbuffer,BITMAP_CHECKSUM);
	PutSector(bmsector,workbuffer);
}

writeheaders()
{
	register unsigned short	buffer;

	/* Alle Header-Blocks werden auf Zieldisk geschrieben */

	for (buffer=0; buffer<NUMBLOCKS; ++buffer) {
		dirstack[buffer] = NULL;
	}

	for (buffer = 2; buffer<NUMBLOCKS; ++buffer) {
		if (buffermap[buffer]) {
			dirstack[sectormap[buffer]] = (ULONG) buffermap[buffer];
			buffermap[buffer] = NULL;
		}
	}

	for (buffer = 0; buffer<NUMBLOCKS; ++buffer) {
		if (dirstack[buffer]) {
			PutSector((ULONG) buffer,dirstack[buffer]);
		}
	}
	PutSector(NULL,NULL);
}

writeblank()
{
	register short int	i;

	/* Formatiert nicht belegte Tracks auf der Zieldisk */

	if (formatflag) {
		for (i=0; i < NUMTRACKS; i++) {
			if ( !(trackmap[i])) {
				formattrack(i);
				WaitIO(diskreq2);
				trackmap[i]=1;
			}
		}
	}
}

writeboot()
{
	register unsigned long *workbuffer;

	/* Bootsectoren uebertragen */

	workbuffer = allocbuffer();
	ReadSector (0L,workbuffer);
	WriteSector(0L,workbuffer);
	WaitIO(diskreq1);
	ReadSector (1L,workbuffer);
	WriteSector(1L,workbuffer);
	WaitIO(diskreq1);
	diskreq1 -> iotd_Req.io_Command = ETD_UPDATE;
	diskreq1 -> iotd_Count		= diskchangecount1;
	DoIO (diskreq1);
	releasebuffer(workbuffer);
}

init()
{
	register unsigned long	*headbuffer,*workbuffer,*buffer;
	register unsigned long	count,offset,mask;

	/* Feldinitialisierungen */

	for (count=0L; count < NUMBLOCKS; ++count) {
		sectormap [count] = NULL;
		buffermap [count] = NULL;
		dirstack  [count] = NULL;
		readmap   [count] = EMPTY;
	}
	sectormap[0] = 0L;
	sectormap[1] = 1L;

	/* Trackbuffer */

	for (count=0; count <= NUMTRACKS; count++) {
		trackmap[count] = 0;
	}

	if ( !(trackbuffer = AllocMem(NUMSECS * BLOCKSIZE, MEMF_CHIP | MEMF_CLEAR))) {
		printf("Can't allocate Trackbuffer !\n");
		error();
	}

	/* Rootblock laden */

	smptr = ROOTBLOCK;
	headbuffer = buffermap[ROOTBLOCK] = GetSector(ROOTBLOCK);
	sectormap[ROOTBLOCK] = smptr++;
	sectormap[headbuffer[BITMAPINDEX]] = smptr++;

	/* Markieren aller nicht belegter Bloecke (BAM-Selection) */

	workbuffer = GetSector(headbuffer[BITMAPINDEX]);
	for (count = 2L; count < NUMBLOCKS; ++count) {
		offset = 1L + ((count-2L)/32L);
		mask   = 1L << ((count-2L) & 0x1fL);
		if (workbuffer[offset] & mask) {
			if (readmap[count]) {
				releasebuffer(readmap[count]);
			}
			readmap[count] = BLOCKED;
		}
	}
	readmap[ROOTBLOCK] = readmap[headbuffer[BITMAPINDEX]] = BLOCKED;
	readmap[0] = readmap[1] = BLOCKED;
	releasebuffer(workbuffer);
}

pushdir(sector)
register unsigned long	sector;
{
	dirstack[dirstackptr++] = sector;
}

unsigned long popdir()
{
	if ((dirstackptr) == 0)
		return (NULL);
	else
		return (dirstack[--dirstackptr]);
}

pushfile(sector)
register unsigned long	sector;
{
	filestack[filestackptr++] = sector;
}

unsigned long popfile()
{
	if ((filestackptr) == 0)
		return (NULL);
	else
		return (filestack[--filestackptr]);
}

DoCheckSum(buffer,pos)
register unsigned long	*buffer;
register unsigned short	pos;
{
	register unsigned long	count;
	register unsigned long	checksum;

	/* CheckSum fuer normale und Bit-Map Blocks errechnen */

	buffer[pos] = checksum = NULL;
	for (count=0; count < SIZE; ++count) {
		checksum += (unsigned long) buffer[count];
	}
	buffer[pos] -= (unsigned long) checksum;
}

ReadSector (sector,buffer)
register unsigned long sector,*buffer;
{
	unsigned long	offset = sector * BLOCKSIZE;

	/* Laedt einen Sector von der Quell-Diskette */

	diskreq0 -> iotd_Req.io_Length 	= BLOCKSIZE;      
	diskreq0 -> iotd_Req.io_Data 	= (APTR) buffer;	
	diskreq0 -> iotd_Req.io_Command = ETD_READ;
	diskreq0 -> iotd_Count 		= diskchangecount0;
	diskreq0 -> iotd_Req.io_Offset 	= offset;
	DoIO (diskreq0);
	if (diskreq0 -> iotd_Req.io_Error) {
		printf("Error %d while reading sector %4d from drive df%1ld: !\n",
			(BYTE) diskreq0 -> iotd_Req.io_Error,
			sector, sourcedrive);
		error();
	}
}

unsigned long *GetSector(sector)
register unsigned long	sector;
{
	register unsigned long	*workbuffer,*temp;
	register unsigned long	track,sec;

	/* Holt einen Sector von der Quell-Diskette oder */
	/* liefert den entsprechenden Buffer. */

	Chk_Abort();

	if (sector >= NUMBLOCKS) {
		printf("Illegal sector detected, Disk in drive df%1ld: is corrupt !\n",
			sourcedrive);
		printf("Use diskdoctor to correct this disk.\n");
		error();
	}
	if (readmap[sector]) {
		if (readmap[sector] == BLOCKED) {
			printf("Double Reference to sector %ld, disk in df%1ld: is corrupt !\n",
				sector,sourcedrive);
			printf("Use diskdoctor to correct this disk.\n");
			error();
		} else {
			temp = readmap[sector];
			readmap[sector] = BLOCKED;
			return (temp);
		}
	} else {
		workbuffer = allocbuffer();
		ReadSector (sector,workbuffer);
		readmap[sector] = BLOCKED;
		track = sector / SECS_PER_TRACK;
		for (sec=track*SECS_PER_TRACK; sec < (track+1)*SECS_PER_TRACK; sec++) {
			if (readmap[sec] == EMPTY) {
				if (readmap[sec] = allocbuffer()) {
					ReadSector(sec,readmap[sec]);
				} else {
					break;
				}
			}
		}
		return (workbuffer);
	}
}

formattrack(track)
register short int track;
{

	/* Formatiert einen Track auf der Zieldiskette */

	Chk_Abort();

	diskreq2 -> iotd_Req.io_Length	= NUMSECS * BLOCKSIZE;      
	diskreq2 -> iotd_Req.io_Data	= (APTR) trackbuffer;	
	diskreq2 -> iotd_Req.io_Command = TD_FORMAT;
	diskreq2 -> iotd_Count		= diskchangecount1;
	diskreq2 -> iotd_Req.io_Offset	= track * NUMSECS * BLOCKSIZE;
	SendIO (diskreq2);
	if (diskreq1 -> iotd_Req.io_Error) {
		printf("Error %ud while formatting Cylinder %2ud on Drive df%1ld: !\n",
			(BYTE) diskreq2 -> iotd_Req.io_Error,
			track/2, destinationdrive);
		error();
	}
}

WriteSector(sector,buffer)
register unsigned long sector;
register unsigned long *buffer;
{
	register unsigned long	offset = sector * BLOCKSIZE;
	register short int	track;

	/* Falls noch nicht geschehen, formatieren des Ziel-Tracks */

	track = (sector / NUMSECS);
	if ( (formatflag) && !(trackmap[track])) {
		formattrack(track);
		trackmap[track] = 1;
	}

	/* Schreibt einen Sector auf die Ziel-Diskette */

	diskreq1 -> iotd_Req.io_Length	= BLOCKSIZE;      
	diskreq1 -> iotd_Req.io_Data	= (APTR) buffer;	
	diskreq1 -> iotd_Req.io_Command = ETD_WRITE;
	diskreq1 -> iotd_Count		= diskchangecount1;
	diskreq1 -> iotd_Req.io_Offset	= offset;
	SendIO (diskreq1); /* Asynchroner Schreibzugriff */
}

PutSector(sector,buffer)
register unsigned long sector;
register unsigned long *buffer;
{

	/* Schreibt Sector auf Zieldisk und deallociert den Buffer */

	if (oldbuffer) {
		WaitIO(diskreq1); /* Warten auf das Ende des letzten Schreibzugriffs */
		if (diskreq1 -> iotd_Req.io_Error) {
			printf("Error %d while writing Sector %4ld to drive df%1ld: !\n",
				(BYTE) diskreq1 -> iotd_Req.io_Error,
				sector, destinationdrive);
			error();
		}
		releasebuffer(oldbuffer);
	}
	if (oldbuffer = buffer) {
		WriteSector(sector, buffer);
	}
}

unsigned long *allocbuffer()
{
	register unsigned long	*buffer;
	register short int	sec;

	/* Allociert 512 Byte Chip-Memory als Sector-Buffer */
	/* Falls kein Chip-Mem mehr vorhanden ist, wird ein */
	/* bestehender, nicht unbedingt benoetigter Sector  */
	/* deallociert. 				    */

	buffer = AllocMem((long) BLOCKSIZE, MEMF_CHIP);
	if (buffer == NULL) {
		for (sec=2; sec<NUMBLOCKS; sec++) {
			if ((readmap[sec]) && 
			    (readmap[sec] != BLOCKED) &&
			    (readmap[sec][TYPE] == T_DATA)) {
				buffer = readmap[sec];
				readmap[sec] = EMPTY;
				break;
			}
		}
		if (sec == NUMBLOCKS) {
			printf("Sorry, not enough CHIP-Memory to continue ...\n");
			printf("Program terminated unsuccessfully.\n");
			error();
		}
	}
	return (buffer);
}

releasebuffer(buffer)
register unsigned long *buffer;
{

	/* Deallociert einen 512-Byte Buffer */

	if (buffer) {
		FreeMem(buffer, (long) BLOCKSIZE);
	}
}

openall()
{
	register unsigned long	err;

	/* Oeffnen der Disk-Devices */

	if ( !(diskport = CreatePort (NULL, NULL))) {
		printf("Can't create DiskPort !\n");
		error();
	}

	if ( !(diskreq0 = CreateExtIO (diskport, (long) sizeof (*diskreq0)))) {
		printf("Can't make IO block !\n");
		error();
	}

	if ( !(diskreq1 = CreateExtIO (diskport, (long) sizeof (*diskreq1)))) {
		printf("Can't make IO block !\n");
		error();
	}

	if ( !(diskreq2 = CreateExtIO (diskport, (long) sizeof (*diskreq2)))) {
		printf("Can't make IO block !\n");
		error();
	}

	if (err = OpenDevice (TD_NAME, sourcedrive, diskreq0, NULL)) {
		printf ("Can't get drive %1ld, Error %ld.\n", sourcedrive, err);
		error();
	}

	if (err = OpenDevice (TD_NAME, destinationdrive, diskreq1, NULL)) {
		printf ("Can't get drive %1ld, Error %ld\n", destinationdrive, err);
		error();
	}

	if (err = OpenDevice (TD_NAME, destinationdrive, diskreq2, NULL)) {
		printf ("Can't get drive %1ld, Error %ld\n", destinationdrive, err);
		error();
	}

	/* Disk-Validation abstellen */

	dos_packet(DeviceProc(destdevice), ACTION_INHIBIT,
		   1L,0L,0L,0L,0L,0L,0L);

	dos_packet(DeviceProc(sourcedevice), ACTION_INHIBIT,
		   1L,0L,0L,0L,0L,0L,0L);
}

checkdisks()
{
	diskreq0 -> iotd_Req.io_Command = TD_CHANGESTATE;
	DoIO (diskreq0);
	if (diskreq0 -> iotd_Req.io_Actual) {
		printf("No Disk in Drive %1ld !\n",sourcedrive);
		error();
	}

	diskreq1 -> iotd_Req.io_Command = TD_CHANGESTATE;
	DoIO (diskreq1);
	if (diskreq1 -> iotd_Req.io_Actual) {
		printf("No Disk in Drive %1ld !\n",destinationdrive);
		error();
	}

	diskreq1 -> iotd_Req.io_Command = TD_PROTSTATUS;
	DoIO (diskreq1);
	if (diskreq1 -> iotd_Req.io_Actual) {
		printf("Disk in Drive %1ld is write-protected !\n",destinationdrive);
		error();
	}

	diskreq0 -> iotd_Req.io_Command = TD_CHANGENUM;
	DoIO (diskreq0);
	diskchangecount0 = diskreq0 -> iotd_Req.io_Actual;

	diskreq1 -> iotd_Req.io_Command = TD_CHANGENUM;
	DoIO (diskreq1);
	diskchangecount1 = diskreq1 -> iotd_Req.io_Actual;

}

Chk_Abort()
{
	if (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
		printf("[F** BREAK\n");
		error();
	}
}

error()
{
	closeall();
	if (wbflag) {
		printf("Press Return to abort.\n");
		getchar();
	}
	exit(20);
}

closeall()
{
	register unsigned long	count;

	/* Schliessen der Disk-Devices */
	/* Deallocieren der Buffer     */
	/* Schliessen der Library      */

	if (oldbuffer) {
		releasebuffer(oldbuffer);
	}

	for (count = NULL; count<NUMBLOCKS; ++count) {
		if (buffermap[count]) {
			releasebuffer(buffermap[count]);
		}
		if ((readmap[count]) && (readmap[count] != BLOCKED)) {
			releasebuffer(readmap[count]);
		} 
	}

	if (trackbuffer) {
		FreeMem(trackbuffer,NUMSECS * BLOCKSIZE);
	}

	if (diskreq0) {
		if (((long) diskreq0 -> iotd_Req.io_Device != -1L) &&
		    ((long) diskreq0 -> iotd_Req.io_Device != 0L)) {
			CloseDevice (diskreq0);
		}
		DeleteExtIO (diskreq0, (long) sizeof (*diskreq0));
	}

	if (diskreq1) {

		if (((long) diskreq1 -> iotd_Req.io_Device != -1L) &&
		    ((long) diskreq1 -> iotd_Req.io_Device != 0L)) {
			CloseDevice (diskreq1);

			/* Forced Diskchange im Destinationdrive */

			dos_packet(DeviceProc(destdevice), ACTION_INHIBIT,
				   0L,0L,0L,0L,0L,0L,0L);

			dos_packet(DeviceProc(sourcedevice), ACTION_INHIBIT,
				   0L,0L,0L,0L,0L,0L,0L);
		}
		DeleteExtIO (diskreq1, (long) sizeof (*diskreq1));
	}

	if (diskreq2) {
		if (((long) diskreq1 -> iotd_Req.io_Device != -1L) &&
		    ((long) diskreq1 -> iotd_Req.io_Device != 0L)) {
			CloseDevice (diskreq2);
		}
		DeleteExtIO (diskreq2, (long) sizeof (*diskreq2));
	}

	if (diskport) {
		DeletePort (diskport);
	}
	if (IconBase) {
		CloseLibrary(IconBase);
	}
}
