Don't add PV moves on board clicking in AnalyzeMode
[xboard.git] / backend.c
index 1113781..61a9f1e 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -5,7 +5,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
  *
  * Enhancements Copyright 2005 Alessandro Scotti
  *
@@ -224,6 +224,7 @@ FILE *WriteTourneyFile P((char *results, FILE *f));
 void DisplayTwoMachinesTitle P(());
 static void ExcludeClick P((int index));
 void ToggleSecond P((void));
+void PauseEngine P((ChessProgramState *cps));
 
 #ifdef WIN32
        extern void ConsoleCreate();
@@ -463,7 +464,7 @@ long timeControl_2; /* [AS] Allow separate time controls */
 char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC, activePartner; /* [HGM] secondary TC: merge of MPS, TC and inc */
 long timeRemaining[2][MAX_MOVES];
 int matchGame = 0, nextGame = 0, roundNr = 0;
-Boolean waitingForGame = FALSE;
+Boolean waitingForGame = FALSE, startingEngine = FALSE;
 TimeMark programStartTime, pauseStart;
 char ics_handle[MSG_SIZ];
 int have_set_title = 0;
@@ -785,6 +786,7 @@ InitEngine (ChessProgramState *cps, int n)
     cps->analysisSupport = 2; /* detect */
     cps->analyzing = FALSE;
     cps->initDone = FALSE;
+    cps->reload = FALSE;
 
     /* New features added by Tord: */
     cps->useFEN960 = FALSE;
@@ -838,6 +840,8 @@ InitEngine (ChessProgramState *cps, int n)
 
 ChessProgramState *savCps;
 
+GameMode oldMode;
+
 void
 LoadEngine ()
 {
@@ -847,21 +851,25 @@ LoadEngine ()
     if(gameInfo.variant != StringToVariant(appData.variant)) {
        // we changed variant when loading the engine; this forces us to reset
        Reset(TRUE, savCps != &first);
-       EditGameEvent(); // for consistency with other path, as Reset changes mode
+       oldMode = BeginningOfGame; // to prevent restoring old mode
     }
     InitChessProgram(savCps, FALSE);
-    SendToProgram("force\n", savCps);
+    if(gameMode == EditGame) SendToProgram("force\n", savCps); // in EditGame mode engine must be in force mode
     DisplayMessage("", "");
     if (startedFromSetupPosition) SendBoard(savCps, backwardMostMove);
     for (i = backwardMostMove; i < currentMove; i++) SendMoveToProgram(i, savCps);
     ThawUI();
     SetGNUMode();
+    if(oldMode == AnalyzeMode) AnalyzeModeEvent();
 }
 
 void
 ReplaceEngine (ChessProgramState *cps, int n)
 {
-    EditGameEvent();
+    oldMode = gameMode; // remember mode, so it can be restored after loading sequence is complete
+    keepInfo = 1;
+    if(oldMode != BeginningOfGame) EditGameEvent();
+    keepInfo = 0;
     UnloadEngine(cps);
     appData.noChessProgram = FALSE;
     appData.clockMode = TRUE;
@@ -875,7 +883,7 @@ ReplaceEngine (ChessProgramState *cps, int n)
 extern char *engineName, *engineDir, *engineChoice, *engineLine, *nickName, *params;
 extern Boolean isUCI, hasBook, storeVariant, v1, addToList, useNick;
 
-static char resetOptions[] = 
+static char resetOptions[] =
        "-reuse -firstIsUCI false -firstHasOwnBookUCI true -firstTimeOdds 1 "
        "-firstInitString \"" INIT_STRING "\" -firstComputerString \"" COMPUTER_STRING "\" "
        "-firstFeatures \"\" -firstLogo \"\" -firstAccumulateTC 1 "
@@ -947,7 +955,7 @@ Load (ChessProgramState *cps, int i)
        if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR;
        quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes
        snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s\n",
-                       quote, p, quote, appData.directory[i], 
+                       quote, p, quote, appData.directory[i],
                        useNick ? " -fn \"" : "",
                        useNick ? nickName : "",
                        useNick ? "\"" : "",
@@ -1284,16 +1292,16 @@ ParseTimeControl (char *tc, float ti, int mps)
 
     if(mps)
       snprintf(buf, MSG_SIZ, ":%d/%s+%g", mps, mytc, ti);
-    else 
+    else
       snprintf(buf, MSG_SIZ, ":%s+%g", mytc, ti);
   } else {
     if(mps)
       snprintf(buf, MSG_SIZ, ":%d/%s", mps, mytc);
-    else 
+    else
       snprintf(buf, MSG_SIZ, ":%s", mytc);
   }
   fullTimeControlString = StrSave(buf); // this should now be in PGN format
-  
+
   if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
     return FALSE;
   }
