/* device.c:
 *
 * Handler for ISO-9660 (+ Rock Ridge) + HFS CDROM filing system.
 * Originally based on DOSDEV V1.10 (2 Nov 87) by Matthew Dillon.
 * Major changes made by Nicola Salmoria.
 *
 * ----------------------------------------------------------------------
 * This code is (C) Copyright 1993,1994 by Frank Munkert.
 * All rights reserved.
 * This software may be freely distributed and redistributed for
 * non-commercial purposes, provided this notice is included.
 * ----------------------------------------------------------------------
 * [History removed]
 */

#include <stdlib.h>
#include <string.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/resident.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <dos/filehandler.h>
#include <devices/timer.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/trackdisk.h>
#include <utility/date.h>
#include <intuition/intuitionbase.h>
#include <workbench/workbench.h>
#include <resources/filesysres.h>
#include <clib/alib_protos.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/intuition.h>
#include <proto/icon.h>
#include <proto/wb.h>

#include "cdrom.h"
#include "generic.h"
#include "rock.h"

#define VERSION 37
#define REVISION 5
static const UBYTE versionstring20[] = "\0$VER: amicdfilesystem 37.5 (2.10.94)";


/*
 * The only purpose of this Resident structure is to make
 * Version CD0:
 * return the version number of the filesystem.
 */
static const struct Resident versiontag =
{
	RTC_MATCHWORD,
	&versiontag,
	&versiontag + 1,
	0,
	VERSION,
	0,
	-81,
	NULL,
	&versionstring20[7],
	NULL
};


/* the only global data used is library bases, therefore this code is */
/* reentrant, and could be run several times. If we added the filesystem */
/* to the filesystem resource, one instance would be used for multiple */
/* devices. */

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
struct Library *UtilityBase;
struct IntuitionBase *IntuitionBase;
struct Library *InputBase;
struct Library *IconBase;
struct Library *WorkbenchBase;



/* global data is kept in the HData structure, instead, which is located */
/* on the stack. */

struct HConfig
{
	BOOL UseTrackdisk;			/* Use trackdisk calls instead of SCSI-direct */

	BOOL Lowercase;				/* Map ISO names to lower case */

	ULONG ScanInterval;			/* Time between successive diskchange checks */
};

struct HData
{
	struct MsgPort *MyPort;		/* our handler port */
	struct DosList *MyDevNode;		/* our device node */
	struct FileSysStartupMsg *fssm;
	struct DosEnvec *envec;
	struct DosList *CurrentVolume;	 /* current volume, or NULL */
	BPTR LockList;			/* list of open locks */
	LONG DiskInserted;		/* see #defines below */
	LONG Inhibited;			/* !0 if the volume is inhibited (nesting count) */

	CDROM *CD;
	VOLUME *CDVolume;
	CDROM_OBJ *TopLevelObj;

	UBYTE VolumeName[40];		/* When a dos disk is inserted, contains the */
								/* volume name in BCPL form. */
								/* When an audio disk is inserted, contains */
								/* tha name which will be used for the AppIcon, */
								/* for example CD0:CDDA */

	struct AppIcon *AppIcon;	/* CDDA AppIcon */
	struct MsgPort *AppPort;	/* MsgPort used by the AppIcon */

	struct MsgPort *TimerPort;		/* timer.device I/O port */
	struct timerequest *TimerIO;	/* timer.device I/O request */
	struct IOStdReq *InputIO;		/* input.device I/O request */

	UBYTE PlayCDDA[128];			/* Command invoked if appicon is activated */
									/* (initialized by Show_CDDA_Icon()) */
	BOOL Playing;				/* TRUE if a CDDA disk is playing */
	BOOL pad;

	struct HConfig HConfig;		/* configuration */
};


/* allowed values for the DiskInserted field */

#define DI_NO_DISK 0		/* no disk in the drive */
#define DI_DISK_OK 1		/* disk in drive */
#define DI_UNREADABLE_DISK 2	/* disk in drive, but couldn't be read for some reason */
#define DI_CDDA_DISK 3		/* CDDA disk in drive */




ULONG Centrypoint(VOID);

VOID ReturnPacket(struct DosPacket *packet,struct MsgPort *port,LONG res1,LONG res2);
VOID AddToFSResource(ULONG dostype,BPTR seglist);
void btos(LONG, char *);
CDROM_OBJ *OpenObject(BPTR parentlock,BSTR name,struct HData *HData);
BPTR cdlock(CDROM_OBJ *, int,struct HData *HData);
void cdunlock(BPTR lock,struct HData *HData);
LONG CheckLock(BPTR lock,struct HData *HData);
CDROM_OBJ *getlockfile(BPTR lock,struct HData *HData);
int Check_For_Volume_Name_Prefix (char *);
VOID Fill_InfoData(struct InfoData *id,struct HData *HData);
void Fill_FileInfoBlock (struct FileInfoBlock *, CDROM_INFO *, VOLUME *,struct HData *HData);
void Mount(struct HData *HData);
struct DosList *CreateVolumeNode(struct HData *HData);
void RemoveVolumeNode (struct HData *HData);
VOID RemoveDosList(struct DosList *vol);
void Check_Disk (struct HData *HData);
void Send_Timer_Request(struct HData *HData);
int Open_Timer_Device(struct HData *HData);
int Open_Input_Device(struct HData *HData);
VOID CreateInputEvent(BOOL inserted,struct HData *HData);
int Get_Startup (struct HData *HData);

VOID Display_Error(UBYTE *text,APTR par1, ...);
VOID Show_CDDA_Icon(struct HData *HData);
VOID Hide_CDDA_Icon (struct HData *HData);

ULONG _stackswap(ULONG function(),...);




ULONG entrypoint(VOID)
{
/* we must always swap the stack because when mounted from RDB we will get */
/* only 600 bytes of stack. */
return(_stackswap(Centrypoint));
}



