/*  interface.c-- high-level interface functions and misc. utilities */
/* Copyright 1996,97    Robert Masenten                      */
/*                                                           */
/* This is part of the source for AGiliTy, the (Mostly) Universal  */
/*       AGT Interpreter                                           */

/* This module contains a miscellany of things that are somewhat */
/* system dependent but not enough so to justify being put in */
/* OS_<whatever>.c */
/* --writestr() and writeln().*/
/* --Hooks for sound, pictures, and fonts.  */
/* --yesno() and wait_return() */
/* --Some lower level file stuff */
/* --main() and command line parseing stuff */

#include <stdlib.h>
#include <ctype.h>
#include <errno.h>

#include "agtread.h"
#include "uagt.h"

/* #define DEBUG_BELLS_AND_WHISTLES */

/* Warning for fontcmd, pictcmd, musiccmd:
  These all extract filenames from fontlist, pictlist, pixlist, songlist.
 Any of these are allowed to be NULL and this should be checked
 before accessing them.  */

#ifdef DEBUG_BELLS_AND_WHISTLES
void bnw_report(char *cmdstr,filename *list,int index)
{
  writeln("");
  writestr(">** ");writestr(cmdstr);writestr(" ");
  if (list!=NULL) {
    writestr(list[index]);
    writestr(" ");}
  writeln("**<");
}
#endif /* DEBUG_BELLS_AND_WHISTLES */

void fontcmd(int cmd,int font)
/* 0=Load font, name is fontlist[font]  
   1=Restore original (startup) font  
*/
{
#ifdef DEBUG_BELLS_AND_WHISTLES
  if (cmd==0) bnw_report("Loading Font",fontlist,font);
  else if (cmd==1) bnw_report("Restoring original font",NULL,0);
#endif
  return;
}

void pictcmd(int cmd,int pict)
/* 1=show global picture, name is pictlist[pict]
   2=show room picture, name is pixlist[pict]
  */
{
#ifdef DEBUG_BELLS_AND_WHISTLES
  if (cmd==1) bnw_report("Showing picture",pictlist,pict);
  else if (cmd==2) bnw_report("Showing pix",pixlist,pict);
  agt_getkey(0);
#endif
   return;
}



int musiccmd(int cmd,int song)
/* For cmd=1 or 2, the name of the song is songlist[song]
  The other commands don't take an additional argument.
   1=play song 
   2=repeat song
   3=end repeat
   4=end song
   5=suspend song
   6=resume song
   7=clean-up
   8=turn sound on
   9=turn sound off
   -1=Is a song playing? (0=false, -1=true)
   -2=Is the sound on?  (0=false, -1=true)
*/
{
  if (cmd==8) sound_on=1;
  else if (cmd==9) sound_on=0;
#ifdef DEBUG_BELLS_AND_WHISTLES
  switch (cmd) {
     case 1:bnw_report("Play song",songlist,song);break;
     case 2:bnw_report("Repeat song",songlist,song);break;
     case 3:bnw_report("End repeat",NULL,0);break;
     case 4:bnw_report("End song",NULL,0);break;
     case 5:bnw_report("Suspend song",NULL,0);break;
     case 6:bnw_report("Resume song",NULL,0);break;
     case 7:bnw_report("Clean up",NULL,0);break;
     case 8:bnw_report("Sound On",NULL,0);break;
     case 9:bnw_report("Sound Off",NULL,0);break;
     case -1:return yesno("Is song playing?");
     case -2:return 1;
     }
#endif
  return 0;
}



static char linebuff[100];
static int lp;  /* Line pointer */
static bool savenl, needfill; /* Used for paragraph filling */
static bool quotemode=0; 

void debugout(const char *s)
{
  int i;

  if (DEBUG_OUT) fputs(s,debugfile);
  else {
    lp=0;
    for(;*s!=0;s++) {
      if (curr_x+lp>=screen_width || lp>80) {
	if (lp+curr_x>=screen_width)
	  lp=screen_width-curr_x-1;
	linebuff[lp]=0;
	agt_puts(linebuff);
	agt_newline();
	lp=0;
      }
      if (*s=='\n') 
	{linebuff[lp]=0;
	 agt_puts(linebuff);
	 agt_newline();
	 lp=0;}
      else if (*s=='\t') 
	{for(i=0;i<3;i++) linebuff[lp++]=' ';}
      else if (*s>=0 && *s<=9) linebuff[lp++]=' ';
      else linebuff[lp++]=*s;
    }
    linebuff[lp]=0;
    agt_puts(linebuff);
  }
}