@@ -1845,6 +1853,7 @@ read_from_player (InputSourceRef isr, VOIDSTAR closure, char *message, int count
 {
     int outError, outCount;
     static int gotEof = 0;
+    static FILE *ini;
 
     /* Pass data read from player on to ICS */
     if (count > 0) {
@@ -1853,6 +1862,17 @@ read_from_player (InputSourceRef isr, VOIDSTAR closure, char *message, int count
        if (outCount < count) {
             DisplayFatalError(_("Error writing to ICS"), outError, 1);
        }
+       if(have_sent_ICS_logon == 2) {
+         if(ini = fopen(appData.icsLogon, "w")) { // save first two lines (presumably username & password) on init script file
+           fprintf(ini, "%s", message);
+           have_sent_ICS_logon = 3;
+         } else
+           have_sent_ICS_logon = 1;
+       } else if(have_sent_ICS_logon == 3) {
+           fprintf(ini, "%s", message);
+           fclose(ini);
+         have_sent_ICS_logon = 1;
+       }
     } else if (count < 0) {
        RemoveInputSource(isr);
        DisplayFatalError(_("Error reading from keyboard"), error, 1);
@@ -3438,9 +3458,15 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
                continue;
            }
 
-           if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
-               ICSInitScript();
-               have_sent_ICS_logon = 1;
+           if (looking_at(buf, &i, "login:")) {
+             if (!have_sent_ICS_logon) {
+               if(ICSInitScript())
+                 have_sent_ICS_logon = 1;
+               else // no init script was found
+                 have_sent_ICS_logon = (appData.autoCreateLogon ? 2 : 1); // flag that we should capture username + password
+             } else { // we have sent (or created) the InitScript, but apparently the ICS rejected it
+                 have_sent_ICS_logon = (appData.autoCreateLogon ? 2 : 1); // request creation of a new script
+             }
                continue;
            }
 
@@ -4160,9 +4186,13 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int
 void
 ParseBoard12 (char *string)
 {
+#if ZIPPY
+    int i, takeback;
+    char *bookHit = NULL; // [HGM] book
+#endif
     GameMode newGameMode;
-    int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0, i;
-    int j, k, n, moveNum, white_stren, black_stren, white_time, black_time, takeback;
+    int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;
+    int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
     int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
     char to_play, board_chars[200];
     char move_str[MSG_SIZ], str[MSG_SIZ], elapsed_time[MSG_SIZ];
@@ -4174,7 +4204,6 @@ ParseBoard12 (char *string)
     int fromX, fromY, toX, toY;
     char promoChar;
     int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
-    char *bookHit = NULL; // [HGM] book
     Boolean weird = FALSE, reqFlag = FALSE;
 
     fromX = fromY = toX = toY = -1;
@@ -5397,7 +5426,8 @@ ParsePV (char *pv, Boolean storeComments, Boolean atEnd)
   Boolean valid;
   int nr = 0;
 
-  if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
+  lastParseAttempt = pv; if(!*pv) return;    // turns out we crash when we parse an empty PV
+  if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && currentMove < forwardMostMove) {
     PushInner(currentMove, forwardMostMove); // [HGM] engine might not be thinking on forwardMost position!
     pushed = TRUE;
   }
@@ -5462,6 +5492,8 @@ MultiPV (ChessProgramState *cps)
        return -1;
 }
 
+Boolean extendGame; // signals to UnLoadPV() if walked part of PV has to be appended to game
+
 Boolean
 LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane)
 {
@@ -5493,6 +5525,7 @@ LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane)
        }
        ParsePV(buf+startPV, FALSE, gameMode != AnalyzeMode);
        *start = startPV; *end = index-1;
+       extendGame = (gameMode == AnalyzeMode && appData.autoExtend);
        return TRUE;
 }
 
@@ -5502,7 +5535,7 @@ PvToSAN (char *pv)
        static char buf[10*MSG_SIZ];
        int i, k=0, savedEnd=endPV, saveFMM = forwardMostMove;
        *buf = NULLCHAR;
-       if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV);
+       if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV); // shelve PV of PV-walk
        ParsePV(pv, FALSE, 2); // this appends PV to game, suppressing any display of it
        for(i = forwardMostMove; i<endPV; i++){
            if(i&1) snprintf(buf+k, 10*MSG_SIZ-k, "%s ", parseList[i]);
@@ -5510,6 +5543,7 @@ PvToSAN (char *pv)
            k += strlen(buf+k);
        }
        snprintf(buf+k, 10*MSG_SIZ-k, "%s", lastParseAttempt); // if we ran into stuff that could not be parsed, print it verbatim
+       if(pushed) { PopInner(0); pushed = FALSE; } // restore game continuation shelved by ParsePV
        if(forwardMostMove < savedEnd) { PopInner(0); forwardMostMove = saveFMM; } // PopInner would set fmm to endPV!
        endPV = savedEnd;
        return buf;
@@ -5521,6 +5555,7 @@ LoadPV (int x, int y)
   int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
   lastX = x; lastY = y;
   ParsePV(lastPV[which], FALSE, TRUE); // load the PV of the thinking engine in the boards array.
+  extendGame = FALSE;
   return TRUE;
 }
 
@@ -5531,7 +5566,7 @@ UnLoadPV ()
   if(endPV < 0) return;
   if(appData.autoCopyPV) CopyFENToClipboard();
   endPV = -1;
-  if(gameMode == AnalyzeMode && currentMove > forwardMostMove) {
+  if(extendGame && currentMove > forwardMostMove) {
        Boolean saveAnimate = appData.animate;
        if(pushed) {
            if(shiftKey && storedGames < MAX_VARIATIONS-2) { // wants to start variation, and there is space
@@ -6061,7 +6096,7 @@ InitPosition (int redraw)
       initialPosition[2][0] = BlackAngel;
       initialPosition[6][BOARD_WIDTH-1] = WhiteMarshall;
       initialPosition[5][BOARD_WIDTH-1] = WhiteAngel;
-      initialPosition[1][1] = initialPosition[2][1] = 
+      initialPosition[1][1] = initialPosition[2][1] =
       initialPosition[6][BOARD_WIDTH-2] = initialPosition[5][BOARD_WIDTH-2] = 1;
      }
   if (appData.debugMode) {
@@ -6516,7 +6551,7 @@ OKToStartUserMove (int x, int y)
 }
 
 Boolean
-OnlyMove (int *x, int *y, Boolean captures) 
+OnlyMove (int *x, int *y, Boolean captures)
 {
     DisambiguateClosure cl;
     if (appData.zippyPlay || !appData.testLegality) return FALSE;
@@ -6729,12 +6764,12 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar)
     if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ) {
          if( pup != EmptySquare ) return;
          moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
-          if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n", 
+          if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
                moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]);
           // holdings might not be sent yet in ICS play; we have to figure out which piece belongs here
           if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down
           fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
-          while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++; 
+          while(PieceToChar(fromX) == '.' || PieceToNumber(fromX) != fromY && fromX != (int) EmptySquare) fromX++;
          fromY = DROP_RANK;
     }
 
@@ -7109,7 +7144,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        return;
       }
       doubleClick = FALSE;
-      if(gameMode == AnalyzeMode && pausing && first.excludeMoves) { // use pause state to exclude moves
+      if(gameMode == AnalyzeMode && (pausing || controlKey) && first.excludeMoves) { // use pause state to exclude moves
        doubleClick = TRUE; gatingPiece = boards[currentMove][y][x];
       }
       fromX = x; fromY = y; toX = toY = -1;
@@ -7191,7 +7226,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            }
           }
           if(x == fromX && y == fromY) return; // if OnlyMove altered (x,y) we go on
-          second = FALSE; 
+          second = FALSE;
        }
        // ignore clicks on holdings
        if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
