
/* logic.c */

#include "defs.h"
#include <devices/trackdisk.h>
#include <exec/memory.h>

/* Externals exec.library */

extern void				*AllocMem();

/* Externals in fastdisk.c */

extern void DoCheckSum();
extern unsigned long *GetSector();
extern void PutSector();
extern unsigned long *allocbuffer();
extern void moveboot();

extern unsigned long	*trackbuffer;
extern unsigned long	*writebuffer;
extern unsigned long	*readbuffer;
extern unsigned long	*trackmap;
extern unsigned long	**buffermap;
extern unsigned long	**readmap;
extern unsigned long	*sectormap;
extern unsigned long	*stack;
extern unsigned short	fastloadflag;
extern unsigned long	SourceBlocks;
extern unsigned long	last_checked;
extern struct DevPar	Source,Dest;

/* Variables */

unsigned long	dirstackptr, filestackptr, smptr;
unsigned long	seccount, rootoff;
unsigned long	*BMSector[MAX_BITMAPBLOCKS];

/* Internal Functions */

unsigned long	popdir(), popfile(), getfreesector();


travel(head)
unsigned long	head;
{
	register unsigned long	*headbuffer,*workbuffer,*auxbuffer;
	register unsigned long	sector,hc;
	register unsigned long	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) {
			if (!buffermap[sector]) {
				workbuffer = buffermap[sector] = GetSector(sector);
			} else {
				printf("Loop detected!\n");
				printf("Disk in Device %s is inconsistent.\n",Source.Device);
				printf("Use Diskdoctor to repair this Disk.");
				error();
			}
			if ((workbuffer[TYPE] == T_SHORT) &&
			    (workbuffer[SECONDARY_TYPE] == ST_DIR)) {
				pushdir(sector);
				if (fastloadflag) {
					sectormap[sector] = Dest.RootBlock + Dest.NumBMSecs + dirstackptr;
				} else {
					sectormap[sector] = smptr++;
				}
				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];
					if (!buffermap[sector]) {
						auxbuffer = buffermap[sector] = GetSector(sector);
					} else {
						printf("Loop detected!\n");
						printf("Disk in Device %s is inconsistent.\n",Source.Device);
						printf("Use Diskdoctor to repair this Disk.");
						error();
					}
					count += auxbuffer[BLOCK_COUNT];
					sectormap[sector] = smptr++;
				}

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

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

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

			hc=workbuffer[HASHCHAIN];
			while (hc) {
				if (!buffermap[hc]) {
					workbuffer = buffermap[hc] = GetSector(hc);
				} else {
					printf("Loop detected!\n");
					printf("Disk in Device %s is inconsistent.\n",Source.Device);
					printf("Use Diskdoctor to repair this Disk.");
					error();
				}
				if ((workbuffer[TYPE] == T_SHORT) &&
				    (workbuffer[SECONDARY_TYPE] == ST_DIR)) {
					pushdir(hc);
					if (fastloadflag) {
						sectormap[hc] = Dest.RootBlock + Dest.NumBMSecs + dirstackptr;
					} else {
						sectormap[hc] = smptr++;
					}
					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];
						if (!buffermap[sector]) {
							auxbuffer = buffermap[sector] = GetSector(sector);
						} else {
							printf("Loop detected!\n");
							printf("Disk in Device %s is inconsistent.\n",Source.Device);
							printf("Use Diskdoctor to repair this Disk.");
							error();
						}
						count += auxbuffer[BLOCK_COUNT];
						sectormap[sector] = smptr++;
					}

					/*	Is it a special-File ?		*/

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

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

	/*	Centers the Header-Blocks on the Disk */

	if ((diff = (smptr - Dest.NumBMSecs - Dest.RootBlock) / Dest.SecsPerCyl) > 2L) {
		diff = (((diff >> 1) + 1L) * Dest.SecsPerCyl);
		if (diff - Dest.RootBlock == NULL) {
			diff -= Dest.ReservedBlks;
		}
	} else {
		diff = 0L;
	}

	if (fastloadflag) {
		rootoff += dirstackptr;
		diff -= dirstackptr;
	}

	for (sector=Source.ReservedBlks; sector < Source.NumBlocks; ++sector) {
		dest = sectormap[sector];
		if ((dest)
		 && ((dest < Dest.RootBlock) || (dest >= Dest.RootBlock + Dest.NumBMSecs))
		 && !((fastloadflag) && (buffermap[sector][SECONDARY_TYPE] == ST_DIR))) {
			if ((dest > Dest.RootBlock + Dest.NumBMSecs) &&
		        (dest-diff < Dest.RootBlock + rootoff)) {
				sectormap[sector] -= (diff+rootoff);
			} else {
				sectormap[sector] -= diff;
			}
		}
	}
	seccount = Dest.RootBlock - diff;
}

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

	/* First copy all special Files */

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

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

	/* strcmp() without Upper-/Lower-Case */

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

