/****************************************************************************
*
*	KeyMacro-Handler.c ----	KeyMacro handler.
*
*	Author ----------------	Olaf Barthel, MXM
*				Brabeckstrasse 35
*				D-3000 Hannover 71
*
*	KeyMacro  ©  Copyright  1990  by  MXM;  Executable  program,
*	documentation  and  source  code are shareware.  If you like
*	this  program  a  small donation will entitle you to receive
*	updates and new programs from MXM.
*
****************************************************************************/

	/* Function prototypes. */

struct Process *			CreateFuncProc(UBYTE *Name,LONG Priority,APTR InitCode,ULONG StackSize);
VOID					FreeString(BPTR Byte);
BPTR					CreateBSTR(UBYTE *s);
BPTR					CopyPath(VOID);
VOID					FakeCLI(VOID);
VOID					ClearPath(BPTR InitPath);
VOID					StopFakery(VOID);
VOID *					DeleteMacroMsg(struct MacroMessage *scm_Msg);
VOID __saveds				Executor(VOID);
struct MacroKey *			FindMacroKey(LONG Code,LONG Qualifier);
struct InputEvent * __asm __saveds	EventHandler(register __a0 struct InputEvent *Event);
BYTE					OpenAll(VOID);
VOID					CloseAll(VOID);
BYTE					UStrCmp(UBYTE *a,UBYTE *b);

VOID *					AllocRem(LONG ByteSize,LONG Requirements);
VOID *					FreeRem(LONG *MemoryBlock);
VOID *					SendMacroMsg(struct MacroMessage *scm_Msg,struct MsgPort *scm_Port);
ULONG					KeyInvert(UBYTE AnsiKey,struct InputEvent *Event,struct KeyMap *KeyMap,BYTE Depth);

LONG __saveds				Main(VOID);

	/* Shared library identifiers. */

extern struct ExecBase	*SysBase;
struct IntuitionBase	*IntuitionBase;
struct Library		*LayersBase;

	/* Global handshake data. */

struct MSeg		*MSeg;

	/* Process<->Process communication data. */

struct Process		*ExecuteProc;
struct MsgPort		*ExecutePort;

	/* Input device data. */

struct MsgPort		*InputDevPort;
struct IOStdReq		*InputRequestBlock;
struct Interrupt	*InputHandler;
struct InputEvent	*FakeInputEvent;

	/* Console device data. */

struct IOStdReq		*ConsoleRequest;
struct MsgPort		*ConsolePort;

	/* Timer device data. */

struct timerequest	*TimeRequest;
struct MsgPort		*TimePort;

BYTE			 SigBit = -1;

struct ExecBase		*SysBase;
struct DosLibrary	*DOSBase;

	/* Main():
	 *
	 *	This is the entry point to the handler process.
	 */

