#ifndef DEVICES_GAMEPORT_H
#include <devices/gameport.h>
#endif

#ifndef PREFS_PREFHDR_H
#include <prefs/prefhdr.h>
#endif

#ifndef DOS_EXALL_H
#include <dos/exall.h>
#endif

#ifndef WORKBENCH_STARTUP_H
#include <workbench/startup.h>
#endif

#ifndef INTUITION_IMAGECLASS_H
#include <intuition/imageclass.h>
#endif

#ifndef GRAPHICS_VIDEOCONTROL_H
#include <graphics/videocontrol.h>
#endif

#include <proto/locale.h>
#include <proto/layers.h>
#include <proto/commodities.h>
#include <proto/gadtools.h>
#include <proto/diskfont.h>
#include <proto/icon.h>
#include <proto/wb.h>
#include <proto/asl.h>
#include <proto/utility.h>

#include "Version.h"

#define CATCOMP_BLOCK

#include "xmris.h"
#include "defmris.h"
#include "ConfigKeys.h"
#include "ChangeUser.h"
#include "Manual.h"
#include "BOOPSILib.h"
#include "ConfigFont.h"
#include "ConfigScreen.h"
#include "Errors.h"

#include "StaticSavedsAsmA1A2.h"

#include "Catalog.h"

#define JOYSTICK_TRIES		200

/************************************************************************/

const char VersionString[]=
  "$VER: " PROGRAM_NAME " " PROGRAM_VERSION " (" PROGRAM_DATE ")based on Xmris " XMRISVERSION " (" DATE ")";

const ULONG StackSize=45000;

/************************************************************************/

extern struct DiskObject mrisIcon;

/************************************************************************/

struct GameSupportBase *GameSupportBase;

struct DosLibrary *DOSBase;
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
struct Library *UtilityBase;
struct Library *LocaleBase;
struct Library *LayersBase;
struct Library *CxBase;
struct Library *GadToolsBase;
struct Library *KeymapBase;
struct Library *IconBase;
struct Library *WorkbenchBase;

struct Locale *Locale;
struct Catalog *Catalog;

struct TimeFormats Formats;

#if defined(__SASC)
UWORD __chip DitherPattern[2]={0x5555,0xaaaa};
#else
UWORD *DitherPattern;
#endif

struct MiscState MiscState;

struct MsgPort WindowPort=
{
  {NULL,NULL,NT_MSGPORT,0,NULL},
  PA_SIGNAL,
  0,
  NULL,
  {(struct Node *)&WindowPort.mp_MsgList.lh_Tail,NULL,(struct Node *)&WindowPort.mp_MsgList.lh_Head,NT_MESSAGE}
};

struct MsgPort MiscPort=
{
  {NULL,NULL,NT_MSGPORT,0,NULL},
  PA_SIGNAL,
  0,
  NULL,
  {(struct Node *)&MiscPort.mp_MsgList.lh_Tail,NULL,(struct Node *)&MiscPort.mp_MsgList.lh_Head,NT_MESSAGE}
};

struct TextAttr TextAttr;
char *ScreenDescription;

/************************************************************************/

static char *WindowTitle;
static ULONG AllocateJoystickCounter;

static struct Window *About;

static struct Window *CompileInfo;
extern const char CompileDate[];
extern const char CompileTime[];

static BPTR GardenDir;

static struct MsgPort WBPort=
{
  {NULL,NULL,NT_MSGPORT,0,NULL},
  PA_SIGNAL,
  SIGBREAKB_CTRL_F,
  NULL,
  {(struct Node *)&WBPort.mp_MsgList.lh_Tail,NULL,(struct Node *)&WBPort.mp_MsgList.lh_Head,NT_MESSAGE}
};

static CxObj *Broker;
static struct NewBroker NewBroker=
{
  NB_VERSION,
  NULL,
  NULL,
  NULL,
  NBU_UNIQUE,
  COF_SHOW_HIDE,
  0,
  &MiscPort
};

static struct AsyncAslMessage ConfigMessage;

static struct Gadget IconifyGadget=
{
  NULL,
  0,0,0,0,
  GFLG_GADGHIMAGE | GFLG_GADGIMAGE | GFLG_RELRIGHT,
  GACT_RELVERIFY | GACT_TOPBORDER,
  GTYP_BOOLGADGET,
  NULL,
  NULL,
  NULL,
  0,
  NULL,
  0,
  NULL
};

/************************************************************************/

static const char * const MrisToolTypes[]=
{
  "UP",			/* 0 */
  "DOWN",		/* 1 */
  "LEFT",		/* 2 */
  "RIGHT",		/* 3 */
  "THROW",		/* 4 */
  "PAUSE",		/* 5 */
  "QUIT",		/* 6 */
  "ICONIZE",		/* 7 */
  "RANDOM",		/* 8 */
  "DYNAMIC",		/* 9 */
  "USER",		/* 10 */
  "PUBSCREEN",		/* 11 */
  "SCREEN",		/* 12 */
  "JOYSTICK",		/* 13 */
  "FONT",		/* 14 */
  "MSIT"		/* 15 */
};

/************************************************************************/

static int ProcessMisc(void);

/************************************************************************/
/*									*/
/* Compare two scores							*/
/*									*/
/************************************************************************/

STATIC_SAVEDS_ASM_A1A2(ULONG,ScoreCompareCode,void *,Score2,void *,Score1)

{
  LONG Diff;

  Diff=((HIGH_SCORE *)Score1)->score-((HIGH_SCORE *)Score2)->score;
  if (Diff==0)
    {
      Diff=((HIGH_SCORE *)Score1)->screen-((HIGH_SCORE *)Score2)->screen;
      if (Diff==0)
	{
	  Diff=((HIGH_SCORE *)Score1)->elapsed-((HIGH_SCORE *)Score2)->elapsed;
	  if (Diff==0)
	    {
	      Diff=((HIGH_SCORE *)Score1)->Score.TimeStamp-((HIGH_SCORE *)Score2)->Score.TimeStamp;
	    }
	}
    }
  return (ULONG)Diff;
}

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

static const struct Hook ScoreCompareHook=
{
  {NULL,NULL},ScoreCompareCode,NULL,NULL
};

/************************************************************************/

const struct ScoreDef ScoreDef=
{
  {
    &ScoreCompareHook,
    PROGRAM_NAME,
    { HIGH_SCORES, HIGH_SCORES, HIGH_SCORES },
    ARRAYSIZE(ScoreDef.Chunks)
  },
  {
    {MAKE_ID('S','C','O','R'),GS_SCOREDEFF_INTEGER,sizeof(ULONG)},
    {MAKE_ID('L','E','V','L'),GS_SCOREDEFF_INTEGER,sizeof(ULONG)},
    {MAKE_ID('E','L','A','P'),GS_SCOREDEFF_INTEGER,sizeof(ULONG)}
  }
};

/************************************************************************/

#define NM(Type,Name,Flags) {NM_##Type,(STRPTR)MSG_##Name##_MENU,NULL,Flags,0,(APTR)MSG_##Name##_MENU}

static struct NewMenu NewMenu[]=
{
  {NM_TITLE,(STRPTR)MSG_GAME_MENU,NULL,0,0,NULL},
  NM(ITEM,GAME_USER,0),
  NM(ITEM,GAME_ERRORS,0),
  {NM_ITEM,NM_BARLABEL,NULL,0,0,NULL},
  NM(ITEM,GAME_ABOUT,0),
  {NM_ITEM,NM_BARLABEL,NULL,0,0,NULL},
  NM(ITEM,GAME_QUIT,0),

  {NM_TITLE,(STRPTR)MSG_GARDENS_MENU,NULL,0,0,(APTR)MSG_GARDENS_MENU},

  {NM_TITLE,(STRPTR)MSG_DEMO_MENU,NULL,0,0,(APTR)MSG_DEMO_MENU},
  NM(ITEM,DEMO_SCORES,NM_ITEMDISABLED),
  NM(ITEM,DEMO_BOARD,NM_ITEMDISABLED),
  NM(ITEM,DEMO_KEYS,0),
  NM(ITEM,DEMO_NAME,0),
  NM(ITEM,DEMO_SPRITES,0),

  {NM_TITLE,(STRPTR)MSG_CONFIG_MENU,NULL,0,0,NULL},

  NM(ITEM,CONFIG_RANDOM,CHECKIT | MENUTOGGLE),

  {NM_ITEM,(STRPTR)MSG_CONFIG_CONTROL_MENU,NULL,0,0,NULL},
  NM(SUB,CONFIG_CONTROL_JOYSTICK,CHECKIT | MENUTOGGLE | CHECKED),
  NM(SUB,CONFIG_CONTROL_KEYS,0),

  {NM_ITEM,(STRPTR)MSG_CONFIG_DISPLAY_MENU,NULL,0,0,NULL},
  NM(SUB,CONFIG_DISPLAY_MSIT,CHECKIT | MENUTOGGLE),
  NM(SUB,CONFIG_DISPLAY_DYNAMIC,CHECKIT | MENUTOGGLE | CHECKED),
  NM(SUB,CONFIG_DISPLAY_SCREEN,0),
  NM(SUB,CONFIG_DISPLAY_FONT,0),

  {NM_ITEM,NM_BARLABEL,NULL,0,0,NULL},
  NM(ITEM,CONFIG_SAVE,0),
  NM(ITEM,CONFIG_SAVEDEFAULT,0),

  {NM_END}
};

struct StringNode
{
  struct StringNode *Next;
};

static struct StringNode *GardenStrings;

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

void FreeBOOPSIClasses(void)

{
  if (FreeClass(BOOPSILibC_Root))
    {
      BOOPSILibC_Root=NULL;
    }
  if (FreeClass(BOOPSILibC_Group))
    {
      BOOPSILibC_Group=NULL;
    }
  if (FreeClass(BOOPSILibC_String))
    {
      BOOPSILibC_String=NULL;
    }
  if (FreeClass(BOOPSILibC_Button))
    {
      BOOPSILibC_Button=NULL;
    }
  if (FreeClass(BOOPSILibC_Label))
    {
      BOOPSILibC_Label=NULL;
    }
  if (FreeClass(BOOPSILibC_Frame))
    {
      BOOPSILibC_Frame=NULL;
    }
}

/************************************************************************/

int CreateBOOPSIClasses(void)

{
  if (BOOPSILibC_Group!=NULL || CreateBOOPSILibC_Group()!=NULL)
    {
      if (BOOPSILibC_String!=NULL || CreateBOOPSILibC_String()!=NULL)
	{
	  if (BOOPSILibC_Button!=NULL || CreateBOOPSILibC_Button()!=NULL)
	    {
	      if (BOOPSILibC_Label!=NULL || CreateBOOPSILibC_Label()!=NULL)
		{
		  if (BOOPSILibC_Frame!=NULL || CreateBOOPSILibC_Frame()!=NULL)
		    {
		      if (BOOPSILibC_Root!=NULL || CreateBOOPSILibC_Root()!=NULL)
			{
			  return TRUE;
			}
		    }
		}
	    }
	}
    }
  FreeBOOPSIClasses();
  return FALSE;
}

/************************************************************************/
/*									*/
/* Delete a Pixmap							*/
/*									*/
/************************************************************************/

static void DeletePixmap(Pixmap *Data)

{
  if (Data->Layer!=NULL)
    {
      while (!DeleteLayer(0,Data->Layer))
	{
	  Delay(TICKS_PER_SECOND);	/* anyone with a better idea? */
	}
      Data->Layer=NULL;
    }
  if (Data->LayerInfo!=NULL)
    {
      DisposeLayerInfo(Data->LayerInfo);
      Data->LayerInfo=NULL;
    }
  WaitBlit();
  FreeBitMap(Data->BitMap);
  Data->BitMap=NULL;
}

/************************************************************************/
/*									*/
/* Delete our offscreen bitmaps						*/
/*									*/
/************************************************************************/

static void FreeBitmaps(void)

{
  int i;

  DeletePixmap(&display.copy);
  DeletePixmap(&display.back);
  for (i=0; i<BOARD_SCORES; i++)
    {
      DeletePixmap(&update.score.list[i].image);
      DeletePixmap(&update.score.list[i].mask);
    }
}

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

static void FreeJoystick(void)

{
  if (MiscState.Joystick)
    {
      MiscState.Joystick=FALSE;
      AbortIO(&GameSupportBase->Joystick.Request);
      WaitIO(&GameSupportBase->Joystick.Request);
      GS_FreeJoystick();
    }
  AllocateJoystickCounter=0;
}

/************************************************************************/
/*									*/
/* Free dynamic colors							*/
/*									*/
/************************************************************************/

static void ReleaseDynamic(void)

{
  if (display.dynamic)
    {
      struct ColorMap *ColorMap;

      ColorMap=display.Screen->Screen->ViewPort.ColorMap;
      ReleasePen(ColorMap,display.DynamicColor2);
      ReleasePen(ColorMap,display.DynamicColor1);
      display.dynamic=FALSE;
    }
}