short is_special(buffer)
register unsigned char	*buffer;
{
	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()
{

	/* Returns next free Sector on the Destination Disk */

	if (seccount == Dest.ReservedBlks) {
		return (seccount = Dest.NumBlocks-1L);
	} else {
		return (--seccount);
	}
}

makefiles()
{
	register unsigned long	file;

	/* Copies all File-Datablocks
	   The new Sectorpositions are changed in the Headerblocks */

	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 == Dest.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 + 1L;
					if (next_sector == Dest.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);
	}
		/*	Changes 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[FIRST_DATA] = sectormap[headbuffer[FIRST_DATA]];
		headbuffer[EXTENSION]  = sectormap[headbuffer[EXTENSION]];
		headbuffer[PARENT]     = sectormap[headbuffer[PARENT]];
		headbuffer[HEADER_KEY] = sectormap[headbuffer[HEADER_KEY]];
		headbuffer[HASHCHAIN]  = sectormap[headbuffer[HASHCHAIN]];
		DoCheckSum(headbuffer,CHECKSUM);
		if (more_to_come) {
			headbuffer = buffermap[more_to_come];
		}
	} while (more_to_come);
}

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

	/* Changes in the 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);
	}
	if (stack) {
		FreeMem(stack, SourceBlocks << 2);
	}
	stack = NULL;
}

makeroot()
{
	register unsigned long	*headbuffer;
	register unsigned long	sector,secnum;
	register unsigned long	mask,offset,count;

	headbuffer = buffermap[Source.RootBlock];

	/* Changes in the Rootblock */

	for ( sector = FH_BLOCKLIST; sector >= FH_ENDLIST; --sector) {
		if (headbuffer[sector]) {
			headbuffer[sector] = sectormap[headbuffer[sector]];
		}
	}
	for (count = NULL; count < MAX_BITMAPBLOCKS; count++) {
		if (count < Dest.NumBMSecs) {
			headbuffer[BITMAPLISTSTART+count] = Dest.RootBlock + 1L + count;
		} else {
			headbuffer[BITMAPLISTSTART+count] = NULL;
		}
	}
	headbuffer[CREATE_TICKS] += 1L;
	DoCheckSum(headbuffer,CHECKSUM);

	/* Create new Bit-Map Sectors */

	for (sector=0L; sector < Dest.NumBMSecs; sector++) {
		BMSector[sector] = allocbuffer();
		for (count=1L; count < Dest.Size; ++count) {
			BMSector[sector][count] = 0xFFFFFFFF;
		}
	}

	/* Mark Header and Data-Blocks */

	for (count = Source.ReservedBlks; count < Source.NumBlocks; ++count) {
		sector = sectormap[count];
		if (sector) {
			sector -= Dest.ReservedBlks;
			secnum = sector / Dest.BlocksPerBMSec;
			sector = sector % Dest.BlocksPerBMSec;
			offset = 1L + (sector >> 5);
			mask   = (ULONG) 1L << (sector & 0x1f);
			BMSector[secnum][offset] -= (ULONG) mask;
		}
	}

	/* Mark Bitmapblocks */

	for (count=0L; count < Dest.NumBMSecs; ++count) {
		sector = Dest.RootBlock + 1L + count - Dest.ReservedBlks;
		secnum = sector / Dest.BlocksPerBMSec;
		sector = sector % Dest.BlocksPerBMSec;
		offset = 1L + (sector >> 5);
		mask   = (ULONG) 1L << (sector & 0x1f);
		BMSector[secnum][offset] -= (ULONG) mask;
	}

	/* Write Bitmapblocks */

	for (sector=0L; sector < Dest.NumBMSecs; sector++) {
		DoCheckSum(BMSector[sector],BITMAP_CHECKSUM);
		PutSector(headbuffer[BITMAPLISTSTART + sector], BMSector[sector]);
		BMSector[sector] = NULL;
	}
}

