* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
*
* Enhancements Copyright 2005 Alessandro Scotti
*
# include "zippy.h"
#endif
#include "backendz.h"
+#include "evalgraph.h"
#include "gettext.h"
#ifdef ENABLE_NLS
void DisplayTwoMachinesTitle P(());
static void ExcludeClick P((int index));
void ToggleSecond P((void));
+void PauseEngine P((ChessProgramState *cps));
#ifdef WIN32
extern void ConsoleCreate();
{
int outError, outCount;
static int gotEof = 0;
+ static FILE *ini;
/* Pass data read from player on to ICS */
if (count > 0) {
if (outCount < count) {
DisplayFatalError(_("Error writing to ICS"), outError, 1);
}
+ if(have_sent_ICS_logon == 2) {
+ if(ini = fopen(appData.icsLogon, "w")) { // save first two lines (presumably username & password) on init script file
+ fprintf(ini, "%s", message);
+ have_sent_ICS_logon = 3;
+ } else
+ have_sent_ICS_logon = 1;
+ } else if(have_sent_ICS_logon == 3) {
+ fprintf(ini, "%s", message);
+ fclose(ini);
+ have_sent_ICS_logon = 1;
+ }
} else if (count < 0) {
RemoveInputSource(isr);
DisplayFatalError(_("Error reading from keyboard"), error, 1);
}
void
+PlotSingleSeekAd (int i)
+{
+ PlotSeekAd(i);
+}
+
+void
AddAd (char *handle, char *rating, int base, int inc, char rated, char *type, int nr, Boolean plot)
{
char buf[MSG_SIZ], *ext = "";
seekNrList[nrOfSeekAds] = nr;
zList[nrOfSeekAds] = 0;
seekAdList[nrOfSeekAds++] = StrSave(buf);
- if(plot) PlotSeekAd(nrOfSeekAds-1);
+ if(plot) PlotSingleSeekAd(nrOfSeekAds-1);
}
}
for(i=0; i<4000; i+= 100) if(i>=minRating && i<maxRating) {
int yy =((double)i - minRating)/(maxRating - minRating)*(h-vMargin-squareSize/8-1) + vMargin;
yy = h-1-yy;
- DrawSeekAxis(hMargin+5*(i%500==0), yy, hMargin-5, yy); // rating ticks
+ DrawSeekAxis(hMargin-5, yy, hMargin+5*(i%500==0), yy); // rating ticks
if(i%500 == 0) {
char buf[MSG_SIZ];
snprintf(buf, MSG_SIZ, "%d", i);
continue;
}
- if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
- ICSInitScript();
- have_sent_ICS_logon = 1;
+ if (looking_at(buf, &i, "login:")) {
+ if (!have_sent_ICS_logon) {
+ if(ICSInitScript())
+ have_sent_ICS_logon = 1;
+ else // no init script was found
+ have_sent_ICS_logon = (appData.autoCreateLogon ? 2 : 1); // flag that we should capture username + password
+ } else { // we have sent (or created) the InitScript, but apparently the ICS rejected it
+ have_sent_ICS_logon = (appData.autoCreateLogon ? 2 : 1); // request creation of a new script
+ }
continue;
}
Boolean valid;
int nr = 0;
- if (gameMode == AnalyzeMode && currentMove < forwardMostMove) {
+ lastParseAttempt = pv; if(!*pv) return; // turns out we crash when we parse an empty PV
+ if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && currentMove < forwardMostMove) {
PushInner(currentMove, forwardMostMove); // [HGM] engine might not be thinking on forwardMost position!
pushed = TRUE;
}
static char buf[10*MSG_SIZ];
int i, k=0, savedEnd=endPV, saveFMM = forwardMostMove;
*buf = NULLCHAR;
- if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV);
+ if(forwardMostMove < endPV) PushInner(forwardMostMove, endPV); // shelve PV of PV-walk
ParsePV(pv, FALSE, 2); // this appends PV to game, suppressing any display of it
for(i = forwardMostMove; i<endPV; i++){
if(i&1) snprintf(buf+k, 10*MSG_SIZ-k, "%s ", parseList[i]);
k += strlen(buf+k);
}
snprintf(buf+k, 10*MSG_SIZ-k, "%s", lastParseAttempt); // if we ran into stuff that could not be parsed, print it verbatim
+ if(pushed) { PopInner(0); pushed = FALSE; } // restore game continuation shelved by ParsePV
if(forwardMostMove < savedEnd) { PopInner(0); forwardMostMove = saveFMM; } // PopInner would set fmm to endPV!
endPV = savedEnd;
return buf;
}
}
- if(doubleClick) { // [HGM] exclude: move entered with double-click on from square is for exclusion, not playing
+ if(doubleClick && gameMode == AnalyzeMode) { // [HGM] exclude: move entered with double-click on from square is for exclusion, not playing
if(ExcludeOneMove(fromY, fromX, toY, toX, promoChar, '*')) // toggle
ClearPremoveHighlights(); // was included
else ClearHighlights(), SetPremoveHighlights(ff, rf, ft, rt); // exclusion indicated by premove highlights
return;
}
doubleClick = FALSE;
+ if(gameMode == AnalyzeMode && (pausing || controlKey) && first.excludeMoves) { // use pause state to exclude moves
+ doubleClick = TRUE; gatingPiece = boards[currentMove][y][x];
+ }
fromX = x; fromY = y; toX = toY = -1;
if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) ||
// even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection
ClearHighlights();
}
} else {
+#if 0
+// [HGM] this must be done after the move is made, as with arrow it could lead to a board redraw with piece still on from square
/* Finish drag move */
if (appData.highlightLastMove) {
SetHighlights(fromX, fromY, toX, toY);
} else {
ClearHighlights();
}
+#endif
DragPieceEnd(xPix, yPix); dragging = 0;
/* Don't animate move and drag both */
appData.animate = FALSE;
}
- MarkTargetSquares(1);
// moves into holding are invalid for now (except in EditPosition, adapting to-square)
if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) {
}
ClearHighlights();
fromX = fromY = -1;
+ MarkTargetSquares(1);
DrawPosition(TRUE, boards[currentMove]);
return;
}
if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) {
SetHighlights(fromX, fromY, toX, toY);
+ MarkTargetSquares(1);
if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand) {
// [HGM] super: promotion to captured piece selected from holdings
ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
if(saveAnimate && !appData.animate && currentMove != oldMove && // drag-move was performed
Explode(boards[currentMove-1], fromX, fromY, toX, toY))
DrawPosition(TRUE, boards[currentMove]);
+ MarkTargetSquares(1);
fromX = fromY = -1;
}
appData.animate = saveAnimate;
|| majors + (12*pCnt[BlackFerz-side] | 6*pCnt[BlackAlfil-side]) > 16; // KCKAA, KCKAX, KCKEEX, KCKEXX (XX!=HH), KCKXXX
// TO DO: cases wih an unpromoted f-Pawn acting as platform for an opponent Cannon
+ } else if(v == VariantKnightmate) {
+ if(nMine == 1) return FALSE;
+ if(nMine == 2 && nHis == 1 && pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side] + pCnt[WhiteKnight+side]) return FALSE; // KBK is only draw
} else if(pCnt[WhiteKing] == 1 && pCnt[BlackKing] == 1) { // other variants with orthodox Kings
int nBishops = pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side];
// In any case it determnes if the game is a claimable draw (filling in EP_STATUS).
// Actually ending the game is now based on the additional internal condition canAdjudicate.
// Only when the game is ended, and the opponent is a computer, this opponent gets the move relayed.
- int k, count = 0; static int bare = 1;
+ int k, drop, count = 0; static int bare = 1;
ChessProgramState *engineOpponent = (gameMode == TwoMachinesPlay ? cps->other : (cps ? NULL : &first));
Boolean canAdjudicate = !appData.icsActive;
/* Check for rep-draws */
count = 0;
+ drop = gameInfo.holdingsSize && (gameInfo.variant != VariantSuper && gameInfo.variant != VariantSChess
+ && gameInfo.variant != VariantGreat && gameInfo.variant != VariantGrand);
for(k = forwardMostMove-2;
- k>=backwardMostMove && k>=forwardMostMove-100 &&
+ k>=backwardMostMove && k>=forwardMostMove-100 && (drop ||
(signed char)boards[k][EP_STATUS] < EP_UNKNOWN &&
- (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE;
+ (signed char)boards[k+2][EP_STATUS] <= EP_NONE && (signed char)boards[k+1][EP_STATUS] <= EP_NONE);
k-=2)
{ int rights=0;
if(CompareBoards(boards[k], boards[forwardMostMove])) {
}
static int savedWhitePlayer, savedBlackPlayer, pairingReceived;
+static ChessProgramState *stalledEngine;
+static char stashedInputMove[MSG_SIZ];
void
HandleMachineMove (char *message, ChessProgramState *cps)
if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
(sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
{
+ if(pausing && !cps->pause) { // for pausing engine that does not support 'pause', we stash its move for processing when we resume.
+ if(appData.debugMode) fprintf(debugFP, "pause %s engine after move\n", cps->which);
+ safeStrCpy(stashedInputMove, message, MSG_SIZ);
+ stalledEngine = cps;
+ if(appData.ponderNextMove) { // bring opponent out of ponder
+ if(gameMode == TwoMachinesPlay) {
+ if(cps->other->pause)
+ PauseEngine(cps->other);
+ else
+ SendToProgram("easy\n", cps->other);
+ }
+ }
+ StopClocks();
+ return;
+ }
+
/* This method is only useful on engines that support ping */
if (cps->lastPing != cps->lastPong) {
if (gameMode == BeginningOfGame) {
) {
/* white pawn promotion */
board[toY][toX] = CharToPiece(ToUpper(promoChar));
- if(gameInfo.variant==VariantBughouse ||
- gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+ if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
+ && 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)
) {
/* black pawn promotion */
board[toY][toX] = CharToPiece(ToLower(promoChar));
- if(gameInfo.variant==VariantBughouse ||
- gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
+ if((gameInfo.variant==VariantBughouse || gameInfo.variant==VariantCrazyhouse)
+ && 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)
AnimateMove(boards[forwardMostMove - 1],
fromX, fromY, toX, toY);
}
- if (appData.highlightLastMove) {
- SetHighlights(fromX, fromY, toX, toY);
- }
}
currentMove = forwardMostMove;
}
if (instant) return;
DisplayMove(currentMove - 1);
+ if (!pausing || gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
+ if (appData.highlightLastMove) { // [HGM] moved to after DrawPosition, as with arrow it could redraw old board
+ SetHighlights(fromX, fromY, toX, toY);
+ }
+ }
DrawPosition(FALSE, boards[currentMove]);
DisplayBothClocks();
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
fromX = fromY = -1; // [HGM] abort any move the user is entering.
+ if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit)
+
if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {
/* If we are playing on ICS, the server decides when the
game is over, but the engine can offer to draw, claim
if (*appData.savePositionFile != NULLCHAR) {
SavePositionToFile(appData.savePositionFile);
}
+ AddGameToBook(FALSE); // Only does something during Monte-Carlo book building
}
}
}
GameInfo dummyInfo;
+static int creatingBook;
int
GameContainsPosition (FILE *f, ListGame *lg)
cm = (ChessMove) Myylex();
}
+ if(!creatingBook) {
if (first.pr == NoProc) {
StartChessProgram(&first);
}
}
DisplayBothClocks();
}
+ }
/* [HGM] server: flag to write setup moves in broadcast file as one */
loadFlag = appData.suppressLoadMoves;
if (oldGameMode == AnalyzeFile ||
oldGameMode == AnalyzeMode) {
appData.loadGameIndex = -1; // [HGM] order auto-stepping through games
- keepInfo = 1;
AnalyzeFileEvent();
- keepInfo = 0;
}
+ if(creatingBook) return TRUE;
if (!matchMode && pos > 0) {
ToNrEvent(pos); // [HGM] no autoplay if selected on position
} else
}
void
+PauseEngine (ChessProgramState *cps)
+{
+ SendToProgram("pause\n", cps);
+ cps->pause = 2;
+}
+
+void
+UnPauseEngine (ChessProgramState *cps)
+{
+ SendToProgram("resume\n", cps);
+ cps->pause = 1;
+}
+
+void
PauseEvent ()
{
if (appData.debugMode)
if (pausing) {
pausing = FALSE;
ModeHighlight();
+ if(stalledEngine) { // [HGM] pause: resume game by releasing withheld move
+ StartClocks();
+ if(gameMode == TwoMachinesPlay) { // we might have to make the opponent resume pondering
+ if(stalledEngine->other->pause == 2) UnPauseEngine(stalledEngine->other);
+ else if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine->other);
+ }
+ if(appData.ponderNextMove) SendToProgram("hard\n", stalledEngine);
+ HandleMachineMove(stashedInputMove, stalledEngine);
+ stalledEngine = NULL;
+ return;
+ }
if (gameMode == MachinePlaysWhite ||
- gameMode == MachinePlaysBlack) {
+ gameMode == TwoMachinesPlay ||
+ gameMode == MachinePlaysBlack) { // the thinking engine must have used pause mode, or it would have been stalledEngine
+ if(first.pause) UnPauseEngine(&first);
+ else if(appData.ponderNextMove) SendToProgram("hard\n", &first);
+ if(second.pause) UnPauseEngine(&second);
+ else if(gameMode == TwoMachinesPlay && appData.ponderNextMove) SendToProgram("hard\n", &second);
StartClocks();
} else {
DisplayBothClocks();
Reset(FALSE, TRUE);
SendToICS(ics_prefix);
SendToICS("refresh\n");
- } else if (currentMove < forwardMostMove) {
+ } else if (currentMove < forwardMostMove && gameMode != AnalyzeMode) {
ForwardInner(forwardMostMove);
}
pauseExamInvalid = FALSE;
case TwoMachinesPlay:
if (forwardMostMove == 0)
return; /* don't pause if no one has moved */
- if ((gameMode == MachinePlaysWhite &&
- !WhiteOnMove(forwardMostMove)) ||
- (gameMode == MachinePlaysBlack &&
- WhiteOnMove(forwardMostMove))) {
+ if(gameMode == TwoMachinesPlay) { // [HGM] pause: stop clocks if engine can be paused immediately
+ ChessProgramState *onMove = (WhiteOnMove(forwardMostMove) == (first.twoMachinesColor[0] == 'w') ? &first : &second);
+ if(onMove->pause) { // thinking engine can be paused
+ PauseEngine(onMove); // do it
+ if(onMove->other->pause) // pondering opponent can always be paused immediately
+ PauseEngine(onMove->other);
+ else
+ SendToProgram("easy\n", onMove->other);
+ StopClocks();
+ } else if(appData.ponderNextMove) SendToProgram("easy\n", onMove); // pre-emptively bring out of ponder
+ } else if(gameMode == (WhiteOnMove(forwardMostMove) ? MachinePlaysWhite : MachinePlaysBlack)) { // engine on move
+ if(first.pause) {
+ PauseEngine(&first);
+ StopClocks();
+ } else if(appData.ponderNextMove) SendToProgram("easy\n", &first); // pre-emptively bring out of ponder
+ } else { // human on move, pause pondering by either method
+ if(first.pause)
+ PauseEngine(&first);
+ else if(appData.ponderNextMove)
+ SendToProgram("easy\n", &first);
StopClocks();
}
+ // if no immediate pausing is possible, wait for engine to move, and stop clocks then
+ case AnalyzeMode:
pausing = TRUE;
ModeHighlight();
break;
}
}
+/* Toggle ShowThinking */
void
+ToggleShowThinking()
+{
+ appData.showThinking = !appData.showThinking;
+ ShowThinkingEvent();
+}
+
+int
AnalyzeModeEvent ()
{
- if (gameMode == AnalyzeMode) { ToggleSecond(); return; }
+ char buf[MSG_SIZ];
+
+ if (!first.analysisSupport) {
+ snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
+ DisplayError(buf, 0);
+ return 0;
+ }
+ /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
+ if (appData.icsActive) {
+ if (gameMode != IcsObserving) {
+ snprintf(buf, MSG_SIZ, _("You are not observing a game"));
+ DisplayError(buf, 0);
+ /* secure check */
+ if (appData.icsEngineAnalyze) {
+ if (appData.debugMode)
+ fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
+ ExitAnalyzeMode();
+ ModeHighlight();
+ }
+ return 0;
+ }
+ /* if enable, user wants to disable icsEngineAnalyze */
+ if (appData.icsEngineAnalyze) {
+ ExitAnalyzeMode();
+ ModeHighlight();
+ return 0;
+ }
+ appData.icsEngineAnalyze = TRUE;
+ if (appData.debugMode)
+ fprintf(debugFP, _("ICS engine analyze starting... \n"));
+ }
+
+ if (gameMode == AnalyzeMode) { ToggleSecond(); return 0; }
if (appData.noChessProgram || gameMode == AnalyzeMode)
- return;
+ return 0;
if (gameMode != AnalyzeFile) {
if (!appData.icsEngineAnalyze) {
EditGameEvent();
- if (gameMode != EditGame) return;
+ if (gameMode != EditGame) return 0;
}
+ if (!appData.showThinking) ToggleShowThinking();
ResurrectChessProgram();
SendToProgram("analyze\n", &first);
first.analyzing = TRUE;
StartAnalysisClock();
GetTimeMark(&lastNodeCountTime);
lastNodeCount = 0;
+ return 1;
}
void
if (appData.noChessProgram || gameMode == AnalyzeFile)
return;
+ if (!first.analysisSupport) {
+ char buf[MSG_SIZ];
+ snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
+ DisplayError(buf, 0);
+ return;
+ }
+
if (gameMode != AnalyzeMode) {
+ keepInfo = 1; // mere annotating should not alter PGN tags
EditGameEvent();
+ keepInfo = 0;
if (gameMode != EditGame) return;
+ if (!appData.showThinking) ToggleShowThinking();
ResurrectChessProgram();
SendToProgram("analyze\n", &first);
first.analyzing = TRUE;
gameMode = AnalyzeFile;
pausing = FALSE;
ModeHighlight();
- SetGameInfo();
StartAnalysisClock();
GetTimeMark(&lastNodeCountTime);
lastNodeCount = 0;
if(appData.timeDelay > 0) StartLoadGameTimer((long)(1000.0f * appData.timeDelay));
+ AnalysisPeriodicEvent(1);
}
void
}
void
+CreateBookEvent ()
+{
+ ListGame * lg = (ListGame *) gameList.head;
+ FILE *f;
+ int nItem;
+ static int secondTime = FALSE;
+
+ if( !(f = GameFile()) || ((ListGame *) gameList.tailPred)->number <= 0 ) {
+ DisplayError(_("Game list not loaded or empty"), 0);
+ return;
+ }
+
+ if(!secondTime && (f = fopen(appData.polyglotBook, "r"))) {
+ fclose(f);
+ secondTime++;
+ DisplayNote(_("Book file exists! Try again for overwrite."));
+ return;
+ }
+
+ creatingBook = TRUE;
+ secondTime = FALSE;
+
+ /* Get list size */
+ for (nItem = 1; nItem <= ((ListGame *) gameList.tailPred)->number; nItem++){
+ LoadGame(f, nItem, "", TRUE);
+ AddGameToBook(TRUE);
+ lg = (ListGame *) lg->node.succ;
+ }
+
+ creatingBook = FALSE;
+ FlushBook();
+}
+
+void
BookEvent ()
{
if (appData.noChessProgram) return;
if (BoolFeature(&p, "exclude", &cps->excludeMoves, cps)) continue;
if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;
if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;
- if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */
+ if (BoolFeature(&p, "pause", &cps->pause, cps)) continue; // [HGM] pause
if (IntFeature(&p, "done", &val, cps)) {
FeatureDone(cps, val);
continue;
ToNrEvent(currentMove+1);
}
+void
+LoadTheme ()
+{
+ char *p, *q, buf[MSG_SIZ];
+ if(engineLine && engineLine[0]) { // a theme was selected from the listbox
+ snprintf(buf, MSG_SIZ, "-theme %s", engineLine);
+ ParseArgsFromString(buf);
+ ActivateTheme(TRUE); // also redo colors
+ return;
+ }
+ p = nickName;
+ if(*p && !strchr(p, '"')) // theme name specified and well-formed; add settings to theme list
+ {
+ int len;
+ q = appData.themeNames;
+ snprintf(buf, MSG_SIZ, "\"%s\"", nickName);
+ if(appData.useBitmaps) {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt true -lbtf \"%s\" -dbtf \"%s\" -lbtm %d -dbtm %d",
+ appData.liteBackTextureFile, appData.darkBackTextureFile,
+ appData.liteBackTextureMode,
+ appData.darkBackTextureMode );
+ } else {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt false -lsc %s -dsc %s",
+ Col2Text(2), // lightSquareColor
+ Col2Text(3) ); // darkSquareColor
+ }
+ if(appData.useBorder) {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub true -border \"%s\"",
+ appData.border);
+ } else {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub false");
+ }
+ if(appData.useFont) {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf true -pf \"%s\" -fptc \"%s\" -fpfcw %s -fpbcb %s",
+ appData.renderPiecesWithFont,
+ appData.fontToPieceTable,
+ Col2Text(9), // appData.fontBackColorWhite
+ Col2Text(10) ); // appData.fontForeColorBlack
+ } else {
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -upf false -pid \"%s\"",
+ appData.pieceDirectory);
+ if(!appData.pieceDirectory[0])
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -wpc %s -bpc %s",
+ Col2Text(0), // whitePieceColor
+ Col2Text(1) ); // blackPieceColor
+ }
+ snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -hsc %s -phc %s\n",
+ Col2Text(4), // highlightSquareColor
+ Col2Text(5) ); // premoveHighlightColor
+ appData.themeNames = malloc(len = strlen(q) + strlen(buf) + 1);
+ if(insert != q) insert[-1] = NULLCHAR;
+ snprintf(appData.themeNames, len, "%s\n%s%s", q, buf, insert);
+ if(q) free(q);
+ }
+ ActivateTheme(FALSE);
+}