@@ -7593,6 +7628,9 @@ MatingPotential (int pCnt[], int side, int nMine, int nHis, int stale, int bisCo
                        || majors + (12*pCnt[BlackFerz-side] | 6*pCnt[BlackAlfil-side]) > 16; // KCKAA, KCKAX, KCKEEX, KCKEXX (XX!=HH), KCKXXX
                // TO DO: cases wih an unpromoted f-Pawn acting as platform for an opponent Cannon
 
+       } else if(v == VariantKnightmate) {
+               if(nMine == 1) return FALSE;
+               if(nMine == 2 && nHis == 1 && pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side] + pCnt[WhiteKnight+side]) return FALSE; // KBK is only draw
        } else if(pCnt[WhiteKing] == 1 && pCnt[BlackKing] == 1) { // other variants with orthodox Kings
                int nBishops = pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side];
 
@@ -7645,7 +7683,7 @@ Adjudicate (ChessProgramState *cps)
        //       In any case it determnes if the game is a claimable draw (filling in EP_STATUS).
        //       Actually ending the game is now based on the additional internal condition canAdjudicate.
        //       Only when the game is ended, and the opponent is a computer, this opponent gets the move relayed.
-       int k, count = 0; static int bare = 1;
+       int k, drop, count = 0; static int bare = 1;
        ChessProgramState *engineOpponent = (gameMode == TwoMachinesPlay ? cps->other : (cps ? NULL : &first));
        Boolean canAdjudicate = !appData.icsActive;
 
@@ -7729,13 +7767,21 @@ Adjudicate (ChessProgramState *cps)
                        boards[forwardMostMove][EP_STATUS] = nrW == nrB ? EP_STALEMATE :
                                                   ((nrW < nrB) != WhiteOnMove(forwardMostMove) ?
                                                                        EP_CHECKMATE : EP_WINS);
-                   else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi)
+                   else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShogi)
                        boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // and in these variants being stalemated loses
                }
                break;
              case MT_CHECKMATE:
                reason = "Xboard adjudication: Checkmate";
                boards[forwardMostMove][EP_STATUS] = (gameInfo.variant == VariantLosers ? EP_WINS : EP_CHECKMATE);
+               if(gameInfo.variant == VariantShogi) {
+                   if(forwardMostMove > backwardMostMove
+                      && moveList[forwardMostMove-1][1] == '@'
+                      && CharToPiece(ToUpper(moveList[forwardMostMove-1][0])) == WhitePawn) {
+                       reason = "XBoard adjudication: pawn-drop mate";
+                       boards[forwardMostMove][EP_STATUS] = EP_WINS;
+                   }
+               }
                break;
            }
 
@@ -7800,10 +7846,12 @@ Adjudicate (ChessProgramState *cps)
 
                 /* Check for rep-draws */
                 count = 0;