LONG __saveds
Main()
{
	struct Process		*ThatsMe;
	ULONG			 SignalSet;
	struct MacroMessage	*MacroMsg;
	LONG			 i;

	SysBase = *(struct ExecBase **)4;

		/* Do I know myself? */

	ThatsMe = (struct Process *)SysBase -> ThisTask;

		/* Don't let anybody call us from CLI. */

	if(ThatsMe -> pr_CLI)
		goto Quit;

		/* Can we find the global MsgPort? */

	if(!(MSeg = (struct MSeg *)FindPort(PORTNAME)))
	{
		Forbid();

		Signal(MSeg -> Father,MSeg -> RingBack);

		goto Quit;
	}

		/* This older revision probably doesn't support
		 * some newer structure tags -> exit.
		 */

	if(MSeg -> Revision < REVISION)
	{
		Forbid();

		Signal(MSeg -> Father,MSeg -> RingBack);

		goto Quit;
	}

		/* The MsgPort is already owned by someone
		 * else.
		 */

	if(MSeg -> Port . mp_Flags & PA_SIGNAL)
	{
		Forbid();

		Signal(MSeg -> Father,MSeg -> RingBack);

		goto Quit;
	}

		/* Open the libraries. */

	if(!OpenAll())
	{
		CloseAll();

		Forbid();

		Signal(MSeg -> Father,MSeg -> RingBack);

		goto Quit;
	}

		/* Start the executing process. */

	MSeg -> Child = (struct Task *)ThatsMe;

	if(!(ExecuteProc = (struct Process *)CreateFuncProc("KeyMacro CLI",10,Executor,4000)))
	{
		CloseAll();

		Forbid();

		MSeg -> Child = NULL;
		Signal(MSeg -> Father,MSeg -> RingBack);

		goto Quit;
	}

		/* Wait for handshake signal. */

	Wait(SIG_SHAKE);

		/* Process creation failed. */

	if(!ExecuteProc)
	{
		CloseAll();

		Forbid();

		MSeg -> Child = NULL;
		Signal(MSeg -> Father,MSeg -> RingBack);

		goto Quit;
	}

		/* Now we are truly running. */

	Signal(MSeg -> Father,MSeg -> RingBack);

	MSeg -> Father = NULL;

		/* Re-init the MsgPort flags. */

	MSeg -> Port . mp_Flags		= PA_SIGNAL;
	MSeg -> Port . mp_SigBit	= SigBit;
	MSeg -> Port . mp_SigTask	= MSeg -> Child;

		/* Wait until somebody kicks us out. */

	FOREVER
	{
		SignalSet = Wait(SIG_CLOSE | SIG_PORT);

			/* We are to shut down. */

		if(SignalSet & SIG_CLOSE)
		{
			if(ExecuteProc)
			{
				Signal((struct Task *)ExecuteProc,SIG_CLOSE);

				Wait(SIG_SHAKE);
			}

			CloseAll();

			Forbid();

			Signal(MSeg -> Father,SIG_CLOSE);

			goto Quit;
		}

			/* A message arrived at our home port. */

		if(SignalSet & SIG_PORT)
		{
				/* Walk through the list of messages. */

			while(MacroMsg = (struct MacroMessage *)GetMsg(&MSeg -> Port))
			{
					/* Execute a keyboard macro. */

				if(MacroMsg -> mm_Type == MM_INPUT)
				{
					struct MacroKey *TempMacroKey = MacroMsg -> mm_MacroKey;

					if(TempMacroKey)
					{
							/* Let the execute process run the command. */

						if(TempMacroKey -> mk_Type == MK_COMMAND)
						{
							struct MacroMessage CommandMsg;

							CommandMsg . mm_Type		= MM_EXECUTE;

							CommandMsg . mm_FileName	= TempMacroKey -> mk_String;
							CommandMsg . mm_WindowName	= TempMacroKey -> mk_Window;

							SendMacroMsg(&CommandMsg,ExecutePort);
						}

							/* Build a keyboard macro. */

						if(TempMacroKey -> mk_Type == MK_WORD)
						{
							ConsoleRequest -> io_Command	= CD_ASKDEFAULTKEYMAP;
							ConsoleRequest -> io_Length	= sizeof(struct KeyMap);
							ConsoleRequest -> io_Data	= (APTR)MSeg -> DefaultKeyMap;
							ConsoleRequest -> io_Flags	= IOF_QUICK;

							DoIO(ConsoleRequest);

							InputRequestBlock -> io_Command	= IND_WRITEEVENT;
							InputRequestBlock -> io_Data	= (APTR)FakeInputEvent;
							InputRequestBlock -> io_Length	= sizeof(struct InputEvent);

							for(i = 0 ; i < strlen(TempMacroKey -> mk_String) ; i++)
							{
								if(KeyInvert(TempMacroKey -> mk_String[i],FakeInputEvent,MSeg -> DefaultKeyMap,6))
								{
									FakeInputEvent -> ie_NextEvent	= NULL;
									FakeInputEvent -> ie_Class	= IECLASS_RAWKEY;
									FakeInputEvent -> ie_SubClass	= KM_SUBCLASS;

									FakeInputEvent -> ie_Qualifier |= IEQUALIFIER_RELATIVEMOUSE;

									TimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

									DoIO(TimeRequest);

									FakeInputEvent -> ie_TimeStamp = TimeRequest -> tr_time;

									DoIO(InputRequestBlock);

									if(MSeg -> Delay)
									{
										TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
										TimeRequest -> tr_time . tv_secs	= 0;
										TimeRequest -> tr_time . tv_micro	= MSeg -> Delay;

										DoIO(TimeRequest);
									}
								}
							}
						}
					}
				}

				DeleteMacroMsg(MacroMsg);
			}
		}
	}

Quit:	;
}

	/* UStrCmp():
	 *
	 *	strcmp function which ignores case and allows to
	 *	abbreviate the name of the source string to match
	 *	against the destination string.
	 */

