/*
 * $RCSfile: AmigaInterface.c $
 *
 * $Author: marcel $
 *
 * $Revision: 1.1 $
 *
 * $Date: 1995/05/27 15:54:23 $
 *
 * $Locker: marcel $
 *
 * $State: Exp $
 */

/* standard headers */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <graphics/text.h>
#include <graphics/displayinfo.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <dos/exall.h>
#include <dos/dostags.h>
#include <dos/dos.h>

#include <clib/macros.h>

#include <proto/diskfont.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/dos.h>
#include <proto/asl.h>

/* EAGUI headers */
#include "EAGUI.h"
#include "EAGUI_pragmas.h"

/* custom object */
#include "TextField.c"

/* RARS interface */
typedef struct con_vec { double alpha, vc; };
typedef struct colors { int nose, tail; };
typedef struct car_ID { struct con_vec *rob_ptr; struct colors paint_job; char *rob_name; };
extern struct car_ID		drivers[];
extern char				trackfile[];
extern int				car_count;
extern int				lap_count;
extern int				race_count;
extern int				real_speed;
extern int				no_display;
extern int				keep_order;
extern int				randomotion;
extern int				practice;
extern int				surface;
extern int				rndmiz;
extern int				no_waiting;
extern int				randomotion;
extern int				practice;

/* defines */
#define GADID_DRVAVAIL		1
#define GADID_DRVSEL		2
#define GADID_DRVADD		3
#define GADID_DRVREMOVE		4
#define GADID_DRVUP			5
#define GADID_DRVDOWN		6
#define GADID_TRACK			7
#define GADID_PRACLAPS		8
#define GADID_RACES			9
#define GADID_LAPS			10
#define GADID_SURFACE		11
#define GADID_KEEPORDER		12
#define GADID_NOREALTIME		13
#define GADID_NODISPLAY		14
#define GADID_NORANDOMSEED	15
#define GADID_NORANDOM		16
#define GADID_NOKEYBOARD		17
#define GADID_SCREENMODE		18
#define GADID_SCREENMODETEXT	19
#define GADID_SAVE			20
#define GADID_USE			21
#define GADID_CANCEL		22
#define GADID_COUNT			23

#define MAXCARS			16
#define ENVBUFSIZE			1024

/* special defines that refer to tagitems inside taglists */
#define tracknr			(tracktags[0].ti_Data)
#define drvavailnr			(drvavailtags[0].ti_Data)
#define drvselnr			(drvseltags[0].ti_Data)

/* globals */
struct ScreenModeRequester *		smreq_ptr = NULL;
struct NameInfo				nameinfo;
ULONG						scrdepth = 4;
ULONG						scrwidth = 640;
ULONG						scrheight = 480;
ULONG						scrid = HIRESLACE_KEY;
ULONG						scroscantype = OSCAN_TEXT;
ULONG						scrautoscroll = TRUE;
STATIC ULONG					wleft, wtop, wwidth, wheight;
STATIC ULONG					praclaps, races, laps, surfacetype;
STATIC ULONG					keeporder, nodisplay, norealtime, norandomseed, norandom, nokeyboard;
STATIC struct List				tracklist;
STATIC struct List				drvavaillist;
STATIC struct List				drvsellist;
STATIC struct ea_Object *		winobj_ptr		= NULL;
STATIC struct Window *			win_ptr			= NULL;
STATIC struct Screen *			scr_ptr			= NULL;
STATIC struct Gadget *			gadlist_ptr		= NULL;
STATIC struct Gadget *			gadget_ptr_ptr[GADID_COUNT];		/* array of gadget pointers */
STATIC APTR 					visualinfo_ptr		= NULL;
STATIC struct DrawInfo *			drawinfo_ptr		= NULL;
STATIC struct TextFont *			tf_ptr			= NULL;
struct Library *				EAGUIBase			= NULL;
STATIC struct TextAttr 			textattr 			= {NULL, 0, FS_NORMAL, FPF_DISKFONT};
STATIC struct Hook				relhook;
STATIC struct Hook				tfminsizehook;
STATIC struct Hook				tfrenderhook;
STATIC struct IntuiMessage		imsg;
STATIC struct ci_TextField		textfield1;
STATIC BOOL					cancelrars		= FALSE;		/* user clicked cancel/close */
STATIC BOOL					saveenv			= FALSE;		/* use or save */
STATIC UBYTE					rcs_id_string[]	= "$Id: AmigaInterface.c 1.1 1995/05/27 15:54:23 marcel Exp marcel $";
STATIC STRPTR					labels[]			= {
											"Dirt",
											"Paved",
											NULL};
STATIC struct TagItem			cytaglist1[]		= {
											{GTCY_Labels,			(LONG)labels},
											{GTCY_Active,			0},
											{TAG_DONE}};
STATIC struct TagItem			texttaglist1[]		= {
											{GTTX_Border,			TRUE},
											{GTTX_Text,			(ULONG)"default"},
											{TAG_DONE}};
STATIC struct TagItem			cbtaglist1[]		= {
											{GTCB_Scaled,			TRUE},
											{TAG_DONE}};
STATIC struct TagItem			drvavailtags[]		= {
											{GTLV_MakeVisible,		0},
											{GTLV_Labels,			(ULONG)&drvavaillist},
											{GTLV_ShowSelected,		NULL},
											{TAG_DONE}};
STATIC struct TagItem			drvseltags[]		= {
											{GTLV_MakeVisible,		0},
											{GTLV_Labels,			(ULONG)&drvsellist},
											{GTLV_ShowSelected,		NULL},
											{TAG_DONE}};
STATIC struct TagItem			tracktags[]		= {
											{GTLV_MakeVisible,		0},
											{GTLV_Labels,			(ULONG)&tracklist},
											{GTLV_ShowSelected,		NULL},
											{TAG_DONE}};
