Merge branch 'v4.8.x'
authorArun Persaud <arun@nubati.net>
Sun, 6 Mar 2016 00:32:37 +0000 (16:32 -0800)
committerArun Persaud <arun@nubati.net>
Sun, 6 Mar 2016 00:32:37 +0000 (16:32 -0800)
26 files changed:
1  2 
args.h
backend.c
backend.h
board.c
common.h
dialogs.c
dialogs.h
draw.c
engineoutput.c
frontend.h
gtk/xboard.c
gtk/xoptions.c
gtk/xtimer.c
menus.c
moves.c
moves.h
parser.c
parser.h
pgntags.c
winboard/defaults.h
winboard/jaws.c
winboard/winboard.c
winboard/woptions.c
winboard/wsettings.c
xaw/xboard.c
xaw/xoptions.c

diff --combined args.h
--- 1/args.h
--- 2/args.h
+++ b/args.h
@@@ -5,7 -5,7 +5,7 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -517,7 -517,6 +517,7 @@@ ArgDescriptor argDescriptors[] = 
    { "themeNames", ArgString, (void *) &appData.themeNames, TRUE, (ArgIniType) "native -upf false -ub false -ubt false -pid \"\"\n" },
    { "addMasterOption", ArgMaster, NULL, FALSE, INVALID },
    { "installEngine", ArgInstall, (void *) &firstChessProgramNames, FALSE, (ArgIniType) "" },
 +  { "installTheme", ArgInstall, (void *) &appData.themeNames, FALSE, (ArgIniType) "" },
    { "initialMode", ArgString, (void *) &appData.initialMode, FALSE, (ArgIniType) "" },
    { "mode", ArgString, (void *) &appData.initialMode, FALSE, INVALID },
    { "variant", ArgString, (void *) &appData.variant, FALSE, (ArgIniType) "normal" },
    { "ub", ArgBoolean, (void *) &appData.useBorder, FALSE, INVALID },
    { "border", ArgFilename, (void *) &appData.border, TRUE, (ArgIniType) "" },
    { "finger", ArgFilename, (void *) &appData.finger, FALSE, (ArgIniType) "" },
 -  { "inscriptions", ArgString, (void *) &appData.inscriptions, XBOARD, (ArgIniType) "" },
 +  { "epd", ArgTrue, (void *) &appData.epd, FALSE, INVALID },
 +  { "inscriptions", ArgString, (void *) &appData.inscriptions, FALSE, (ArgIniType) "" },
    { "autoInstall", ArgString, (void *) &appData.autoInstall, XBOARD, (ArgIniType) "" },
    { "fixedSize", ArgBoolean, (void *) &appData.fixedSize, TRUE, (ArgIniType) FALSE },
  
diff --combined backend.c
+++ b/backend.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -192,6 -193,7 +193,6 @@@ void GameEnds P((ChessMove result, cha
  void EditPositionDone P((Boolean fakeRights));
  void PrintOpponents P((FILE *fp));
  void PrintPosition P((FILE *fp, int move));
 -void StartChessProgram P((ChessProgramState *cps));
  void SendToProgram P((char *message, ChessProgramState *cps));
  void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
  void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
@@@ -295,8 -297,6 +296,8 @@@ int promoDefaultAltered
  int keepInfo = 0; /* [HGM] to protect PGN tags in auto-step game analysis */
  static int initPing = -1;
  int border;       /* [HGM] width of board rim, needed to size seek graph  */
 +char bestMove[MSG_SIZ];
 +int solvingTime, totalTime;
  
  /* States for ics_getting_history */
  #define H_FALSE 0
@@@ -442,7 -442,6 +443,7 @@@ char *currentDebugFile; // [HGM] debug 
  char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
  char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
  char thinkOutput1[MSG_SIZ*10];
 +char promoRestrict[MSG_SIZ];
  
  ChessProgramState first, second, pairing;
  
@@@ -515,7 -514,7 +516,7 @@@ AppData appData
  Board boards[MAX_MOVES];
  /* [HGM] Following 7 needed for accurate legality tests: */
  signed char  castlingRank[BOARD_FILES]; // and corresponding ranks
 -signed char  initialRights[BOARD_FILES];
 +unsigned char initialRights[BOARD_FILES];
  int   nrCastlingRights; // For TwoKings, or to implement castling-unknown status
  int   initialRulePlies, FENrulePlies;
  FILE  *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
@@@ -1740,7 -1739,6 +1741,7 @@@ InitBackEnd3 P((void)
              if(!blackPlaysFirst) {
                  startedFromPositionFile = TRUE;
                  CopyBoard(filePosition, boards[0]);
 +                CopyBoard(initialPosition, boards[0]);
              }
        }
        if (initialMode == AnalyzeMode) {
@@@ -2128,7 -2126,7 +2129,7 @@@ StringToVariant (char *e
      } else
      for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
        if (p = StrCaseStr(e, variantNames[i])) {
 -      if(p && i >= VariantShogi && (p != e || isalpha(p[strlen(variantNames[i])]))) continue;
 +      if(p && i >= VariantShogi && (p != e && !appData.icsActive || isalpha(p[strlen(variantNames[i])]))) continue;
        v = (VariantClass) i;
        found = TRUE;
        break;
@@@ -4162,7 -4160,6 +4163,7 @@@ read_from_ics (InputSourceRef isr, VOID
                          fprintf(debugFP, "Sending premove:\n");
                        SendToICS(str);
                      } else if (gotPremove) {
 +                      int oldFMM = forwardMostMove;
                        gotPremove = 0;
                        ClearPremoveHighlights();
                        if (appData.debugMode)
                            UserMoveEvent(premoveFromX, premoveFromY,
                                        premoveToX, premoveToY,
                                          premovePromoChar);
 +                      if(forwardMostMove == oldFMM) { // premove was rejected, highlight last opponent move
 +                        if(moveList[oldFMM-1][1] != '@')
 +                          SetHighlights(moveList[oldFMM-1][0]-AAA, moveList[oldFMM-1][1]-ONE,
 +                                        moveList[oldFMM-1][2]-AAA, moveList[oldFMM-1][3]-ONE);
 +                        else // (drop)
 +                          SetHighlights(-1, -1, moveList[oldFMM-1][2]-AAA, moveList[oldFMM-1][3]-ONE);
 +                      }
                      }
                    }
  
@@@ -4897,13 -4887,13 +4898,13 @@@ ParseBoard12 (char *string
                    if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
                    old = (k==toY && j==toX) ? boards[moveNum-1][fromY][fromX] : boards[moveNum-1][k][j]; // trace back mover
                    if(old == new) continue;
 -                  if(old == PROMOTED new) boards[moveNum][k][j] = old; // prevent promoted pieces to revert to primordial ones
 +                  if(old == PROMOTED(new)) boards[moveNum][k][j] = old;// prevent promoted pieces to revert to primordial ones
                    else if(new == WhiteWazir || new == BlackWazir) {
                        if(old < WhiteCannon || old >= BlackPawn && old < BlackCannon)
 -                           boards[moveNum][k][j] = PROMOTED old; // choose correct type of Gold in promotion
 +                           boards[moveNum][k][j] = PROMOTED(old); // choose correct type of Gold in promotion
                        else boards[moveNum][k][j] = old; // preserve type of Gold
                    } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!)
 -                      boards[moveNum][k][j] = PROMOTED new; // use non-primordial representation of chosen piece
 +                      boards[moveNum][k][j] = PROMOTED(new); // use non-primordial representation of chosen piece
                }
          } else {
            /* Move from ICS was illegal!?  Punt. */
@@@ -5153,18 -5143,16 +5154,18 @@@ SendMoveToProgram (int moveNum, ChessPr
        } else
        if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square
        char *m = moveList[moveNum];
 +      static char c[2];
 +      *c = m[7]; // promoChar
        if((boards[moveNum][m[6]-ONE][m[5]-AAA] < BlackPawn) == (boards[moveNum][m[1]-ONE][m[0]-AAA] < BlackPawn)) // move is kludge to indicate castling
          snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", m[0], m[1] - '0', // convert to two moves
                                               m[2], m[3] - '0',
                                               m[5], m[6] - '0',
                                               m[2] + (m[0] > m[5] ? 1 : -1), m[3] - '0');
        else
 -        snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", m[0], m[1] - '0', // convert to two moves
 +        snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d%s\n", m[0], m[1] - '0', // convert to two moves
                                               m[5], m[6] - '0',
                                               m[5], m[6] - '0',
 -                                             m[2], m[3] - '0');
 +                                             m[2], m[3] - '0', c);
          SendToProgram(buf, cps);
        } else
        if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
@@@ -5347,7 -5335,7 +5348,7 @@@ UploadGameEvent (
      SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n");
  }
  
 -int killX = -1, killY = -1, kill2X, kill2Y; // [HGM] lion: used for passing e.p. capture square to MakeMove
 +int killX = -1, killY = -1, kill2X = -1, kill2Y = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
  int legNr = 1;
  
  void
@@@ -5368,10 -5356,6 +5369,10 @@@ CoordsToComputerAlgebraic (int rf, int 
        } else {
            sprintf(move, "%c%c%c%c%c\n",
                      AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar);
 +        if(killX >= 0 && killY >= 0) {
 +          sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY);
 +          if(kill2X >= 0 && kill2Y >= 0) sprintf(move+7, "%c%c%c\n", AAA + killX, ONE + killY, promoChar);
 +        }
        }
      }
  }
@@@ -5394,22 -5378,12 +5395,22 @@@ int dragging
  static ClickType lastClickType;
  
  int
 +PieceInString (char *s, ChessSquare piece)
 +{
 +  char *p, ID = ToUpper(PieceToChar(piece)), suffix = PieceSuffix(piece);
 +  while((p = strchr(s, ID))) {
 +    if(!suffix || p[1] == suffix) return TRUE;
 +    s = p;
 +  }
 +  return FALSE;
 +}
 +
 +int
  Partner (ChessSquare *p)
  { // change piece into promotion partner if one shogi-promotes to the other
 -  int stride = gameInfo.variant == VariantChu ? 22 : 11;
 -  ChessSquare partner;
 -  partner = (*p/stride & 1 ? *p - stride : *p + stride);
 +  ChessSquare partner = promoPartner[*p];
    if(PieceToChar(*p) != '+' && PieceToChar(partner) != '+') return 0;
 +  if(PieceToChar(*p) == '+') partner = boards[currentMove][fromY][fromX];
    *p = partner;
    return 1;
  }
