exchanged some sprintf with snprintf
[xboard.git] / backend.c
index 5496797..3205cde 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -2,8 +2,10 @@
  * backend.c -- Common back end for X and Windows NT versions of\r
  * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $\r
  *\r
- * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.\r
- * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.\r
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
+ * Massachusetts.  Enhancements Copyright\r
+ * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
+ * Foundation, Inc.\r
  *\r
  * The following terms apply to Digital Equipment Corporation's copyright\r
  * interest in XBoard:\r
  * SOFTWARE.\r
  * ------------------------------------------------------------------------\r
  *\r
- * The following terms apply to the enhanced version of XBoard distributed\r
- * by the Free Software Foundation:\r
+ * The following terms apply to the enhanced version of XBoard\r
+ * distributed by the Free Software Foundation:\r
  * ------------------------------------------------------------------------\r
- * This program is free software; you can redistribute it and/or modify\r
+ *\r
+ * GNU XBoard is free software: you can redistribute it and/or modify\r
  * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
+ * the Free Software Foundation, either version 3 of the License, or (at\r
+ * your option) any later version.\r
  *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
+ * GNU XBoard is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * General Public License for more details.\r
  *\r
  * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
- * ------------------------------------------------------------------------\r
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
  *\r
- * See the file ChangeLog for a revision history.  */\r
+ *------------------------------------------------------------------------\r
+ ** See the file ChangeLog for a revision history.  */\r
 \r
 /* [AS] Also useful here for debugging */\r
 #ifdef WIN32\r
@@ -55,7 +57,7 @@
 \r
 #else\r
 \r
-#define DoSleep( n )\r
+#define DoSleep( n ) if( (n) >= 0) sleep(n)\r
 \r
 #endif\r
 \r
@@ -68,6 +70,7 @@
 #include <sys/types.h>\r
 #include <sys/stat.h>\r
 #include <math.h>\r
+#include <ctype.h>\r
 \r
 #if STDC_HEADERS\r
 # include <stdlib.h>\r
@@ -120,6 +123,16 @@ extern int gettimeofday(struct timeval *, struct timezone *);
 # include "zippy.h"\r
 #endif\r
 #include "backendz.h"\r
+#include "gettext.h" \r
\r
+#ifdef ENABLE_NLS \r
+# define _(s) gettext (s) \r
+# define N_(s) gettext_noop (s) \r
+#else \r
+# define _(s) (s) \r
+# define N_(s) s \r
+#endif \r
+\r
 \r
 /* A point in time */\r
 typedef struct {\r
@@ -127,23 +140,6 @@ typedef struct {
     int ms;    /* Assuming this is >= 16 bits */\r
 } TimeMark;\r
 \r
-/* Search stats from chessprogram */\r
-typedef struct {\r
-  char movelist[2*MSG_SIZ]; /* Last PV we were sent */\r
-  int depth;              /* Current search depth */\r
-  int nr_moves;           /* Total nr of root moves */\r
-  int moves_left;         /* Moves remaining to be searched */\r
-  char move_name[MOVE_LEN];  /* Current move being searched, if provided */\r
-  unsigned long nodes;    /* # of nodes searched */\r
-  int time;               /* Search time (centiseconds) */\r
-  int score;              /* Score (centipawns) */\r
-  int got_only_move;      /* If last msg was "(only move)" */\r
-  int got_fail;           /* 0 - nothing, 1 - got "--", 2 - got "++" */\r
-  int ok_to_send;         /* handshaking between send & recv */\r
-  int line_is_book;       /* 1 if movelist is book moves */\r
-  int seen_stat;          /* 1 if we've seen the stat01: line */\r
-} ChessProgramStats;\r
-\r
 int establish P((void));\r
 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,\r
                         char *buf, int count, int error));\r
@@ -161,7 +157,7 @@ int LoadGameFromFile P((char *filename, int n, char *title, int useList));
 int LoadPositionFromFile P((char *filename, int n, char *title));\r
 int SavePositionToFile P((char *filename));\r
 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,\r
-                 Board board));\r
+                 Board board, char *castle, char *ep));\r
 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));\r
 void ShowMove P((int fromX, int fromY, int toX, int toY));\r
 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
@@ -215,7 +211,18 @@ void ParseFeatures P((char* args, ChessProgramState *cps));
 void InitBackEnd3 P((void));\r
 void FeatureDone P((ChessProgramState* cps, int val));\r
 void InitChessProgram P((ChessProgramState *cps, int setup));\r
+void OutputKibitz(int window, char *text);\r
+int PerpetualChase(int first, int last);\r
+int EngineOutputIsUp();\r
+void InitDrawingSizes(int x, int y);\r
+\r
+#ifdef WIN32\r
+       extern void ConsoleCreate();\r
+#endif\r
+\r
 ChessProgramState *WhitePlayer();\r
+void InsertIntoMemo P((int which, char *text)); // [HGM] kibitz: in engineo.c\r
+int VerifyDisplayMode P(());\r
 \r
 char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment\r
 void InitEngineUCI( const char * iniDir, ChessProgramState * cps ); // [HGM] moved here from winboard.c\r
@@ -224,15 +231,15 @@ char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [H
 extern char installDir[MSG_SIZ];\r
 \r
 extern int tinyLayout, smallLayout;\r
-static ChessProgramStats programStats;\r
+ChessProgramStats programStats;\r
 static int exiting = 0; /* [HGM] moved to top */\r
-static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
-extern int startedFromPositionFile;\r
+static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;\r
 int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */\r
 char endingGame = 0;    /* [HGM] crash: flag to prevent recursion of GameEnds() */\r
 int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS     */\r
 VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */\r
 int lastIndex = 0;      /* [HGM] autoinc: last game/position used in match mode */\r
+int opponentKibitzes;\r
 \r
 /* States for ics_getting_history */\r
 #define H_FALSE 0\r
@@ -287,6 +294,8 @@ static char * safeStrCpy( char * dst, const char * src, size_t count )
     return dst;\r
 }\r
 \r
+#if 0\r
+//[HGM] for future use? Conditioned out for now to suppress warning.\r
 static char * safeStrCat( char * dst, const char * src, size_t count )\r
 {\r
     size_t  dst_len;\r
@@ -303,6 +312,27 @@ static char * safeStrCat( char * dst, const char * src, size_t count )
 \r
     return dst;\r
 }\r
+#endif\r
+\r
+/* Some compiler can't cast u64 to double\r
+ * This function do the job for us:\r
+\r
+ * We use the highest bit for cast, this only\r
+ * works if the highest bit is not\r
+ * in use (This should not happen)\r
+ *\r
+ * We used this for all compiler\r
+ */\r
+double\r
+u64ToDouble(u64 value)\r
+{\r
+  double r;\r
+  u64 tmp = value & u64Const(0x7fffffffffffffff);\r
+  r = (double)(s64)tmp;\r
+  if (value & u64Const(0x8000000000000000))\r
+       r +=  9.2233720368547758080e18; /* 2^63 */\r
+ return r;\r
+}\r
 \r
 /* Fake up flags for now, as we aren't keeping track of castling\r
    availability yet. [HGM] Change of logic: the flag now only\r
@@ -318,9 +348,11 @@ PosFlags(index)
   if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;\r
   switch (gameInfo.variant) {\r
   case VariantSuicide:\r
-  case VariantGiveaway:\r
-    flags |= F_IGNORE_CHECK;\r
     flags &= ~F_ALL_CASTLE_OK;\r
+  case VariantGiveaway:                // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!\r
+    flags |= F_IGNORE_CHECK;\r
+  case VariantLosers:\r
+    flags |= F_MANDATORY_CAPTURE; //[HGM] losers: sets flag so TestLegality rejects non-capts if capts exist\r
     break;\r
   case VariantAtomic:\r
     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
@@ -369,12 +401,8 @@ int gotPremove = 0;
 Boolean alarmSounded;\r
 /* end premove variables */\r
 \r
-#define ICS_GENERIC 0\r
-#define ICS_ICC 1\r
-#define ICS_FICS 2\r
-#define ICS_CHESSNET 3 /* not really supported */\r
-int ics_type = ICS_GENERIC;\r
 char *ics_prefix = "$";\r
+int ics_type = ICS_GENERIC;\r
 \r
 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;\r
 int pauseExamForwardMostMove = 0;\r
@@ -495,6 +523,13 @@ ChessSquare CapablancaArray[2][BOARD_SIZE] = {
         BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
 };\r
 \r
+ChessSquare GreatArray[2][BOARD_SIZE] = {\r
+    { WhiteDragon, WhiteKnight, WhiteAlfil, WhiteGrasshopper, WhiteKing, \r
+        WhiteSilver, WhiteCardinal, WhiteAlfil, WhiteKnight, WhiteDragon },\r
+    { BlackDragon, BlackKnight, BlackAlfil, BlackGrasshopper, BlackKing, \r
+        BlackSilver, BlackCardinal, BlackAlfil, BlackKnight, BlackDragon },\r
+};\r
+\r
 ChessSquare JanusArray[2][BOARD_SIZE] = {\r
     { WhiteRook, WhiteAngel, WhiteKnight, WhiteBishop, WhiteKing, \r
         WhiteQueen, WhiteBishop, WhiteKnight, WhiteAngel, WhiteRook },\r
@@ -528,6 +563,7 @@ ChessSquare FalconArray[2][BOARD_SIZE] = {
 #define XiangqiPosition FIDEArray\r
 #define CapablancaArray FIDEArray\r
 #define GothicArray FIDEArray\r
+#define GreatArray FIDEArray\r
 #endif // !(BOARD_SIZE>=10)\r
 \r
 #if (BOARD_SIZE>=12)\r
@@ -583,6 +619,7 @@ InitBackEnd1()
     ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options\r
 \r
     GetTimeMark(&programStartTime);\r
+    srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level\r
 \r
     ClearProgramStats();\r
     programStats.ok_to_send = 1;\r
@@ -632,7 +669,7 @@ InitBackEnd1()
     if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,\r
                          appData.movesPerSession)) {\r
        char buf[MSG_SIZ];\r
-       sprintf(buf, "bad timeControl option %s", appData.timeControl);\r
+       snprintf(buf, sizeof(buf), _("bad timeControl option %s"), appData.timeControl);\r
        DisplayFatalError(buf, 0, 2);\r
     }\r
 \r
@@ -647,7 +684,7 @@ InitBackEnd1()
            searchTime = min * 60 + sec;\r
        } else {\r
            char buf[MSG_SIZ];\r
-           sprintf(buf, "bad searchTime option %s", appData.searchTime);\r
+           snprintf(buf, sizeof(buf), _("bad searchTime option %s"), appData.searchTime);\r
            DisplayFatalError(buf, 0, 2);\r
        }\r
     }\r
@@ -712,6 +749,8 @@ InitBackEnd1()
     first.useFEN960 = FALSE; second.useFEN960 = FALSE;\r
     first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
     /* End of new features added by Tord. */\r
+    first.fenOverride  = appData.fenOverride1;\r
+    second.fenOverride = appData.fenOverride2;\r
 \r
     /* [HGM] time odds: set factor for each machine */\r
     first.timeOdds  = appData.firstTimeOdds;\r
@@ -734,6 +773,10 @@ InitBackEnd1()
     first.debug = second.debug = FALSE;\r
     first.supportsNPS = second.supportsNPS = UNKNOWN;\r
 \r
+    /* [HGM] options */\r
+    first.optionSettings  = appData.firstOptions;\r
+    second.optionSettings = appData.secondOptions;\r
+\r
     first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
     second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
     first.isUCI = appData.firstIsUCI; /* [AS] */\r
@@ -744,7 +787,7 @@ InitBackEnd1()
     if (appData.firstProtocolVersion > PROTOVER ||\r
        appData.firstProtocolVersion < 1) {\r
       char buf[MSG_SIZ];\r
-      sprintf(buf, "protocol version %d not supported",\r
+      sprintf(buf, _("protocol version %d not supported"),\r
              appData.firstProtocolVersion);\r
       DisplayFatalError(buf, 0, 2);\r
     } else {\r
@@ -754,7 +797,7 @@ InitBackEnd1()
     if (appData.secondProtocolVersion > PROTOVER ||\r
        appData.secondProtocolVersion < 1) {\r
       char buf[MSG_SIZ];\r
-      sprintf(buf, "protocol version %d not supported",\r
+      sprintf(buf, _("protocol version %d not supported"),\r
              appData.secondProtocolVersion);\r
       DisplayFatalError(buf, 0, 2);\r
     } else {\r
@@ -788,7 +831,7 @@ InitBackEnd1()
        q = first.program;\r
        while (*q != ' ' && *q != NULLCHAR) q++;\r
        p = q;\r
-       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] bckslash added */\r
+       while (p > first.program && *(p-1) != '/' && *(p-1) != '\\') p--; /* [HGM] backslash added */\r
        programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)\r
                                        + strlen(PATCHLEVEL) + (q - p));\r
        sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);\r
@@ -812,7 +855,7 @@ InitBackEnd1()
       case VariantBughouse:     /* need four players and two boards */\r
       case VariantKriegspiel:   /* need to hide pieces and move details */\r
       /* case VariantFischeRandom: (Fabien: moved below) */\r
-       sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);\r
+       sprintf(buf, _("Variant %s supported only in ICS mode"), appData.variant);\r
        DisplayFatalError(buf, 0, 2);\r
        return;\r
 \r
@@ -827,7 +870,7 @@ InitBackEnd1()
       case Variant35:\r
       case Variant36:\r
       default:\r
-       sprintf(buf, "Unknown variant name %s", appData.variant);\r
+       sprintf(buf, _("Unknown variant name %s"), appData.variant);\r
        DisplayFatalError(buf, 0, 2);\r
        return;\r
 \r
@@ -860,6 +903,7 @@ InitBackEnd1()
       case VariantCapaRandom: /* should work */\r
       case VariantJanus:      /* should work */\r
       case VariantSuper:      /* experimental */\r
+      case VariantGreat:      /* experimental, requires legality testing to be off */\r
        break;\r
       }\r
     }\r
@@ -1054,7 +1098,7 @@ InitBackEnd2()
     } else {\r
       /* kludge: allow timeout for initial "feature" commands */\r
       FreezeUI();\r
-      DisplayMessage("", "Starting chess program");\r
+      DisplayMessage("", _("Starting chess program"));\r
       ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);\r
     }\r
 }\r
@@ -1068,14 +1112,19 @@ InitBackEnd3 P((void))
 \r
     InitChessProgram(&first, startedFromSetupPosition);\r
 \r
+\r
     if (appData.icsActive) {\r
+#ifdef WIN32\r
+        /* [DM] Make a console window if needed [HGM] merged ifs */\r
+        ConsoleCreate(); \r
+#endif\r
        err = establish();\r
        if (err != 0) {\r
            if (*appData.icsCommPort != NULLCHAR) {\r
-               sprintf(buf, "Could not open comm port %s",  \r
+               sprintf(buf, _("Could not open comm port %s"),  \r
                        appData.icsCommPort);\r
            } else {\r
-               sprintf(buf, "Could not connect to host %s, port %s",  \r
+               snprintf(buf, sizeof(buf), _("Could not connect to host %s, port %s"),  \r
                        appData.icsHost, appData.icsPort);\r
            }\r
            DisplayFatalError(buf, err, 1);\r
@@ -1120,7 +1169,7 @@ InitBackEnd3 P((void))
     } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {\r
       initialMode = Training;\r
     } else {\r
-      sprintf(buf, "Unknown initialMode %s", appData.initialMode);\r
+      sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);\r
       DisplayFatalError(buf, 0, 2);\r
       return;\r
     }\r
@@ -1128,7 +1177,7 @@ InitBackEnd3 P((void))
     if (appData.matchMode) {\r
        /* Set up machine vs. machine match */\r
        if (appData.noChessProgram) {\r
-           DisplayFatalError("Can't have a match with no chess programs",\r
+           DisplayFatalError(_("Can't have a match with no chess programs"),\r
                              0, 2);\r
            return;\r
        }\r
@@ -1140,7 +1189,7 @@ InitBackEnd3 P((void))
            if (!LoadGameFromFile(appData.loadGameFile,\r
                                  index,\r
                                  appData.loadGameFile, FALSE)) {\r
-               DisplayFatalError("Bad game file", 0, 1);\r
+               DisplayFatalError(_("Bad game file"), 0, 1);\r
                return;\r
            }\r
        } else if (*appData.loadPositionFile != NULLCHAR) {\r
@@ -1149,7 +1198,7 @@ InitBackEnd3 P((void))
            if (!LoadPositionFromFile(appData.loadPositionFile,\r
                                      index,\r
                                      appData.loadPositionFile)) {\r
-               DisplayFatalError("Bad position file", 0, 1);\r
+               DisplayFatalError(_("Bad position file"), 0, 1);\r
                return;\r
            }\r
        }\r
@@ -1161,7 +1210,7 @@ InitBackEnd3 P((void))
        /* Set up other modes */\r
        if (initialMode == AnalyzeFile) {\r
          if (*appData.loadGameFile == NULLCHAR) {\r
-           DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);\r
+           DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);\r
            return;\r
          }\r
        }\r
@@ -1189,11 +1238,11 @@ InitBackEnd3 P((void))
        }\r
        if (initialMode == AnalyzeMode) {\r
          if (appData.noChessProgram) {\r
-           DisplayFatalError("Analysis mode requires a chess engine", 0, 2);\r
+           DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("Analysis mode does not work with ICS mode",0,2);\r
+           DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);\r
            return;\r
          }\r
          AnalyzeModeEvent();\r