ULONG __saveds Centrypoint(VOID)
{
struct DosPacket *packet;
struct Process *process;
struct HData HData;
LONG timercount = 0;


SysBase = *((struct ExecBase **)4);

process = (struct Process *)FindTask(NULL);
/* if we were run from a shell, fail */
if (process->pr_CLI) return(RETURN_FAIL);

WaitPort(&process->pr_MsgPort);		/* get startup packet */
packet = (struct DosPacket *)GetMsg(&process->pr_MsgPort)->mn_Node.ln_Name;

memset(&HData,0,sizeof(struct HData));

if (!(HData.fssm = BADDR(packet->dp_Arg2)) ||
		!(HData.envec = BADDR(HData.fssm->fssm_Environ)) ||
	/* make sure that the mountlist has enough fields */
		HData.envec->de_TableSize < DE_DOSTYPE)
{
	Forbid();
	ReturnPacket(packet,&process->pr_MsgPort,DOSFALSE,0);
	return(0);		/* exit */
}

HData.MyDevNode = BADDR(packet->dp_Arg3);

/* add the seglist to the filesystem.resource so it will be reused by other */
/* devices with the same DosType */
AddToFSResource(HData.envec->de_DosType,HData.MyDevNode->dol_misc.dol_handler.dol_SegList);

if (SysBase->LibNode.lib_Version < 37 ||
		!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",37)) ||
		!(UtilityBase = OpenLibrary("utility.library",37)) ||
		!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37)) ||
		!(HData.MyPort = CreateMsgPort()) ||
		!Open_Timer_Device(&HData) ||
		!Open_Input_Device(&HData) ||
		!Get_Startup(&HData))
{
	if (DOSBase)
	{
		DeleteMsgPort(HData.MyPort);
		CloseLibrary(IntuitionBase);
		CloseLibrary(UtilityBase);
		CloseLibrary(DOSBase);
	}
	Forbid();
	ReturnPacket(packet,&process->pr_MsgPort,DOSFALSE,0);
	return(0);		/* exit */
}

/* initialize dn_Task so that DOS won't start a new process for every access */
HData.MyDevNode->dol_Task = HData.MyPort;

ReturnPacket(packet,HData.MyPort,DOSTRUE,0);

Send_Timer_Request(&HData);

Check_Disk(&HData);


for (;;)
{
	ULONG dossignal,appsignal,timersignal,sigrec;


	dossignal = 1L << HData.MyPort->mp_SigBit;
	appsignal = HData.AppPort ? 1L << HData.AppPort->mp_SigBit : 0;
	timersignal = 1L << HData.TimerPort->mp_SigBit;

	sigrec = Wait(dossignal | timersignal | appsignal | SIGBREAKF_CTRL_F);

	if (sigrec & SIGBREAKF_CTRL_F)
	{
D(kprintf("diskchange interrupt!\n"));
		Check_Disk(&HData);
	}

	if (sigrec & timersignal)
	{
		if (HData.HConfig.ScanInterval)
		{
			if (++timercount == HData.HConfig.ScanInterval)
			{
				Check_Disk(&HData);

				timercount = 0;
			}
		}

/* turn off the motor if it is on (Motor_Off() knows whether it's on) */
		Motor_Off(HData.CD);

/* retry to display the AppIcon, in case something went wrong before */
		if (HData.DiskInserted == DI_CDDA_DISK) Show_CDDA_Icon(&HData);

		GetMsg(HData.TimerPort);
		Send_Timer_Request(&HData);
	}

	if (sigrec & appsignal)
	{
		struct Message *msg;


		while (msg = GetMsg(HData.AppPort))
		{
			ReplyMsg (msg);
			if (HData.PlayCDDA[0])
				SystemTags(HData.PlayCDDA,
						SYS_Input,Open("NIL:",MODE_OLDFILE),
						SYS_Output,Open("NIL:",MODE_NEWFILE),
						SYS_Asynch,TRUE,
					TAG_END);
			else
			{
				int res;


				if (HData.Playing) res = Stop_Play_Audio(HData.CD);
				else res = Start_Play_Audio(HData.CD);

				if (res) HData.Playing = !HData.Playing;
			}
		}
	}

	if (sigrec & dossignal)
	{
		struct Message *msg;


		while (msg = GetMsg(HData.MyPort))
		{
			LONG res1,res2;


			packet = (struct DosPacket *)msg->mn_Node.ln_Name;

			switch (packet->dp_Type)
			{
			case ACTION_FINDINPUT:
			case ACTION_FINDUPDATE:
			case ACTION_FINDOUTPUT:
D(switch (packet->dp_Type)
	{
	case ACTION_FINDUPDATE:
		kprintf("ACTION_FINDUPDATE fh %lx lock %lx name \"%b\" ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3);
		break;
	case ACTION_FINDINPUT:
		kprintf("ACTION_FINDINPUT fh %lx lock %lx name \"%b\" ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3);
		break;
	case ACTION_FINDOUTPUT:
		kprintf("ACTION_FINDOUTPUT fh %lx lock %lx name \"%b\" ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3);
		break;
	}
)
				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg2,&HData)))
				{
					CDROM_OBJ *obj;


					if (!(obj = OpenObject(packet->dp_Arg2,packet->dp_Arg3,&HData)))
					{
						res2 = IoErr();
						if (res2 == ERROR_OBJECT_NOT_FOUND &&
								packet->dp_Type == ACTION_FINDOUTPUT)
							res2 = ERROR_DISK_WRITE_PROTECTED;
						else if (res2 == TDERR_DiskChanged)
							res2 = packet->dp_Arg2 ? ERROR_DEVICE_NOT_MOUNTED : ERROR_NO_DISK;
					}
					else
					{
						if (packet->dp_Type == ACTION_FINDOUTPUT)
						{
							Close_Object(obj);
							res2 = ERROR_DISK_WRITE_PROTECTED;
						}
						else if (obj->directory_f)
						{
							Close_Object(obj);
							res2 = ERROR_OBJECT_WRONG_TYPE;
						}
						else
						{
				/* make a lock out of the object, this way the icon will not */
				/* disappear from the Workbench if there are files open */
							if (((struct FileHandle *)BADDR(packet->dp_Arg1))->fh_Args =
									cdlock(obj,ACCESS_READ,&HData))
								res1 = DOSTRUE;
							else res2 = ERROR_NO_FREE_STORE;
						}
					}
				}
				break;

			case ACTION_FH_FROM_LOCK:
D(kprintf("ACTION_FH_FROM_LOCK fh %lx lock %lx ->\n",packet->dp_Arg1,packet->dp_Arg2));
				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg2,&HData)))
				{
					CDROM_OBJ *obj = getlockfile(packet->dp_Arg2,&HData);


					if (obj->directory_f)
						res2 = ERROR_OBJECT_WRONG_TYPE;
					else
					{
						((struct FileHandle *)BADDR(packet->dp_Arg1))->fh_Args = packet->dp_Arg2;
						res1 = DOSTRUE;
					}
				}
				break;

			case ACTION_END:
D(kprintf("ACTION_END fh_Args %lx ->\n",packet->dp_Arg1));
				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					cdunlock(packet->dp_Arg1,&HData);
					res1 = DOSTRUE;
				}
				break;

			case ACTION_READ:
D(kprintf("ACTION_READ fh_Args %lx buffer %lx len %ld ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3));
				res1 = -1;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					res1 = Read_From_File(HData.CDVolume,
							getlockfile(packet->dp_Arg1,&HData),
							(APTR)packet->dp_Arg2,packet->dp_Arg3);
					res2 = IoErr();
					if (res2 == TDERR_DiskChanged)
						res2 = ERROR_DEVICE_NOT_MOUNTED;

if (res1 > 0) res2 = 0;
				}
				break;

			case ACTION_SEEK:
D(kprintf("ACTION_SEEK fh_Args %lx pos %ld offs %ld ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3));
				res1 = -1;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					CDROM_OBJ *obj = getlockfile(packet->dp_Arg1,&HData);


					res1 = obj->pos;
					if (res2 = Seek_Position(obj,packet->dp_Arg2,packet->dp_Arg3))
						res1 = -1;
				}
				break;

			case ACTION_LOCATE_OBJECT:
D(kprintf("ACTION_LOCATE_OBJECT lock %lx name \"%b\" mode %ld ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3));
				res1 = NULL;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					CDROM_OBJ *obj;


					if (!(obj = OpenObject(packet->dp_Arg1,packet->dp_Arg2,&HData)))
					{
						res2 = IoErr();
						if (res2 == TDERR_DiskChanged)
							res2 = packet->dp_Arg1 ? ERROR_DEVICE_NOT_MOUNTED : ERROR_NO_DISK;
					}
					else
					{
						if (!(res1 = cdlock(obj,packet->dp_Arg3,&HData)))
							res2 = ERROR_NO_FREE_STORE;
					}
				}
				break;

			case ACTION_FREE_LOCK:
D(kprintf("ACTION_FREE_LOCK lock %lx ->\n",packet->dp_Arg1));
				cdunlock(packet->dp_Arg1,&HData);
				res1 = DOSTRUE;
				break;

			case ACTION_COPY_DIR:
			case ACTION_COPY_DIR_FH:
D(switch (packet->dp_Type)
	{
	case ACTION_COPY_DIR:
		kprintf("ACTION_COPY_DIR lock %lx ->\n",packet->dp_Arg1);
		break;
	case ACTION_COPY_DIR_FH:
		kprintf("ACTION_COPY_DIR_FH fh_Args %lx ->\n",packet->dp_Arg1);
		break;
	}
)
				res1 = NULL;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)) && packet->dp_Arg1)
				{
					CDROM_OBJ *new;


					if (!(new = Clone_Object(getlockfile(packet->dp_Arg1,&HData))))
						res2 = ERROR_NO_FREE_STORE;
					else
					{
						if (!(res1 = cdlock(new,ACCESS_READ,&HData)))
							res2 = ERROR_NO_FREE_STORE;
					}
				}
				break;

			case ACTION_PARENT:
			case ACTION_PARENT_FH:
D(switch (packet->dp_Type)
	{
	case ACTION_PARENT:
		D(kprintf("ACTION_PARENT lock %lx ->\n",packet->dp_Arg1));
		break;
	case ACTION_PARENT_FH:
		kprintf("ACTION_PARENT_FH fh_Args %lx ->\n",packet->dp_Arg1);
		break;
	}
)
				res1 = NULL;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					if (packet->dp_Arg1)
					{
						CDROM_OBJ *obj = getlockfile(packet->dp_Arg1,&HData);
						CDROM_OBJ *parent;


						if (Is_Top_Level_Object(HData.CDVolume,obj))
							res2 = 0;
						else
						{
							if (!(parent = Find_Parent(HData.CDVolume,obj)))
							{
								res2 = IoErr();
								if (res2 == TDERR_DiskChanged)
									res2 = ERROR_DEVICE_NOT_MOUNTED;
							}
							else
							{
								if (!(res1 = cdlock(parent,ACCESS_READ,&HData)))
									res2 = ERROR_NO_FREE_STORE;
							}
						}
					}
					else
						res2 = ERROR_OBJECT_NOT_FOUND;
				}
				break;

			case ACTION_SAME_LOCK: /*	Lock  Lock				 Bool	   */
				{
					CDROM_OBJ *obj1 = getlockfile(packet->dp_Arg1,&HData);
					CDROM_OBJ *obj2 = getlockfile(packet->dp_Arg2,&HData);
					struct FileLock *plock1,*plock2;

D(kprintf("ACTION_SAME_LOCK lock1 %lx lock2 %lx ->\n",packet->dp_Arg1,packet->dp_Arg2));

					plock1 = BADDR(packet->dp_Arg1);
					plock2 = BADDR(packet->dp_Arg2);

					if (plock1->fl_Volume != plock2->fl_Volume)
						res1 = DOSFALSE;
					else if (Same_Objects(obj1,obj2))
						res1 = DOSTRUE;
					else
						res1 = DOSFALSE;

					res2 = 0;
				}
				break;

			case ACTION_EXAMINE_OBJECT:
			case ACTION_EXAMINE_FH:
D(switch (packet->dp_Type)
	{
	case ACTION_EXAMINE_OBJECT:
		kprintf("ACTION_EXAMINE_OBJECT lock %lx fib %lx ->\n",packet->dp_Arg1,packet->dp_Arg2);
		break;
	case ACTION_EXAMINE_FH:
		kprintf("ACTION_EXAMINE_FH fh_Args %lx fib %lx ->\n",packet->dp_Arg1,packet->dp_Arg2);
		break;
	}
)
				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					struct FileInfoBlock *fib;
					CDROM_INFO info;


					fib = BADDR(packet->dp_Arg2);
					memset(fib,0,sizeof(struct FileInfoBlock));
					if (!(res2 = CDROM_Info(HData.CDVolume,
							getlockfile(packet->dp_Arg1,&HData),&info)))
					{
						Fill_FileInfoBlock(fib,&info,HData.CDVolume,&HData);
						res1 = DOSTRUE;
					}
				}
				break;

			case ACTION_EXAMINE_NEXT:
D(kprintf("ACTION_EXAMINE_NEXT lock %lx fib %lx (%s) ->\n",packet->dp_Arg1,packet->dp_Arg2,&((struct FileInfoBlock *)BADDR(packet->dp_Arg2))->fib_FileName[1]));

				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					struct FileInfoBlock *fib;
					CDROM_OBJ *dir = getlockfile(packet->dp_Arg1,&HData);
					CDROM_INFO info;


					fib = BADDR(packet->dp_Arg2);
					if (!dir->directory_f)
						res2 = ERROR_OBJECT_WRONG_TYPE;
					else if (res2 = Examine_Next(HData.CDVolume,dir, &info,(unsigned long *)&fib->fib_DiskKey))
					{
						if (res2 == TDERR_DiskChanged)
							res2 = ERROR_DEVICE_NOT_MOUNTED;
					}
					else
					{
						Fill_FileInfoBlock(fib,&info,HData.CDVolume,&HData);
						res1 = DOSTRUE;
					}
				}
				break;

			case ACTION_INFO:
D(kprintf("ACTION_INFO lock %lx infodata %lx ->\n",packet->dp_Arg1,packet->dp_Arg2));
				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
				{
					Fill_InfoData(BADDR(packet->dp_Arg2),&HData);
					res1 = DOSTRUE;
				}
				break;

			case ACTION_DISK_INFO:
D(kprintf("ACTION_DISK_INFO infodata %lx ->\n",packet->dp_Arg1));
				Fill_InfoData(BADDR(packet->dp_Arg1),&HData);
				res1 = DOSTRUE;
				break;

			case ACTION_CURRENT_VOLUME:
D(kprintf("ACTION_CURRENT_VOLUME fh_Args %lx ->\n",packet->dp_Arg1));
				if (packet->dp_Arg1)
					res1 = ((struct FileLock *)BADDR(packet->dp_Arg1))->fl_Volume;
				else res1 = MKBADDR(HData.CurrentVolume);
				res2 = HData.fssm->fssm_Unit;
				break;

			case ACTION_INHIBIT:
D(kprintf("ACTION_INHIBIT bool %ld ->\n",packet->dp_Arg1));
				if (packet->dp_Arg1 != DOSFALSE)
				{
					RemoveVolumeNode(&HData);
					HData.Inhibited++;
					HData.DiskInserted = DI_DISK_OK;	/* this way any operation */
									/* will fail with error "not a dos disk" */
				}
				else
				{
					if (HData.Inhibited)
					{
						HData.Inhibited--;
						HData.CD->t_changeint2 = (unsigned long)-2;
						HData.DiskInserted = DI_NO_DISK;	/* force a disk check */
						Check_Disk(&HData);
					}
				}
				break;

			case ACTION_READ_LINK:
				{
					CDROM_OBJ *obj;
					CDROM_OBJ *parentdir = getlockfile (packet->dp_Arg1,&HData);
					char *outbuf = (char *) packet->dp_Arg3;
					t_ulong maxlength = packet->dp_Arg4;
					int offs;
					char buf[256];
					int res;

D(kprintf("ACTION_READ_LINK lock %lx path %s buffer %lx bufsiz %ld ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4));

					res1 = 0;
					if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
					{
						offs = Check_For_Volume_Name_Prefix ((char *) packet->dp_Arg2);
						obj = Open_Object(HData.CDVolume,parentdir, (char *) packet->dp_Arg2 + offs);
						if (obj)
						{
							res = Get_Link_Name (HData.CDVolume,obj, buf, sizeof (buf));
							if (res == 0 || strlen (buf) + HData.VolumeName[0] + 1 >= maxlength)
								strncpy (outbuf, "illegal_link", maxlength - 1);
							else
							{
								if (buf[0] == ':')
									strcpy (outbuf,&HData.VolumeName[0]);
								else
									outbuf[0] = 0;

								strcat (outbuf, buf);
							}

							outbuf[maxlength - 1] = 0;
							Close_Object(obj);
							res1 = strlen (outbuf);
						}
						else
						{
							res2 = IoErr();
							if (res2 == TDERR_DiskChanged)
								res2 = packet->dp_Arg1 ? ERROR_DEVICE_NOT_MOUNTED : ERROR_NO_DISK;
						}
					}
				}
				break;

			case ACTION_RENAME_DISK:
			case ACTION_SERIALIZE_DISK:
D(switch (packet->dp_Type)
	{
	case ACTION_RENAME_DISK:
		kprintf("ACTION_RENAME_DISK name \"%b\" ->\n",packet->dp_Arg1);
		break;
	case ACTION_SERIALIZE_DISK:
		kprintf("ACTION_SERIALIZE_DISK ->\n");
		break;
	}
)
				res1 = DOSFALSE;
				if (!HData.CurrentVolume)
					res2 = HData.DiskInserted == DI_NO_DISK ? ERROR_NO_DISK : ERROR_NOT_A_DOS_DISK;
				else res2 = ERROR_DISK_WRITE_PROTECTED;
				break;

			case ACTION_FORMAT:
D(kprintf("ACTION_FORMAT name \"%b\" dostype %lx ->\n",packet->dp_Arg1,packet->dp_Arg2));
				res1 = DOSFALSE;
				if (HData.DiskInserted == DI_NO_DISK) res2 = ERROR_NO_DISK;
				else res2 = ERROR_DISK_WRITE_PROTECTED;
				break;

			case ACTION_WRITE:
			case ACTION_SET_FILE_SIZE:
D(switch (packet->dp_Type)
	{
	case ACTION_WRITE:
		kprintf("ACTION_WRITE fh_Args %lx buffer %lx len %ld ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3);
		break;
	case ACTION_SET_FILE_SIZE:
		kprintf("ACTION_SET_FILE_SIZE fh_Args %lx pos %ld mode %ld ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3);
		break;
	}
)
				res1 = -1;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
					res2 = ERROR_DISK_WRITE_PROTECTED;
				break;

			case ACTION_DELETE_OBJECT:
			case ACTION_RENAME_OBJECT:
			case ACTION_CREATE_DIR:
			case ACTION_MAKE_LINK:
D(switch (packet->dp_Type)
	{
	case ACTION_DELETE_OBJECT:
		kprintf("ACTION_DELETE_OBJECT lock %lx name \"%b\" ->\n",packet->dp_Arg1,packet->dp_Arg2);
		break;
	case ACTION_RENAME_OBJECT:
		kprintf("ACTION_RENAME_OBJECT lock %lx name \"%b\" newlock %lx newname \"%b\" ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4);
		break;
	case ACTION_CREATE_DIR:
		kprintf("ACTION_CREATE_DIR lock %lx name \"%b\" ->\n",packet->dp_Arg1,packet->dp_Arg2);
		break;
	case ACTION_MAKE_LINK:
		kprintf("ACTION_MAKE_LINK lock %lx name \"%b\" dest %lx soft %ld ->\n",packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4);
		break;
	}
)
				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg1,&HData)))
					res2 = ERROR_DISK_WRITE_PROTECTED;
				break;

			case ACTION_SET_PROTECT:
			case ACTION_SET_COMMENT:
			case ACTION_SET_DATE:
			case ACTION_SET_OWNER:
D(switch (packet->dp_Type)
	{
	case ACTION_SET_PROTECT:
		kprintf("ACTION_SET_PROTECT (arg1 unused) lock %lx name \"%b\" bits %lx ->\n",packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4);
		break;
	case ACTION_SET_COMMENT:
		kprintf("ACTION_SET_COMMENT (arg1 unused) lock %lx name \"%b\" comment \"%b\" ->\n",packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4);
		break;
	case ACTION_SET_DATE:
		kprintf("ACTION_SET_DATE (arg1 unused) lock %lx name \"%b\" date %lx ->\n",packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4);
		break;
	case ACTION_SET_OWNER:
		kprintf("ACTION_SET_OWNER (arg1 unused) lock %lx name \"%b\" owner %lx ->\n",packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4);
		break;
	}
)
				res1 = DOSFALSE;
				if (!(res2 = CheckLock(packet->dp_Arg2,&HData)))
					res2 = ERROR_DISK_WRITE_PROTECTED;
				break;

			case ACTION_IS_FILESYSTEM:
D(kprintf("ACTION_IS_FILESYSTEM ->\n"));
				res1 = DOSTRUE;
				break;

			case ACTION_FLUSH:
D(kprintf("ACTION_FLUSH ->\n"));
				res1 = DOSTRUE;
				break;

			default:
D(kprintf("Unknown pkt #%ld  %lx %lx %lx %lx %lx ->\n",(LONG)packet->dp_Type,
		packet->dp_Arg1,packet->dp_Arg2,packet->dp_Arg3,packet->dp_Arg4,packet->dp_Arg5));
				res1 = DOSFALSE;
				res2 = ERROR_ACTION_NOT_KNOWN;
				break;
			}

			ReturnPacket(packet,HData.MyPort,res1,res2);
		}
	}
}
}