/************************************************************************/
/*									*/
/* Free the sprites							*/
/*									*/
/************************************************************************/

static void FreeSprites(void)

{
  if (Sprites!=NULL)
    {
      if (display.window!=NULL)
	{
	  SetABPenDrMd(display.window->RPort,0,0,JAM1);
	  RectFill(display.window->RPort,display.OriginX,display.OriginY,
		   display.OriginX+WINDOW_WIDTH-1,display.OriginY+WINDOW_HEIGHT-1);
	}
      ReleaseDynamic();
      GS_FreeSprites(Sprites,display.Screen->Screen);
      Sprites=NULL;
      FreeSysRequest(About);
      About=NULL;
    }
}

/************************************************************************/
/*									*/
/* Process a "key".							*/
/*									*/
/************************************************************************/

static INLINE void KeyDown(int Key)

{
  global.pressed|=1<<Key;
  if (Key==KEY_PAUSE)
    {
      timer_set((unsigned long)0, TIMING_PAUSE);
      global.pause = 1;
    }
}

/************************************************************************/

static INLINE void KeyUp(int Key)

{
  global.pressed&=~(1<<Key);
}

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

static void InactiveWindow(void)

{
  int Key;

  FreeJoystick();
  if (MiscState.GameMode)
    {
      KeyDown(KEY_PAUSE);
    }
  for (Key=0; Key<KEYS; Key++)
    {
      KeyUp(Key);
    }
}

/************************************************************************/
/*									*/
/* Free all GUI related resources (except for the menu)			*/
/*									*/
/************************************************************************/

static void CloseGUI(void)

{
  if (!ErrorFlag)
    {
      CloseErrorWindow();
    }
  if (display.Screen!=NULL)
    {
      if (display.VisualInfo!=NULL)
	{
	  if (display.window!=NULL)
	    {
	      InactiveWindow();
	      CloseConfigKeysWindow();
	      CloseChangeUserWindow();
	      CloseManual();
	      FreeSysRequest(CompileInfo);
	      CompileInfo=NULL;
	      global.pressed=0;
	      FreeSprites();
	      ClearMenuStrip(display.window);
	      ((struct Process *)WindowPort.mp_SigTask)->pr_WindowPtr=display.window->UserData;
	      CloseWindowSafely(display.window);
	      display.window=NULL;
	      FreeBitmaps();
	      if (IconifyImageClass!=NULL)
		{
		  DisposeObject(IconifyGadget.GadgetRender);
		  IconifyGadget.GadgetRender=NULL;
		  FreeClass(IconifyImageClass);
		  IconifyImageClass=NULL;
		}
	      if (MiscState.HappyBlanker)
		{
		  MiscState.HappyBlanker=FALSE;
		  GS_NoHappyBlanker();
		}
	    }
	  if (data.font!=NULL)
	    {
	      CloseFont(data.font);
	      data.font=NULL;
	    }
	  FreeVisualInfo(display.VisualInfo);
	  display.VisualInfo=NULL;
	}
      FreeScreenDrawInfo(display.Screen->Screen,display.DrawInfo);
      if (FreeScreen(display.Screen,FALSE))
	{
	  display.Screen=NULL;
	}
    }
  ErrorFlag=FALSE;
}

/************************************************************************/
/*									*/
/* Change key description						*/
/* In case of failure, the key is not changed.				*/
/* If Description==NULL, the default binding is used			*/
/*									*/
/************************************************************************/

int ChangeKey(int Key, char *Description)

{
  if (Description==NULL)
    {
      if (Key==KEYS)
	{
	  Description="help";
	}
      else if (Key==KEYS+1)
	{
	  Description="lshift rshift help";
	}
      else
	{
	  Description=GetString(MSG_KEY_UP+Key);
	}
      ParseIX(data.keysyms[Key].Description=Description,&data.keysyms[Key].InputXpression);
    }
  else
    {
      struct InputXpression InputXpression;
      char *String;

      if (ParseIX(Description,&InputXpression)!=0 || InputXpression.ix_Class!=IECLASS_RAWKEY)
	{
	  DisplayError(MSG_INVALIDKEY_ERROR,Description,GetString(MSG_DEMO_UP+Key));
	  return FALSE;
	}
      if ((String=GS_MemoryAlloc(strlen(Description)+1))==NULL)
	{
	  DisplayError(0,ERROR_NO_FREE_STORE);
	  return FALSE;
	}
      if (data.keysyms[Key].Description!=GetString(MSG_KEY_UP+Key))
	{
	  GS_MemoryFree(data.keysyms[Key].Description);
	}
      strcpy((data.keysyms[Key].Description=String),Description);
      data.keysyms[Key].InputXpression=InputXpression;
    }
  return TRUE;
}

/************************************************************************/
/*									*/
/* Free key descriptions						*/
/*									*/
/************************************************************************/

static INLINE void FreeKeys(void)

{
  int Key;

  for (Key=0; Key<KEYS; Key++)
    {
      ChangeKey(Key,NULL);
    }
}

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

static INLINE void FreeGardens(void)

{
  struct Menu *Menu;

  Menu=FindMenu(MSG_GARDENS_MENU);
  while (Menu->FirstItem!=NULL)
    {
      struct MenuItem *Item;

      Item=Menu->FirstItem;
      Menu->FirstItem=Item->NextItem;
      Item->NextItem=NULL;
      FreeMenus((struct Menu *)Item);
    }
}

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

static void CloseAll(int RC, LONG Error)

{
  if (GadToolsBase!=NULL)
    {
      if (GameSupportBase!=NULL)
	{
	  if (CxBase!=NULL)
	    {
	      CloseErrorWindow();
	      FreeErrors();
	      DeleteCxObj(Broker);
#if !defined(__SASC)
	      if (DitherPattern!=NULL)
		{
#endif
		  CloseGUI();
		  if (board.list!=NULL)
		    {
		      FreeFile((LONG *)(((char *)board.list)-12)-1);
		    }
		  FreeKeys();
#if !defined(__SASC)
		  FreeMem(DitherPattern,2*sizeof(UWORD));
		}
#endif
	      while (FontMessage.Message.mn_Node.ln_Type!=NT_UNKNOWN ||
		     ConfigMessage.Message.mn_Node.ln_Type!=NT_UNKNOWN ||
		     ScreenMessage.Message.mn_Node.ln_Type!=NT_UNKNOWN)
		{
		  WaitPort(&MiscPort);
		  ProcessMisc();
		}
	      while (ProcessMisc())
		;
	      GS_MemoryFree(WindowTitle);
	      GS_ReleaseScoreHandle(scoring.ScoreHandle);
	      if (display.Menustrip!=NULL)
		{
		  FreeGardens();
		  FreeMenus(display.Menustrip);
		}
	      while (GardenStrings!=NULL)
		{
		  struct GardenString *Next;

		  Next=GardenStrings->Next;
		  GS_MemoryFree(GardenStrings);
		  GardenStrings=Next;
		}
	      if (Locale!=NULL)
		{
		  CloseCatalog(Catalog);
		  CloseLocale(Locale);
		}
	      FreeVec(TextAttr.ta_Name);
	      CloseLibrary(CxBase);
	      if (display.Screen!=NULL)
		{
		  FreeScreen(display.Screen,TRUE);
		}
	    }
	  if (GardenDir!=MKBADDR(NULL))
	    {
	      UnLock(GardenDir);
	    }
	  GS_MemoryFree(ScreenDescription);
	  GS_MemoryFree(NewBroker.nb_Name);
	  CloseLibrary(&GameSupportBase->Library);
	}
      CloseLibrary(GadToolsBase);
      CloseLibrary(IconBase);
    }
  FreeSignal(MiscPort.mp_SigBit);
  MyExit(RC,Error);
}

/************************************************************************/
/*									*/
/* Install a clipping rectangle into a Pixmap				*/
/*									*/
/************************************************************************/

struct Region *InstallBounds(Pixmap *Data, struct Rectangle Rectangle)

{
  struct Region *Region;

  assert(Data->Layer!=NULL);
  if ((Region=NewRegion())!=NULL)
    {
      if (OrRectRegion(Region,&Rectangle))
	{
	  struct Region *OldRegion;

	  OldRegion=InstallClipRegion(Data->Layer,Region);
	  assert(OldRegion==NULL);
	  return Region;
	}
      DisposeRegion(Region);
    }
  return NULL;
}

/************************************************************************/
/*									*/
/* Remove the current clipping rectangle from the Pixmap		*/
/*									*/
/************************************************************************/

void RemoveBounds(Pixmap *Data)

{
  struct Region *Region;

  assert(Data->Layer!=NULL);
  Region=InstallClipRegion(Data->Layer,NULL);
  assert(Region!=NULL);
  DisposeRegion(Region);
}

/************************************************************************/
/*									*/
/* Get a localized string						*/
/*									*/
/************************************************************************/

const char *GetString(LONG StringID)

{
  const char *String;

  String=LocaleBase!=NULL ? (char *)GetCatalogStr(Catalog,StringID,NULL) : NULL;
  if (String==NULL)
    {
      const LONG *l;
      const UWORD *w;

      l=(const LONG *)CatCompBlock;
      while (*l != StringID)
	{
	  w=(const UWORD *)((ULONG)l + 4);
	  l=(const LONG *)((ULONG)l + (ULONG)*w +6);
	}
      String=(char *)((ULONG)l + 6);
    }
  return String;
}

/************************************************************************/
/*									*/
/* Obtain dynamic colors, if required					*/
/*									*/
/************************************************************************/

static void ObtainDynamic(void)

{
  display.dynamic=FALSE;
  if (GfxBase->LibNode.lib_Version>=39)
    {
      if (!data.mono)
	{
	  struct ColorMap *ColorMap;

	  if ((ColorMap=display.Screen->Screen->ViewPort.ColorMap)!=NULL)
	    {
	      if ((display.DynamicColor1=ObtainPen(ColorMap,-1,0,0,0,PENF_EXCLUSIVE | PENF_NO_SETCOLOR))!=~0)
		{
		  if ((display.DynamicColor2=ObtainPen(ColorMap,-1,0,0,0,PENF_EXCLUSIVE | PENF_NO_SETCOLOR))!=~0)
		    {
		      display.dynamic=TRUE;
		    }
		  else
		    {
		      ReleasePen(ColorMap,display.DynamicColor1);
		    }
		}
	    }
	}
    }
}

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

void Sleep(void)

{
  MiscState.Sleep=TRUE;
  GS_WindowSleep(display.window);
  GS_WindowSleep(ConfigKeysWindow);
  GS_WindowSleep(ChangeUserWindow);
  GS_WindowSleep(ErrorWindow);
}

/************************************************************************/

void Wakeup(void)

{
  MiscState.Sleep=FALSE;
  GS_WindowWakeup(ChangeUserWindow);
  GS_WindowWakeup(ConfigKeysWindow);
  GS_WindowWakeup(display.window);
  GS_WindowWakeup(ErrorWindow);
}

/************************************************************************/
/*									*/
/* Load the sprites							*/
/*									*/
/************************************************************************/

static int LoadSprites(void)