BYTE
UStrCmp(UBYTE *a,UBYTE *b)
{
	SHORT i;

	for(i = 0 ; i < strlen(a) ; i++)
		if(ToUpper(a[i]) != ToUpper(b[i]))
			return(1);

	return(0);
}

	/* CreateFuncProc():
	 *
	 *	Create an independent process from a 'C' routine.
	 */

struct Process *
CreateFuncProc(UBYTE *Name,LONG Priority,APTR InitCode,ULONG StackSize)
{
	struct Process *ChildProc = NULL;

	struct FakeSeg
	{
		BPTR	NextSeg;

		WORD	FirstCode;
		APTR	RealCode;
	} *FakeSeg;

	if(FakeSeg = (struct FakeSeg *)AllocMem(sizeof(struct FakeSeg),MEMF_PUBLIC | MEMF_CLEAR))
	{
		struct MsgPort *ChildPort;

		FakeSeg -> FirstCode	= 0x4EF9;
		FakeSeg -> RealCode	= InitCode;

		if(ChildPort = (struct MsgPort *)CreateProc(Name,Priority,MKBADDR(FakeSeg),StackSize))
			ChildProc = (struct Process *)ChildPort -> mp_SigTask;

		Delay(TICKS_PER_SECOND);

		FreeMem(FakeSeg,sizeof(struct FakeSeg));
	}

	return(ChildProc);
}

	/* FreeString(Byte):
	 *
	 *	Frees the memory occupied by the contents of a BSTR.
	 */

VOID
FreeString(BPTR Byte)
{
	LONG *Ptr = (LONG *)BADDR(Byte);

	FreeMem(Ptr - 1,Ptr[-1]);
}

	/* CreateBSTR(s):
	 *
	 *	Allocates enough memory to hold the contents of
	 *	a given string and makes it a BSTR.
	 */

BPTR
CreateBSTR(UBYTE *s)
{
	LONG	 BlockLength;
	SHORT	 Length;
	UBYTE	*Byte;

	Length = strlen(s);

	BlockLength = (Length + 8) & ~3;

	if(!(Byte = (UBYTE *)AllocMem(BlockLength,MEMF_PUBLIC | MEMF_CLEAR)))
		return(NULL);

	*(LONG *)Byte = BlockLength;

	Byte[4] = Length;
	strncpy(Byte + 5,s,Length);

	return(MKBADDR(Byte + 4));
}

	/* CopyPath():
	 *
	 *	Builds a fake pathlist inherited from any valid
	 *	CLI process or Workbench.
	 */

BPTR
CopyPath()
{
	BPTR				*Next1,*Next2,*Last,NewPath = NULL;
	struct Process			*Father;
	struct CommandLineInterface	*CLI;

	Last = &NewPath;

		/* If using ARP this will also give us a valid
		 * pathlist.
		 */

	if(!(Father = (struct Process *)FindTask("Workbench")))
		if(!(Father = (struct Process *)FindTask("ARP Shell Process")))
			if(!(Father = (struct Process *)FindTask("New CLI")))
				if(!(Father = (struct Process *)FindTask("Initial CLI")))
					return(NULL);

	if(CLI = (struct CommandLineInterface *)BADDR(Father -> pr_CLI))
	{
		for(Next1 = (BPTR *)BADDR(CLI -> cli_CommandDir) ; Next1 ; Next1 = (BPTR *)BADDR(*Next1))
		{
			if(!(Next2 = (BPTR *)AllocMem(2 * sizeof(BPTR),MEMF_PUBLIC | MEMF_CLEAR)))
				break;

			*Last = MKBADDR(Next2);
			 Last = Next2;

			Next2[1] = (BPTR)DupLock(Next1[1]);
			Next2[0] = NULL;
		}
	}

	return(NewPath);
}

	/* FakeCLI():
	 *
	 *	Creates a fake CLI structure for our process. This
	 *	includes pathlist, currentdir, prompt and stack.
	 */