STATIC struct ea_Object *		gr1_ptr, *gr2_ptr, *gr3_ptr, *ob1_ptr, *ob2_ptr,
							*ob3_ptr, *ob4_ptr, *ob5_ptr, *ob6_ptr, *ob7_ptr,
							*ob8_ptr, *ob9_ptr, *ob10_ptr;
STATIC UBYTE					envbuf_ptr[ENVBUFSIZE];
STATIC struct NewMenu				newmenu_ptr[] = {
	{NM_TITLE,	"Project",			NULL,	0,	0,	NULL},
	{NM_ITEM,		"About...",			"?",		0,	0,	NULL},
	{NM_ITEM,		"Quit",				"Q",		0,	0,	NULL},
	{NM_END,		NULL,				NULL,	0,	0,	NULL},
};
STATIC struct Menu *				menu_ptr;

/* prototypes */
STATIC ULONG __saveds __asm		HookEntry(register __a0 struct Hook *, register __a2 VOID *, register __a1 VOID *);
STATIC VOID					InitHook(struct Hook *, ULONG (*)(), VOID *);
STATIC ULONG					rel_sameleftborder(struct Hook *, struct List *, APTR);
STATIC LONG					init(VOID);
STATIC VOID					cleanup(STRPTR);
STATIC VOID					resizewindow(VOID);
STATIC ULONG					handlemsgs(VOID);

/* cleanup a general list */
STATIC VOID cleanup_list(struct List *list_ptr)
{
	struct Node *node_ptr;
	struct Node *next_ptr;

	if (IsListEmpty(list_ptr))
	{
		return;
	}

	node_ptr = list_ptr->lh_Head;
	while (next_ptr = node_ptr->ln_Succ)
	{
		FreeVec(node_ptr->ln_Name);
		FreeVec(node_ptr);
		node_ptr = next_ptr;
	}
}

STATIC ULONG finddriver(STRPTR name_ptr)
{
	ULONG i;

	for (i = 0; i < MAXCARS; i++)
	{
		if (strcmp(name_ptr, drivers[i].rob_name) == 0)
		{
			return(i);
		}
	}
	return(~0UL);
}

STATIC STRPTR findname(struct List *list_ptr, ULONG number)
{
	struct Node *node_ptr;
	ULONG nr;

	if (IsListEmpty(list_ptr))
	{
		return(NULL);
	}

	node_ptr = list_ptr->lh_Head;
	nr = 0;
	while (node_ptr->ln_Succ)
	{
		if (nr == number)
		{
			return(node_ptr->ln_Name);
		}
		nr++;
		node_ptr = node_ptr->ln_Succ;
	}
	return(NULL);
}

STATIC ULONG findnr(struct List *list_ptr, STRPTR name_ptr)
{
	struct Node *node_ptr;
	ULONG nr;

	if (IsListEmpty(list_ptr))
	{
		return(~(0UL));
	}

	node_ptr = list_ptr->lh_Head;
	nr = 0;
	while (node_ptr->ln_Succ)
	{
		if (strcmp(node_ptr->ln_Name, name_ptr) == 0)
		{
			return(nr);
		}
		nr++;
		node_ptr = node_ptr->ln_Succ;
	}
	return(~(0UL));
}

STATIC VOID addsorted(struct List *list_ptr, struct Node *newnode_ptr)
{
	struct Node *node_ptr;

	if (!(IsListEmpty(list_ptr)))
	{
		node_ptr = list_ptr->lh_TailPred;
		while (node_ptr->ln_Pred)
		{
			if (stricmp(node_ptr->ln_Name, newnode_ptr->ln_Name) < 0)
			{
				Insert(list_ptr, newnode_ptr, node_ptr);
				return;
			}
			node_ptr = node_ptr->ln_Pred;
		}
	}
	AddHead(list_ptr, newnode_ptr);
	return;
}

/* build driver lists */
STATIC VOID buildlist_drivers(VOID)
{
	ULONG i;
	struct Node *node_ptr;
	STRPTR name_ptr;

	for (i = 0; i < MAXCARS; i++)
	{
		if (node_ptr = AllocVec(sizeof(struct Node), MEMF_PUBLIC | MEMF_CLEAR))
		{
			if (name_ptr = AllocVec(strlen(drivers[i].rob_name) + 1, MEMF_PUBLIC))
			{
				strcpy(name_ptr, drivers[i].rob_name);
				node_ptr->ln_Name = name_ptr;
				if (i < car_count)
				{
					AddTail(&drvsellist, node_ptr);
				}
				else
				{
					addsorted(&drvavaillist, node_ptr);
				}
			}
			else
			{
				FreeVec(node_ptr);
			}
		}
	}
}