@@ -1204,36 +1253,36 @@ InitBackEnd3 P((void))
          AnalysisPeriodicEvent(1);\r
        } else if (initialMode == MachinePlaysWhite) {\r
          if (appData.noChessProgram) {\r
-           DisplayFatalError("MachineWhite mode requires a chess engine",\r
+           DisplayFatalError(_("MachineWhite mode requires a chess engine"),\r
                              0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("MachineWhite mode does not work with ICS mode",\r
+           DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),\r
                              0, 2);\r
            return;\r
          }\r
          MachineWhiteEvent();\r
        } else if (initialMode == MachinePlaysBlack) {\r
          if (appData.noChessProgram) {\r
-           DisplayFatalError("MachineBlack mode requires a chess engine",\r
+           DisplayFatalError(_("MachineBlack mode requires a chess engine"),\r
                              0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("MachineBlack mode does not work with ICS mode",\r
+           DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),\r
                              0, 2);\r
            return;\r
          }\r
          MachineBlackEvent();\r
        } else if (initialMode == TwoMachinesPlay) {\r
          if (appData.noChessProgram) {\r
-           DisplayFatalError("TwoMachines mode requires a chess engine",\r
+           DisplayFatalError(_("TwoMachines mode requires a chess engine"),\r
                              0, 2);\r
            return;\r
          }\r
          if (appData.icsActive) {\r
-           DisplayFatalError("TwoMachines mode does not work with ICS mode",\r
+           DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),\r
                              0, 2);\r
            return;\r
          }\r
@@ -1244,7 +1293,7 @@ InitBackEnd3 P((void))
          EditPositionEvent();\r
        } else if (initialMode == Training) {\r
          if (*appData.loadGameFile == NULLCHAR) {\r
-           DisplayFatalError("Training mode requires a game file", 0, 2);\r
+           DisplayFatalError(_("Training mode requires a game file"), 0, 2);\r
            return;\r
          }\r
          TrainingEvent();\r
@@ -1270,18 +1319,18 @@ establish()
     } else if (*appData.gateway != NULLCHAR) {\r
        if (*appData.remoteShell == NULLCHAR) {\r
            /* Use the rcmd protocol to run telnet program on a gateway host */\r
-           sprintf(buf, "%s %s %s",\r
+           snprintf(buf, sizeof(buf), "%s %s %s",\r
                    appData.telnetProgram, appData.icsHost, appData.icsPort);\r
            return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);\r
 \r
        } else {\r
            /* Use the rsh program to run telnet program on a gateway host */\r
            if (*appData.remoteUser == NULLCHAR) {\r
-               sprintf(buf, "%s %s %s %s %s", appData.remoteShell,\r
+               snprintf(buf, sizeof(buf), "%s %s %s %s %s", appData.remoteShell,\r
                        appData.gateway, appData.telnetProgram,\r
                        appData.icsHost, appData.icsPort);\r
            } else {\r
-               sprintf(buf, "%s %s -l %s %s %s %s",\r
+               snprintf(buf, sizeof(buf), "%s %s -l %s %s %s %s",\r
                        appData.remoteShell, appData.gateway, \r
                        appData.remoteUser, appData.telnetProgram,\r
                        appData.icsHost, appData.icsPort);\r
@@ -1392,14 +1441,14 @@ read_from_player(isr, closure, message, count, error)
        gotEof = 0;\r
        outCount = OutputMaybeTelnet(icsPR, message, count, &outError);\r
        if (outCount < count) {\r
-            DisplayFatalError("Error writing to ICS", outError, 1);\r
+            DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
        }\r
     } else if (count < 0) {\r
        RemoveInputSource(isr);\r
-       DisplayFatalError("Error reading from keyboard", error, 1);\r
+       DisplayFatalError(_("Error reading from keyboard"), error, 1);\r
     } else if (gotEof++ > 0) {\r
        RemoveInputSource(isr);\r
-       DisplayFatalError("Got end of file from keyboard", 0, 0);\r
+       DisplayFatalError(_("Got end of file from keyboard"), 0, 0);\r
     }\r
 }\r
 \r
@@ -1414,7 +1463,7 @@ SendToICS(s)
     count = strlen(s);\r
     outCount = OutputMaybeTelnet(icsPR, s, count, &outError);\r
     if (outCount < count) {\r
-       DisplayFatalError("Error writing to ICS", outError, 1);\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
     }\r
 }\r
 \r
@@ -1439,7 +1488,7 @@ SendToICSDelayed(s,msdelay)
     outCount = OutputToProcessDelayed(icsPR, s, count, &outError,\r
                                      msdelay);\r
     if (outCount < count) {\r
-       DisplayFatalError("Error writing to ICS", outError, 1);\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
     }\r
 }\r
 \r
@@ -1531,7 +1580,8 @@ StringToVariant(e)
 \r
     if (!found) {\r
       if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))\r
-         || StrCaseStr(e, "wild/fr")) {\r
+         || StrCaseStr(e, "wild/fr") \r
+         || StrCaseStr(e, "frc") || StrCaseStr(e, "960")) {\r
         v = VariantFischeRandom;\r
       } else if ((i = 4, p = StrCaseStr(e, "wild")) ||\r
                 (i = 1, p = StrCaseStr(e, "w"))) {\r
@@ -1673,13 +1723,16 @@ StringToVariant(e)
         case 49:\r
           v = VariantSuper;\r
          break;\r
+        case 50:\r
+          v = VariantGreat;\r
+         break;\r
        case -1:\r
          /* Found "wild" or "w" in the string but no number;\r
             must assume it's normal chess. */\r
          v = VariantNormal;\r
          break;\r
        default:\r
-         sprintf(buf, "Unknown wild type %d", wnum);\r
+         sprintf(buf, _("Unknown wild type %d"), wnum);\r
          DisplayError(buf, 0);\r
          v = VariantUnknown;\r
          break;\r
@@ -1687,7 +1740,7 @@ StringToVariant(e)
       }\r
     }\r
     if (appData.debugMode) {\r
-      fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",\r
+      fprintf(debugFP, _("recognized '%s' (%d) as variant %s\n"),\r
              e, wnum, VariantName(v));\r
     }\r
     return v;\r
@@ -1752,7 +1805,7 @@ SendToPlayer(data, length)
     int error, outCount;\r
     outCount = OutputToProcess(NoProc, data, length, &error);\r
     if (outCount < length) {\r
-       DisplayFatalError("Error writing to display", error, 1);\r
+       DisplayFatalError(_("Error writing to display"), error, 1);\r
     }\r
 }\r
 \r
@@ -1838,7 +1891,7 @@ TelnetRequest(ddww, option)
     msg[2] = option;\r
     outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);\r
     if (outCount < 3) {\r
-       DisplayFatalError("Error writing to ICS", outError, 1);\r
+       DisplayFatalError(_("Error writing to ICS"), outError, 1);\r
     }\r
 }\r
 \r
@@ -1889,7 +1942,7 @@ CopyHoldings(Board board, char *holdings, ChessSquare lowestPiece)
         j = PieceToNumber(piece);\r
         if(j >= gameInfo.holdingsSize) continue; /* ignore pieces that do not fit */\r
         if(j < 0) continue;               /* should not happen */\r
-        piece = (ChessSquare) ( j + (int)lowestPiece );\r
+        piece = (ChessSquare) ( (int)piece + (int)lowestPiece );\r
         board[holdingsStartRow+j*direction][holdingsColumn] = piece;\r
         board[holdingsStartRow+j*direction][countsColumn]++;\r
     }\r
@@ -1902,7 +1955,7 @@ VariantSwitch(Board board, VariantClass newVariant)
 {\r
    int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;\r
    int oldCurrentMove = currentMove, oldForwardMostMove = forwardMostMove, oldBackwardMostMove = backwardMostMove;\r
-   Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;\r
+//   Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;\r
 \r
    startedFromPositionFile = FALSE;\r
    if(gameInfo.variant == newVariant) return;\r
@@ -1996,6 +2049,7 @@ static int player2Rating = -1;
 /*----------------------------*/\r
 \r
 ColorClass curColor = ColorNormal;\r
+int suppressKibitz = 0;\r
 \r
 void\r
 read_from_ics(isr, closure, data, count, error)\r
@@ -2027,9 +2081,9 @@ read_from_ics(isr, closure, data, count, error)
     int buf_len;\r
     int next_out;\r
     int tkind;\r
+    int backup;    /* [DM] For zippy color lines */\r
     char *p;\r
 \r
-#ifdef WIN32\r
     if (appData.debugMode) {\r
       if (!error) {\r
        fprintf(debugFP, "<ICS: ");\r
@@ -2037,7 +2091,6 @@ read_from_ics(isr, closure, data, count, error)
        fprintf(debugFP, "\n");\r
       }\r
     }\r
-#endif\r
 \r
     if (appData.debugMode) { int f = forwardMostMove;\r
         fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,\r
@@ -2208,7 +2261,34 @@ read_from_ics(isr, closure, data, count, error)
                parse[parse_pos++] = buf[i];\r
                if (buf[i] == '\n') {\r
                    parse[parse_pos] = NULLCHAR;\r
-                   AppendComment(forwardMostMove, StripHighlight(parse));\r
+                   if(!suppressKibitz) // [HGM] kibitz\r
+                       AppendComment(forwardMostMove, StripHighlight(parse));\r
+                   else { // [HGM kibitz: divert memorized engine kibitz to engine-output window\r
+                       int nrDigit = 0, nrAlph = 0, i;\r
+                       if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input\r
+                       { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }\r
+                       parse[parse_pos] = NULLCHAR;\r
+                       // try to be smart: if it does not look like search info, it should go to\r
+                       // ICS interaction window after all, not to engine-output window.\r
+                       for(i=0; i<parse_pos; i++) { // count letters and digits\r
+                           nrDigit += (parse[i] >= '0' && parse[i] <= '9');\r
+                           nrAlph  += (parse[i] >= 'a' && parse[i] <= 'z');\r
+                           nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');\r
+                       }\r
+                       if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info\r
+                           int depth=0; float score;\r
+                           if(sscanf(parse, "!!! %f/%d", &score, &depth) == 2 && depth>0) {\r
+                               // [HGM] kibitz: save kibitzed opponent info for PGN and eval graph\r
+                               pvInfoList[forwardMostMove-1].depth = depth;\r
+                               pvInfoList[forwardMostMove-1].score = 100*score;\r
+                           }\r
+                           OutputKibitz(suppressKibitz, parse);\r
+                       } else {\r
+                           char tmp[MSG_SIZ];\r
+                           sprintf(tmp, _("your opponent kibitzes: %s"), parse);\r
+                           SendToPlayer(tmp, strlen(tmp));\r
+                       }\r
+                   }\r
                    started = STARTED_NONE;\r
                } else {\r
                    /* Don't match patterns against characters in chatter */\r
@@ -2265,7 +2345,7 @@ read_from_ics(isr, closure, data, count, error)
 \r
            if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {\r
              char buf[MSG_SIZ];\r
-             sprintf(buf, "%s@%s", ics_handle, appData.icsHost);\r
+             snprintf(buf, sizeof(buf), "%s@%s", ics_handle, appData.icsHost);\r
              DisplayIcsInteractionTitle(buf);\r
              have_set_title = TRUE;\r
            }\r
@@ -2289,17 +2369,53 @@ read_from_ics(isr, closure, data, count, error)
            }\r
 \r
            oldi = i;\r
+           // [HGM] kibitz: try to recognize opponent engine-score kibitzes, to divert them to engine-output window\r
+           if (appData.autoKibitz && started == STARTED_NONE && \r
+                !appData.icsEngineAnalyze &&                     // [HGM] [DM] ICS analyze\r
+               (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack || gameMode == IcsObserving)) {\r
+               if(looking_at(buf, &i, "* kibitzes: ") &&\r
+                  (StrStr(star_match[0], gameInfo.white) == star_match[0] || \r
+                   StrStr(star_match[0], gameInfo.black) == star_match[0]   )) { // kibitz of self or opponent\r
+                       suppressKibitz = TRUE;\r
+                       if((StrStr(star_match[0], gameInfo.white) == star_match[0]\r
+                               && (gameMode == IcsPlayingWhite)) ||\r
+                          (StrStr(star_match[0], gameInfo.black) == star_match[0]\r
+                               && (gameMode == IcsPlayingBlack))   ) // opponent kibitz\r
+                           started = STARTED_CHATTER; // own kibitz we simply discard\r
+                       else {\r
+                           started = STARTED_COMMENT; // make sure it will be collected in parse[]\r
+                           parse_pos = 0; parse[0] = NULLCHAR;\r
+                           savingComment = TRUE;\r
+                           suppressKibitz = gameMode != IcsObserving ? 2 :\r
+                               (StrStr(star_match[0], gameInfo.white) == NULL) + 1;\r
+                       } \r
+                       continue;\r
+               } else\r
+               if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz\r
+                   started = STARTED_CHATTER;\r
+                   suppressKibitz = TRUE;\r
+               }\r
+           } // [HGM] kibitz: end of patch\r
+\r
            if (appData.zippyTalk || appData.zippyPlay) {\r
+                /* [DM] Backup address for color zippy lines */\r
+                backup = i;\r
 #if ZIPPY\r
-               if (ZippyControl(buf, &i) ||\r
-                   ZippyConverse(buf, &i) ||\r
-                   (appData.zippyPlay && ZippyMatch(buf, &i))) {\r
-                   loggedOn = TRUE;\r
-                   continue;\r
+       #ifdef WIN32\r
+               if (loggedOn == TRUE)\r
+                       if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) ||\r
+                          (appData.zippyPlay && ZippyMatch(buf, &backup)));\r
+       #else\r
+                if (ZippyControl(buf, &i) ||\r
+                    ZippyConverse(buf, &i) ||\r
+                    (appData.zippyPlay && ZippyMatch(buf, &i))) {\r
+                     loggedOn = TRUE;\r
+                      if (!appData.colorize) continue;\r
                }\r
+       #endif\r
 #endif\r
-           } else {\r
-               if (/* Don't color "message" or "messages" output */\r
+           } // [DM] 'else { ' deleted\r
+               if (/* Don't color "message" or "messages" output */\r
                    (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||\r
                    looking_at(buf, &i, "*. * at *:*: ") ||\r
                    looking_at(buf, &i, "--* (*:*): ") ||\r
@@ -2454,7 +2570,6 @@ read_from_ics(isr, closure, data, count, error)
                        curColor = ColorSeek;\r
                    }\r
                    continue;\r
-               }\r
            }\r
 \r
            if (looking_at(buf, &i, "\\   ")) {\r
@@ -2565,7 +2680,7 @@ read_from_ics(isr, closure, data, count, error)
                  case H_GOT_UNWANTED_HEADER:\r
                  case H_GETTING_MOVES:\r
                    /* Should not happen */\r
-                   DisplayError("Error gathering move list: two headers", 0);\r
+                   DisplayError(_("Error gathering move list: two headers"), 0);\r
                    ics_getting_history = H_FALSE;\r
                    break;\r
                }\r
@@ -2579,7 +2694,7 @@ read_from_ics(isr, closure, data, count, error)
                    gameInfo.whiteRating = string_to_rating(star_match[1]);\r
                    gameInfo.blackRating = string_to_rating(star_match[3]);\r
                    if (appData.debugMode)\r
-                     fprintf(debugFP, "Ratings from header: W %d, B %d\n", \r
+                     fprintf(debugFP, _("Ratings from header: W %d, B %d\n"), \r
                              gameInfo.whiteRating, gameInfo.blackRating);\r
                }\r
                continue;\r
@@ -2612,7 +2727,7 @@ read_from_ics(isr, closure, data, count, error)
                    break;\r
                  case H_GETTING_MOVES:\r
                    /* Should not happen */\r
-                   DisplayError("Error gathering move list: nested", 0);\r
+                   DisplayError(_("Error gathering move list: nested"), 0);\r
                    ics_getting_history = H_FALSE;\r
                    break;\r
                  case H_GOT_REQ_HEADER:\r
@@ -2637,7 +2752,7 @@ read_from_ics(isr, closure, data, count, error)
            \r
            if (looking_at(buf, &i, "% ") ||\r
                ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)\r
-                && looking_at(buf, &i, "}*"))) {\r
+                && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book\r
                savingComment = FALSE;\r
                switch (started) {\r
                  case STARTED_MOVES:\r
@@ -2656,11 +2771,18 @@ read_from_ics(isr, closure, data, count, error)
                                  }\r
                                  SendTimeRemaining(&first, TRUE);\r
                                }\r
+#if 0\r
                                if (first.useColors) {\r
                                  SendToProgram("white\ngo\n", &first);\r
                                } else {\r
                                  SendToProgram("go\n", &first);\r
                                }\r
+#else\r
+                               if (first.useColors) {\r
+                                 SendToProgram("white\n", &first); // [HGM] book: made sending of "go\n" book dependent\r
+                               }\r
+                               bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: probe book for initial pos\r
+#endif\r
                                first.maybeThinking = TRUE;\r
                            } else {\r
                                if (first.usePlayother) {\r
@@ -2681,11 +2803,18 @@ read_from_ics(isr, closure, data, count, error)
                                  }\r
                                  SendTimeRemaining(&first, FALSE);\r
                                }\r
+#if 0\r
                                if (first.useColors) {\r
                                  SendToProgram("black\ngo\n", &first);\r
                                } else {\r
                                  SendToProgram("go\n", &first);\r
                                }\r
+#else\r
+                               if (first.useColors) {\r
+                                 SendToProgram("black\n", &first);\r
+                               }\r
+                               bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE);\r
+#endif\r
                                first.maybeThinking = TRUE;\r
                            } else {\r
                                if (first.usePlayother) {\r
@@ -2738,6 +2867,17 @@ read_from_ics(isr, closure, data, count, error)
                  default:\r
                    break;\r
                }\r
+               if(bookHit) { // [HGM] book: simulate book reply\r
+                   static char bookMove[MSG_SIZ]; // a bit generous?\r
+\r
+                   programStats.nodes = programStats.depth = programStats.time = \r
+                   programStats.score = programStats.got_only_move = 0;\r
+                   sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
+\r
+                   strcpy(bookMove, "move ");\r
+                   strcat(bookMove, bookHit);\r
+                   HandleMachineMove(bookMove, &first);\r
+               }\r
                continue;\r
            }\r
            \r
@@ -2824,7 +2964,7 @@ read_from_ics(isr, closure, data, count, error)
                    if (forwardMostMove > backwardMostMove) {\r
                        currentMove = --forwardMostMove;\r
                        DisplayMove(currentMove - 1); /* before DMError */\r
-                       DisplayMoveError("Illegal move (rejected by ICS)");\r
+                       DisplayMoveError(_("Illegal move (rejected by ICS)"));\r
                        DrawPosition(FALSE, boards[currentMove]);\r
                        SwitchClocks();\r
                        DisplayBothClocks();\r
@@ -2979,6 +3119,11 @@ read_from_ics(isr, closure, data, count, error)
                if (gameMode == IcsObserving &&\r
                    atoi(star_match[0]) == ics_gamenum)\r
                  {\r
+                      /* icsEngineAnalyze */\r
+                      if (appData.icsEngineAnalyze) {\r
+                            ExitAnalyzeMode();\r
+                            ModeHighlight();\r
+                      }\r
                      StopClocks();\r
                      gameMode = IcsIdle;\r
                      ics_gamenum = -1;\r
@@ -3120,11 +3265,12 @@ read_from_ics(isr, closure, data, count, error)
            i++;                /* skip unparsed character and loop back */\r
        }\r
        \r
-       if (started != STARTED_MOVES && started != STARTED_BOARD &&\r
+       if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window\r
            started != STARTED_HOLDINGS && i > next_out) {\r
            SendToPlayer(&buf[next_out], i - next_out);\r
            next_out = i;\r
        }\r
+       suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above\r
        \r
        leftover_len = buf_len - leftover_start;\r
        /* if buffer ends with something we couldn't parse,\r
@@ -3132,9 +3278,9 @@ read_from_ics(isr, closure, data, count, error)
        \r
     } else if (count == 0) {\r
        RemoveInputSource(isr);\r
-        DisplayFatalError("Connection closed by ICS", 0, 0);\r
+        DisplayFatalError(_("Connection closed by ICS"), 0, 0);\r
     } else {\r
-       DisplayFatalError("Error reading from ICS", error, 1);\r
+       DisplayFatalError(_("Error reading from ICS"), error, 1);\r
     }\r
 }\r
 \r
@@ -3164,8 +3310,8 @@ ParseBoard12(string)
      char *string;\r
 { \r
     GameMode newGameMode;\r
-    int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;\r
-    int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;\r
+    int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;\r
+    int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;\r
     int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;\r
     char to_play, board_chars[200];\r
     char move_str[500], str[500], elapsed_time[500];\r
@@ -3184,7 +3330,7 @@ ParseBoard12(string)
     newGame = FALSE;\r
 \r
     if (appData.debugMode)\r
-      fprintf(debugFP, "Parsing board: %s\n", string);\r
+      fprintf(debugFP, _("Parsing board: %s\n"), string);\r
 \r
     move_str[0] = NULLCHAR;\r
     elapsed_time[0] = NULLCHAR;\r
@@ -3207,7 +3353,7 @@ ParseBoard12(string)
               &ticking);\r
 \r
     if (n < 21) {\r
-       sprintf(str, "Failed to parse board string:\n\"%s\"", string);\r
+        snprintf(str, sizeof(str), _("Failed to parse board string:\n\"%s\""), string);\r
        DisplayError(str, 0);\r
        return;\r
     }\r
@@ -3216,7 +3362,7 @@ ParseBoard12(string)
     moveNum = (moveNum - 1) * 2;\r
     if (to_play == 'B') moveNum++;\r
     if (moveNum >= MAX_MOVES) {\r
-      DisplayFatalError("Game too long; increase MAX_MOVES and recompile",\r
+      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),\r
                        0, 1);\r
       return;\r
     }\r
@@ -3278,7 +3424,7 @@ ParseBoard12(string)
        return;\r
       case H_GETTING_MOVES:\r
        /* Should not happen */\r
-       DisplayError("Error gathering move list: extra board", 0);\r
+       DisplayError(_("Error gathering move list: extra board"), 0);\r
        ics_getting_history = H_FALSE;\r
        return;\r
     }\r
