/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by Olaf Barthel & MXM
 *
 *	Name .....: OldFormat.c
 *	Created ..: Wednesday 29-May-91 17:06
 *	Revision .: 1
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	29-May-91       Olsen           Created this file!
 *
 ****************************************************************************
 *
 *	This file contains disk initialization routines written for
 *	Kickstart 1.2 and 1.3. They will probably work with Kickstart 2.0
 *	and above and require that the approriate filing system is
 *	`inhibited' before any write access takes place. Manx and Arp
 *	have different routines to pass the the approriate Dos packet,
 *	so this part of the formatting process is omitted.
 *
 *	The main routine (EraseDisk) is extensively documented, the rest
 *	lacks a decent description. Nevertheless, these routines work
 *	like the Format dos.library call available under Kickstart 2.0
 *
 * $Revision Header ********************************************************/

#include <libraries/filehandler.h>
#include <libraries/dosextens.h>
#include <devices/trackdisk.h>
#include <exec/memory.h>

#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

struct BitmapBlock
{
	ULONG			Checksum;
	ULONG			Bitmap[127];
};

struct BitmapExtension
{
	ULONG			BitmapPointers[127];
	ULONG			BitmapExtension;
};

struct RootDirectory
{
	ULONG			PrimaryType;
	ULONG			HeaderKey;

	ULONG			WhoKnows0;
	ULONG			HashTableSize;
	ULONG			WhoKnows1;

	ULONG			Checksum;

	ULONG			HashTable[72];

	ULONG			BitmapFlag;

	ULONG			BitmapPointers[25];
	ULONG			BitmapExtension;

	struct DateStamp	LastRootChange;

	UBYTE			DiskName[32];

	ULONG			WhoKnows2[2];

	struct DateStamp	LastDiskChange;
	struct DateStamp	CreationDate;

	ULONG			NextHash;
	ULONG			ParentDir;

	ULONG			WhoKnows3;

	ULONG			SecondaryType;
};

STATIC UBYTE
Local2Upper(UBYTE c)
{
	return((UBYTE)((((c) >= 224 && (c) <= 254) || ((c) >= 'a' && (c) <= 'z')) ? (c) - 32 : (c)));
}

STATIC LONG
StrCmp(UBYTE *a,UBYTE *b)
{
	for( ; Local2Upper(*a) == Local2Upper(*b) ; a++, b++)
	{
		if(!(*a))
			return(0);
	}

	return((LONG)(Local2Upper(*a) - Local2Upper(*b)));
}

STATIC struct FileSysStartupMsg *
FindDevice(UBYTE *DevName)
{
	extern struct DosLibrary *DOSBase;

	UBYTE			*Pointer,i,Name[40];
	struct DeviceNode	*DevInfo;

	Forbid();

	DevInfo = (struct DeviceNode *)BADDR(((struct DosInfo *)BADDR(((struct RootNode *)DOSBase -> dl_Root) -> rn_Info)) -> di_DevInfo);

	while(DevInfo)
	{
		if(DevInfo -> dn_Type == DLT_DEVICE)
		{
			Pointer = (UBYTE *)BADDR(DevInfo -> dn_Name);

			for(i = 0 ; i < Pointer[0] ; i++)
				Name[i] = Pointer[i + 1];

			Name[Pointer[0]    ] = ':';
			Name[Pointer[0] + 1] = 0;

			if(!StrCmp(Name,DevName))
			{
				Permit();

				return(BADDR(DevInfo -> dn_Startup));
			}
		}

		DevInfo = (struct DeviceNode *)BADDR(DevInfo -> dn_Next);
	}

	Permit();

	return(NULL);
}

STATIC ULONG
Checksum(ULONG *DiskSector)
{
	ULONG Sum;
	SHORT i;

	for(Sum = 0, i = 0 ; i < (TD_SECTOR >> 2) ; i++)
		Sum += DiskSector[i];

	return(Sum);
}

STATIC LONG
WriteBlock(struct IOExtTD *DevRequest,APTR Buffer,ULONG Offset)
{
	DevRequest -> iotd_Req . io_Command	= CMD_WRITE;
	DevRequest -> iotd_Req . io_Data	= Buffer;
	DevRequest -> iotd_Req . io_Length	= TD_SECTOR;
	DevRequest -> iotd_Req . io_Offset	= Offset * TD_SECTOR;

	return((LONG)DoIO(DevRequest));
}

STATIC LONG
EraseDisk(UBYTE *DiskName,struct IOExtTD *DevRequest,ULONG FirstBlock,ULONG Blocks,ULONG Reserved,ULONG DosType)
{
	STATIC ULONG Bits[32] =
	{
		0xFFFFFFFE,0xFFFFFFFD,0xFFFFFFFB,0xFFFFFFF7,
		0xFFFFFFEF,0xFFFFFFDF,0xFFFFFFBF,0xFFFFFF7F,
		0xFFFFFEFF,0xFFFFFDFF,0xFFFFFBFF,0xFFFFF7FF,
		0xFFFFEFFF,0xFFFFDFFF,0xFFFFBFFF,0xFFFF7FFF,
		0xFFFEFFFF,0xFFFDFFFF,0xFFFBFFFF,0xFFF7FFFF,
		0xFFEFFFFF,0xFFDFFFFF,0xFFBFFFFF,0xFF7FFFFF,
		0xFEFFFFFF,0xFDFFFFFF,0xFBFFFFFF,0xF7FFFFFF,
		0xEFFFFFFF,0xDFFFFFFF,0xBFFFFFFF,0x7FFFFFFF
	};

	ULONG			*Bitmap,AllocSize;
	APTR			 Buffer;
	struct BitmapBlock	*BitmapBlock;
	struct BitmapExtension	*BitmapExtension;
	struct RootDirectory	*RootDirectory;
	LONG			 i,Count,Root,Len;
	LONG			 BitmapCount,BitmapBlocks;
	LONG			 ExtensionCount,ExtensionBlocks = 0;
	LONG			 Error;

		/* Number of long words to allocate for the bitmap. */

	AllocSize = (Blocks - Reserved + 31) / 32;

		/* Allocate a block-sized buffer. */

	if(Buffer = AllocMem(TD_SECTOR,MEMF_PUBLIC|MEMF_CHIP))
	{
			/* Clear the buffer. */

		memset(Buffer,0,TD_SECTOR);

			/* Enough space to write the boot block? */

		if(Reserved)
		{
				/* Put the DOS type field into the first longword. */

			*(ULONG *)Buffer = DosType;

				/* And write it. */

			if(Error = WriteBlock(DevRequest,Buffer,0 + FirstBlock))
				return(Error);

			memset(Buffer,0,TD_SECTOR);

				/* Clear the rest. */

			for(i = 1 ; i < Reserved ; i++)
			{
				if(Error = WriteBlock(DevRequest,Buffer,FirstBlock + i))
					return(Error);
			}
		}

			/* Allocate space for the bitmap. */

		if(Bitmap = (ULONG *)AllocMem(sizeof(ULONG) * AllocSize,MEMF_PUBLIC))
		{
				/* Mark all blocks as `unused'. */

			for(i = 0 ; i < AllocSize ; i++)
				Bitmap[i] = ~0;

				/* Determine number of bitmap blocks. */

			Count = BitmapBlocks = (AllocSize + 126) / 127;

				/* If there are more than 25 bitmap blocks to create,
				 * we will need at least one bitmap extension block.
				 */

			if(Count > 25)
			{
					/* The old filing system doesn't support bitmap
					 * extension blocks.
					 */

				if(DosType == ID_DOS_DISK)
					return(-1);

					/* Subtract 25 blocks since they will fit into the
					 * root block.
					 */

				Count -= 25;

					/* Add the extension blocks. */

				do
				{
					ExtensionBlocks++;

						/* Subtract the blocks that will fit into the
						 * extension block.
						 */

					if(Count > 127)
						Count -= 127;
					else
						Count = 0;
				}
				while(Count);
			}

				/* Calculate the root block offset. */

			Root = Blocks >> 1;

			RootDirectory = Buffer;

			memset(RootDirectory,0,TD_SECTOR);

				/* Fill in the root directory data. */

			RootDirectory -> PrimaryType	= 2;
			RootDirectory -> HashTableSize	= 128 - 56;
			RootDirectory -> BitmapFlag	= DOSTRUE;

			DateStamp(&RootDirectory -> LastRootChange);

			if((Len = strlen(DiskName)) > 31)
				Len = 31;

			RootDirectory -> DiskName[0]	= Len;

			CopyMem(DiskName,&RootDirectory -> DiskName[1],Len);

			DateStamp(&RootDirectory -> LastDiskChange);
			DateStamp(&RootDirectory -> CreationDate);

			RootDirectory -> SecondaryType	= 1;

				/* Fill in the bitmap block pointers. */

			for(i = 0 ; i < BitmapBlocks ; i++)
			{
				if(i == 25)
					break;
				else
					RootDirectory -> BitmapPointers[i] = Root + 1 + i + FirstBlock;
			}

				/* If there are more than 25 bitmap blocks, fill in the
				 * pointer to the first bitmap extension block.
				 */

			if(BitmapBlocks > 25)
				RootDirectory -> BitmapExtension = Root + 1 + 25 + FirstBlock;

				/* Calculate the block checksum. */

			RootDirectory -> Checksum = - Checksum((ULONG *)RootDirectory);

				/* And write the block. */

			if(Error = WriteBlock(DevRequest,RootDirectory,Root + FirstBlock))
				return(Error);

				/* The blocks to be occupied by the root block, the bitmap
				 * blocks and the extension blocks are to be marked as
				 * being `in use'.
				 */

			for(i = Root - Reserved ; i < Root - Reserved + 1 + BitmapBlocks + ExtensionBlocks ; i++)
				Bitmap[i / 32] &= Bits[i % 32];

			BitmapBlock = Buffer;

				/* The root block has been written, now continue with the
				 * bitmap blocks.
				 */

			for(i = 0 ; i < BitmapBlocks ; i++)
			{
					/* Copy the bitmap data. */

				CopyMem(&Bitmap[127 * i],&BitmapBlock -> Bitmap[0],127 * sizeof(ULONG));

					/* Calculate the block checksum. */

				BitmapBlock -> Checksum = 0;
				BitmapBlock -> Checksum = - Checksum((ULONG *)BitmapBlock);

					/* And write the block. */

				if(Error = WriteBlock(DevRequest,BitmapBlock,Root + 1 + i + FirstBlock))
					return(Error);
			}

			BitmapExtension = Buffer;

				/* If there are any bitmap extension blocks, create them. */

			if(ExtensionBlocks)
			{
					/* Remember the first blocks to deal with. */

				BitmapCount	= Root + 1 + FirstBlock;
				ExtensionCount	= BitmapCount + BitmapBlocks;

					/* Subtract the first 25 blocks. */

				BitmapBlocks -= 25;

					/* As long as there are any blocks, put them into
					 * the extension blocks.
					 */

				while(BitmapBlocks)
				{
					memset(BitmapExtension,0,TD_SECTOR);

						/* Fill in the bitmap pointers. */

					for(i = 0 ; i < (BitmapBlocks < 127 ? BitmapBlocks : 127) ; i++)
						BitmapExtension -> BitmapPointers[i] = BitmapCount++;

						/* Subtract the bitmap blocks bound to the extension
						 * block.
						 */

					if(BitmapBlocks > 127)
						BitmapBlocks -= 127;
					else
						BitmapBlocks = 0;

						/* If there are any bitmap blocks left, chain
						 * this bitmap extension block to the next one.
						 */

					if(BitmapBlocks)
						BitmapExtension -> BitmapExtension = ExtensionCount + 1;

						/* And write it. */

					if(Error = WriteBlock(DevRequest,BitmapExtension,ExtensionCount++))
						return(Error);
				}
			}

				/* Free the data. */

			FreeMem(Bitmap,sizeof(ULONG) * AllocSize);
			FreeMem(Buffer,TD_SECTOR);

			return(0);
		}

		FreeMem(Buffer,TD_SECTOR);
	}

	return(-1);
}