+                drop = gameInfo.holdingsSize && (gameInfo.variant != VariantSuper && gameInfo.variant != VariantSChess
+                                              && gameInfo.variant != VariantGreat && gameInfo.variant != VariantGrand);
                 for(k = forwardMostMove-2;
-                    k>=backwardMostMove && k>=forwardMostMove-100 &&
+                    k>=backwardMostMove && k>=forwardMostMove-100 && (drop ||
                         (signed char)boards[k][EP_STATUS] < EP_UNKNOWN &&
-                        (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE;
+                        (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE);
                     k-=2)
                 {   int rights=0;
                     if(CompareBoards(boards[k], boards[forwardMostMove])) {
@@ -7829,7 +7877,7 @@ Adjudicate (ChessProgramState *cps)
                              /* adjudicate after user-specified nr of repeats */
                             int result = GameIsDrawn;
                             char *details = "XBoard adjudication: repetition draw";
-                            if(gameInfo.variant == VariantXiangqi && appData.testLegality) {
+                            if((gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantShogi) && appData.testLegality) {
                                // [HGM] xiangqi: check for forbidden perpetuals
                                int m, ourPerpetual = 1, hisPerpetual = 1;
                                for(m=forwardMostMove; m>k; m-=2) {
@@ -7847,6 +7895,12 @@ Adjudicate (ChessProgramState *cps)
                                if(hisPerpetual && !ourPerpetual) { // he is checking us, but did not repeat yet
                                    break; // (or we would have caught him before). Abort repetition-checking loop.
                                } else
+                               if(gameInfo.variant == VariantShogi) { // in Shogi other repetitions are draws
+                                   if(BOARD_HEIGHT == 5 && BOARD_RGHT - BOARD_LEFT == 5) { // but in mini-Shogi gote wins!
+                                       result = BlackWins;
+                                       details = "Xboard adjudication: repetition";
+                                   }
+                               } else // it must be XQ
                                // Now check for perpetual chases
                                if(!ourPerpetual && !hisPerpetual) { // no perpetual check, test for chase
                                    hisPerpetual = PerpetualChase(k, forwardMostMove);
@@ -7995,7 +8049,7 @@ LoadError (char *errmess, ChessProgramState *cps)
     if(cps->initDone) return FALSE;
     cps->isr = NULL; // this should suppress further error popups from breaking pipes
     DestroyChildProcess(cps->pr, 9 ); // just to be sure
-    cps->pr = NoProc; 
+    cps->pr = NoProc;
     if(cps == &first) {
        appData.noChessProgram = TRUE;
        gameMode = MachinePlaysBlack; ModeHighlight(); // kludge to unmark Machine Black menu
@@ -8020,6 +8074,8 @@ DeferredBookMove (void)
 }
 
 static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
+static ChessProgramState *stalledEngine;
+static char stashedInputMove[MSG_SIZ];
 
 void
 HandleMachineMove (char *message, ChessProgramState *cps)
@@ -8085,6 +8141,22 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
        (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
     {
+        if(pausing && !cps->pause) { // for pausing engine that does not support 'pause', we stash its move for processing when we resume.
+           if(appData.debugMode) fprintf(debugFP, "pause %s engine after move\n", cps->which);
+           safeStrCpy(stashedInputMove, message, MSG_SIZ);
+           stalledEngine = cps;
+           if(appData.ponderNextMove) { // bring opponent out of ponder
+               if(gameMode == TwoMachinesPlay) {
+                   if(cps->other->pause)
+                       PauseEngine(cps->other);
+                   else
+                       SendToProgram("easy\n", cps->other);
+               }
+           }
+           StopClocks();
+           return;
+       }
+
         /* This method is only useful on engines that support ping */
         if (cps->lastPing != cps->lastPong) {
          if (gameMode == BeginningOfGame) {
@@ -8155,7 +8227,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            /* Machine move could not be parsed; ignore it. */
          snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"),
                    machineMove, _(cps->which));
-           DisplayError(buf1, 0);
+           DisplayMoveError(buf1);
             snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",
                     machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType);
            if (gameMode == TwoMachinesPlay) {
@@ -8352,7 +8424,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
       return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
     }
 
-    if ((!appData.testLegality || gameInfo.variant == VariantFairy) && 
+    if ((!appData.testLegality || gameInfo.variant == VariantFairy) &&
                                        !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
       int dummy, s=6; char buf[MSG_SIZ];
       if(appData.icsActive || forwardMostMove != 0 || cps != &first) return;
@@ -8420,7 +8492,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
          snprintf(buf1, sizeof(buf1), "%swhisper %s\n", ics_prefix, message + 11);
          SendToICS(buf1);
        }
-      }
+      } else if(appData.autoComment) AppendComment (forwardMostMove, message + 11, 1); // in local mode, add as move comment
       return;
     }
     if (!strncmp(message, "tellall ", 8)) {
@@ -9408,11 +9480,11 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                ) {
        /* white pawn promotion */
         board[toY][toX] = CharToPiece(ToUpper(promoChar));
-        if(gameInfo.variant==VariantBughouse ||
-           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+        if(board[toY][toX] < WhiteCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
             board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY >= BOARD_HEIGHT>>1)
+              && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality)
               && (toX != fromX)
                && gameInfo.variant != VariantXiangqi
                && gameInfo.variant != VariantBerolina
@@ -9469,11 +9541,11 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                ) {
        /* black pawn promotion */
        board[toY][toX] = CharToPiece(ToLower(promoChar));
-        if(gameInfo.variant==VariantBughouse ||
-           gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+        if(board[toY][toX] < BlackCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
             board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY < BOARD_HEIGHT>>1)
+              && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality)
               && (toX != fromX)
                && gameInfo.variant != VariantXiangqi
                && gameInfo.variant != VariantBerolina
@@ -9730,12 +9802,12 @@ ShowMove (int fromX, int fromY, int toX, int toY)
     if (instant) return;
 
     DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
     if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
            if (appData.highlightLastMove) { // [HGM] moved to after DrawPosition, as with arrow it could redraw old board
                SetHighlights(fromX, fromY, toX, toY);
            }
     }
+    DrawPosition(FALSE, boards[currentMove]);
     DisplayBothClocks();
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
 }
@@ -9897,6 +9969,33 @@ InitChessProgram (ChessProgramState *cps, int setup)
 
 
 void
+ResendOptions (ChessProgramState *cps)
+{ // send the stored value of the options
+  int i;
+  char buf[MSG_SIZ];
+  Option *opt = cps->option;
+  for(i=0; i<cps->nrOptions; i++, opt++) {
+      switch(opt->type) {
+        case Spin:
+        case Slider:
+        case CheckBox:
+           snprintf(buf, MSG_SIZ, "option %s=%d\n", opt->name, opt->value);
+          break;
+        case ComboBox:
+          snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->choice[opt->value]);
+          break;
+        default:
+           snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->textValue);
+          break;
+        case Button:
+        case SaveButton:
+          continue;
+      }
+      SendToProgram(buf, cps);
+  }
+}
+
+void
 StartChessProgram (ChessProgramState *cps)
 {
     char buf[MSG_SIZ];
@@ -9936,9 +10035,12 @@ StartChessProgram (ChessProgramState *cps)
     cps->isr = AddInputSource(cps->pr, TRUE, ReceiveFromProgram, cps);
     if (cps->protocolVersion > 1) {
       snprintf(buf, MSG_SIZ, "xboard\nprotover %d\n", cps->protocolVersion);
-      cps->nrOptions = 0; // [HGM] options: clear all engine-specific options
-      cps->comboCnt = 0;  //                and values of combo boxes
+      if(!cps->reload) { // do not clear options when reloading because of -xreuse
+        cps->nrOptions = 0; // [HGM] options: clear all engine-specific options
+        cps->comboCnt = 0;  //                and values of combo boxes
+      }
       SendToProgram(buf, cps);
+      if(cps->reload) ResendOptions(cps);
     } else {
       SendToProgram("xboard\n", cps);
     }
@@ -9959,7 +10061,6 @@ TwoMachinesEventIfReady P((void))
     return;
   }
   DisplayMessage("", ""); curMess = 0;
-  ThawUI();
   TwoMachinesEvent();
 }
 
@@ -10019,7 +10120,15 @@ WriteTourneyFile (char *results, FILE *f)
        fprintf(f, "-loadPositionFile \"%s\"\n", appData.loadPositionFile);
        fprintf(f, "-loadPositionIndex %d\n", appData.loadPositionIndex);
        fprintf(f, "-rewindIndex %d\n", appData.rewindIndex);
+       fprintf(f, "-usePolyglotBook %s\n", appData.usePolyglotBook ? "true" : "false");
+       fprintf(f, "-polyglotBook %s\n", appData.polyglotBook);
+       fprintf(f, "-bookDepth %d\n", appData.bookDepth);
+       fprintf(f, "-bookVariation %d\n", appData.bookStrength);
        fprintf(f, "-discourageOwnBooks %s\n", appData.defNoBook ? "true" : "false");
+       fprintf(f, "-defaultHashSize %d\n", appData.defaultHashSize);
+       fprintf(f, "-defaultCacheSizeEGTB %d\n", appData.defaultCacheSizeEGTB);
+       fprintf(f, "-ponderNextMove %s\n", appData.ponderNextMove ? "true" : "false");
+       fprintf(f, "-smpCores %d\n", appData.smpCores);
        if(searchTime > 0)
                fprintf(f, "-searchTime \"%d:%02d\"\n", searchTime/60, searchTime%60);
        else {
@@ -10248,6 +10357,13 @@ SetPlayer (int player, char *p)
        ParseArgsFromString(resetOptions); appData.fenOverride[0] = NULL; appData.pvSAN[0] = FALSE;
        appData.firstHasOwnBookUCI = !appData.defNoBook; appData.protocolVersion[0] = PROTOVER;
        ParseArgsFromString(buf);
+    } else { // no engine with this nickname is installed!
+       snprintf(buf, MSG_SIZ, _("No engine %s is installed"), engineName);
+       ReserveGame(nextGame, ' '); // unreserve game and drop out of match mode with error
+       matchMode = FALSE; appData.matchGames = matchGame = roundNr = 0;
+       ModeHighlight();
+       DisplayError(buf, 0);
+       return 0;
     }
     free(engineName);
     return i;
@@ -10313,7 +10429,7 @@ Pairing (int nr, int nPlayers, int *whitePlayer, int *blackPlayer, int *syncInte
        *blackPlayer = curRound + appData.tourneyType;
     }
 
-    // take care of white/black alternation per round. 
+    // take care of white/black alternation per round.
     // For cycles and games this is already taken care of by default, derived from matchGame!
     return curRound & 1;
 }
@@ -10322,7 +10438,7 @@ int
 NextTourneyGame (int nr, int *swapColors)
 {   // !!!major kludge!!! fiddle appData settings to get everything in order for next tourney game
     char *p, *q;
-    int whitePlayer, blackPlayer, firstBusy=1000000000, syncInterval = 0, nPlayers;
+    int whitePlayer, blackPlayer, firstBusy=1000000000, syncInterval = 0, nPlayers, OK = 1;
     FILE *tf;
     if(appData.tourneyFile[0] == NULLCHAR) return 1; // no tourney, always allow next game
     tf = fopen(appData.tourneyFile, "r");
@@ -10362,7 +10478,7 @@ NextTourneyGame (int nr, int *swapColors)
            SendToProgram(buf, &pairing);
            return 0; // wait for pairing engine to answer (which causes NextTourneyGame to be called again...
        }
-       pairingReceived = 0;                              // ... so we continue here 
+       pairingReceived = 0;                              // ... so we continue here
        *swapColors = 0;
        appData.matchGames = appData.tourneyCycles * syncInterval - 1;
        whitePlayer = savedWhitePlayer-1; blackPlayer = savedBlackPlayer-1;
@@ -10374,18 +10490,18 @@ NextTourneyGame (int nr, int *swapColors)
     // redefine engines, engine dir, etc.
     NamesToList(firstChessProgramNames, command, mnemonic, "all"); // get mnemonics of installed engines
     if(first.pr == NoProc) {
-      SetPlayer(whitePlayer, appData.participants); // find white player amongst it, and parse its engine line
+      if(!SetPlayer(whitePlayer, appData.participants)) OK = 0; // find white player amongst it, and parse its engine line
       InitEngine(&first, 0);  // initialize ChessProgramStates based on new settings.
     }
     if(second.pr == NoProc) {
       SwapEngines(1);
-      SetPlayer(blackPlayer, appData.participants); // find black player amongst it, and parse its engine line
+      if(!SetPlayer(blackPlayer, appData.participants)) OK = 0; // find black player amongst it, and parse its engine line
       SwapEngines(1);         // and make that valid for second engine by swapping
       InitEngine(&second, 1);
     }
     CommonEngineInit();     // after this TwoMachinesEvent will create correct engine processes
     UpdateLogos(FALSE);     // leave display to ModeHiglight()
-    return 1;
+    return OK;
 }
 
 void
@@ -10486,6 +10602,8 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
 
     fromX = fromY = -1; // [HGM] abort any move the user is entering.
 
+    if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit)
+
     if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {
        /* If we are playing on ICS, the server decides when the
           game is over, but the engine can offer to draw, claim
@@ -10578,6 +10696,10 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
                       resultDetails = buf;
                 }
                 /* (Claiming a loss is accepted no questions asked!) */
+           } else if(matchMode && result == GameIsDrawn && !strcmp(resultDetails, "Engine Abort Request")) {
+               forwardMostMove = backwardMostMove; // [HGM] delete game to surpress saving
+               result = GameUnfinished;
+               if(!*appData.tourneyFile) matchGame--; // replay even in plain match
            }
            /* [HGM] bare: don't allow bare King to win */
            if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper
@@ -10622,6 +10744,9 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
                    && lastSavedGame != GameCheckSum() // [HGM] save: suppress duplicates
                                                                ) {
                    if (*appData.saveGameFile != NULLCHAR) {
+                       if(result == GameUnfinished && matchMode && *appData.tourneyFile)
+                           AutoSaveGame(); // [HGM] protect tourney PGN from aborted games, and prompt for name instead
+                       else
                        SaveGameToFile(appData.saveGameFile, TRUE);
                    } else if (appData.autoSaveGames) {
                        AutoSaveGame();
@@ -10629,6 +10754,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
                    if (*appData.savePositionFile != NULLCHAR) {
                        SavePositionToFile(appData.savePositionFile);
                    }
+                   AddGameToBook(FALSE); // Only does something during Monte-Carlo book building
                }
            }
 