@@ -3479,6 +3625,16 @@ ParseBoard12(string)
     \r
     /* Update currentMove and known move number limits */\r
     newMove = newGame || moveNum > forwardMostMove;\r
+\r
+    /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */\r
+    if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) {\r
+        takeback = forwardMostMove - moveNum;\r
+        for (i = 0; i < takeback; i++) {\r
+             if (appData.debugMode) fprintf(debugFP, "take back move\n");\r
+             SendToProgram("undo\n", &first);\r
+        }\r
+    }\r
+\r
     if (newGame) {\r
        forwardMostMove = backwardMostMove = currentMove = moveNum;\r
        if (gameMode == IcsExamining && moveNum == 0) {\r
@@ -3548,8 +3704,37 @@ ParseBoard12(string)
            strcat(parseList[moveNum - 1], " ");\r
            strcat(parseList[moveNum - 1], elapsed_time);\r
            moveList[moveNum - 1][0] = NULLCHAR;\r
-       } else if (ParseOneMove(move_str, moveNum - 1, &moveType,\r
-                               &fromX, &fromY, &toX, &toY, &promoChar)) {\r
+       } else if (strcmp(move_str, "none") == 0) {\r
+           // [HGM] long SAN: swapped order; test for 'none' before parsing move\r
+           /* Again, we don't know what the board looked like;\r
+              this is really the start of the game. */\r
+           parseList[moveNum - 1][0] = NULLCHAR;\r
+           moveList[moveNum - 1][0] = NULLCHAR;\r
+           backwardMostMove = moveNum;\r
+           startedFromSetupPosition = TRUE;\r
+           fromX = fromY = toX = toY = -1;\r
+       } else {\r
+         // [HGM] long SAN: if legality-testing is off, disambiguation might not work or give wrong move. \r
+         //                 So we parse the long-algebraic move string in stead of the SAN move\r
+         int valid; char buf[MSG_SIZ], *prom;\r
+\r
+         // str looks something like "Q/a1-a2"; kill the slash\r
+         if(str[1] == '/') \r
+               sprintf(buf, "%c%s", str[0], str+2);\r
+         else  strcpy(buf, str); // might be castling\r
+         if((prom = strstr(move_str, "=")) && !strstr(buf, "=")) \r
+               strcat(buf, prom); // long move lacks promo specification!\r
+         if(!appData.testLegality && move_str[1] != '@') { // drops never ambiguous (parser chokes on long form!)\r
+               if(appData.debugMode) \r
+                       fprintf(debugFP, "replaced ICS move '%s' by '%s'\n", move_str, buf);\r
+               strcpy(move_str, buf);\r
+          }\r
+         valid = ParseOneMove(move_str, moveNum - 1, &moveType,\r
+                               &fromX, &fromY, &toX, &toY, &promoChar)\r
+              || ParseOneMove(buf, moveNum - 1, &moveType,\r
+                               &fromX, &fromY, &toX, &toY, &promoChar);\r
+         // end of long SAN patch\r
+         if (valid) {\r
            (void) CoordsToAlgebraic(boards[moveNum - 1],\r
                                     PosFlags(moveNum - 1), EP_UNKNOWN,\r
                                     fromY, fromX, toY, toX, promoChar,\r
@@ -3565,6 +3750,7 @@ ParseBoard12(string)
                     strcat(parseList[moveNum - 1], "+");\r
                break;\r
              case MT_CHECKMATE:\r
+             case MT_STAINMATE: // [HGM] xq: for notation stalemate that wins counts as checkmate\r
                strcat(parseList[moveNum - 1], "#");\r
                break;\r
            }\r
@@ -3573,15 +3759,7 @@ ParseBoard12(string)
            /* currentMoveString is set as a side-effect of ParseOneMove */\r
            strcpy(moveList[moveNum - 1], currentMoveString);\r
            strcat(moveList[moveNum - 1], "\n");\r
-       } else if (strcmp(move_str, "none") == 0) {\r
-           /* Again, we don't know what the board looked like;\r
-              this is really the start of the game. */\r
-           parseList[moveNum - 1][0] = NULLCHAR;\r
-           moveList[moveNum - 1][0] = NULLCHAR;\r
-           backwardMostMove = moveNum;\r
-           startedFromSetupPosition = TRUE;\r
-           fromX = fromY = toX = toY = -1;\r
-       } else {\r
+         } else {\r
            /* Move from ICS was illegal!?  Punt. */\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "Illegal move from ICS '%s'\n", move_str);\r
@@ -3598,6 +3776,7 @@ ParseBoard12(string)
            strcat(parseList[moveNum - 1], elapsed_time);\r
            moveList[moveNum - 1][0] = NULLCHAR;\r
            fromX = fromY = toX = toY = -1;\r
+         }\r
        }\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "Move parsed to '%s'\n", parseList[moveNum - 1]);\r