LONG
Format(UBYTE *Drive,UBYTE *Name,ULONG DosType)
{
	struct FileSysStartupMsg	*Startup;
	struct DosEnvec			*DosEnvec;
	LONG				 Success = FALSE;

	if(Startup = FindDevice(Drive))
	{
		struct IOExtTD	*DevRequest;
		struct MsgPort	*DevPort;

		if(DevPort = CreatePort(NULL,0))
		{
			if(DevRequest = (struct IOExtTD *)CreateExtIO(DevPort,sizeof(struct IOExtTD)))
			{
				if(!OpenDevice(&((UBYTE *)BADDR(Startup -> fssm_Device))[1],Startup -> fssm_Unit,DevRequest,Startup -> fssm_Flags))
				{
					DosEnvec = (struct DosEnvec *)BADDR(Startup -> fssm_Environ);

					DevRequest -> iotd_Req . io_Command	= TD_MOTOR;
					DevRequest -> iotd_Req . io_Length	= 1;

					DoIO(DevRequest);

					if(!EraseDisk(Name,DevRequest,DosEnvec -> de_LowCyl * DosEnvec -> de_BlocksPerTrack * DosEnvec -> de_Surfaces,(DosEnvec -> de_HighCyl - DosEnvec -> de_LowCyl + 1) * DosEnvec -> de_BlocksPerTrack * DosEnvec -> de_Surfaces,DosEnvec -> de_Reserved,DosType))
						Success = TRUE;

					DevRequest -> iotd_Req . io_Command	= TD_MOTOR;
					DevRequest -> iotd_Req . io_Length	= 0;

					DoIO(DevRequest);

					CloseDevice(DevRequest);
				}

				DeleteExtIO(DevRequest);
			}

			DeletePort(DevPort);
		}
	}

	return(Success);
}