{
  struct MenuItem *Item;
  const char *Name;
  ULONG Gender;

  Sleep();
  Item=(struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_MSIT_MENU);
  Gender=!!(Item->Flags & CHECKED);
  Name=Gender ? "msit" : "mris";
  if ((Sprites=GS_LoadSprites(Name,display.Screen->Screen))==NULL)
    {
      char String[ERRORSTRING_LENGTH];

      DisplayError(MSG_SPRITES_ERROR,Name,ErrorString(String,IoErr()));
      Gender=!Gender;
      Name=Gender ? "msit" : "mris";
      if ((Sprites=GS_LoadSprites(Name,display.Screen->Screen))==NULL)
	{
	  DisplayError(MSG_SPRITES_ERROR,Name,ErrorString(String,IoErr()));
	}
    }
  if (Sprites!=NULL)
    {
      WORD x;
      struct RastPort RastPort;

      data.gender=Gender;
      ClearMenuStrip(display.window);
      if (Gender)
	{
	  Item->Flags|=CHECKED;
	  memcpy(ball_hold,she_hold,sizeof(ball_hold));
	  lettering[0] = letter_msit;
	}
      else
	{
	  Item->Flags&=~CHECKED;
	  memcpy(ball_hold,he_hold,sizeof(ball_hold));
	  lettering[0] = letter_mris;
	}
      ResetMenuStrip(display.window,display.Menustrip);

      sprites=(SPRITE *)Sprites->Sprites;
      InitRastPort(&RastPort);
      RastPort.BitMap=sprites[SPRITE_SPECIAL].image;
      for (x=0; x<=7; x++)
	{
	  backgrounds[x>>1][x&1]=ReadPixel(&RastPort,x,0);
	}
      display.black=ReadPixel(&RastPort,8,0);
      display.white=ReadPixel(&RastPort,9,0);
      display.border=ReadPixel(&RastPort,10,0);

      data.mono=(Sprites->Colors.ColorCount==2);
      if (((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_DYNAMIC_MENU))->Flags&CHECKED)
	{
	  ObtainDynamic();
	}

      /* create display.xorBitmap */
      {
	ULONG xor;
	int Plane;

	xor=display.black^display.white;
	assert((xor&~0xff)==0);
	for (Plane=0; Plane<ARRAYSIZE(display.xorBitmap.Planes); Plane++)
	  {
	    display.xorBitmap.Planes[Plane]=(xor&(1<<Plane)) ? (PLANEPTR)~0 : (PLANEPTR)0;
	  }
	display.xorBitmap.Depth=ARRAYSIZE(display.xorBitmap.Planes);
	display.xorBitmap.BytesPerRow=((WINDOW_WIDTH+15)/16)*2;
	display.xorBitmap.Rows=WINDOW_HEIGHT;
      }

      /*{{{  set foreground & background types*/
      {
	ULONG Set;
	ULONG Clear;
	ULONG i;
    
	Set = 0;
	Clear = (1<<display.DrawInfo->dri_Depth)-1;
	/*{{{  set on and off*/
	for(i=0; i<Sprites->Colors.ColorCount; i++)
	  {
	    Set|=Sprites->Colors.Colors[i].Pen;
	    Clear&=Sprites->Colors.Colors[i].Pen;
	  }
	/*}}}*/
	display.background = display.white == Clear ? COLOUR_ZERO :
	  display.white == Set ? COLOUR_ONE : COLOUR_WEIRD;
	display.foreground = display.black == Clear ? COLOUR_ZERO :
	  display.black == Set ? COLOUR_ONE : COLOUR_WEIRD;
      }

      /*{{{  create fills*/
      if (data.mono)
	{
	  int i;

	  SetDrMd(&RastPort,JAM1);
	  SetAfPt(&RastPort,DitherPattern,1);
	  for (i=0; i<FILLS; i++)
	    {
	      SPRITE *sptr;

	      sptr=&sprites[SPRITE_FILLS+i];
	      RastPort.BitMap=sptr->image;
	      RectFill(&RastPort,0,0,sptr->size.x-1,sptr->size.y-1);
	    }
	}

      /*{{{  digits special*/
      {
	SPRITE    *dptr;

	dptr = &sprites[SPRITE_DIGITS];
	XFillRectangle(display.display, display.back, GCN(GC_CLEAR),
		       0, 0, dptr->size.x, dptr->size.y);
	XCopyArea(display.display, dptr->mask, display.back, GCN(GC_MASK),
		  0, 0, dptr->size.x, dptr->size.y, 0, 0);
	XCopyArea(display.display, display.back, dptr->image, GCN(GC_OR),
		  0, 0, dptr->size.x, dptr->size.y, 0, 0);
      }
    }
  Wakeup();
  BeginRefresh(display.window);
  EndRefresh(display.window,TRUE);
  return Sprites!=NULL;
}

/************************************************************************/
/*									*/
/* Allocate a fullsize offscreen bitmap					*/
/*									*/
/************************************************************************/

static int CreateOffscreen(Pixmap *Data)

{
  if ((Data->LayerInfo=NewLayerInfo())!=NULL)
    {
      Data->BitMap=AllocBitMap(WINDOW_WIDTH,WINDOW_HEIGHT,display.DrawInfo->dri_Depth,
			       BMF_CLEAR | BMF_INTERLEAVED,display.Screen->Screen->RastPort.BitMap);
      if (Data->BitMap!=NULL)
	{
	  Data->Layer=CreateBehindLayer(Data->LayerInfo,Data->BitMap,0,0,WINDOW_WIDTH-1,WINDOW_HEIGHT-1,LAYERSIMPLE,NULL);
	  if (Data->Layer!=NULL)
	    {
	      SetFont(Data->Layer->rp,data.font);
	      return TRUE;
	    }
	}
    }
  return FALSE;
}

/************************************************************************/
/*									*/
/* Allocate our various offscreen bitmaps				*/
/*									*/
/************************************************************************/

static INLINE int AllocateBitmaps(void)

{
  if (CreateOffscreen(&display.copy))
    {
      if (CreateOffscreen(&display.back))
	{
	  int  i;

	  for (i=0; i<BOARD_SCORES; i++)
	    {
	      update.score.list[i].image.BitMap=AllocBitMap(DIGIT_WIDTH*4,DIGIT_HEIGHT,display.DrawInfo->dri_Depth,
							    BMF_INTERLEAVED,display.Screen->Screen->RastPort.BitMap);
	      if (update.score.list[i].image.BitMap==NULL)
		{
		  break;
		}
	      update.score.list[i].mask.BitMap=AllocBitMap(DIGIT_WIDTH*4,DIGIT_HEIGHT,display.DrawInfo->dri_Depth,
							   BMF_INTERLEAVED,display.Screen->Screen->RastPort.BitMap);
	      if (update.score.list[i].mask.BitMap==NULL)
		{
		  break;
		}
	    }
	  if (i==BOARD_SCORES)
	    {
	      return TRUE;
	    }
	}
    }
  FreeBitmaps();
  return FALSE;
}

/************************************************************************/
/*									*/
/* Process an JoystickEvent						*/
/*									*/
/************************************************************************/

static INLINE void HandleJoystick(void)

{
  switch (GameSupportBase->Joystick.Event.ie_Code)
    {
    case IECODE_LBUTTON:
      KeyDown(KEY_THROW);
      break;

    case IECODE_RBUTTON:
      KeyDown(KEY_PAUSE);
      break;

    case IECODE_LBUTTON | IECODE_UP_PREFIX:
      KeyUp(KEY_THROW);
      break;

    case IECODE_RBUTTON | IECODE_UP_PREFIX:
      KeyUp(KEY_PAUSE);
      break;

    case IECODE_NOBUTTON:
      switch (GameSupportBase->Joystick.Event.ie_X)
	{
	case 1:
	  KeyUp(KEY_LEFT);
	  KeyDown(KEY_RIGHT);
	  break;

	case -1:
	  KeyUp(KEY_RIGHT);
	  KeyDown(KEY_LEFT);
	  break;

	case 0:
	  KeyUp(KEY_RIGHT);
	  KeyUp(KEY_LEFT);
	  break;
	}

      switch (GameSupportBase->Joystick.Event.ie_Y)
	{
	case 1:
	  KeyUp(KEY_UP);
	  KeyDown(KEY_DOWN);
	  break;

	case -1:
	  KeyUp(KEY_DOWN);
	  KeyDown(KEY_UP);
	  break;

	case 0:
	  KeyUp(KEY_UP);
	  KeyUp(KEY_DOWN);
	  break;
	}
      break;
    }
  GS_SendJoystick();
}

/************************************************************************/
/*									*/
/* Create a message window						*/
/*									*/
/************************************************************************/

static struct Window *CreateMessage(const char *Title, const char *Template, ...)

{
  struct EasyStruct EasyStruct;
  struct Window *Window;

  EasyStruct.es_StructSize=5*4;
  EasyStruct.es_Flags=0;
  EasyStruct.es_Title=(char *)Title;
  EasyStruct.es_TextFormat=(char *)Template;
  EasyStruct.es_GadgetFormat=GetString(MSG_MESSAGE_WINDOW_CLOSE);
  Window=BuildEasyRequestArgs(display.window,&EasyStruct,0,&Template+1);
  if (Window==(struct Window *)1)
    {
      Window=NULL;
    }
  return Window;
}

/************************************************************************/
/*									*/
/* Process an IDCMP_RAWKEY event					*/
/*									*/
/************************************************************************/

static INLINE void HandleKey(const struct IntuiMessage *Message)

{
  if (!(Message->Qualifier & IEQUALIFIER_REPEAT))
    {
      struct InputEvent InputEvent;
      int Key;

      InputEvent.ie_NextEvent=NULL;
      InputEvent.ie_Class=IECLASS_RAWKEY;
      InputEvent.ie_SubClass=0;
      InputEvent.ie_Code=Message->Code&~IECODE_UP_PREFIX;
      InputEvent.ie_Qualifier=Message->Qualifier;
      InputEvent.ie_position.ie_addr=*(APTR *)Message->IAddress;
      InputEvent.ie_TimeStamp.tv_secs=Message->Seconds;
      InputEvent.ie_TimeStamp.tv_micro=Message->Micros;
      for (Key=0; Key<ARRAYSIZE(data.keysyms); Key++)
	{
	  if (MatchIX(&InputEvent,&data.keysyms[Key].InputXpression))
	    {
	      if (Key<KEYS)
		{
		  ((Message->Code&IECODE_UP_PREFIX) ? KeyUp : KeyDown)(Key);
		}
	      else if (Key==KEYS)
		{
		  if (!(Message->Code&IECODE_UP_PREFIX))
		    {
		      OpenManual();
		    }
		}
	      else
		{
		  assert(Key==KEYS+1);
		  if (!(Message->Code&IECODE_UP_PREFIX))
		    {
		      if (CompileInfo==NULL)
			{
			  CompileInfo=
			    CreateMessage(PROGRAM_NAME " " PROGRAM_VERSION " (" PROGRAM_DATE ")",
					  "Compiler: " COMPILER "\n"
					  "Date: %s, %s\n"
					  "CPU: " CPU "\n"
					  "AmigaOS: V%ld (Release " ROM_RELEASE ")",
					  CompileDate, CompileTime, ROM_VERSION);
			}
		    }
		}
	      break;
	    }
	}
    }
}

/************************************************************************/
/*									*/
/* Open the game font							*/
/*									*/
/************************************************************************/

static INLINE int ObtainFont(void)

{
  if ((DiskfontBase=OpenLibrary((char *)DiskfontName,0))==NULL)
    {
      DisplayError(MSG_NO_LIBRARY,DiskfontName);
    }
  if (TextAttr.ta_Name!=NULL)
    {
      if ((data.font=(DiskfontBase!=NULL ? OpenDiskFont : OpenFont)(&TextAttr))==NULL)
	{
	  DisplayError(MSG_FONT_ERROR,TextAttr.ta_Name,TextAttr.ta_YSize);
	}
    }
  if (data.font==NULL)
    {
      static struct TextAttr TimesAttr={"Times.font", 18, 0, 0};

      data.font=(DiskfontBase!=NULL ? OpenDiskFont : OpenFont)(&TimesAttr);
      if (data.font==NULL)
	{
	  DisplayError(MSG_FONT_ERROR,TimesAttr.ta_Name,TimesAttr.ta_YSize);
	  data.font=OpenFont(display.Screen->Screen->Font);
	  if (data.font==NULL)
	    {
	      static struct TextAttr TopazAttr={"topaz.font", 9, 0, 0};

	      data.font=OpenFont(&TopazAttr);
	    }
	}
    }
  CloseLibrary(DiskfontBase);
  return data.font!=NULL;
}

/************************************************************************/
/*									*/
/* Make an iconify gadget for our window				*/
/*									*/
/************************************************************************/

static INLINE void CreateIconifyGadget(void)

{
  if (CreateIconifyImageClass()!=NULL)
    {
      if ((IconifyGadget.GadgetRender=NewObjectA(IconifyImageClass,NULL,NULL))!=NULL)
	{
	  Object *DepthImage;

	  IconifyGadget.SelectRender=IconifyGadget.GadgetRender;
	  DepthImage=NewObject(NULL, SYSICLASS,
			       SYSIA_DrawInfo, display.DrawInfo,
			       SYSIA_Which, DEPTHIMAGE,
			       SYSIA_Size, ((display.Screen->Screen->Flags & SCREENHIRES) ?
					    ((display.Screen->Screen->ViewPort.Modes & LACE) ?
					     SYSISIZE_HIRES : SYSISIZE_MEDRES) :
					    SYSISIZE_LOWRES),
			       TAG_DONE);
	  if (DepthImage!=NULL)
	    {
	      {
		ULONG Width;

		GetAttr(IA_Width,DepthImage,&Width);
		IconifyGadget.Width=(WORD)Width;
	      }
	      DisposeObject(DepthImage);
	      IconifyGadget.LeftEdge=display.window->BorderRight-2*IconifyGadget.Width;
	      IconifyGadget.Height=display.window->BorderTop;
	      ((struct Image *)IconifyGadget.GadgetRender)->Width=IconifyGadget.Width;
	      ((struct Image *)IconifyGadget.GadgetRender)->Height=IconifyGadget.Height;
	      AddGadget(display.window,&IconifyGadget,0);
	      RefreshGadgets(display.window->FirstGadget,display.window,NULL);
	    }
	}
    }
}

/************************************************************************/
/*									*/
/* Allocate all GUI related resources (except for the menu)		*/
/*									*/
/************************************************************************/

static void OpenGUI(void)