@@ -3612,7 +3791,7 @@ ParseBoard12(string)
            if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||\r
                (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {\r
                if (moveList[moveNum - 1][0] == NULLCHAR) {\r
-                   sprintf(str, "Couldn't parse move \"%s\" from ICS",\r
+                   sprintf(str, _("Couldn't parse move \"%s\" from ICS"),\r
                            move_str);\r
                    DisplayError(str, 0);\r
                } else {\r
@@ -3634,7 +3813,7 @@ ParseBoard12(string)
                }\r
            } else if (gameMode == IcsObserving || gameMode == IcsExamining) {\r
              if (moveList[moveNum - 1][0] == NULLCHAR) {\r
-               sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);\r
+               sprintf(str, _("Couldn't parse move \"%s\" from ICS"), move_str);\r
                DisplayError(str, 0);\r
              } else {\r
                if(gameInfo.variant == currentlyInitializedVariant) // [HGM] refrain sending moves engine can't understand!\r
@@ -3722,9 +3901,9 @@ ParseBoard12(string)
     if(bookHit) { // [HGM] book: simulate book reply\r
        static char bookMove[MSG_SIZ]; // a bit generous?\r
 \r
-       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
-       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
        strcpy(bookMove, "move ");\r
        strcat(bookMove, bookHit);\r
@@ -3836,7 +4015,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
 \r
     switch (moveType) {\r
       default:\r
-       sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",\r
+       sprintf(user_move, _("say Internal error; bad moveType %d (%d,%d-%d,%d)"),\r
                (int)moveType, fromX, fromY, toX, toY);\r
        DisplayError(user_move + strlen("say "), 0);\r
        break;\r
@@ -3874,10 +4053,14 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
       case BlackPromotionChancellor:\r
       case WhitePromotionArchbishop:\r
       case BlackPromotionArchbishop:\r
-        if(gameInfo.variant == VariantShatranj)\r
+        if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier)\r
             sprintf(user_move, "%c%c%c%c=%c\n",\r
                 AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
                PieceToChar(WhiteFerz));\r
+        else if(gameInfo.variant == VariantGreat)\r
+            sprintf(user_move, "%c%c%c%c=%c\n",\r
+                AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+               PieceToChar(WhiteMan));\r
         else\r
             sprintf(user_move, "%c%c%c%c=%c\n",\r
                 AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
@@ -3938,7 +4121,7 @@ ProcessICSInitScript(f)
 void\r
 AlphaRank(char *move, int n)\r
 {\r
-    char *p = move, c; int x, y;\r
+//    char *p = move, c; int x, y;\r
 \r
     if (appData.debugMode) {\r
         fprintf(debugFP, "alphaRank(%s,%d)\n", move, n);\r
@@ -4082,106 +4265,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     }\r
 }\r
 \r
-/* [AS] FRC game initialization */\r
-static int FindEmptySquare( Board board, int n )\r
-{\r
-    int i = 0;\r
-\r
-    while( 1 ) {\r
-        while( board[0][i] != EmptySquare ) i++;\r
-        if( n == 0 )\r
-            break;\r
-        n--;\r
-        i++;\r
-    }\r
-\r
-    return i;\r
-}\r
-\r
-#if 0\r
-static void ShuffleFRC( Board board )\r
-{\r
-    int i;\r
-\r
-    srand( time(0) );\r
-    \r
-    for( i=0; i<8; i++ ) {\r
-        board[0][i] = EmptySquare;\r
-    }\r
-\r
-    board[0][(rand() % 4)*2  ] = WhiteBishop; /* On dark square */\r
-    board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */\r
-    board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen;\r
-    board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight;\r
-    board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[1]  = initialRights[4]  =\r
-    castlingRights[0][1] = castlingRights[0][4] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
-    initialRights[2]  = initialRights[5]  =\r
-    castlingRights[0][2] = castlingRights[0][5] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[0]  = initialRights[3]  =\r
-    castlingRights[0][0] = castlingRights[0][3] = i;\r
-\r
-    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
-        board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
-    }\r
-}\r
-\r
-static unsigned char FRC_KnightTable[10] = {\r
-    0x00, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x22, 0x23, 0x33\r
-};\r
-\r
-static void SetupFRC( Board board, int pos_index )\r
-{\r
-    int i;\r
-    unsigned char knights;\r
-\r
-    /* Bring the position index into a safe range (just in case...) */\r
-    if( pos_index < 0 ) pos_index = 0;\r
-\r
-    pos_index %= 960;\r
-\r
-    /* Clear the board */\r
-    for( i=0; i<8; i++ ) {\r
-        board[0][i] = EmptySquare;\r
-    }\r
-\r
-    /* Place bishops and queen */\r
-    board[0][ (pos_index % 4)*2 + 1 ] = WhiteBishop; /* On lite square */\r
-    pos_index /= 4;\r
-    \r
-    board[0][ (pos_index % 4)*2     ] = WhiteBishop; /* On dark square */\r
-    pos_index /= 4;\r
-\r
-    board[0][ FindEmptySquare(board, pos_index % 6) ] = WhiteQueen;\r
-    pos_index /= 6;\r
-\r
-    /* Place knigths */\r
-    knights = FRC_KnightTable[ pos_index ];\r
-\r
-    board[0][ FindEmptySquare(board, knights / 16) ] = WhiteKnight;\r
-    board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;\r
-\r
-    /* Place rooks and king */\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[1]  = initialRights[4]  =\r
-    castlingRights[0][1] = castlingRights[0][4] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
-    initialRights[2]  = initialRights[5]  =\r
-    castlingRights[0][2] = castlingRights[0][5] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[0]  = initialRights[3]  =\r
-    castlingRights[0][0] = castlingRights[0][3] = i;\r
-\r
-    /* Mirror piece placement for black */\r
-    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
-        board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
-    }\r
-}\r
-#else\r
-// [HGM] shuffle: a more general way to suffle opening setups, applicable to arbitrry variants.\r
+// [HGM] shuffle: a general way to suffle opening setups, applicable to arbitrary variants.\r
 // All positions will have equal probability, but the current method will not provide a unique\r
 // numbering scheme for arrays that contain 3 or more pieces of the same kind.\r
 #define DARK 1\r
@@ -4190,7 +4274,7 @@ static void SetupFRC( Board board, int pos_index )
 \r
 int squaresLeft[4];\r
 int piecesLeft[(int)BlackPawn];\r
-long long int seed, nrOfShuffles;\r
+int seed, nrOfShuffles;\r
 \r
 void GetPositionNumber()\r
 {      // sets global variable seed\r
@@ -4198,7 +4282,6 @@ void GetPositionNumber()
 \r
        seed = appData.defaultFrcPosition;\r
        if(seed < 0) { // randomize based on time for negative FRC position numbers\r
-               srandom(time(0)); \r
                for(i=0; i<50; i++) seed += random();\r
                seed = random() ^ random() >> 8 ^ random() << 8;\r
                if(seed<0) seed = -seed;\r
@@ -4211,9 +4294,9 @@ int put(Board board, int pieceType, int rank, int n, int shade)
        int i;\r
 \r
        for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {\r
-               if( ((i-BOARD_LEFT)&1)+1 & shade && board[rank][i] == EmptySquare && n-- == 0) {\r
+               if( (((i-BOARD_LEFT)&1)+1) & shade && board[rank][i] == EmptySquare && n-- == 0) {\r
                        board[rank][i] = (ChessSquare) pieceType;\r
-                       squaresLeft[(i-BOARD_LEFT&1) + 1]--;\r
+                       squaresLeft[((i-BOARD_LEFT)&1) + 1]--;\r
                        squaresLeft[ANY]--;\r
                        piecesLeft[pieceType]--; \r
                        return i;\r
@@ -4280,7 +4363,7 @@ void SetUpShuffle(Board board, int number)
            // in variants with super-numerary Kings and Rooks, we leave these for the shuffle\r
        }\r
 \r
-       if((BOARD_RGHT-BOARD_LEFT & 1) == 0)\r
+       if(((BOARD_RGHT-BOARD_LEFT) & 1) == 0)\r
            // only for even boards make effort to put pairs of colorbound pieces on opposite colors\r
            for(p = (int) WhiteKing; p > (int) WhitePawn; p--) {\r
                if(p != (int) WhiteBishop && p != (int) WhiteFerz && p != (int) WhiteAlfil) continue;\r
@@ -4337,8 +4420,6 @@ void SetUpShuffle(Board board, int number)
        if(number >= 0) appData.defaultFrcPosition %= nrOfShuffles; // normalize\r
 }\r
 \r
-#endif\r
-\r
 int SetCharTable( char *table, const char * map )\r
 /* [HGM] moved here from winboard.c because of its general usefulness */\r
 /*       Basically a safe strcpy that uses the last character as King */\r
@@ -4456,11 +4537,6 @@ InitPosition(redraw)
       break;\r
     case VariantTwoKings:\r
       pieces = twoKingsArray;\r
-      nrCastlingRights = 8;                 /* add rights for second King */\r
-      castlingRights[0][6] = initialRights[2] = 5;\r
-      castlingRights[0][7] = initialRights[5] = 5;\r
-      castlingRank[6] = 0;\r
-      castlingRank[7] = BOARD_HEIGHT-1;\r
       break;\r
     case VariantCapaRandom:\r
       shuffleOpenings = TRUE;\r
@@ -4481,10 +4557,10 @@ InitPosition(redraw)
       nrCastlingRights = 6;\r
         castlingRights[0][0] = initialRights[0] = BOARD_RGHT-1;\r
         castlingRights[0][1] = initialRights[1] = BOARD_LEFT;\r
-        castlingRights[0][2] = initialRights[2] = BOARD_WIDTH-1>>1;\r
+        castlingRights[0][2] = initialRights[2] =(BOARD_WIDTH-1)>>1;\r
         castlingRights[0][3] = initialRights[3] = BOARD_RGHT-1;\r
         castlingRights[0][4] = initialRights[4] = BOARD_LEFT;\r
-        castlingRights[0][5] = initialRights[5] = BOARD_WIDTH-1>>1;\r
+        castlingRights[0][5] = initialRights[5] =(BOARD_WIDTH-1)>>1;\r
       break;\r
     case VariantFalcon:\r
       pieces = FalconArray;\r
@@ -4521,6 +4597,12 @@ InitPosition(redraw)
       pieces = fairyArray;\r
       SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); \r
       break;\r
+    case VariantGreat:\r
+      pieces = GreatArray;\r
+      gameInfo.boardWidth = 10;\r
+      SetCharTable(pieceToChar, "PN....E...S..HWGMKpn....e...s..hwgmk");\r
+      gameInfo.holdingsSize = 8;\r
+      break;\r
     case VariantSuper:\r
       pieces = FIDEArray;\r
       SetCharTable(pieceToChar, "PNBRQ..SE.......V.AKpnbrq..se.......v.ak");\r
@@ -4562,7 +4644,7 @@ InitPosition(redraw)
     }\r
     if(gameInfo.holdingsSize) gameInfo.holdingsWidth = 2;\r
     if(BOARD_HEIGHT > BOARD_SIZE || BOARD_WIDTH > BOARD_SIZE)\r
-        DisplayFatalError("Recompile to support this BOARD_SIZE!", 0, 2);\r
+        DisplayFatalError(_("Recompile to support this BOARD_SIZE!"), 0, 2);\r
 \r
     pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */\r
     if(pawnRow < 1) pawnRow = 1;\r
@@ -4619,6 +4701,12 @@ InitPosition(redraw)
      }\r
 \r
      if(gameInfo.variant == VariantSuper) Prelude(initialPosition);\r
+     if(gameInfo.variant == VariantGreat) { // promotion commoners\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-1] = WhiteMan;\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-2] = 9;\r
+       initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;\r
+       initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;\r
+     }\r
 #if 0\r
     if(gameInfo.variant == VariantFischeRandom) {\r
       if( appData.defaultFrcPosition < 0 ) {\r
@@ -4674,7 +4762,7 @@ SendBoard(cps, moveNum)
     char message[MSG_SIZ];\r
     \r
     if (cps->useSetboard) {\r
-      char* fen = PositionToFEN(moveNum, cps->useFEN960);\r
+      char* fen = PositionToFEN(moveNum, cps->fenOverride);\r
       sprintf(message, "setboard %s\n", fen);\r
       SendToProgram(message, cps);\r
       free(fen);\r
@@ -4822,7 +4910,7 @@ OKToStartUserMove(x, y)
       case IcsPlayingBlack:\r
        if (appData.zippyPlay) return FALSE;\r
        if (white_piece) {\r
-           DisplayMoveError("You are playing Black");\r
+           DisplayMoveError(_("You are playing Black"));\r
            return FALSE;\r
        }\r
        break;\r
@@ -4831,18 +4919,18 @@ OKToStartUserMove(x, y)
       case IcsPlayingWhite:\r
        if (appData.zippyPlay) return FALSE;\r
        if (!white_piece) {\r
-           DisplayMoveError("You are playing White");\r
+           DisplayMoveError(_("You are playing White"));\r
            return FALSE;\r
        }\r
        break;\r
 \r
       case EditGame:\r
        if (!white_piece && WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
            return FALSE;\r
        }           \r
        if (white_piece && !WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
            return FALSE;\r
        }           \r
        if (cmailMsgLoaded && (currentMove < cmailOldMove)) {\r
@@ -4862,7 +4950,7 @@ OKToStartUserMove(x, y)
        if (appData.icsActive) return FALSE;\r
        if (!appData.noChessProgram) {\r
            if (!white_piece) {\r
-               DisplayMoveError("You are playing White");\r
+               DisplayMoveError(_("You are playing White"));\r
                return FALSE;\r
            }\r
        }\r
@@ -4870,11 +4958,11 @@ OKToStartUserMove(x, y)
        \r
       case Training:\r
        if (!white_piece && WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
            return FALSE;\r
        }           \r
        if (white_piece && !WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
            return FALSE;\r
        }           \r
        break;\r
@@ -4885,7 +4973,7 @@ OKToStartUserMove(x, y)
     }\r
     if (currentMove != forwardMostMove && gameMode != AnalyzeMode\r
        && gameMode != AnalyzeFile && gameMode != Training) {\r
-       DisplayMoveError("Displayed position is not current");\r
+       DisplayMoveError(_("Displayed position is not current"));\r
        return FALSE;\r
     }\r
     return TRUE;\r
@@ -4955,7 +5043,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
       case MachinePlaysWhite:\r
        /* User is moving for Black */\r
        if (WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
             return ImpossibleMove;\r
        }\r
        break;\r
@@ -4963,7 +5051,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
       case MachinePlaysBlack:\r
        /* User is moving for White */\r
        if (!WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
             return ImpossibleMove;\r
        }\r
        break;\r
@@ -4977,13 +5065,13 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
             (int) boards[currentMove][fromY][fromX] < (int) EmptySquare) {\r
            /* User is moving for Black */\r
            if (WhiteOnMove(currentMove)) {\r
-               DisplayMoveError("It is White's turn");\r
+               DisplayMoveError(_("It is White's turn"));\r
                 return ImpossibleMove;\r
            }\r
        } else {\r
            /* User is moving for White */\r
            if (!WhiteOnMove(currentMove)) {\r
-               DisplayMoveError("It is Black's turn");\r
+               DisplayMoveError(_("It is Black's turn"));\r
                 return ImpossibleMove;\r
            }\r
        }\r
@@ -4993,7 +5081,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
        /* User is moving for Black */\r
        if (WhiteOnMove(currentMove)) {\r
            if (!appData.premove) {\r
-               DisplayMoveError("It is White's turn");\r
+               DisplayMoveError(_("It is White's turn"));\r
            } else if (toX >= 0 && toY >= 0) {\r
                premoveToX = toX;\r
                premoveToY = toY;\r
@@ -5014,7 +5102,7 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
        /* User is moving for White */\r
        if (!WhiteOnMove(currentMove)) {\r
            if (!appData.premove) {\r
-               DisplayMoveError("It is Black's turn");\r
+               DisplayMoveError(_("It is Black's turn"));\r
            } else if (toX >= 0 && toY >= 0) {\r
                premoveToX = toX;\r
                premoveToY = toY;\r
@@ -5073,11 +5161,11 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar)
     /* [HGM] but possibly ignore an IllegalMove result */\r
     if (appData.testLegality) {\r
        if (moveType == IllegalMove || moveType == ImpossibleMove) {\r
-           DisplayMoveError("Illegal move");\r
+           DisplayMoveError(_("Illegal move"));\r
             return ImpossibleMove;\r
        }\r
     }\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar);\r
     return moveType;\r
     /* [HGM] <popupFix> in stead of calling FinishMove directly, this\r
        function is made into one that returns an OK move type if FinishMove\r
@@ -5096,8 +5184,8 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
      /*char*/int promoChar;\r
 {\r
     char *bookHit = 0;\r
-\r
-    if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR) { \r
+if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", moveType, promoChar);\r
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) { \r
        // [HGM] superchess: suppress promotions to non-available piece\r
        int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
        if(WhiteOnMove(currentMove)) {\r
@@ -5111,7 +5199,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
        move type in caller when we know the move is a legal promotion */\r
     if(moveType == NormalMove && promoChar)\r
         moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", moveType, promoChar);\r
     /* [HGM] convert drag-and-drop piece drops to standard form */\r
     if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {\r
          moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;\r
@@ -5129,9 +5217,9 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
        * If they don't match, display an error message.\r
        */\r
       int saveAnimate;\r
-      Board testBoard;\r
+      Board testBoard; char testRights[BOARD_SIZE]; char testStatus;\r
       CopyBoard(testBoard, boards[currentMove]);\r
-      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard);\r
+      ApplyMove(fromX, fromY, toX, toY, promoChar, testBoard, testRights, &testStatus);\r
 \r
       if (CompareBoards(testBoard, boards[currentMove+1])) {\r
        ForwardInner(currentMove+1);\r
@@ -5150,10 +5238,10 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
          gameMode = PlayFromGameFile;\r
          ModeHighlight();\r
          SetTrainingModeOff();\r
-         DisplayInformation("End of game");\r
+         DisplayInformation(_("End of game"));\r
        }\r
       } else {\r
-       DisplayError("Incorrect move", 0);\r
+       DisplayError(_("Incorrect move"), 0);\r
       }\r
       return 1;\r
     }\r
@@ -5175,18 +5263,6 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
 \r
   MakeMove(fromX, fromY, toX, toY, promoChar); /*updates forwardMostMove*/\r
 \r
-    if(gameInfo.variant == VariantSuper && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
-       // [HGM] superchess: take promotion piece out of holdings\r
-       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
-       if(WhiteOnMove(forwardMostMove-1)) {\r
-           if(!--boards[forwardMostMove][k][BOARD_WIDTH-2])\r
-               boards[forwardMostMove][k][BOARD_WIDTH-1] = EmptySquare;\r
-       } else {\r
-           if(!--boards[forwardMostMove][BOARD_HEIGHT-1-k][1])\r
-               boards[forwardMostMove][BOARD_HEIGHT-1-k][0] = EmptySquare;\r
-       }\r
-    }\r
-\r
   if (gameMode == BeginningOfGame) {\r
     if (appData.noChessProgram) {\r
       gameMode = EditGame;\r
@@ -5206,7 +5282,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
     }\r
     ModeHighlight();\r
   }\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", moveType, promoChar);\r
   /* Relay move to ICS or chess engine */\r
   if (appData.icsActive) {\r
     if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
@@ -5240,6 +5316,7 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
     case MT_CHECK:\r
       break;\r
     case MT_CHECKMATE:\r
+    case MT_STAINMATE:\r
       if (WhiteOnMove(currentMove)) {\r
        GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
       } else {\r
@@ -5265,9 +5342,9 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
   if(bookHit) { // [HGM] book: simulate book reply\r
        static char bookMove[MSG_SIZ]; // a bit generous?\r
 \r
-       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
-       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
        strcpy(bookMove, "move ");\r
        strcat(bookMove, bookHit);\r
@@ -5291,14 +5368,14 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
        to do anything in between, can call this routine the old way. \r
     */\r
     ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar);\r
-\r
+if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar);\r
     if(moveType != ImpossibleMove)\r
         FinishMove(moveType, fromX, fromY, toX, toY, promoChar);\r
 }\r
 \r
 void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )\r
 {\r
-    char * hint = lastHint;\r
+//    char * hint = lastHint;\r
     FrontEndProgramStats stats;\r
 \r
     stats.which = cps == &first ? 0 : 1;\r
@@ -5327,7 +5404,7 @@ char *SendMoveToBookUser(int moveNr, ChessProgramState *cps, int initial)
     //first determine if the incoming move brings opponent into his book\r
     if(appData.usePolyglotBook && (cps == &first ? !appData.firstHasOwnBookUCI : !appData.secondHasOwnBookUCI))\r
        bookHit = ProbeBook(moveNr+1, appData.polyglotBook); // returns move\r
-    if(appData.debugMode && bookHit) fprintf(debugFP, "book hit = %s\n", bookHit);\r
+    if(appData.debugMode) fprintf(debugFP, "book hit = %s\n", bookHit ? bookHit : "(NULL)");\r
     if(bookHit != NULL && !cps->bookSuspend) {\r
        // make sure opponent is not going to reply after receiving move to book position\r
        SendToProgram("force\n", cps);\r
@@ -5489,11 +5566,11 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
         if (!ParseOneMove(machineMove, forwardMostMove, &moveType,\r
                               &fromX, &fromY, &toX, &toY, &promoChar)) {\r
            /* Machine move could not be parsed; ignore it. */\r
-            sprintf(buf1, "Illegal move \"%s\" from %s machine",\r
+            sprintf(buf1, _("Illegal move \"%s\" from %s machine"),\r
                    machineMove, cps->which);\r
            DisplayError(buf1, 0);\r
-            sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d%c",\r
-                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
+            sprintf(buf1, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",\r
+                    machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType);\r
            if (gameMode == TwoMachinesPlay) {\r
              GameEnds(machineWhite ? BlackWins : WhiteWins,\r
                        buf1, GE_XBOARD);\r
@@ -5524,6 +5601,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                         machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);\r
                 GameEnds(machineWhite ? BlackWins : WhiteWins,\r
                            buf1, GE_XBOARD);\r
+               return;\r
            } else if(gameInfo.variant != VariantFischeRandom && gameInfo.variant != VariantCapaRandom)\r
            /* [HGM] Kludge to handle engines that send FRC-style castling\r
               when they shouldn't (like TSCP-Gothic) */\r
@@ -5538,6 +5616,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                toX--;\r
                currentMoveString[2]--;\r
                break;\r
+            default: ; // nothing to do, but suppresses warning of pedantic compilers\r
            }\r
         }\r
        hintRequested = FALSE;\r
@@ -5553,15 +5632,15 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            first.initDone) {\r
          SendMoveToICS(moveType, fromX, fromY, toX, toY);\r
          ics_user_moved = 1;\r
-         if(appData.autoKibitz) { /* [HGM] kibitz: send most-recent PV info to ICS */\r
+         if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */\r
                char buf[3*MSG_SIZ];\r
 \r
-               sprintf(buf, "kibitz %d/%+.2f (%.2f sec, %.0f nodes, %1.0f knps) PV = %s\n",\r
-                       programStats.depth,\r
+               sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %.0f nodes, %1.0f knps) PV=%s\n",\r
                        programStats.score / 100.,\r
+                       programStats.depth,\r
                        programStats.time / 100.,\r
-                       (double) programStats.nodes,\r
-                       programStats.nodes / (10*abs(programStats.time) + 1.),\r
+                       u64ToDouble(programStats.nodes),\r
+                       u64ToDouble(programStats.nodes) / (10*abs(programStats.time) + 1.),\r
                        programStats.movelist);\r
                SendToICS(buf);\r
          }\r
@@ -5614,58 +5693,38 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        if( gameMode == TwoMachinesPlay ) {\r
          // [HGM] some adjudications useful with buggy engines\r
             int k, count = 0, epFile = epStatus[forwardMostMove]; static int bare = 1;\r
-         if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper) {\r
+         if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {\r
 \r
-            if(appData.testLegality)\r
-            // don't wait for engine to announce game end if we can judge ourselves\r
-            switch (MateTest(boards[forwardMostMove],\r
-                                 PosFlags(forwardMostMove), epFile,\r
-                                       castlingRights[forwardMostMove]) ) {\r
-             case MT_NONE:\r
-             case MT_CHECK:\r
-             default:\r
-               break;\r
-             case MT_STALEMATE:\r
-               epStatus[forwardMostMove] = EP_STALEMATE;\r
-                if(appData.checkMates) {\r
-                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                   GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate",\r
-                       GE_XBOARD );\r
-               }\r
-               break;\r
-             case MT_CHECKMATE:\r
-               epStatus[forwardMostMove] = EP_CHECKMATE;\r
-                if(appData.checkMates) {\r
-                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                   GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
-                   "Xboard adjudication: Checkmate", \r
-                   GE_XBOARD );\r
-               }\r
-               break;\r
-           }\r
 \r
            if( appData.testLegality )\r
            {   /* [HGM] Some more adjudications for obstinate engines */\r
                int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0,\r
-                    NrWQ=0, NrBQ=0, NrW=0, bishopsColor = 0,\r
-                    NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k;\r
+                    NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0,\r
+                    NrPieces=0, NrPawns=0, PawnAdvance=0, i, j;\r
                static int moveCount = 6;\r
+               ChessMove result;\r
+               char *reason = NULL;\r
 \r
-                /* First absolutely insufficient mating material. Count what is on board. */\r
+                /* Count what is on board. */\r
                for(i=0; i<BOARD_HEIGHT; i++) for(j=BOARD_LEFT; j<BOARD_RGHT; j++)\r
                {   ChessSquare p = boards[forwardMostMove][i][j];\r
                    int m=i;\r
 \r
                    switch((int) p)\r
                    {   /* count B,N,R and other of each side */\r
+                        case WhiteKing:\r
+                        case BlackKing:\r
+                            NrK++; break; // [HGM] atomic: count Kings\r
                         case WhiteKnight:\r
                              NrWN++; break;\r
                         case WhiteBishop:\r
+                        case WhiteFerz:    // [HGM] shatranj: kludge to mke it work in shatranj\r
                              bishopsColor |= 1 << ((i^j)&1);\r
                              NrWB++; break;\r
                         case BlackKnight:\r
                              NrBN++; break;\r
                         case BlackBishop:\r
+                        case BlackFerz:    // [HGM] shatranj: kludge to mke it work in shatranj\r
                              bishopsColor |= 1 << ((i^j)&1);\r
                              NrBB++; break;\r
                         case WhiteRook:\r
@@ -5685,10 +5744,115 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                     }\r
                     NrPieces += (p != EmptySquare);\r
                     NrW += ((int)p < (int)BlackPawn);\r
+                   if(gameInfo.variant == VariantXiangqi && \r
+                     (p == WhiteFerz || p == WhiteAlfil || p == BlackFerz || p == BlackAlfil)) {\r
+                       NrPieces--; // [HGM] XQ: do not count purely defensive pieces\r
+                        NrW -= ((int)p < (int)BlackPawn);\r
+                   }\r
                 }\r
 \r
-                if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2\r
-                 || NrPieces == 4 && NrBB+NrWB == NrPieces-2 && bishopsColor != 3)\r
+               /* Some material-based adjudications that have to be made before stalemate test */\r
+               if(gameInfo.variant == VariantAtomic && NrK < 2) {\r
+                   // [HGM] atomic: stm must have lost his King on previous move, as destroying own K is illegal\r
+                    epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as if stm is checkmated\r
+                    if(appData.checkMates) {\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
+                                                       "Xboard adjudication: King destroyed", GE_XBOARD );\r
+                         return;\r
+                    }\r
+               }\r
+\r
+               /* Bare King in Shatranj (loses) or Losers (wins) */\r
+                if( NrW == 1 || NrPieces - NrW == 1) {\r
+                  if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)\r
+                    epStatus[forwardMostMove] = EP_WINS;  // mark as win, so it becomes claimable\r
+                    if(appData.checkMates) {\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets to see move\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
+                         return;\r
+                    }\r
+                 } else\r
+                  if( gameInfo.variant == VariantShatranj && --bare < 0)\r
+                  {    /* bare King */\r
+                       epStatus[forwardMostMove] = EP_WINS; // make claimable as win for stm\r
+                       if(appData.checkMates) {\r
+                           /* but only adjudicate if adjudication enabled */\r
+                           SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\r
+                           ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                           GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, \r
+                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
+                           return;\r
+                       }\r
+                 }\r
+                } else bare = 1;\r
+\r
+\r
+            // don't wait for engine to announce game end if we can judge ourselves\r
+            switch (MateTest(boards[forwardMostMove], PosFlags(forwardMostMove), epFile,\r
+                                       castlingRights[forwardMostMove]) ) {\r
+             case MT_CHECK:\r
+               if(gameInfo.variant == Variant3Check) { // [HGM] 3check: when in check, test if 3rd time\r
+                   int i, checkCnt = 0;    // (should really be done by making nr of checks part of game state)\r
+                   for(i=forwardMostMove-2; i>=backwardMostMove; i-=2) {\r
+                       if(MateTest(boards[i], PosFlags(i), epStatus[i], castlingRights[i]) == MT_CHECK)\r
+                           checkCnt++;\r
+                       if(checkCnt >= 2) {\r
+                           reason = "Xboard adjudication: 3rd check";\r
+                           epStatus[forwardMostMove] = EP_CHECKMATE;\r
+                           break;\r
+                       }\r
+                   }\r
+               }\r
+             case MT_NONE:\r
+             default:\r
+               break;\r
+             case MT_STALEMATE:\r
+             case MT_STAINMATE:\r
+               reason = "Xboard adjudication: Stalemate";\r
+               if(epStatus[forwardMostMove] != EP_CHECKMATE) { // [HGM] don't touch win through baring or K-capt\r
+                   epStatus[forwardMostMove] = EP_STALEMATE;   // default result for stalemate is draw\r
+                   if(gameInfo.variant == VariantLosers  || gameInfo.variant == VariantGiveaway) // [HGM] losers:\r
+                       epStatus[forwardMostMove] = EP_WINS;    // in these variants stalemated is always a win\r
+                   else if(gameInfo.variant == VariantSuicide) // in suicide it depends\r
+                       epStatus[forwardMostMove] = NrW == NrPieces-NrW ? EP_STALEMATE :\r
+                                                  ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ?\r
+                                                                       EP_CHECKMATE : EP_WINS);\r
+                   else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)\r
+                       epStatus[forwardMostMove] = EP_CHECKMATE; // and in these variants being stalemated loses\r
+               }\r
+               break;\r
+             case MT_CHECKMATE:\r
+               reason = "Xboard adjudication: Checkmate";\r
+               epStatus[forwardMostMove] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);\r
+               break;\r
+           }\r
+\r
+               switch(i = epStatus[forwardMostMove]) {\r
+                   case EP_STALEMATE:\r
+                       result = GameIsDrawn; break;\r
+                   case EP_CHECKMATE:\r
+                       result = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins; break;\r
+                   case EP_WINS:\r
+                       result = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins; break;\r
+                   default:\r
+                       result = (ChessMove) 0;\r
+               }\r
+                if(appData.checkMates && result) { // [HGM] mates: adjudicate finished games if requested\r
+                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                   GameEnds( result, reason, GE_XBOARD );\r
+                   return;\r
+               }\r
+\r
+                /* Next absolutely insufficient mating material. */\r
+                if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && \r
+                                    gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible\r
+                       (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||\r
+                        NrPieces == NrBB+NrWB+2 && bishopsColor != 3)) // [HGM] all Bishops (Ferz!) same color\r
                 {    /* KBK, KNK, KK of KBKB with like Bishops */\r
 \r
                      /* always flag draws, for judging claims */\r
@@ -5696,25 +5860,14 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 \r
                      if(appData.materialDraws) {\r
                          /* but only adjudicate them if adjudication enabled */\r
+                        SendToProgram("force\n", cps->other); // suppress reply\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see last move */\r
                          ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                          GameEnds( GameIsDrawn, "Xboard adjudication: Insufficient mating material", GE_XBOARD );\r
                          return;\r
                      }\r
                 }\r
 \r