VOID AddToFSResource(ULONG dostype,BPTR seglist)
{
struct FileSysResource *FileSysResBase;


if (FileSysResBase = (struct FileSysResource *)OpenResource(FSRNAME))
{
	struct FileSysEntry *fse,*nfse;


	Forbid();

	fse = (struct FileSysEntry *)FileSysResBase->fsr_FileSysEntries.lh_Head;
	while (nfse = (struct FileSysEntry *)fse->fse_Node.ln_Succ)
	{
	/* if filesystem already in resource, return */
		if (fse->fse_DosType == dostype) break;

		fse = nfse;
	}

	Permit();

	if (!nfse && (fse = AllocMem(sizeof(struct FileSysEntry),MEMF_PUBLIC | MEMF_CLEAR)))
	{
		fse->fse_Node.ln_Name = (UBYTE *)&versionstring20[7];
		fse->fse_DosType = dostype;
		fse->fse_Version = ((LONG)VERSION) << 16 | REVISION;
		fse->fse_PatchFlags = 0x180;
		fse->fse_SegList = seglist;
		fse->fse_GlobalVec = -1;

		Forbid();
		AddHead(&FileSysResBase->fsr_FileSysEntries,fse);
		Permit();
	}
}
}



/*
 * Return a packet back to DOS.
 */
VOID ReturnPacket(struct DosPacket *packet,struct MsgPort *port,LONG res1,LONG res2)
{
struct Message *mess;
struct MsgPort *replyport;


D(kprintf("%lx %ld\n",res1,res2));

packet->dp_Res1 = res1;
packet->dp_Res2 = res2;
replyport = packet->dp_Port;
mess = packet->dp_Link;
packet->dp_Port = port;
mess->mn_Node.ln_Name = (UBYTE *)packet;
PutMsg(replyport,mess);
}



/*
 *  Convert a BSTR into a normal string.. copying the string into buf.
 *  I use normal strings for internal storage, and convert back and forth
 *  when required.
 */

void btos(LONG bstr, char *buf)
{
	unsigned char *str = BADDR(bstr);
	memcpy(buf,&str[1],str[0]);
	buf[str[0]] = 0;
}



/*
 * Locate and open an object on the CD-ROM. Returns a CDROM_OBJ pointer,
 * or NULL if an error occurred. If an error occurred, IoErr() contains
 * the error code.
 */
CDROM_OBJ *OpenObject(BPTR parentlock,BSTR name,struct HData *HData)
{
CDROM_OBJ *parentdir;
CDROM_OBJ *obj;
int offs;
char	buf[256];


parentdir = getlockfile(parentlock,HData);
btos(name,buf);

offs = Check_For_Volume_Name_Prefix(buf);
if (!buf[offs])
{
	if (parentdir)
		obj = Clone_Object(parentdir);
	else
		obj = Open_Top_Level_Directory(HData->CDVolume);
}
else
	obj = Open_Object(HData->CDVolume,parentdir,buf + offs);

if (obj)
{
	if (obj->symlink_f)
	{
		Close_Object(obj);
		obj = NULL;
		SetIoErr(ERROR_IS_SOFT_LINK);
	}
}
/* else IoErr() already contains the error code set by Open_Object() */

return(obj);
}



/*
 *  The lock function.	The file has already been checked to see if it
 *  is lockable given the mode.
 * Returns NULL if out of memory.
 */

BPTR cdlock(CDROM_OBJ *cdfile, int mode,struct HData *HData)
{
struct FileLock *lock;


if (lock = AllocVec(sizeof(struct FileLock),MEMF_PUBLIC | MEMF_CLEAR))
{
	lock->fl_Key = (long) cdfile;
	lock->fl_Access = ACCESS_READ;
	lock->fl_Task = HData->MyPort;
	lock->fl_Volume = MKBADDR(HData->CurrentVolume);

	lock->fl_Link = HData->LockList;
	HData->LockList = MKBADDR(lock);
}

return(MKBADDR(lock));
}



void cdunlock (BPTR lock,struct HData *HData)

{
struct FileLock *flock,*cl,*ncl;
struct DosList *vol;


if (!lock) return;

flock = BADDR(lock);
vol = BADDR(flock->fl_Volume);
if (vol == HData->CurrentVolume)
	cl = (struct FileLock *)&HData->LockList;
else
	cl = (struct FileLock *)&vol->dol_misc.dol_volume.dol_LockList;

while (cl && (ncl = BADDR(cl->fl_Link)) != flock) cl = ncl;

if (cl) cl->fl_Link = ncl->fl_Link;

if (vol != HData->CurrentVolume && !vol->dol_misc.dol_volume.dol_LockList)
{
	RemoveDosList(vol);
	CreateInputEvent(FALSE,HData);
}

Close_Object(getlockfile(lock,HData));
FreeVec(flock);				/* free lock		*/
}



/*
 * Check if the volume associated to the given lock is present in the drive.
 * Return 0 if everything OK, or an error code (to be assigned to res2) if
 * an error occurred.
 */
LONG CheckLock(BPTR lock,struct HData *HData)
{
if (lock && BADDR(((struct FileLock *)BADDR(lock))->fl_Volume) != HData->CurrentVolume)
	return(ERROR_DEVICE_NOT_MOUNTED);
else if (!HData->CurrentVolume)
	return(HData->DiskInserted == DI_NO_DISK ? ERROR_NO_DISK : ERROR_NOT_A_DOS_DISK);
else return(0);
}



/*
 *  GETLOCKFILE(bptrlock)
 *
 *  Return the CDROM_OBJ (file or directory) associated with the
 *  given lock, which is passed as a BPTR.
 *
 *  According to the DOS spec, the only way a NULL lock will ever be
 *  passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
 *  In anycase, If a NULL lock is passed to me I simply assume it means
 *  the root directory of the CDROM.
 */