VOID
FakeCLI()
{
	struct Process			*MyProcess = (struct Process *)SysBase -> ThisTask;
	struct CommandLineInterface	*CLI;

	if(CLI = (struct CommandLineInterface *)AllocMem(sizeof(struct CommandLineInterface),MEMF_PUBLIC | MEMF_CLEAR))
	{
		MyProcess -> pr_CLI = MKBADDR(CLI);

		CLI -> cli_SetName	= CreateBSTR("SYS:");
		CLI -> cli_Prompt	= CreateBSTR("%N> ");
		CLI -> cli_DefaultStack	= 4000;

		CurrentDir(Lock("SYS:",ACCESS_READ));

		CLI -> cli_CommandDir	= CopyPath();
	}
}

	/* ClearPath(InitPath):
	 *
	 *	Frees the contents of our fake pathlist.
	 */

VOID
ClearPath(BPTR InitPath)
{
	BPTR *Next,*Path;

	for(Path = (BPTR *)BADDR(InitPath) ; Path ; Path = Next)
	{
		Next = (BPTR *)BADDR(Path[0]);

		if(Path[1])
			UnLock(Path[1]);

		FreeMem(Path,2 * sizeof(BPTR));
	}
}

	/* StopFakery():
	 *
	 *	Removes the contents of our fake CLI structure.
	 */

VOID
StopFakery()
{
	struct CommandLineInterface	*CLI;
	struct Process			*MyProcess;
	BPTR				 MyCD;

	MyProcess = (struct Process *)SysBase -> ThisTask;

	if(CLI = (struct CommandLineInterface *)BADDR(MyProcess -> pr_CLI))
	{
		if(MyCD = (BPTR)CurrentDir(NULL))
			UnLock(MyCD);

		FreeString(CLI -> cli_SetName);
		FreeString(CLI -> cli_Prompt);

		ClearPath(CLI -> cli_CommandDir);

		MyProcess -> pr_CLI = NULL;

		FreeMem(CLI,sizeof(struct CommandLineInterface));
	}
}

	/* DeleteMacroMsg(scm_Msg):
	 *
	 *	Remove a message from memory.
	 */

VOID *
DeleteMacroMsg(struct MacroMessage *scm_Msg)
{
	if(scm_Msg && scm_Msg -> mm_Message . mn_Node . ln_Name == (UBYTE *)scm_Msg)
		FreeRem(scm_Msg);

	return(NULL);
}

	/* Executor():
	 *
	 *	This is the dummy process to execute programs.
	 */