/* scan directory for track files, and build the track list */
STATIC VOID scandir_tracks(VOID)
{
	struct ExAllControl *	eac;
	BPTR					lock;
	struct ExAllData 		EAData[12]; /* 12 is arbitrarily chosen bufferlength */
	int					status = 0;
	char * 				pattern_ptr;
	BOOL					more;
	struct ExAllData * 		ead;
	struct Node *			node_ptr;
	int 					namelength;

	if (!(pattern_ptr = AllocVec(2 * strlen("#?.trk") + 2, MEMF_PUBLIC)))
	{
		/* could not allocate parse buffer */
		return;
	}

	/* get lock on current directory */
	lock = Lock("", ACCESS_READ);

	if (lock == 0)
	{
		/* could not lock operator directory */
		FreeVec(pattern_ptr);
		return;
	}

	if (eac = AllocDosObject(DOS_EXALLCONTROL, NULL))
	{
		/* Init ExAll control structure */
		eac->eac_LastKey = 0;
		ParsePatternNoCase("#?.trk", pattern_ptr , 2 * strlen("#?.trk") + 2);
		eac->eac_MatchString = (UBYTE *)pattern_ptr;

		/* Walk through directory */
		do
		{
			more = ExAll(lock, EAData, sizeof(EAData), ED_NAME, eac);
			if ((!more) && (IoErr() != ERROR_NO_MORE_ENTRIES)) 
			{
				/* ABNORMAL FAILURE */
				status = 20;
			}
			else if (eac->eac_Entries == 0) 
			{
				/* NO ENTRIES */
			}
			else
			{
				/* set pointer to first ExAllData structure in buffer */
				ead = (struct ExAllData *) EAData;
				do
				{
					/* allocate memory for track node and name */
					if (node_ptr = AllocVec(sizeof(struct Node), MEMF_CLEAR | MEMF_PUBLIC))
					{
						/* copy name to node, remove filename extension */
						namelength = strlen(ead->ed_Name) - 4;
						if (node_ptr->ln_Name = AllocVec(namelength + 1, MEMF_PUBLIC))
						{
							strncpy(node_ptr->ln_Name, ead->ed_Name, namelength);
							node_ptr->ln_Name[namelength] = '\0';

							/* add to list */
							addsorted(&tracklist, node_ptr);
						}
						else
						{
							/* could not allocate string */
							FreeVec(node_ptr);
						}
					}
					/* to next element in buffer */
					ead = ead->ed_Next;
				} while (ead); 
			}
		} while (more); /* load next part of directory into the buffer */
		FreeVec(pattern_ptr);
		FreeDosObject(DOS_EXALLCONTROL, eac);
	}
	else
	{
		/* could not allocate dosobject */
		status = 20;
	}
	/* Release directory */
	UnLock(lock);
	return;
}

/* functions for hook handling */
STATIC ULONG __saveds __asm HookEntry
(
	register __a0 struct Hook *	hook_ptr,
	register __a2 VOID *		object_ptr,
	register __a1 VOID *		message_ptr
)
{
	return((*hook_ptr->h_SubEntry)(hook_ptr, object_ptr, message_ptr));
}

STATIC VOID InitHook(struct Hook *h_ptr, ULONG (*func_ptr)(), VOID *data_ptr)
{
	if (h_ptr)
	{
		h_ptr->h_Entry = (ULONG (*)())HookEntry;
		h_ptr->h_SubEntry = func_ptr;
		h_ptr->h_Data = data_ptr;
	}
}

/* same leftborder relation */
STATIC ULONG rel_sameleftborder(struct Hook *hook_ptr, struct List *list_ptr, APTR msg_ptr)
{
     struct ea_RelationObject *ro_ptr;
     ULONG mlb;
     ULONG lb;
     mlb = 0;

     /* examine the list of objects that are affected by the relation */
     ro_ptr = (struct ea_RelationObject *)list_ptr->lh_Head;
     while (ro_ptr->node.ln_Succ)
     {
          lb = ea_GetAttr(ro_ptr->object_ptr, EA_BorderLeft);

          /* find the maximum values of the minimum sizes */
          mlb = MAX(lb, mlb);

          ro_ptr = (struct ea_RelationObject *)ro_ptr->node.ln_Succ;
     }

     /* set all objects to the newly found minimum sizes */
     ro_ptr = (struct ea_RelationObject *)list_ptr->lh_Head;
     while (ro_ptr->node.ln_Succ)
     {
          ea_SetAttr(ro_ptr->object_ptr, EA_BorderLeft, mlb);

          ro_ptr = (struct ea_RelationObject *)ro_ptr->node.ln_Succ;
     }
     return(0);
}