CDROM_OBJ *getlockfile(BPTR lock,struct HData *HData)
{
  struct FileLock *rl = BADDR(lock);

  if (rl)
	return (CDROM_OBJ *) rl->fl_Key;
  return HData->TopLevelObj;
}

/*
 * If p_pathname contains a ':' character, return the position of the first
 * character after ':'
 * Otherwise, return 0.
 */

int Check_For_Volume_Name_Prefix (char *p_pathname)
{
  char *pos = strchr (p_pathname, ':');

  return pos ? (pos - p_pathname) + 1 : 0;
}



/*
 * Fill an InfoData structure with info on the current volume.
 */
VOID Fill_InfoData(struct InfoData *id,struct HData *HData)
{
memset(id,0,sizeof(struct InfoData));
id->id_UnitNumber = HData->fssm->fssm_Unit;
id->id_DiskState = ID_WRITE_PROTECTED;
if (HData->CurrentVolume)
{
	id->id_NumBlocks	 = Volume_Size(HData->CDVolume);
	id->id_NumBlocksUsed = id->id_NumBlocks - Volume_Free(HData->CDVolume);
D(kprintf("total %ld used %ld\n",id->id_NumBlocks,id->id_NumBlocksUsed));

	id->id_DiskType = ID_DOS_DISK;
	id->id_VolumeNode = MKBADDR(HData->CurrentVolume);
	id->id_BytesPerBlock = Block_Size(HData->CDVolume);
	id->id_InUse = HData->LockList;
}
else
{
	if (HData->Inhibited) id->id_DiskType = 0x42555359;		/* BUSY */
	else switch (HData->DiskInserted)
	{
	case DI_NO_DISK:
	case DI_CDDA_DISK:
		id->id_DiskType = ID_NO_DISK_PRESENT;
		break;

	case DI_UNREADABLE_DISK:
		id->id_DiskType = ID_UNREADABLE_DISK;
		break;

	case DI_DISK_OK:
	default:
		id->id_DiskType = ID_NOT_REALLY_DOS;
		break;
	}
}
}



/*
 * Fills a FileInfoBlock with the information contained in the
 * directory record of a CD-ROM directory or file.
 */

void Fill_FileInfoBlock (struct FileInfoBlock *p_fib, CDROM_INFO *p_info, VOLUME *p_volume,struct HData *HData)
{
char *src = p_info->name;
char *dest = p_fib->fib_FileName+1;
int len = p_info->name_length;


if (p_info->symlink_f)
	p_fib->fib_DirEntryType = ST_SOFTLINK;
else
	p_fib->fib_DirEntryType = p_info->directory_f ? ST_USERDIR : ST_FILE;

p_fib->fib_EntryType = p_fib->fib_DirEntryType;

if (len == 1 && *src == ':')
{
	/* root of file system: */
	p_fib->fib_DirEntryType = ST_USERDIR;
	/* file name == volume name: */
	/* VolumeName is a BCPL string, but it's NULL-terminated. */
	strcpy(p_fib->fib_FileName,HData->VolumeName);
}
else
{
	short real_len;


	/* copy file name: */
	memcpy(dest,src,len);

	real_len = len;

	/* remove version number */
	{
		WORD i;


		i = len;
		while (i)
		{
			if (src[--i] == ';')
			{
				real_len = i;
				break;
			}
		}
	}

	p_fib->fib_FileName[0] = real_len;
	/* NULL terminate the string */
	p_fib->fib_FileName[real_len + 1] = 0;
}

p_fib->fib_Protection = 0;
p_fib->fib_Size = p_info->file_length;
p_fib->fib_NumBlocks = p_info->file_length >> 11;
if (p_info->symlink_f)
	strcpy (p_fib->fib_Comment, "\x0DSymbolic link");
else
	p_fib->fib_Comment[0] = 0;

p_fib->fib_Date.ds_Days   = p_info->date / (24 * 60 * 60);
p_fib->fib_Date.ds_Minute = (p_info->date % (24 * 60 * 60)) / 60;
p_fib->fib_Date.ds_Tick   = (p_info->date % 60) * TICKS_PER_SECOND;
}


/*
 * Mount a volume.
 */

void Mount(struct HData *HData)
{
Clear_Sector_Buffers(HData->CD);	/* make sure the buffers are empty */

HData->CD->t_changeint2 = HData->CD->t_changeint;
HData->DiskInserted = DI_DISK_OK;

if (!(HData->CDVolume = Open_Volume(HData->CD,
		HData->HConfig.Lowercase)))
{
D(kprintf ("!!! cannot open VOLUME !!!\n"));

/* A secondary error less than ERROR_NO_FREE_STORE is supposed to be a device */
/* error. */
	if (IoErr() < ERROR_NO_FREE_STORE)
		HData->DiskInserted = DI_UNREADABLE_DISK;

	if (Has_Audio_Tracks(HData->CD))
	{
		HData->DiskInserted = DI_CDDA_DISK;
		Show_CDDA_Icon(HData);
	}
	CreateInputEvent(TRUE,HData);
	return;
}

if (!(HData->TopLevelObj = Open_Top_Level_Directory(HData->CDVolume)))
{
D(kprintf ("!!! cannot open top level directory !!!\n"));
	Close_Volume(HData->CDVolume);
	HData->CDVolume = NULL;

	CreateInputEvent(TRUE,HData);
	return;
}

D(kprintf ("***mounting***\n"));

Volume_ID(HData->CDVolume,&HData->VolumeName[1],sizeof(HData->VolumeName)-2);
if (!(HData->VolumeName[0] = strlen(&HData->VolumeName[1])))
	strcpy(HData->VolumeName,"\7Unnamed");

/* if we are (probably) dealing with a floppy, read geometry */
if (HData->CD->use_trackdisk && !HData->envec->de_LowCyl &&
		Volume_Size(HData->CDVolume) < 10000)
{
	struct DriveGeometry dg;


	HData->CD->scsireq->io_Command = TD_GETGEOMETRY;
	HData->CD->scsireq->io_Data = &dg;
	HData->CD->scsireq->io_Length = sizeof(struct DriveGeometry);
	if (!DoIO((struct IORequest *)HData->CD->scsireq))
	{
		HData->envec->de_SizeBlock = dg.dg_SectorSize / 4;
		HData->envec->de_HighCyl = dg.dg_Cylinders - 1;
		HData->envec->de_Surfaces = dg.dg_Heads;
		HData->envec->de_BlocksPerTrack = dg.dg_TrackSectors;
	}
}

if (!CreateVolumeNode(HData))
{
	Close_Object(HData->TopLevelObj);
	Close_Volume(HData->CDVolume);
	HData->CDVolume = NULL;
}

CreateInputEvent(TRUE,HData);
}


/*
 * Create Volume node and add to the device list. This will
 * cause the WORKBENCH to recognize us as a disk. If we don't
 * create a Volume node, Wb will not recognize us.
 */