@@@ -5432,10 -5406,8 +5433,10 @@@ Sweep (int step
        else if(promoSweep == BlackPawn && step < 0 && !toggleFlag) promoSweep = WhitePawn;
        else if(promoSweep == WhiteKing && step > 0 && !toggleFlag) promoSweep = BlackKing;
        if(!step) step = -1;
 -    } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
 +    } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' ||
            !toggleFlag && PieceToChar(promoSweep) == '+' || // skip promoted versions of other
 +          promoRestrict[0] ? !PieceInString(promoRestrict, promoSweep) : // if choice set available, use it 
 +          promoSweep == pawn ||
            appData.testLegality && (promoSweep == king || gameInfo.variant != VariantChuChess &&
              (promoSweep == WhiteLion || promoSweep == BlackLion)));
      if(toX >= 0) {
@@@ -5570,7 -5542,6 +5571,7 @@@ ParseOneMove (char *move, int moveNum, 
          *toX = currentMoveString[2] - AAA;
          *toY = currentMoveString[3] - ONE;
        *promoChar = currentMoveString[4];
 +      if(*promoChar == ';') *promoChar = currentMoveString[7];
          if (*fromX < BOARD_LEFT || *fromX >= BOARD_RGHT || *fromY < 0 || *fromY >= BOARD_HEIGHT ||
              *toX < BOARD_LEFT || *toX >= BOARD_RGHT || *toY < 0 || *toY >= BOARD_HEIGHT) {
      if (appData.debugMode) {
@@@ -5688,26 -5659,21 +5689,26 @@@ ParsePV (char *pv, Boolean storeComment
  }
  
  int
 -MultiPV (ChessProgramState *cps)
 +MultiPV (ChessProgramState *cps, int kind)
  {     // check if engine supports MultiPV, and if so, return the number of the option that sets it
        int i;
 -      for(i=0; i<cps->nrOptions; i++)
 -          if(!strcmp(cps->option[i].name, "MultiPV") && cps->option[i].type == Spin)
 -              return i;
 +      for(i=0; i<cps->nrOptions; i++) {
 +          char *s = cps->option[i].name;
 +          if((kind & 1) && !StrCaseCmp(s, "MultiPV") && cps->option[i].type == Spin) return i;
 +          if((kind & 2) && StrCaseStr(s, "multi") && StrCaseStr(s, "PV")
 +                        && StrCaseStr(s, "margin") && cps->option[i].type == Spin) return -i-2;
 +      }
        return -1;
  }
  
  Boolean extendGame; // signals to UnLoadPV() if walked part of PV has to be appended to game
 +static int multi, pv_margin;
 +static ChessProgramState *activeCps;
  
  Boolean
  LoadMultiPV (int x, int y, char *buf, int index, int *start, int *end, int pane)
  {
 -      int startPV, multi, lineStart, origIndex = index;
 +      int startPV, lineStart, origIndex = index;
        char *p, buf2[MSG_SIZ];
        ChessProgramState *cps = (pane ? &second : &first);
  
        do{ while(buf[index] && buf[index] != '\n') index++;
        } while(buf[index] == '\n' && buf[index+1] == '\\' && buf[index+2] == ' ' && index++); // join kibitzed PV continuation line
        buf[index] = 0;
 -      if(lineStart == 0 && gameMode == AnalyzeMode && (multi = MultiPV(cps)) >= 0) {
 -              int n = cps->option[multi].value;
 -              if(origIndex > 17 && origIndex < 24) { if(n>1) n--; } else if(origIndex > index - 6) n++;
 +      if(lineStart == 0 && gameMode == AnalyzeMode) {
 +          int n = 0;
 +          if(origIndex > 17 && origIndex < 24) n--; else if(origIndex > index - 6) n++;
 +          if(n == 0) { // click not on "fewer" or "more"
 +              if((multi = -2 - MultiPV(cps, 2)) >= 0) {
 +                  pv_margin = cps->option[multi].value;
 +                  activeCps = cps; // non-null signals margin adjustment
 +              }
 +          } else if((multi = MultiPV(cps, 1)) >= 0) {
 +              n += cps->option[multi].value; if(n < 1) n = 1;
                snprintf(buf2, MSG_SIZ, "option MultiPV=%d\n", n);
                if(cps->option[multi].value != n) SendToProgram(buf2, cps);
                cps->option[multi].value = n;
                *start = *end = 0;
                return FALSE;
 +          }
        } else if(strstr(buf+lineStart, "exclude:") == buf+lineStart) { // exclude moves clicked
                ExcludeClick(origIndex - lineStart);
                return FALSE;
  UnLoadPV ()
  {
    int oldFMM = forwardMostMove; // N.B.: this was currentMove before PV was loaded!
 +  if(activeCps) {
 +    if(pv_margin != activeCps->option[multi].value) {
 +      char buf[MSG_SIZ];
 +      snprintf(buf, MSG_SIZ, "option %s=%d\n", "Multi-PV Margin", pv_margin);
 +      SendToProgram(buf, activeCps);
 +      activeCps->option[multi].value = pv_margin;
 +    }
 +    activeCps = NULL;
 +    return;
 +  }
    if(endPV < 0) return;
    if(appData.autoCopyPV) CopyFENToClipboard();
    endPV = -1;
@@@ -5822,17 -5770,6 +5823,17 @@@ MovePV (int x, int y, int h
  { // step through PV based on mouse coordinates (called on mouse move)
    int margin = h>>3, step = 0, threshold = (pieceSweep == EmptySquare ? 10 : 15);
  
 +  if(activeCps) { // adjusting engine's multi-pv margin
 +    if(x > lastX) pv_margin++; else
 +    if(x < lastX) pv_margin -= (pv_margin > 0);
 +    if(x != lastX) {
 +      char buf[MSG_SIZ];
 +      snprintf(buf, MSG_SIZ, "margin = %d", pv_margin);
 +      DisplayMessage(buf, "");
 +    }
 +    lastX = x;
 +    return;
 +  }
    // we must somehow check if right button is still down (might be released off board!)
    if(endPV < 0 && pieceSweep == EmptySquare) return; // needed in XBoard because lastX/Y is shared :-(
    if(abs(x - lastX) < threshold && abs(y - lastY) < threshold) return;
@@@ -6018,7 -5955,7 +6019,7 @@@ ptclen (const char *s, char *escapes
  {
      int n = 0;
      if(!*escapes) return strlen(s);
 -    while(*s) n += (*s != ':' && !strchr(escapes, *s)), s++;
 +    while(*s) n += (*s != '/' && *s != '-' && *s != '^' && *s != '*' && !strchr(escapes, *s)) - 2*(*s == '='), s++;
      return n;
  }
  
@@@ -6028,57 -5965,27 +6029,57 @@@ SetCharTableEsc (unsigned char *table, 
  /*       Basically a safe strcpy that uses the last character as King */
  {
      int result = FALSE; int NrPieces;
 +    unsigned char partner[EmptySquare];
  
      if( map != NULL && (NrPieces=ptclen(map, escapes)) <= (int) EmptySquare
                      && NrPieces >= 12 && !(NrPieces&1)) {
 -        int i, j = 0; /* [HGM] Accept even length from 12 to 88 */
 +        int i, ii, offs, j = 0; /* [HGM] Accept even length from 12 to 88 */
  
          for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';
 -        for( i=0; i<NrPieces/2-1; i++ ) {
 -            char *p;
 -            if(map[j] == ':' && *escapes) i = CHUPROMOTED WhitePawn, j++;
 -            table[i] = map[j++];
 -            if(p = strchr(escapes, map[j])) j++, table[i] += 64*(p - escapes + 1);
 +        for( i=offs=0; i<NrPieces/2-1; i++ ) {
 +            char *p, c=0;
 +            if(map[j] == '/') offs = WhitePBishop - i, j++;
 +            if(*escapes && (map[j] == '*' || map[j] == '-' || map[j] == '^')) c = map[j++];
 +            table[i+offs] = map[j++];
 +            if(p = strchr(escapes, map[j])) j++, table[i+offs] += 64*(p - escapes + 1);
 +            if(c) partner[i+offs] = table[i+offs], table[i+offs] = c;
 +            if(*escapes && map[j] == '=') pieceNickName[i+offs] = map[++j], j++;
          }
          table[(int) WhiteKing]  = map[j++];
 -        for( i=0; i<NrPieces/2-1; i++ ) {
 -            char *p;
 -            if(map[j] == ':' && *escapes) i = CHUPROMOTED WhitePawn, j++;
 -            table[WHITE_TO_BLACK i] = map[j++];
 -            if(p = strchr(escapes, map[j])) j++, table[WHITE_TO_BLACK i] += 64*(p - escapes + 1);
 +        for( ii=offs=0; ii<NrPieces/2-1; ii++ ) {
 +            char *p, c=0;
 +            if(map[j] == '/') offs = WhitePBishop - ii, j++;
 +            i = WHITE_TO_BLACK ii;
 +            if(*escapes && (map[j] == '*' || map[j] == '-' || map[j] == '^')) c = map[j++];
 +            table[i+offs] = map[j++];
 +            if(p = strchr(escapes, map[j])) j++, table[i+offs] += 64*(p - escapes + 1);
 +            if(c) partner[i+offs] = table[i+offs], table[i+offs] = c;
 +            if(*escapes && map[j] == '=') pieceNickName[i+offs] = map[++j], j++;
          }
          table[(int) BlackKing]  = map[j++];
  
 +
 +        if(*escapes) { // set up promotion pairing
 +            for( i=0; i<(int) EmptySquare; i++ ) promoPartner[i] = (i%BlackPawn < 11 ? i + 11 : i%BlackPawn < 22 ? i - 11 : i); // default
 +            // pieceToChar entirely filled, so we can look up specified partners
 +            for(i=0; i<EmptySquare; i++) { // adjust promotion pairing
 +                int c = table[i];
 +                if(c == '^' || c == '-') { // has specified partner
 +                    int p;
 +                    for(p=0; p<EmptySquare; p++) if(table[p] == partner[i]) break;
 +                    if(c == '^') table[i] = '+';
 +                    if(p < EmptySquare) {
 +                        if(promoPartner[promoPartner[p]] == p) promoPartner[promoPartner[p]] = promoPartner[p]; // divorce old partners
 +                        if(promoPartner[promoPartner[i]] == i) promoPartner[promoPartner[i]] = promoPartner[i];
 +                        promoPartner[p] = i, promoPartner[i] = p; // and marry this couple
 +                    }
 +                } else if(c == '*') {
 +                    table[i] = partner[i];
 +                    promoPartner[i] = (i < BlackPawn ? WhiteTokin : BlackTokin); // promotes to Tokin
 +                }
 +            }
 +        }
 +
          result = TRUE;
      }
  
@@@ -6171,7 -6078,7 +6172,7 @@@ InitPosition (int redraw
        initialPosition[CASTLING][i] = initialRights[i] = NoRights; /* but no rights yet */
      initialPosition[EP_STATUS] = EP_NONE;
      initialPosition[TOUCHED_W] = initialPosition[TOUCHED_B] = 0;
 -    SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k");
 +    SetCharTableEsc(pieceToChar, "PNBRQ...........Kpnbrq...........k", SUFFIXES);
      if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
           SetCharTable(pieceNickName, appData.pieceNickNames);
      else SetCharTable(pieceNickName, "............");
        gameInfo.boardWidth  = 12;
        gameInfo.boardHeight = 12;
        nrCastlingRights = 0;
 -      SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN:+.++.++++++++++.+++++K"
 -                                   "p.brqsexogcathd.vmlifn:+.++.++++++++++.+++++k", SUFFIXES);
 +      SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN.........^T..^L......^A^H/^F^G^M.^E^X^O^I.^P.^B^R..^D^S^C^VK"
 +                                   "p.brqsexogcathd.vmlifn.........^t..^l......^a^h/^f^g^m.^e^x^o^i.^p.^b^r..^d^s^c^vk", SUFFIXES);
        break;
      case VariantCourier:
        pieces = CourierArray;
@@@ -6491,11 -6398,11 +6492,11 @@@ SendBoard (ChessProgramState *cps, int 
          if ((int) *bp < (int) BlackPawn) {
            if(j == BOARD_RGHT+1)
                 snprintf(message, MSG_SIZ, "%c@%d\n", PieceToChar(*bp), bp[-1]);
 -          else snprintf(message, MSG_SIZ, "%c%c%c\n", PieceToChar(*bp), AAA + j, ONE + i);
 +          else snprintf(message, MSG_SIZ, "%c%c%d\n", PieceToChar(*bp), AAA + j, ONE + i - '0');
              if(message[0] == '+' || message[0] == '~') {
 -            snprintf(message, MSG_SIZ,"%c%c%c+\n",
 -                        PieceToChar((ChessSquare)(DEMOTED *bp)),
 -                        AAA + j, ONE + i);
 +            snprintf(message, MSG_SIZ,"%c%c%d+\n",
 +                        PieceToChar((ChessSquare)(DEMOTED(*bp))),
 +                        AAA + j, ONE + i - '0');
              }
              if(cps->alphaRank) { /* [HGM] shogi: translate coords */
                  message[1] = BOARD_RGHT   - 1 - j + '1';
              && ((int) *bp >= (int) BlackPawn)) {
            if(j == BOARD_LEFT-2)
                 snprintf(message, MSG_SIZ, "%c@%d\n", ToUpper(PieceToChar(*bp)), bp[1]);
 -          else snprintf(message,MSG_SIZ, "%c%c%c\n", ToUpper(PieceToChar(*bp)),
 -                    AAA + j, ONE + i);
 +          else snprintf(message,MSG_SIZ, "%c%c%d\n", ToUpper(PieceToChar(*bp)),
 +                    AAA + j, ONE + i - '0');
              if(message[0] == '+' || message[0] == '~') {
 -            snprintf(message, MSG_SIZ,"%c%c%c+\n",
 -                        PieceToChar((ChessSquare)(DEMOTED *bp)),
 -                        AAA + j, ONE + i);
 +            snprintf(message, MSG_SIZ,"%c%c%d+\n",
 +                        PieceToChar((ChessSquare)(DEMOTED(*bp))),
 +                        AAA + j, ONE + i - '0');
              }
              if(cps->alphaRank) { /* [HGM] shogi: translate coords */
                  message[1] = BOARD_RGHT   - 1 - j + '1';
@@@ -6684,8 -6591,9 +6685,8 @@@ HasPromotionChoice (int fromX, int from
  
      piece = boards[currentMove][fromY][fromX];
      if(gameInfo.variant == VariantChu) {
 -        int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece;
          promotionZoneSize = BOARD_HEIGHT/3;
 -        highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion;
 +        highestPromotingPiece = (PieceToChar(piece) == '+' || PieceToChar(CHUPROMOTED(piece)) != '+') ? WhitePawn : WhiteKing;
      } else if(gameInfo.variant == VariantShogi) {
          promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8);
          highestPromotingPiece = (int)WhiteAlfil;
            *promoChoice = PieceToChar(p++);
            if(*promoChoice != '.') break;
        }
 -      return FALSE;
 +      if(!*engineVariant) return FALSE; // if used as parent variant there might be promotion choice
      }
      // no sense asking what we must promote to if it is going to explode...
      if(gameInfo.variant == VariantAtomic && boards[currentMove][toY][toX] != EmptySquare) {
@@@ -6967,7 -6875,7 +6968,7 @@@ int doubleClick
  Boolean addToBookFlag;
  
  void
 -UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar)
 +UserMoveEvent (int fromX, int fromY, int toX, int toY, int promoChar)
  {
      ChessMove moveType;
      ChessSquare pup;
                            "fromY %d, toX %d, toY %d\n",
                            fromX, fromY, toX, toY);
            }
 +            DrawPosition(TRUE, boards[currentMove]); // [HGM] repair animation damage done by premove (in particular emptying from-square)
              return;
        }
        break;
                            "fromY %d, toX %d, toY %d\n",
                            fromX, fromY, toX, toY);
            }
 +            DrawPosition(TRUE, boards[currentMove]);
              return;
        }
        break;
            return;
        } else if (toX >= 0 && toY >= 0) {
            if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) {
 -              ChessSquare q, p = boards[0][rf][ff];
 -              if(p >= BlackPawn) p = BLACK_TO_WHITE p;
 -              if(CHUPROMOTED p < BlackPawn) p = q = CHUPROMOTED boards[0][rf][ff];
 -              else p = CHUDEMOTED (q = boards[0][rf][ff]);
 -              if(PieceToChar(q) == '+') gatingPiece = p;
 +              ChessSquare p = boards[0][rf][ff];
 +              if(PieceToChar(p) == '+') gatingPiece = CHUDEMOTED(p); else
 +              if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p); 
            }
            boards[0][toY][toX] = boards[0][fromY][fromX];
            if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
                }
            } else
            boards[0][fromY][fromX] = gatingPiece;
 +          ClearHighlights();
            DrawPosition(FALSE, boards[currentMove]);
            return;
        }
  
      if(fromY == DROP_RANK && fromX == EmptySquare && (gameMode == AnalyzeMode || gameMode == EditGame || PosFlags(0) & F_NULL_MOVE)) moveType = NormalMove;
  
 +    if(moveType == IllegalMove && legal[toY][toX] > 1) moveType = NormalMove; // someone explicitly told us this move is legal
 +
      /* [HGM] but possibly ignore an IllegalMove result */
      if (appData.testLegality) {
        if (moveType == IllegalMove || moveType == ImpossibleMove) {
      if(addToBookFlag) { // adding moves to book
        char buf[MSG_SIZ], move[MSG_SIZ];
          CoordsToAlgebraic(boards[currentMove], PosFlags(currentMove), fromY, fromX, toY, toX, promoChar, move);
 -      if(killX >= 0) snprintf(move, MSG_SIZ, "%c%dx%c%d-%c%d", fromX + AAA, fromY + ONE - '0', killX + AAA, killY + ONE - '0', toX + AAA, toY + ONE - '0');
 +      if(killX >= 0) snprintf(move, MSG_SIZ, "%c%dx%c%d-%c%d%c", fromX + AAA, fromY + ONE - '0',
 +                                                                 killX + AAA, killY + ONE - '0', toX + AAA, toY + ONE - '0', promoChar);
        snprintf(buf, MSG_SIZ, "  0.0%%     1  %s\n", move);
        AddBookMove(buf);
        addToBookFlag = FALSE;
@@@ -7375,7 -7279,7 +7376,7 @@@ MarkByFEN(char *fen
            int s = 0;
            marker[r][f] = 0;
            if(*fen == 'M') legal[r][f] = 2; else // request promotion choice
 -          if(*fen >= 'A' && *fen <= 'Z') legal[r][f] = 1; else
 +          if(*fen >= 'A' && *fen <= 'Z') legal[r][f] = 3; else
            if(*fen >= 'a' && *fen <= 'z') *fen += 'A' - 'a';
            if(*fen == '/' && f > BOARD_LEFT) f = BOARD_LEFT, r--; else
            if(*fen == 'T') marker[r][f++] = 0; else
@@@ -7407,8 -7311,8 +7408,8 @@@ Mark (Board board, int flags, ChessMov
      if(rf == fromY && ff == fromX && (killX < 0 ? !(rt == rf && ft == ff) && legNr & 1 : rt == killY && ft == killX || legNr & 2))
        (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
                         || kind == WhiteCapturesEnPassant
 -                       || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0), legal[rt][ft] = 1;
 -    else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 1;
 +                       || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0), legal[rt][ft] = 3;
 +    else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3, legal[rt][ft] = 3;
  }
  
  static int hoverSavedValid;
@@@ -7459,8 -7363,8 +7460,8 @@@ CanPromote (ChessSquare piece, int y
        // some variants have fixed promotion piece, no promotion at all, or another selection mechanism
        if(IS_SHOGI(gameInfo.variant)          || gameInfo.variant == VariantXiangqi ||
           gameInfo.variant == VariantSuper    || gameInfo.variant == VariantGreat   ||
 -         gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
 -         gameInfo.variant == VariantMakruk) return FALSE;
 +        (gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier ||
 +           gameInfo.variant == VariantMakruk) && !*engineVariant) return FALSE;
        return (piece == BlackPawn && y <= zone ||
                piece == WhitePawn && y >= BOARD_HEIGHT-1-zone ||
                piece == BlackLance && y <= zone ||
@@@ -7622,7 -7526,7 +7623,7 @@@ LeftClick (ClickType clickType, int xPi
                DragPieceBegin(xPix, yPix, FALSE); dragging = 1;
                if(appData.sweepSelect && CanPromote(piece = boards[currentMove][fromY][fromX], fromY)) {
                    promoSweep = defaultPromoChoice;
 -                  selectFlag = 0; lastX = xPix; lastY = yPix;
 +                  selectFlag = 0; lastX = xPix; lastY = yPix; *promoRestrict = 0;
                    Sweep(0); // Pawn that is going to promote: preview promotion piece
                    DisplayMessage("", _("Pull pawn backwards to under-promote"));
                }
            return;
        }
      }
 -printf("to click %d,%d\n",x,y);
 +
      /* fromX != -1 */
      if (clickType == Press && gameMode != EditPosition) {
        ChessSquare fromP;
                DragPieceBegin(xPix, yPix, FALSE);
                if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) {
                    promoSweep = defaultPromoChoice;
 -                  selectFlag = 0; lastX = xPix; lastY = yPix;
 +                  selectFlag = 0; lastX = xPix; lastY = yPix; *promoRestrict = 0;
                    Sweep(0); // Pawn that is going to promote: preview promotion piece
                }
            }
        // ignore clicks on holdings
        if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
      }
 -printf("A type=%d\n",clickType);
  
 -    if(x == fromX && y == fromY && gameMode == EditPosition && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) {
 +    if(x == fromX && y == fromY && clickType == Press && gameMode == EditPosition && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) {
        gatingPiece = boards[currentMove][fromY][fromX]; // prepare to copy rather than move
 +      DragPieceBegin(xPix, yPix, FALSE); dragging = 1;
        return;
      }
  
      }
  
      clearFlag = 0;
 -printf("B\n");
 +
      if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] &&
         fromX >= BOARD_LEFT && fromX < BOARD_RGHT && (x != killX || y != killY) && !sweepSelecting) {
        if(dragging) DragPieceEnd(xPix, yPix), dragging = 0;
        DrawPosition(TRUE, NULL);
        return; // ignore to-click
      }
 -printf("(%d,%d)-(%d,%d) %d %d\n",fromX,fromY,toX,toY,x,y);
 +
      /* we now have a different from- and (possibly off-board) to-square */
      /* Completed move */
      if(!sweepSelecting) {
        if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) {
          if(appData.sweepSelect) {
            promoSweep = defaultPromoChoice;
 -          if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED piece) == '+') promoSweep = CHUPROMOTED piece;
 +          if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED(piece)) == '+') promoSweep = CHUPROMOTED(piece);
            selectFlag = 0; lastX = xPix; lastY = yPix;
 +          ReportClick("put", x, y); // extra put to prompt engine for 'choice' command
            Sweep(0); // Pawn that is going to promote: preview promotion piece
            sweepSelecting = 1;
            DisplayMessage("", _("Pull pawn backwards to under-promote"));
        }
      } else if(sweepSelecting) { // this must be the up-click corresponding to the down-click that started the sweep
        sweepSelecting = 0; appData.animate = FALSE; // do not animate, a selected piece already on to-square
 +        *promoRestrict = 0;
        if (appData.animate || appData.highlightLastMove) {
            SetHighlights(fromX, fromY, toX, toY);
        } else {
            ClearHighlights();
        }
  #endif
 +      if(PieceToChar(CHUPROMOTED(boards[currentMove][fromY][fromX])) == '+')
 +        defaultPromoChoice = CHUPROMOTED(boards[currentMove][fromY][fromX]);
        if(gameInfo.variant == VariantChuChess && piece != WhitePawn && piece != BlackPawn) defaultPromoChoice = piece;
        if(marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square
          dragging *= 2;            // flag button-less dragging if we are dragging
@@@ -7991,23 -7891,6 +7992,23 @@@ RightClick (ClickType action, int x, in
  }
  
  void
 +Wheel (int dir, int x, int y)
 +{
 +    if(gameMode == EditPosition) {
 +      int xSqr = EventToSquare(x, BOARD_WIDTH);
 +      int ySqr = EventToSquare(y, BOARD_HEIGHT);
 +      if(ySqr < 0 || xSqr < BOARD_LEFT || xSqr >= BOARD_RGHT) return;
 +      if(flipView) xSqr = BOARD_WIDTH - 1 - xSqr; else ySqr = BOARD_HEIGHT - 1 - ySqr;
 +      do {
 +          boards[currentMove][ySqr][xSqr] += dir;
 +          if((int) boards[currentMove][ySqr][xSqr] < WhitePawn) boards[currentMove][ySqr][xSqr] = BlackKing;
 +          if((int) boards[currentMove][ySqr][xSqr] > BlackKing) boards[currentMove][ySqr][xSqr] = WhitePawn;
 +      } while(PieceToChar(boards[currentMove][ySqr][xSqr]) == '.');
 +      DrawPosition(FALSE, boards[currentMove]);
 +    } else if(dir > 0) ForwardEvent(); else BackwardEvent();
 +}
 +
 +void
  SendProgramStatsToFrontend (ChessProgramState * cps, ChessProgramStats * cpstats)
  {
  //    char * hint = lastHint;
@@@ -8225,7 -8108,7 +8226,7 @@@ Adjudicate (ChessProgramState *cps
        // most tests only when we understand the game, i.e. legality-checking on
            if( appData.testLegality )
            {   /* [HGM] Some more adjudications for obstinate engines */
 -              int nrW, nrB, bishopColor, staleW, staleB, nr[EmptySquare+1], i;
 +              int nrW, nrB, bishopColor, staleW, staleB, nr[EmptySquare+2], i;
                static int moveCount = 6;
                ChessMove result;
                char *reason = NULL;
@@@ -8667,7 -8550,7 +8668,7 @@@ DeferredBookMove (void
  
  static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
  static ChessProgramState *stalledEngine;
 -static char stashedInputMove[MSG_SIZ];
 +static char stashedInputMove[MSG_SIZ], abortEngineThink;
  
  void
  HandleMachineMove (char *message, ChessProgramState *cps)
      ChessMove moveType;
      char promoChar, roar;
      char *p, *pv=buf1;
 -    int machineWhite, oldError;
 +    int oldError;
      char *bookHit;
  
      if(cps == &pairing && sscanf(message, "%d-%d", &savedWhitePlayer, &savedBlackPlayer) == 2) {
@@@ -8750,29 -8633,24 +8751,29 @@@ FakeBookMove: // [HGM] book: we jump he
            return;
        }
  
 +      if(cps->usePing) {
 +
          /* This method is only useful on engines that support ping */
 +        if(abortEngineThink) {
 +          if (appData.debugMode) {
 +              fprintf(debugFP, "Undoing move from aborted think of %s\n", cps->which);
 +          }
 +            SendToProgram("undo\n", cps);
 +          return;
 +      }
 +
          if (cps->lastPing != cps->lastPong) {
 -        if (gameMode == BeginningOfGame) {
            /* Extra move from before last new; ignore */
            if (appData.debugMode) {
                fprintf(debugFP, "Ignoring extra move from %s\n", cps->which);
            }
          return;
        }
  
 +      } else {
 +
 +      int machineWhite = FALSE;
 +
        switch (gameMode) {
          case BeginningOfGame:
            /* Extra move from before last reset; ignore */
            }
            return;
        }
 +      }
  
          if(cps->alphaRank) AlphaRank(machineMove, 4);
  
              snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c, %c%c) res=%d",
                      machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, kill2X+AAA, kill2Y+ONE, moveType);
            if (gameMode == TwoMachinesPlay) {
 -            GameEnds(machineWhite ? BlackWins : WhiteWins,
 +            GameEnds(cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
                         buf1, GE_XBOARD);
            }
            return;
              if(moveType == IllegalMove) {
              snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",
                          machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);
 -                GameEnds(machineWhite ? BlackWins : WhiteWins,
 +                GameEnds(cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
                             buf1, GE_XBOARD);
                return;
             } else if(!appData.fischerCastling)
             free(fen);
             GameEnds(GameUnfinished, NULL, GE_XBOARD);
          }
 +        if(appData.epd) {
 +           if(solvingTime >= 0) {
 +              snprintf(buf1, MSG_SIZ, "%d. %4.2fs\n", matchGame, solvingTime/100.);
 +              totalTime += solvingTime; first.matchWins++;
 +           } else {
 +              snprintf(buf1, MSG_SIZ, "%d. wrong (%s)\n", matchGame, parseList[backwardMostMove]);
 +              second.matchWins++;
 +           }
 +           OutputKibitz(2, buf1);
 +           GameEnds(GameUnfinished, NULL, GE_XBOARD);
 +        }
  
          /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */
          if( gameMode == TwoMachinesPlay && appData.adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) {
      }
      if(sscanf(message, "piece %s %s", buf2, buf1) == 2) {
        ChessSquare piece = WhitePawn;
 -      char *p=buf2, *q, *s = SUFFIXES, ID = *p;
 -      if(*p == '+') piece = CHUPROMOTED WhitePawn, ID = *++p;
 +      char *p=message+6, *q, *s = SUFFIXES, ID = *p;
 +      if(*p == '+') piece = CHUPROMOTED(WhitePawn), ID = *++p;
        if(q = strchr(s, p[1])) ID += 64*(q - s + 1), p++;
 -      piece += CharToPiece(ID) - WhitePawn;
 +      piece += CharToPiece(ID & 255) - WhitePawn;
        if(cps != &first || appData.testLegality && *engineVariant == NULLCHAR
        /* always accept definition of  */       && piece != WhiteFalcon && piece != BlackFalcon
        /* wild-card pieces.            */       && piece != WhiteCobra  && piece != BlackCobra
        if(piece < EmptySquare) {
          pieceDefs = TRUE;
          ASSIGN(pieceDesc[piece], buf1);
 -        if(isupper(*p) && p[1] == '&') { ASSIGN(pieceDesc[WHITE_TO_BLACK piece], buf1); }
 +        if((ID & 32) == 0 && p[1] == '&') { ASSIGN(pieceDesc[WHITE_TO_BLACK piece], buf1); }
        }
        return;
      }
 +    if(sscanf(message, "choice %s", promoRestrict) == 1 && promoSweep != EmptySquare) {
 +      promoSweep = CharToPiece(currentMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict));
 +      Sweep(0);
 +      return;
 +    }
      /* [HGM] Allow engine to set up a position. Don't ask me why one would
       * want this, I was asked to put it in, and obliged.
       */
            }
            initPing = -1;
          }
 +      if(cps->lastPing == cps->lastPong && abortEngineThink) {
 +          abortEngineThink = FALSE;
 +          DisplayMessage("", "");
 +          ThawUI();
 +      }
        return;
      }
      if(!strncmp(message, "highlight ", 10)) {
            buf1[0] = NULLCHAR;
            if (sscanf(message, "%d%c %d %d " u64Display " %[^\n]\n",
                       &plylev, &plyext, &curscore, &time, &nodes, buf1) >= 5) {
 +              char score_buf[MSG_SIZ];
  
                if(nodes>>32 == u64Const(0xFFFFFFFF))   // [HGM] negative node count read
                    nodes += u64Const(0x100000000);
  
                if(appData.pvSAN[cps==&second]) pv = PvToSAN(buf1);
  
 +              if(*bestMove) { // rememer time best EPD move was first found
 +                  int ff1, tf1, fr1, tr1, ff2, tf2, fr2, tr2; char pp1, pp2;
 +                  ChessMove mt;
 +                  int ok = ParseOneMove(bestMove, forwardMostMove, &mt, &ff1, &fr1, &tf1, &tr1, &pp1);
 +                  ok    &= ParseOneMove(pv, forwardMostMove, &mt, &ff2, &fr2, &tf2, &tr2, &pp2);
 +                  solvingTime = (ok && ff1==ff2 && fr1==fr2 && tf1==tf2 && tr1==tr2 && pp1==pp2 ? time : -1);
 +              }
 +
                if(serverMoves && (time > 100 || time == 0 && plylev > 7)) {
                        char buf[MSG_SIZ];
                        FILE *f;
                      [AS] Protect the thinkOutput buffer from overflow... this
                      is only useful if buf1 hasn't overflowed first!
                  */
 -              snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "[%d]%c%+.2f %s%s",
 +              if((gameMode == AnalyzeMode && appData.whitePOV || appData.scoreWhite) && !WhiteOnMove(forwardMostMove)) curscore *= -1;
 +              if(curscore >= MATE_SCORE) 
 +                  snprintf(score_buf, MSG_SIZ, "#%d", curscore - MATE_SCORE);
 +              else if(curscore <= -MATE_SCORE) 
 +                  snprintf(score_buf, MSG_SIZ, "#%d", curscore + MATE_SCORE);
 +              else
 +                  snprintf(score_buf, MSG_SIZ, "%+.2f", ((double) curscore) / 100.0);
 +              snprintf(thinkOutput, sizeof(thinkOutput)/sizeof(thinkOutput[0]), "[%d]%c%s %s%s",
                         plylev,
                         (gameMode == TwoMachinesPlay ?
                          ToUpper(cps->twoMachinesColor[0]) : ' '),
 -                       ((double) curscore) / 100.0,
 +                       score_buf,
                         prefixHint ? lastHint : "",
                         prefixHint ? " " : "" );
  
@@@ -10224,11 -10064,6 +10225,11 @@@ ApplyMove (int fromX, int fromY, int to
        board[toY][toX + (killX < fromX ? 1 : -1)] = killed;
          board[EP_STATUS] = EP_NONE; // capture was fake!
      } else
 +    if(nrCastlingRights == 0 && board[toY][toX] < EmptySquare && (piece < BlackPawn) == (board[toY][toX] < BlackPawn)) {
 +        board[fromY][fromX] = board[toY][toX]; // capture own will lead to swapping
 +        board[toY][toX] = piece;
 +        board[EP_STATUS] = EP_NONE; // capture was fake!
 +    } else
      /* Code added by Tord: */
      /* FRC castling assumed when king captures friendly rook. [HGM] or RxK for S-Chess */
      if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook ||
        }
      /* End of code added by Tord */
  
 +    } else if (pieceDesc[piece] && piece == king && !strchr(pieceDesc[piece], 'O') && strchr(pieceDesc[piece], 'i')) {
 +      board[fromY][fromX] = EmptySquare; // never castle if King has virgin moves defined on it other than castling
 +      board[toY][toX] = piece;
      } else if (board[fromY][fromX] == king
          && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */
          && toY == fromY && toX > fromX+1) {
                 ) {
        /* white pawn promotion */
          board[toY][toX] = CharToPiece(ToUpper(promoChar));
 -        if(board[toY][toX] < WhiteCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
 -            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
 +        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 || abs(toX - fromX) > 4)
                 ) {
        /* black pawn promotion */
        board[toY][toX] = CharToPiece(ToLower(promoChar));
 -        if(board[toY][toX] < BlackCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
 -            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
 +        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 || abs(toX - fromX) > 4)
          p = (int) captured;
          if (p >= (int) BlackPawn) {
            p -= (int)BlackPawn;
 -          if(DEMOTED p >= 0 && PieceToChar(p) == '+') {
 +          if(DEMOTED(p) >= 0 && PieceToChar(p) == '+') {
                    /* Restore shogi-promoted piece to its original  first */
 -                  captured = (ChessSquare) (DEMOTED captured);
 -                  p = DEMOTED p;
 +                  captured = (ChessSquare) (DEMOTED(captured));
 +                  p = DEMOTED(p);
            }
            p = PieceToNumber((ChessSquare)p);
            if(p >= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; }
            board[p][BOARD_WIDTH-1] = BLACK_TO_WHITE captured;
        } else {
            p -= (int)WhitePawn;
 -          if(DEMOTED p >= 0 && PieceToChar(p) == '+') {
 -                  captured = (ChessSquare) (DEMOTED captured);
 -                  p = DEMOTED p;
 +          if(DEMOTED(p) >= 0 && PieceToChar(p) == '+') {
 +                  captured = (ChessSquare) (DEMOTED(captured));
 +                  p = DEMOTED(p);
            }
            p = PieceToNumber((ChessSquare)p);
            if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; }
      } else
      if(promoChar == '+') {
          /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */
 -        board[toY][toX] = (ChessSquare) (CHUPROMOTED piece);
 +        board[toY][toX] = (ChessSquare) (CHUPROMOTED(piece));
          if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight))
            board[toY][toX] = piece + WhiteLion - WhiteKnight; // adjust Knight promotions to Lion
      } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
          ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
 -      if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified
 -         && pieceToChar[PROMOTED newPiece] == '~') newPiece = PROMOTED newPiece; // but promoted version available
 +      if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan)  // unpromoted piece specified
 +         && pieceToChar[PROMOTED(newPiece)] == '~') newPiece = PROMOTED(newPiece);// but promoted version available
          board[toY][toX] = newPiece;
      }
      if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