/* initialize everything */
STATIC LONG init(VOID)
{
	LONG w, h, bl, br, bt, bb;

	/* open the EAGUI library */
	if (!(EAGUIBase = OpenLibrary(EAGUILIBRARYNAME, EAGUILIBRARYVERSION)))
	{
		cleanup("Couldn't open EAGUI.library.\n");
	}

	/* lock the screen */
	if (!(scr_ptr = LockPubScreen(NULL)))
	{
		cleanup("Couldn't lock default public screen.\n");
	}

	/* get the screen font */
	textattr.ta_Name = (STRPTR)(scr_ptr->RastPort.Font->tf_Message.mn_Node.ln_Name);
	textattr.ta_YSize = scr_ptr->RastPort.Font->tf_YSize;

	/* open the font */
	if (!(tf_ptr = OpenDiskFont(&textattr)))
	{
		cleanup("Couldn't open font.\n");
	}

	/* initialize the relation */
	InitHook(&relhook, rel_sameleftborder, NULL);

	/* initialize textfield hooks */
	InitHook(&tfminsizehook, meth_MinSize_TextField, NULL);
	InitHook(&tfrenderhook, meth_Render_TextField, NULL);

	/* set up some defaults for all objects */
	ea_SetAttr(NULL, EA_DefGTTextAttr, (ULONG)&textattr);

	/* now we can build the object tree */
	if (!( winobj_ptr = VGroup
		EA_BorderLeft,		4,
		EA_BorderRight,	4,
		EA_BorderTop,		4,
		EA_BorderBottom,	4,
		EA_Child,			ea_NewObject(EA_TYPE_CUSTOMIMAGE,
			EA_MinSizeMethod,	&tfminsizehook,
			EA_RenderMethod,	&tfrenderhook,
			EA_UserData,		&textfield1,
			TAG_DONE),
		EA_Child,			HGroup
			EA_Weight,		1,
			EA_Child,			GTListView("Available")
				EA_ID,			GADID_DRVAVAIL,
				EA_InstanceAddress,	&gadget_ptr_ptr[GADID_DRVAVAIL],
				EA_Weight,		3,
				EA_GTTagList,		drvavailtags,
				End,
			EA_Child,			VGroup
				EA_BorderLeft,		4,
				EA_BorderRight,	4,
				EA_Weight,		1,
				EA_Child,			EmptyBox(1)
					End,
				EA_Child,			GTButton("Add")
					EA_ID,			GADID_DRVADD,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_DRVADD],
					EA_BorderBottom,	2,
					End,
				EA_Child,			GTButton("Remove")
					EA_ID,			GADID_DRVREMOVE,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_DRVREMOVE],
					EA_BorderTop,		2,
					EA_BorderBottom,	2,
					End,
				EA_Child,			GTButton("Up")
					EA_ID,			GADID_DRVUP,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_DRVUP],
					EA_BorderTop,		2,
					EA_BorderBottom,	2,
					End,
				EA_Child,			GTButton("Down")
					EA_ID,			GADID_DRVDOWN,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_DRVDOWN],
					EA_BorderTop,		2,
					End,
				EA_Child,			EmptyBox(1)
					End,
				End,
			EA_Child,			GTListView("Selected")
				EA_ID,			GADID_DRVSEL,
				EA_InstanceAddress,	&gadget_ptr_ptr[GADID_DRVSEL],
				EA_Weight,		3,
				EA_GTTagList,		drvseltags,
				End,
			End,
		EA_Child,			HGroup
			EA_Weight,		1,
			EA_Child,			GTListView("Track")
				EA_ID,			GADID_TRACK,
				EA_InstanceAddress,	&gadget_ptr_ptr[GADID_TRACK],
				EA_Weight,		1,
				EA_GTTagList,	tracktags,
				End,
			EA_Child,			gr1_ptr = VGroup
				EA_BorderLeft,		4,
				EA_Weight,		1,
				EA_Child,			EmptyBox(1)
					End,
				EA_Child,			ob1_ptr = GTInteger("Practice laps")
					EA_ID,			GADID_PRACLAPS,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_PRACLAPS],
					EA_BorderBottom,	2,
					End,
				EA_Child,			ob2_ptr = GTInteger("Races")
					EA_ID,			GADID_RACES,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_RACES],
					EA_BorderBottom,	2,
					EA_BorderTop,		2,
					End,
				EA_Child,			ob3_ptr = GTInteger("Laps")
					EA_ID,			GADID_LAPS,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_LAPS],
					EA_BorderBottom,	2,
					EA_BorderTop,		2,
					End,
				EA_Child,			ob4_ptr = GTCycle
					EA_ID,			GADID_SURFACE,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_SURFACE],
					EA_BorderTop,		2,
					EA_GTText,		"Surface",
					EA_GTTagList,		cytaglist1,
					End,
				EA_Child,			EmptyBox(1)
					End,
				End,
			End,
		EA_Child,			HGroup
			EA_BorderTop,		4,
			EA_BorderBottom,	4,
			EA_Child,			EmptyBox(1)
				End,
			EA_Child,			gr2_ptr = VGroup
				EA_Child,			ob5_ptr = GTCheckBox("Keep order")
					EA_ID,			GADID_KEEPORDER,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_KEEPORDER],
					EA_GTTagList,		cbtaglist1,
					EA_BorderBottom,	2,
					End,
				EA_Child,			ob6_ptr = GTCheckBox("No realtime")
					EA_ID,			GADID_NOREALTIME,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_NOREALTIME],
					EA_GTTagList,		cbtaglist1,
					EA_BorderBottom,	2,
					EA_BorderTop,		2,
					End,
				EA_Child,			ob7_ptr = GTCheckBox("No display")
					EA_ID,			GADID_NODISPLAY,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_NODISPLAY],
					EA_GTTagList,		cbtaglist1,
					EA_BorderTop,		2,
					End,
				End,
			EA_Child,			EmptyBox(1)
				End,
			EA_Child,			gr3_ptr = VGroup
				EA_Child,			ob8_ptr = GTCheckBox("No random seed")
					EA_ID,			GADID_NORANDOMSEED,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_NORANDOMSEED],
					EA_GTTagList,		cbtaglist1,
					EA_BorderBottom,	2,
					End,
				EA_Child,			ob9_ptr = GTCheckBox("No random")
					EA_ID,			GADID_NORANDOM,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_NORANDOM],
					EA_GTTagList,		cbtaglist1,
					EA_BorderBottom,	2,
					EA_BorderTop,		2,
					End,
				EA_Child,			ob10_ptr = GTCheckBox("No keyboard")
					EA_ID,			GADID_NOKEYBOARD,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_NOKEYBOARD],
					EA_GTTagList,		cbtaglist1,
					EA_BorderTop,		2,
					End,
				End,
			End,
		EA_Child,			HGroup
			EA_Child,			GTButton("Screenmode...")
					EA_ID,			GADID_SCREENMODE,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_SCREENMODE],
				EA_Weight,		1,
				End,
			EA_Child,			GTText(NULL)
					EA_ID,			GADID_SCREENMODETEXT,
					EA_InstanceAddress,	&gadget_ptr_ptr[GADID_SCREENMODETEXT],
				EA_Weight,		3,
				EA_GTTagList,		texttaglist1,
				End,
			End,
		EA_Child,			HGroup
			EA_BorderTop,		4,
			EA_Child,			GTButton("Save")
				EA_ID,			GADID_SAVE,
				EA_InstanceAddress,	&gadget_ptr_ptr[GADID_SAVE],
				EA_Weight,		1,
				End,
			EA_Child,			EmptyBox(2)
				End,
			EA_Child,			GTButton("Use")
				EA_ID,			GADID_USE,
				EA_InstanceAddress,	&gadget_ptr_ptr[GADID_USE],
				EA_Weight,		1,
				End,
			EA_Child,			EmptyBox(2)
				End,
			EA_Child,			GTButton("Cancel")
				EA_ID,			GADID_CANCEL,
				EA_InstanceAddress,	&gadget_ptr_ptr[GADID_CANCEL],
				EA_Weight,		1,
				End,
			End,
		End ) )
	{
		cleanup("Couldn't init the objects.\n");
	}

	/* layout relations */
     ea_NewRelation(gr1_ptr, &relhook,
          EA_Object,          ob1_ptr,
          EA_Object,          ob2_ptr,
          EA_Object,          ob3_ptr,
          EA_Object,          ob4_ptr,
          TAG_DONE);
     ea_NewRelation(gr2_ptr, &relhook,
          EA_Object,          ob5_ptr,
          EA_Object,          ob6_ptr,
          EA_Object,          ob7_ptr,
          TAG_DONE);
     ea_NewRelation(gr3_ptr, &relhook,
          EA_Object,          ob8_ptr,
          EA_Object,          ob9_ptr,
          EA_Object,          ob10_ptr,
          TAG_DONE);

	/* get VisualInfo and DrawInfo */
	if (!(visualinfo_ptr = GetVisualInfo(scr_ptr, TAG_DONE)))
	{
		cleanup("Couldn't get the visual info.\n");
	}
	if (!(drawinfo_ptr = GetScreenDrawInfo(scr_ptr)))
	{
		cleanup("Couldn't get the draw info.\n");
	}

	/* fill in the textfield structure */
	textfield1.tf_string_ptr = "Drivers";				/* title */
	textfield1.tf_textattr_ptr = &textattr;				/* font */
	textfield1.tf_flags = CITF_ALIGNTOP;				/* alignment flags */
	textfield1.tf_frontpen = 1;						/* frontpen color index */

	/* create the menu */
	if (!(menu_ptr = CreateMenusA(newmenu_ptr, NULL)))
	{
		cleanup("Couldn't create the menu.\n");
	}
	if (LayoutMenus(menu_ptr, visualinfo_ptr,
		GTMN_NewLookMenus,		TRUE,
		TAG_DONE) == FALSE)
	{
		cleanup("Couldn't layout the menu.\n");
	}

     /* obtain the minimum dimensions of every object in the tree */
     ea_GetMinSizes(winobj_ptr);

	/* get some attributes */
	ea_GetAttrs(winobj_ptr,
		EA_MinWidth,		&w,
		EA_MinHeight,		&h,
		EA_BorderLeft,		&bl,
		EA_BorderRight,	&br,
		EA_BorderTop,		&bt,
		EA_BorderBottom,	&bb,
		TAG_DONE);

	/* get the environment variable that contains the window dimensions */
	if (GetVar("RARSWINDOW", (STRPTR)envbuf_ptr, (long)ENVBUFSIZE, (long)0) > 0)
	{
		sscanf(envbuf_ptr, "%d %d %d %d", &wleft, &wtop, &wwidth, &wheight);
	}
	else
	{
		/* use defaults for window dimensions */
		wheight = (h + bt + bb) * 3 / 2;
		wwidth = (w + bl + br) * 2;
		wleft = 0;
		wtop = 0;
	}

     /* open the window */
     if (!(win_ptr = OpenWindowTags(NULL,
		WA_ScreenTitle,	"RARS Amiga - Copyright © 1995 Marcel Offermans",
     	WA_Title,			"RARS Amiga",
		WA_Flags,			(WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SIZEGADGET | WFLG_SIZEBBOTTOM | WFLG_ACTIVATE | WFLG_NEWLOOKMENUS),
		WA_IDCMP,			(IDCMP_CLOSEWINDOW | BUTTONIDCMP | INTEGERIDCMP | CYCLEIDCMP | LISTVIEWIDCMP | CHECKBOXIDCMP | IDCMP_REFRESHWINDOW | IDCMP_NEWSIZE | IDCMP_MENUPICK),
		WA_Left,			wleft,
		WA_Top,			wtop,
		WA_Width,			wwidth,
		WA_Height,		wheight,
		TAG_DONE)))
	{
		cleanup("Couldn't open the window.\n");
	}

	/* set the window limits */
	WindowLimits(
		win_ptr,
		w + win_ptr->BorderLeft + win_ptr->BorderRight + bl + br,
		h + win_ptr->BorderTop + win_ptr->BorderBottom + bt + bb,
		~0,
		~0);

	/* add the menu to this window */
	SetMenuStrip(win_ptr, menu_ptr);

	/* create the gadgets and add them to the window */
	resizewindow();

	if (!(smreq_ptr = (struct ScreenModeRequester *)AllocAslRequest(ASL_ScreenModeRequest, NULL)))
	{
		cleanup("Couldn't allocate screenmode requester.");
	}
	return(0);
}