struct DosList *CreateVolumeNode(struct HData *HData)
{
struct DosInfo *di = BADDR(((struct RootNode *)DOSBase->dl_Root)->rn_Info);
struct DosList *dl;
ULONG p_volume_date = Volume_Creation_Date(HData->CDVolume);


Forbid();

for (dl = BADDR(di->di_DevInfo);dl;dl = BADDR(dl->dol_Next))
{
	if (dl->dol_Type == DLT_VOLUME &&
			!Stricmp(BADDR(dl->dol_Name),HData->VolumeName) &&
			dl->dol_misc.dol_volume.dol_DiskType == HData->envec->de_DosType &&
			dl->dol_misc.dol_volume.dol_VolumeDate.ds_Days == p_volume_date / (24 * 60 * 60) &&
			dl->dol_misc.dol_volume.dol_VolumeDate.ds_Minute == (p_volume_date % (24 * 60 * 60)) / 60 &&
			dl->dol_misc.dol_volume.dol_VolumeDate.ds_Tick == (p_volume_date % 60) * TICKS_PER_SECOND)
		break;
}

if (!dl)
{
	if (dl = MakeDosEntry(&HData->VolumeName[1],DLT_VOLUME))
	{
		dl->dol_misc.dol_volume.dol_DiskType = HData->envec->de_DosType;
		dl->dol_misc.dol_volume.dol_VolumeDate.ds_Days = p_volume_date / (24 * 60 * 60);
		dl->dol_misc.dol_volume.dol_VolumeDate.ds_Minute = (p_volume_date % (24 * 60 * 60)) / 60;
		dl->dol_misc.dol_volume.dol_VolumeDate.ds_Tick = (p_volume_date % 60) * TICKS_PER_SECOND;
		dl->dol_Next = di->di_DevInfo;
		di->di_DevInfo = MKBADDR(dl);
	}
}

/* make sure that dl_Task is NULL, otherwise it means that another handler */
/* is using the same disk - we can't create our volume */
if (dl && !dl->dol_Task)
{
	struct FileLock *lock;


	dl->dol_Task = HData->MyPort;
	HData->CurrentVolume = dl;
	HData->LockList = dl->dol_misc.dol_volume.dol_LockList;
	dl->dol_misc.dol_volume.dol_LockList = NULL;

	lock = BADDR(HData->LockList);
	while (lock)
	{
		lock->fl_Task = HData->MyPort;
		lock = BADDR(lock->fl_Link);
	}
}
else dl = NULL;

Permit();

return(dl);
}



/*
 *	Remove Volume entry.
 */

void RemoveVolumeNode(struct HData *HData)
{
if (HData->DiskInserted != DI_NO_DISK)
{
	Hide_CDDA_Icon(HData);
	HData->CD->t_changeint2 = HData->CD->t_changeint;
	HData->DiskInserted = DI_NO_DISK;
	HData->Playing = FALSE;

	if (HData->CurrentVolume)
	{
		D(kprintf("***unmounting***\n"));

	/* remove the volume node only if there are no outstanding locks */
		if (!HData->LockList)
			RemoveDosList(HData->CurrentVolume);
		else
		{
			HData->CurrentVolume->dol_Task = NULL;
			HData->CurrentVolume->dol_misc.dol_volume.dol_LockList = HData->LockList;
		}

		Close_Object(HData->TopLevelObj);
		Close_Volume(HData->CDVolume);

		HData->CurrentVolume = NULL;
		HData->LockList = NULL;
		HData->CDVolume = NULL;
	}

	CreateInputEvent(FALSE,HData);
}
}



VOID RemoveDosList(struct DosList *vol)
{
struct DosInfo *di;
struct DosList *dl;
void *dlp;


D(kprintf("Removing Volume node\n"));
Forbid ();

di = BADDR(((struct RootNode *)DOSBase->dl_Root)->rn_Info);

dlp = &di->di_DevInfo;
for (dl = BADDR(di->di_DevInfo);dl && dl != vol;dl = BADDR(dl->dol_Next))
	dlp = &dl->dol_Next;
if (dl == vol)
{
	*(BPTR *)dlp = dl->dol_Next;
	FreeDosEntry(dl);
}

Permit();
}



/*
 *  Open timer device structures:
 */

int Open_Timer_Device(struct HData *HData)
{
if (HData->TimerPort = CreateMsgPort())
{
	if (HData->TimerIO = (struct timerequest *)
			CreateIORequest(HData->TimerPort,sizeof(struct timerequest)))
	{
		if (!OpenDevice(TIMERNAME,UNIT_VBLANK,HData->TimerIO,0))
			return(1);

		DeleteIORequest(HData->TimerIO);
	}

	DeleteMsgPort(HData->TimerPort);
	HData->TimerPort = NULL;
}

return(0);
}



/*
 *  Open input device structures:
 */

int Open_Input_Device(struct HData *HData)
{
struct MsgPort *port;


if (port = CreateMsgPort())
{
	if (HData->InputIO = CreateIORequest(port,sizeof(struct IOStdReq)))
	{
		if (!OpenDevice("input.device",0,HData->InputIO,0))
		{
			InputBase = (struct Library *)HData->InputIO->io_Device;
			return(1);
		}

		DeleteIORequest(HData->InputIO);
	}

	DeleteMsgPort(port);
}

return(0);
}



/*
 *  Send timer request
 */

void Send_Timer_Request(struct HData *HData)
{
HData->TimerIO->tr_node.io_Command = TR_ADDREQUEST;
HData->TimerIO->tr_time.tv_secs = 1;
HData->TimerIO->tr_time.tv_micro = 0;
SendIO(HData->TimerIO);
}



/*
 *  Check whether the disk has been removed or inserted.
 */

void Check_Disk (struct HData *HData)
{
  int i;
  unsigned long l1, l2;


/* don't check for disk if we are inhibited */
if (HData->Inhibited) return;

D(kprintf ("Checking Disk...\n"));

if (HData->CD->use_trackdisk)
{
	i = (Test_Unit_Ready(HData->CD));
	l1 = HData->CD->t_changeint;
	l2 = HData->CD->t_changeint2;
	if (l1 != l2)
	{
		if (i)
		{
D(kprintf ("disk has been inserted (T %ld)\n", l1));
			RemoveVolumeNode(HData);
			Mount(HData);
		}
		else
		{
			if (HData->DiskInserted != DI_NO_DISK)
			{
D(kprintf ("disk has been removed (T %ld)\n", l1));
				RemoveVolumeNode(HData);
			}
		}
	}
}
else
{
	if (HData->DiskInserted != DI_NO_DISK)
	{
		if (!Test_Unit_Ready(HData->CD))
		{
D(kprintf ("disk has been removed\n"));
			RemoveVolumeNode(HData);
		}
	}
	else
	{
/* have to call Test_Unit_Ready() twice because the first SCSI command */
/* after a disk insertion is always rejected. */
		if (Test_Unit_Ready (HData->CD) || Test_Unit_Ready (HData->CD))
		{
D(kprintf ("disk has been inserted\n"));
			Mount(HData);
		}
	}
}
}



