X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=37929713badf534efd4f20f21392efc44d98aaeb;hb=e9c8ca5c16307c2366d4128507b57f7a0d4e6a2b;hp=79cc86cd2d1ec7a3e799392f01f51d8d7df47552;hpb=2f5dfd12315375ba76634df6970d124042377740;p=xboard.git diff --git a/backend.c b/backend.c index 79cc86c..3792971 100644 --- a/backend.c +++ b/backend.c @@ -981,13 +981,14 @@ FloatToFront(char **list, char *engineLine) ASSIGN(*list, tidy+1); } -char *insert, *wbOptions; // point in ChessProgramNames were we should insert new engine +char *insert, *wbOptions, *currentEngine[2]; // point in ChessProgramNames were we should insert new engine void Load (ChessProgramState *cps, int i) { char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ], buf3[MSG_SIZ], jar; if(engineLine && engineLine[0]) { // an engine was selected from the combo box + ASSIGN(currentEngine[i], engineLine); snprintf(buf, MSG_SIZ, "-fcp %s", engineLine); SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second* ParseArgsFromString(resetOptions); appData.pvSAN[0] = FALSE; @@ -1030,7 +1031,7 @@ Load (ChessProgramState *cps, int i) q = firstChessProgramNames; if(nickName[0]) snprintf(buf, MSG_SIZ, "\"%s\" -fcp ", nickName); else buf[0] = NULLCHAR; quote = strchr(p, '"') ? '\'' : '"'; // use single quotes around engine command if it contains double quotes - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s\n", + snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), "%c%s%c -fd \"%s\"%s%s%s%s%s%s%s%s", quote, p, quote, appData.directory[i], useNick ? " -fn \"" : "", useNick ? nickName : "", @@ -1040,12 +1041,13 @@ Load (ChessProgramState *cps, int i) isUCI ? (isUCI == TRUE ? " -fUCI" : gameInfo.variant == VariantShogi ? " -fUSI" : " -fUCCI") : "", storeVariant ? " -variant " : "", storeVariant ? VariantName(gameInfo.variant) : ""); - if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf)-1, MSG_SIZ-strlen(buf), " %s\n", wbOptions); - firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 1); + if(wbOptions && wbOptions[0]) snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " %s", wbOptions); + firstChessProgramNames = malloc(len = strlen(q) + strlen(buf) + 2); if(insert != q) insert[-1] = NULLCHAR; - snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert); + snprintf(firstChessProgramNames, len, "%s\n%s\n%s", q, buf, insert); if(q) free(q); FloatToFront(&appData.recentEngineList, buf); + ASSIGN(currentEngine[i], buf); } ReplaceEngine(cps, i); } @@ -1591,8 +1593,6 @@ MatchEvent (int mode) NextMatchGame(); } -char *comboLine = NULL; // [HGM] recent: WinBoard's first-engine combobox line - void InitBackEnd3 P((void)) { @@ -1600,6 +1600,7 @@ InitBackEnd3 P((void)) char buf[MSG_SIZ]; int err, len; + ParseFeatures(appData.features[0], &first); if(!appData.icsActive && !appData.noChessProgram && !appData.matchMode && // mode involves only first engine !strcmp(appData.variant, "normal") && // no explicit variant request appData.NrRanks == -1 && appData.NrFiles == -1 && appData.holdingsSize == -1 && // no size overrides requested @@ -1623,7 +1624,7 @@ InitBackEnd3 P((void)) free(programVersion); programVersion = (char*) malloc(8 + strlen(PACKAGE_STRING) + strlen(first.tidy)); sprintf(programVersion, "%s + %s", PACKAGE_STRING, first.tidy); - FloatToFront(&appData.recentEngineList, comboLine ? comboLine : appData.firstChessProgram); + FloatToFront(&appData.recentEngineList, currentEngine[0] ? currentEngine[0] : appData.firstChessProgram); } if (appData.icsActive) { @@ -4301,7 +4302,7 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int } else if (count == 0) { RemoveInputSource(isr); - DisplayFatalError(_("Connection closed by ICS"), 0, 0); + DisplayFatalError(_("Connection closed by ICS"), 0, 6666); } else { DisplayFatalError(_("Error reading from ICS"), error, 1); } @@ -5169,7 +5170,7 @@ SendMoveToProgram (int moveNum, ChessProgramState *cps) m[2], m[3] - '0', m[5], m[6] - '0', m[2] + (m[0] > m[5] ? 1 : -1), m[3] - '0'); - else if(*c && m[8]) { // kill square followed by 2 characters: 2nd kill square rather than promo suffix + else if(*c && m[8] != '\n') { // kill square followed by 2 characters: 2nd kill square rather than promo suffix *c = m[9]; if(*c == '\n') *c = NULLCHAR; snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d,%c%d%c%d%s\n", m[0], m[1] - '0', // convert to three moves m[7], m[8] - '0', @@ -5386,7 +5387,7 @@ CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char 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); + sprintf(move+4, ";%c%c%c\n", AAA + killX, ONE + killY, promoChar); if(kill2X >= 0 && kill2Y >= 0) sprintf(move+7, "%c%c%c\n", AAA + kill2X, ONE + kill2Y, promoChar); } } @@ -6498,7 +6499,7 @@ SendBoard (ChessProgramState *cps, int moveNum) * deprecated "black" command. */ if (!WhiteOnMove(moveNum)) // [HGM] but better a deprecated command than an illegal move... - SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn ? "a2a3\n" : "black\n", cps); + SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn && !pieceDesc[WhitePawn] ? "a2a3\n" : "black\nforce\n", cps); if(!cps->extendedEdit) left = BOARD_LEFT, right = BOARD_RGHT; // only board proper @@ -6702,9 +6703,12 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) // invalid move return FALSE; + if(legal[toY][toX] == 4) return FALSE; + piece = boards[currentMove][fromY][fromX]; if(gameInfo.variant == VariantChu) { promotionZoneSize = BOARD_HEIGHT/3; + if(legal[toY][toX] == 6) return FALSE; // no promotion if highlights deny it highestPromotingPiece = (PieceToChar(piece) == '+' || PieceToChar(CHUPROMOTED(piece)) != '+') ? WhitePawn : WhiteKing; } else if(gameInfo.variant == VariantShogi) { promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8); @@ -7397,13 +7401,13 @@ MarkByFEN(char *fen) { int r, f; if(!appData.markers || !appData.highlightDragging) return; - for(r=0; r= 'A' && *fen <= 'Z') legal[r][f] = 3; else + if(*fen == 'B') legal[r][f] = 4; else // request auto-promotion to victim + if(*fen >= 'A' && *fen <= 'Z') legal[r][f] = 6; 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 @@ -7537,12 +7541,13 @@ void ReportClick(char *action, int x, int y) } Boolean right; // instructs front-end to use button-1 events as if they were button 3 +Boolean deferChoice; void LeftClick (ClickType clickType, int xPix, int yPix) { int x, y; - Boolean saveAnimate; + static Boolean saveAnimate; static int second = 0, promotionChoice = 0, clearFlag = 0, sweepSelecting = 0, flashing = 0, saveFlash; char promoChoice = NULLCHAR; ChessSquare piece; @@ -7550,6 +7555,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) if(flashing) return; + if(!deferChoice) { // when called for a retry, skip everything to the point where we left off x = EventToSquare(xPix, BOARD_WIDTH); y = EventToSquare(yPix, BOARD_HEIGHT); if (!flipView && y >= 0) { @@ -7640,7 +7646,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) 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 = killX = killY = kill2X = kill2Y = -1; + fromX = x; fromY = y; toX = toY = killX = killY = kill2X = kill2Y = -1; *promoRestrict = NULLCHAR; 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 appData.sweepSelect && CanPromote(boards[currentMove][fromY][fromX], fromY) && originalY != y) { @@ -7692,7 +7698,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) !(fromP == BlackKing && toP == BlackRook && frc)))) { /* Clicked again on same color piece -- changed his mind */ second = (x == fromX && y == fromY); - killX = killY = kill2X = kill2Y = -1; + killX = killY = kill2X = kill2Y = -1; *promoRestrict = NULLCHAR; if(second && gameMode == AnalyzeMode && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) { second = FALSE; // first double-click rather than scond click doubleClick = first.excludeMoves; // used by UserMoveEvent to recognize exclude moves @@ -7893,14 +7899,24 @@ LeftClick (ClickType clickType, int xPix, int yPix) } // off-board moves should not be highlighted - if(x < 0 || y < 0) ClearHighlights(); - else ReportClick("put", x, y); + if(x < 0 || y < 0) { + ClearHighlights(); + DrawPosition(FALSE, NULL); + } else ReportClick("put", x, y); if(gatingPiece != EmptySquare && gameInfo.variant == VariantSChess) promoChoice = ToLower(PieceToChar(gatingPiece)); + } if(legal[toY][toX] == 2) { // highlight-induced promotion if(piece == defaultPromoChoice) promoChoice = NULLCHAR; // deferral else promoChoice = ToLower(PieceToChar(defaultPromoChoice)); + } else if(legal[toY][toX] == 4) { // blue target square: engine must supply promotion choice + if(!*promoRestrict) { // but has not done that yet + deferChoice = TRUE; // set up retry for when it does + return; // and wait for that + } + promoChoice = ToLower(*promoRestrict); // force engine's choice + deferChoice = FALSE; } if (legal[toY][toX] == 2 && !appData.sweepSelect || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, appData.sweepSelect)) { @@ -7930,6 +7946,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) if(saveAnimate && !appData.animate && currentMove != oldMove && // drag-move was performed Explode(boards[currentMove-1], fromX, fromY, toX, toY)) DrawPosition(TRUE, boards[currentMove]); + else DrawPosition(FALSE, NULL); fromX = fromY = -1; flashing = 0; } @@ -9170,9 +9187,13 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } return; } - if(sscanf(message, "choice %s", promoRestrict) == 1 && promoSweep != EmptySquare) { - promoSweep = CharToPiece(currentMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict)); - Sweep(0); + if(!appData.testLegality && sscanf(message, "choice %s", promoRestrict) == 1) { + if(deferChoice) { + LeftClick(Press, 0, 0); // finish the click that was interrupted + } else if(promoSweep != EmptySquare) { + promoSweep = CharToPiece(currentMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict)); + if(strlen(promoRestrict) > 1) Sweep(0); + } return; } /* [HGM] Allow engine to set up a position. Don't ask me why one would @@ -10192,16 +10213,16 @@ ParseGameHistory (char *game) void ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) { - ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, berolina = 0; + ChessSquare captured = board[toY][toX], piece, pawn, king, killed, killed2; int p, rookX, oldEP, epRank, epFile, lastFile, lastRank, berolina = 0; int promoRank = gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand || gameInfo.variant == VariantChuChess ? 3 : 1; /* [HGM] compute & store e.p. status and castling rights for new position */ /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */ if(gameInfo.variant == VariantBerolina) berolina = EP_BEROLIN_A; - oldEP = (signed char)board[EP_FILE]; epRank = board[EP_RANK]; + oldEP = (signed char)board[EP_STATUS]; epRank = board[EP_RANK]; epFile = board[EP_FILE]; lastFile = board[LAST_FILE],lastRank = board[LAST_RANK]; board[EP_STATUS] = EP_NONE; - board[EP_FILE] = board[EP_RANK] = 100; + board[EP_FILE] = board[EP_RANK] = board[LAST_FILE] = board[LAST_RANK] = 100; if (fromY == DROP_RANK) { /* must be first */ @@ -10233,6 +10254,13 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) } pawn = board[fromY][fromX]; + if(pieceDesc[pawn] && strchr(pieceDesc[pawn], 'e')) { // piece with user-defined e.p. capture + if(captured == EmptySquare && toX == epFile && (toY == (epRank & 127) || toY + (pawn < BlackPawn ? -1 : 1) == epRank - 128)) { + captured = board[lastRank][lastFile]; // remove victim + board[lastRank][lastFile] = EmptySquare; + pawn = EmptySquare; // kludge to suppress old e.p. code + } + } if( pawn == WhiteLance || pawn == BlackLance ) { if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantChu ) { if(gameInfo.variant == VariantSpartan) board[EP_STATUS] = EP_PAWN_MOVE; // in Spartan no e.p. rights must be set @@ -10250,6 +10278,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) if(toX fromX) board[EP_STATUS] = toX; + board[LAST_FILE] = toX; board[LAST_RANK] = toY; } } else if( pawn == BlackPawn ) { @@ -10263,6 +10292,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) if(toX fromX) board[EP_STATUS] = toX; + board[LAST_FILE] = toX; board[LAST_RANK] = toY; } } @@ -10357,18 +10387,16 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) 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) + && (epFile == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4) && (toX != fromX) && gameInfo.variant != VariantXiangqi && gameInfo.variant != VariantBerolina && (pawn == WhitePawn) && (board[toY][toX] == EmptySquare)) { + if(lastFile == 100) lastFile = (board[fromY][toX] == BlackPawn ? toX : fromX), lastRank = fromY; // assume FIDE e.p. if victim present board[fromY][fromX] = EmptySquare; board[toY][toX] = piece; - if(toY == epRank - 128 + 1) - captured = board[toY - 2][toX], board[toY - 2][toX] = EmptySquare; - else - captured = board[toY - 1][toX], board[toY - 1][toX] = EmptySquare; + captured = board[lastRank][lastFile], board[lastRank][lastFile] = EmptySquare; } else if ((fromY == BOARD_HEIGHT-4) && (toX == fromX) && gameInfo.variant == VariantBerolina @@ -10424,18 +10452,16 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) 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) + && (epFile == toX && epRank == toY || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4) && (toX != fromX) && gameInfo.variant != VariantXiangqi && gameInfo.variant != VariantBerolina && (pawn == BlackPawn) && (board[toY][toX] == EmptySquare)) { + if(lastFile == 100) lastFile = (board[fromY][toX] == WhitePawn ? toX : fromX), lastRank = fromY; board[fromY][fromX] = EmptySquare; board[toY][toX] = piece; - if(toY == epRank - 128 - 1) - captured = board[toY + 2][toX], board[toY + 2][toX] = EmptySquare; - else - captured = board[toY + 1][toX], board[toY + 1][toX] = EmptySquare; + captured = board[lastRank][lastFile], board[lastRank][lastFile] = EmptySquare; } else if ((fromY == 3) && (toX == fromX) && gameInfo.variant == VariantBerolina @@ -10902,31 +10928,47 @@ InitChessProgram (ChessProgramState *cps, int setup) } -void -ResendOptions (ChessProgramState *cps) +char * +ResendOptions (ChessProgramState *cps, int toEngine) { // send the stored value of the options int i; - char buf[MSG_SIZ]; + static char buf2[MSG_SIZ*10]; + char buf[MSG_SIZ], *p = buf2; Option *opt = cps->option; + *p = NULLCHAR; for(i=0; inrOptions; i++, opt++) { + *buf = NULLCHAR; switch(opt->type) { case Spin: case Slider: case CheckBox: - snprintf(buf, MSG_SIZ, "option %s=%d\n", opt->name, opt->value); + if(opt->value != *(int*) (opt->name + MSG_SIZ - 104)) + snprintf(buf, MSG_SIZ, "%s=%d", opt->name, opt->value); break; case ComboBox: - snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->choice[opt->value]); + if(opt->value != *(int*) (opt->name + MSG_SIZ - 104)) + snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->choice[opt->value]); break; default: - snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->textValue); + if(strcmp(opt->textValue, opt->name + MSG_SIZ - 100)) + snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->textValue); break; case Button: case SaveButton: continue; } - SendToProgram(buf, cps); + if(*buf) { + if(toEngine) { + snprintf(buf2, MSG_SIZ, "option %s\n", buf); + SendToProgram(buf2, cps); + } else { + if(p != buf2) *p++ = ','; + strncpy(p, buf, 10*MSG_SIZ-1 - (p - buf2)); + while(*p) p++; + } + } } + return buf2; } void @@ -10974,7 +11016,7 @@ StartChessProgram (ChessProgramState *cps) cps->comboCnt = 0; // and values of combo boxes } SendToProgram(buf, cps); - if(cps->reload) ResendOptions(cps); + if(cps->reload) ResendOptions(cps, TRUE); } else { SendToProgram("xboard\n", cps); } @@ -11195,7 +11237,7 @@ CreateTourney (char *name) int NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) { - char buf[MSG_SIZ], *p, *q; + char buf[2*MSG_SIZ], *p, *q; int i=1, header, skip, all = !strcmp(group, "all"), depth = 0; insert = names; // afterwards, this global will point just after last retrieved engine line or group end in the 'names' skip = !all && group[0]; // if group requested, we start in skip mode @@ -11228,6 +11270,32 @@ NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) return i; } +void +SaveEngineSettings (int n) +{ + int len; char *p, *q, *s, buf[MSG_SIZ], *optionSettings; + if(!currentEngine[n] || !currentEngine[n][0]) { DisplayMessage("saving failed: engine not from list", ""); return; } // no engine from list is loaded + p = strstr(firstChessProgramNames, currentEngine[n]); + if(!p) { DisplayMessage("saving failed: engine not found in list", ""); return; } // sanity check; engine could be deleted from list after loading + optionSettings = ResendOptions(n ? &second : &first, FALSE); + len = strlen(currentEngine[n]); + q = p + len; *p = 0; // cut list into head and tail piece + s = strstr(currentEngine[n], "firstOptions"); + if(s && (s[-1] == '-' || s[-1] == '/') && (s[12] == ' ' || s[12] == '=') && (s[13] == '"' || s[13] == '\'')) { + char *r = s + 14; + while(*r && *r != s[13]) r++; + s[14] = 0; // cut currentEngine into head and tail part, removing old settings + snprintf(buf, MSG_SIZ, "%s%s%s", currentEngine[n], optionSettings, *r ? r : "\""); // synthesize new engine line + } else if(*optionSettings) { + snprintf(buf, MSG_SIZ, "%s -firstOptions \"%s\"", currentEngine[n], optionSettings); + } + ASSIGN(currentEngine[n], buf); // updated engine line + len = p - firstChessProgramNames + strlen(q) + strlen(currentEngine[n]) + 1; + s = malloc(len); + snprintf(s, len, "%s%s%s", firstChessProgramNames, currentEngine[n], q); + FREE(firstChessProgramNames); firstChessProgramNames = s; // new list +} + // following implemented as macro to avoid type limitations #define SWAP(item, temp) temp = appData.item[0]; appData.item[0] = appData.item[n]; appData.item[n] = temp; @@ -11275,6 +11343,7 @@ GetEngineLine (char *s, int n) if(n == 1) SwapEngines(n); ParseArgsFromString(buf); if(n == 1) SwapEngines(n); + if(n < 2) { ASSIGN(currentEngine[n], command[i]); } if(n == 0 && *appData.secondChessProgram == NULLCHAR) { SwapEngines(1); // set second same as first if not yet set (to suppress WB startup dialog) ParseArgsFromString(buf); @@ -11652,7 +11721,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays) && result != GameIsDrawn) { int i, j, k=0, oppoKings = 0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn); for(j=BOARD_LEFT; j= 0 && p <= (int)WhiteKing) k++; oppoKings += (p + color == WhiteKing + BlackPawn - color); } @@ -14857,7 +14926,9 @@ MachineWhiteEvent () safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0])); strcat(bookMove, bookHit); - HandleMachineMove(bookMove, &first); + savedMessage = bookMove; // args for deferred call + savedState = &first; + ScheduleDelayedEvent(DeferredBookMove, 1); } } @@ -14932,7 +15003,9 @@ MachineBlackEvent () safeStrCpy(bookMove, "move ", sizeof(bookMove)/sizeof(bookMove[0])); strcat(bookMove, bookHit); - HandleMachineMove(bookMove, &first); + savedMessage = bookMove; // args for deferred call + savedState = &first; + ScheduleDelayedEvent(DeferredBookMove, 1); } } @@ -15002,7 +15075,7 @@ WaitForEngine (ChessProgramState *cps, DelayedEventCallback retry) void TwoMachinesEvent P((void)) { - int i; + int i, move = forwardMostMove; char buf[MSG_SIZ]; ChessProgramState *onmove; char *bookHit = NULL; @@ -15119,8 +15192,8 @@ TwoMachinesEvent P((void)) } } - ResetClocks(); - if (!first.sendTime || !second.sendTime) { + if (!first.sendTime || !second.sendTime || move == 0) { // [HGM] first engine changed sides from Reset, so recalc time odds + ResetClocks(); timeRemaining[0][forwardMostMove] = whiteTimeRemaining; timeRemaining[1][forwardMostMove] = blackTimeRemaining; } @@ -17146,8 +17219,10 @@ StringFeature (char **p, char *name, char **loc, ChessProgramState *cps) if (strncmp((*p), name, len) == 0 && (*p)[len] == '=' && (*p)[len+1] == '\"') { (*p) += len + 2; - ASSIGN(*loc, *p); // kludge alert: assign rest of line just to be sure allocation is large enough so that sscanf below always fits - sscanf(*p, "%[^\"]", *loc); + len = strlen(*p) + 1; if(len < MSG_SIZ && !strcmp(name, "option")) len = MSG_SIZ; // make sure string options have enough space to change their value + FREE(*loc); *loc = malloc(len); + strncpy(*loc, *p, len); + sscanf(*p, "%[^\"]", *loc); // should always fit, because we allocated at least strlen(*p) while (**p && **p != '\"') (*p)++; if (**p == '\"') (*p)++; snprintf(buf, MSG_SIZ, "accepted %s\n", name); @@ -17249,6 +17324,8 @@ ParseOption (Option *opt, ChessProgramState *cps) strcat(buf, "\n"); SendToProgram(buf, cps); } + *(int*) (opt->name + MSG_SIZ - 104) = opt->value; // hide default values somewhere + if(opt->target == &opt->textValue) strncpy(opt->name + MSG_SIZ - 100, opt->textValue, 99); return TRUE; } @@ -17329,6 +17406,7 @@ ParseFeatures (char *args, ChessProgramState *cps) if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue; if (StringFeature(&p, "option", &q, cps)) { // read to freshly allocated temp buffer first if(cps->reload) { FREE(q); q = NULL; continue; } // we are reloading because of xreuse + if(cps->nrOptions == 0) { ASSIGN(cps->option[0].name, _("Make Persistent -save")); ParseOption(&(cps->option[cps->nrOptions++]), cps); } FREE(cps->option[cps->nrOptions].name); cps->option[cps->nrOptions].name = q; q = NULL; if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature @@ -19046,9 +19124,10 @@ int transparency[2]; void LoadTheme () { - char *p, *q, buf[MSG_SIZ]; +#define BUF_SIZ (2*MSG_SIZ) + char *p, *q, buf[BUF_SIZ]; if(engineLine && engineLine[0]) { // a theme was selected from the listbox - snprintf(buf, MSG_SIZ, "-theme %s", engineLine); + snprintf(buf, BUF_SIZ, "-theme %s", engineLine); ParseArgsFromString(buf); ActivateTheme(TRUE); // also redo colors return; @@ -19058,46 +19137,48 @@ LoadTheme () { int len; q = appData.themeNames; - snprintf(buf, MSG_SIZ, "\"%s\"", nickName); + snprintf(buf, BUF_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, + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -ubt true -lbtf \"%s\"", + Shorten(appData.liteBackTextureFile)); + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -dbtf \"%s\" -lbtm %d -dbtm %d", + Shorten(appData.darkBackTextureFile), appData.liteBackTextureMode, appData.darkBackTextureMode ); } else { - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ubt false"); + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -ubt false"); } if(!appData.useBitmaps || transparency[0]) { - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -lsc %s", Col2Text(2) ); // lightSquareColor + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -lsc %s", Col2Text(2) ); // lightSquareColor } if(!appData.useBitmaps || transparency[1]) { - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -dsc %s", Col2Text(3) ); // darkSquareColor + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -dsc %s", Col2Text(3) ); // darkSquareColor } if(appData.useBorder) { - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub true -border \"%s\"", + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -ub true -border \"%s\"", appData.border); } else { - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -ub false"); + snprintf(buf+strlen(buf), BUF_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", + snprintf(buf+strlen(buf), BUF_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"); + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -upf false"); if(appData.pieceDirectory[0]) { - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -pid \"%s\"", appData.pieceDirectory); + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -pid \"%s\"", Shorten(appData.pieceDirectory)); if(appData.trueColors != 2) // 2 is a kludge to suppress this in WinBoard - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -trueColors %s", appData.trueColors ? "true" : "false"); + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -trueColors %s", appData.trueColors ? "true" : "false"); } - if(!appData.pieceDirectory[0] && !appData.trueColors) - snprintf(buf+strlen(buf), MSG_SIZ-strlen(buf), " -wpc %s -bpc %s", + if(!appData.pieceDirectory[0] || !appData.trueColors) + snprintf(buf+strlen(buf), BUF_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", + snprintf(buf+strlen(buf), BUF_SIZ-strlen(buf), " -hsc %s -phc %s\n", Col2Text(4), // highlightSquareColor Col2Text(5) ); // premoveHighlightColor appData.themeNames = malloc(len = strlen(q) + strlen(buf) + 1);