-               /* Shatranj baring rule */\r
-                if( gameInfo.variant == VariantShatranj && (NrW == 1 || NrPieces - NrW == 1) )\r
-                {    /* bare King */\r
-\r
-                     if(--bare < 0 && appData.checkMates) {\r
-                         /* but only adjudicate them if adjudication enabled */\r
-                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                         GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, \r
-                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
-                         return;\r
-                     }\r
-                } else bare = 1;\r
-\r
                 /* Then some trivial draws (only adjudicate, cannot be claimed) */\r
                 if(NrPieces == 4 && \r
                    (   NrWR == 1 && NrBR == 1 /* KRKR */\r
@@ -5724,6 +5877,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                   ) ) {\r
                      if(--moveCount < 0 && appData.trivialDraws)\r
                      {    /* if the first 3 moves do not show a tactical win, declare draw */\r
+                         SendToProgram("force\n", cps->other); // suppress reply\r
+                         SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                           ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                           GameEnds( GameIsDrawn, "Xboard adjudication: Trivial draw", GE_XBOARD );\r
                           return;\r
@@ -5790,7 +5945,43 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                         if( rights == 0 && ++count > appData.drawRepeats-2\r
                             && appData.drawRepeats > 1) {\r
                              /* adjudicate after user-specified nr of repeats */\r
+                            SendToProgram("force\n", cps->other); // suppress reply\r
+                            SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                              ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                            if(gameInfo.variant == VariantXiangqi && appData.testLegality) { \r
+                               // [HGM] xiangqi: check for forbidden perpetuals\r
+                               int m, ourPerpetual = 1, hisPerpetual = 1;\r
+                               for(m=forwardMostMove; m>k; m-=2) {\r
+                                   if(MateTest(boards[m], PosFlags(m), \r
+                                                       EP_NONE, castlingRights[m]) != MT_CHECK)\r
+                                       ourPerpetual = 0; // the current mover did not always check\r
+                                   if(MateTest(boards[m-1], PosFlags(m-1), \r
+                                                       EP_NONE, castlingRights[m-1]) != MT_CHECK)\r
+                                       hisPerpetual = 0; // the opponent did not always check\r
+                               }\r
+                               if(appData.debugMode) fprintf(debugFP, "XQ perpetual test, our=%d, his=%d\n",\r
+                                                                       ourPerpetual, hisPerpetual);\r
+                               if(ourPerpetual && !hisPerpetual) { // we are actively checking him: forfeit\r
+                                   GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                                          "Xboard adjudication: perpetual checking", GE_XBOARD );\r
+                                   return;\r
+                               }\r
+                               if(hisPerpetual && !ourPerpetual)   // he is checking us, but did not repeat yet\r
+                                   break; // (or we would have caught him before). Abort repetition-checking loop.\r
+                               // Now check for perpetual chases\r
+                               if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase\r
+                                   hisPerpetual = PerpetualChase(k, forwardMostMove);\r
+                                   ourPerpetual = PerpetualChase(k+1, forwardMostMove);\r
+                                   if(ourPerpetual && !hisPerpetual) { // we are actively chasing him: forfeit\r
+                                       GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                                                     "Xboard adjudication: perpetual chasing", GE_XBOARD );\r
+                                       return;\r
+                                   }\r
+                                   if(hisPerpetual && !ourPerpetual)   // he is chasing us, but did not repeat yet\r
+                                       break; // Abort repetition-checking loop.\r
+                               }\r
+                               // if neither of us is checking or chasing all the time, or both are, it is draw\r
+                            }\r
                              GameEnds( GameIsDrawn, "Xboard adjudication: repetition draw", GE_XBOARD );\r
                              return;\r
                         }\r
@@ -5812,6 +6003,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                          epStatus[forwardMostMove] = EP_RULE_DRAW;\r
                          /* this is used to judge if draw claims are legal */\r
                 if(appData.ruleMoves > 0 && count >= 2*appData.ruleMoves) {\r
+                        SendToProgram("force\n", cps->other); // suppress reply\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                          ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                          GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );\r
                          return;\r
@@ -5831,6 +6024,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                          if(epStatus[forwardMostMove] == EP_INSUF_DRAW)\r
                              p = "Draw claim: insufficient mating material";\r
                          if( p != NULL ) {\r
+                            SendToProgram("force\n", cps->other); // suppress reply\r
+                            SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                              GameEnds( GameIsDrawn, p, GE_XBOARD );\r
                              ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
                              return;\r
@@ -5838,14 +6033,15 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                 }\r
 \r
 \r
-        }\r
-\r
-        if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
-           ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+               if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {\r
+                   SendToProgram("force\n", cps->other); // suppress reply\r
+                   SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                   ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
 \r
-            GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
+                   GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );\r
 \r
-            return;\r
+                   return;\r
+               }\r
         }\r
 \r
        bookHit = NULL;\r
@@ -5893,9 +6089,9 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                strcat(bookMove, bookHit);\r
                message = bookMove;\r
                cps = cps->other;\r
-               programStats.depth = programStats.nodes = programStats.time = \r
+               programStats.nodes = programStats.depth = programStats.time = \r
                programStats.score = programStats.got_only_move = 0;\r
-               sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+               sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
                if(cps->lastPing != cps->lastPong) {\r
                    savedMessage = message; // args for deferred call\r
@@ -5928,7 +6124,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
         GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD);\r
 \r
         if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) {\r
-            DisplayError("Bad FEN received from engine", 0);\r
+            DisplayError(_("Bad FEN received from engine"), 0);\r
             return ;\r
         } else {\r
            Reset(FALSE, FALSE);\r
@@ -5958,7 +6154,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellopponent ", 13)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%ssay %s\n", ics_prefix, message + 13);\r
+         snprintf(buf1, sizeof(buf1), "%ssay %s\n", ics_prefix, message + 13);\r
          SendToICS(buf1);\r
        }\r
       } else {\r
@@ -5969,7 +6165,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellothers ", 11)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%swhisper %s\n", ics_prefix, message + 11);\r
+         snprintf(buf1, sizeof(buf1), "%swhisper %s\n", ics_prefix, message + 11);\r
          SendToICS(buf1);\r
        }\r
       }\r
@@ -5978,7 +6174,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if (!strncmp(message, "tellall ", 8)) {\r
       if (appData.icsActive) {\r
        if (loggedOn) {\r
-         sprintf(buf1, "%skibitz %s\n", ics_prefix, message + 8);\r
+         snprintf(buf1, sizeof(buf1), "%skibitz %s\n", ics_prefix, message + 8);\r
          SendToICS(buf1);\r
        }\r
       } else {\r
@@ -6070,7 +6266,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            cps->analysisSupport = FALSE;\r
            cps->analyzing = FALSE;\r
            Reset(FALSE, TRUE);\r
-           sprintf(buf2, "%s does not support analysis", cps->tidy);\r
+           sprintf(buf2, _("%s does not support analysis"), cps->tidy);\r
            DisplayError(buf2, 0);\r
            return;\r
        }\r
@@ -6116,7 +6312,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        DisplayMove(currentMove-1); /* before DisplayMoveError */\r
        SwitchClocks();\r
        DisplayBothClocks();\r
-       sprintf(buf1, "Illegal move \"%s\" (rejected by %s chess program)",\r
+       sprintf(buf1, _("Illegal move \"%s\" (rejected by %s chess program)"),\r
                parseList[currentMove], cps->which);\r
        DisplayMoveError(buf1);\r
        DrawPosition(FALSE, boards[currentMove]);\r
@@ -6148,7 +6344,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        || (StrStr(message, "Permission denied") != NULL)) {\r
 \r
        cps->maybeThinking = FALSE;\r
-       sprintf(buf1, "Failed to start %s chess program %s on %s: %s\n",\r
+       snprintf(buf1, sizeof(buf1), _("Failed to start %s chess program %s on %s: %s\n"),\r
                cps->which, cps->program, cps->host, message);\r
        RemoveInputSource(cps->isr);\r
        DisplayFatalError(buf1, 0, 1);\r
@@ -6166,12 +6362,12 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                (void) CoordsToAlgebraic(boards[forwardMostMove],\r
                                    PosFlags(forwardMostMove), EP_UNKNOWN,\r
                                    fromY, fromX, toY, toX, promoChar, buf1);\r
-               sprintf(buf2, "Hint: %s", buf1);\r
+               snprintf(buf2, sizeof(buf2), _("Hint: %s"), buf1);\r
                DisplayInformation(buf2);\r
            } else {\r
                /* Hint move could not be parsed!? */\r
-               sprintf(buf2,\r
-                       "Illegal hint move \"%s\"\nfrom %s chess program",\r
+             snprintf(buf2, sizeof(buf2),\r
+                       _("Illegal hint move \"%s\"\nfrom %s chess program"),\r
                        buf1, cps->which);\r
                DisplayError(buf2, 0);\r
            }\r
@@ -6353,10 +6549,10 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
        } else if (gameMode == MachinePlaysWhite ||\r
                   gameMode == MachinePlaysBlack) {\r
          if (userOfferedDraw) {\r
-           DisplayInformation("Machine accepts your draw offer");\r
+           DisplayInformation(_("Machine accepts your draw offer"));\r
            GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
          } else {\r
-            DisplayInformation("Machine offers a draw\nSelect Action / Draw to agree");\r
+            DisplayInformation(_("Machine offers a draw\nSelect Action / Draw to agree"));\r
          }\r
        }\r
     }\r
@@ -6370,7 +6566,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                                ) {\r
        int plylev, mvleft, mvtot, curscore, time;\r
        char mvname[MOVE_LEN];\r
-       unsigned long nodes;\r
+       u64 nodes; // [DM]\r
        char plyext;\r
        int ignore = FALSE;\r
        int prefixHint = FALSE;\r
@@ -6387,6 +6583,9 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            break;\r
          case AnalyzeMode:\r
          case AnalyzeFile:\r
+            break;\r
+          case IcsObserving: /* [DM] icsEngineAnalyze */\r
+            if (!appData.icsEngineAnalyze) ignore = TRUE;\r
            break;\r
          case TwoMachinesPlay:\r
            if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) {\r
@@ -6400,7 +6599,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 \r
        if (!ignore) {\r
            buf1[0] = NULLCHAR;\r
-           if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",\r
+           if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",\r
                       &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {\r
 \r
                if (plyext != ' ' && plyext != '\t') {\r
@@ -6424,8 +6623,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                if(cps->nps >= 0) { /* [HGM] nps: use engine nodes or time to decrement clock */\r
                        int ticklen;\r
 \r
-                       if(cps->nps == 0) ticklen = 10*time;       // use engine reported time\r
-                       else ticklen = (1000. * nodes) / cps->nps; // convert node count to time\r
+                       if(cps->nps == 0) ticklen = 10*time;                    // use engine reported time\r
+                       else ticklen = (1000. * u64ToDouble(nodes)) / cps->nps; // convert node count to time\r
                        if(WhiteOnMove(forwardMostMove)) \r
                             whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen;\r
                        else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;\r
@@ -6484,7 +6683,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                     strcat( thinkOutput, buf1 );\r
                 }\r
 \r
-               if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+                if (currentMove == forwardMostMove || gameMode == AnalyzeMode\r
+                        || gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
                    DisplayMove(currentMove - 1);\r
                    DisplayAnalysis();\r
                }\r
@@ -6511,12 +6711,13 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 \r
                 SendProgramStatsToFrontend( cps, &programStats );\r
                 \r
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {\r
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || \r
+                           gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
                    DisplayMove(currentMove - 1);\r
                    DisplayAnalysis();\r
                }\r
                return;\r
-           } else if (sscanf(message,"stat01: %d %lu %d %d %d %s",\r
+           } else if (sscanf(message,"stat01: %d " u64Display " %d %d %d %s",\r
                              &time, &nodes, &plylev, &mvleft,\r
                              &mvtot, mvname) >= 5) {\r
                /* The stat01: line is from Crafty (9.29+) in response\r
@@ -6571,7 +6772,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                    strcat(programStats.movelist, p);\r
                 }\r
 \r
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {\r
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||\r
+                           gameMode == AnalyzeFile || appData.icsEngineAnalyze) {\r
                    DisplayMove(currentMove - 1);\r
                    DisplayAnalysis();\r
                }\r
@@ -6581,7 +6783,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
         else {\r
            buf1[0] = NULLCHAR;\r
 \r
-           if (sscanf(message, "%d%c %d %d %lu %[^\n]\n",\r
+           if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",\r
                       &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) \r
             {\r
                 ChessProgramStats cpstats;\r
@@ -6715,7 +6917,7 @@ ParseGameHistory(game)
            break;\r
          case AmbiguousMove:\r
            /* bug? */\r
-           sprintf(buf, "Ambiguous move in ICS output: \"%s\"", yy_text);\r
+           sprintf(buf, _("Ambiguous move in ICS output: \"%s\""), yy_text);\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "Ambiguous move from ICS: '%s'\n", yy_text);\r
     fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
@@ -6725,7 +6927,7 @@ ParseGameHistory(game)
            return;\r
          case ImpossibleMove:\r
            /* bug? */\r
-           sprintf(buf, "Illegal move in ICS output: \"%s\"", yy_text);\r
+           sprintf(buf, _("Illegal move in ICS output: \"%s\""), yy_text);\r
   if (appData.debugMode) {\r
     fprintf(debugFP, "Impossible move from ICS: '%s'\n", yy_text);\r
     fprintf(debugFP, "board L=%d, R=%d, H=%d, holdings=%d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT, gameInfo.holdingsWidth);\r
@@ -6736,7 +6938,7 @@ ParseGameHistory(game)
          case (ChessMove) 0:   /* end of file */\r
            if (boardIndex < backwardMostMove) {\r
                /* Oops, gap.  How did that happen? */\r
-               DisplayError("Gap in move list", 0);\r
+               DisplayError(_("Gap in move list"), 0);\r
                return;\r
            }\r
            backwardMostMove =  blackPlaysFirst ? 1 : 0;\r
@@ -6791,11 +6993,13 @@ ParseGameHistory(game)
                                 EP_UNKNOWN, fromY, fromX, toY, toX, promoChar,\r
                                 parseList[boardIndex]);\r
        CopyBoard(boards[boardIndex + 1], boards[boardIndex]);\r
+        {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[boardIndex+1][i] = castlingRights[boardIndex][i];}\r
        /* currentMoveString is set as a side-effect of yylex */\r
        strcpy(moveList[boardIndex], currentMoveString);\r
        strcat(moveList[boardIndex], "\n");\r
        boardIndex++;\r
-       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex]);\r
+       ApplyMove(fromX, fromY, toX, toY, promoChar, boards[boardIndex], \r
+                                       castlingRights[boardIndex], &epStatus[boardIndex]);\r
         switch (MateTest(boards[boardIndex], PosFlags(boardIndex),\r
                                  EP_UNKNOWN, castlingRights[boardIndex]) ) {\r
          case MT_NONE:\r
@@ -6807,6 +7011,7 @@ ParseGameHistory(game)
                 strcat(parseList[boardIndex - 1], "+");\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            strcat(parseList[boardIndex - 1], "#");\r
            break;\r
        }\r
@@ -6816,51 +7021,55 @@ ParseGameHistory(game)
 \r
 /* Apply a move to the given board  */\r
 void\r
-ApplyMove(fromX, fromY, toX, toY, promoChar, board)\r
+ApplyMove(fromX, fromY, toX, toY, promoChar, board, castling, ep)\r
      int fromX, fromY, toX, toY;\r
      int promoChar;\r
      Board board;\r
+     char *castling;\r
+     char *ep;\r
 {\r
   ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;\r
 \r
     /* [HGM] compute & store e.p. status and castling rights for new position */\r
-    /* if we are updating a board for which those exist (i.e. in boards[])    */\r
-    if((p = ((int)board - (int)boards[0])/((int)boards[1]-(int)boards[0])) < MAX_MOVES && p > 0)\r
-    { int i, j;\r
+    /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */\r
+    { int i;\r
 \r
       if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A;\r
-      oldEP = epStatus[p-1];\r
-      epStatus[p] = EP_NONE;\r
+      oldEP = *ep;\r
+      *ep = EP_NONE;\r
 \r
       if( board[toY][toX] != EmptySquare ) \r
-           epStatus[p] = EP_CAPTURE;  \r
+           *ep = EP_CAPTURE;  \r
 \r
       if( board[fromY][fromX] == WhitePawn ) {\r
-           epStatus[p] = EP_PAWN_MOVE; \r
-           if( toY-fromY==2)\r
+           if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
+              *ep = EP_PAWN_MOVE;\r
+           if( toY-fromY==2) {\r
                if(toX>BOARD_LEFT   && board[toY][toX-1] == BlackPawn &&\r
                        gameInfo.variant != VariantBerolina || toX < fromX)\r
-                     epStatus[p] = toX | berolina;\r
+                     *ep = toX | berolina;\r
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn &&\r
                        gameInfo.variant != VariantBerolina || toX > fromX) \r
-                     epStatus[p] = toX;\r
+                     *ep = toX;\r
+          }\r
       } else \r
       if( board[fromY][fromX] == BlackPawn ) {\r
-           epStatus[p] = EP_PAWN_MOVE; \r
-           if( toY-fromY== -2)\r
+           if(fromY != toY) // [HGM] Xiangqi sideway Pawn moves should not count as 50-move breakers\r
+              *ep = EP_PAWN_MOVE; \r
+           if( toY-fromY== -2) {\r
                if(toX>BOARD_LEFT   && board[toY][toX-1] == WhitePawn &&\r
                        gameInfo.variant != VariantBerolina || toX < fromX)\r
-                     epStatus[p] = toX | berolina;\r
+                     *ep = toX | berolina;\r
                if(toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn &&\r
                        gameInfo.variant != VariantBerolina || toX > fromX) \r
-                     epStatus[p] = toX;\r
+                     *ep = toX;\r
+          }\r
        }\r
 \r
        for(i=0; i<nrCastlingRights; i++) {\r
-           castlingRights[p][i] = castlingRights[p-1][i];\r
-           if(castlingRights[p][i] == fromX && castlingRank[i] == fromY ||\r
-              castlingRights[p][i] == toX   && castlingRank[i] == toY   \r
-             ) castlingRights[p][i] = -1; // revoke for moved or captured piece\r
+           if(castling[i] == fromX && castlingRank[i] == fromY ||\r
+              castling[i] == toX   && castlingRank[i] == toY   \r
+             ) castling[i] = -1; // revoke for moved or captured piece\r
        }\r
 \r
     }\r
@@ -7051,7 +7260,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
       if (captured != EmptySquare && gameInfo.holdingsSize > 0\r
           && gameInfo.variant != VariantBughouse        ) {\r
         /* [HGM] holdings: Add to holdings, if holdings exist */\r
-       if(gameInfo.variant == VariantSuper) { \r
+       if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { \r
                // [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip\r
                captured = (int) captured >= (int) BlackPawn ? BLACK_TO_WHITE captured : WHITE_TO_BLACK captured;\r
        }\r
@@ -7099,6 +7308,19 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         board[toY][toX] = (ChessSquare) (PROMOTED piece);\r
     }\r
 \r
+    if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) \r
+               && promoChar != NULLCHAR && gameInfo.holdingsSize) { \r
+       // [HGM] superchess: take promotion piece out of holdings\r
+       int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));\r
+       if((int)piece < (int)BlackPawn) { // determine stm from piece color\r
+           if(!--board[k][BOARD_WIDTH-2])\r
+               board[k][BOARD_WIDTH-1] = EmptySquare;\r
+       } else {\r
+           if(!--board[BOARD_HEIGHT-1-k][1])\r
+               board[BOARD_HEIGHT-1-k][0] = EmptySquare;\r
+       }\r
+    }\r
+\r
 }\r
 \r
 /* Updates forwardMostMove */\r