/*
 * generate a `disk inserted/removed' event, in order to get Workbench to
 * rescan the DosList and update the list of volume icons.
 */

VOID CreateInputEvent(BOOL inserted,struct HData *HData)
{
struct InputEvent ie;


memset(&ie,0,sizeof(struct InputEvent));
ie.ie_Class = inserted ? IECLASS_DISKINSERTED : IECLASS_DISKREMOVED;
HData->InputIO->io_Command = IND_WRITEEVENT;
HData->InputIO->io_Data = &ie;
HData->InputIO->io_Length = sizeof(struct InputEvent);
DoIO(HData->InputIO);
}



int
Get_Startup (struct HData *HData)
{
  enum {
    ARG_LOWERCASE,
    ARG_TRACKDISK,
    ARG_POLL,
    ARGCOUNT
  };

LONG Args[ARGCOUNT];
UBYTE *LocalBuffer;
  struct RDArgs *ArgsPtr;
  int result = FALSE,len,i;
UBYTE *p_startup;


  /* Clear the argument vector. */
  memset (Args, 0, sizeof(Args));

  /* valid startup entry? */
if (HData->envec->de_TableSize >= DE_CONTROL)
	p_startup = BADDR(HData->envec->de_Control);
else p_startup = NULL;

if (!p_startup) p_startup = "";

  /* Get the contents of the startup field. */
  len = p_startup[0];
  if (!(LocalBuffer = AllocVec(len + 2,MEMF_ANY)))
	return(FALSE);

  memcpy (LocalBuffer, p_startup + 1, len);
  /* terminate string with LF (fix ReadArgs() bug) */
  LocalBuffer[len] = '\n';
  /* Provide null-termination. */
  /* Provide null-termination. */
  LocalBuffer[len+1] = 0;

  /* Remove leading quotes. */
  for (i = 0 ; i < len ; i++) {
    if (LocalBuffer[i] != ' ') {
      if (LocalBuffer[i] == '\"')
	LocalBuffer[i] = ' ';
      break;
    }
  }

  /* Remove trailing quotes. */
  for (i = len - 1 ; i >= 0 ; i--) {
    if (LocalBuffer[i] != ' '){
      if (LocalBuffer[i] == '\"')
	LocalBuffer[i] = ' ';
      break;
    }
  }

if (ArgsPtr = (struct RDArgs *) AllocDosObjectTags (DOS_RDARGS,TAG_DONE))
{
	/* Don't prompt for input! */
	ArgsPtr->RDA_Flags |= RDAF_NOPROMPT;

	/* Set up for local parsing. */
	ArgsPtr->RDA_Source.CS_Buffer = LocalBuffer;
	ArgsPtr->RDA_Source.CS_Length = strlen ((char *) LocalBuffer);
	ArgsPtr->RDA_Source.CS_CurChr = 0;

	/* Read the arguments. */
	if (ReadArgs(
			"L=LOWERCASE/S,"
			"T=TRACKDISK/S,"
			"P=POLL/S",
			Args,ArgsPtr))
	{
		result = TRUE;


		HData->HConfig.Lowercase = Args[ARG_LOWERCASE];
D(kprintf("lowercase = %ld\n",HData->HConfig.Lowercase));
		HData->HConfig.UseTrackdisk = Args[ARG_TRACKDISK];
D(kprintf("trackdisk = %ld\n",HData->HConfig.UseTrackdisk));

		if (Args[ARG_POLL])
			HData->HConfig.ScanInterval = 3;
D(kprintf("scaninterval = %ld\n",HData->HConfig.ScanInterval));

		FreeArgs(ArgsPtr);
	}

	FreeDosObject (DOS_RDARGS, ArgsPtr);
}

  if (result) {
    if (!(HData->CD = Open_CDROM (&((UBYTE *)BADDR(HData->fssm->fssm_Device))[1],
			HData->fssm->fssm_Unit,HData->fssm->fssm_Flags,
			HData->envec->de_LowCyl * HData->envec->de_Surfaces *
				HData->envec->de_BlocksPerTrack * 4 * HData->envec->de_SizeBlock,
			HData->HConfig.UseTrackdisk,
	/* if the Mask is in 24-bit range, use 24BITDMA memory */
			(HData->envec->de_Mask & 0xff000000) ?
				HData->envec->de_BufMemType : HData->envec->de_BufMemType | MEMF_24BITDMA,
			HData->envec->de_NumBuffers,5, /* file buffers */
			!HData->HConfig.ScanInterval))) {
      result = FALSE;
    }
  }

FreeVec(LocalBuffer);

  return result;
}






VOID Show_CDDA_Icon(struct HData *HData)
{
struct DiskObject *appicon;


if (!IconBase) IconBase = OpenLibrary("icon.library",37);
if (!WorkbenchBase) WorkbenchBase = OpenLibrary("workbench.library",37);

if (!IconBase || !WorkbenchBase || HData->AppIcon) return;

if (!(appicon = GetDiskObject("ENV:sys/def_CDDAdisk")) &&
		!(appicon = GetDefDiskObject(WBKICK)))
	return;

strncpy(HData->VolumeName,&((UBYTE *)BADDR(HData->MyDevNode->dol_Name))[1],sizeof(HData->VolumeName)-1);
if (strlen(HData->VolumeName) < sizeof(HData->VolumeName)-5)
	strcat(HData->VolumeName,":CDDA");

if (HData->AppPort = CreateMsgPort())
	HData->AppIcon = AddAppIconA(0,0,HData->VolumeName,HData->AppPort,NULL,appicon,NULL);

/* copy the default tool name */
if (appicon->do_DefaultTool)
	strcpy(HData->PlayCDDA,appicon->do_DefaultTool);
else HData->PlayCDDA[0] = 0;

/* the icon may be freed immediately, the Workbench copies it internally */
FreeDiskObject(appicon);

/* AddAppIconA may fail if the Workbench has not yet been loaded. */
if (!HData->AppIcon)
{
	DeleteMsgPort(HData->AppPort);
	HData->AppPort = NULL;
}
}



void Hide_CDDA_Icon(struct HData *HData)
{
struct Message *msg;


if (HData->AppIcon)
{
	RemoveAppIcon(HData->AppIcon);
	HData->AppIcon = NULL;

	if (HData->AppPort)
	{
		while (msg = GetMsg(HData->AppPort))
			ReplyMsg(msg);
		DeleteMsgPort(HData->AppPort);

		HData->AppPort = NULL;
	}
}
}