{
  if (display.Screen!=NULL || (display.Screen=GetScreen(ScreenDescription,NewBroker.nb_Name))!=NULL)
    {
      display.DrawInfo=GetScreenDrawInfo(display.Screen->Screen);
      if ((display.VisualInfo=GetVisualInfo(display.Screen->Screen,NULL))!=NULL)
	{
	  static struct TagItem LayoutTags[]=
	    {
	      {GTMN_NewLookMenus, TRUE},
	      {TAG_DONE}
	    };

	  LayoutMenusA(display.Menustrip,display.VisualInfo,LayoutTags);
	  if (ObtainFont())
	    {
	      WORD X, Y, Width, Height;
	      ULONG IntuiLock;

	      IntuiLock=LockIBase(0);
	      X=display.Screen->Screen->MouseX;
	      Y=display.Screen->Screen->MouseY;
	      UnlockIBase(IntuiLock);
	      display.OriginX=display.Screen->Screen->WBorLeft;
	      display.OriginY=display.Screen->Screen->WBorTop+display.Screen->Screen->Font->ta_YSize+1;
	      Width=WINDOW_WIDTH+display.OriginX+display.Screen->Screen->WBorRight;
	      Height=WINDOW_HEIGHT+display.OriginY+display.Screen->Screen->WBorBottom;
	      if (Width<=display.Screen->Screen->Width && Height<=display.Screen->Screen->Height)
		{
		  static struct TagItem WindowTags[]=
		    {
		      {WA_Flags,  WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH | WFLG_ACTIVATE | WFLG_NEWLOOKMENUS},
		      {WA_RptQueue, 0},
		      {WA_PointerDelay, TRUE},
		      {TAG_DONE}
		    };

		  X-=Width/2;
		  Y-=Height/2;
		  display.window=OpenWindowTags(NULL,
						WA_Left, X,
						WA_Top, Y,
						WA_Width, Width,
						WA_Height, Height,
						WA_ScreenTitle, GetString(MSG_SCREEN_TITLE),
						WA_PubScreen, display.Screen->Screen,
						WA_Title, WindowTitle,
						TAG_MORE, WindowTags);
		  if (display.window!=NULL)
		    {
		      display.window->UserData=((struct Process *)WindowPort.mp_SigTask)->pr_WindowPtr;
		      ((struct Process *)WindowPort.mp_SigTask)->pr_WindowPtr=display.window;
		      CreateIconifyGadget();
		      if (AllocateBitmaps())
			{
			  if (LoadSprites())
			    {
			      SetMenuStrip(display.window,display.Menustrip);
			      display.window->UserPort=&WindowPort;
			      ModifyIDCMP(display.window,
					  IDCMP_MENUPICK |
					  IDCMP_GADGETUP |
					  IDCMP_CLOSEWINDOW |
					  IDCMP_REFRESHWINDOW |
					  IDCMP_RAWKEY |
					  IDCMP_ACTIVEWINDOW |
					  IDCMP_INACTIVEWINDOW);
			      if (display.window->Flags & WFLG_WINDOWACTIVE)
				{
				  AllocateJoystickCounter=JOYSTICK_TRIES;
				}
			      if (scoring.UserName[0]=='\0')
				{
				  OpenChangeUserWindow();
				}
			      return;
			    }
			}
		      else
			{
			  DisplayError(0,ERROR_NO_FREE_STORE);
			}
		    }
		  else
		    {
		      DisplayError(MSG_WINDOW_ERROR);
		    }
		}
	      else
		{
		  DisplayError(MSG_SCREENSIZE_ERROR,ScreenDescription,Width,Height);
		}
	    }
	}
      else
	{
	  DisplayError(0,ERROR_NO_FREE_STORE);
	}
    }
  else
    {
      LONG Error;

      Error=IoErr();
      if (Error==OSERR_PUBNOTUNIQUE)
	{
	  char *String;

	  String=GS_FormatString(GetString(MSG_OSERR_PUBNOTUNIQUE),&NewBroker.nb_Name,NULL,NULL);
	  DisplayError(MSG_SCREEN_ERROR,ScreenDescription,String);
	  GS_MemoryFree(String);
	}
      else
	{
	  char String[ERRORSTRING_LENGTH];

	  DisplayError(MSG_SCREEN_ERROR,ScreenDescription,ErrorString(String,Error));
	}
    }
  CloseGUI();
}

/************************************************************************/
/*									*/
/* Handle config/control/joystick menu change				*/
/*									*/
/************************************************************************/

static INLINE void ChangeJoystick(const struct MenuItem *Item)

{
  if (Item->Flags & CHECKED)
    {
      if (!MiscState.Joystick)
	{
	  AllocateJoystickCounter=JOYSTICK_TRIES;
	}
    }
  else
    {
      FreeJoystick();
    }
}

/************************************************************************/
/*									*/
/* Handle config/display/msit menu change				*/
/*									*/
/************************************************************************/

static INLINE int ChangeGender(const struct MenuItem *Item)

{
  if (data.gender!=!!(Item->Flags & CHECKED))
    {
      FreeSprites();
      if (LoadSprites())
	{
	  return TRUE;
	}
      CloseGUI();
    }
  return FALSE;
}

/************************************************************************/
/*									*/
/* Handle config/display/dynamic menu change				*/
/*									*/
/************************************************************************/

static INLINE int ChangeDynamic(const struct MenuItem *Item)

{
  if (Item->Flags & CHECKED)
    {
      if (!display.dynamic)
	{
	  ObtainDynamic();
	  return (int)display.dynamic;
	}
    }
  else if (display.dynamic)
    {
      ReleaseDynamic();
      return TRUE;
    }
  return FALSE;
}

/************************************************************************/
/*									*/
/* Set the window title							*/
/*									*/
/************************************************************************/

void UpdateWindowTitle(void)

{
  struct
    {
      const char *User;
      const char *Garden;
    } Params;

  GS_MemoryFree(WindowTitle);
  if (scoring.UserName[0]!='\0')
    {
      Params.User=scoring.UserName;
    }
  else
    {
      Params.User=GetString(MSG_USER_UNKNOWN);
    }
  if (board.Name==NULL)
    {
      WindowTitle=GS_FormatString("%s",&Params.User,NULL,Locale);
    }
  else
    {
      Params.Garden=board.Name;
      WindowTitle=GS_FormatString("%s: %s",&Params,NULL,Locale);
    }
  if (display.window!=NULL)
    {
      SetWindowTitles(display.window,WindowTitle,(UBYTE *)~0);
    }
  scoring.mine.score=0;
}

/************************************************************************/
/*									*/
/*									*/
/************************************************************************/

static void HandleMessageWindow(struct Window **WindowPtr)

{
  struct Window *Window;

  Window=*WindowPtr;
  if (Window!=NULL)
    {
      if (SysReqHandler(Window,NULL,FALSE)!=-2)
	{
	  FreeSysRequest(Window);
	  *WindowPtr=NULL;
	}
    }
}

/************************************************************************/

static void EnableMenu(UWORD ID, int Item)

{
  struct Menu *Menu;

  Menu=(struct Menu *)FindMenu(ID);
  if (display.window!=NULL)
    {
      OnMenu(display.window,((ULONG)(Item ? GTMENUITEM_USERDATA(Menu) : GTMENU_USERDATA(Menu)))>>16);
    }
  else
    {
      Menu->Flags|=Item ? ITEMENABLED : MENUENABLED;
    }
}

/************************************************************************/
/*									*/
/* Load selected boards.						*/
/* Returns TRUE if boards have changed.					*/
/*									*/
/************************************************************************/

static int LoadBoards(void)

{
  static struct MenuItem *BoardItem;

  struct MenuItem *Item;
  struct StringNode *Name;

  for (Item=((struct Menu *)FindMenu(MSG_GARDENS_MENU))->FirstItem, Name=GardenStrings;
       Item!=NULL && !(Item->Flags&CHECKED);
       Item=Item->NextItem, Name=Name->Next)
    {
      assert(Name!=NULL);
    }
  if (Item!=NULL && BoardItem!=Item)
    {
      LONG *File;
      BPTR OldCurrentDir;

      OldCurrentDir=CurrentDir(GardenDir);
      if ((File=ReadFile(((char *)(Name+1))+1))!=NULL)
	{
	  LONG Size;

	  Size=(*File)-12;
	  if (Size>0 && ((Size%sizeof(BOARD))==0) && strncmp((char *)(File+1),"mris Garden!",12)==0)
	    {
	      LONG i;

	      for (i=0; i<Size/sizeof(BOARD); i++)
		{
		  if (check_board(((BOARD *)(((char *)(File+1))+12))+i))
		    {
		      DisplayError(MSG_LOADGARDEN_ERROR,((char *)(Name+1))+1,GetString(MSG_INVALIDGARDEN_ERROR));
		      Size=0;
		    }
		}
	      if (Size>0)
		{
		  if (board.list!=NULL)
		    {
		      FreeFile((LONG *)(((char *)board.list)-12)-1);
		    }
		  board.Name=((char *)(Name+1))+1;
		  board.boards=Size/sizeof(BOARD);
		  board.list=(BOARD *)(((char *)(File+1))+12);
		  BoardItem=Item;
		  CurrentDir(OldCurrentDir);
		  GS_ReleaseScoreHandle(scoring.ScoreHandle);
		  scoring.ScoreHandle=NULL;
		  UpdateWindowTitle();
		  EnableMenu(MSG_DEMO_BOARD_MENU,TRUE);
		  EnableMenu(MSG_DEMO_SCORES_MENU,TRUE);
		  return TRUE;
		}
	    }
	  else
	    {
	      DisplayError(MSG_LOADGARDEN_ERROR,((char *)(Name+1))+1,GetString(MSG_NOGARDEN_ERROR));
	    }
	  FreeFile(File);
	}
      else
	{
	  char String[ERRORSTRING_LENGTH];

	  DisplayError(MSG_LOADGARDEN_ERROR,((char *)(Name+1))+1,ErrorString(String,IoErr()));
	}
      CurrentDir(OldCurrentDir);
      if (display.window!=NULL)
	{
	  ClearMenuStrip(display.window);
	  BoardItem->Flags|=CHECKED;
	  Item->Flags&=~CHECKED;
	  ResetMenuStrip(display.window,display.Menustrip);
	}
    }
  return FALSE;
}

/************************************************************************/

static struct DiskObject *GetPutDiskObject(struct DiskObject *DiskObject, char *Name)

{
  if (DiskObject==NULL)
    {
      if ((DiskObject=GetDiskObject(Name))==NULL)
	{
	  LONG Error;

	  Error=IoErr();
	  if (Error==ERROR_OBJECT_NOT_FOUND)
	    {
	      DiskObject=&mrisIcon;
	    }
	  else
	    {
	      char String[ERRORSTRING_LENGTH];

	      DisplayError(MSG_GETICON_ERROR,Name,ErrorString(String,Error));
	    }
	}
    }
  else
    {
      if (!PutDiskObject(Name,DiskObject))
	{
	  char String[ERRORSTRING_LENGTH];

	  DisplayError(MSG_PUTICON_ERROR,Name,ErrorString(String,IoErr()));
	  DiskObject=NULL;
	}
    }
  return DiskObject;
}

/************************************************************************/
/*									*/
/* Get or Put the default icon						*/
/* If DiskObject==NULL, then get the default icon.			*/
/* If DiskObject!=NULL, then write it as default icon.			*/
/*									*/
/************************************************************************/

static struct DiskObject *DoDefaultIcon(struct DiskObject *DiskObject)

{
  BPTR OldCurrentDir;

  if (WorkbenchMessage!=NULL)
    {
      assert(WorkbenchMessage->sm_NumArgs>0 && WorkbenchMessage->sm_ArgList[0].wa_Name!=NULL);
      OldCurrentDir=CurrentDir(WorkbenchMessage->sm_ArgList[0].wa_Lock);
      DiskObject=GetPutDiskObject(DiskObject,WorkbenchMessage->sm_ArgList[0].wa_Name);
    }
  else
    {
      char ProgramName[256];

      GetProgramName(ProgramName,sizeof(ProgramName));
      OldCurrentDir=CurrentDir(GetProgramDir());
      DiskObject=GetPutDiskObject(DiskObject,FilePart(ProgramName));
    }
  CurrentDir(OldCurrentDir);
  return DiskObject;
}

/************************************************************************/

static INLINE void ReconstructGame(void)

{
  if (display.window!=NULL)
    {
      KeyDown(KEY_PAUSE);
      KeyUp(KEY_PAUSE);
      if (MiscState.GameMode)
	{
	  RedrawGame();
	}
    }
}

/************************************************************************/
/*									*/
/* Compare tooltype							*/
/*									*/
/************************************************************************/

static int CompareTooltype(const char *ToolType, const char *Name)

{
  int Parenthesis;

  while (*ToolType==' ')
    {
      ToolType++;
    }
  if ((Parenthesis=(*ToolType=='(')))
    {
      while (*(++ToolType)==' ')
	;
    }
  while (*Name!='\0')
    {
      if (toupper(*ToolType)!=toupper(*Name))
	{
	  return FALSE;
	}
      Name++;
      ToolType++;
    }
  return (*ToolType=='\0' || *ToolType==' ' || *ToolType=='=' || (Parenthesis && *ToolType==')'));
}