/* clean up everything that was created */
STATIC VOID cleanup(STRPTR str_ptr)
{
	int rc = 0;

	/* if a string is passed, we assume there was some kind of error */
	if (str_ptr)
	{
		Printf("Error: %s", str_ptr);
		rc = 20;
	}

	if (smreq_ptr)
	{
		FreeAslRequest(smreq_ptr);
		smreq_ptr = NULL;
	}

	if (gadlist_ptr)
	{
		RemoveGList(win_ptr, gadlist_ptr, -1);
		ea_FreeGadgetList(winobj_ptr, gadlist_ptr);
		gadlist_ptr = NULL;
	}

	if (win_ptr)
	{
		CloseWindow(win_ptr);
		win_ptr = NULL;
	}

	if (menu_ptr)
	{
		FreeMenus(menu_ptr);
		menu_ptr = NULL;
	}

	if (drawinfo_ptr)
	{
		FreeScreenDrawInfo(scr_ptr, drawinfo_ptr);
		drawinfo_ptr = NULL;
	}

	if (visualinfo_ptr)
	{
		FreeVisualInfo(visualinfo_ptr);
		visualinfo_ptr = NULL;
	}

	if (winobj_ptr)
	{
		ea_DisposeObject(winobj_ptr);
		winobj_ptr = NULL;
	}

	if (tf_ptr)
	{
		CloseFont(tf_ptr);
		tf_ptr = NULL;
	}

	if (scr_ptr)
	{
		UnlockPubScreen(NULL, scr_ptr);
		scr_ptr = NULL;
	}

	if (EAGUIBase)
	{
		CloseLibrary(EAGUIBase);
		EAGUIBase = NULL;
	}

	/* cleanup lists */
	cleanup_list(&tracklist);
	cleanup_list(&drvavaillist);
	cleanup_list(&drvsellist);

	if (rc > 0)
	{
		exit(rc);
	}
}