@@ -10745,6 +10871,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
            SendToProgram("quit\n", &first);
             DoSleep( appData.delayAfterQuit );
            DestroyChildProcess(first.pr, first.useSigterm);
+           first.reload = TRUE;
        }
        first.pr = NoProc;
     }
@@ -10770,11 +10897,12 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
            SendToProgram("quit\n", &second);
             DoSleep( appData.delayAfterQuit );
            DestroyChildProcess(second.pr, second.useSigterm);
+           second.reload = TRUE;
        }
        second.pr = NoProc;
     }
 
-    if (matchMode && (gameMode == TwoMachinesPlay || waitingForGame && exiting)) {
+    if (matchMode && (gameMode == TwoMachinesPlay || (waitingForGame || startingEngine) && exiting)) {
        char resChar = '=';
         switch (result) {
        case WhiteWins:
@@ -10799,7 +10927,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays)
          break;
        }
 
-       if(waitingForGame) resChar = ' '; // quit while waiting for round sync: unreserve already reserved game
+       if(exiting) resChar = ' '; // quit while waiting for round sync: unreserve already reserved game
        if(appData.tourneyFile[0]){ // [HGM] we are in a tourney; update tourney file with game result
            if(appData.afterGame && appData.afterGame[0]) RunCommand(appData.afterGame);
            ReserveGame(nextGame, resChar); // sets nextGame
@@ -10898,8 +11026,8 @@ ResurrectChessProgram ()
 
     if (appData.noChessProgram) return 1;
 
-    if(matchMode && appData.tourneyFile[0]) { // [HGM] tourney: make sure we get features after engine replacement. (Should we always do this?)
-       if(WaitForEngine(&first, TwoMachinesEventIfReady)) { doInit = 1; return 0; } // request to do init on next visit
+    if(matchMode /*&& appData.tourneyFile[0]*/) { // [HGM] tourney: make sure we get features after engine replacement. (Should we always do this?)
+       if(WaitForEngine(&first, TwoMachinesEventIfReady)) { doInit = 1; return 0; } // request to do init on next visit, because we started engine
        if(!doInit) return 1; // this replaces testing first.pr != NoProc, which is true when we get here, but first time no reason to abort
        doInit = 0; // we fell through (first time after starting the engine); make sure it doesn't happen again
     } else {
@@ -11734,12 +11862,12 @@ InitSearch ()
        if(piece < BlackPawn) piece += BlackPawn; else if(piece < EmptySquare) piece -= BlackPawn; // color-flip
        reverseBoard[r][f] = piece;
     }
-    reverseBoard[EP_STATUS-1] = soughtBoard[EP_STATUS-1] ^ 3; 
+    reverseBoard[EP_STATUS-1] = soughtBoard[EP_STATUS-1] ^ 3;
     for(r=0; r<6; r++) reverseBoard[CASTLING][r] = boards[currentMove][CASTLING][(r+3)%6];
     if(appData.findMirror && appData.searchMode <= 3 && (!nrCastlingRights
-                || (boards[currentMove][CASTLING][2] == NoRights || 
+                || (boards[currentMove][CASTLING][2] == NoRights ||
                     boards[currentMove][CASTLING][0] == NoRights && boards[currentMove][CASTLING][1] == NoRights )
-                && (boards[currentMove][CASTLING][5] == NoRights || 
+                && (boards[currentMove][CASTLING][5] == NoRights ||
                     boards[currentMove][CASTLING][3] == NoRights && boards[currentMove][CASTLING][4] == NoRights ) )
       ) {
        flipSearch = TRUE;
@@ -11761,6 +11889,7 @@ InitSearch ()
 }
 
 GameInfo dummyInfo;
+static int creatingBook;
 
 int
 GameContainsPosition (FILE *f, ListGame *lg)
@@ -12224,6 +12353,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
        cm = (ChessMove) Myylex();
     }
 
+  if(!creatingBook) {
     if (first.pr == NoProc) {
        StartChessProgram(&first);
     }
@@ -12236,6 +12366,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     }
        DisplayBothClocks();
     }
+  }
 
     /* [HGM] server: flag to write setup moves in broadcast file as one */
     loadFlag = appData.suppressLoadMoves;
@@ -12300,11 +12431,10 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList)
     if (oldGameMode == AnalyzeFile ||
        oldGameMode == AnalyzeMode) {
       appData.loadGameIndex = -1; // [HGM] order auto-stepping through games
-      keepInfo = 1;
       AnalyzeFileEvent();
-      keepInfo = 0;
     }
 
+    if(creatingBook) return TRUE;
     if (!matchMode && pos > 0) {
        ToNrEvent(pos); // [HGM] no autoplay if selected on position
     } else
@@ -13339,6 +13469,20 @@ ExitEvent (int status)
 }
 
 void