/************************************************************************/
/*									*/
/* Free the new tooltype array						*/
/*									*/
/************************************************************************/

static void FreeTooltypes(char **ToolTypes)

{
  char **ToolType;

  for (ToolType=ToolTypes; *ToolType; ToolType++)
    {
      int i;

      for (i=0; i<ARRAYSIZE(MrisToolTypes); i++)
	{
	  if (CompareTooltype(*ToolType,MrisToolTypes[i]))
	    {
	      GS_MemoryFree(*ToolType);
	    }
	}
    }
  GS_MemoryFree(ToolTypes);
}

/************************************************************************/
/*									*/
/* Create new tooltype array						*/
/*									*/
/************************************************************************/

static char **CreateTooltypes(char **ToolTypes)

{
  ULONG Count;
  char **ToolType;
  char **NewToolTypes;

  Count=0;
  if (ToolTypes!=NULL)
    {
      for (ToolType=ToolTypes; *ToolType!=NULL; ToolType++)
	{
	  int i;

	  Count++;
	  for (i=0; i<ARRAYSIZE(MrisToolTypes); i++)
	    {
	      if (CompareTooltype(*ToolType,MrisToolTypes[i]))
		{
		  Count--;
		  break;
		}
	    }
	}
    }
  if ((NewToolTypes=GS_MemoryAlloc(sizeof(char *)*(Count+ARRAYSIZE(MrisToolTypes)+1)))!=NULL)
    {
      char **NewToolType;
      int Error;
      int i;

      NewToolType=NewToolTypes;
      Error=FALSE;

      for (i=0; !Error && i<ARRAYSIZE(MrisToolTypes); i++)
	{
	  char *Params[2];

	  Params[0]=MrisToolTypes[i];
	  switch (i)
	    {
	    case 8:
	      Params[1]=(((struct MenuItem *)FindMenu(MSG_CONFIG_RANDOM_MENU))->Flags & CHECKED) ? "YES" : "NO";
	      *NewToolType=GS_FormatString("%s=%s",Params,NULL,NULL);
	      break;

	    case 9:
	      Params[1]=(((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_DYNAMIC_MENU))->Flags & CHECKED) ? "YES" : "NO";
	      *NewToolType=GS_FormatString("%s=%s",Params,NULL,NULL);
	      break;

	    case 10:
	      if (scoring.UserName[0]=='\0')
		{
		  Params[1]="<user name>";
		  *NewToolType=GS_FormatString("(%s=%s)",Params,NULL,NULL);
		}
	      else
		{
		  Params[1]=scoring.UserName;
		  *NewToolType=GS_FormatString("%s=%s",Params,NULL,NULL);
		}
	      break;

	    case 11:
	      Params[1]="<public screen name>";
	      *NewToolType=GS_FormatString("(%s=%s)",Params,NULL,NULL);
	      break;

	    case 12:
	      if (ScreenDescription==NULL)
		{
		  Params[1]="";
		}
	      else
		{
		  Params[1]=ScreenDescription;
		}
	      *NewToolType=GS_FormatString("%s=%s",Params,NULL,NULL);
	      break;

	    case 13:
	      Params[1]=(((struct MenuItem *)FindMenu(MSG_CONFIG_CONTROL_JOYSTICK_MENU))->Flags & CHECKED) ? "YES" : "NO";
	      *NewToolType=GS_FormatString("%s=%s",Params,NULL,NULL);
	      break;

	    case 14:
	      if (TextAttr.ta_Name!=NULL)
		{
		  *NewToolType=GS_FormatString("FONT=%s SIZE %u",&TextAttr,NULL,NULL);
		}
	      else
		{
		  Params[1]="<font description>";
		  *NewToolType=GS_FormatString("(%s=%s)",Params,NULL,NULL);
		}
	      break;

	    case 15:
	      Params[1]=(((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_MSIT_MENU))->Flags & CHECKED) ? "YES" : "NO";
	      *NewToolType=GS_FormatString("%s=%s",Params,NULL,NULL);
	      break;

	    default:
	      assert(i<KEYS);
	      if (data.keysyms[i].Description!=GetString(MSG_KEY_UP+i))
		{
		  Params[1]=data.keysyms[i].Description;
		  *NewToolType=GS_FormatString("%s=%s",Params,NULL,NULL);
		}
	      else
		{
		  Params[1]="<key description>";
		  *NewToolType=GS_FormatString("(%s=%s)",Params,NULL,NULL);
		}
	      break;
	    }
	  if (*NewToolType==NULL)
	    {
	      Error=TRUE;
	    }
	  else
	    {
	      NewToolType++;
	    }
	}
      if (!Error)
	{
	  if (ToolTypes!=NULL)
	    {
	      for (ToolType=ToolTypes; *ToolType!=NULL; ToolType++)
		{
		  for (i=0; i<ARRAYSIZE(MrisToolTypes) && !CompareTooltype(*ToolType,MrisToolTypes[i]); i++)
		    ;
		  if (i==ARRAYSIZE(MrisToolTypes))
		    {
		      *(NewToolType++)=*ToolType;
		    }
		}
	    }
	  *NewToolType=NULL;
	}
      else
	{
	  FreeTooltypes(NewToolTypes);
	  NewToolTypes=NULL;
	}
    }
  return NewToolTypes;
}

/************************************************************************/
/*									*/
/* Save current config as default					*/
/*									*/
/************************************************************************/

static INLINE void SaveDefaultConfig(void)

{
  struct DiskObject *DiskObject;

  if ((DiskObject=DoDefaultIcon(NULL))!=NULL)
    {
      char **OldToolTypes;

      OldToolTypes=DiskObject->do_ToolTypes;
      if ((DiskObject->do_ToolTypes=CreateTooltypes(OldToolTypes))!=NULL)
	{
	  UBYTE OldType;
	  char *OldDefaultTool;
	  struct DrawerData *OldDrawerData;
	  char *OldToolWindow;

	  OldType=DiskObject->do_Type;
	  OldDefaultTool=DiskObject->do_DefaultTool;
	  OldDrawerData=DiskObject->do_DrawerData;
	  OldToolWindow=DiskObject->do_ToolWindow;

	  DiskObject->do_Type=WBTOOL;
	  DiskObject->do_DefaultTool=NULL;
	  DiskObject->do_DrawerData=NULL;
	  DiskObject->do_ToolWindow=NULL;
	  DiskObject->do_StackSize=StackSize+2000;

	  DoDefaultIcon(DiskObject);

	  DiskObject->do_Type=OldType;
	  DiskObject->do_DefaultTool=OldDefaultTool;
	  DiskObject->do_DrawerData=OldDrawerData;
	  DiskObject->do_ToolWindow=OldToolWindow;
	  FreeTooltypes(DiskObject->do_ToolTypes);
	}
      DiskObject->do_ToolTypes=OldToolTypes;
      if (DiskObject!=&mrisIcon)
	{
	  FreeDiskObject(DiskObject);
	}
    }
}

/************************************************************************/
/*									*/
/* Put up the filerequester to save the configuration			*/
/*									*/
/************************************************************************/

static INLINE void OpenSaveConfig(void)

{
  if (ConfigMessage.Requester==NULL)
    {
      if ((AslBase=OpenLibrary((char *)AslName,0))!=NULL)
	{
	  static struct TagItem TagList[]=
	    {
	      {ASLFR_InitialPattern, (ULONG)"#?.info"},
	      {ASLFR_Flags1, FRF_DOSAVEMODE | FRF_DOPATTERNS},
	      {TAG_DONE}
	    };

	  ConfigMessage.Message.mn_ReplyPort=&MiscPort;
	  ConfigMessage.Requester=AllocAslRequestTags(ASL_FileRequest,
						      ASLFR_Screen, display.Screen->Screen,
						      ASLFR_Locale, Locale,
						      ASLFR_TitleText, GetString(MSG_SAVEREQUESTER_TITLE),
						      TAG_MORE, TagList);
	  if (ConfigMessage.Requester==NULL)
	    {
	      CloseLibrary(AslBase);
	    }
	}
      else
	{
	  DisplayError(MSG_NO_LIBRARY,AslName);
	}
    }
  if (ConfigMessage.Message.mn_Node.ln_Type==NT_UNKNOWN && ConfigMessage.Requester!=NULL)
    {
      if (WorkbenchMessage!=NULL && WorkbenchMessage->sm_NumArgs>0)
	{
	  BPTR OldCurrentDir;

	  OldCurrentDir=CurrentDir(WorkbenchMessage->sm_ArgList[0].wa_Lock);
	  AsyncAslRequest(&ConfigMessage);
	  CurrentDir(OldCurrentDir);
	}
      else
	{
      	  AsyncAslRequest(&ConfigMessage);
	}
    }
}

/************************************************************************/
/*									*/
/* Check whether an icon is really our own program icon.		*/
/* "Name" must not have an ".info" suffix				*/
/*									*/
/************************************************************************/

static INLINE int CheckProgramIcon(BPTR DirLock, const char *Name)

{
  if (WorkbenchMessage!=NULL)
    {
      if (SameLock(DirLock,WorkbenchMessage->sm_ArgList[0].wa_Lock)==LOCK_SAME)
	{
	  if (stricmp(Name,WorkbenchMessage->sm_ArgList[0].wa_Name)==0)
	    {
	      return TRUE;
	    }
	}
    }
  else
    {
      if (SameLock(DirLock,GetProgramDir())==LOCK_SAME)
	{
	  char ProgramName[256];

	  GetProgramName(ProgramName,sizeof(ProgramName));
	  if (stricmp(Name,FilePart(ProgramName))==0)
	    {
	      return TRUE;
	    }
	}
    }
  return FALSE;
}

/************************************************************************/
/*									*/
/* Do the save configuration						*/
/*									*/
/************************************************************************/

static INLINE void SaveConfig(void)

{
  if (ConfigMessage.Result)
    {
      BPTR DirLock;

      if ((DirLock=Lock(((struct FileRequester *)ConfigMessage.Requester)->fr_Drawer,SHARED_LOCK))!=MKBADDR(NULL))
	{
	  char *Name;
	  LONG NameLength;

	  Name=((struct FileRequester *)ConfigMessage.Requester)->fr_File;
	  NameLength=strlen(Name);
	  if (NameLength>5 && stricmp(&Name[NameLength-5],".info")==0)
	    {
	      Name[NameLength-5]='\0';
	      NameLength=-NameLength;
	    }
	  if (!CheckProgramIcon(DirLock,Name))
	    {
	      struct DiskObject *DiskObject;

	      DirLock=CurrentDir(DirLock);
	      if ((DiskObject=GetDiskObject(Name))==NULL)
		{
		  if (IoErr()==ERROR_OBJECT_NOT_FOUND)
		    {
		      DiskObject=DoDefaultIcon(NULL);
		    }
		}
	      if (DiskObject!=NULL)
		{
		  char **OldToolTypes;

		  OldToolTypes=DiskObject->do_ToolTypes;
		  if ((DiskObject->do_ToolTypes=CreateTooltypes(OldToolTypes))!=NULL)
		    {
		      char *MrisPath;

		      if ((MrisPath=GS_MemoryAlloc(1024))!=NULL)
			{
			  if (NameFromLock(GetProgramDir(),MrisPath,1024))
			    {
			      if (WorkbenchMessage!=NULL)
				{
				  assert(WorkbenchMessage->sm_NumArgs>0);
				  if (!AddPart(MrisPath,WorkbenchMessage->sm_ArgList[0].wa_Name,1024))
				    {
				      GS_MemoryFree(MrisPath);
				      MrisPath=NULL;
				    }
				}
			      else
				{
				  char ProgramName[256];

				  GetProgramName(ProgramName,sizeof(ProgramName));
				  if (!AddPart(MrisPath,FilePart(ProgramName),1024))
				    {
				      GS_MemoryFree(MrisPath);
				      MrisPath=NULL;
				    }
				}
			    }
			  else
			    {
			      GS_MemoryFree(MrisPath);
			      MrisPath=NULL;
			    }
			}
		      if (MrisPath!=NULL)
			{
			  UBYTE OldType;
			  char *OldDefaultTool;
			  struct DrawerData *OldDrawerData;
			  char *OldToolWindow;

			  OldType=DiskObject->do_Type;
			  OldDefaultTool=DiskObject->do_DefaultTool;
			  OldDrawerData=DiskObject->do_DrawerData;
			  OldToolWindow=DiskObject->do_ToolWindow;

			  DiskObject->do_Type=WBPROJECT;
			  DiskObject->do_DefaultTool=MrisPath;
			  DiskObject->do_DrawerData=NULL;
			  DiskObject->do_ToolWindow=NULL;
			  DiskObject->do_StackSize=StackSize+2000;

			  if (!PutDiskObject(Name,DiskObject))
			    {
			      char String[ERRORSTRING_LENGTH];

			      DisplayError(MSG_PUTICON_ERROR,Name,ErrorString(String,IoErr()));
			    }

			  DiskObject->do_Type=OldType;
			  DiskObject->do_DefaultTool=OldDefaultTool;
			  DiskObject->do_DrawerData=OldDrawerData;
			  DiskObject->do_ToolWindow=OldToolWindow;

			  GS_MemoryFree(MrisPath);
			}
		      FreeTooltypes(DiskObject->do_ToolTypes);
		    }
		  DiskObject->do_ToolTypes=OldToolTypes;
		  if (DiskObject!=&mrisIcon)
		    {
		      FreeDiskObject(DiskObject);
		    }
		}
	      DirLock=CurrentDir(DirLock);
	    }
	  else
	    {
	      SaveDefaultConfig();
	    }
	  if (NameLength<0)
	    {
	      Name[(-NameLength)-5]='.';
	    }
	  UnLock(DirLock);
	}
    }
  FreeAslRequest(ConfigMessage.Requester);
  ConfigMessage.Requester=NULL;
  CloseLibrary(AslBase);
  ConfigMessage.Message.mn_Node.ln_Type=NT_UNKNOWN;
}