VOID __saveds
Executor()
{
	ULONG			 SignalSet;
	BPTR			 NIL;
	struct MacroMessage	*ExecuteMsg;
	struct Window		*TheWindow;
	UBYTE			 TempLine[300];

	struct Process		*ThatsMe;

	BYTE			 Activate;

	ThatsMe = (struct Process *)SysBase -> ThisTask;

		/* These are inherited from the father process,
		 * we had better cleared them out.
		 */

	ThatsMe -> pr_WindowPtr = (APTR)-1;

		/* Try to allocate a port (we can't use our builtin
		 * DOS port since we are actually calling DOS
		 * routines which may mix up the messages coming
		 * in).
		 */

	if(!(ExecutePort = (struct MsgPort *)CreatePort(NULL,0)))
	{
		Forbid();

		ExecuteProc = NULL;

		Signal(MSeg -> Child,SIG_SHAKE);

		goto Quit;
	}

		/* Open the NULL-Handler. */

	if(!(NIL = Open("NULL:",MODE_NEWFILE)))
	{
		Forbid();

		ExecuteProc = NULL;

		Signal(MSeg -> Child,SIG_SHAKE);
		DeletePort(ExecutePort);

		goto Quit;
	}

		/* Pretend to be a CLI. */

	FakeCLI();

		/* This path leads nowhere. */

	ThatsMe -> pr_CIS		= NIL;
	ThatsMe -> pr_COS		= NIL;

	ThatsMe -> pr_ConsoleTask	= (APTR)DeviceProc("NULL:");

		/* We're on the scene now. */

	Signal(MSeg -> Child,SIG_SHAKE);

	FOREVER
	{
		SignalSet = Wait(SIG_CLOSE | (1 << ExecutePort -> mp_SigBit));

			/* Shut down? */

		if(SignalSet & SIG_CLOSE)
		{
			StopFakery();
			Close(NIL);
			DeletePort(ExecutePort);

			ExecuteProc = NULL;

			Forbid();

			Signal(MSeg -> Child,SIG_SHAKE);

			goto Quit;
		}

			/* Execute a command? */

		while(ExecuteMsg = (struct MacroMessage *)GetMsg(ExecutePort))
		{
			TheWindow	= NULL;
			Activate	= TRUE;

				/* Try to find a matching window title. */

			if(ExecuteMsg -> mm_WindowName)
			{
				ULONG		 IntuiLock;

				struct Screen	*ExScreen;
				struct Window	*ExWindow;

				IntuiLock = LockIBase(NULL);

				if(UStrCmp(ExecuteMsg -> mm_WindowName,IntuitionBase -> ActiveWindow -> Title))
				{
					ExScreen = IntuitionBase -> FirstScreen;

					do
					{
						ExWindow = ExScreen -> FirstWindow;

						do
						{
							if(!UStrCmp(ExecuteMsg -> mm_WindowName,ExWindow -> Title))
							{
								TheWindow = ExWindow;
								break;
							}
						}
						while((ExWindow = ExWindow -> NextWindow) && !TheWindow);
					}
					while((ExScreen = ExScreen -> NextScreen) && !TheWindow);
				}
				else
				{
					TheWindow = IntuitionBase -> ActiveWindow;
					Activate = FALSE;
				}

				UnlockIBase(IntuiLock);
			}

			if(!TheWindow)
			{
					/* No chance, execute the command. */

				strcpy(TempLine,"C:Run <NULL: >NULL: ");
				strcat(TempLine,ExecuteMsg -> mm_FileName);

				Execute(TempLine,NULL,NIL);
			}

			DeleteMacroMsg(ExecuteMsg);

				/* Found a window? Bring it to the front. */

			if(TheWindow)
			{
				LockLayers(TheWindow -> RPort -> Layer -> LayerInfo);

				WindowToFront(TheWindow);

				UnlockLayers(TheWindow -> RPort -> Layer -> LayerInfo);

				ScreenToFront(TheWindow -> WScreen);

				if(Activate)
					ActivateWindow(TheWindow);
			}
		}
	}

		/* Finished, fall through. */

Quit:	;
}

	/* FindMacroKey(Code,Qualifier):
	 *
	 *	Find a macro key entry in the linked list of
	 *	macro key structures.
	 */

struct MacroKey *
FindMacroKey(LONG Code,LONG Qualifier)
{
	SHORT i;

	if(MSeg -> MacroList)
	{
		ObtainSemaphore(&MSeg -> MacroSemaphore);

		for(i = 0 ; i < MSeg -> NumMacros ; i++)
		{
			if(MSeg -> MacroList[i] -> mk_CommandKey == Code && (Qualifier & ~HIDEFLAGS) == MSeg -> MacroList[i] -> mk_CommandQualifier)
			{
				ReleaseSemaphore(&MSeg -> MacroSemaphore);

				return(MSeg -> MacroList[i]);
			}
		}

		ReleaseSemaphore(&MSeg -> MacroSemaphore);
	}

	return(NULL);
}

	/* EventHandler(Event):
	 *
	 *	The input event handler.
	 */

struct InputEvent * __asm __saveds
EventHandler(register __a0 struct InputEvent *Event)
{
	struct MacroKey		*HandlerKey;
	struct InputEvent	*ChainEvent;

	for(ChainEvent = Event ; ChainEvent ; ChainEvent = ChainEvent -> ie_NextEvent)
	{
		if(ChainEvent -> ie_Class == IECLASS_RAWKEY && !(ChainEvent -> ie_Code & IECODE_UP_PREFIX) && ChainEvent -> ie_SubClass != KM_SUBCLASS)
		{
			if(HandlerKey = (struct MacroKey *)FindMacroKey(ChainEvent -> ie_Code,ChainEvent -> ie_Qualifier))
			{
				struct MacroMessage HandlerMsg;

				HandlerMsg . mm_Type		= MM_INPUT;
				HandlerMsg . mm_MacroKey	= HandlerKey;

				SendMacroMsg(&HandlerMsg,&MSeg -> Port);

				ChainEvent -> ie_Class = IECLASS_NULL;
			}
		}
	}

	return(Event);
}

	/* OpenAll():
	 *
	 *	Initialize the input event handler.
	 */

