/*
	FS1541

	disk.c


	Basic disk interaction routines.

*/

#include <string.h>

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <dos/dos.h>
#include <intuition/intuition.h>
#include <devices/timer.h>

#include <proto/exec.h>
#include <proto/intuition.h>

#include "disk.h"
#include "volume.h"

LONG numsofterrors = 0;
static struct MsgPort *diskport;
struct IOExtTD *diskreq;
BOOL autoscan = TRUE;

struct MsgPort *dpsender;

static UBYTE *diskimage;
static int diskscanned;
static BYTE sectab[683];
static int curblk = 357; /* Preloading starts at 18,0 = BAM */
static int devopen = 0;
ULONG chgcount;

static BOOL ignoreall, abortall;

struct IntuitionBase *IntuitionBase;

int wprotected=FALSE, hardwprot=FALSE;

static void cacheblock(ULONG n, BOOL write);

/*-------------------------------------------------------------------------*/

LONG InitDiskSS(STRPTR device, ULONG unit, ULONG flags)
{
	LONG error = 0;

	if((diskimage = AllocVec(D64_SIZE, MEMF_PUBLIC)))
	{
		if((diskport = CreateMsgPort()))
		{
			if((diskreq = CreateIORequest(diskport, sizeof(struct IOExtTD))))
			{
			    if((devopen = (!OpenDevice(device,unit,(struct IORequest *)diskreq,flags))))
				{
	
					return(0);
	
				} else error = ERROR_DEVICE_NOT_MOUNTED;
			} else error = ERROR_NO_FREE_STORE;
		} else error = ERROR_NO_FREE_STORE;
	} else error = ERROR_NO_FREE_STORE;

	QuitDiskSS();
	return(error);
}

void QuitDiskSS(void)
{
	if(devopen)
		CloseDevice((struct IORequest*)diskreq);

	if(diskreq)
		DeleteIORequest(diskreq);

	if(diskport)
		DeleteMsgPort(diskport);

	if(diskimage)
		FreeVec(diskimage);
}

/*-------------------------------------------------------------------------*/

void ResetDisk(void)
{
	diskscanned = !autoscan;

	memset(sectab, SEC_NOT_LOADED, 683);

	curblk = 357;

	abortall = FALSE;
	ignoreall = FALSE;

	diskreq->iotd_Req.io_Command = TD_CHANGENUM;
	diskreq->iotd_Req.io_Flags = IOF_QUICK;
	DoIO((struct IORequest*)diskreq);
	chgcount = diskreq->iotd_Req.io_Actual;

	diskreq->iotd_Req.io_Command = ETD_CLEAR;
	diskreq->iotd_Count = chgcount;
	diskreq->iotd_Req.io_Flags = IOF_QUICK;
	DoIO((struct IORequest*)diskreq);

	diskreq->iotd_Req.io_Command = TD_PROTSTATUS;
	diskreq->iotd_Req.io_Flags = IOF_QUICK;
	DoIO((struct IORequest*)diskreq);
	hardwprot = diskreq->iotd_Req.io_Actual;
}

/* Asynchronous disk preloader */
BOOL LoadDisk(void)
{
	if(!diskscanned && curvolumenode && autoscan)
	{
		int readblk;

		for(readblk=curblk;readblk<683;readblk++)
			if(sectab[readblk] == SEC_NOT_LOADED)
				break;

		if(readblk == 683)
		{
			if(curblk == 0)
			{
				/* Loading finished - switch off motor. */
				diskreq->iotd_Req.io_Command = TD_MOTOR;
				diskreq->iotd_Req.io_Flags = 0;
				diskreq->iotd_Req.io_Length = 0;
				DoIO((struct IORequest*)diskreq);
				diskscanned = TRUE;
				return(TRUE);
			}
			else
				curblk = 0;

			return(FALSE);
		}

		curblk = readblk;
		cacheblock(readblk, FALSE);

		return(FALSE);

	} else return(TRUE);
}