+PauseEngine (ChessProgramState *cps)
+{
+    SendToProgram("pause\n", cps);
+    cps->pause = 2;
+}
+
+void
+UnPauseEngine (ChessProgramState *cps)
+{
+    SendToProgram("resume\n", cps);
+    cps->pause = 1;
+}
+
+void
 PauseEvent ()
 {
     if (appData.debugMode)
@@ -13346,8 +13490,24 @@ PauseEvent ()
     if (pausing) {
        pausing = FALSE;
        ModeHighlight();
+       if(stalledEngine) { // [HGM] pause: resume game by releasing withheld move
+           StartClocks();
+           if(gameMode == TwoMachinesPlay) { // we might have to make the opponent resume pondering
+               if(stalledEngine->other->pause == 2) UnPauseEngine(stalledEngine->other);
+               else if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine->other);
+           }
+           if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine);
+           HandleMachineMove(stashedInputMove, stalledEngine);
+           stalledEngine = NULL;
+           return;
+       }
        if (gameMode == MachinePlaysWhite ||
-           gameMode == MachinePlaysBlack) {
+           gameMode == TwoMachinesPlay   ||
+           gameMode == MachinePlaysBlack) { // the thinking engine must have used pause mode, or it would have been stalledEngine
+           if(first.pause)  UnPauseEngine(&first);
+           else if(appData.ponderNextMove) SendToProgram("hard\n", &first);
+           if(second.pause) UnPauseEngine(&second);
+           else if(gameMode == TwoMachinesPlay && appData.ponderNextMove) SendToProgram("hard\n", &second);
            StartClocks();
        } else {
            DisplayBothClocks();
@@ -13359,7 +13519,7 @@ PauseEvent ()
            Reset(FALSE, TRUE);
            SendToICS(ics_prefix);
            SendToICS("refresh\n");
-       } else if (currentMove < forwardMostMove) {
+       } else if (currentMove < forwardMostMove && gameMode != AnalyzeMode) {
            ForwardInner(forwardMostMove);
        }
        pauseExamInvalid = FALSE;
@@ -13390,12 +13550,29 @@ PauseEvent ()
          case TwoMachinesPlay:
            if (forwardMostMove == 0)
              return;           /* don't pause if no one has moved */