static char *get_log(void)
/* Read string from logfile_in */ 
{
  char *s;

  s=rmalloc(100);s[0]=0;
  fgets(s,1000,log_in);
  if (feof(log_in)) {  /* Reached end of logfile */
    logflag&=~2;
    fclose(log_in);
  } else { /* Need to delay or wait for keypress */
    if (logdelay==-1) agt_getkey(0);
    else agt_delay(logdelay);
    if (s[0]!=0) writeln(s);
  }
  return s;
}


static void put_log(const char *s)
/* Write s to logfile_out */
{
  fputs(s,log_out);
  if (s[strlen(s)-1]!='\n')
    fputs("\n",log_out);
}


char *agt_readline(int in_type)
{
  char *s;

  if (PURE_INPUT) agt_textcolor(-1);
  if (logflag&2) 
    s=get_log();
  else  
    s=agt_input(in_type);
  if (PURE_INPUT) agt_textcolor(-2);

  if (logflag&1)
    put_log(s);
  
  return s;
}

char agt_getchar(void)
{
  char c, *s, buff[2];

  if (PURE_INPUT) agt_textcolor(-1);
  if (logflag&2) {
    s=get_log();
    c=s[0];
    rfree(s);
  } else 
    c=agt_getkey(1);
  if (PURE_INPUT) agt_textcolor(-2);
  if (logflag&1) {
    buff[0]=c;buff[1]=0;
    put_log(buff);
  }
  return c;
}

void agt_center(bool b) 
/* 1=turn on text centering, 0=turn off */
/* At the moment, this is only used for game end messages */
/* When it is on, text output with writeln() should be 'centered'; */
/* it is up to the interface to decide what that means. */
/* writestr() should not be called while centering is on. */
{
  center_on=b;
}

void agt_par(bool b) 
/* This has been added for the sake of anyone trying to get this to */
/*  work on less-than-80 column screens. My personal opinion is that this */
/* is probably hopeless; many AGT games assume 80-column format for   */
/* creating tables and ascii graphics. Nevertheless... */
/*  Text between an agt_par(1) and an agt_par(0) is logically connected */
/* (all part of one description) and so it *might* be possible to reformat */
/* it, treating multiple lines as being one paragraph. */ 
/*    At the very least, you should look for blank lines and indentation */
/* since a single section of text could contain multiple paragraphs. */
/* Sections of text _not_ between an agt_par(1) and an agt_par(0) should */
/* be treated as though each line were a new paragraph */
{
  par_fill_on=b;
  if (b==0 && savenl) agt_newline();
  savenl=0;needfill=0;
}

/* This handles the various format code. They all show up after
   '\r'; unrecogonized codes are just ignored */
static uchar xlat_format_code(uchar c)
{
  if (c==0xFF) 
    if (fix_ascii) return trans_ibm[0xFF-0x80];
    else return 0xFF;   
  return 0;
}

static void run_format_code(uchar c)
{
  if (c<13) 
    agt_textcolor(c-3);
}

#define format_code(c) ((c>0 && c<=LAST_TEXTCODE)||((uchar)c==FORMAT_CODE))  