@@ -7107,15 +7329,15 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
      int fromX, fromY, toX, toY;\r
      int promoChar;\r
 {\r
-    forwardMostMove++;\r
+//    forwardMostMove++; // [HGM] bare: moved downstream\r
 \r
-    if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting */\r
+    if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */\r
         int timeLeft; static int lastLoadFlag=0; int king, piece;\r
-        piece = boards[forwardMostMove-1][fromY][fromX];\r
+        piece = boards[forwardMostMove][fromY][fromX];\r
         king = piece < (int) BlackPawn ? WhiteKing : BlackKing;\r
         if(gameInfo.variant == VariantKnightmate)\r
             king += (int) WhiteUnicorn - (int) WhiteKing;\r
-        if(forwardMostMove == 1) {\r
+        if(forwardMostMove == 0) {\r
             if(blackPlaysFirst) \r
                 fprintf(serverMoves, "%s;", second.tidy);\r
             fprintf(serverMoves, "%s;", first.tidy);\r
@@ -7133,9 +7355,9 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
                 fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_LEFT, ONE+fromY, AAA+toX+1,ONE+toY);\r
         }\r
         // e.p. suffix\r
-        if( (boards[forwardMostMove-1][fromY][fromX] == WhitePawn ||\r
-             boards[forwardMostMove-1][fromY][fromX] == BlackPawn   ) &&\r
-             boards[forwardMostMove-1][toY][toX] == EmptySquare\r
+        if( (boards[forwardMostMove][fromY][fromX] == WhitePawn ||\r
+             boards[forwardMostMove][fromY][fromX] == BlackPawn   ) &&\r
+             boards[forwardMostMove][toY][toX] == EmptySquare\r
              && fromX != toX )\r
                 fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);\r
         // promotion suffix\r
@@ -7143,28 +7365,31 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
                 fprintf(serverMoves, ":%c:%c%c", promoChar, AAA+toX, ONE+toY);\r
         if(!loadFlag) {\r
             fprintf(serverMoves, "/%d/%d",\r
-               pvInfoList[forwardMostMove-1].depth, pvInfoList[forwardMostMove-1].score);\r
-            if(forwardMostMove & 1) timeLeft = whiteTimeRemaining/1000;\r
-            else                    timeLeft = blackTimeRemaining/1000;\r
+               pvInfoList[forwardMostMove].depth, pvInfoList[forwardMostMove].score);\r
+            if(forwardMostMove+1 & 1) timeLeft = whiteTimeRemaining/1000;\r
+            else                      timeLeft = blackTimeRemaining/1000;\r
             fprintf(serverMoves, "/%d", timeLeft);\r
         }\r
         fflush(serverMoves);\r
     }\r
 \r
-    if (forwardMostMove >= MAX_MOVES) {\r
-      DisplayFatalError("Game too long; increase MAX_MOVES and recompile",\r
+    if (forwardMostMove+1 >= MAX_MOVES) {\r
+      DisplayFatalError(_("Game too long; increase MAX_MOVES and recompile"),\r
                        0, 1);\r
       return;\r
     }\r
     SwitchClocks();\r
-    timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
-    timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
-    if (commentList[forwardMostMove] != NULL) {\r
-       free(commentList[forwardMostMove]);\r
-       commentList[forwardMostMove] = NULL;\r
-    }\r
-    CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);\r
-    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);\r
+    timeRemaining[0][forwardMostMove+1] = whiteTimeRemaining;\r
+    timeRemaining[1][forwardMostMove+1] = blackTimeRemaining;\r
+    if (commentList[forwardMostMove+1] != NULL) {\r
+       free(commentList[forwardMostMove+1]);\r
+       commentList[forwardMostMove+1] = NULL;\r
+    }\r
+    CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);\r
+    {int i; for(i=0; i<BOARD_SIZE; i++) castlingRights[forwardMostMove+1][i] = castlingRights[forwardMostMove][i];}\r
+    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1], \r
+                               castlingRights[forwardMostMove+1], &epStatus[forwardMostMove+1]);\r
+    forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board\r
     gameInfo.result = GameUnfinished;\r
     if (gameInfo.resultDetails != NULL) {\r
        free(gameInfo.resultDetails);\r
@@ -7188,6 +7413,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
             strcat(parseList[forwardMostMove - 1], "+");\r
        break;\r
       case MT_CHECKMATE:\r
+      case MT_STAINMATE:\r
        strcat(parseList[forwardMostMove - 1], "#");\r
        break;\r
     }\r