/************************************************************************/
/*									*/
/* Process window messages						*/
/*									*/
/************************************************************************/

static int ProcessWindow(void)

{
  struct IntuiMessage *Message;

  if ((Message=(struct IntuiMessage *)GetMsg(&WindowPort))!=NULL)
    {
      if (Message->IDCMPWindow==display.window)
	{
	  switch (Message->Class)
	    {
	    case IDCMP_GADGETUP:
	      if ((struct Gadget *)Message->IAddress==&IconifyGadget)
		{
		  KeyDown(KEY_ICONIZE);
		}
	      break;

	    case IDCMP_CLOSEWINDOW:
	      global.quit=TRUE;
	      break;

	    case IDCMP_REFRESHWINDOW:
	      BeginRefresh(display.window);
	      refresh_window();
	      EndRefresh(display.window,TRUE);
	      break;

	    case IDCMP_RAWKEY:
	      HandleKey(Message);
	      break;

	    case IDCMP_ACTIVEWINDOW:
	      if (((struct MenuItem *)FindMenu(MSG_CONFIG_CONTROL_JOYSTICK_MENU))->Flags & CHECKED)
		{
		  AllocateJoystickCounter=JOYSTICK_TRIES;
		}
	      break;

	    case IDCMP_INACTIVEWINDOW:
	      InactiveWindow();
	      break;

	    case IDCMP_MENUPICK:
	      {
		UWORD Selection;
		int Modified;

		Modified=FALSE;
		Selection=Message->Code;
		while (Selection!=MENUNULL && !global.quit && Sprites!=NULL)
		  {
		    struct MenuItem *Item;
		    UWORD UserData;

		    Item=ItemAddress(display.Menustrip,Selection);
		    UserData=((ULONG)GTMENUITEM_USERDATA(Item))&0xffff;
		    switch (UserData)
		      {
		      case MSG_GAME_ABOUT_MENU:
			if (About==NULL)
			  {
			    About=CreateMessage(GetString(MSG_ABOUT_TITLE),
						GetString(MSG_ABOUT_BODY),
						PROGRAM_VERSION " (" PROGRAM_DATE ")",
						GetString(MSG_BROKER_TITLE),
						GetString(MSG_GAME_DESCRIPTION),
						GetString(MSG_COPYRIGHT1),
						GetString(MSG_COPYRIGHT2),
						GetString(MSG_COPYRIGHT3),
						data.gender ? PROGRAM_MSIT : PROGRAM_MRIS,
						display.dynamic ? GetString(MSG_DISPLAYINFO_DYNAMIC) : "",
						Sprites->Colors.DistinctColors,
						Sprites->Colors.ColorCount);
			  }
			break;

		      case MSG_CONFIG_DISPLAY_FONT_MENU:
			OpenConfigFont();
			break;

		      case MSG_CONFIG_DISPLAY_SCREEN_MENU:
			OpenConfigScreen();
			break;

		      case MSG_CONFIG_CONTROL_KEYS_MENU:
			OpenConfigKeysWindow();
			break;

		      case MSG_CONFIG_RANDOM_MENU:
			if (!MiscState.GameMode)
			  {
			    data.random=(Item->Flags & CHECKED) ? TRUE : FALSE;
			  }
			break;

		      case MSG_GAME_USER_MENU:
			OpenChangeUserWindow();
			break;

		      case MSG_GAME_ERRORS_MENU:
			OpenErrorWindow();
			break;

		      case MSG_GAME_QUIT_MENU:
			global.quit=TRUE;
			break;

		      case MSG_CONFIG_DISPLAY_DYNAMIC_MENU:
			Modified|=ChangeDynamic(Item);
			break;

		      case MSG_CONFIG_CONTROL_JOYSTICK_MENU:
			ChangeJoystick(Item);
			break;

		      case MSG_CONFIG_DISPLAY_MSIT_MENU:
			Modified|=ChangeGender(Item);
			break;

		      case MSG_GARDENS_MENU:
			Modified|=LoadBoards();
			break;

		      case MSG_CONFIG_SAVEDEFAULT_MENU:
			SaveDefaultConfig();
			break;

		      case MSG_CONFIG_SAVE_MENU:
			OpenSaveConfig();
			break;

		      default:
			if (UserData>MSG_DEMO_MENU && UserData<=MSG_DEMO_SPRITES_MENU)
			  {
			    MiscState.JumpDemo=UserData-MSG_DEMO_MENU;
			    Modified=TRUE;
			  }
			break;
		      }
		    Selection=Item->NextSelect;
		  }
		if (!global.quit && Modified)
		  {
		    ReconstructGame();
		  }
	      }
	      break;
	    }
	  ReplyMsg(&Message->ExecMessage);
	}
      else if (Message->IDCMPWindow==ConfigKeysWindow)
	{
	  ProcessConfigKeysWindow(Message);
	}
      else if (Message->IDCMPWindow==ChangeUserWindow)
	{
	  ProcessChangeUserWindow(Message);
	}
      else
	{
	  assert(Message->IDCMPWindow==ErrorWindow);
	  ProcessErrorWindow(Message);
	}
      return TRUE;
    }
  return FALSE;
}

/************************************************************************/
/*									*/
/* This is the main "game" loop while we're iconified			*/
/*									*/
/************************************************************************/

static INLINE void HandleIconified(void)

{
  struct DiskObject *DiskObject;
  struct AppIcon *AppIcon;

#ifdef DEBUG
  DiskObject=NULL;
#endif
  SetSignal(0,SIGBREAKF_CTRL_F);
  if ((WorkbenchBase=OpenLibrary("workbench.library",37))!=NULL)
    {
      if ((DiskObject=DoDefaultIcon(NULL))==NULL)
	{
	  DiskObject=&mrisIcon;
	}
      DiskObject->do_CurrentX=NO_ICON_POSITION;
      DiskObject->do_CurrentY=NO_ICON_POSITION;
      AppIcon=AddAppIconA(0,0,"mris",&WBPort,NULL,DiskObject,NULL);
    }
  while (!global.quit && display.window==NULL)
    {
      ULONG Signals;

      Signals=Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_F | (1<<MiscPort.mp_SigBit));
      if (Signals & SIGBREAKF_CTRL_C)
	{
	  global.quit=TRUE;
	}
      else if (Signals & SIGBREAKF_CTRL_F)
	{
	  OpenGUI();
	  ReconstructGame();
	}
      while (ProcessMisc())
	;
      while (ProcessWindow())
	;
    }
  if (WorkbenchBase!=NULL)
    {
      if (AppIcon!=NULL)
	{
	  struct Message *Message;

	  RemoveAppIcon(AppIcon);
	  while ((Message=GetMsg(&WBPort))!=NULL)
	    {
	      ReplyMsg(Message);
	    }
	}
      if (DiskObject!=&mrisIcon)	/* DiskObject _is_ initialized */
	{
	  FreeDiskObject(DiskObject);
	}
      CloseLibrary(WorkbenchBase);
    }
}

/************************************************************************/
/*									*/
/* Process a message from MiscPort					*/
/*									*/
/************************************************************************/

static int ProcessMisc(void)

{
  struct Message *Message;

  if ((Message=GetMsg(&MiscPort))!=NULL)
    {
      if (Message==&GameSupportBase->Joystick.Request.io_Message)
	{
	  HandleJoystick();
	}
      else if (Message==&FontMessage.Message)
	{
	  if (ProcessConfigFont())
	    {
	      ReconstructGame();
	    }
	}
      else if (Message==&ConfigMessage.Message)
	{
	  SaveConfig();
	}
      else if (Message==&ScreenMessage.Message)
	{
	  if (ProcessConfigScreen())
	    {
	      ErrorFlag=FALSE;
	      CloseGUI();
	      if (display.Screen!=NULL)
		{
		  FreeScreen(display.Screen,TRUE);
		  display.Screen=NULL;
		}
	      OpenGUI();
	      ReconstructGame();
	    }
	}
      else /* CxMessage */
	{
	  if (CxMsgType((CxMsg *)Message)==CXM_COMMAND)
	    {
	      switch (CxMsgID((CxMsg *)Message))
		{
		case CXCMD_APPEAR:
		  if (display.window==NULL)
		    {
		      SetSignal(SIGBREAKF_CTRL_F,SIGBREAKF_CTRL_F);
		    }
		  else
		    {
		      ShowWindow(display.window);
		    }
		  break;

		case CXCMD_DISAPPEAR:
		  if (display.window!=NULL)
		    {
		      KeyDown(KEY_ICONIZE);
		    }
		  break;

		case CXCMD_KILL:
		  SetSignal(SIGBREAKF_CTRL_C,SIGBREAKF_CTRL_C);
		  break;
		}
	    }
	  ReplyMsg(Message);
	}
      return TRUE;
    }
  return FALSE;
}

/************************************************************************/
/*									*/
/* This is the main game loop						*/
/*									*/
/************************************************************************/

static INLINE void Game(void)

{
  PROTOANIMATE((*animate));

  timer_open();
  animate = animate_demo;
  while(!global.quit)
    {
      if (display.window!=NULL)
	{
	  int DoMessages;

	  HandleMessageWindow(&CompileInfo);
	  HandleMessageWindow(&About);
	  color_cycle();
	  if (CheckSignal(SIGBREAKF_CTRL_C))
	    {
	      global.quit=TRUE;
	    }
	  DoMessages=TRUE;
	  while (!global.quit && display.window!=NULL && DoMessages)
	    {
	      DoMessages=ProcessMisc();

	      /* process IDCMP message */
	      if (ProcessWindow())
		{
		  DoMessages=TRUE;
		}

	      /* process Manual message */
	      if (ProcessManual())
		{
		  DoMessages=TRUE;
		}
	    }
	  if (display.window!=NULL && !global.quit)
	    {
	      if (AllocateJoystickCounter>0)
		{
		  if (GS_AllocateJoystick(&MiscPort,GPCT_ABSJOYSTICK))
		    {
		      AllocateJoystickCounter=0;
		      MiscState.Joystick=TRUE;
		      GS_SendJoystick();
		    }
		  else
		    {
		      AllocateJoystickCounter--;
		    }
		}
	      if (MiscState.GameMode && !MiscState.GamePause && !MiscState.HappyBlanker)
		{
		  if (GS_HappyBlanker())
		    {
		      MiscState.HappyBlanker=TRUE;
		    }
		}
	      if(global.pressed & (1 << KEY_THROW))
		{
		  if(global.throw == 0)
		    global.throw = 1;
		}
	      else if(global.throw == 2)
		global.throw = 0;
	      if(global.pressed & 1 << KEY_ICONIZE)
		{
		  KeyDown(KEY_PAUSE);
		  InactiveWindow();
		  CloseGUI();
		}
	      else
		{
		  assert(animate != (PROTOANIMATE((*)))NULL);
		  animate = (PROTOANIMATE((*)))(*animate)((PROTOVOID(*))NULL);
		  show_updates();
		  timer_wait();
		  assert(monster.normals < 20);
		}
	    }
	}
      else
	{
	  HandleIconified();
	}
    }
  if (MiscState.GameMode)
    {
      StopGame();
    }
  FreeJoystick();
  timer_close();
}

/************************************************************************/

struct Menu *CreateMenustrip(struct NewMenu *NewMenu)

{
  unsigned int i;

  for (i=0; NewMenu[i].nm_Type!=NM_END; i++)
    {
      if ((ULONG)NewMenu[i].nm_Label<1024)
	{
	  char *Item;

	  Item=GetString((LONG)NewMenu[i].nm_Label);
	  if (Item[0]!='\0')
	    {
	      NewMenu[i].nm_CommKey=Item;
	    }
	  NewMenu[i].nm_Label=Item+1;
	}
    }
  return CreateMenusA(NewMenu,NULL);
}