BYTE
OpenAll()
{
	if(!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",33)))
		return(FALSE);

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",33)))
		return(FALSE);

	if(!(LayersBase = (struct Library *)OpenLibrary("layers.library",33)))
		return(FALSE);

	if(!(InputDevPort = (struct MsgPort *)CreatePort(NULL,0)))
		return(FALSE);

	if(!(InputRequestBlock = (struct IOStdReq *)CreateStdIO(InputDevPort)))
		return(FALSE);

	if(OpenDevice("input.device",0,(struct IORequest *)InputRequestBlock,0))
		return(FALSE);

	if(!(InputHandler = (struct Interrupt *)AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC | MEMF_CLEAR)))
		return(FALSE);

	if(!(ConsolePort = (struct MsgPort *)CreatePort(NULL,0)))
		return(FALSE);

	if(!(ConsoleRequest = (struct IOStdReq *)CreateStdIO(ConsolePort)))
		return(FALSE);

	if(OpenDevice("console.device",CONU_LIBRARY,ConsoleRequest,0))
		return(FALSE);

	if(!(TimePort = (struct MsgPort *)CreatePort(NULL,0)))
		return(FALSE);

	if(!(TimeRequest = (struct timerequest *)CreateExtIO(TimePort,sizeof(struct timerequest))))
		return(FALSE);

	if(OpenDevice(TIMERNAME,UNIT_VBLANK,TimeRequest,0))
		return(FALSE);

	if(!(FakeInputEvent = (struct InputEvent *)AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC | MEMF_CLEAR)))
		return(FALSE);

	if((SigBit = AllocSignal(-1)) == -1)
		return(FALSE);

	InputHandler -> is_Code			= (APTR)EventHandler;
	InputHandler -> is_Node . ln_Pri	= MSeg -> Pri;
	InputHandler -> is_Node . ln_Name	= "KeyMacro-Handler";

	InputRequestBlock -> io_Command		= IND_ADDHANDLER;
	InputRequestBlock -> io_Data		= (APTR)InputHandler;

	if(DoIO(InputRequestBlock))
		return(FALSE);

	ConsoleRequest -> io_Command	= CD_ASKDEFAULTKEYMAP;
	ConsoleRequest -> io_Length	= sizeof(struct KeyMap);
	ConsoleRequest -> io_Data	= (APTR)MSeg -> DefaultKeyMap;
	ConsoleRequest -> io_Flags	= IOF_QUICK;

	if(DoIO(ConsoleRequest))
		return(FALSE);

	return(TRUE);
}

	/* CloseAll():
	 *
	 *	Remove the input event handler.
	 */

VOID
CloseAll()
{
	if(InputRequestBlock)
	{
		if(InputRequestBlock -> io_Device)
		{
			if(InputHandler)
			{
				InputRequestBlock -> io_Command	= IND_REMHANDLER;
				InputRequestBlock -> io_Data	= (APTR)InputHandler;

				DoIO(InputRequestBlock);
			}

			CloseDevice(InputRequestBlock);
		}

		DeleteStdIO(InputRequestBlock);
	}

	if(TimeRequest)
	{
		if(TimeRequest -> tr_node . io_Device)
			CloseDevice(TimeRequest);

		DeleteExtIO(TimeRequest);
	}

	if(TimePort)
		DeletePort(TimePort);

	if(ConsoleRequest)
	{
		if(ConsoleRequest -> io_Device)
			CloseDevice(ConsoleRequest);

		DeleteStdIO(ConsoleRequest);
	}

	if(FakeInputEvent)
		FreeMem(FakeInputEvent,sizeof(struct InputEvent));

	if(InputHandler)
		FreeMem(InputHandler,sizeof(struct Interrupt));

	if(InputDevPort)
		DeletePort(InputDevPort);

	if(ConsolePort)
		DeletePort(ConsolePort);

	if(SigBit != -1)
		FreeSignal(SigBit);

	if(LayersBase)
		CloseLibrary(LayersBase);

	if(IntuitionBase)
		CloseLibrary(IntuitionBase);

	if(DOSBase)
		CloseLibrary(DOSBase);
}