@@ -7203,6 +7429,7 @@ ShowMove(fromX, fromY, toX, toY)
 {\r
     int instant = (gameMode == PlayFromGameFile) ?\r
        (matchMode || (appData.timeDelay == 0 && !pausing)) : pausing;\r
+    if(appData.noGUI) return;\r
     if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {\r
        if (!instant) {\r
            if (forwardMostMove == currentMove + 1) {\r
@@ -7288,7 +7515,7 @@ InitChessProgram(cps, setup)
       char *v = VariantName(gameInfo.variant);\r
       if (cps->protocolVersion != 1 && StrStr(cps->variants, v) == NULL) {\r
         /* [HGM] in protocol 1 we have to assume all variants valid */\r
-       sprintf(buf, "Variant %s not supported by %s", v, cps->tidy);\r
+       sprintf(buf, _("Variant %s not supported by %s"), v, cps->tidy);\r
        DisplayFatalError(buf, 0, 1);\r
        return;\r
       }\r
@@ -7308,6 +7535,8 @@ InitChessProgram(cps, setup)
            overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
       if( gameInfo.variant == VariantSuper )\r
            overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
+      if( gameInfo.variant == VariantGreat )\r
+           overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 8;\r
 \r
       if(overruled) {\r
            sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, \r
@@ -7340,7 +7569,7 @@ InitChessProgram(cps, setup)
     }\r
 \r
     if (cps->sendICS) {\r
-      sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
+      snprintf(buf, sizeof(buf), "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
       SendToProgram(buf, cps);\r
     }\r
     cps->maybeThinking = FALSE;\r
@@ -7388,17 +7617,17 @@ StartChessProgram(cps)
        err = OpenRcmd(cps->host, appData.remoteUser, cps->program, &cps->pr);\r
     } else {\r
        if (*appData.remoteUser == NULLCHAR) {\r
-           sprintf(buf, "%s %s %s", appData.remoteShell, cps->host,\r
+         snprintf(buf, sizeof(buf), "%s %s %s", appData.remoteShell, cps->host,\r
                    cps->program);\r
        } else {\r
-           sprintf(buf, "%s %s -l %s %s", appData.remoteShell,\r
+         snprintf(buf, sizeof(buf), "%s %s -l %s %s", appData.remoteShell,\r
                    cps->host, appData.remoteUser, cps->program);\r
        }\r
        err = StartChildProcess(buf, "", &cps->pr);\r
     }\r
     \r
     if (err != 0) {\r
-       sprintf(buf, "Startup failure on '%s'", cps->program);\r
+       sprintf(buf, _("Startup failure on '%s'"), cps->program);\r
        DisplayFatalError(buf, err, 1);\r
        cps->pr = NoProc;\r
        cps->isr = NULL;\r
@@ -7408,6 +7637,8 @@ StartChessProgram(cps)
     cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);\r
     if (cps->protocolVersion > 1) {\r
       sprintf(buf, "xboard\nprotover %d\n", cps->protocolVersion);\r
+      cps->nrOptions = 0; // [HGM] options: clear all engine-specific options\r
+      cps->comboCnt = 0;  //                and values of combo boxes\r
       SendToProgram(buf, cps);\r
     } else {\r
       SendToProgram("xboard\n", cps);\r
@@ -7419,13 +7650,13 @@ void
 TwoMachinesEventIfReady P((void))\r
 {\r
   if (first.lastPing != first.lastPong) {\r
-    DisplayMessage("", "Waiting for first chess program");\r
-    ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);\r
+    DisplayMessage("", _("Waiting for first chess program"));\r
+    ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000\r
     return;\r
   }\r
   if (second.lastPing != second.lastPong) {\r
-    DisplayMessage("", "Waiting for second chess program");\r
-    ScheduleDelayedEvent(TwoMachinesEventIfReady, 1000);\r
+    DisplayMessage("", _("Waiting for second chess program"));\r
+    ScheduleDelayedEvent(TwoMachinesEventIfReady, 10); // [HGM] fast: lowered from 1000\r
     return;\r
   }\r
   ThawUI();\r
@@ -7541,20 +7772,35 @@ GameEnds(result, resultDetails, whosays)
         if(gameMode == TwoMachinesPlay && appData.testClaims) {\r
            if(appData.testLegality && whosays >= GE_ENGINE1 ) {\r
                 char claimer;\r
+               ChessMove trueResult = (ChessMove) -1;\r
 \r
                 claimer = whosays == GE_ENGINE1 ?      /* color of claimer */\r
                                             first.twoMachinesColor[0] :\r
                                             second.twoMachinesColor[0] ;\r
-                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) &&\r
-                    (result == WhiteWins && claimer == 'w' ||\r
-                     result == BlackWins && claimer == 'b'   ) ) {\r
-               if (appData.debugMode) {\r
-                    fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
-                       result, epStatus[forwardMostMove], forwardMostMove);\r
+\r
+               // [HGM] losers: because the logic is becoming a bit hairy, determine true result first\r
+               if(epStatus[forwardMostMove] == EP_CHECKMATE) {\r
+                   /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                   trueResult = WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins;\r
+               } else\r
+               if(epStatus[forwardMostMove] == EP_WINS) { // added code for games where being mated is a win\r
+                   /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                   trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;\r
+               } else\r
+               if(epStatus[forwardMostMove] == EP_STALEMATE) { // only used to indicate draws now\r
+                   trueResult = GameIsDrawn; // default; in variants where stalemate loses, Status is CHECKMATE\r
                }\r
-                      /* [HGM] verify: engine mate claims accepted if they were flagged */\r
-                     if(epStatus[forwardMostMove] != EP_CHECKMATE &&\r
-                        result != (WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins)) {\r
+\r
+               // now verify win claims, but not in drop games, as we don't understand those yet\r
+                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper\r
+                                                || gameInfo.variant == VariantGreat) &&\r
+                    (result == WhiteWins && claimer == 'w' ||\r
+                     result == BlackWins && claimer == 'b'   ) ) { // case to verify: engine claims own win\r
+                     if (appData.debugMode) {\r
+                       fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
+                               result, epStatus[forwardMostMove], forwardMostMove);\r
+                     }\r
+                     if(result != trueResult) {\r
                              sprintf(buf, "False win claim: '%s'", resultDetails);\r
                              result = claimer == 'w' ? BlackWins : WhiteWins;\r
                              resultDetails = buf;\r
@@ -7573,7 +7819,10 @@ GameEnds(result, resultDetails, whosays)
                 /* (Claiming a loss is accepted no questions asked!) */\r
            }\r
            /* [HGM] bare: don't allow bare King to win */\r
-           if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper) && result != GameIsDrawn)\r
+           if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
+              && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway \r
+              && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course...\r
+              && result != GameIsDrawn)\r
            {   int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);\r
                for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {\r
                        int p = (int)boards[forwardMostMove][i][j] - color;\r
@@ -7797,7 +8046,7 @@ GameEnds(result, resultDetails, whosays)
        } else {\r
            char buf[MSG_SIZ];\r
            gameMode = nextGameMode;\r
-           sprintf(buf, "Match %s vs. %s: final score %d-%d-%d",\r
+           sprintf(buf, _("Match %s vs. %s: final score %d-%d-%d"),\r
                    first.tidy, second.tidy,\r
                    first.matchWins, second.matchWins,\r
                    appData.matchGames - (first.matchWins + second.matchWins));\r
@@ -7866,8 +8115,8 @@ ResurrectChessProgram()
        timeRemaining[1][currentMove] = blackTimeRemaining;\r
     }\r
 \r
-    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) &&\r
-       first.analysisSupport) {\r
+    if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile ||\r
+                appData.icsEngineAnalyze) && first.analysisSupport) {\r
       SendToProgram("analyze\n", &first);\r
       first.analyzing = TRUE;\r
     }\r
@@ -7905,6 +8154,7 @@ Reset(redraw, init)
     ics_gamenum = -1;\r
     white_holding[0] = black_holding[0] = NULLCHAR;\r
     ClearProgramStats();\r
+    opponentKibitzes = FALSE; // [HGM] kibitz: do not reserve space in engine-output window in zippy mode\r
     \r
     ResetFrontEnd();\r
     ClearHighlights();\r
@@ -8150,6 +8400,7 @@ LoadGameOneMove(readAhead)
          case MT_CHECK:\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            if (WhiteOnMove(currentMove)) {\r
                GameEnds(BlackWins, "Black mates", GE_FILE);\r
            } else {\r
@@ -8185,6 +8436,7 @@ LoadGameOneMove(readAhead)
          case MT_CHECK:\r
            break;\r
          case MT_CHECKMATE:\r
+         case MT_STAINMATE:\r
            if (WhiteOnMove(currentMove)) {\r
                GameEnds(BlackWins, "Black mates", GE_FILE);\r
            } else {\r
@@ -8210,7 +8462,7 @@ LoadGameOneMove(readAhead)
        if (appData.testLegality) {\r
            if (appData.debugMode)\r
              fprintf(debugFP, "Parsed IllegalMove: %s\n", yy_text);\r
-           sprintf(move, "Illegal move: %d.%s%s",\r
+           sprintf(move, _("Illegal move: %d.%s%s"),\r
                    (forwardMostMove / 2) + 1,\r
                    WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
            DisplayError(move, 0);\r
@@ -8230,7 +8482,7 @@ LoadGameOneMove(readAhead)
       case AmbiguousMove:\r
        if (appData.debugMode)\r
          fprintf(debugFP, "Parsed AmbiguousMove: %s\n", yy_text);\r
-       sprintf(move, "Ambiguous move: %d.%s%s",\r
+       sprintf(move, _("Ambiguous move: %d.%s%s"),\r
                (forwardMostMove / 2) + 1,\r
                WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
        DisplayError(move, 0);\r
@@ -8241,7 +8493,7 @@ LoadGameOneMove(readAhead)
       case ImpossibleMove:\r
        if (appData.debugMode)\r
          fprintf(debugFP, "Parsed ImpossibleMove (type = %d): %s\n", moveType, yy_text);\r
-       sprintf(move, "Illegal move: %d.%s%s",\r
+       sprintf(move, _("Illegal move: %d.%s%s"),\r
                (forwardMostMove / 2) + 1,\r
                WhiteOnMove(forwardMostMove) ? " " : ".. ", yy_text);\r
        DisplayError(move, 0);\r
@@ -8289,7 +8541,7 @@ LoadGameFromFile(filename, n, title, useList)
     } else {\r
        f = fopen(filename, "rb");\r
        if (f == NULL) {\r
-           sprintf(buf, "Can't open \"%s\"", filename);\r
+         snprintf(buf, sizeof(buf),  _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        }\r
@@ -8301,7 +8553,7 @@ LoadGameFromFile(filename, n, title, useList)
     if (useList && n == 0) {\r
        int error = GameListBuild(f);\r
        if (error) {\r
-           DisplayError("Cannot build game list", error);\r
+           DisplayError(_("Cannot build game list"), error);\r
        } else if (!ListEmpty(&gameList) &&\r
                   ((ListGame *) gameList.tailPred)->number > 1) {\r
            GameListPopUp(f, title);\r
@@ -8345,6 +8597,7 @@ MakeRegisteredMove()
                break;\r
                \r
              case MT_CHECKMATE:\r
+             case MT_STAINMATE:\r
                if (WhiteOnMove(currentMove)) {\r
                    GameEnds(BlackWins, "Black mates", GE_PLAYER);\r
                } else {\r
@@ -8390,7 +8643,7 @@ CmailLoadGame(f, gameNumber, title, useList)
     int retVal;\r
 \r
     if (gameNumber > nCmailGames) {\r
-       DisplayError("No more games in this message", 0);\r
+       DisplayError(_("No more games in this message"), 0);\r
        return FALSE;\r
     }\r
     if (f == lastLoadGameFP) {\r
@@ -8431,11 +8684,11 @@ ReloadGame(offset)
 {\r
     int gameNumber = lastLoadGameNumber + offset;\r
     if (lastLoadGameFP == NULL) {\r
-       DisplayError("No game has been loaded yet", 0);\r
+       DisplayError(_("No game has been loaded yet"), 0);\r
        return FALSE;\r
     }\r
     if (gameNumber <= 0) {\r
-       DisplayError("Can't back up any further", 0);\r
+       DisplayError(_("Can't back up any further"), 0);\r
        return FALSE;\r
     }\r
     if (cmailMsgLoaded) {\r
@@ -8491,7 +8744,7 @@ LoadGame(f, gameNumber, title, useList)
            gn = 1;\r
        }\r
        else {\r
-           DisplayError("Game number out of range", 0);\r
+           DisplayError(_("Game number out of range"), 0);\r
            return FALSE;\r
        }\r
     } else {\r
@@ -8502,7 +8755,7 @@ LoadGame(f, gameNumber, title, useList)
                gameNumber == 1) {\r
                gn = 1;\r
            } else {\r
-               DisplayError("Can't seek on game file", 0);\r
+               DisplayError(_("Can't seek on game file"), 0);\r
                return FALSE;\r
            }\r
        }\r
@@ -8515,7 +8768,7 @@ LoadGame(f, gameNumber, title, useList)
     yynewfile(f);\r
 \r
     if (lg && lg->gameInfo.white && lg->gameInfo.black) {\r
-       sprintf(buf, "%s vs. %s", lg->gameInfo.white,\r
+      snprintf(buf, sizeof(buf), "%s vs. %s", lg->gameInfo.white,\r
                lg->gameInfo.black);\r
            DisplayTitle(buf);\r
     } else if (*title != NULLCHAR) {\r
@@ -8560,7 +8813,7 @@ LoadGame(f, gameNumber, title, useList)
                nCmailGames = CMAIL_MAX_GAMES - gn;\r
            } else {\r
                Reset(TRUE, TRUE);\r
-               DisplayError("Game not found in file", 0);\r
+               DisplayError(_("Game not found in file"), 0);\r
            }\r
            return FALSE;\r
 \r
@@ -8687,7 +8940,7 @@ LoadGame(f, gameNumber, title, useList)
          startedFromSetupPosition = TRUE;\r
          if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {\r
            Reset(TRUE, TRUE);\r
-           DisplayError("Bad FEN position in file", 0);\r
+           DisplayError(_("Bad FEN position in file"), 0);\r
            return FALSE;\r
          }\r
          CopyBoard(boards[0], initial_position);\r
@@ -8845,7 +9098,7 @@ LoadGame(f, gameNumber, title, useList)
     if ((cm == (ChessMove) 0 && lastLoadGameStart != (ChessMove) 0) ||\r
        cm == WhiteWins || cm == BlackWins ||\r
        cm == GameIsDrawn || cm == GameUnfinished) {\r
-       DisplayMessage("", "No moves in game");\r
+       DisplayMessage("", _("No moves in game"));\r
        if (cmailMsgLoaded) {\r
            if (appData.debugMode)\r
              fprintf(debugFP, "Setting flipView to %d.\n", FALSE);\r
@@ -8916,11 +9169,11 @@ ReloadPosition(offset)
 {\r
     int positionNumber = lastLoadPositionNumber + offset;\r
     if (lastLoadPositionFP == NULL) {\r
-       DisplayError("No position has been loaded yet", 0);\r
+       DisplayError(_("No position has been loaded yet"), 0);\r
        return FALSE;\r
     }\r
     if (positionNumber <= 0) {\r
-       DisplayError("Can't back up any further", 0);\r
+       DisplayError(_("Can't back up any further"), 0);\r
        return FALSE;\r
     }\r
     return LoadPosition(lastLoadPositionFP, positionNumber,\r
@@ -8942,7 +9195,7 @@ LoadPositionFromFile(filename, n, title)
     } else {\r
        f = fopen(filename, "rb");\r
        if (f == NULL) {\r
-           sprintf(buf, "Can't open \"%s\"", filename);\r
+            snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -8983,7 +9236,7 @@ LoadPosition(f, positionNumber, title)
     if (positionNumber < 0) {\r
        /* Negative position number means to seek to that byte offset */\r
        if (fseek(f, -positionNumber, 0) == -1) {\r
-           DisplayError("Can't seek on position file", 0);\r
+           DisplayError(_("Can't seek on position file"), 0);\r
            return FALSE;\r
        };\r
        pn = 1;\r
@@ -8994,14 +9247,14 @@ LoadPosition(f, positionNumber, title)
                positionNumber == 1) {\r
                pn = 1;\r
            } else {\r
-               DisplayError("Can't seek on position file", 0);\r
+               DisplayError(_("Can't seek on position file"), 0);\r
                return FALSE;\r
            }\r
        }\r
     }\r
     /* See if this file is FEN or old-style xboard */\r
     if (fgets(line, MSG_SIZ, f) == NULL) {\r
-       DisplayError("Position not found in file", 0);\r
+       DisplayError(_("Position not found in file"), 0);\r
        return FALSE;\r
     }\r
 #if 0\r
@@ -9031,7 +9284,7 @@ LoadPosition(f, positionNumber, title)
            /* skip positions before number pn */\r
            if (fgets(line, MSG_SIZ, f) == NULL) {\r
                Reset(TRUE, TRUE);\r
-               DisplayError("Position not found in file", 0);\r
+               DisplayError(_("Position not found in file"), 0);\r
                return FALSE;\r
            }\r
            if (fenMode || line[0] == '#') pn--;\r
@@ -9040,7 +9293,7 @@ LoadPosition(f, positionNumber, title)
 \r
     if (fenMode) {\r
        if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {\r
-           DisplayError("Bad FEN position in file", 0);\r
+           DisplayError(_("Bad FEN position in file"), 0);\r
            return FALSE;\r
        }\r
     } else {\r
@@ -9072,10 +9325,10 @@ LoadPosition(f, positionNumber, title)
        strcpy(moveList[0], "");\r
        strcpy(parseList[0], "");\r
        CopyBoard(boards[1], initial_position);\r
-       DisplayMessage("", "Black to play");\r
+       DisplayMessage("", _("Black to play"));\r
     } else {\r
        currentMove = forwardMostMove = backwardMostMove = 0;\r
-       DisplayMessage("", "White to play");\r
+       DisplayMessage("", _("White to play"));\r
     }\r
           /* [HGM] copy FEN attributes as well */\r
           {   int i;\r
@@ -9156,7 +9409,7 @@ SaveGameToFile(filename, append)
     } else {\r
        f = fopen(filename, append ? "a" : "w");\r
        if (f == NULL) {\r
-           sprintf(buf, "Can't open \"%s\"", filename);\r
+           snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -9265,7 +9518,7 @@ SaveGamePGN(f)
 {\r
     int i, offset, linelen, newblock;\r
     time_t tm;\r
-    char *movetext;\r
+//    char *movetext;\r
     char numtext[32];\r
     int movelen, numlen, blank;\r
     char move_buffer[100]; /* [AS] Buffer for move+PV info */\r
@@ -9277,7 +9530,7 @@ SaveGamePGN(f)
     PrintPGNTags(f, &gameInfo);\r
     \r
     if (backwardMostMove > 0 || startedFromSetupPosition) {\r
-        char *fen = PositionToFEN(backwardMostMove, 1);\r
+        char *fen = PositionToFEN(backwardMostMove, NULL);\r
         fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);\r
        fprintf(f, "\n{--------------\n");\r
        PrintPosition(f, backwardMostMove);\r
@@ -9340,7 +9593,16 @@ SaveGamePGN(f)
        linelen += numlen;\r
 \r
        /* Get move */\r
-       movelen = strlen(parseList[i]); /* [HGM] pgn: line-break point before move */\r
+       strcpy(move_buffer, parseList[i]); // [HGM] pgn: print move via buffer, so it can be edited\r
+       movelen = strlen(move_buffer); /* [HGM] pgn: line-break point before move */\r
+        if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {\r
+               int p = movelen - 1;\r
+               if(move_buffer[p] == ' ') p--;\r
+               if(move_buffer[p] == ')') { // [HGM] pgn: strip off ICS time if we have extended info\r
+                   while(p && move_buffer[--p] != '(');\r
+                   if(p && move_buffer[p-1] == ' ') move_buffer[movelen=p-1] = 0;\r
+               }\r
+        }\r
 \r
        /* Print move */\r
        blank = linelen > 0 && movelen > 0;\r
@@ -9353,7 +9615,7 @@ SaveGamePGN(f)
            fprintf(f, " ");\r
            linelen++;\r
        }\r
-       fprintf(f, parseList[i]);\r
+       fprintf(f, move_buffer);\r
        linelen += movelen;\r
 \r
         /* [AS] Add PV info if present */\r
@@ -9361,25 +9623,19 @@ SaveGamePGN(f)
             /* [HGM] add time */\r
             char buf[MSG_SIZ]; int seconds = 0;\r
 \r
-#if 0\r
+#if 1\r
             if(i >= backwardMostMove) {\r
                if(WhiteOnMove(i))\r
                        seconds = timeRemaining[0][i] - timeRemaining[0][i+1]\r
-                                 + GetTimeQuota(i/2) / WhitePlayer()->timeOdds;\r
+                                 + GetTimeQuota(i/2) / (1000*WhitePlayer()->timeOdds);\r
                else\r
                        seconds = timeRemaining[1][i] - timeRemaining[1][i+1]\r
-                                  + GetTimeQuota(i/2) / WhitePlayer()->other->timeOdds;\r
+                                  + GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds);\r
             }\r
             seconds = (seconds+50)/100; // deci-seconds, rounded to nearest\r
 #else\r
             seconds = (pvInfoList[i].time + 5)/10; // [HGM] PVtime: use engine time\r
 #endif\r
-    if (appData.debugMode,0) {\r
-        fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n",\r
-                timeRemaining[0][i+1], timeRemaining[0][i],\r
-                     timeRemaining[1][i+1], timeRemaining[1][i], seconds\r
-        );\r
-    }\r
 \r
             if( seconds <= 0) buf[0] = 0; else\r
             if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else {\r
@@ -9527,7 +9783,7 @@ SavePositionToFile(filename)
     } else {\r
        f = fopen(filename, "a");\r
        if (f == NULL) {\r
-           sprintf(buf, "Can't open \"%s\"", filename);\r
+           snprintf(buf, sizeof(buf), _("Can't open \"%s\""), filename);\r
            DisplayError(buf, errno);\r
            return FALSE;\r
        } else {\r
@@ -9556,7 +9812,7 @@ SavePosition(f, dummy, dummy2)
        PrintPosition(f, currentMove);\r
        fprintf(f, "--------------]\n");\r
     } else {\r
-       fen = PositionToFEN(currentMove, 1);\r
+       fen = PositionToFEN(currentMove, NULL);\r
        fprintf(f, "%s\n", fen);\r
        free(fen);\r
     }\r
@@ -9657,17 +9913,17 @@ RegisterMove()
     }\r
 \r
     if (cmailOldMove == -1) {\r
-       DisplayError("You have edited the game history.\nUse Reload Same Game and make your move again.", 0);\r
+       DisplayError(_("You have edited the game history.\nUse Reload Same Game and make your move again."), 0);\r
        return FALSE;\r
     }\r
 \r
     if (currentMove > cmailOldMove + 1) {\r
-       DisplayError("You have entered too many moves.\nBack up to the correct position and try again.", 0);\r
+       DisplayError(_("You have entered too many moves.\nBack up to the correct position and try again."), 0);\r
        return FALSE;\r
     }\r
 \r
     if (currentMove < cmailOldMove) {\r
-       DisplayError("Displayed position is not current.\nStep forward to the correct position and try again.", 0);\r
+       DisplayError(_("Displayed position is not current.\nStep forward to the correct position and try again."), 0);\r
        return FALSE;\r
     }\r
 \r
@@ -9715,7 +9971,7 @@ RegisterMove()
        cmailMoveRegistered[lastLoadGameNumber - 1] = TRUE;\r
        nCmailMovesRegistered ++;\r
     } else if (nCmailGames == 1) {\r
-       DisplayError("You have not made a move yet", 0);\r
+       DisplayError(_("You have not made a move yet"), 0);\r
        return FALSE;\r
     }\r
 \r
@@ -9736,18 +9992,18 @@ MailMoveEvent()
     char *arcDir;\r
 \r
     if (! cmailMsgLoaded) {\r
-       DisplayError("The cmail message is not loaded.\nUse Reload CMail Message and make your move again.", 0);\r
+       DisplayError(_("The cmail message is not loaded.\nUse Reload CMail Message and make your move again."), 0);\r
        return;\r
     }\r
 \r
     if (nCmailGames == nCmailResults) {\r
-       DisplayError("No unfinished games", 0);\r
+       DisplayError(_("No unfinished games"), 0);\r
        return;\r
     }\r
 \r
 #if CMAIL_PROHIBIT_REMAIL\r
     if (cmailMailedMove) {\r
-       sprintf(msg, "You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line.", appData.cmailGameName);\r
+       sprintf(msg, _("You have already mailed a move.\nWait until a move arrives from your opponent.\nTo resend the same move, type\n\"cmail -remail -game %s\"\non the command line."), appData.cmailGameName);\r
        DisplayError(msg, 0);\r
        return;\r
     }\r
@@ -9759,10 +10015,10 @@ MailMoveEvent()
        || (nCmailMovesRegistered + nCmailResults == nCmailGames)) {\r
        sprintf(string, partCommandString,\r
                appData.debugMode ? " -v" : "", appData.cmailGameName);\r
-       commandOutput = popen(string, "rb");\r
+       commandOutput = popen(string, "r");\r
 \r
        if (commandOutput == NULL) {\r
-           DisplayError("Failed to invoke cmail", 0);\r
+           DisplayError(_("Failed to invoke cmail"), 0);\r
        } else {\r
            for (nBuffers = 0; (! feof(commandOutput)); nBuffers ++) {\r
                nBytes = fread(buffer, 1, MSG_SIZ - 1, commandOutput);\r
@@ -9824,7 +10080,7 @@ CmailMsg()
     if (!cmailMsgLoaded) return "";\r
 \r
     if (cmailMailedMove) {\r
-       sprintf(cmailMsg, "Waiting for reply from opponent\n");\r
+       sprintf(cmailMsg, _("Waiting for reply from opponent\n"));\r
     } else {\r
        /* Create a list of games left */\r
        sprintf(string, "[");\r
@@ -9847,17 +10103,17 @@ CmailMsg()
            switch (nCmailGames) {\r
              case 1:\r
                sprintf(cmailMsg,\r
-                       "Still need to make move for game\n");\r
+                       _("Still need to make move for game\n"));\r
                break;\r
                \r
              case 2:\r
                sprintf(cmailMsg,\r
-                       "Still need to make moves for both games\n");\r
+                       _("Still need to make moves for both games\n"));\r
                break;\r
                \r
              default:\r
                sprintf(cmailMsg,\r
-                       "Still need to make moves for all %d games\n",\r
+                       _("Still need to make moves for all %d games\n"),\r
                        nCmailGames);\r
                break;\r
            }\r
@@ -9865,21 +10121,21 @@ CmailMsg()
            switch (nCmailGames - nCmailMovesRegistered - nCmailResults) {\r
              case 1:\r
                sprintf(cmailMsg,\r
-                       "Still need to make a move for game %s\n",\r
+                       _("Still need to make a move for game %s\n"),\r
                        string);\r
                break;\r
                \r
              case 0:\r
                if (nCmailResults == nCmailGames) {\r
-                   sprintf(cmailMsg, "No unfinished games\n");\r
+                   sprintf(cmailMsg, _("No unfinished games\n"));\r
                } else {\r
-                   sprintf(cmailMsg, "Ready to send mail\n");\r
+                   sprintf(cmailMsg, _("Ready to send mail\n"));\r
                }\r
                break;\r
                \r
              default:\r
                sprintf(cmailMsg,\r
-                       "Still need to make moves for games %s\n",\r
+                       _("Still need to make moves for games %s\n"),\r
                        string);\r
            }\r
        }\r
@@ -10047,9 +10303,9 @@ EditCommentEvent()
     char title[MSG_SIZ];\r
 \r
     if (currentMove < 1 || parseList[currentMove - 1][0] == NULLCHAR) {\r
-       strcpy(title, "Edit comment");\r
+       strcpy(title, _("Edit comment"));\r
     } else {\r
-       sprintf(title, "Edit comment on %d.%s%s", (currentMove - 1) / 2 + 1,\r
+       sprintf(title, _("Edit comment on %d.%s%s"), (currentMove - 1) / 2 + 1,\r
                WhiteOnMove(currentMove - 1) ? " " : ".. ",\r
                parseList[currentMove - 1]);\r
     }\r
@@ -10073,17 +10329,19 @@ AnalyzeModeEvent()
       return;\r
 \r
     if (gameMode != AnalyzeFile) {\r
-       EditGameEvent();\r
-       if (gameMode != EditGame) return;\r
+        if (!appData.icsEngineAnalyze) {\r
+               EditGameEvent();\r
+               if (gameMode != EditGame) return;\r
+        }\r
        ResurrectChessProgram();\r
        SendToProgram("analyze\n", &first);\r
        first.analyzing = TRUE;\r
        /*first.maybeThinking = TRUE;*/\r
        first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
-       AnalysisPopUp("Analysis",\r
-                     "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");\r
+       AnalysisPopUp(_("Analysis"),\r
+                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));\r
     }\r
-    gameMode = AnalyzeMode;\r
+    if (!appData.icsEngineAnalyze) gameMode = AnalyzeMode;\r
     pausing = FALSE;\r
     ModeHighlight();\r
     SetGameInfo();\r
@@ -10107,8 +10365,8 @@ AnalyzeFileEvent()
        first.analyzing = TRUE;\r
        /*first.maybeThinking = TRUE;*/\r
        first.maybeThinking = FALSE; /* avoid killing GNU Chess */\r
-       AnalysisPopUp("Analysis",\r
-                     "Starting analysis mode...\nIf this message stays up, your chess program does not support analysis.");\r
+       AnalysisPopUp(_("Analysis"),\r
+                     _("Starting analysis mode...\nIf this message stays up, your chess program does not support analysis."));\r
     }\r
     gameMode = AnalyzeFile;\r
     pausing = FALSE;\r
@@ -10141,7 +10399,7 @@ MachineWhiteEvent()
         EditPositionDone();\r
 \r
     if (!WhiteOnMove(currentMove)) {\r
-       DisplayError("It is not White's turn", 0);\r
+       DisplayError(_("It is not White's turn"), 0);\r
        return;\r
     }\r
   \r
@@ -10184,14 +10442,15 @@ MachineWhiteEvent()
     if (appData.autoFlipView && !flipView) {\r
       flipView = !flipView;\r
       DrawPosition(FALSE, NULL);\r
+      DisplayBothClocks();       // [HGM] logo: clocks might have to be exchanged;\r
     }\r
 \r
     if(bookHit) { // [HGM] book: simulate book reply\r
        static char bookMove[MSG_SIZ]; // a bit generous?\r
 \r
-       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
-       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
        strcpy(bookMove, "move ");\r
        strcat(bookMove, bookHit);\r
@@ -10220,7 +10479,7 @@ MachineBlackEvent()
         EditPositionDone();\r
 \r
     if (WhiteOnMove(currentMove)) {\r
-       DisplayError("It is not Black's turn", 0);\r
+       DisplayError(_("It is not Black's turn"), 0);\r
        return;\r
     }\r
     \r
@@ -10259,13 +10518,14 @@ MachineBlackEvent()
     if (appData.autoFlipView && flipView) {\r
       flipView = !flipView;\r
       DrawPosition(FALSE, NULL);\r
+      DisplayBothClocks();       // [HGM] logo: clocks might have to be exchanged;\r
     }\r
     if(bookHit) { // [HGM] book: simulate book reply\r
        static char bookMove[MSG_SIZ]; // a bit generous?\r
 \r
-       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
-       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
        strcpy(bookMove, "move ");\r
        strcat(bookMove, bookHit);\r
@@ -10312,7 +10572,7 @@ TwoMachinesEvent P((void))
       case MachinePlaysWhite:\r
       case MachinePlaysBlack:\r
        if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
-           DisplayError("Wait until your turn,\nor select Move Now", 0);\r
+           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);\r
            return;\r
        }\r
        /* fall through */\r
@@ -10344,7 +10604,7 @@ TwoMachinesEvent P((void))
        } else {\r
          /* kludge: allow timeout for initial "feature" command */\r
          FreezeUI();\r
-         DisplayMessage("", "Starting second chess program");\r
+         DisplayMessage("", _("Starting second chess program"));\r
          ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT);\r
        }\r
        return;\r
@@ -10409,9 +10669,9 @@ TwoMachinesEvent P((void))
     if(bookHit) { // [HGM] book: simulate book reply\r
        static char bookMove[MSG_SIZ]; // a bit generous?\r
 \r
-       programStats.depth = programStats.nodes = programStats.time = \r
+       programStats.nodes = programStats.depth = programStats.time = \r
        programStats.score = programStats.got_only_move = 0;\r
-       sprintf(programStats.movelist, "%s (xbook)", bookMove);\r
+       sprintf(programStats.movelist, "%s (xbook)", bookHit);\r
 \r
        strcpy(bookMove, "move ");\r
        strcat(bookMove, bookHit);\r
@@ -10425,7 +10685,7 @@ TrainingEvent()
     if (gameMode == Training) {\r
       SetTrainingModeOff();\r
       gameMode = PlayFromGameFile;\r
-      DisplayMessage("", "Training mode off");\r
+      DisplayMessage("", _("Training mode off"));\r
     } else {\r
       gameMode = Training;\r
       animateTraining = appData.animate;\r
@@ -10433,10 +10693,10 @@ TrainingEvent()
       /* make sure we are not already at the end of the game */\r
       if (currentMove < forwardMostMove) {\r
        SetTrainingModeOn();\r
-       DisplayMessage("", "Training mode on");\r
+       DisplayMessage("", _("Training mode on"));\r
       } else {\r
        gameMode = PlayFromGameFile;\r
-       DisplayError("Already at end of game", 0);\r
+       DisplayError(_("Already at end of game"), 0);\r
       }\r
     }\r
     ModeHighlight();\r
@@ -10517,13 +10777,13 @@ EditGameEvent()
        break;\r
       case IcsPlayingBlack:\r
       case IcsPlayingWhite:\r
-       DisplayError("Warning: You are still playing a game", 0);\r
+       DisplayError(_("Warning: You are still playing a game"), 0);\r
        break;\r
       case IcsObserving:\r
-       DisplayError("Warning: You are still observing a game", 0);\r
+       DisplayError(_("Warning: You are still observing a game"), 0);\r
        break;\r
       case IcsExamining:\r
-       DisplayError("Warning: You are still examining a game", 0);\r
+       DisplayError(_("Warning: You are still examining a game"), 0);\r
        break;\r
       case IcsIdle:\r
        break;\r
@@ -10593,6 +10853,12 @@ EditPositionEvent()
 void\r
 ExitAnalyzeMode()\r
 {\r
+    /* [DM] icsEngineAnalyze - possible call from other functions */\r
+    if (appData.icsEngineAnalyze) {\r
+        appData.icsEngineAnalyze = FALSE;\r
+\r
+        DisplayMessage("",_("Close ICS engine analyze..."));\r
+    }\r
     if (first.analysisSupport && first.analyzing) {\r
       SendToProgram("exit\n", &first);\r
       first.analyzing = FALSE;\r
@@ -10813,7 +11079,7 @@ DropMenuEvent(selection, x, y)
       case IcsPlayingWhite:\r
       case MachinePlaysBlack:\r
        if (!WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is Black's turn");\r
+           DisplayMoveError(_("It is Black's turn"));\r
            return;\r
        }\r
        moveType = WhiteDrop;\r
@@ -10821,7 +11087,7 @@ DropMenuEvent(selection, x, y)
       case IcsPlayingBlack:\r
       case MachinePlaysWhite:\r
        if (WhiteOnMove(currentMove)) {\r
-           DisplayMoveError("It is White's turn");\r
+           DisplayMoveError(_("It is White's turn"));\r
            return;\r
        }\r
        moveType = BlackDrop;\r
@@ -10838,7 +11104,7 @@ DropMenuEvent(selection, x, y)
                                 + (int) BlackPawn - (int) WhitePawn);\r
     }\r
     if (boards[currentMove][y][x] != EmptySquare) {\r
-       DisplayMoveError("That square is occupied");\r
+       DisplayMoveError(_("That square is occupied"));\r
        return;\r
     }\r
 \r
@@ -10862,7 +11128,7 @@ AcceptEvent()
            GameEnds(GameIsDrawn, "Draw agreed", GE_PLAYER);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_ACCEPT;\r
        } else {\r
-           DisplayError("There is no pending offer on this move", 0);\r
+           DisplayError(_("There is no pending offer on this move"), 0);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
        }\r
     } else {\r
@@ -10888,7 +11154,7 @@ DeclineEvent()
            DisplayComment(cmailOldMove - 1, "Draw declined");\r
 #endif /*NOTDEF*/\r
        } else {\r
-           DisplayError("There is no pending offer on this move", 0);\r
+           DisplayError(_("There is no pending offer on this move"), 0);\r
        }\r
     } else {\r
        /* Not used for offers from chess program */\r
@@ -10924,7 +11190,7 @@ CallFlagEvent()
                else\r
                  GameEnds(BlackWins, "Black wins on time", GE_PLAYER);\r
            } else {\r
-               DisplayError("Your opponent is not out of time", 0);\r
+               DisplayError(_("Your opponent is not out of time"), 0);\r
            }\r
            break;\r
          case MachinePlaysBlack:\r
@@ -10935,7 +11201,7 @@ CallFlagEvent()
                else\r
                  GameEnds(WhiteWins, "White wins on time", GE_PLAYER);\r
            } else {\r
-               DisplayError("Your opponent is not out of time", 0);\r
+               DisplayError(_("Your opponent is not out of time"), 0);\r
            }\r
            break;\r
        }\r
@@ -10970,7 +11236,7 @@ DrawEvent()
            DisplayComment(currentMove - 1, offer);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_DRAW;\r
        } else {\r
-           DisplayError("You must make your move before offering a draw", 0);\r
+           DisplayError(_("You must make your move before offering a draw"), 0);\r
            cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;\r
        }\r
     } else if (first.offeredDraw) {\r
@@ -11284,11 +11550,11 @@ void
 RevertEvent()\r
 {\r
     if (gameMode != IcsExamining) {\r
-       DisplayError("You are not examining a game", 0);\r
+       DisplayError(_("You are not examining a game"), 0);\r
        return;\r
     }\r
     if (pausing) {\r
-       DisplayError("You can't revert while pausing", 0);\r
+       DisplayError(_("You can't revert while pausing"), 0);\r
        return;\r
     }\r
     SendToICS(ics_prefix);\r
@@ -11302,7 +11568,7 @@ RetractMoveEvent()
       case MachinePlaysWhite:\r
       case MachinePlaysBlack:\r
        if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) {\r
-           DisplayError("Wait until your turn,\nor select Move Now", 0);\r
+           DisplayError(_("Wait until your turn,\nor select Move Now"), 0);\r
            return;\r
        }\r
        if (forwardMostMove < 2) return;\r
@@ -11342,14 +11608,14 @@ MoveNowEvent()
     switch (gameMode) {\r
       case MachinePlaysWhite:\r
        if (!WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("It is your turn", 0);\r
+           DisplayError(_("It is your turn"), 0);\r
            return;\r
        }\r
        cps = &first;\r
        break;\r
       case MachinePlaysBlack:\r
        if (WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("It is your turn", 0);\r
+           DisplayError(_("It is your turn"), 0);\r
            return;\r
        }\r
        cps = &first;\r
@@ -11399,19 +11665,19 @@ HintEvent()
     switch (gameMode) {\r
       case MachinePlaysWhite:\r
        if (WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
       case BeginningOfGame:\r
       case MachinePlaysBlack:\r
        if (!WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
       default:\r
-       DisplayError("No hint available", 0);\r
+       DisplayError(_("No hint available"), 0);\r
        return;\r
     }\r
     SendToProgram("hint\n", &first);\r
@@ -11425,14 +11691,14 @@ BookEvent()
     switch (gameMode) {\r
       case MachinePlaysWhite:\r
        if (WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
       case BeginningOfGame:\r
       case MachinePlaysBlack:\r
        if (!WhiteOnMove(forwardMostMove)) {\r
-           DisplayError("Wait until your turn", 0);\r
+           DisplayError(_("Wait until your turn"), 0);\r
            return;\r
        }\r
        break;\r
@@ -11814,7 +12080,7 @@ SendToProgram(message, cps)
     outCount = OutputToProcess(cps->pr, message, count, &error);\r
     if (outCount < count && !exiting \r
                          && !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */\r
-       sprintf(buf, "Error writing to %s chess program", cps->which);\r
+       sprintf(buf, _("Error writing to %s chess program"), cps->which);\r
         if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
             if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
                 gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
@@ -11844,12 +12110,12 @@ ReceiveFromProgram(isr, closure, message, count, error)
     if (count <= 0) {\r
        if (count == 0) {\r
            sprintf(buf,\r
-                   "Error: %s chess program (%s) exited unexpectedly",\r
+                   _("Error: %s chess program (%s) exited unexpectedly"),\r
                    cps->which, cps->program);\r
         if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
                 if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
                     gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
-                    sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program);\r
+                    sprintf(buf, _("%s program exits in draw position (%s)"), cps->which, cps->program);\r
                 } else {\r
                     gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;\r
                 }\r
@@ -11859,7 +12125,7 @@ ReceiveFromProgram(isr, closure, message, count, error)
            DisplayFatalError(buf, 0, 1);\r
        } else {\r
            sprintf(buf,\r
-                   "Error reading from %s chess program (%s)",\r
+                   _("Error reading from %s chess program (%s)"),\r
                    cps->which, cps->program);\r
            RemoveInputSource(cps->isr);\r
 \r
@@ -11903,6 +12169,14 @@ ReceiveFromProgram(isr, closure, message, count, error)
                        message);\r
        }\r
     }\r
+\r
+    /* [DM] if icsEngineAnalyze is active we block all whisper and kibitz output, because nobody want to see this */\r
+    if (appData.icsEngineAnalyze) {\r
+        if (strstr(message, "whisper") != NULL ||\r
+             strstr(message, "kibitz") != NULL || \r
+            strstr(message, "tellics") != NULL) return;\r
+    }\r
+\r
     HandleMachineMove(message, cps);\r
 }\r
 \r
@@ -12082,6 +12356,64 @@ StringFeature(p, name, loc, cps)
   return FALSE;\r
 }\r
 \r
+int \r
+ParseOption(Option *opt, ChessProgramState *cps)\r
+// [HGM] options: process the string that defines an engine option, and determine\r
+// name, type, default value, and allowed value range\r
+{\r
+       char *p, *q, buf[MSG_SIZ];\r
+       int n, min = (-1)<<31, max = 1<<31, def;\r
+\r
+       if(p = strstr(opt->name, " -spin ")) {\r
+           if((n = sscanf(p, " -spin %d %d %d", &def, &min, &max)) < 3 ) return FALSE;\r
+           if(max < min) max = min; // enforce consistency\r
+           if(def < min) def = min;\r
+           if(def > max) def = max;\r
+           opt->value = def;\r
+           opt->min = min;\r
+           opt->max = max;\r
+           opt->type = Spin;\r
+       } else if(p = strstr(opt->name, " -string ")) {\r
+           opt->textValue = p+9;\r
+           opt->type = TextBox;\r
+       } else if(p = strstr(opt->name, " -check ")) {\r
+           if(sscanf(p, " -check %d", &def) < 1) return FALSE;\r
+           opt->value = (def != 0);\r
+           opt->type = CheckBox;\r
+       } else if(p = strstr(opt->name, " -combo ")) {\r
+           opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type\r
+           cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices\r
+           opt->value = n = 0;\r
+           while(q = StrStr(q, " /// ")) {\r
+               n++; *q = 0;    // count choices, and null-terminate each of them\r
+               q += 5;\r
+               if(*q == '*') { // remember default, which is marked with * prefix\r
+                   q++;\r
+                   opt->value = n;\r
+               }\r
+               cps->comboList[cps->comboCnt++] = q;\r
+           }\r
+           cps->comboList[cps->comboCnt++] = NULL;\r
+           opt->max = n + 1;\r
+           opt->type = ComboBox;\r
+       } else if(p = strstr(opt->name, " -button")) {\r
+           opt->type = Button;\r
+       } else if(p = strstr(opt->name, " -save")) {\r
+           opt->type = SaveButton;\r
+       } else return FALSE;\r
+       *p = 0; // terminate option name\r
+       // now look if the command-line options define a setting for this engine option.\r
+       if(cps->optionSettings && cps->optionSettings[0])\r
+           p = strstr(cps->optionSettings, opt->name); else p = NULL;\r
+       if(p && (p == cps->optionSettings || p[-1] == ',')) {\r
+               sprintf(buf, "option %s", p);\r
+               if(p = strstr(buf, ",")) *p = 0;\r
+               strcat(buf, "\n");\r
+               SendToProgram(buf, cps);\r
+       }\r
+       return TRUE;\r
+}\r
+\r
 void\r
 FeatureDone(cps, val)\r
      ChessProgramState* cps;\r
@@ -12155,6 +12487,16 @@ ParseFeatures(args, cps)
     if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue;\r
     if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
     if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;\r
+    if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {\r
+       ParseOption(&(cps->option[cps->nrOptions++]), cps); // [HGM] options: add option feature\r
+       if(cps->nrOptions >= MAX_OPTIONS) {\r
+           cps->nrOptions--;\r
+           sprintf(buf, "%s engine has too many options\n", cps->which);\r
+           DisplayError(buf, 0);\r
+       }\r
+       continue;\r
+    }\r
+    if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;\r
     /* End of additions by HGM */\r
 \r
     /* unknown feature: complain and skip */\r
@@ -12276,6 +12618,8 @@ DisplayMove(moveNumber)
     char res[MSG_SIZ];\r
     char cpThinkOutput[MSG_SIZ];\r
 \r
+    if(appData.noGUI) return; // [HGM] fast: suppress display of moves\r
+    \r
     if (moveNumber == forwardMostMove - 1 || \r
        gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
 \r
@@ -12313,7 +12657,7 @@ DisplayMove(moveNumber)
     } else {\r
        res[0] = NULLCHAR;\r
     }\r
-    \r
+\r
     if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
        DisplayMessage(res, cpThinkOutput);\r
     } else {\r
@@ -12330,7 +12674,8 @@ DisplayAnalysisText(text)
 {\r
     char buf[MSG_SIZ];\r
 \r
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile \r
+               || appData.icsEngineAnalyze) {\r
        sprintf(buf, "Analysis (%s)", first.tidy);\r
        AnalysisPopUp(buf, text);\r
     }\r
@@ -12366,8 +12711,8 @@ DisplayAnalysis()
     } else {\r
         safeStrCpy( lst, programStats.movelist, sizeof(lst));\r
 \r
-       nps = (((double)programStats.nodes) /\r
-              (((double)programStats.time)/100.0));\r
+        nps = (u64ToDouble(programStats.nodes) /\r
+             ((double)programStats.time /100.0));\r
 \r
        cs = programStats.time % 100;\r
        s = programStats.time / 100;\r
@@ -12378,32 +12723,32 @@ DisplayAnalysis()
 \r
        if (programStats.moves_left > 0 && appData.periodicUpdates) {\r
          if (programStats.move_name[0] != NULLCHAR) {\r
-           sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+           sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
                    programStats.depth,\r
                    programStats.nr_moves-programStats.moves_left,\r
                    programStats.nr_moves, programStats.move_name,\r
                    ((float)programStats.score)/100.0, lst,\r
                    only_one_move(lst)?\r
                    xtra[programStats.got_fail] : "",\r
-                   programStats.nodes, (int)nps, h, m, s, cs);\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
          } else {\r
-           sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+           sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
                    programStats.depth,\r
                    programStats.nr_moves-programStats.moves_left,\r
                    programStats.nr_moves, ((float)programStats.score)/100.0,\r
                    lst,\r
                    only_one_move(lst)?\r
                    xtra[programStats.got_fail] : "",\r
-                   programStats.nodes, (int)nps, h, m, s, cs);\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
          }\r
        } else {\r
-           sprintf(buf, "depth=%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
+           sprintf(buf, "depth=%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",\r
                    programStats.depth,\r
                    ((float)programStats.score)/100.0,\r
                    lst,\r
                    only_one_move(lst)?\r
                    xtra[programStats.got_fail] : "",\r
-                   programStats.nodes, (int)nps, h, m, s, cs);\r
+                   (u64)programStats.nodes, (int)nps, h, m, s, cs);\r
        }\r
     }\r
     DisplayAnalysisText(buf);\r
@@ -12426,16 +12771,16 @@ DisplayComment(moveNumber, text)
                    WhiteOnMove(moveNumber) ? " " : ".. ",\r
                    parseList[moveNumber]);\r
         }\r
+       // [HGM] PV info: display PV info together with (or as) comment\r
+       if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
+           if(text == NULL) text = "";                                           \r
+           score = pvInfoList[moveNumber].score;\r
+           sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
+                              depth, (pvInfoList[moveNumber].time+50)/100, text);\r
+           text = buf;\r
+       }\r
     } else title[0] = 0;\r
 \r
-    // [HGM] PV info: display PV info together with (or as) comment\r
-    if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
-        if(text == NULL) text = "";                                           \r
-        score = pvInfoList[moveNumber].score;\r
-        sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
-                              depth, (pvInfoList[moveNumber].time+50)/100, text);\r
-        CommentPopUp(title, buf);\r
-    } else\r
     if (text != NULL)\r
         CommentPopUp(title, text);\r
 }\r