/************************************************************************/

static INLINE void MyCreateMenustrip(void)

{
  struct Menu *Menu;
  UWORD MenuNum;

  if ((display.Menustrip=CreateMenustrip(NewMenu))==NULL)
    {
      CloseAll(RETURN_ERROR,ERROR_NO_FREE_STORE);
    }
  for (Menu=display.Menustrip, MenuNum=0; Menu!=NULL; Menu=Menu->NextMenu, MenuNum++)
    {
      struct MenuItem *Item;
      UWORD ItemNum;

      GTMENU_USERDATA(Menu)=(APTR)(((ULONG)GTMENU_USERDATA(Menu))|(FULLMENUNUM(MenuNum,NOITEM,NOSUB)<<16));
      for (Item=Menu->FirstItem, ItemNum=0; Item!=NULL; Item=Item->NextItem, ItemNum++)
	{
	  GTMENUITEM_USERDATA(Item)=(APTR)(((ULONG)GTMENUITEM_USERDATA(Item))|(FULLMENUNUM(MenuNum,ItemNum,NOSUB)<<16));
	}
    }
}

/************************************************************************/

static INLINE void InitKeys(void)

{
  int Key;

  for (Key=0; Key<ARRAYSIZE(data.keysyms); Key++)
    {
      ChangeKey(Key,NULL);
    }
}

/************************************************************************/
/*									*/
/* Locate a menu item							*/
/*									*/
/************************************************************************/

void *FindMenu(UWORD UserData)

{
  struct Menu *Menu;

  for (Menu=display.Menustrip; Menu!=NULL; Menu=Menu->NextMenu)
    {
      struct MenuItem *Item;

      if ((((ULONG)GTMENU_USERDATA(Menu))&0xffff)==UserData)
	{
	  return Menu;
	}
      for (Item=Menu->FirstItem; Item!=NULL; Item=Item->NextItem)
	{
	  struct MenuItem *SubItem;

	  if ((((ULONG)GTMENUITEM_USERDATA(Item))&0xffff)==UserData)
	    {
	      return Item;
	    }
	  for (SubItem=Item->SubItem; SubItem!=NULL; SubItem=SubItem->NextItem)
	    {
	      if ((((ULONG)GTMENUITEM_USERDATA(SubItem))&0xffff)==UserData)
		{
		  return SubItem;
		}
	    }
	}
    }
  return NULL;
}

/************************************************************************/
/*									*/
/* Determine default username						*/
/*									*/
/************************************************************************/

static INLINE void InitUser(void)

{
  static const char * const UserVariables[]={"USER","USERNAME","REALNAME"};

  int i;

  for (i=ARRAYSIZE(UserVariables)-1;
       i>=0 && GetVar(UserVariables[i],scoring.UserName,sizeof(scoring.UserName),0)<5;
       i--)
    ;
  if (i<0)
    {
      scoring.UserName[0]='\0';
    }
}

/************************************************************************/
/*									*/
/* As usual, Commodore didn't properly design their localization	*/
/* facilities. Mris requires two strings which are country, not		*/
/* language dependent (time formats). Since we only have catalogs,	*/
/* I decided to put these strings into the catalog (although this is	*/
/* not correct). But then, CatComp complains about the % formatting	*/
/* commands...								*/
/* Therefore we use '\1' instead of %, and fix the strings in memory.	*/
/*									*/
/************************************************************************/

static char *RepairFormatString(LONG StringID)

{
  char *String;
  char *t;

  String=GetString(StringID);
  for (t=String; *t!='\0'; t++)
    {
      if (*t=='\1')
	{
	  *t='%';
	}
    }
  return String;
}

/************************************************************************/

static INLINE void Init(void)