writeheaders()
{
	register unsigned long	buffer,count;
	register unsigned long  **writemap;

	/* Write Header-Blocks */

	for (buffer = NULL; buffer < Source.NumBlocks; ++buffer) {
		if ((readmap[buffer]) && (readmap[buffer] != BLOCKED)) {
			printf("releasing sector #%ld\n",buffer);
			releasebuffer(readmap[buffer]);
		} 
	}

	FreeMem(readmap, Source.NumBlocks << 2);
	readmap = NULL;

	if (!(writemap = AllocMem(Dest.NumBlocks << 2,MEMF_CLEAR))) {
		printf("Can't allocate WriteMap!\n");
		error();
	}

	for (buffer = 0L; buffer < Source.NumBlocks; ++buffer) {
		if ((buffermap[buffer]) && (sectormap[buffer] < Dest.NumBlocks)) {
			writemap[sectormap[buffer]] = (unsigned long *) buffermap[buffer];
			buffermap[buffer] = NULL;
		} else if (sectormap[buffer] >= Dest.NumBlocks) {
			printf("Illegal Sector Conversion. Sector #%ld Internal Error\n",sectormap[buffer]);
			for (count=0L; count < Dest.NumBlocks; count++) {
				if (writemap[count]) {
					releasebuffer(writemap[count]);
				}
			}
			FreeMem(writemap, Dest.NumBlocks << 2);
			error();
		}
	}

	for (buffer = 0L; buffer < Dest.NumBlocks; ++buffer) {
		if (writemap[buffer]) {
			PutSector(buffer, writemap[buffer]);
		}
	}
	PutSector(NULL,NULL); /* Syncs and Frees current buffer */
	FreeMem(writemap, Dest.NumBlocks << 2);
}

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

	dirstackptr = filestackptr = NULL;
	readmap   = NULL;
	sectormap = NULL;
	buffermap = NULL;
	trackmap  = NULL;
	stack     = NULL;
	last_checked = Source.ReservedBlks;

	rootoff = 1L + Dest.NumBMSecs; /* Root + BitmapBlocks */

	/* Header-Offsets */

	FH_BLOCKLIST	= Source.Size-51L;
	BMFLAG			= Source.Size-50L;
	BITMAPLISTSTART	= Source.Size-49L;
	BITMAPLISTEND	= Source.Size-24L;
	NAME			= Source.Size-20L;
	CREATE_TICKS	= Source.Size-5L;
	HASHCHAIN		= Source.Size-4L;
	PARENT			= Source.Size-3L;
	EXTENSION		= Source.Size-2L;
	SECONDARY_TYPE	= Source.Size-1L;
	NAMEOFFSET	    = NAME * 4L + 1L;
	NAMELENGTH	    = NAME * 4L;

	/* Feldinitialisierungen */

	if (!(trackmap = AllocMem(Dest.NumTracks, MEMF_CLEAR))) {
		printf("Can't allocate TrackMap!\n");
		error();
	}

	if (!(buffermap = AllocMem(Source.NumBlocks << 2, MEMF_CLEAR))) {
		printf("Can't allocate Buffermap!\n");
		error();
	}

	if (!(readmap = AllocMem(Source.NumBlocks << 2, MEMF_CLEAR))) {
		printf("Can't allocate Readmap!\n");
		error();
	}

	if (!(sectormap = AllocMem(Source.NumBlocks << 2, MEMF_CLEAR))) {
		printf("Can't allocate Sectormap!\n");
		error();
	}

	for (count=0L; count < Source.ReservedBlks; count++) {
		sectormap[count] = (ULONG) count;
	}

	if ( !(trackbuffer = AllocMem(Dest.NumSecs * Dest.BlockSize, Dest.MemBufType | MEMF_CLEAR))) {
		printf("Can't allocate Trackbuffer !\n");
		error();
	}

	if ( !(writebuffer = AllocMem(Dest.BlockSize, Dest.MemBufType | MEMF_CLEAR))) {
		printf("Can't allocate Writebuffer !\n");
		error();
	}

	if ( !(readbuffer = AllocMem(Dest.BlockSize, Source.MemBufType | MEMF_CLEAR))) {
		printf("Can't allocate Readbuffer !\n");
		error();
	}

	for (count=0L; count < Dest.NumSecs * Dest.Size; count++) {
		trackbuffer[count] = (ULONG) 0xFFFFFFFF;
	}

	moveboot();

	/* Load Rootblock */

	smptr = Dest.RootBlock;
	headbuffer = buffermap[Source.RootBlock] = GetSector(Source.RootBlock);
	sectormap[Source.RootBlock] = smptr++;

	/* Check Rootblock */

	if ((headbuffer[TYPE] != T_SHORT) || (headbuffer[SECONDARY_TYPE] != ST_ROOT)) {
		printf("Can't find Rootblock of Disk in Device %s !\n",Source.Device);
		error();
	}

	/* Check for Validation */

	if (headbuffer[BMFLAG] == NULL) {
		printf("Disk in Device %s is not validated !\n", Source.Device);
		error();
	}

	/* Allocate Bitmapsectors */

	smptr += Dest.NumBMSecs;

	/* Mark all unused Blocks (BAM-Selection) */

	SourceBlocks = Source.ReservedBlks;
	for (count = 0L; count < Source.NumBMSecs; count++) {
		workbuffer = GetSector(headbuffer[BITMAPLISTSTART + count]);
		for (pos=0L; pos < Source.BlocksPerBMSec; pos++) {
			offset = 1L + (pos >> 5);
			mask   = 1L << (pos & 0x1fL);
			sector = (count * Source.BlocksPerBMSec) + pos + Source.ReservedBlks;
			if (sector == Source.NumBlocks) {
				releasebuffer(workbuffer);
				return;
			}
			if ((ULONG) (workbuffer[offset] & mask)) {
				if ((readmap[sector] != EMPTY) && (readmap[sector] != BLOCKED)) {
					releasebuffer(readmap[sector]);
				}
				readmap[sector] = BLOCKED;
			} else {
				SourceBlocks++;
			}
		}
		releasebuffer(workbuffer);
	}
}

checksize()
{
	if (SourceBlocks > Dest.NumBlocks) {
		printf("There is not enough room on the Destination-Disk\n");
		error();
	}
	if (!(stack = AllocMem(SourceBlocks << 2, MEMF_CLEAR))) {
		printf("Can't allocate Directorystack!\n");
		error();
	}
}

pushdir(sector)
register unsigned long	sector;
{
	stack[SourceBlocks - (++dirstackptr)] = sector;
}

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

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

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