void writestr(const char *s)
{
  int i, j;
  char c;
  int endmark, old_x;
 
  if (par_fill_on && savenl && !isspace(s[0]))
    agt_newline();
  savenl=0;
  i=0;lp=0;

  while(s[i]!=0) {
    for(;s[i]!=0 && lp<90 && curr_x+lp<screen_width;i++)
      if (s[i]=='\t') 
	for(j=0;j<TAB_SIZE && curr_x+lp<screen_width;j++) linebuff[lp++]=' ';
      else if (format_code(s[i])) {linebuff[lp++]=' ';break;}  /* Color code */
      else if (s[i]=='\r') { /* New format code */
	if (s[i+1]==0) continue; /* Bogus format code */
	if ( ((uchar)s[i+1])<13) break;
	c=(char)xlat_format_code((uchar)s[++i]);
	if (c!=0) linebuff[lp++]=c;
      }
      else if (s[i]=='\n') { break;}
      else linebuff[lp++]=s[i];

    linebuff[lp]=0;

    /* Backtrack to last space; in case of formatting codes, we should
     already have one */
    endmark=lp;
    
    if (!isspace(s[i]) && !format_code(s[i]) && s[i]!=0)
      {/* If we aren't conveniently at a break...*/
	do {    /* Find last space */
	  endmark--;
	} while(endmark>0 && !isspace(linebuff[endmark]));
      }

    if (endmark==0 && !isspace(linebuff[endmark])) /* Can't find a break */
      if (curr_x+lp<screen_width)  /* Not a line break */
	endmark=lp; /* Break at end; it doesn't matter that much */
      else  /* We _need_ a line break but are having trouble finding one */ 
	if (curr_x>0)  /* already stuff on this line printed previously */
	  endmark=0;  /* i.e. print out nothing; move it to next line */
	else   /* We have a single word that is longer than our line */
	  endmark=screen_width;   /* Give up */
    
    c=linebuff[endmark];
    linebuff[endmark]=0;
    old_x=curr_x;

    agt_puts(linebuff);

    linebuff[endmark]=c;

    if (old_x+lp>=screen_width)
      /* Need to insert line break and skip any spaces */
      {
	if (!quotemode) agt_newline(); 
	else return; /* In quote mode, just truncate */

	/* Now set up beginning of next line: skip over whitespace */
	while(endmark<lp && isspace(linebuff[endmark])) 
	  endmark++;  /* Eliminate EOL whitespace */
	if (endmark==lp) {/* Nothing left; eliminate whitespace at beginning
			    of next line */
	  while(isspace(s[i]) && s[i]!='\r') i++;
	  lp=endmark=0;
	}
	needfill=1;
	if (endmark==lp && s[i]==0) {
	  needfill=2;
	  return; /* If only spaces left, don't print them */
	}
      }

    /* Now copy remaining text */
    for(j=0;endmark<lp;j++,endmark++) linebuff[j]=linebuff[endmark];
    lp=j;

    /* Now to deal with format codes */
    if ((unsigned char)s[i]==FORMAT_CODE) { 
      i++;
      if (bold_mode)  /* Translate as BOLD toggle */
	{
	  if (textbold)
	    agt_textcolor(-2);  /* Turn bold off */
	  else agt_textcolor(-1); /* Turn bold on */
	  textbold=!textbold;
	}
      else /* translate as BLACK */
	agt_textcolor(0);
    }
    else if (s[i]>0 && s[i]<=LAST_TEXTCODE) 
      agt_textcolor(s[i++]);
    else if (s[i]=='\r') {
      run_format_code((uchar)s[i+1]);
      i+=2;
    }
    else if (s[i]=='\n') {
      i+=1;
      agt_newline();
    }
  }
}



void writeln(const char *s)
{
  int i,pad;
  char *padstr;

  if (center_on && strlen(s)+curr_x<screen_width) {
    pad=(screen_width-strlen(s))/2;
    padstr=rmalloc((pad+1)*sizeof(char));
    for(i=0;i<pad;i++) padstr[i]=' ';
    padstr[i]=0;
    agt_puts(padstr);
    rfree(padstr);
  }
  writestr(s);
  /* needfill=2 if writestr ended with a line that wrapped only because
     of excess spaces; needfill==1 if writestr wrapped a line for any
     reason */
  if ((needfill==2) || (par_fill_on && needfill)) { 
    if (needfill!=2) savenl=1;
    needfill=0;
    }
  else agt_newline();
}
 

static char fixstatchar(uchar c) 
/* Eliminate formating characters in the status line */
{
  if (c=='\t' || c<=LAST_TEXTCODE || 
      (c==FORMAT_CODE) || c=='\r' || c=='\n')
    return ' ';
  return c;
}