{
  BPTR OldCurrentDir;

  MiscPort.mp_SigBit=WindowPort.mp_SigBit=AllocSignal(-1);
  if ((GadToolsBase=OpenLibrary("gadtools.library",ROM_VERSION))==NULL)
    {
      MyExit(RETURN_CATASTROPHY,0);
    }
  if ((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",ROM_VERSION))==NULL ||
      (IconBase=OpenLibrary("icon.library",ROM_VERSION))==NULL)
    {
      CloseAll(RETURN_FAIL,0);
    }
  OldCurrentDir=CurrentDir(GetProgramDir());
  GameSupportBase=(struct GameSupportBase *)OpenLibrary("gamesupport.library",0);
  CurrentDir(OldCurrentDir);
  CloseLibrary(&DOSBase->dl_lib);
  if (GameSupportBase==NULL)
    {
      CloseAll(RETURN_FAIL,0);
    }
  DOSBase=GameSupportBase->DOSBase;
  IntuitionBase=GameSupportBase->IntuitionBase;
  GfxBase=GameSupportBase->GfxBase;
  UtilityBase=GameSupportBase->UtilityBase;
  LayersBase=GameSupportBase->LayersBase;
  KeymapBase=GameSupportBase->KeymapBase;
  if ((CxBase=OpenLibrary("commodities.library",37))==NULL)
    {
      CloseAll(RETURN_FAIL,0);
    }
  if ((LocaleBase=GameSupportBase->LocaleBase)!=NULL)
    {
      static struct TagItem CatalogTags[]=
	{
	  {OC_BuiltInLanguage, (ULONG)"deutsch"},
	  {OC_Version, 1},
	  {TAG_DONE}
	};

      Locale=OpenLocale(NULL);
      if ((Catalog=OpenCatalogA(Locale,PROGRAM_NAME ".catalog",CatalogTags))==NULL)
	{
	  LONG Error;

	  if ((Error=IoErr())!=0)
	    {
	      CloseAll(RETURN_FAIL,Error);
	    }
	}
    }
  Formats.ElapsedTime=RepairFormatString(MSG_ELAPSED_TIME_FORMAT);
  Formats.DateTime=RepairFormatString(MSG_DATE_FORMAT);
#if !defined(__SASC)
  if ((DitherPattern=AllocMem(2*sizeof(UWORD),MEMF_CHIP))==NULL)
    {
      CloseAll(RETURN_FAIL,ERROR_NO_FREE_STORE);
    }
  DitherPattern[0]=0x5555;
  DitherPattern[1]=0xaaaa;
#endif
  scoring.mine.Score.Name=scoring.UserName;
  scoring.display=-1;
  WBPort.mp_SigTask=MiscPort.mp_SigTask=WindowPort.mp_SigTask=FindTask(NULL);
}

/************************************************************************/

void StartGame(void)

{
  struct Menu *Menu;
  struct MenuItem *Item;

  Item=(struct MenuItem *)FindMenu(MSG_CONFIG_RANDOM_MENU);
  OffMenu(display.window,((ULONG)GTMENUITEM_USERDATA(Item))>>16);
  data.random=(Item->Flags & CHECKED) ? TRUE : FALSE;
  Menu=(struct Menu *)FindMenu(MSG_GARDENS_MENU);
  OffMenu(display.window,((ULONG)GTMENU_USERDATA(Menu))>>16);
  Menu=(struct Menu *)FindMenu(MSG_DEMO_MENU);
  OffMenu(display.window,((ULONG)GTMENU_USERDATA(Menu))>>16);
  MiscState.GameMode=TRUE;
  MiscState.JumpDemo=0;
  MiscState.GamePause=FALSE;
  LoadBoards();
}

/************************************************************************/

void StopGame(void)

{
  if (MiscState.HappyBlanker)
    {
      MiscState.HappyBlanker=FALSE;
      GS_NoHappyBlanker();
    }
  history.msec += global.msec;
  scoring.mine.score = global.score;
  scoring.mine.screen = global.screen;
  scoring.mine.elapsed = (unsigned)(history.msec / 1000);
  if (scoring.ScoreHandle==NULL && board.boards>0)
    {
      scoring.ScoreHandle=GS_ObtainScoreHandle(&ScoreDef.ScoreDef,board.Name,scoring.UserName);
    }
  if (scoring.ScoreHandle!=NULL && scoring.mine.score>=SCORE_THRESHOLD)
    {
      GS_InsertScore(scoring.ScoreHandle,&scoring.mine.Score);
    }
  else
    {
      ULONG Micros;

      CurrentTime(&scoring.mine.Score.TimeStamp,&Micros);
    }
  EnableMenu(MSG_DEMO_MENU,FALSE);
  EnableMenu(MSG_GARDENS_MENU,FALSE);
  EnableMenu(MSG_CONFIG_RANDOM_MENU,TRUE);
  MiscState.GameMode=FALSE;
  MiscState.GamePause=FALSE;
}

/************************************************************************/
/*									*/
/* Read garden directory, and create initial "Gardens" menu		*/
/*									*/
/************************************************************************/

static INLINE void InitGardens(void)

{
  LONG Error;

  if ((GardenDir=Lock("PROGDIR:mris gardens",SHARED_LOCK))!=MKBADDR(NULL))
    {
      struct ExAllControl *ExAllControl;
      BPTR OldCurrentDir;

      OldCurrentDir=CurrentDir(GardenDir);
      if ((ExAllControl=AllocDosObject(DOS_EXALLCONTROL,NULL))!=NULL)
	{
	  void *ExAllData;

	  if ((ExAllData=GS_MemoryAlloc(4096))!=NULL)
	    {
	      BOOL More;

	      Error=0;
	      ExAllControl->eac_LastKey=0;
	      ExAllControl->eac_MatchString=NULL;
	      ExAllControl->eac_MatchFunc=NULL;
	      do
		{
		  More=ExAll(GardenDir,ExAllData,4096,ED_SIZE,ExAllControl);
		  if (!More)
		    {
		      LONG Err;

		      if ((Err=IoErr())!=ERROR_NO_MORE_ENTRIES)
			{
			  if (Error==0)
			    {
			      Error=Err;
			    }
			  break;
			}
		    }
		  if (ExAllControl->eac_Entries>0)
		    {
		      struct ExAllData *Data;

		      for (Data=ExAllData; Data!=NULL; Data=Data->ed_Next)
			{
			  if ((Data->ed_Size>=12+sizeof(BOARD)) && ((Data->ed_Size-12)%sizeof(BOARD))==0)
			    {
			      BPTR Filehandle;

			      if ((Filehandle=Open(Data->ed_Name,MODE_OLDFILE))!=MKBADDR(NULL))
				{
				  char Header[12];

				  if (Read(Filehandle,Header,12)==12)
				    {
				      if (strncmp(Header,"mris Garden!",12)==0)
					{
					  struct StringNode *Name;

					  if ((Name=GS_MemoryAlloc(sizeof(struct StringNode)+strlen(Data->ed_Name)+1+1))!=NULL)
					    {
					      struct StringNode **Current;

					      strcpy(((char *)(Name+1))+1,Data->ed_Name);
					      for (Current=&GardenStrings;
						   *Current!=NULL && (Locale!=NULL ?
								      StrnCmp(Locale,Data->ed_Name,
									      ((char *)((*Current)+1))+1,
									      -1,SC_COLLATE2) :
								      Stricmp(Data->ed_Name,
									      ((char *)((*Current)+1))+1))>0;
						   Current=&(*Current)->Next)
						;
					      Name->Next=*Current;
					      *Current=Name;
					    }
					  else
					    {
					      Error=ERROR_NO_FREE_STORE;
					    }
					}
				    }
				  Close(Filehandle);
				}
			    }
			}
		    }
		}
	      while (More);
	      if (Error==ERROR_NO_MORE_ENTRIES)
		{
		  Error=0;
		}
	      GS_MemoryFree(ExAllData);
	      if (Error==0)
		{
		  struct StringNode *Name;
		  struct MenuItem **Item;
		  ULONG Count;

		  Count=0;
		  Item=&((struct Menu *)FindMenu(MSG_GARDENS_MENU))->FirstItem;
		  for (Name=GardenStrings; Error==0 && Name!=NULL; Name=Name->Next)
		    {
		      struct NewMenu NewMenu[2];

		      NewMenu[0].nm_Type=NM_ITEM;
		      if (Count<10)
			{
			  NewMenu[0].nm_CommKey=(char *)(Name+1);
			  *(char *)(Name+1)=Count==9 ? '0' : '1'+Count;
			}
		      else
			{
			  NewMenu[0].nm_CommKey=NULL;
			}
		      NewMenu[0].nm_Label=((char *)(Name+1))+1;
		      NewMenu[0].nm_Flags=(stricmp(NewMenu[0].nm_Label,"Digits")==0) ? CHECKIT|CHECKED : CHECKIT;
		      NewMenu[0].nm_MutualExclude=~(1<<Count++);
		      NewMenu[0].nm_UserData=(APTR)MSG_GARDENS_MENU;
		      NewMenu[1].nm_Type=NM_END;
		      if ((*Item=(struct MenuItem *)CreateMenusA(NewMenu,NULL))!=NULL)
			{
			  Item=&(*Item)->NextItem;
			}
		      else
			{
			  Error=ERROR_NO_FREE_STORE;
			}
		    }
		}
	    }
	  else
	    {
	      Error=ERROR_NO_FREE_STORE;
	    }
	  FreeDosObject(DOS_EXALLCONTROL,ExAllControl);
	}
      else
	{
	  Error=ERROR_NO_FREE_STORE;
	}
      CurrentDir(OldCurrentDir);
      if (Error==0)
	{
	  LoadBoards();
	  UpdateWindowTitle();
	}
    }
  else
    {
      Error=IoErr();
    }
  if (Error!=0)
    {
      CloseAll(RETURN_FAIL,Error);
    }
}

/************************************************************************/
/*									*/
/* Process a YES/NO tooltype.						*/
/*									*/
/************************************************************************/

static void ProcessBoolean(char **ToolTypes, char *Name, UWORD MenuID)

{
  char *ToolType;

  if ((ToolType=FindToolType(ToolTypes,Name))!=NULL)
    {
      struct MenuItem *Item;

      Item=FindMenu(MenuID);
      if (MatchToolValue(ToolType,"YES"))
	{
	  Item->Flags|=CHECKED;
	}
      else if (MatchToolValue(ToolType,"NO"))
	{
	  Item->Flags&=~CHECKED;
	}
      else
	{
	  while (*ToolType==' ' || *ToolType=='\t')
	    {
	      ToolType++;
	    }
	  if (ToolType=='\0')
	    {
	      Item->Flags|=CHECKED;
	    }
	  else
	    {
	      DisplayError(MSG_BOOLTOOLTYPE_ERROR,ToolType,Name);
	    }
	}
    }
}

/************************************************************************/
/*									*/
/* Process a tooltype array.						*/
/*									*/
/************************************************************************/

static void ProcessToolTypes(char **ToolTypes)

{
  char *ToolType;
  int Key;

  ProcessBoolean(ToolTypes,MrisToolTypes[8],MSG_CONFIG_RANDOM_MENU);
  ProcessBoolean(ToolTypes,MrisToolTypes[9],MSG_CONFIG_DISPLAY_DYNAMIC_MENU);
  ProcessBoolean(ToolTypes,MrisToolTypes[13],MSG_CONFIG_CONTROL_JOYSTICK_MENU);
  ProcessBoolean(ToolTypes,MrisToolTypes[15],MSG_CONFIG_DISPLAY_MSIT_MENU);

  for (Key=0; Key<KEYS; Key++)
    {
      if ((ToolType=FindToolType(ToolTypes,MrisToolTypes[Key]))!=NULL)
	{
	  ChangeKey(Key,ToolType);
	}
    }

  if ((ToolType=FindToolType(ToolTypes,MrisToolTypes[10]))!=NULL)
    {
      while (*ToolType==' ')
	{
	  ToolType++;
	}
      strncpy(scoring.UserName,ToolType,sizeof(scoring.UserName)-1);
      scoring.UserName[sizeof(scoring.UserName)-1]='\0';
    }

  if ((ToolType=FindToolType(ToolTypes,MrisToolTypes[11]))!=NULL)
    {
      while (*ToolType==' ')
	{
	  ToolType++;
	}
      GS_MemoryFree(ScreenDescription);
      if (*ToolType=='\0')
	{
	  ScreenDescription=NULL;
	}
      else
	{
	  if ((ScreenDescription=GS_FormatString("PUBSCREEN \"%s\"",&ToolType,NULL,NULL))==NULL)
	    {
	      DisplayError(0,ERROR_NO_FREE_STORE);
	    }
	}
    }

  if ((ToolType=FindToolType(ToolTypes,MrisToolTypes[12]))!=NULL)
    {
      while (*ToolType==' ')
	{
	  ToolType++;
	}
      GS_MemoryFree(ScreenDescription);
      if ((ScreenDescription=GS_MemoryAlloc(strlen(ToolType)+1))!=NULL)
	{
	  strcpy(ScreenDescription,ToolType);
	}
      else
	{
	  DisplayError(0,ERROR_NO_FREE_STORE);
	}
    }

  if ((ToolType=FindToolType(ToolTypes,MrisToolTypes[14]))!=NULL)
    {
      LONG Error;
      struct TextAttr NewTextAttr;

      if ((Error=MakeTextAttr(ToolType,&NewTextAttr))==0)
	{
	  if (NewTextAttr.ta_YSize<CELL_HEIGHT)
	    {
	      FreeVec(TextAttr.ta_Name);
	      TextAttr.ta_Name=NewTextAttr.ta_Name;
	      TextAttr.ta_YSize=NewTextAttr.ta_YSize;
	    }
	  else
	    {
	      FreeVec(NewTextAttr.ta_Name);
	    }
	}
      else
	{
	  char String[ERRORSTRING_LENGTH];

	  DisplayError(MSG_FONTDESC_ERROR,ToolType,ErrorString(String,IoErr()));
	}
    }
}

/************************************************************************/
/*									*/
/* Check out our program name						*/
/*									*/
/************************************************************************/

static INLINE void CheckName(void)

{
  const char *Name;
  char ProgramName[256];

  if (WorkbenchMessage==NULL)
    {
      GetProgramName(ProgramName,sizeof(ProgramName));
      Name=FilePart(ProgramName);
    }
  else
    {
      Name=FindTask(NULL)->tc_Node.ln_Name;
    }
  while (*Name!='\0')
    {
      if (strnicmp(Name,"msit",4)==0)
	{
	  ((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_MSIT_MENU))->Flags|=CHECKED;
	  break;
	}
      Name++;
    }
}

/************************************************************************/
/*									*/
/* Get arguments							*/
/*									*/
/************************************************************************/

static INLINE void ObtainArgs(void)

{
  CheckName();
  if (WorkbenchMessage==NULL)
    {
      struct DiskObject *DiskObject;

      if ((DiskObject=DoDefaultIcon(NULL))!=NULL)
	{
	  struct
	    {
	      LONG Random;
	      LONG NoRandom;
	      LONG Dynamic;
	      LONG NoDynamic;
	      LONG Joystick;
	      LONG NoJoystick;
	      LONG mris;
	      LONG msit;
	      char *User;
	      char *Pubscreen;
	      char *Screen;
	      char *Font;
	      char *Keys[KEYS];
	    } Arguments;
	  struct RDArgs *RDArgs;

	  ProcessToolTypes(DiskObject->do_ToolTypes);
	  memset(&Arguments,0,sizeof(Arguments));
	  if ((RDArgs=ReadArgs("RANDOM/S,NORANDOM/S,"
			       "DYNAMIC/S,NODYNAMIC/S,"
			       "JOYSTICK/S,NOJOYSTICK/S,"
			       "MRIS/S,MSIT/S,"
			       "USER=USERNAME/K,"
			       "PUBSCREEN/K,"
			       "SCREEN/K,"
			       "FONT/K,"
			       "UP/K,DOWN/K,LEFT/K,RIGHT/K,THROW/K,PAUSE/K,QUIT/K,ICONIZE/K",
			       (LONG *)&Arguments,NULL))!=NULL)
	    {
	      int Key;

	      if (Arguments.Random)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_RANDOM_MENU))->Flags|=CHECKED;
		}
	      if (Arguments.NoRandom)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_RANDOM_MENU))->Flags&=~CHECKED;
		}
	      if (Arguments.Dynamic)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_DYNAMIC_MENU))->Flags|=CHECKED;
		}
	      if (Arguments.NoDynamic)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_DYNAMIC_MENU))->Flags&=~CHECKED;
		}
	      if (Arguments.Joystick)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_CONTROL_JOYSTICK_MENU))->Flags|=CHECKED;
		}
	      if (Arguments.NoJoystick)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_CONTROL_JOYSTICK_MENU))->Flags&=~CHECKED;
		}
	      if (Arguments.mris)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_MSIT_MENU))->Flags&=~CHECKED;
		}
	      if (Arguments.msit)
		{
		  ((struct MenuItem *)FindMenu(MSG_CONFIG_DISPLAY_MSIT_MENU))->Flags|=CHECKED;
		}
	      if (Arguments.User!=NULL)
		{
		  strncpy(scoring.UserName,Arguments.User,sizeof(scoring.UserName)-1);
		  scoring.UserName[sizeof(scoring.UserName)-1]='\0';
		}
	      for (Key=0; Key<KEYS; Key++)
		{
		  if (Arguments.Keys[Key]!=NULL)
		    {
		      if (Arguments.Keys[Key][0]=='\0')
			{
			  ChangeKey(Key,NULL);
			}
		      else
			{
			  ChangeKey(Key,Arguments.Keys[Key]);
			}
		    }
		}
	      if (Arguments.Pubscreen!=NULL)
		{
		  GS_MemoryFree(ScreenDescription);
		  if (Arguments.Pubscreen[0]=='\0')
		    {
		      ScreenDescription=NULL;
		    }
		  else
		    {
		      if ((ScreenDescription=GS_FormatString("PUBSCREEN \"%s\"",&Arguments.Pubscreen,NULL,NULL))==NULL)
			{
			  DisplayError(0,ERROR_NO_FREE_STORE);
			}
		    }
		}
	      if (Arguments.Screen!=NULL)
		{
		  GS_MemoryFree(ScreenDescription);
		  if ((ScreenDescription=GS_MemoryAlloc(strlen(Arguments.Screen)+1))!=NULL)
		    {
		      strcpy(ScreenDescription,Arguments.Screen);
		    }
		  else
		    {
		      DisplayError(0,ERROR_NO_FREE_STORE);
		    }
		}
	      if (Arguments.Font!=NULL)
		{
		  struct TextAttr NewTextAttr;
		  LONG Error;

		  if ((Error=MakeTextAttr(Arguments.Font,&NewTextAttr))==0)
		    {
		      if (NewTextAttr.ta_YSize<CELL_HEIGHT)
			{
			  FreeVec(TextAttr.ta_Name);
			  TextAttr.ta_Name=NewTextAttr.ta_Name;
			  TextAttr.ta_YSize=NewTextAttr.ta_YSize;
			}
		      else
			{
			  FreeVec(NewTextAttr.ta_Name);
			}
		    }
		  else
		    {
		      char String[ERRORSTRING_LENGTH];

		      DisplayError(MSG_FONTDESC_ERROR,Arguments.Font,ErrorString(String,Error));
		    }
		}
	      FreeArgs(RDArgs);
	    }
	  else
	    {
	      char String[ERRORSTRING_LENGTH];

	      DisplayError(MSG_COMMANDLINE_ERROR,ErrorString(String,IoErr()));
	    }
	  if (DiskObject!=&mrisIcon)
	    {
	      FreeDiskObject(DiskObject);
	    }
	}
    }
  else
    {
      LONG Count;

      for (Count=0; Count<WorkbenchMessage->sm_NumArgs; Count++)
	{
	  BPTR OldCurrentDir;
	  char *Name;
	  struct DiskObject *DiskObject;
	  
	  OldCurrentDir=CurrentDir(WorkbenchMessage->sm_ArgList[Count].wa_Lock);
	  Name=WorkbenchMessage->sm_ArgList[Count].wa_Name;
	  if ((DiskObject=GetDiskObject(Name))!=NULL)
	    {
	      ProcessToolTypes(DiskObject->do_ToolTypes);
	      FreeDiskObject(DiskObject);
	    }
	  else
	    {
	      char String[ERRORSTRING_LENGTH];
	      
	      DisplayError(MSG_GETICON_ERROR,Name,ErrorString(String,IoErr()));
	    }
	  CurrentDir(OldCurrentDir);
	}
    }
  data.random=(((struct MenuItem *)FindMenu(MSG_CONFIG_RANDOM_MENU))->Flags & CHECKED) ? TRUE : FALSE;
}

/************************************************************************/

static INLINE void CreateBroker(void)

{
  LONG Error;
  ULONG Count;

  Count=0;
  NewBroker.nb_Title=GetString(MSG_BROKER_TITLE);
  NewBroker.nb_Descr=GetString(MSG_GAME_DESCRIPTION);
  do
    {
      GS_MemoryFree(NewBroker.nb_Name);
      if (Count==0)
	{
	  NewBroker.nb_Name=GS_FormatString("mris",&Count,NULL,NULL);
	}
      else
	{
	  NewBroker.nb_Name=GS_FormatString("mris.%lu",&Count,NULL,NULL);
	}
      if (NewBroker.nb_Name!=NULL)
	{
	  if ((Broker=CxBroker(&NewBroker,&Error))==NULL)
	    {
	      Count++;
	    }
	}
      else
	{
	  Error=ERROR_NO_FREE_STORE;
	}
    }
  while (Error==CBERR_DUP);
  ActivateCxObj(Broker,TRUE);
}

/************************************************************************/

void AmigaMain(void)

{
  Init();
  MyCreateMenustrip();
  InitKeys();
  InitUser();
  ObtainArgs();
  InitGardens();
  CreateBroker();
  OpenGUI();
  Game();
  CloseAll(RETURN_OK,0);
}