void MotorOff(void)
{
	if(diskscanned && CheckIO((struct IORequest*)UDStimer))
	{
		diskreq->iotd_Req.io_Command = ETD_UPDATE;
		diskreq->iotd_Req.io_Flags = 0;
		DoIO((struct IORequest*)diskreq);

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

/*-------------------------------------------------------------------------*/

static LONG ts2block(UBYTE t, UBYTE s)
{
	if(t<1)
	{
		return(-1);
	}
	else if(t<=17)
	{
		if(s>21)
			return(-1);
		else
			return(21*(t-1) + s);
	}
	else if(t<=24)
	{
		if(s>19)
			return(-1);
		else
			return(21*17 + 19*(t-18) + s);
	}
	else if(t<=30)
	{
		if(s>18)
			return(-1);
		else
			return(21*17 + 19*7 + 18*(t-25) + s);
	}
	else if(t<=35)
	{
		if(s>17)
			return(-1);
		else
			return(21*17 + 19*7 + 18*6 + 17*(t-31) + s);
	}
	else
		return(-1);
}

struct DataBlock *getblock_ts(UBYTE t, UBYTE s)
{
	LONG blk = ts2block(t, s);

	if(blk>=0)
		return(getputblock(blk, FALSE));
	else
		return(NULL);
}

struct DataBlock *putblock_ts(UBYTE t, UBYTE s, APTR data)
{
	LONG blk = ts2block(t, s);

	if(blk>=0)
	{
		CopyMem(data, diskimage+(blk*256), 256);
		return(getputblock(blk, TRUE));
	}
	else
		return(NULL);
}

struct DataBlock *getputblock(ULONG blk, BOOL write)
{
	if(write)
		cacheblock(blk, TRUE);

	switch(sectab[blk])
	{
		case SEC_NOT_LOADED:
			curblk = blk;
			cacheblock(blk, FALSE);
			if(sectab[blk] == SEC_OK)
				return((struct DataBlock*)(diskimage+(blk*256)));
			/* fall through */

		case SEC_ERROR:
		default:
		{
			static struct EasyStruct req =
			{
				sizeof(struct EasyStruct),
				0,
				"FS1541 Error",
				"Volume %s has a\nread/write error on\nblock %ld.",
				"Ignore|Ignore All|Abort All|Abort"
			};

			if(curvolumenode)
			{
				LONG num = -1;

				if(abortall)
					num = 0;
				if(ignoreall)
					num = 1;

				if(num == -1 && (IntuitionBase = (struct IntuitionBase*)OpenLibrary("intuition.library",36)))
				{
					struct Process *pr;

					if((dpsender->mp_Flags & PF_ACTION) != PA_SIGNAL
					|| (pr = dpsender->mp_SigTask)->pr_Task.tc_Node.ln_Type != NT_PROCESS
					|| pr->pr_WindowPtr != (APTR)-1)
					{
						ULONG args[2] = { (ULONG)&curvolumenode->name[1], blk };

						num = EasyRequestArgs(NULL, &req, NULL, &args);
					}
					else
						num = 0;

					CloseLibrary((struct Library*)IntuitionBase);
				}

				switch(num)
				{
					case 3: default:
						abortall = TRUE;
						/* fall through */
					case 0:
						return(NULL);

					case 2:
						ignoreall = TRUE;
						/* fall through */							
					case 1:
						return((struct DataBlock*)(diskimage+(blk*256)));
				}
			}

			return(NULL);
		}

		case SEC_OK:
			return((struct DataBlock*)(diskimage+(blk*256)));
	}
}

static void cacheblock(ULONG n, BOOL write)
{
	diskreq->iotd_Count = chgcount;
	diskreq->iotd_Req.io_Command = write ? ETD_WRITE : ETD_READ;
	diskreq->iotd_Req.io_Flags = 0;
	diskreq->iotd_Req.io_Data = diskimage+(n*256);
	diskreq->iotd_Req.io_Offset = 256*n;
	diskreq->iotd_Req.io_Length = 256;
	if(!DoIO((struct IORequest*)diskreq))
		sectab[n] = SEC_OK;
	else
	{
		sectab[n] = SEC_ERROR;
		numsofterrors++;

		if(!curvolumenode)
		{
			/* Switch off the motor, unread/writable disk. */
			diskreq->iotd_Req.io_Command = TD_MOTOR;
			diskreq->iotd_Req.io_Flags = 0;
			diskreq->iotd_Req.io_Length = 0;
			DoIO((struct IORequest*)diskreq);
		}
	}
}