void print_statline(void)
/* Use strings in l_stat and r_stat */
{
  int i,j;
  char s[100], *t;
  static bool lastline=0; /* Was a non-empty status line printed  last time? */

  /* If both strings empty, don't print the status line */
  if (l_stat[0]==0 && r_stat[0]==0 && !lastline) return;
  lastline=(l_stat[0] || r_stat[0]);

  i=status_width-strlen(l_stat)-strlen(r_stat);

  j=0;
  if (r_stat[0]==0) {/* Center the status line */
    while(j<i/2) s[j++]=' ';
    i-=j;
  } else if (i>6) {s[j++]=' ';i-=2;}  /* If statline is wide enough, put a 
				       space on each side */

  if (strlen(l_stat)<status_width)    /* Copy left side of status line into s*/
    for(t=l_stat;*t!=0;t++) s[j++]=fixstatchar(*t); 

  for(;i>0;i--) s[j++]=' ';  /* Insert space between left and right sides */

  if (j+strlen(r_stat)<=status_width) /*Copy right side into s */
    for(t=r_stat;*t!=0;t++) s[j++]=fixstatchar(*t);

  while(j<status_width) s[j++]=' '; /* Pad any extra width with spaces */
  s[j]=0;  /* Put end of string marker */
  agt_statline(s); /* Output it */
}


void padout(int padleng)
{
  int i;
  char *pstr;
  
  if (padleng<=0) return;
  pstr=rmalloc(padleng+1);
  for(i=0;i<padleng;i++) pstr[i]=' ';
  pstr[padleng]=0;
  writestr(pstr);
  free(pstr);
}

static int textwidth(char *s)
{
  int n;

  n=0;
  for(;*s!=0;s++) n+=(*s=='\t') ? TAB_SIZE : 1;
  return n;
}

void textbox(char *(txt[]),int len,unsigned long flags)
/* TB_TTL, TB_BOLD, TB_BORDER, TB_CENTER */
{
  int i, width, padwidth;
  int *linewidth;

  agt_textcolor(7);
  if (flags & TB_BOLD) agt_textcolor(-1);
  else agt_textcolor(-2);

  linewidth=rmalloc(len*sizeof(int));

  width=0;  /* This contains the maximum width of any line */
  for(i=0;i<len;i++) {
    linewidth[i]=textwidth(txt[i]);
    if (linewidth[i]>width) width=linewidth[i];
  }

  agt_makebox(width,len,flags&~(TB_BOLD|TB_CENTER));
  quotemode=1;  /* So newlines will cause truncation rather than a 
		   real newline */
  for(i=0;i<len;i++) {
    padwidth=width-linewidth[i]; /* Amount of padding we need */
    if (flags&TB_CENTER)
      {  padout(padwidth/2);padwidth-=padwidth/2;}
    writestr(txt[i]);
    padout(padwidth);
    if (i!=len-1) agt_qnewline();
  }
  agt_endbox();
  quotemode=0; /* Back to normal */

  agt_textcolor(7);
  textbold=0;
}


#ifndef REPLACE_MENU

int agt_menu(char *header,int size,int width,menuentry *menu)
/* This is _very_ minimal as it stands */
{
  int i,j;
  char sbuff[10];
  int numcol, colheight;

  if (size==0) return 0;

  width=width+5;
  numcol=screen_width/width;
  colheight=size/numcol;
  if (size%numcol!=0) colheight++;
      
  writeln(header);  
  for(i=0;i<colheight;i++) {
    for(j=0;j<numcol;j++) {
      if (j*colheight+i>=size) break;
      sprintf(sbuff,"%2d.",j*colheight+i+1);
      writestr(sbuff);
      writestr(menu[j*colheight+i]);
      if (j<numcol-1) padout(width-3-strlen(menu[j*colheight+i]));
    }
    writeln("");
  }
  do {
    writestr("Choice:");
    i=read_number()-1;
    if (i<0 || i>=size) 
      writeln("Please choose an option from the menu.");
  } while (i<0 || i>=size);
  return i;
}

#endif /* REPLACE_MENU */







void prompt_out(int n)
/* n=1 standard prompt
   n=2 question prompt */
{
  agt_textcolor(7);
  if (PURE_INPUT && n==1) agt_textcolor(-1);
  if (n==1) {
    agt_newline();
    agt_puts("> ");}
  if (n==2) agt_puts("? ");
  agt_textcolor(7);
}



void wait_return(void)
{
  writeln("          --- HIT ANY KEY ---");
  agt_getkey(0);
}


bool yesno(const char *s)
/* True for yes, false for no. */
{
  char c;

  writestr(s);writestr(" ");
  c='y';
  do {
    if (c!='y') 
      writestr("Please answer <y>es or <n>o. ");
    c=tolower(agt_getchar());
  } while (c!='y' && c!='n');
  return (c=='y');
}


#ifndef REPLACE_GETFILE