-           if ((gameMode == MachinePlaysWhite &&
-                !WhiteOnMove(forwardMostMove)) ||
-               (gameMode == MachinePlaysBlack &&
-                WhiteOnMove(forwardMostMove))) {
+           if(gameMode == TwoMachinesPlay) { // [HGM] pause: stop clocks if engine can be paused immediately
+               ChessProgramState *onMove = (WhiteOnMove(forwardMostMove) == (first.twoMachinesColor[0] == 'w') ? &first : &second);
+               if(onMove->pause) {           // thinking engine can be paused
+                   PauseEngine(onMove);      // do it
+                   if(onMove->other->pause)  // pondering opponent can always be paused immediately
+                       PauseEngine(onMove->other);
+                   else
+                       SendToProgram("easy\n", onMove->other);
+                   StopClocks();
+               } else if(appData.ponderNextMove) SendToProgram("easy\n", onMove); // pre-emptively bring out of ponder
+           } else if(gameMode == (WhiteOnMove(forwardMostMove) ? MachinePlaysWhite : MachinePlaysBlack)) { // engine on move
+               if(first.pause) {
+                   PauseEngine(&first);
+                   StopClocks();
+               } else if(appData.ponderNextMove) SendToProgram("easy\n", &first); // pre-emptively bring out of ponder
+           } else { // human on move, pause pondering by either method
+               if(first.pause)
+                   PauseEngine(&first);
+               else if(appData.ponderNextMove)
+                   SendToProgram("easy\n", &first);
                StopClocks();
            }
+           // if no immediate pausing is possible, wait for engine to move, and stop clocks then
          case AnalyzeMode:
            pausing = TRUE;
            ModeHighlight();
@@ -13531,7 +13708,9 @@ AnalyzeFileEvent ()
     }
 
     if (gameMode != AnalyzeMode) {
+       keepInfo = 1; // mere annotating should not alter PGN tags
        EditGameEvent();
+       keepInfo = 0;
        if (gameMode != EditGame) return;
        if (!appData.showThinking) ToggleShowThinking();
        ResurrectChessProgram();
@@ -13544,7 +13723,6 @@ AnalyzeFileEvent ()
     gameMode = AnalyzeFile;
     pausing = FALSE;
     ModeHighlight();
-    SetGameInfo();
 
     StartAnalysisClock();
     GetTimeMark(&lastNodeCountTime);
@@ -13720,7 +13898,7 @@ DisplayTwoMachinesTitle ()
                   gameInfo.white, _("vs."), gameInfo.black,
                   nextGame+1, appData.matchGames+1,
                   appData.tourneyType>0 ? "gt" : appData.tourneyType<0 ? "sw" : "rr");