/* getstate */
STATIC VOID getstate(VOID)
{
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_TRACK], win_ptr, NULL, GTLV_Selected, &tracknr, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_DRVAVAIL], win_ptr, NULL, GTLV_Selected, &drvavailnr, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Selected, &drvselnr, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_PRACLAPS], win_ptr, NULL, GTIN_Number, &praclaps, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_RACES], win_ptr, NULL, GTIN_Number, &races, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_LAPS], win_ptr, NULL, GTIN_Number, &laps, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_SURFACE], win_ptr, NULL, GTCY_Active, &surfacetype, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_KEEPORDER], win_ptr, NULL, GTCB_Checked, &keeporder, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_NOREALTIME], win_ptr, NULL, GTCB_Checked, &norealtime, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_NODISPLAY], win_ptr, NULL, GTCB_Checked, &nodisplay, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_NORANDOMSEED], win_ptr, NULL, GTCB_Checked, &norandomseed, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_NORANDOM], win_ptr, NULL, GTCB_Checked, &norandom, TAG_DONE);
	GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_NOKEYBOARD], win_ptr, NULL, GTCB_Checked, &nokeyboard, TAG_DONE);
}

/* setstate */
STATIC VOID setstate(VOID)
{
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_TRACK], win_ptr, NULL, GTLV_Selected, tracknr, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVAVAIL], win_ptr, NULL, GTLV_Selected, drvavailnr, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Selected, drvselnr, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_PRACLAPS], win_ptr, NULL, GTIN_Number, praclaps, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_RACES], win_ptr, NULL, GTIN_Number, races, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_LAPS], win_ptr, NULL, GTIN_Number, laps, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_SURFACE], win_ptr, NULL, GTCY_Active, surfacetype, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_KEEPORDER], win_ptr, NULL, GTCB_Checked, keeporder, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_NOREALTIME], win_ptr, NULL, GTCB_Checked, norealtime, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_NODISPLAY], win_ptr, NULL, GTCB_Checked, nodisplay, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_NORANDOMSEED], win_ptr, NULL, GTCB_Checked, norandomseed, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_NORANDOM], win_ptr, NULL, GTCB_Checked, norandom, TAG_DONE);
	GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_NOKEYBOARD], win_ptr, NULL, GTCB_Checked, nokeyboard, TAG_DONE);
	if (GetDisplayInfoData(NULL, (UBYTE *)&nameinfo, sizeof(struct NameInfo), DTAG_NAME, scrid))
	{
		GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_SCREENMODETEXT], win_ptr, NULL, GTTX_Text, nameinfo.Name, TAG_DONE);
	}
}

/* (re)create the gadget list */
STATIC VOID resizewindow(VOID)
{
	LONG bl, br, bt, bb;

	/* get gadget state */
	getstate();

	/* if necessary, remove the gadget list from the window, and clean it up */
	if (gadlist_ptr)
	{
		RemoveGList(win_ptr, gadlist_ptr, -1);
		ea_FreeGadgetList(winobj_ptr, gadlist_ptr);
		gadlist_ptr = NULL;
	}

	ea_GetAttrs(winobj_ptr,
		EA_BorderLeft,		&bl,
		EA_BorderRight,	&br,
		EA_BorderTop,		&bt,
		EA_BorderBottom,	&bb,
		TAG_DONE);

	 ea_SetAttrs(winobj_ptr,
		EA_Width,		win_ptr->Width -
					win_ptr->BorderLeft -
					win_ptr->BorderRight -
					bl -
					br,
		EA_Height,	win_ptr->Height -
					win_ptr->BorderTop -
					win_ptr->BorderBottom -
					bt -
					bb,
		EA_Left,		win_ptr->BorderLeft,
		EA_Top,		win_ptr->BorderTop,
		TAG_DONE);

	ea_LayoutObjects(winobj_ptr);

	if (ea_CreateGadgetList(winobj_ptr, &gadlist_ptr, visualinfo_ptr, drawinfo_ptr) != EA_ERROR_OK)
	{     
		cleanup("Couldn't create the gadget list.\n");
	}     

	EraseRect(win_ptr->RPort,     
		win_ptr->BorderLeft,
		win_ptr->BorderTop,
		win_ptr->Width - win_ptr->BorderRight - 1,
		win_ptr->Height - win_ptr->BorderBottom - 1);

	RefreshWindowFrame(win_ptr);

	AddGList(win_ptr, gadlist_ptr, -1, -1, NULL);
	RefreshGList(gadlist_ptr, win_ptr, NULL, -1);
	GT_RefreshWindow(win_ptr, NULL);

	/* finally, we render the imagery, if there is any */
	ea_RenderObjects(winobj_ptr, win_ptr->RPort);

	/* set gadget state */
	setstate();
}

VOID about_rars(VOID)
{
	struct EasyStruct es = {
		sizeof(struct EasyStruct),
		0,
		"About RARS Amiga",
		"Robot Auto Racing Simulation\n\n"
		"Amiga version\n\n"
		"Copyright © 1995 Marcel Offermans",
		"Ok"};

	EasyRequest(NULL, &es, NULL, NULL);
}