@@@ -10494,10 -10326,10 +10495,10 @@@ MakeMove (int fromX, int fromY, int toX
      if(killX >= 0 && killY >= 0) x = killX, y = killY; // [HGM] lion: make SAN move to intermediate square, if there is one
      (void) CoordsToAlgebraic(boards[forwardMostMove],
                             PosFlags(forwardMostMove),
 -                           fromY, fromX, y, x, promoChar,
 +                           fromY, fromX, y, x, (killX < 0)*promoChar,
                             s);
      if(killX >= 0 && killY >= 0)
 -        sprintf(s + strlen(s), "%c%c%d", p == EmptySquare || toX == fromX && toY == fromY ? '-' : 'x', toX + AAA, toY + ONE - '0');
 +        sprintf(s + strlen(s), "%c%c%d%c", p == EmptySquare || toX == fromX && toY == fromY ? '-' : 'x', toX + AAA, toY + ONE - '0', promoChar);
  
      if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */
          int timeLeft; static int lastLoadFlag=0; int king, piece;
@@@ -11561,17 -11393,16 +11562,17 @@@ GameEnds (ChessMove result, char *resul
               && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway
               && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course...
               && result != GameIsDrawn)
 -          {   int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);
 +          {   int i, j, k=0, oppoKings = 0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);
                for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {
                        int p = (signed char)boards[forwardMostMove][i][j] - color;
                        if(p >= 0 && p <= (int)WhiteKing) k++;
 +                      oppoKings += (p + color == WhiteKing + BlackPawn - color);
                }
                if (appData.debugMode) {
                     fprintf(debugFP, "GE(%d, %s, %d) bare king k=%d color=%d\n",
                        result, resultDetails ? resultDetails : "(null)", whosays, k, color);
                }
 -              if(k <= 1) {
 +              if(k <= 1 && oppoKings > 0) { // the latter needed in Atomic, where bare K wins if opponent King already destroyed
                        result = GameIsDrawn;
                        snprintf(buf, MSG_SIZ, "%s but bare king", resultDetails);
                        resultDetails = buf;
@@@ -12158,13 -11989,13 +12159,13 @@@ LoadGameOneMove (ChessMove readAhead
        case BlackASideCastleFR:
        /* POP Fabien */
        if (appData.debugMode)
 -        fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
 +        fprintf(debugFP, "Parsed %s into %s virgin=%x,%x\n", yy_text, currentMoveString, boards[forwardMostMove][TOUCHED_W], boards[forwardMostMove][TOUCHED_B]);
          fromX = currentMoveString[0] - AAA;
          fromY = currentMoveString[1] - ONE;
          toX = currentMoveString[2] - AAA;
          toY = currentMoveString[3] - ONE;
        promoChar = currentMoveString[4];
 -      if(promoChar == ';') promoChar = NULLCHAR;
 +      if(promoChar == ';') promoChar = currentMoveString[7];
        break;
  
        case WhiteDrop:
@@@ -13118,7 -12949,7 +13119,7 @@@ LoadGame (FILE *f, int gameNumber, cha
        gameInfo.event = StrSave(yy_text);
      }
  
 -    startedFromSetupPosition = FALSE;
 +    startedFromSetupPosition = startedFromPositionFile; // [HGM]
      while (cm == PGNTag) {
        if (appData.debugMode)
          fprintf(debugFP, "Parsed PGNTag: %s\n", yy_text);
            return FALSE;
          }
          CopyBoard(boards[0], initial_position);
 -        if(*engineVariant) // [HGM] for now, assume FEN in engine-defined variant game is default initial position
 +        if(*engineVariant || gameInfo.variant == VariantFairy) // [HGM] for now, assume FEN in engine-defined variant game is default initial position
            CopyBoard(initialPosition, initial_position);
          if (blackPlaysFirst) {
            currentMove = forwardMostMove = backwardMostMove = 1;
@@@ -13482,14 -13313,10 +13483,14 @@@ LoadPosition (FILE *f, int positionNumb
      }
  
      if (fenMode) {
 +      char *p;
        if (!ParseFEN(initial_position, &blackPlaysFirst, line, TRUE)) {
            DisplayError(_("Bad FEN position in file"), 0);
            return FALSE;
        }
 +      if((p = strstr(line, ";")) && (p = strstr(p+1, "bm "))) { // EPD with best move
 +          sscanf(p+3, "%s", bestMove);
 +      } else *bestMove = NULLCHAR;
      } else {
        (void) fgets(line, MSG_SIZ, f);
        (void) fgets(line, MSG_SIZ, f);
@@@ -15112,16 -14939,6 +15113,16 @@@ EditGameEvent (
        case MachinePlaysBlack:
        case BeginningOfGame:
        SendToProgram("force\n", &first);
 +      if(gameMode == (forwardMostMove & 1 ? MachinePlaysBlack : MachinePlaysWhite)) { // engine is thinking
 +          if (first.usePing) { // [HGM] always send ping when we might interrupt machine thinking
 +              char buf[MSG_SIZ];
 +              abortEngineThink = TRUE;
 +              snprintf(buf, MSG_SIZ, "ping %d\n", initPing = ++first.lastPing);
 +              SendToProgram(buf, &first);
 +              DisplayMessage("Aborting engine think", "");
 +              FreezeUI();
 +          }
 +      }
        SetUserThinkingEnables();
        break;
        case PlayFromGameFile:
@@@ -15455,7 -15272,7 +15456,7 @@@ EditPositionMenuEvent (ChessSquare sele
        case PromotePiece:
          if(piece >= (int)WhitePawn && piece < (int)WhiteMan ||
             piece >= (int)BlackPawn && piece < (int)BlackMan   ) {
 -            selection = (ChessSquare) (PROMOTED piece);
 +            selection = (ChessSquare) (PROMOTED(piece));
          } else if(piece == EmptySquare) selection = WhiteSilver;
          else selection = (ChessSquare)((int)piece - 1);
          goto defaultlabel;
        case DemotePiece:
          if(piece > (int)WhiteMan && piece <= (int)WhiteKing ||
             piece > (int)BlackMan && piece <= (int)BlackKing   ) {
 -            selection = (ChessSquare) (DEMOTED piece);
 +            selection = (ChessSquare) (DEMOTED(piece));
          } else if(piece == EmptySquare) selection = BlackSilver;
          else selection = (ChessSquare)((int)piece + 1);
          goto defaultlabel;
@@@ -16315,7 -16132,7 +16316,7 @@@ PrintPosition (FILE *fp, int move
      for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
          for (j = BOARD_LEFT; j < BOARD_RGHT; j++) {
            char c = PieceToChar(boards[move][i][j]);
 -          fputc(c == 'x' ? '.' : c, fp);
 +          fputc(c == '?' ? '.' : c, fp);
              fputc(j == BOARD_RGHT - 1 ? '\n' : ' ', fp);
        }
      }
@@@ -17032,7 -16849,6 +17033,7 @@@ ParseOption (Option *opt, ChessProgramS
        char *p, *q, buf[MSG_SIZ];
        int n, min = (-1)<<31, max = 1<<31, def;
  
 +      opt->target = &opt->value;   // OK for spin/slider and checkbox
        if(p = strstr(opt->name, " -spin ")) {
            if((n = sscanf(p, " -spin %d %d %d", &def, &min, &max)) < 3 ) return FALSE;
            if(max < min) max = min; // enforce consistency
        } else if((p = strstr(opt->name, " -string "))) {
            opt->textValue = p+9;
            opt->type = TextBox;
 +          opt->target = &opt->textValue;
        } else if((p = strstr(opt->name, " -file "))) {
            // for now -file is a synonym for -string, to already provide compatibility with future polyglots
 -          opt->textValue = p+7;
 +          opt->target = opt->textValue = p+7;
            opt->type = FileName; // FileName;
 +          opt->target = &opt->textValue;
        } else if((p = strstr(opt->name, " -path "))) {
            // for now -file is a synonym for -string, to already provide compatibility with future polyglots
 -          opt->textValue = p+7;
 +          opt->target = opt->textValue = p+7;
            opt->type = PathName; // PathName;
 +          opt->target = &opt->textValue;
        } else if(p = strstr(opt->name, " -check ")) {
            if(sscanf(p, " -check %d", &def) < 1) return FALSE;
            opt->value = (def != 0);
@@@ -17129,9 -16942,9 +17130,9 @@@ FeatureDone (ChessProgramState *cps, in
        (cb == TwoMachinesEventIfReady)) {
      CancelDelayedEvent();
      ScheduleDelayedEvent(cb, val ? 1 : 3600000);
 -  }
 +  } else if(!val && !cps->reload) ClearOptions(cps); // let 'spurious' done=0 clear engine's option list
    cps->initDone = val;
 -  if(val) cps->reload = FALSE;
 +  if(val) cps->reload = FALSE,  RefreshSettingsDialog(cps, val);
  }
  
  /* Parse feature command from engine */
@@@ -18046,7 -17859,7 +18047,7 @@@ char 
  PositionToFEN (int move, char *overrideCastling, int moveCounts)
  {
      int i, j, fromX, fromY, toX, toY;
 -    int whiteToPlay;
 +    int whiteToPlay, haveRights = nrCastlingRights;
      char buf[MSG_SIZ];
      char *p, *q;
      int emptycount;
                  if(PieceToChar(piece) == '+') {
                      /* [HGM] write promoted pieces as '+<unpromoted>' (Shogi) */
                      *p++ = '+';
 -                    piece = (ChessSquare)(CHUDEMOTED piece);
 +                    piece = (ChessSquare)(CHUDEMOTED(piece));
                  }
                  *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece));
                  if(*p = PieceSuffix(piece)) p++;
                  if(p[-1] == '~') {
                      /* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */
 -                    p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED piece));
 +                    p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED(piece)));
                      *p++ = '~';
                  }
            }
      *p++ = whiteToPlay ? 'w' : 'b';
      *p++ = ' ';
  
 +  if(pieceDesc[WhiteKing] && strchr(pieceDesc[WhiteKing], 'i') && !strchr(pieceDesc[WhiteKing], 'O')) { // redefined without castling
 +    haveRights = 0; q = p;
 +    for(i=BOARD_RGHT-1; i>=BOARD_LEFT; i--) {
 +      piece = boards[move][0][i];
 +      if(piece >= WhitePawn && piece <= WhiteKing && pieceDesc[piece] && strchr(pieceDesc[piece], 'i')) { // piece with initial move
 +        if(!(boards[move][TOUCHED_W] & 1<<i)) *p++ = 'A' + i; // print file ID if it has not moved
 +      }
 +    }
 +    for(i=BOARD_RGHT-1; i>=BOARD_LEFT; i--) {
 +      piece = boards[move][BOARD_HEIGHT-1][i];
 +      if(piece >= BlackPawn && piece <= BlackKing && pieceDesc[piece] && strchr(pieceDesc[piece], 'i')) { // piece with initial move
 +        if(!(boards[move][TOUCHED_B] & 1<<i)) *p++ = 'a' + i; // print file ID if it has not moved
 +      }
 +    }
 +    if(p == q) *p++ = '-';
 +    *p++ = ' ';
 +  }
 +
    if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines
      while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; else --p;
    } else {
 -  if(nrCastlingRights) {
 +  if(haveRights) {
       int handW=0, handB=0;
       if(gameInfo.variant == VariantSChess) { // for S-Chess, all virgin backrank pieces must be listed
        for(i=0; i<BOARD_HEIGHT; i++) handW += boards[move][i][BOARD_RGHT]; // count white held pieces
          /* [HGM] write true castling rights */
          if( nrCastlingRights == 6 ) {
              int q, k=0;
 -            if(boards[move][CASTLING][0] == BOARD_RGHT-1 &&
 +            if(boards[move][CASTLING][0] != NoRights &&
                 boards[move][CASTLING][2] != NoRights  ) k = 1, *p++ = 'K';
 -            q = (boards[move][CASTLING][1] == BOARD_LEFT &&
 +            q = (boards[move][CASTLING][1] != NoRights &&
                   boards[move][CASTLING][2] != NoRights  );
              if(handW) { // for S-Chess with pieces in hand, list virgin pieces between K and Q
                  for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q; i--)
              }
            if(q) *p++ = 'Q';
              k = 0;
 -            if(boards[move][CASTLING][3] == BOARD_RGHT-1 &&
 +            if(boards[move][CASTLING][3] != NoRights &&
                 boards[move][CASTLING][5] != NoRights  ) k = 1, *p++ = 'k';
 -            q = (boards[move][CASTLING][4] == BOARD_LEFT &&
 +            q = (boards[move][CASTLING][4] != NoRights &&
                   boards[move][CASTLING][5] != NoRights  );
              if(handB) {
                  for(i=BOARD_RGHT-1-k; i>=BOARD_LEFT+q; i--)
@@@ -18267,7 -18062,7 +18268,7 @@@ ParseFEN (Board board, int *blackPlaysF
      int i, j, k, w=0, subst=0, shuffle=0, wKingRank = -1, bKingRank = -1;
      char *p, c;
      int emptycount, virgin[BOARD_FILES];
 -    ChessSquare piece;
 +    ChessSquare piece, king = (gameInfo.variant == VariantKnightmate ? WhiteUnicorn : WhiteKing);
  
      p = fen;
  
                  while (emptycount--)
                          board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;
                  if (*p == '/') p++;
 -              else if(autoSize) { // we stumbled unexpectedly into end of board
 +              else if(autoSize && i != BOARD_HEIGHT-1) { // we stumbled unexpectedly into end of board
                      for(k=i; k<BOARD_HEIGHT; k++) { // too few ranks; shift towards bottom
                        for(j=0; j<BOARD_WIDTH; j++) board[k-i][j] = board[k][j];
                      }
                      if(q = strchr(s, p[1])) p++;
                      piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0));
                      if(piece == EmptySquare) return FALSE; /* unknown piece */
 -                    piece = (ChessSquare) (CHUPROMOTED piece ); p++;
 +                    piece = (ChessSquare) (CHUPROMOTED(piece)); p++;
                      if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */
                  } else {
                      char c = *p++;
  
                  if(piece==EmptySquare) return FALSE; /* unknown piece */
                  if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */
 -                    piece = (ChessSquare) (PROMOTED piece);
 +                    piece = (ChessSquare) (PROMOTED(piece));
                      if(PieceToChar(piece) != '~') return FALSE; /* cannot be a promoted piece */
                      p++;
                  }
                  board[i][(j++)+gameInfo.holdingsWidth] = piece;
 -                if(piece == WhiteKing) wKingRank = i;
 -                if(piece == BlackKing) bKingRank = i;
 +                if(piece == king) wKingRank = i;
 +                if(piece == WHITE_TO_BLACK king) bKingRank = i;
            } else {
                return FALSE;
            }
      }
      while (*p == '/' || *p == ' ') p++;
  
 -    if(autoSize) appData.NrFiles = w, InitPosition(TRUE);
 +    if(autoSize && w != 0) appData.NrFiles = w, InitPosition(TRUE);
  
      /* [HGM] by default clear Crazyhouse holdings, if present */
      if(gameInfo.holdingsWidth) {
  
      /* set defaults in case FEN is incomplete */
      board[EP_STATUS] = EP_UNKNOWN;
 +    board[TOUCHED_W] = board[TOUCHED_B] = 0;
      for(i=0; i<nrCastlingRights; i++ ) {
          board[CASTLING][i] =
              appData.fischerCastling ? NoRights : initialRights[i];
                                  && board[castlingRank[5]][initialRights[5]] != BlackKing) board[CASTLING][5] = NoRights;
      FENrulePlies = 0;
  
 +    if(pieceDesc[WhiteKing] && strchr(pieceDesc[WhiteKing], 'i') && !strchr(pieceDesc[WhiteKing], 'O')) { // redefined without castling
 +      char *q = p;
 +      int w=0, b=0;
 +      while(isalpha(*p)) {
 +        if(isupper(*p)) w |= 1 << (*p++ - 'A');
 +        if(islower(*p)) b |= 1 << (*p++ - 'a');
 +      }
 +      if(*p == '-') p++;
 +      if(p != q) {
 +        board[TOUCHED_W] = ~w;
 +        board[TOUCHED_B] = ~b;
 +        while(*p == ' ') p++;
 +      }
 +    } else
 +
      if(nrCastlingRights) {
        int fischer = 0;
        if(gameInfo.variant == VariantSChess) for(i=0; i<BOARD_FILES; i++) virgin[i] = 0;
          }
          if(gameInfo.variant == VariantTwoKings || gameInfo.variant == VariantKnightmate)
              whiteKingFile = blackKingFile = BOARD_WIDTH >> 1; // for these variant scanning fails
 -        if(whiteKingFile == NoRights || board[0][whiteKingFile] != WhiteUnicorn
 -                                     && board[0][whiteKingFile] != WhiteKing) whiteKingFile = NoRights;
 -        if(blackKingFile == NoRights || board[BOARD_HEIGHT-1][blackKingFile] != BlackUnicorn
 -                                     && board[BOARD_HEIGHT-1][blackKingFile] != BlackKing) blackKingFile = NoRights;
 +        if(whiteKingFile == NoRights || board[castlingRank[2]][whiteKingFile] != WhiteUnicorn
 +                                     && board[castlingRank[2]][whiteKingFile] != WhiteKing) whiteKingFile = NoRights;
 +        if(blackKingFile == NoRights || board[castlingRank[5]][blackKingFile] != BlackUnicorn
 +                                     && board[castlingRank[5]][blackKingFile] != BlackKing) blackKingFile = NoRights;
          switch(c) {
            case'K':
                for(i=BOARD_RGHT-1; board[castlingRank[2]][i]!=WhiteRook && i>whiteKingFile; i--);
diff --combined backend.h
+++ b/backend.h
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -217,8 -218,6 +218,8 @@@ void ics_printf P((char *format, ...))
  int GetEngineLine P((char *nick, int engine));
  void AddGameToBook P((int always));
  void FlushBook P((void));
 +char PieceToChar P((ChessSquare p));
 +int LoadPieceDesc P((char *s));
  
  char *StrStr P((char *string, char *match));
  char *StrCaseStr P((char *string, char *match));
@@@ -418,15 -417,13 +419,15 @@@ char *EngineDefinedVariant P((ChessProg
  void SettingsPopUp P((ChessProgramState *cps)); // [HGM] really in front-end, but CPS not known in frontend.h
  int WaitForEngine P((ChessProgramState *cps, DelayedEventCallback x));
  void Load P((ChessProgramState *cps, int n));
 -int MultiPV P((ChessProgramState *cps));
 +int MultiPV P((ChessProgramState *cps, int kind));
  void MoveHistorySet P(( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo ));
  void MakeEngineOutputTitle P((void));
  void LoadTheme P((void));
  void CreateBookEvent P((void));
  char *SupportedVariant P((char *list, VariantClass v, int w, int h, int s, int proto, char *engine));
  char *CollectPieceDescriptors P((void));
 +void RefreshSettingsDialog P((ChessProgramState *cps, int val));
 +void StartChessProgram P((ChessProgramState *cps));
  
  
  /* A point in time */
diff --combined board.c
+++ b/board.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * The following terms apply to Digital Equipment Corporation's copyright
   * interest in XBoard:
@@@ -670,7 -671,6 +671,7 @@@ ChangeDragPiece (ChessSquare piece
  {
    anims[Player].dragPiece = piece;
    SetDragPiece(Player, piece);
 +  damage[0][fromY][fromX] = True;
  }
  
  void
@@@ -1190,10 -1190,10 +1191,10 @@@ ArrowDamage (int s_col, int s_row, int 
      int hor, vert, i, n = partnerUp * twoBoards;
      hor = 64*s_col + 32; vert = 64*s_row + 32;
      for(i=0; i<= 64; i++) {
 -            damage[n][vert+6>>6][hor+6>>6] |= 2;
 -            damage[n][vert-6>>6][hor+6>>6] |= 2;
 -            damage[n][vert+6>>6][hor-6>>6] |= 2;
 -            damage[n][vert-6>>6][hor-6>>6] |= 2;
 +            damage[n][vert+8>>6][hor+8>>6] |= 2;
 +            damage[n][vert-8>>6][hor+8>>6] |= 2;
 +            damage[n][vert+8>>6][hor-8>>6] |= 2;
 +            damage[n][vert-8>>6][hor-8>>6] |= 2;
              hor += d_col - s_col; vert += d_row - s_row;
      }
  }
@@@ -1212,20 -1212,20 +1213,20 @@@ DrawArrowBetweenSquares (int s_col, in
      SquareToPos( s_row, s_col, &s_x, &s_y);
      SquareToPos( d_row, d_col, &d_x, &d_y);
  
 -    if( d_y > s_y ) {
 +    if( d_y > s_y && d_y - s_y > abs(d_x - s_x)/2) {
          d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
      }
 -    else if( d_y < s_y ) {
 +    else if( d_y < s_y && s_y - d_y > abs(d_x - d_y)/2) {
          d_y += squareSize / 2 + squareSize / 4;
      }
      else {
          d_y += squareSize / 2;
      }
  
 -    if( d_x > s_x ) {
 +    if( d_x > s_x && d_x - s_x > abs(d_y - s_y)/2) {
          d_x += squareSize / 2 - squareSize / 4;
      }
 -    else if( d_x < s_x ) {
 +    else if( d_x < s_x && s_x - d_x > abs(d_y - s_y)/2) {
          d_x += squareSize / 2 + squareSize / 4;
      }
      else {
diff --combined common.h
+++ b/common.h
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -226,7 -227,6 +227,7 @@@ typedef char *String
  #define BELLCHAR                '\007'
  #define NULLCHAR                '\000'
  #define FEATURE_TIMEOUT         10000 /*ms*/
 +#define MATE_SCORE              100000
  
  #define CLOCK_FONT 0
  #define MESSAGE_FONT 1
@@@ -294,26 -294,18 +295,26 @@@ typedef enum 
      WhiteFerz, WhiteAlfil, WhiteAngel, WhiteMarshall, WhiteWazir, WhiteMan,
      WhiteCannon, WhiteNightrider, WhiteCardinal, WhiteDragon, WhiteGrasshopper,
      WhiteSilver, WhiteFalcon, WhiteLance, WhiteCobra, WhiteUnicorn, WhiteLion,
 -    WhiteTokin, WhiteClaw, WhitePCardinal, WhitePDragon, WhiteCat,
 -    WhitePSword, WhiteMonarch, WhiteMother, WhiteNothing, WhitePRook, WhitePDagger,
 -    WhiteDolphin, WhiteStag, WhiteHorned, WhiteEagle, WhiteSword,
 -    WhiteCrown, WhiteHCrown, WhiteHorse, WhiteDrunk, WhitePBishop, WhiteKing,
 +    WhiteSword, WhiteZebra, WhiteCamel, WhiteTower, WhiteWolf,
 +    WhiteHat, WhiteDuck, WhiteAmazon, WhiteFlying, WhiteGnu, WhiteCub,
 +    WhiteShield, WhiteHorse, WhiteWizard, WhiteCopper, WhiteIron,
 +    WhiteViking, WhiteFlag, WhiteAxe, WhiteDolphin, WhiteCat, WhiteClaw,
 +    WhiteWheel, WhiteButterfly, WhitePBishop, WhitePRook, WhiteHCrown,
 +    WhiteShierd, WhiteMonarch, WhiteMother, WhiteNothing, WhiteDrunk, WhiteWheer,
 +    WhiteTokin, WhitePKnight, WhitePCardinal, WhitePDragon, WhitePLance,
 +    WhitePSilver, WhiteDagger, WhitePSword, WhitePDagger, WhiteCrown, WhiteKing,
      BlackPawn, BlackKnight, BlackBishop, BlackRook, BlackQueen,
      BlackFerz, BlackAlfil, BlackAngel, BlackMarshall, BlackWazir, BlackMan,
      BlackCannon, BlackNightrider, BlackCardinal, BlackDragon, BlackGrasshopper,
      BlackSilver, BlackFalcon, BlackLance, BlackCobra, BlackUnicorn, BlackLion,
 -    BlackTokin, BlackClaw, BlackPCardinal, BlackPDragon, BlackCat,
 -    BlackPSword, BlackMonarch, BlackMother, BlackNothing, BlackPRook, BlackPDagger,
 -    BlackDolphin, BlackStag, BlackHorned, BlackEagle, BlackSword,
 -    BlackCrown, BlackHCrown, BlackHorse, BlackDrunk, BlackPBishop, BlackKing,
 +    BlackSword, BlackZebra, BlackCamel, BlackTower, BlackWolf,
 +    BlackHat, BlackDuck, BlackAmazon, BlackFlying, BlackGnu, BlackCub,
 +    BlackShield, BlackHorse, BlackWizard, BlackCopper, BlackIron,
 +    BlackViking, BlackFlag, BlackAxe, BlackDolphin, BlackCat, BlackClaw,
 +    BlackWheel, BlackButterfly, BlackPBishop, BlackPRook, BlackHCrown,
 +    BlackShierd, BlackMonarch, BlackMother, BlackNothing, BlackDrunk, BlackWheer,
 +    BlackTokin, BlackPKnight, BlackPCardinal, BlackPDragon, BlackPLance,
 +    BlackPSilver, BlackDagger, BlackPSword, BlackPDagger, BlackCrown, BlackKing,
      EmptySquare, DarkSquare,
      NoRights, // [HGM] gamestate: for castling rights hidden in board[CASTLING]
      ClearBoard, WhitePlay, BlackPlay, PromotePiece, DemotePiece /*for use on EditPosition menus*/
  /* [HGM] some macros that can be used as prefixes to convert piece types */
  #define WHITE_TO_BLACK (int)BlackPawn - (int)WhitePawn + (int)
  #define BLACK_TO_WHITE (int)WhitePawn - (int)BlackPawn + (int)
 -#define PROMOTED       (int)WhiteDragon - (int)WhiteRook + (int)
 -#define DEMOTED        (int)WhiteRook - (int)WhiteDragon + (int)
 +#define PROMO          (int)WhiteDragon - (int)WhiteRook + (int)
 +#define PROMOTED(X)    (promoPartner[X])
 +#define DEMOTED(X)     (promoPartner[X])
  #define SHOGI          (int)EmptySquare + (int)
 -#define CHUPROMOTED    ((int)WhitePDragon - (int)WhiteDragon)*(gameInfo.variant == VariantChu) + PROMOTED
 -#define CHUDEMOTED     ((int)WhiteDragon - (int)WhitePDragon)*(gameInfo.variant == VariantChu) + DEMOTED
 +#define CHUPROMOTED(X) (promoPartner[X])
 +#define CHUDEMOTED(X)  (promoPartner[X])
  #define IS_SHOGI(V)    ((V) == VariantShogi || (V) == VariantChu)
  #define IS_LION(V)     ((V) == WhiteLion || (V) == BlackLion)
  
@@@ -532,7 -523,6 +533,7 @@@ typedef struct 
      Boolean fischerCastling;/* [HGM] fischer: allow Fischr castling in any variant */
      Boolean matchMode;
      int matchGames;
 +    Boolean epd;
      Boolean monoMode;
      Boolean debugMode;
      Boolean clockMode;
diff --combined dialogs.c
+++ b/dialogs.c
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * dialogs.c -- platform-independent code for dialogs of XBoard
   *
-  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
   * ------------------------------------------------------------------------
   *
   * GNU XBoard is free software: you can redistribute it and/or modify
@@@ -93,13 -93,11 +93,13 @@@ in
  SetCurrentComboSelection (Option *opt)
  {
      int j;
 +    if(currentCps) ; else
      if(!opt->textValue) opt->value = *(int*)opt->target; /* numeric */else {
        for(j=0; opt->choice[j]; j++) // look up actual value in list of possible values, to get selection nr
            if(*(char**)opt->target && !strcmp(*(char**)opt->target, ((char**)opt->textValue)[j])) break;
        opt->value = j + (opt->choice[j] == NULL);
      }
 +    SetComboChoice(opt, opt->value);
      return opt->value;
  }
  
@@@ -333,10 -331,8 +333,10 @@@ UpgradeParticipant (
  static void
  PseudoOK ()
  {
 +    if(matchMode) return;
      GenericReadout(matchOptions, -2); // read all, but suppress calling of MatchOK
      ASSIGN(appData.participants, engineName);
 +    ASSIGN(appData.tourneyFile, tfName);
      PopDown(MasterDlg); // early popdown to prevent FreezeUI called through MatchEvent from causing XtGrab warning
  }
  
@@@ -1462,22 -1458,6 +1462,22 @@@ SecondSettingsProc (
     SettingsPopUp(&second);
  }
  
 +void
 +RefreshSettingsDialog (ChessProgramState *cps, int val)
 +{
 +   if(val == 1) { // option values changed
 +      if(shellUp[TransientDlg] && cps == currentCps) {
 +         GenericUpdate(cps->option, -1); // normally update values when dialog is up
 +      }
 +      return; // and be done
 +   }
 +   if(val == 2) { // option list changed
 +      if(!shellUp[TransientDlg] || cps != currentCps) return; // our dialog is not up, so nothing to do
 +   }
 +   PopDown(TransientDlg); // make sure any other dialog closes first
 +   SettingsPopUp(cps);    // and popup new one
 +}
 +
  //----------------------------------------------- Load Engine --------------------------------------
  
  char *engineDir, *engineLine, *nickName, *params;
@@@ -1689,10 -1669,6 +1689,10 @@@ SetTcType (int n
  void
  TimeControlProc ()
  {
 +   if(gameMode != BeginningOfGame) {
 +      DisplayError(_("Changing time control during a game is not implemented"), 0);
 +      return;
 +   }
     tmpMoves = appData.movesPerSession;
     tmpInc = appData.timeIncrement; if(tmpInc < 0) tmpInc = 0;
     tmpOdds1 = tmpOdds2 = 1; tcType = 0;
@@@ -1795,7 -1771,7 +1795,7 @@@ PromotionPopUp (char choice
  { // choice depends on variant: prepare dialog acordingly
    count = 8;
    SetPromo(_("Cancel"), --count, -1); // Beware: GenericPopUp cannot handle user buttons named "cancel" (lowe case)!
 -  if(choice != '+') {
 +  if(choice != '+' && !IS_SHOGI(gameInfo.variant)) {
      if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
          gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
          gameInfo.variant == VariantGiveaway) {
@@@ -1968,7 -1944,6 +1968,7 @@@ OutputChatMessage (int partner, char *m
      char *p = texts[partner];
      int len = strlen(mess) + 1;
  
 +    if(!DialogExists(ChatDlg)) return;
      if(p) len += strlen(p);
      texts[partner] = (char*) malloc(len);
      snprintf(texts[partner], len, "%s%s", p ? p : "", mess);
@@@ -2286,8 -2261,8 +2286,8 @@@ ErrorPopUp (char *title, char *label, i
  {
      errorUp = True;
      errorOptions[1].name = label;
 -    if(dialogError = shellUp[TransientDlg])
 -      GenericPopUp(errorOptions+1, title, FatalDlg, TransientDlg, MODAL, 0); // pop up as daughter of the transient dialog
 +    if(dialogError = shellUp[MasterDlg])
 +      GenericPopUp(errorOptions+1, title, FatalDlg, MasterDlg, MODAL, 0); // pop up as daughter of the transient dialog
      else
        GenericPopUp(errorOptions+modal, title, modal ? FatalDlg: ErrorDlg, BoardWindow, modal, 0); // kludge: option start address indicates modality
  }
@@@ -2579,7 -2554,7 +2579,7 @@@ MenuCallback (int n
  static Option *
  Exp (int n, int x, int y)
  {
 -    static int but1, but3, oldW, oldH;
 +    static int but1, but3, oldW, oldH, oldX, oldY;
      int menuNr = -3, sizing, f, r;
      TimeMark now;
      extern Boolean right;
      }
  
      if(n == 0) { // motion
 +      oldX = x; oldY = y;
        if(SeekGraphClick(Press, x, y, 1)) return NULL;
        if((but1 || dragging == 2) && !PromoScroll(x, y)) DragPieceMove(x, y);
        if(but3) MovePV(x, y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
        case  3: menuNr = RightClick(Press,   x, y, &pmFromX, &pmFromY), but3 = 1; break;
        case -2: shiftKey = !shiftKey;
        case -3: menuNr = RightClick(Release, x, y, &pmFromX, &pmFromY), but3 = 0; break;
 -      case  4: BackwardEvent(); break;
 -      case  5: ForwardEvent(); break;
 +      case  4: Wheel(-1, oldX, oldY); break;
 +      case  5: Wheel(1, oldX, oldY); break;
        case 10:
            sizing = (oldW != x || oldH != y);
            oldW = x; oldH = y;
diff --combined dialogs.h
+++ b/dialogs.h
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * dialogs.h -- shared variables for generic dialog popup of XBoard
   *
-  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
   * ------------------------------------------------------------------------
   *
   * GNU XBoard is free software: you can redistribute it and/or modify
@@@ -154,7 -154,6 +154,7 @@@ void SetWidgetText  P((Option *opt, cha
  void GetWidgetState  P((Option *opt, int *state));
  void SetWidgetState  P((Option *opt, int state));
  void SetWidgetLabel P((Option *opt, char *buf));
 +void SetComboChoice  P((Option *opt, int choice));
  void SetDialogTitle  P((DialogClass dlg, char *title));
  void LoadListBox P((Option *opt, char *emptyText, int n1, int n2));
  void HighlightListBoxItem P((Option *opt, int nr));
diff --combined draw.c
--- 1/draw.c
--- 2/draw.c
+++ b/draw.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * The following terms apply to Digital Equipment Corporation's copyright
   * interest in XBoard:
@@@ -54,9 -55,9 +55,9 @@@
  #include <stdio.h>
  #include <math.h>
  #include <cairo/cairo.h>
 -#include <cairo/cairo-xlib.h>
  #include <librsvg/rsvg.h>
  #include <librsvg/rsvg-cairo.h>
 +#include <pango/pangocairo.h>
  
  #if STDC_HEADERS
  # include <stdlib.h>
@@@ -105,9 -106,9 +106,9 @@@ extern char *getenv()
  Boolean cairoAnimate;
  Option *currBoard;
  cairo_surface_t *csBoardWindow;
 -static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4];   // png 256 x 256 images
 -static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn];    // scaled pieces as used
 -static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
 +static cairo_surface_t *pngPieceImages[2][(int)BlackPawn];   // png 256 x 256 images
 +static cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn];  // scaled pieces as used
 +static cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn]; // scaled pieces in store
  static RsvgHandle *svgPieces[2][(int)BlackPawn+4]; // vector pieces in store
  static cairo_surface_t *pngBoardBitmap[2], *pngOriginalBoardBitmap[2];
  int useTexture, textureW[2], textureH[2];
@@@ -143,9 -144,9 +144,9 @@@ SelectPieces(VariantClass v
           pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
        if(v == VariantShogi && BOARD_HEIGHT != 7) { // no exceptions in Tori Shogi
           pngPieceBitmaps[i][(int)WhiteCannon] = pngPieceBitmaps2[i][(int)WhiteTokin];
 -         pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteKing+2];
 -         pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhiteKing+3];
 -         pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhiteKing+4];
 +         pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhitePKnight];
 +         pngPieceBitmaps[i][(int)WhiteGrasshopper] = pngPieceBitmaps2[i][(int)WhitePLance];
 +         pngPieceBitmaps[i][(int)WhiteSilver] = pngPieceBitmaps2[i][(int)WhitePSilver];
           pngPieceBitmaps[i][(int)WhiteQueen] = pngPieceBitmaps2[i][(int)WhiteLance];
           pngPieceBitmaps[i][(int)WhiteFalcon] = pngPieceBitmaps2[i][(int)WhiteMonarch]; // for Sho Shogi
        }
        if(v == VariantChu) {
           pngPieceBitmaps[i][(int)WhiteNightrider] = pngPieceBitmaps2[i][(int)WhiteClaw];
           pngPieceBitmaps[i][(int)WhiteClaw]    = pngPieceBitmaps2[i][(int)WhiteNightrider];
 -         pngPieceBitmaps[i][(int)WhiteUnicorn] = pngPieceBitmaps2[i][(int)WhiteHorned];
 -         pngPieceBitmaps[i][(int)WhiteSilver]  = pngPieceBitmaps2[i][(int)WhiteStag];
 -         pngPieceBitmaps[i][(int)WhiteFalcon]  = pngPieceBitmaps2[i][(int)WhiteEagle];
 -         pngPieceBitmaps[i][(int)WhiteHorned]  = pngPieceBitmaps2[i][(int)WhiteUnicorn];
 -         pngPieceBitmaps[i][(int)WhiteStag]    = pngPieceBitmaps2[i][(int)WhiteSilver];
 -         pngPieceBitmaps[i][(int)WhiteEagle]   = pngPieceBitmaps2[i][(int)WhiteFalcon];
 +         pngPieceBitmaps[i][(int)WhiteUnicorn] = pngPieceBitmaps2[i][(int)WhiteCat];
 +         pngPieceBitmaps[i][(int)WhiteSilver]  = pngPieceBitmaps2[i][(int)WhiteSword];
 +         pngPieceBitmaps[i][(int)WhiteFalcon]  = pngPieceBitmaps2[i][(int)WhiteDagger];
 +         pngPieceBitmaps[i][(int)WhiteCat]     = pngPieceBitmaps2[i][(int)WhiteUnicorn];
 +         pngPieceBitmaps[i][(int)WhiteSword]   = pngPieceBitmaps2[i][(int)WhiteSilver];
 +         pngPieceBitmaps[i][(int)WhiteDagger]  = pngPieceBitmaps2[i][(int)WhiteFalcon];
 +         pngPieceBitmaps[i][(int)WhiteMan]     = pngPieceBitmaps2[i][(int)WhiteCopper];
 +         pngPieceBitmaps[i][(int)WhiteCopper]  = pngPieceBitmaps2[i][(int)WhiteMan];
 +         pngPieceBitmaps[i][(int)WhiteAxe]     = pngPieceBitmaps2[i][(int)WhiteCannon];
 +         pngPieceBitmaps[i][(int)WhiteCannon]  = pngPieceBitmaps2[i][(int)WhiteAxe];
        }
      }
  }
@@@ -252,7 -249,7 +253,7 @@@ CreatePNGBoard (char *s, int kind
      textureW[kind] = 0; // prevents bitmap from being used if not succesfully loaded
      if(strstr(s, ".png")) {
        cairo_surface_t *img = cairo_image_surface_create_from_png (s);
 -      if(img) {
 +      if(cairo_surface_status(img) == CAIRO_STATUS_SUCCESS) {
            char c, *p = s, *q;
            int r, f;
            if(pngOriginalBoardBitmap[kind]) cairo_surface_destroy(pngOriginalBoardBitmap[kind]);
  char *pngPieceNames[] = // must be in same order as internal piece encoding
  { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
    "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Crown", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "Lion",
 -  "GoldPawn", "Claw", "PromoHorse", "PromoDragon", "GoldLance", "PromoSword", "Prince", "Phoenix", "Kylin", "PromoRook", "PromoHSword",
 -  "Dolphin", "Sword", "Leopard", "HSword", "GoldSilver", "Princess", "HCrown", "Knight", "Elephant", "PromoBishop", "King",
 -  "Claw", "GoldKnight", "GoldLance", "GoldSilver", NULL
 +  "Sword", "Zebra", "Camel", "Tower", "Wolf", "Hat", "Duck", "Lance", "Dragon", "Gnu", "Cub",
 +  "LShield", "Pegasus", "Wizard", "Copper", "Iron", "Viking", "Flag", "Axe", "Dolphin", "Leopard", "Claw",
 +  "Left", "Butterfly", "PromoBishop", "PromoRook", "HCrown", "RShield", "Prince", "Phoenix", "Kylin", "Drunk", "Right",
 +  "GoldPawn", "GoldKnight", "PromoHorse", "PromoDragon", "GoldLance", "GoldSilver", "HSword", "PromoSword", "PromoHSword", "Princess", "King",
 +  NULL
  };
  
 -char *backupPiece[] = { "Princess", NULL, NULL, NULL, NULL, NULL, NULL,
 -                      NULL, NULL, NULL, NULL, NULL, NULL, "King", "Queen", "Lion" }; // pieces that map on other when not kanji
 +char *backupPiece[] = { // pieces that map on other in default theme ("Crown" - "Drunk")
 +  "Princess", NULL, NULL, NULL, NULL, NULL, NULL,
 +  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Chancellor", NULL,
 +  NULL, "Knight", NULL, "Commoner", NULL, NULL, NULL, "Canon", NULL, NULL, NULL,
 +  NULL, NULL, NULL, NULL, NULL, NULL, "King", "Queen", "Lion", "Elephant"
 +};
  
  RsvgHandle *
  LoadSVG (char *dir, int color, int piece, int retry)
  
      snprintf(buf, MSG_SIZ, "%s/%s%s.svg", dir, color ? "Black" : "White", name);
  
 -    if(svg || *dir && (svg = rsvg_handle_new_from_file(buf, &svgerror))) {
 +    if(!svg && *dir) {
 +      svg = rsvg_handle_new_from_file(buf, &svgerror);
 +      if(!svg && *appData.inscriptions) { // if there is no piece-specific SVG, but we make inscriptions, try general background
 +      snprintf(buf, MSG_SIZ, "%s/%sTile.svg", dir, color ? "Black" : "White");
 +      svg = rsvg_handle_new_from_file(buf, &svgerror);
 +      }
 +    }
  
 +    if(svg) {
        rsvg_handle_get_dimensions(svg, &svg_dimensions);
        img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, squareSize,  squareSize);
  
  
        return svg;
      }
 -    if(!retry && piece >= WhiteGrasshopper && piece <= WhiteNothing) // pieces that are only different in kanji sets
 +    if(!retry && piece >= WhiteGrasshopper && piece <= WhiteDrunk) // pieces that are only different in kanji sets
          return LoadSVG(dir, color, piece, 1);
      if(svgerror)
        g_error_free(svgerror);
@@@ -374,10 -358,9 +375,10 @@@ ScaleOnePiece (int color, int piece
  
    if(!pngPieceImages[color][piece]) { // we still did not manage to acquire a piece bitmap
      static int warned = 0;
 -    if(!(svgPieces[color][piece] = LoadSVG(svgDir, color, piece, 0)) && !warned) { // try to fall back on installed svg 
 +    if(!(svgPieces[color][piece] = LoadSVG(svgDir, color, piece, 0)) // try to fall back on installed svg 
 +       && !warned && strcmp(pngPieceNames[piece], "Tile")) {         // but do not complain about missing 'Tile'
        char *msg = _("No default pieces installed!\nSelect your own using '-pieceImageDirectory'.");
 -      printf("%s\n", msg); // give up
 +      printf("%s (%s)\n", msg, pngPieceNames[piece]); // give up
        DisplayError(msg, 0);
        warned = 1; // prevent error message being repeated for each piece type
      }
@@@ -663,25 -646,18 +664,25 @@@ DrawLogo (Option *opt, void *logo
      cairo_t *cr;
      int w, h;
  
 -    if(!logo || !opt) return;
 -    img = cairo_image_surface_create_from_png (logo);
 -    w = cairo_image_surface_get_width (img);
 -    h = cairo_image_surface_get_height (img);
 +    if(!opt) return;
      cr = cairo_create(DRAWABLE(opt));
 -//    cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
 -    cairo_scale(cr, (float)opt->max/w, (float)opt->value/h);
 -    cairo_set_source_surface (cr, img, 0, 0);
 -    cairo_paint (cr);
 +    cairo_rectangle (cr, 0, 0, opt->max, opt->value);
 +    cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
 +    cairo_fill(cr); // paint background in case logo does not exist
 +    if(logo) {
 +        img = cairo_image_surface_create_from_png (logo);
 +        if(cairo_surface_status(img) == CAIRO_STATUS_SUCCESS) {
 +          w = cairo_image_surface_get_width (img);
 +          h = cairo_image_surface_get_height (img);
 +//        cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
 +          cairo_scale(cr, (float)opt->max/w, (float)opt->value/h);
 +          cairo_set_source_surface (cr, img, 0, 0);
 +          cairo_paint (cr);
 +        }
 +      cairo_surface_destroy (img);
 +    }
      cairo_destroy (cr);
 -    cairo_surface_destroy (img);
 -    GraphExpose(opt, 0, 0, appData.logoSize, appData.logoSize/2);
 +    GraphExpose(opt, 0, 0, opt->max, opt->value);
  }
  
  static void
@@@ -763,34 -739,6 +764,34 @@@ DrawDot (int marker, int x, int y, int 
  }
  
  static void
 +DrawUnicode (cairo_surface_t *canvas, char *string, int x, int y, char id, int flip)
 +{
 +//    cairo_text_extents_t te;
 +      cairo_t *cr;
 +      int s = 1 - 2*flip;
 +      PangoLayout *layout;
 +      PangoFontDescription *desc;
 +      PangoRectangle r;
 +      char fontName[MSG_SIZ];
 +
 +      cr = cairo_create (canvas);
 +      layout = pango_cairo_create_layout(cr);
 +      pango_layout_set_text(layout, string, -1);
 +      snprintf(fontName, MSG_SIZ, "Sans Normal %dpx", 5*squareSize/8);
 +      desc = pango_font_description_from_string(fontName);
 +      pango_layout_set_font_description(layout, desc);
 +      pango_font_description_free(desc);
 +        pango_layout_get_pixel_extents(layout, NULL, &r);
 +      cairo_translate(cr, x + squareSize/2 - s*r.width/2, y + (8+s)*squareSize/16 - s*r.height/2);
 +      if(s < 0) cairo_rotate(cr, G_PI);
 +      cairo_set_source_rgb(cr, (id == '+' ? 1.0 : 0.0), 0.0, 0.0);
 +      pango_cairo_update_layout(cr, layout);
 +      pango_cairo_show_layout(cr, layout);
 +      g_object_unref(layout);
 +      cairo_destroy(cr);
 +}
 +
 +static void
  DrawText (char *string, int x, int y, int align)
  {
        int xx = x, yy = y;
            yy += -te.y_bearing + 3;
        } else if (align == 4) {
            xx += te.x_bearing + 1, yy += -te.y_bearing + 3;
        }
  
        cairo_move_to (cr, xx-1, yy);
 -      if(align == -2) cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); else
        if(align < 3) cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
        else          cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
        cairo_show_text (cr, string);
  }
  
  void
 -InscribeKanji (ChessSquare piece, int x, int y)
 +InscribeKanji (cairo_surface_t *canvas, ChessSquare piece, int x, int y)
  {
      char *p, *q, buf[10];
 -    int n;
 +    int n, flip = appData.upsideDown && flipView == (piece < BlackPawn);
      if(piece == EmptySquare) return;
      if(piece >= BlackPawn) piece = BLACK_TO_WHITE piece;
      p = appData.inscriptions;
      strncpy(buf, p, 10);
      for(q=buf; (*++q & 0xC0) == 0x80;);
      *q = NULLCHAR;
 -    DrawText(buf, x, y, n > WhiteLion ? -2 : -1);
 +    DrawUnicode(canvas, buf, x, y, PieceToChar(n), flip);
  }
  
  void
@@@ -859,7 -810,7 +860,7 @@@ DrawOneSquare (int x, int y, ChessSquar
        BlankSquare(csBoardWindow, x, y, square_color, piece, 1);
      } else {
        pngDrawPiece(csBoardWindow, piece, square_color, x, y);
 -        if(appData.inscriptions[0]) InscribeKanji(piece, x, y);
 +        if(appData.inscriptions[0]) InscribeKanji(csBoardWindow, piece, x, y);
      }
  
      if(align) { // square carries inscription (coord or piece count)
@@@ -908,7 -859,6 +909,7 @@@ CairoOverlayPiece (ChessSquare piece, c
    if(doubleClick) cairo_paint_with_alpha (pieceSource, 0.6);
    else cairo_paint(pieceSource);
    cairo_destroy (pieceSource);
 +  if(appData.inscriptions[0]) InscribeKanji(dest, piece, 0, 0);
  }
  
  void
diff --combined engineoutput.c
@@@ -5,7 -5,8 +5,8 @@@
   *
   * Copyright 2005 Alessandro Scotti
   *
-  * Enhancements Copyright 1995, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * Enhancements Copyright 1995, 2009, 2010, 2011, 2012, 2013, 2014,
+  * 2015, 2016 Free Software Foundation, Inc.
   *
   * ------------------------------------------------------------------------
   *
@@@ -231,14 -232,11 +232,14 @@@ SetProgramStats (FrontEndProgramStats 
          header[which][0] = NULLCHAR;
          if(gameMode == AnalyzeMode) {
            ChessProgramState *cps = (which ? &second : &first);
 -          if((multi = MultiPV(cps)) >= 0) {
 -            snprintf(header[which], MSG_SIZ, "\t%s viewpoint\t\tfewer / Multi-PV setting = %d / more\n",
 -                                       appData.whitePOV || appData.scoreWhite ? "white" : "mover", cps->option[multi].value);
 +          char *exclu = cps->excludeMoves ? exclusionHeader : "";
 +          if((multi = MultiPV(cps, 3)) != -1) {
 +            char *s = "setting";
 +            if(multi < -1) multi = -2 - multi, s = "margin";
 +            snprintf(header[which], MSG_SIZ, "\t%s viewpoint\t\tfewer / Multi-PV %s = %d / more\n",
 +                                       appData.whitePOV || appData.scoreWhite ? "white" : "mover", s, cps->option[multi].value);
          }
 -          if(!which) snprintf(header[which]+strlen(header[which]), MSG_SIZ-strlen(header[which]), "%s%s", exclusionHeader, columnHeader);
 +          if(!which) snprintf(header[which]+strlen(header[which]), MSG_SIZ-strlen(header[which]), "%s%s", exclu, columnHeader);
            InsertIntoMemo( which, header[which], 0);
          } else {
            snprintf(header[which], MSG_SIZ, "%s", columnHeader);
@@@ -404,14 -402,6 +405,14 @@@ SetEngineColorIcon (int which
  // [HGM] multivar: sort Thinking Output within one depth on score
  
  static int
 +MateFlip (int n)
 +{   // map mate-score to monotonous scale, so sorting compares them correctly
 +    if(n >=  MATE_SCORE) return 2*MATE_SCORE - n;
 +    if(n <= -MATE_SCORE) return -2*MATE_SCORE - n;
 +    return n;
 +}
 +
 +static int
  InsertionPoint (int len, EngineOutputData *ed)
  {
        int i, offs = 0, newScore = ed->score, n = ed->which;
                keys[i+n+2] = ed->moveKey;
                fail[i+n+2] = failType;
                if(ed->moveKey != keys[i+n] && // same move always tops previous one (as a higher score must be a fail low)
 -                 newScore < scores[i+n] && fail[i+n] == ' ') break;
 +                 MateFlip(newScore) < MateFlip(scores[i+n]) && fail[i+n] == ' ') break;
                // if it had higher score as previous, move previous in stead
                scores[i+n+2] = ed->moveKey == keys[i+n] ? newScore : scores[i+n]; // correct scores of fail-low/high searches
                textEnd[i+n+2] = textEnd[i+n] + len;
        return offs + strlen(header[ed->which]);
  }
  
 -#define MATE_SCORE 100000
  static char spaces[] = "            "; // [HGM] align: spaces for padding
  
  static void
diff --combined frontend.h
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -136,7 -137,6 +137,7 @@@ void DragPieceEnd P((int x, int y))
  void DragPieceMove P((int x, int y));
  void LeftClick P((ClickType c, int x, int y));
  int  RightClick P((ClickType c, int x, int y, int *col, int *row));
 +void Wheel P((int dir, int x, int y));
  
  int StartChildProcess P((char *cmdLine, char *dir, ProcRef *pr));
  void DestroyChildProcess P((ProcRef pr, int/*boolean*/ signal));
diff --combined gtk/xboard.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * The following terms apply to Digital Equipment Corporation's copyright
   * interest in XBoard:
@@@ -62,6 -63,7 +63,6 @@@
  #include <pwd.h>
  #include <math.h>
  #include <cairo/cairo.h>
 -#include <cairo/cairo-xlib.h>
  #include <gtk/gtk.h>
  
  #if !OMIT_SOCKETS
@@@ -211,13 -213,11 +212,13 @@@ RETSIGTYPE CmailSigHandler P((int sig))
  RETSIGTYPE IntSigHandler P((int sig));
  RETSIGTYPE TermSizeSigHandler P((int sig));
  char *InsertPxlSize P((char *pattern, int targetPxlSize));
 +#ifdef TODO_GTK
  #if ENABLE_NLS
  XFontSet CreateFontSet P((char *base_fnt_lst));
  #else
  char *FindFont P((char *pattern, int targetPxlSize));
  #endif
 +#endif
  void DelayedDrag P((void));
  void ICSInputBoxPopUp P((void));
  void MoveTypeInProc P((GdkEventKey *eventkey));
@@@ -1022,6 -1022,8 +1023,6 @@@ main (int argc, char **argv
                    programName, appData.boardSize);
            exit(2);
        }
 -      if(BOARD_WIDTH > 8)
 -          squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // scale height
        if (i < 7) {
            /* Find some defaults; use the nearest known size */
            SizeDefaults *szd, *nearest;
        tinyLayout = szd->tinyLayout;
        // [HGM] font: use defaults from settings file if available and not overruled
      }
 +    if(BOARD_WIDTH != 8) {
 +      squareSize = (squareSize*8 + BOARD_WIDTH/2)/BOARD_WIDTH; // keep width the same
 +      lineGap = (squareSize < 37 ? 1 : squareSize < 59 ? 2 : squareSize < 116 ? 3 : 4);
 +    }
  
      defaultLineGap = lineGap;
      if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
@@@ -1777,7 -1775,7 +1778,7 @@@ voi
  ModeHighlight ()
  {
      static int oldPausing = FALSE;
 -    static GameMode oldmode = (GameMode) -1;
 +    static GameMode oldMode = (GameMode) -1;
      char *wname;
      if (!boardWidget) return;
  
        }
      }
  
 -    wname = ModeToWidgetName(oldmode);
 +    wname = ModeToWidgetName(oldMode);
      if (wname != NULL) {
        MarkMenuItem(wname, False);
      }
      if (wname != NULL) {
        MarkMenuItem(wname, True);
      }
 -    oldmode = gameMode;
 +    if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
      MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
 +    oldMode = gameMode;
  
      /* Maybe all the enables should be handled here, not just this one */
      EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
diff --combined gtk/xoptions.c
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * xoptions.c -- Move list window, part of X front end for XBoard
   *
-  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
   * ------------------------------------------------------------------------
   *
   * GNU XBoard is free software: you can redistribute it and/or modify
@@@ -48,6 -48,7 +48,6 @@@ extern char *getenv()
  #include <stdint.h>
  
  #include <cairo/cairo.h>
 -#include <cairo/cairo-xlib.h>
  #include <gtk/gtk.h>
  #include <gdk/gdkkeysyms.h>
  #ifdef OSXAPP
@@@ -243,12 -244,6 +243,12 @@@ SetWidgetLabel (Option *opt, char *buf
  }
  
  void
 +SetComboChoice (Option *opt, int n)
 +{
 +    gtk_combo_box_set_active(opt->handle, n);
 +}
 +
 +void
  SetDialogTitle (DialogClass dlg, char *title)
  {
      gtk_window_set_title(GTK_WINDOW(shells[dlg]), title);
@@@ -549,11 -544,11 +549,11 @@@ HighlightText (Option *opt, int from, i
      if(!(opt->min & INIT)) {
        opt->min |= INIT; // each memo its own init flag!
        gtk_text_buffer_create_tag(opt->handle, "highlight", "background", "yellow", NULL);
      }
      gtk_text_buffer_get_iter_at_offset(opt->handle, &start, from);
      gtk_text_buffer_get_iter_at_offset(opt->handle, &end, to);
 -    gtk_text_buffer_apply_tag_by_name(opt->handle, highlight ? "highlight" : "normal", &start, &end);
 +    if(highlight) gtk_text_buffer_apply_tag_by_name(opt->handle, "highlight", &start, &end);
 +    else gtk_text_buffer_remove_tag_by_name(opt->handle, "highlight", &start, &end);
  }
  
  static char **names;
@@@ -1470,12 -1465,10 +1470,12 @@@ if(appData.debugMode) printf("n=%d, h=%
              /* set button color on new variant dialog */
              if(option[i].textValue) {
                  static char *b = "Bold";
 +                char *v, *p = NULL, n = option[i].value;
 +                if(n >= 0) v = VariantName(n), p = strstr(first.variants, v);
                  gdk_color_parse( option[i].textValue, &color );
                  gtk_widget_modify_bg ( GTK_WIDGET(button), GTK_STATE_NORMAL, &color );
                  gtk_widget_set_sensitive(button, option[i].value >= 0 && (appData.noChessProgram
 -                                       || strstr(first.variants, VariantName(option[i].value))));
 +                                       || p && (!*v || strlen(p) == strlen(v) || p[strlen(v)] == ',')));
                  if(engineVariant[100] ? !strcmp(engineVariant+100, option[i].name) : 
                     gameInfo.variant ? option[i].value == gameInfo.variant : !strcmp(option[i].name, "Normal"))
                      SetWidgetFont(gtk_bin_get_child(GTK_BIN(button)), &b);
diff --combined gtk/xtimer.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * The following terms apply to Digital Equipment Corporation's copyright
   * interest in XBoard:
@@@ -183,14 -184,13 +184,14 @@@ StartLoadGameTimer (long millisec
  
  guint analysisClockTag = 0;
  
 -void
 +int
  AnalysisClockCallback(gpointer data)
  {
      if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
           || appData.icsEngineAnalyze) { // [DM]
        AnalysisPeriodicEvent(0);
      }
 +    return 1;
  }
  
  void
diff --combined menus.c
+++ b/menus.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * The following terms apply to Digital Equipment Corporation's copyright
   * interest in XBoard:
@@@ -257,8 -258,6 +258,8 @@@ QuitProc (
  void
  MatchProc ()
  {
 +    static Enables matchOff[] = { { "Mode.MachineMatch", False }, { NULL, False } };
 +    if(matchMode) SetMenuEnables(matchOff);
      MatchEvent(2);
  }
  
@@@ -358,7 -357,7 +359,7 @@@ AboutProc (
      snprintf(buf, sizeof(buf),
  _("%s%s\n\n"
  "Copyright 1991 Digital Equipment Corporation\n"
- "Enhancements Copyright 1992-2015 Free Software Foundation\n"
+ "Enhancements Copyright 1992-2016 Free Software Foundation\n"
  "Enhancements Copyright 2005 Alessandro Scotti\n\n"
  "%s is free software and carries NO WARRANTY;"
  "see the file COPYING for more information.\n"
@@@ -700,7 -699,6 +701,7 @@@ MenuItem engineMenu[100] = 
    {"----",                         NULL,      NULL,              NothingProc},
    {N_("Engine #1 Settings..."),    NULL,     "Engine#1Settings", FirstSettingsProc},
    {N_("Engine #2 Settings..."),    NULL,     "Engine#2Settings", SecondSettingsProc},
 +  {N_("Common Settings..."), "<Alt><Shift>u","CommonEngine",     UciMenuProc},
    {"----",                         NULL,      NULL,              NothingProc},
    {N_("Hint"),                     NULL,     "Hint",             HintEvent},
    {N_("Book"),                     NULL,     "Book",             BookEvent},
@@@ -715,6 -713,7 +716,6 @@@ MenuItem optionsMenu[] = 
    {N_("General..."),              NULL,             "General",             OptionsProc},
  #endif
    {N_("Time Control..."),        "<Alt><Shift>t",   "TimeControl",         TimeControlProc},
 -  {N_("Common Engine..."),       "<Alt><Shift>u",   "CommonEngine",        UciMenuProc},
    {N_("Adjudications..."),       "<Alt><Shift>j",   "Adjudications",       EngineMenuProc},
    {N_("ICS..."),                  NULL,             "ICS",                 IcsOptionsProc},
    {N_("Tournament..."),           NULL,             "Match",               MatchOptionsProc},
diff --combined moves.c
+++ b/moves.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -71,6 -72,7 +72,6 @@@ int BlackPiece P((ChessSquare))
  int SameColor P((ChessSquare, ChessSquare));
  int PosFlags(int index);
  
 -extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */
  int quickFlag;
  char *pieceDesc[EmptySquare];
  char *defaultDesc[EmptySquare] = {
@@@ -120,13 -122,12 +121,13 @@@ unsigned char pieceToChar[EmptySquare+1
                          'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k',
                          'x' };
  unsigned char pieceNickName[EmptySquare];
 +int promoPartner[EmptySquare];
  
  char
  PieceToChar (ChessSquare p)
  {
      int c;
 -    if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */
 +    if((int)p < 0 || (int)p >= (int)EmptySquare) return('?'); /* [HGM] for safety */
      c = pieceToChar[(int) p];
      if(c & 128) c = c & 63 | 64;
      return c;
@@@ -204,7 -205,7 +205,7 @@@ CollectPieceDescriptors (
      // dump all engine defined pieces, and pieces with non-standard names,
      // but suppress black pieces that are the same as their white counterpart
      ChessSquare p;
 -    static char buf[MSG_SIZ];
 +    static char buf[MSG_SIZ], s[2];
      char *m, c, d, *pieceName = defaultName;
      int len;
      *buf = NULLCHAR;
      if(gameInfo.variant == VariantXiangqi) pieceName = xqName;
      for(p=WhitePawn; p<EmptySquare; p++) {
        if((c = pieceToChar[p]) == '.' || c == '~') continue;  // does not participate
 -      m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED p] : c);
 -      if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == toupper(c)
 -             && (c != '+' || pieceToChar[DEMOTED BLACK_TO_WHITE p] == d)) { // black member of normal pair
 +      m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED(p)] : c);
 +      if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == (c & ~32)
 +             && (c != '+' || pieceToChar[DEMOTED(BLACK_TO_WHITE p)] == d)) {// black member of normal pair
            char *wm = pieceDesc[BLACK_TO_WHITE p];
            if(!m && !wm || m && wm && !strcmp(wm, m)) continue;            // moves as a white piece
        } else                                                              // white or unpaired black
 -      if((p < BlackPawn || CharToPiece(toupper(d)) != EmptySquare) &&     // white or lone black
 +      if((p < BlackPawn || CharToPiece(d & ~32) != EmptySquare) &&        // white or lone black
           !pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name
  // TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults
        if(!m) m = defaultDesc[p];
 +      if(!m) continue;
        len = strlen(buf);
 -      snprintf(buf+len, MSG_SIZ-len, "%s%s%c:%s", len ? ";" : "", c == '+' ? "+" : "", d, m);
 +      *s = (d > 128 ? SUFFIXES[d-128>>6] : 0); d = 64 + (d & 63);
 +      snprintf(buf+len, MSG_SIZ-len, "%s%s%c%s:%s", len ? ";" : "", c == '+' ? "+" : "", d, s, m);
      }
      return buf;
  }
  
 +int
 +LoadPieceDesc (char *s)
 +{
 +    ChessSquare piece;
 +    static char suf[] = SUFFIXES;
 +    char *r, *p, *q = s;
 +    int ok = TRUE, promoted, c;
 +    while(q && *s) {
 +      p = s;
 +      q = strchr(s, ';');
 +      if(q) *q = 0, s = q+1;
 +      if(*p == '+') promoted = 1, p++; else promoted = 0;
 +      c = *p++;
 +      if(!c) { ok = FALSE; continue; } // bad syntax
 +      if(*p && (r = strchr(suf, *p))) c += 64*(r - suf + 1), p++;
 +      if(*p++ != ':') { ok = FALSE; continue; } // bad syntax
 +      if(!strcmp(p, "(null)")) continue; // handle bug in writing of XBoard 4.8.0
 +        piece = CharToPiece(c);
 +      if(piece >= EmptySquare) { ok = FALSE; continue; } // non-existent piece
 +      if(promoted) {
 +          piece = promoPartner[piece];
 +          if(pieceToChar[piece] != '+') { ok = FALSE; continue; } // promoted form does not exist
 +      }
 +      ASSIGN(pieceDesc[piece], p);
 +      if(piece < BlackPawn && (pieceToChar[WHITE_TO_BLACK piece] == pieceToChar[piece] + 32 || promoted)) {
 +          ASSIGN(pieceDesc[WHITE_TO_BLACK piece], p);
 +      }
 +      pieceDefs = TRUE;
 +    }
 +    return ok;
 +}
 +
  // [HGM] gen: configurable move generation from Betza notation sent by engine.
  // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill-
  // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1
@@@ -319,9 -286,9 +320,9 @@@ MovesFromString (Board board, int flags
      if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else
      if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0;
      while(*p) {                  // more moves to go
 -      int expo = 1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
 +      int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0;
        char *cont = NULL;
 -      if(*p == 'i') initial = 1, desc = ++p;
 +      while(*p == 'i') initial++, desc = ++p;
        while(islower(*p)) p++;  // skip prefixes
        if(!isupper(*p)) return; // syntax error: no atom
        dx = xStep[*p-'A'] - '0';// step vector of atom
        if(isdigit(*++p)) expo = atoi(p++);           // read exponent
        if(expo > 9) p++;                             // allow double-digit
        desc = p;                                     // this is start of next move
 +      if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; } else
        if(initial && (board[r][f] != initialPosition[r][f] ||
                       r == 0              && board[TOUCHED_W] & 1<<f ||
                       r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f   ) ) continue;
 -      if(expo > 1 && dx == 0 && dy == 0) {          // castling indicated by O + number
 +      if(expo > 0 && dx == 0 && dy == 0) {          // castling indicated by O + number
            mode |= 1024; dy = 1;
        }
 +      if(expo < 0) expo = 1;                        // use 1 for default
          if(!cont) {
            if(!(mode & 15)) mode |= his + 4;         // no mode spec, use default = mc
        } else {
                if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done
                if(x <  BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; }
                if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; }
 -              if(board[y][x] == DarkSquare) break;  // black squares are supposed to be off board
                if(j) { j--; continue; }              // skip irrespective of occupation
 +              if(board[y][x] == DarkSquare) break;  // black squares are supposed to be off board
                if(!jump    && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked
                if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop
                if(x == f && y == r && !loop) occup = 4;     else // start square counts as empty (if not around cylinder!)
                    if(occup == 4) continue; // skip empty squares
                    if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare)
                                                                    && board[y][x] == initialPosition[y][x]) { // reached initial corner piece
 -                    if(pc != WhiteKing && pc != BlackKing) { // non-royal castling (to be entered as two-leg move via 'Rook')
 +                    if(pc != WhiteKing && pc != BlackKing || expo == 1) { // non-royal castling (to be entered as two-leg move via 'Rook')
                        if(killX < 0) cb(board, flags, FirstLeg,   r, f, y, x, cl); if(killX < f)
                        legNr <<= 1,  cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1;
                      } else
                    }
                    if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare)
                                                                    && board[y][x] == initialPosition[y][x]) {
 -                    if(pc != WhiteKing && pc != BlackKing) {
 +                    if(pc != WhiteKing && pc != BlackKing || expo == 1) {
                        if(killX < 0) cb(board, flags, FirstLeg,   r, f, y, x, cl); if(killX > f)
                        legNr <<= 1,  cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1;
                      } else
@@@ -744,7 -709,7 +745,7 @@@ GenPseudoLegal (Board board, int flags
          if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color
            m = 0; piece = board[rf][ff];
            if(PieceToChar(piece) == '~')
 -                 piece = (ChessSquare) ( DEMOTED piece );
 +                 piece = (ChessSquare) ( DEMOTED(piece) );
            if(filter != EmptySquare && piece != filter) continue;
            if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves
                MovesFromString(board, flags, ff, rf, -1, -1, 0, pieceDesc[piece], callback, closure);
  
              /* Gold General (and all its promoted versions) . First do the */
              /* diagonal forward steps, then proceed as normal Wazir        */
 -            case SHOGI (PROMOTED WhitePawn):
 +            case SHOGI (PROMO WhitePawn):
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
 -            case SHOGI (PROMOTED BlackPawn):
 +            case SHOGI (PROMO BlackPawn):
                if(gameInfo.variant == VariantShogi) goto BlackGold;
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI (PROMOTED WhiteKnight):
 +            case SHOGI (PROMO WhiteKnight):
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
              case SHOGI BlackDrunk:
              case SHOGI BlackAlfil:
                StepBackward(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI (PROMOTED BlackKnight):
 +            case SHOGI (PROMO BlackKnight):
                if(gameInfo.variant == VariantShogi) goto BlackGold;
              case SHOGI WhiteDrunk:
              case SHOGI WhiteAlfil:
                break;
  
  
 -            case SHOGI WhiteStag:
 -            case SHOGI BlackStag:
 +            case SHOGI WhiteGnu:
 +            case SHOGI BlackGnu:
                if(gameInfo.variant == VariantShogi) goto BlackGold;
                SlideVertical(board, flags, rf, ff, callback, closure);
                Ferz(board, flags, rf, ff, callback, closure);
                StepSideways(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI (PROMOTED WhiteQueen):
 +            case SHOGI (PROMO WhiteQueen):
              case SHOGI WhiteTokin:
              case SHOGI WhiteWazir:
            WhiteGold:
                Wazir(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI (PROMOTED BlackQueen):
 +            case SHOGI (PROMO BlackQueen):
              case SHOGI BlackTokin:
              case SHOGI BlackWazir:
              BlackGold:
                StepVertical(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI (PROMOTED WhiteFerz):
 +            case SHOGI (PROMO WhiteFerz):
                if(gameInfo.variant == VariantShogi) goto WhiteGold;
 -            case SHOGI (PROMOTED BlackFerz):
 +            case SHOGI (PROMO BlackFerz):
                if(gameInfo.variant == VariantShogi) goto BlackGold;
              case SHOGI WhitePSword:
              case SHOGI BlackPSword:
                SlideVertical(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI WhiteHorned:
 +            case SHOGI WhiteCat:
                Sting(board, flags, rf, ff, 1, 0, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                if(killX >= 0) break;
                SlideBackward(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI BlackHorned:
 +            case SHOGI BlackCat:
                Sting(board, flags, rf, ff, -1, 0, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                if(killX >= 0) break;
                SlideForward(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI WhiteEagle:
 +            case SHOGI WhiteDagger:
                Sting(board, flags, rf, ff, 1,  1, callback, closure);
                Sting(board, flags, rf, ff, 1, -1, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
                SlideDiagBackward(board, flags, rf, ff, callback, closure);
                break;
  
 -            case SHOGI BlackEagle:
 +            case SHOGI BlackDagger:
                Sting(board, flags, rf, ff, -1,  1, callback, closure);
                Sting(board, flags, rf, ff, -1, -1, callback, closure);
                callback(board, flags, NormalMove, rf, ff, rf, ff, closure);
@@@ -1709,11 -1674,10 +1710,11 @@@ CheckTest (Board board, int flags, int 
        }
        ep = board[EP_STATUS];
        if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
 -          ChessSquare victim = killX < 0 ? EmptySquare : trampled;
 +          ChessSquare victim = saveKill < 0 ? EmptySquare : trampled;
            if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) &&           // capturer is Lion
                (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) &&           // captures from a distance
 -              (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
 +              (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn    // no or worthless 'bridge'
 +                                   || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between)
                     board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
        }
      }
@@@ -1826,7 -1790,7 +1827,7 @@@ LegalityTest (Board board, int flags, i
      if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move.
      if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft);
      piece = filterPiece = board[rf][ff];
 -    if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece;
 +    if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece);
  
      /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */
      /* (perhaps we should disallow moves that obviously leave us in check?)              */
          if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind;
          if(promoChar != '+')
              return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove;
 -        if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') {
 +        if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') {
            if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.')
            return ImpossibleMove;
        }
@@@ -1913,7 -1877,7 +1914,7 @@@ if(appData.debugMode)fprintf(debugFP,"S
              // should test if in zone, really
              if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags))
                  return IllegalMove;
 -            if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
 +            if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion;
          } else
        if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi
        if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) {
@@@ -2035,7 -1999,7 +2036,7 @@@ DisambiguateCallback (Board board, int 
  
      if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff]
           || PieceToChar(board[rf][ff]) == '~'
 -              && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff])
 +              && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff]))
                                                                        ) &&
        (cl->rfIn == -1 || cl->rfIn == rf) &&
        (cl->ffIn == -1 || cl->ffIn == ff) &&
@@@ -2191,7 -2155,7 +2192,7 @@@ Disambiguate (Board board, int flags, D
          else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
      } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it
          ChessSquare p = closure->piece;
 -        if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+')
 +        if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+')
              closure->kind = ImpossibleMove; // used on non-promotable piece
          else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove;
      } else if (c != NULLCHAR) closure->kind = IllegalMove;
@@@ -2236,7 -2200,7 +2237,7 @@@ CoordsToAlgebraicCallback (Board board
      if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare
          (board[rf][ff] == cl->piece
           || PieceToChar(board[rf][ff]) == '~' &&
 -            (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece)
 +            (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece)
                                       ) {
        if (rf == cl->rf) {
            if (ff == cl->ff) {
@@@ -2280,7 -2244,7 +2281,7 @@@ CoordsToAlgebraic (Board board, int fla
  
      if (promoChar == 'x') promoChar = NULLCHAR;
      piece = board[rf][ff];
 -    if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece);
 +    if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece));
  
      switch (piece) {
        case WhitePawn:
        cl.kind = IllegalMove;
        cl.rank = cl.file = cl.either = 0;
          c = PieceToChar(piece) ;
 -        GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed
 +        GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed
  
        if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) {
            /* Generate pretty moves for moving into check, but
               still return IllegalMove.
            */
 -            GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece));
 +            GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece)));
            if (cl.kind == IllegalMove) break;
            cl.kind = IllegalMove;
        }
        */
          if( c == '~' || c == '+') {
             /* [HGM] print nonexistent piece as its demoted version */
 -           piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu));
 +           piece = (ChessSquare) (CHUDEMOTED(piece));
          }
          if(c=='+') *outp++ = c;
          *outp++ = ToUpper(PieceToChar(piece));
        int r, f;
        for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<=BOARD_RGHT; f++)
                c += (board[r][f] == piece); // count on-board pieces of given type
 -      *outp++ = ToUpper(PieceToChar(piece));
 +        *outp = PieceToChar(piece);
 +        if(*outp == '+') outp++, piece = CHUDEMOTED(piece);
 +        *outp++ = ToUpper(PieceToChar(piece));
 +        if(*outp = PieceSuffix(piece)) outp++;
      }
    if(c != 1) { // [HGM] but if there is only one piece of the mentioned type, no from-square, thank you!
      *outp++ = ff + AAA;
diff --combined moves.h
+++ b/moves.h
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -55,6 -56,7 +56,6 @@@
  
  extern ChessSquare PromoPiece P((ChessMove moveType));
  extern ChessMove PromoCharToMoveType P((int whiteOnMove, int promoChar));
 -extern char PieceToChar P((ChessSquare p));
  extern char PieceSuffix P((ChessSquare p));
  extern ChessSquare CharToPiece P((int c));
  extern int PieceToNumber P((ChessSquare p));
@@@ -63,7 -65,6 +64,7 @@@ extern void CopyBoard P((Board to, Boar
  extern int CompareBoards P((Board board1, Board board2));
  extern unsigned char pieceToChar[(int)EmptySquare+1];
  extern unsigned char pieceNickName[(int)EmptySquare];
 +extern int promoPartner[(int)EmptySquare];
  extern char *pieceDesc[(int)EmptySquare];
  extern Board initialPosition;
  extern Boolean pieceDefs;
diff --combined parser.c
+++ b/parser.c
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * parser.c --
   *
-  * Copyright 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * Copyright 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
   * ------------------------------------------------------------------------
   *
   * GNU XBoard is free software: you can redistribute it and/or modify
@@@ -22,7 -22,6 +22,7 @@@
  
  #include "config.h"
  #include <stdio.h>
 +#include <stdlib.h>
  #include <ctype.h>
  #include <string.h>
  #include "common.h"
@@@ -381,7 -380,6 +381,7 @@@ cha
  PromoSuffix (char **p)
  {
      char *start = *p;
 +    if(**p == ' ') return NULLCHAR; // common case, test explicitly for speed
      if(**p == 'e' && (Match("ep", p) || Match("e.p.", p))) { *p = start; return NULLCHAR; } // non-compliant e.p. suffix is no promoChar!
      if(**p == '+' && IS_SHOGI(gameInfo.variant)) { (*p)++; return '+'; }
      if(**p == '=' || (gameInfo.variant == VariantSChess) && **p == '/') (*p)++; // optional = (or / for Seirawan gating)
@@@ -496,7 -494,7 +496,7 @@@ NextUnit (char **p
            if(piece) {
                cl.pieceIn = CharToPiece(wom ? piece : piece + 'a' - 'A');
                if(cl.pieceIn == EmptySquare) return ImpossibleMove; // non-existent piece
 -              if(promoted) cl.pieceIn = (ChessSquare) (CHUPROMOTED cl.pieceIn);
 +              if(promoted) cl.pieceIn = (ChessSquare) (CHUPROMOTED(cl.pieceIn));
            } else cl.pieceIn = EmptySquare;
            if(separator == '@' || separator == '*') { // drop move. We only get here without from-square or promoted piece
                fromY = DROP_RANK; fromX = cl.pieceIn;
                    ChessSquare realPiece = boards[yyboardindex][fromY][fromX];
                    // Note that Disambiguate does not work for illegal moves, but flags them as impossible
                    if(piece) { // check if correct piece indicated
 -                      if(PieceToChar(realPiece) == '~') realPiece = (ChessSquare) (DEMOTED realPiece);
 +                      if(PieceToChar(realPiece) == '~') realPiece = (ChessSquare) (DEMOTED(realPiece));
                        if(!(appData.icsActive && PieceToChar(realPiece) == '+') && // trust ICS if it moves promoted pieces
                           piece && realPiece != cl.pieceIn) return ImpossibleMove;
                    } else if(!separator && **p == '+') { // could be a protocol move, where bare '+' suffix means shogi-style promotion
 -                      if(realPiece < (wom ?  WhiteCannon : BlackCannon) && PieceToChar(PROMOTED realPiece) == '+') // seems to be that
 +                      if(realPiece < (wom ?  WhiteCannon : BlackCannon) && PieceToChar(PROMOTED(realPiece)) == '+') // seems to be that
                           currentMoveString[4] = cl.promoCharIn = *(*p)++; // append promochar after all
                    }
                    result = LegalityTest(boards[yyboardindex], PosFlags(yyboardindex), fromY, fromX, toY, toX, cl.promoCharIn);
@@@ -621,12 -619,12 +621,12 @@@ badMove:// we failed to find algebraic 
                if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
  
                if (wom) {
 -                  rf = 0;
 -                  rt = 0;
 +                  rf = castlingRank[0];
 +                  rt = castlingRank[0];
                    king = WhiteKing;
                } else {
 -                  rf = BOARD_HEIGHT-1;
 -                  rt = BOARD_HEIGHT-1;
 +                  rf = castlingRank[3];
 +                  rt = castlingRank[3];
                    king = BlackKing;
                }
                ff = (BOARD_WIDTH-1)>>1; // this would be d-file
                    /* ICS wild castling */
                    ft = castlingType == 1 ? BOARD_LEFT+1 : (gameInfo.variant == VariantJanus ? BOARD_RGHT-2 : BOARD_RGHT-3);
                } else {
 +                  char *q;
                    ff = BOARD_WIDTH>>1; // e-file
                    ft = castlingType == 1 ? BOARD_RGHT-2 : BOARD_LEFT+2;
 +                  if(pieceDesc[king] && (q = strchr(pieceDesc[king], 'O'))) { // redefined to non-default King stride
 +                      ft = (castlingType == 1 ? ff + atoi(q+1) : ff - atoi(q+1));
 +                  }
                }
                if(PosFlags(0) & F_FRC_TYPE_CASTLING) {
                    if (wom) {
diff --combined parser.h
+++ b/parser.h
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -62,6 -63,4 +63,6 @@@ extern int yyskipmoves;  /* If TRUE, al
  extern char *yy_text;  /* Needed because yytext can be either a char[]
                          or a (non-constant) char* */
  extern int yyoffset P((void));
 -extern signed char initialRights[BOARD_FILES];
 +extern unsigned char initialRights[BOARD_FILES];
 +extern signed char  castlingRank[BOARD_FILES];
 +
diff --combined pgntags.c
+++ b/pgntags.c
@@@ -1,7 -1,8 +1,8 @@@
  /*
   * pgntags.c -- Functions to manage PGN tags
   *
-  * Copyright 1995, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * Copyright 1995, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * Enhancements Copyright 2005 Alessandro Scotti
   *
@@@ -118,7 -119,9 +119,7 @@@ ParsePGNTag (char *tag, GameInfo *gameI
          if(*value && strcmp(value, engineVariant)) // keep current engine-defined variant if it matches
              gameInfo->variant = StringToVariant(value);
      } else if (StrCaseCmp(name, "VariantMen") == 0) {
 -        /* for now ignore this tag, as we have no method yet */
 -        /* for assigning the pieces to XBoard pictograms     */
 -        success = TRUE;
 +        success = LoadPieceDesc(value);
      } else if (StrCaseCmp(name, PGN_OUT_OF_BOOK) == 0) {
          /* [AS] Out of book annotation */
          success = StrSavePtr(value, &gameInfo->outOfBook) != NULL;
diff --combined winboard/defaults.h
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.\r
   *\r
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.\r
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
+  * Software Foundation, Inc.\r
   *\r
   * Enhancements Copyright 2005 Alessandro Scotti\r
   *\r
@@@ -56,8 -57,8 +57,8 @@@
  #define GAME_FILT     "Game files (*.pgn,*.gam)\0*.pgn;*.gam\0All files (*.*)\0*.*\0"\r
  #define DIAGRAM_FILT  "bitmap files (*.bmp)\0*.bmp\0All files (*.*)\0*.*\0"\r
  #define SOUND_FILT    "Wave files (*.wav)\0*.wav\0All files (*.*)\0*.*\0"\r
 -#define OUTER_MARGIN (tinyLayout ? 0 : 4)\r
 -#define INNER_MARGIN (tinyLayout ? 0 : 2)\r
 +#define OUTER_MARGIN (tinyLayout == 2 ? 0 : 4)\r
 +#define INNER_MARGIN (tinyLayout == 2 ? 0 : 2)\r
  #define MESSAGE_LINE_LEFTMARGIN 2\r
  #define MESSAGE_TEXT_MAX 256\r
  /*#define COLOR_ECHOOFF RGB(192,192,192)*/\r
diff --combined winboard/jaws.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.\r
   *\r
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.\r
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
+  * Software Foundation, Inc.\r
   *\r
   * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
   * which was written and is copyrighted by Wayne Christopher.\r
@@@ -270,7 -271,7 +271,7 @@@ InitJAWS(
  \r
                AdaptMenu();\r
                menuBarText[0][8] = menuBarText[0][7]; menuBarText[0][7] = "&JAWS";\r
 -              for(i=0; i<9; i++) menuBarText[1][i] = menuBarText[0][i];\r
 +              for(i=0; i<9; i++) menuBarText[2][i] = menuBarText[1][i] = menuBarText[0][i];\r
        }\r
  \r
        hAccelJAWS = CreateAcceleratorTable(acceleratorsJAWS, 14);\r
diff --combined winboard/winboard.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.\r
   *\r
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.\r
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
+  * Software Foundation, Inc.\r
   *\r
   * Enhancements Copyright 2005 Alessandro Scotti\r
   *\r
@@@ -534,12 -535,12 +535,12 @@@ typedef struct 
  \r
  SizeInfo sizeInfo[] = \r
  {\r
 -  { "tiny",     21, 0, 1, 1, 0, 0 },\r
 -  { "teeny",    25, 1, 1, 1, 0, 0 },\r
 -  { "dinky",    29, 1, 1, 1, 0, 0 },\r
 -  { "petite",   33, 1, 1, 1, 0, 0 },\r
 -  { "slim",     37, 2, 1, 0, 0, 0 },\r
 -  { "small",    40, 2, 1, 0, 0, 0 },\r
 +  { "tiny",     21, 0, 1, 2, 0, 0 },\r
 +  { "teeny",    25, 1, 1, 2, 0, 0 },\r
 +  { "dinky",    29, 1, 1, 2, 0, 0 },\r
 +  { "petite",   33, 1, 1, 2, 0, 0 },\r
 +  { "slim",     37, 2, 1, 1, 0, 0 },\r
 +  { "small",    40, 2, 1, 1, 0, 0 },\r
    { "mediocre", 45, 2, 1, 0, 0, 0 },\r
    { "middling", 49, 2, 0, 0, 0, 0 },\r
    { "average",  54, 2, 0, 0, 0, 0 },\r
@@@ -587,7 -588,7 +588,7 @@@ typedef struct 
    WNDPROC wndproc;\r
  } MyButtonDesc;\r
  \r
 -#define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
 +#define BUTTON_WIDTH (tinyLayout == 2 ? 16 : 32)\r
  #define N_BUTTONS 5\r
  \r
  MyButtonDesc buttonDesc[N_BUTTONS] =\r
  \r
  int tinyLayout = 0, smallLayout = 0;\r
  #define MENU_BAR_ITEMS 9\r
 -char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
 +char *menuBarText[3][MENU_BAR_ITEMS+1] = {\r
    { N_("&File"), N_("&Edit"), N_("&View"), N_("&Mode"), N_("&Action"), N_("E&ngine"), N_("&Options"), N_("&Help"), NULL },\r
 +  { N_("&Fil"), N_("&Ed"), N_("&Vw"), N_("&Mod"), N_("&Act"), N_("E&ng"), N_("&Opt"), N_("&Hlp"), NULL },\r
    { N_("&F"), N_("&E"), N_("&V"), N_("&M"), N_("&A"), N_("&N"), N_("&O"), N_("&H"), NULL },\r
  };\r
  \r
@@@ -1106,8 -1106,6 +1107,8 @@@ InitGeometry(
    screenGeometry.bottom = screenGeometry.top + screenHeight;\r
  }\r
  \r
 +ChessProgramState broadcast;\r
 +\r
  BOOL\r
  InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
  {\r
      appData.ringBellAfterMoves = TRUE;\r
    }\r
    if (appData.debugMode) {\r
 -    debugFP = fopen(appData.nameOfDebugFile, "w");\r
 +    char *c = appData.nameOfDebugFile;\r
 +    if(strstr(c, "///") == c) {\r
 +      broadcast.which = "broadcaster";\r
 +      broadcast.pr   = NoProc;\r
 +      broadcast.isr  = NULL;\r
 +      broadcast.program = c + 3;\r
 +      broadcast.dir  = ".";\r
 +      broadcast.host = "localhost";\r
 +      StartChessProgram(&broadcast);\r
 +      debugFP = (FILE*) _fdopen(_open_osfhandle((long)(((ChildProc*)(broadcast.pr))->hTo), _O_WRONLY), "w");\r
 +    } else\r
 +    debugFP = fopen(c, "w");\r
      setbuf(debugFP, NULL);\r
    }\r
  \r
@@@ -1297,8 -1284,6 +1298,8 @@@ LFfromMFP(LOGFONT* lf, MyFontParams *mf
    lf->lfCharSet = mfp->charset;\r
    lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
  \r
 +\r
 +\r
    lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
    lf->lfQuality = DEFAULT_QUALITY;\r
    lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
@@@ -2366,17 -2351,13 +2367,17 @@@ InitDrawingSizes(BoardSize boardSize, i
    minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
    border = appData.useBorder && appData.border[0] ? squareSize/2 : 0;\r
  \r
 +  // [HGM] decide on tininess based on total board width rather than square size\r
 +  tinyLayout = squareSize * (BOARD_WIDTH);\r
 +  tinyLayout = tinyLayout < 35*8 ? 2 : tinyLayout < 43*8 ? 1 : 0;\r
 +\r
    if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
        lineGap = appData.overrideLineGap;\r
    }\r
  \r
    if (tinyLayout != oldTinyLayout) {\r
      long style = GetWindowLongPtr(hwndMain, GWL_STYLE);\r
 -    if (tinyLayout) {\r
 +    if (tinyLayout == 2) {\r
        style &= ~WS_SYSMENU;\r
        InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
                 "&Minimize\tCtrl+F4");\r
    ReleaseDC(hwndMain, hdc);\r
  \r
    /* Compute where everything goes */\r
 -  if((first.programLogo || second.programLogo) && !tinyLayout) {\r
 +  if((first.programLogo || second.programLogo) && tinyLayout != 2) {\r
          /* [HGM] logo: if either logo is on, reserve space for it */\r
        logoHeight =  2*clockSize.cy;\r
        leftLogoRect.left   = OUTER_MARGIN;\r
                     messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
                     (HMENU) buttonDesc[i].id,\r
                     (HINSTANCE) GetWindowLongPtr(hwndMain, GWLP_HINSTANCE), NULL);\r
 -      if (tinyLayout) {\r
 +      if (tinyLayout == 2) {\r
        SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
                    (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
                    MAKELPARAM(FALSE, 0));\r
         piece = (ChessSquare) ((int) piece + 1)) {\r
        if (pieceBitmap[i][piece] != NULL)\r
        DeleteObject(pieceBitmap[i][piece]);\r
 +      pieceBitmap[i][piece] = NULL;\r
      }\r
    }\r
  \r
    fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
 +\r
    // Orthodox Chess pieces\r
    pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
    pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
      pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
      pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
      pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
 +    pieceBitmap[0][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
 +    pieceBitmap[1][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
 +    pieceBitmap[2][WhiteAmazon] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
      pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
      pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
      pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
      pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
      pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
      pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
 +    pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
 +    pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
 +    pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
 +    pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
 +    pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
 +    pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
 +    pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
 +    pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
 +    pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
 +    pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
 +    pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
 +    pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
  \r
      if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
        pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
    pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
    minorSize = 0;\r
    }\r
 +\r
 +  if(appData.pieceDirectory[0]) for(i=WhitePawn; i<BlackPawn; i++) { // try for all missing pieces with new naming convention\r
 +    char buf[MSG_SIZ];\r
 +    if(pieceBitmap[0][i]) continue;\r
 +    snprintf(buf, MSG_SIZ, "piece%d_", i);\r
 +    pieceBitmap[0][i] = DoLoadBitmap(hInst, buf, squareSize, "s");\r
 +    pieceBitmap[1][i] = DoLoadBitmap(hInst, buf, squareSize, "o");\r
 +    pieceBitmap[2][i] = DoLoadBitmap(hInst, buf, squareSize, "w");\r
 +  }\r
  }\r
  \r
  HBITMAP\r
@@@ -4036,7 -3991,6 +4037,7 @@@ HDCDrawPosition(HDC hdc, BOOLEAN repain
    }\r
  \r
    if( appData.highlightMoveWithArrow ) {\r
 +\r
      DrawArrowHighlight(hdcmem);\r
    }\r
  \r
@@@ -4557,7 -4511,6 +4558,7 @@@ static int promoStyle
  LRESULT CALLBACK\r
  Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
  {\r
 +\r
    char promoChar;\r
  \r
    switch (message) {\r
@@@ -4659,7 -4612,7 +4660,7 @@@ PromotionPopup(HWND hwnd
  void\r
  PromotionPopUp(char choice)\r
  {\r
 -  promoStyle = (choice == '+');\r
 +  promoStyle = (choice == '+' || IS_SHOGI(gameInfo.variant));\r
    DrawPosition(TRUE, NULL);\r
    PromotionPopup(hwndMain);\r
  }\r
@@@ -5130,14 -5083,12 +5131,14 @@@ WndProc(HWND hwnd, UINT message, WPARA
        break;\r
  \r
      case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
 +      if(matchMode) EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_GRAYED);\r
        MatchEvent(2); // distinguish from command-line-triggered case (matchMode=1)\r
        break;\r
  \r
      case IDM_TwoMachines:\r
        TwoMachinesEvent();\r
        /*\r
 +\r
         * refresh the tags dialog only if it's visible\r
         */\r
        if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
              ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
            wpMain.x = lpwp->x;\r
              wpMain.y = lpwp->y;\r
 +\r
          }\r
      }\r
      break;\r
@@@ -6755,7 -6705,7 +6756,7 @@@ TypeInNameDialog(HWND hDlg, UINT messag
      case IDOK:\r
        GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
        appData.userName = strdup(move);\r
 -      SetUserLogo();\r
 +      SetUserLogo(); DisplayLogos();\r
        SetGameInfo();\r
        if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
        snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
@@@ -7695,7 -7645,7 +7696,7 @@@ DisplayAClock(HDC hdc, int timeRemainin
  \r
    if (twoBoards && partnerUp) return;\r
    if (appData.clockMode) {\r
 -    if (tinyLayout)\r
 +    if (tinyLayout == 2)\r
        snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
      else\r
        snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
      oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
      oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
    }\r
 +\r
    oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
  \r
    JAWS_SILENCE\r
@@@ -8028,7 -7977,6 +8029,7 @@@ Enables gnuEnables[] = 
    { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
    { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
  \r
 +\r
    // Needed to switch from ncp to GNU mode on Engine Load\r
    { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
    { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
@@@ -8258,8 -8206,6 +8259,8 @@@ ModeHighlight(
      nowChecked = 0;\r
      break;\r
    }\r
 +  if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
 +    EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
    CheckMark(prevChecked, MF_UNCHECKED);\r
    CheckMark(nowChecked, MF_CHECKED);\r
    CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
diff --combined winboard/woptions.c
@@@ -1,7 -1,8 +1,8 @@@
  /*\r
   * woptions.c -- Options dialog box routines for WinBoard\r
   *\r
-  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.\r
+  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
+  * Software Foundation, Inc.\r
   *\r
   * Enhancements Copyright 2005 Alessandro Scotti\r
   *\r
@@@ -883,7 -884,7 +884,7 @@@ VariantWhichRadio(HWND hDlg
        if(IsDlgButtonChecked(hDlg, j) &&\r
           (appData.noChessProgram || strstr(first.variants, VariantName(i-1)))) return (VariantClass) i-1;\r
    }\r
 -  for(i=0; i<9; i++) { // check for engine-defined variants\r
 +  for(i=0; i<15; i++) { // check for engine-defined variants\r
      if(IsDlgButtonChecked(hDlg, OPT_EngineVariant+i) ) {\r
        GetDlgItemText(hDlg, OPT_EngineVariant+i, engineVariant, MSG_SIZ); // remember name, so we can resolve it later\r
        return VariantUnknown;\r
@@@ -904,7 -905,7 +905,7 @@@ VariantShowRadio(HWND hDlg
        EnableWindow(GetDlgItem(hDlg, j), appData.noChessProgram || strstr(first.variants, VariantName(i-1)));\r
    }\r
    *engineVariant = c;\r
 -  for(i=0; i<9; i++) { // initialize engine-defined variants\r
 +  for(i=0; i<15; i++) { // initialize engine-defined variants\r
      char *v = EngineDefinedVariant(&first, i); // get name of #i\r
      if(v) { // there is such a variant\r
        EnableWindow(GetDlgItem(hDlg, OPT_EngineVariant+i), TRUE);     // and enable the button\r
diff --combined winboard/wsettings.c
@@@ -1,7 -1,8 +1,8 @@@
  /*\r
   * woptions.h -- Options dialog box routines for WinBoard\r
   *\r
-  * Copyright 2003, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.\r
+  * Copyright 2003, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
+  * Software Foundation, Inc.\r
   *\r
   * ------------------------------------------------------------------------\r
   *\r
@@@ -455,7 -456,6 +456,7 @@@ GetOptionValues(HWND hDlg, ChessProgram
  }\r
  \r
  char *defaultExt[] = { NULL, "pgn", "fen", "exe", "trn", "bin", "log", "ini" };\r
 +HWND settingsDlg;\r
  \r
  LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
  {\r
  \r
  //        CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
        SetOptionValues(hDlg, activeCps, activeList);\r
 -\r
 +      settingsDlg = hDlg;\r
          SetFocus(GetDlgItem(hDlg, IDCANCEL));\r
  \r
          break;\r
          case IDOK:\r
            if(!GetOptionValues(hDlg, activeCps, activeList)) return FALSE;\r
              EndDialog( hDlg, 0 );\r
 -          comboCallback = NULL; activeCps = NULL;\r
 +          comboCallback = NULL; activeCps = NULL; settingsDlg = NULL;\r
              return TRUE;\r
  \r
          case IDCANCEL:\r
              EndDialog( hDlg, 1 );\r
 -          comboCallback = NULL; activeCps = NULL;\r
 +          comboCallback = NULL; activeCps = NULL; settingsDlg = NULL;\r
              return TRUE;\r
  \r
        default:\r
@@@ -692,19 -692,6 +693,19 @@@ EngineOptionsPopup(HWND hwnd, ChessProg
      return;\r
  }\r
  \r
 +void\r
 +RefreshSettingsDialog (ChessProgramState *cps, int val)\r
 +{\r
 +    int isUp = (settingsDlg != NULL);\r
 +    if(val == 1) {\r
 +      if(activeCps == cps && isUp) SetOptionValues(settingsDlg, cps, activeList);\r
 +      return;\r
 +    }\r
 +    if(settingsDlg) EndDialog(settingsDlg, 1);\r
 +    comboCallback = NULL; activeCps = NULL; settingsDlg = NULL;\r
 +    if(val == 3 || isUp) EngineOptionsPopup(hwndMain, cps);\r
 +}\r
 +\r
  int EnterGroup P((HWND hDlg));\r
  \r
  static int engineNr, selected;\r
@@@ -900,7 -887,6 +901,7 @@@ int MatchOK(
  \r
  void PseudoOK(HWND hDlg)\r
  {\r
 +    if(matchMode) return;\r
      okFunc = 0;\r
      GetOptionValues(hDlg, activeCps, activeList);\r
      EndDialog( hDlg, 0 );\r
  \r
      if(autoinc) appData.loadGameIndex = appData.loadPositionIndex = -(twice + 1); else\r
      if(!appData.loadGameFile[0]) appData.loadGameIndex = -2*twice; // kludge to pass value of "twice" for use in GUI book\r
 +    if(!autoinc && !twice) { // prevent auto-inc being remembered in index value if checkboxes not ticked\r
 +      if(appData.loadGameIndex < 0) appData.loadGameIndex = 0;\r
 +      if(appData.loadPositionIndex < 0) appData.loadPositionIndex = 0;\r
 +    }\r
      if(swiss) { appData.defaultMatchGames = 1; appData.tourneyType = -1; }\r
 +    ASSIGN(appData.tourneyFile, tfName);\r
  }\r
  \r
  char *GetParticipants(HWND hDlg)\r
diff --combined xaw/xboard.c
@@@ -5,7 -5,8 +5,8 @@@
   * Massachusetts.
   *
   * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
-  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+  * Software Foundation, Inc.
   *
   * The following terms apply to Digital Equipment Corporation's copyright
   * interest in XBoard:
@@@ -148,7 -149,6 +149,7 @@@ extern char *getenv()
  #include <locale.h>
  #endif
  
 +#include <X11/keysym.h>
  #include <X11/Intrinsic.h>
  #include <X11/StringDefs.h>
  #include <X11/Shell.h>
@@@ -1818,7 -1818,7 +1819,7 @@@ ModeHighlight (
  {
      Arg args[16];
      static int oldPausing = FALSE;
 -    static GameMode oldmode = (GameMode) -1;
 +    static GameMode oldMode = (GameMode) -1;
      char *wname;
  
      if (!boardWidget || !XtIsRealized(boardWidget)) return;
        }
      }
  
 -    wname = ModeToWidgetName(oldmode);
 +    wname = ModeToWidgetName(oldMode);
      if (wname != NULL) {
        MarkMenuItem(wname, False);
      }
      if (wname != NULL) {
        MarkMenuItem(wname, True);
      }
 -    oldmode = gameMode;
 +    if(oldMode == TwoMachinesPlay) EnableNamedMenuItem("Mode.MachineMatch", True);
      MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
 +    oldMode = gameMode;
  
      /* Maybe all the enables should be handled here, not just this one */
      EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
diff --combined xaw/xoptions.c
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * xoptions.c -- Move list window, part of X front end for XBoard
   *
-  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
+  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
   * ------------------------------------------------------------------------
   *
   * GNU XBoard is free software: you can redistribute it and/or modify
@@@ -189,12 -189,6 +189,12 @@@ SetWidgetLabel (Option *opt, char *buf
  }
  
  void
 +SetComboChoice (Option *opt, char *n)
 +{
 +    SetWidgetText(opt, opt->choice[n]);
 +}
 +
 +void
  SetDialogTitle (DialogClass dlg, char *title)
  {
      Arg args[16];
@@@ -1136,10 -1130,8 +1136,10 @@@ GenericPopUp (Option *option, char *tit
                                   option[i].max /* w */, shrink ? textHeight : 0 /* h */, option[i].min & 0xE | chain /* chain */);
            XtSetArg(args[j], XtNlabel, _(option[i].name));  j++;
            if(option[i].textValue && *option[i].textValue == '#') { // special for buttons of New Variant dialog
 +              char *p, *v, n = option[i].value;
 +              if(n) v = VariantName(n), p = strstr(first.variants, v);
                XtSetArg(args[j], XtNsensitive, option[i].value >= 0 && (appData.noChessProgram
 -                                       || strstr(first.variants, VariantName(option[i].value)))); j++;
 +                                       || p && (!*v || strlen(p) == strlen(v) || p[strlen(v)] == ','))); j++;
                XtSetArg(args[j], XtNborderWidth, (gameInfo.variant == option[i].value)+1); j++;
            }
            option[i].handle = (void*)