static char *last_save=NULL;
static char *last_log=NULL;
static char *last_script=NULL;

FILE *get_user_file(int ft)
/* ft= 0:script, 1:save 2:restore, 3:log(read) 4:log(write)  */
/* Should return file in open state, ready to be read or written to,
   as the case may be */
{
  /* int extlen;*/
  char *fname,*otype, *ext;
  char *defname;  /* Default file name */
  char *p,*q;
  FILE *fd;

  writestr(" Enter ");
  switch (ft) {
    case 0: writestr("script ");
      defname=last_script;
      otype="a";ext=pSCR; break;
    case 1: writestr("save ");
      defname=last_save;
      otype="wb";ext=pSAV;break;
    case 2: writestr("restore ");
      defname=last_save;
      otype="rb";ext=pSAV;break;
    case 3: writestr("log ");
      defname=last_log;
      otype="r";ext=pLOG;break;
    case 4: writestr("log ");
      defname=last_log;
      otype="w";ext=pLOG;break;
    default: writeln("<INTERNAL ERROR: invalid file type>");
      return NULL;
    }
  writestr("file name");
  if (defname!=NULL) {
    writestr(" (");
    writestr(defname);
    writestr(")");
  }
  writestr(": ");

  if (PURE_INPUT) agt_textcolor(-1);
  fname=agt_input(4);
  if (PURE_INPUT) agt_textcolor(-2);

  for(p=fname;isspace(*p);p++);
  for(q=fname;*p!=0 && !isspace(*p);p++,q++) 
    *q=*p;
  *q=0;
  if (q==fname)   /* ie we are left with the empty string */
    if (defname==NULL) {
      writeln("Never mind.");
      rfree(fname);
      return NULL;}
    else {
      rfree(fname);
      fname=defname;
    }
  else  /* We've gotten a new file name; need to add default extension */    
    if (fname!=defname && strlen(ext)>0 && !check_fname(fname,ext))
	fname=remake_fname(fname,ext);	

  if (otype[0]=='w') { /* Check to see if we are overwriting... */
    fd=fopen(fname,"r");
    if (fd!=NULL) { /* File already exists */
      fclose(fd);
      if (!yesno("This file already exists; overwrite?")) { 
	/* That is, DON'T overwrite */
	if (fname!=defname) rfree(fname);
	return NULL;
      }
    }
  }

  fd=fopen(fname,otype);
  if (fd==NULL) {
    writeln("Cannot open file.");
    if (fname!=defname) rfree(fname);
    return NULL;
  }

  switch(ft) 
    {
    case 0: last_script=fname;break;
    case 1: last_save=fname; break;
    case 2: last_save=fname; break;
    case 3: last_log=fname; break;
    case 4: last_log=fname; break;
    }
  if (fname!=defname) rfree(defname);
  return fd;
}


void set_default_filenames(const char *gamename)
{
  last_save=make_fname(gamename,pSAV);
  last_log=make_fname(gamename,pLOG);
  last_script=make_fname(gamename,pSCR);
}

#endif  /* REPLACE_GETFILE */




void script(uchar onp)
{
  if (onp==script_on)
    if (onp==0) writeln("Scripting wasn't on.");
    else writeln("Scripting is already on.");
  if (onp==1) 
    scriptfile=get_user_file(0);
  else if (scriptfile!=NULL) {
    if (fclose(scriptfile)==-1)
      rprintf("Script file: %s",strerror(errno));
    scriptfile=NULL;}
  script_on=onp && (scriptfile!=NULL);
}


void logon(void)
{
  if (logflag&1) {
    writeln("Already logging");
    return;}
  log_out=get_user_file(4);
  if (log_out!=NULL)
    logflag|=1;
}

void replay(int delay)
{
  if (logflag&2) return; /* Nested replays are meaningless */
  log_in=get_user_file(3);
  if (log_in!=NULL) {
    logflag|=2;
    logdelay=delay;
  }
}

/* These two are intended to be called by the platform-dependent
   interface (e.g. if the user had chosen these from some general purpose
   menu) */
/* They're never called from the rest of the code */

void agt_save(void)
{
  savegame();
}

void agt_restore(void)
{
  doing_restore=1;
}

void agt_restart(void)
{
  doing_restore=2;
}

void agt_quit(void)
{
  doing_restore=4;
}

/* This should be rmalloc'd */
static char *newgame_name;