/* a normal message handling loop */
STATIC ULONG handlemsgs(VOID)
{
	struct IntuiMessage	*	imsg_ptr;
	ULONG				rc = 0;
	struct Node *			node_ptr;
	struct Node *			node2_ptr;
	ULONG				nr;
	STRPTR				name_ptr;
	struct MenuItem *		menuitem_ptr;

	while (imsg_ptr = GT_GetIMsg(win_ptr->UserPort))
	{
		CopyMem((char *)imsg_ptr, (char *)&imsg, (long)sizeof(struct IntuiMessage));

		GT_ReplyIMsg(imsg_ptr);

		switch (imsg.Class)
		{
			case IDCMP_MENUPICK:
				while (imsg.Code != MENUNULL)
				{
					menuitem_ptr = ItemAddress(menu_ptr, imsg.Code);
					switch (MENUNUM(imsg.Code))
					{
						case 0:	/* Project */
							switch(ITEMNUM(imsg.Code))
							{
								case 0:	/* About... */
									about_rars();
									break;
								case 1:	/* Quit */
									rc = 10;
									cancelrars = TRUE;
									break;
							}
							break;
					}
					imsg.Code = menuitem_ptr->NextSelect;
				}
				break;

			case	IDCMP_REFRESHWINDOW:
				GT_BeginRefresh(win_ptr);
				GT_EndRefresh(win_ptr, TRUE);
				break;

			case	IDCMP_CLOSEWINDOW:
				rc = 10;
				cancelrars = TRUE;
				break;

			case IDCMP_GADGETUP:
			case IDCMP_GADGETDOWN:
				switch (((struct Gadget *)imsg.IAddress)->GadgetID)
				{
					case GADID_SAVE:
						saveenv = TRUE;
						rc = 10;
						break;
					case GADID_USE:
						saveenv = FALSE;
						rc = 10;
						break;
					case GADID_CANCEL:
						rc = 10;
						cancelrars = TRUE;
						break;
					case GADID_DRVADD:
						/* search for driver in available list */
						GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_DRVAVAIL], win_ptr, NULL, GTLV_Selected, &nr, TAG_DONE);
						if (name_ptr = findname(&drvavaillist, nr))
						{
							node_ptr = FindName(&drvavaillist, name_ptr);

							/* deactivate listviews */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVAVAIL], win_ptr, NULL, GTLV_Labels, ~0, TAG_DONE);
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, ~0, TAG_DONE);

							/* remove driver from list */
							Remove(node_ptr);

							/* add driver to list */
							AddTail(&drvsellist, node_ptr);

							/* activate listviews */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVAVAIL], win_ptr, NULL, GTLV_Labels, &drvavaillist, TAG_DONE);
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, &drvsellist, TAG_DONE);
						}
						break;

					case GADID_DRVREMOVE:
						/* search for driver in selected list */
						GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Selected, &nr, TAG_DONE);
						if (name_ptr = findname(&drvsellist, nr))
						{
							node_ptr = FindName(&drvsellist, name_ptr);

							/* deactivate listviews */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVAVAIL], win_ptr, NULL, GTLV_Labels, ~0, TAG_DONE);
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, ~0, TAG_DONE);

							/* remove driver from list */
							Remove(node_ptr);

							/* add driver to list */
							addsorted(&drvavaillist, node_ptr);

							/* activate listviews */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVAVAIL], win_ptr, NULL, GTLV_Labels, &drvavaillist, TAG_DONE);
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, &drvsellist, TAG_DONE);
						}
						break;

					case GADID_DRVUP:
						/* search for driver in selected list */
						GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Selected, &nr, TAG_DONE);
						if (name_ptr = findname(&drvsellist, nr))
						{
							node_ptr = FindName(&drvsellist, name_ptr);

							/* deactivate listview */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, ~0, TAG_DONE);

							/* get the node before the current one */
							node2_ptr = node_ptr->ln_Pred->ln_Pred;

							/* if we're not at the start of the list... */
							if (node2_ptr)
							{
								/* remove driver from list */
								Remove(node_ptr);

								/* add driver to list */
								Insert(&drvavaillist, node_ptr, node2_ptr);
								nr--;
							}

							/* activate listview */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, &drvsellist, GTLV_Selected, nr, TAG_DONE);
						}
						break;
					case GADID_DRVDOWN:
						/* search for driver in selected list */
						GT_GetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Selected, &nr, TAG_DONE);
						if (name_ptr = findname(&drvsellist, nr))
						{
							node_ptr = FindName(&drvsellist, name_ptr);

							/* deactivate listview */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, ~0, TAG_DONE);

							/* get the node before the current one */
							node2_ptr = node_ptr->ln_Succ;

							/* remove driver from list */
							Remove(node_ptr);

							/* add driver to list */
							if (node2_ptr)
							{
								Insert(&drvavaillist, node_ptr, node2_ptr);
								nr++;
							}
							else
							{
								AddTail(&drvavaillist, node_ptr);
							}

							/* activate listview */
							GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_DRVSEL], win_ptr, NULL, GTLV_Labels, &drvsellist, GTLV_Selected, nr, TAG_DONE);
						}
						break;
					case GADID_SCREENMODE:
						if (AslRequestTags(smreq_ptr,
							ASLSM_PrivateIDCMP,			TRUE,
							ASLSM_TitleText,			"Select a screenmode for RARS screen...",
							ASLSM_PositiveText,			"Ok",
							ASLSM_NegativeText,			"Cancel",
							ASLSM_InitialDisplayDepth,	scrdepth,
							ASLSM_InitialDisplayWidth,	scrwidth,
							ASLSM_InitialDisplayHeight,	scrheight,
							ASLSM_InitialDisplayID,		scrid,
							ASLSM_InitialOverscanType,	scroscantype,
							ASLSM_InitialHeight,		400,
							ASLSM_DoWidth,				TRUE,
							ASLSM_DoHeight,			TRUE,
							ASLSM_DoDepth,				TRUE,
							ASLSM_DoOverscanType,		TRUE,
							ASLSM_DoAutoScroll,			TRUE,
							ASLSM_SleepWindow,			TRUE,
							ASLSM_Window,				win_ptr,
							TAG_DONE))
						{
							scrdepth = smreq_ptr->sm_DisplayDepth;
							scrwidth = smreq_ptr->sm_DisplayWidth;
							scrheight = smreq_ptr->sm_DisplayHeight;
							scrid = smreq_ptr->sm_DisplayID;
							scroscantype = smreq_ptr->sm_OverscanType;
							scrautoscroll = smreq_ptr->sm_AutoScroll;

							/* and update the text gadget to show the new mode */
							if (GetDisplayInfoData(NULL, (UBYTE *)&nameinfo, sizeof(struct NameInfo), DTAG_NAME, scrid))
							{
								GT_SetGadgetAttrs(gadget_ptr_ptr[GADID_SCREENMODETEXT], win_ptr, NULL, GTTX_Text, nameinfo.Name, TAG_DONE);
							}
						}
						break;
				}
				break;

			case	IDCMP_NEWSIZE:
				resizewindow();
				break;
		}
	}
	return(rc);
}