-        } else 
+        } else
         if (first.twoMachinesColor[0] == 'w') {
          snprintf(buf, MSG_SIZ, "%s %s %s (%d-%d-%d)",
                   gameInfo.white, _("vs."),  gameInfo.black,
@@ -13759,9 +13937,10 @@ WaitForEngine (ChessProgramState *cps, DelayedEventCallback retry)
        StartChessProgram(cps);
        if (cps->protocolVersion == 1) {
          retry();
+         ScheduleDelayedEvent(retry, 1); // Do this also through timeout to avoid recursive calling of 'retry'
        } else {
          /* kludge: allow timeout for initial "feature" command */
-         FreezeUI();
+         if(retry != TwoMachinesEventIfReady) FreezeUI();
          snprintf(buf, MSG_SIZ, _("Starting %s chess program"), _(cps->which));
          DisplayMessage("", buf);
          ScheduleDelayedEvent(retry, FEATURE_TIMEOUT);
@@ -13814,16 +13993,19 @@ TwoMachinesEvent P((void))
 
 //    forwardMostMove = currentMove;
     TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this...
+    startingEngine = TRUE;
 
     if(!ResurrectChessProgram()) return;   /* in case first program isn't running (unbalances its ping due to InitChessProgram!) */
 
-    if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features
+    if(!first.initDone && GetDelayedEvent() == TwoMachinesEventIfReady) return; // [HGM] engine #1 still waiting for feature timeout
     if(first.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position
       ScheduleDelayedEvent(TwoMachinesEventIfReady, 10);
       return;
     }
+    if(WaitForEngine(&second, TwoMachinesEventIfReady)) return; // (if needed:) started up second engine, so wait for features
 
     if(second.protocolVersion >= 2 && !strstr(second.variants, VariantName(gameInfo.variant))) {
+       startingEngine = FALSE;
        DisplayError("second engine does not play this", 0);
        return;
     }
@@ -13857,7 +14039,7 @@ TwoMachinesEvent P((void))
     }
 
     gameMode = TwoMachinesPlay;
-    pausing = FALSE;
+    pausing = startingEngine = FALSE;
     ModeHighlight(); // [HGM] logo: this triggers display update of logos
     SetGameInfo();
     DisplayTwoMachinesTitle();
@@ -14795,7 +14977,7 @@ BackwardInner (int target)
                for(i=target; i>backwardMostMove; i--) { // seek back to start or previous null move
                    if(moveList[i-1][1] == '@' && moveList[i-1][0] == '@') break;
                }
-               SendBoard(&first, i); 
+               SendBoard(&first, i);
              if(second.analyzing) SendBoard(&second, i);
                for(currentMove=i; currentMove<target; currentMove++) {
                    SendMoveToProgram(currentMove, &first);
@@ -15019,6 +15201,40 @@ HintEvent ()
 }
 
 void
+CreateBookEvent ()
+{
+    ListGame * lg = (ListGame *) gameList.head;
+    FILE *f;
+    int nItem;
+    static int secondTime = FALSE;
+
+    if( !(f = GameFile()) || ((ListGame *) gameList.tailPred)->number <= 0 ) {
+        DisplayError(_("Game list not loaded or empty"), 0);
+        return;
+    }
+
+    if(!secondTime && (f = fopen(appData.polyglotBook, "r"))) {
+        fclose(f);
+       secondTime++;
+       DisplayNote(_("Book file exists! Try again for overwrite."));
+       return;
+    }
+
+    creatingBook = TRUE;
+    secondTime = FALSE;
+
+    /* Get list size */
+    for (nItem = 1; nItem <= ((ListGame *) gameList.tailPred)->number; nItem++){
+       LoadGame(f, nItem, "", TRUE);
+       AddGameToBook(TRUE);
+        lg = (ListGame *) lg->node.succ;
+    }
+
+    creatingBook = FALSE;
+    FlushBook();
+}
+
+void
 BookEvent ()
 {
     if (appData.noChessProgram) return;
@@ -15241,7 +15457,7 @@ ReplaceComment (int index, char *text)
     char *p;
     float score;
 
-    if(index && sscanf(text, "%f/%d", &score, &len) == 2 && 
+    if(index && sscanf(text, "%f/%d", &score, &len) == 2 &&
        pvInfoList[index-1].depth == len &&
        fabs(pvInfoList[index-1].score - score*100.) < 0.5 &&
        (p = strchr(text, '\n'))) text = p; // [HGM] strip off first line with PV info, if any
@@ -15554,7 +15770,7 @@ ReceiveFromProgram (InputSourceRef isr, VOIDSTAR closure, char *message, int cou
                   sscanf(message, "tell%c", &c)!=1   && sscanf(message, "0-1 %c", &c)!=1 &&
                   sscanf(message, "1-0 %c", &c)!=1   && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
                   sscanf(message, "setboard %c", &c)!=1   && sscanf(message, "setup %c", &c)!=1 &&
-                  sscanf(message, "hint: %c", &c)!=1 && 
+                  sscanf(message, "hint: %c", &c)!=1 &&
                   sscanf(message, "pong %c", &c)!=1   && start != '#') {
                    quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### ";
                    print = (appData.engineComments >= 2);
@@ -15848,6 +16064,7 @@ FeatureDone (ChessProgramState *cps, int val)
     ScheduleDelayedEvent(cb, val ? 1 : 3600000);
   }
   cps->initDone = val;
+  if(val) cps->reload = FALSE;
 }
 
 /* Parse feature command from engine */
@@ -15892,7 +16109,7 @@ ParseFeatures (char *args, ChessProgramState *cps)
     if (BoolFeature(&p, "exclude", &cps->excludeMoves, cps)) continue;
     if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
     if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
-    if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
+    if (BoolFeature(&p, "pause", &cps->pause, cps)) continue; // [HGM] pause
     if (IntFeature(&p, "done", &val, cps)) {
       FeatureDone(cps, val);
       continue;
@@ -15910,6 +16127,7 @@ ParseFeatures (char *args, ChessProgramState *cps)
     if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
     if (StringFeature(&p, "egt", cps->egtFormats, cps)) continue;
     if (StringFeature(&p, "option", buf, cps)) {
+       if(cps->reload) continue; // we are reloading because of xreuse
        FREE(cps->option[cps->nrOptions].name);
        cps->option[cps->nrOptions].name = malloc(MSG_SIZ);
        safeStrCpy(cps->option[cps->nrOptions].name, buf, MSG_SIZ);
@@ -16036,9 +16254,9 @@ AskQuestionEvent (char *title, char *question, char *replyPrefix, char *which)
 void
 TypeInEvent (char firstChar)
 {
-    if ((gameMode == BeginningOfGame && !appData.icsActive) || 
+    if ((gameMode == BeginningOfGame && !appData.icsActive) ||
         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
-       gameMode == AnalyzeMode || gameMode == EditGame || 
+       gameMode == AnalyzeMode || gameMode == EditGame ||
        gameMode == EditPosition || gameMode == IcsExamining ||
        gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
        isdigit(firstChar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes
@@ -16074,16 +16292,16 @@ TypeInDoneEvent (char *move)
            return;
         }
 
-      if (gameMode != EditGame && currentMove != forwardMostMove && 
+      if (gameMode != EditGame && currentMove != forwardMostMove &&
        gameMode != Training) {
        DisplayMoveError(_("Displayed move is not current"));
       } else {
-       int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, 
+       int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
          &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
        if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized
-       if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, 
+       if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove,
          &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {
-         UserMoveEvent(fromX, fromY, toX, toY, promoChar);     
+         UserMoveEvent(fromX, fromY, toX, toY, promoChar);
        } else {
          DisplayMoveError(_("Could not parse move"));
        }
@@ -17483,3 +17701,59 @@ LoadVariation (int index, char *text)
        ToNrEvent(currentMove+1);
 }
 
+void
+LoadTheme ()
+{
+    char *p, *q, buf[MSG_SIZ];
+    if(engineLine && engineLine[0]) { // a theme was selected from the listbox
+       snprintf(buf, MSG_SIZ, "-theme %s", engineLine);
+       ParseArgsFromString(buf);
+       ActivateTheme(TRUE); // also redo colors
+       return;
+    }
+    p = nickName;
+    if(*p && !strchr(p, '"')) // theme name specified and well-formed; add settings to theme list
+    {
+       int len;
+       q = appData.themeNames;
+       snprintf(buf, MSG_SIZ, "\"%s\"", nickName);
+      if(appData.useBitmaps) {
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt true -lbtf \"%s\" -dbtf \"%s\" -lbtm %d -dbtm %d",
+               appData.liteBackTextureFile, appData.darkBackTextureFile,
+               appData.liteBackTextureMode,
+               appData.darkBackTextureMode );
+      } else {
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt false -lsc %s -dsc %s",
+               Col2Text(2),   // lightSquareColor
+               Col2Text(3) ); // darkSquareColor
+      }
+      if(appData.useBorder) {
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub true -border \"%s\"",
+               appData.border);
+      } else {
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub false");
+      }
+      if(appData.useFont) {
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf true -pf \"%s\" -fptc \"%s\" -fpfcw %s -fpbcb %s",
+               appData.renderPiecesWithFont,
+               appData.fontToPieceTable,
+               Col2Text(9),    // appData.fontBackColorWhite
+               Col2Text(10) ); // appData.fontForeColorBlack
+      } else {
+       snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf false -pid \"%s\"",
+               appData.pieceDirectory);
+       if(!appData.pieceDirectory[0])
+         snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -wpc %s -bpc %s",
+               Col2Text(0),   // whitePieceColor
+               Col2Text(1) ); // blackPieceColor
+      }
+      snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -hsc %s -phc %s\n",
+               Col2Text(4),   // highlightSquareColor
+               Col2Text(5) ); // premoveHighlightColor
+       appData.themeNames = malloc(len = strlen(q) + strlen(buf) + 1);
+       if(insert != q) insert[-1] = NULLCHAR;
+       snprintf(appData.themeNames, len, "%s\n%s%s", q, buf, insert);
+       if(q)   free(q);
+    }
+    ActivateTheme(FALSE);
+}