char *new_game(void)
{
  return newgame_name;
}

void agt_newgame(char *gamename)
{
  newgame_name=gamename;
  doing_restore=3;
}

static bool end_cmd_options;

void set_default_options(void)
{
  DIAG=def_DIAG; interp_arg=def_interp_arg;
  debug_da1=def_debug_da1; RAW_CMD_OUT=def_RAW_CMD_OUT;
  ERR_LEVEL=def_ERR_LEVEL;
  debug_parse=def_debug_parse;
  flag=rmalloc(sizeof(bool));
  DEBUG_AGT_CMD=def_DEBUG_AGT_CMD;
  DEBUG_EXEC_VERB=def_DEBUG_EXEC_VERB;
  DEBUG_DISAMBIG=0;
  irun_mode=0;
  fix_ascii_flag=fix_ascii;
#ifdef OPEN_AS_TEXT
  open_as_binary=0;
#endif
}

void helpmsg(void)
{
  printf("AGiliTy: "
	 "The (Mostly) Universal AGT Interpreter, ");
  printf("%s\n",version_str);
  printf("  Copyright 1996,97 Robert Masenten\n");
  printf("[%s]\n",portstr);
  printf("Syntax: agil <options> <game name>\n");
  printf("Options:\n");
  printf("(all of these can be turned off by putting a - after the option)\n");
  printf(" -i Try to use IBM character set.\n");
  printf(" -1 IRUN Mode: Print messages in first person\n");
  printf(" -h Print out this message\n");
  printf(" -d Debug metacommand execution\n");
  printf(" -p Debug parser\n");
  printf(" -x Debug verb execution loop\n");
  printf(" -a Debug disambiguation system\n");
#ifdef OPEN_AS_TEXT
  printf(" -b Open data files as binary files.\n");
#endif
#ifdef MEM_INFO
  printf(" -m Debug memory allocation\n");
#endif
}


static bool setarg(char **optptr)
{
  if ( (*optptr)[1]=='+') {(*optptr)++;return 1;}
  if ( (*optptr)[1]=='-') {(*optptr)++;return 0;}
  return 1;
}

void parse_options(char *opt,char *next)
{
  if (opt[0]=='-' && opt[1]==0)   /* -- */
    {end_cmd_options=1;return;}
  for(;*opt!=0;opt++)
    switch(*opt) 
      {
      case '?': case 'h':
	helpmsg();
	exit(EXIT_SUCCESS);
      case 'p': debug_parse=setarg(&opt);break; 
      case 'a': DEBUG_DISAMBIG=setarg(&opt);break;
      case 'd': DEBUG_AGT_CMD=setarg(&opt);break;
      case 'x':DEBUG_EXEC_VERB=setarg(&opt);break;
      case 'm': DEBUG_MEM=setarg(&opt);break;
      case 'i': fix_ascii_flag=!setarg(&opt);break;
      case '1': irun_mode=setarg(&opt);break;
#ifdef OPEN_FILE_AS_TEXT
      case 'b': open_as_binary=setarg(&opt);break;
#endif
      default:printf("Do not recognize option %c\n",*opt);
	helpmsg();
	exit(EXIT_FAILURE);
      }
}


#ifndef REPLACE_MAIN

static char *strip_agx(char *gamefile)
{
  gamefile=trunc_fname(gamefile,pAGX);
  gamefile=trunc_fname(gamefile,pEXT); /* Strip off period */
  return gamefile;
}


int main(int argc,char *argv[])
{
  int i;
  char *gamefile; 

  set_default_options();
  end_cmd_options=0;
  gamefile=NULL;
  for(i=1;i<argc;i++)
    if (argv[i][0]=='-' && !end_cmd_options)
      parse_options(argv[i]+1,argv[i+1]);
    else if (gamefile==NULL)
      gamefile=argv[i];
    else fatal("Please specify only one game\n");
  if (gamefile==NULL)  {
    helpmsg();
    exit(EXIT_FAILURE);}

  gamefile=strip_agx(gamefile);
  init_interface(gamefile,argc,argv);
  set_default_filenames(gamefile);
 /* From this point on, MUST use writestr/writeln or may
    cause problems w/ the interfaces on some platforms
    that have to keep track of cursor position */
  run_game(gamefile);
  return EXIT_SUCCESS;
}

#endif /* REPLACE_MAIN */