@@ -12488,9 +12833,9 @@ CheckFlags()
                }\r
            } else {\r
                if (blackFlag) {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell"));\r
                } else {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("White's flag fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("White's flag fell"));\r
                    if (appData.autoCallFlag) {\r
                        GameEnds(BlackWins, "Black wins on time", GE_XBOARD);\r
                        return TRUE;\r
@@ -12510,9 +12855,9 @@ CheckFlags()
                }\r
            } else {\r
                if (whiteFlag) {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("Both flags fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Both flags fell"));\r
                } else {\r
-                    if(gameMode != TwoMachinesPlay) DisplayTitle("Black's flag fell");\r
+                    if(gameMode != TwoMachinesPlay) DisplayTitle(_("Black's flag fell"));\r
                    if (appData.autoCallFlag) {\r
                        GameEnds(WhiteWins, "White wins on time", GE_XBOARD);\r
                        return TRUE;\r
@@ -12560,6 +12905,11 @@ DisplayBothClocks()
    you have neither ftime nor gettimeofday.\r
 */\r
 \r
+/* VS 2008 requires the #include outside of the function */\r
+#if !HAVE_GETTIMEOFDAY && HAVE_FTIME\r
+#include <sys/timeb.h>\r
+#endif\r
+\r
 /* Get the current time as a TimeMark */\r
 void\r
 GetTimeMark(tm)\r
@@ -12577,7 +12927,7 @@ GetTimeMark(tm)
 #else /*!HAVE_GETTIMEOFDAY*/\r
 #if HAVE_FTIME\r
 \r
-#include <sys/timeb.h>\r
+// include <sys/timeb.h> / moved to just above start of function\r
     struct timeb timeB;\r
 \r
     ftime(&timeB);\r
@@ -12998,9 +13348,9 @@ PGNDate()
 \r
 \r
 char *\r
-PositionToFEN(move, useFEN960)\r
+PositionToFEN(move, overrideCastling)\r
      int move;\r
-     int useFEN960;\r
+     char *overrideCastling;\r
 {\r
     int i, j, fromX, fromY, toX, toY;\r
     int whiteToPlay;\r
@@ -13075,6 +13425,9 @@ PositionToFEN(move, useFEN960)
     *p++ = whiteToPlay ? 'w' : 'b';\r
     *p++ = ' ';\r
 \r
+  if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines\r
+    while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' ';\r
+  } else {\r
   if(nrCastlingRights) {\r
      q = p;\r
      if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) {\r
@@ -13132,6 +13485,7 @@ PositionToFEN(move, useFEN960)
     }\r
     *p++ = ' ';\r
   }\r
+  }\r
 \r
     /* [HGM] find reversible plies */\r
     {   int i = 0, j=move;\r
@@ -13389,7 +13743,7 @@ EditPositionPasteFEN(char *fen)
     Board initial_position;\r
 \r
     if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {\r
-      DisplayError("Bad FEN position in clipboard", 0);\r
+      DisplayError(_("Bad FEN position in clipboard"), 0);\r
       return ;\r
     } else {\r
       int savedBlackPlaysFirst = blackPlaysFirst;\r