VOID show_gui(VOID)
{
	ULONG idcmpmask, signals;
	BOOL done = FALSE;
	struct Node *node_ptr;
	ULONG i;
	struct car_ID temp_drv;

	/* initialize the lists */
	NewList(&tracklist);
	NewList(&drvavaillist);
	NewList(&drvsellist);

	/* get a list of tracks */
	scandir_tracks();

	/* build the driver lists */
	buildlist_drivers();

	/* copy globals from RARS to the local variables */
	laps = lap_count;
	races = race_count;
	praclaps = practice;
	surfacetype = surface;
	keeporder = (keep_order == 1);
	nodisplay = (no_display == 1);
	norealtime = (real_speed == 0);
	norandomseed = (rndmiz == 0);
	nokeyboard = (no_waiting == 1);
	norandom = (randomotion == 0);
	tracknr = findnr(&tracklist, trackfile);
	if (tracknr == ~0)
	{
		tracknr = 0;
	}

	/* initialize everything */
	init();

	/* event handling loop */
	idcmpmask = 1L << win_ptr->UserPort->mp_SigBit;
	while (done == FALSE)
	{
		signals = Wait(idcmpmask);
		if (signals & idcmpmask)
		{
			if (handlemsgs() != 0)
			{
				done = TRUE;
			}
		}
	}

	/* get state */
	getstate();

	/* set RARS globals */
	lap_count = laps;
	race_count = races;
	practice = praclaps;
	surface = surfacetype;
	keep_order = (keeporder) ? (1) : (0);
	no_display = (nodisplay) ? (1) : (0);
	real_speed = (norealtime) ? (0) : (1);
	no_waiting = (nokeyboard) ? (1) : (0);
	rndmiz = (norandomseed) ? (0) : (1);
	randomotion = (norandom) ? (0) : (1);
	strcpy(trackfile, findname(&tracklist, tracknr));
	strcat(trackfile, ".trk");

	sprintf(envbuf_ptr, "TRACK=\"%s\" PRACTICELAPS=%d "
		"LAPS=%d RACES=%d SURFACETYPE=%d %s%s%s%s%s%s"
		"SCREENDEPTH=%d SCREENWIDTH=%d SCREENHEIGHT=%d SCREENMODEID=%d "
		"SCREENOVERSCANTYPE=%d %s",
		findname(&tracklist, tracknr),
		praclaps, laps, races, surfacetype,
		(keeporder) ? ("KEEPORDER ") : (""),
		(nodisplay) ? ("NODISPLAY ") : (""),
		(norealtime) ? ("NOREALTIME ") : (""),
		(nokeyboard) ? ("NOKEYBOARD ") : (""),
		(norandomseed) ? ("NORANDOMSEED ") : (""),
		(norandom) ? ("NORANDOM ") : (""),
		scrdepth, scrwidth, scrheight, scrid, scroscantype,
		(scrautoscroll) ? ("") : ("SCREENNOAUTOSCROLL"));

	/* list of drivers in the race */
	car_count = 0;
	node_ptr = drvsellist.lh_Head;

	strcat(envbuf_ptr, "DRIVERS");

	while (node_ptr->ln_Succ)
	{
		if ((i = finddriver(node_ptr->ln_Name)) == ~0UL)
		{
			break;
		}
		temp_drv = drivers[car_count];
		drivers[car_count] = drivers[i];
		drivers[i] = temp_drv;

		strcat(envbuf_ptr, " ");
		strcat(envbuf_ptr, node_ptr->ln_Name);

		/* next car */
		car_count++;
		node_ptr = node_ptr->ln_Succ;
	}

	if (saveenv)		
	{
		SetVar("RARSOPTS", envbuf_ptr, ENVBUFSIZE, GVF_GLOBAL_ONLY | GVF_SAVE_VAR);
	}
	else
	{
		SetVar("RARSOPTS", envbuf_ptr, ENVBUFSIZE, GVF_GLOBAL_ONLY);
	}

	/* save the window dimensions */
	sprintf(envbuf_ptr, "%d %d %d %d", win_ptr->LeftEdge, win_ptr->TopEdge, win_ptr->Width, win_ptr->Height);
	SetVar("RARSWINDOW", envbuf_ptr, ENVBUFSIZE, GVF_GLOBAL_ONLY | GVF_SAVE_VAR);

	/* cleanup everything */
	cleanup(NULL);

	/* if the user clicked on cancel or close, we quit */
	if (cancelrars)
	{
		exit(0);
	}
}
