security fix: replaced sprintf with snprintf
[xboard.git] / winboard / jaws.c
1 /*\r
2  * JAWS.c -- Code for Windows front end to XBoard to use it with JAWS\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts.\r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 // This file collects all patches for the JAWS version, so they can all be included in winboard.c\r
56 // in one big swoop. At the bottom of this file you can read instructions for how to patch\r
57 // WinBoard to work with JAWS with the aid of this file. Note that the code in this file\r
58 // is for WinBoard 4.3 and later; for older WB versions you would have to throw out the\r
59 // piece names for all pieces from Guard to Unicorn, #define ONE as '1', AAA as 'a',\r
60 // BOARD_LEFT as 0, BOARD_RGHT and BOARD_HEIGHT as 8, and set holdingssizes to 0.\r
61 // You will need to build with jaws.rc in stead of winboard.rc.\r
62 \r
63 // from resource.h\r
64 \r
65 #define IDM_PossibleAttackMove          1800\r
66 #define IDM_PossibleAttacked            1801\r
67 #define IDM_SayMachineMove              1802\r
68 #define IDM_ReadRow                     1803\r
69 #define IDM_ReadColumn                  1804\r
70 #define IDM_SayCurrentPos               1805\r
71 #define IDM_SayAllBoard                 1806\r
72 #define IDM_SayUpperDiagnols            1807\r
73 #define IDM_SayLowerDiagnols            1808\r
74 #define IDM_SayClockTime                1810\r
75 #define IDM_SayWhosTurn                 1811\r
76 #define IDM_SayKnightMoves              1812\r
77 #define ID_SHITTY_HI                    1813\r
78 #define IDM_SayWhitePieces              1816\r
79 #define IDM_SayBlackPieces              1817\r
80 \r
81 \r
82 // from common.h, but 'extern' added to it, so the actual declaraton can remain in backend.c\r
83 \r
84 extern long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
85 \r
86 // from moves.c, added WinBoard_F piece types and ranks / files\r
87 \r
88 char *squareToChar[] = { "ay", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" };\r
89 \r
90 char *squareToNum[] = {"naught", "1", "2", "3", "4", "5", "6", "7", "8", "9" };\r
91 \r
92 char *ordinals[] = {"zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth"};\r
93 \r
94 char *pieceToName[] = {\r
95                 "White Pawn", "White Knight", "White Bishop", "White Rook", "White Queen",\r
96                 "White Guard", "White Elephant", "White Arch Bishop", "White Chancellor",\r
97                 "White General", "White Man", "White Cannon", "White Night Rider",\r
98                 "White Crowned Bishop", "White Crowned Rook", "White Grass Hopper", "White Veteran",\r
99                 "White Falcon", "White Amazon", "White Snake", "White Unicorn",\r
100                 "White King",\r
101                 "Black Pawn", "Black Knight", "Black Bishop", "Black Rook", "Black Queen",\r
102                 "Black Guard", "Black Elephant", "Black Arch Bishop", "Black Chancellor",\r
103                 "Black General", "Black Man", "Black Cannon", "Black Night Rider",\r
104                 "Black Crowned Bishop", "Black Crowned Rook", "Black Grass Hopper", "Black Veteran",\r
105                 "Black Falcon", "Black Amazon", "Black Snake", "Black Unicorn",\r
106                 "Black King",\r
107                 "Empty"\r
108         };\r
109 \r
110 char *pieceTypeName[] = {\r
111                 "Pawn", "Knight", "Bishop", "Rook", "Queen",\r
112                 "Guard", "Elephant", "Arch Bishop", "Chancellor",\r
113                 "General", "Man", "Cannon", "Night Rider",\r
114                 "Crowned Bishop", "Crowned Rook", "Grass Hopper", "Veteran",\r
115                 "Falcon", "Amazon", "Snake", "Unicorn",\r
116                 "King",\r
117                 "Pawn", "Knight", "Bishop", "Rook", "Queen",\r
118                 "Guard", "Elephant", "Arch Bishop", "Chancellor",\r
119                 "General", "Man", "Cannon", "Night Rider",\r
120                 "Crowned Bishop", "Crowned Rook", "Grass Hopper", "Veteran",\r
121                 "Falcon", "Amazon", "Snake", "Unicorn",\r
122                 "King",\r
123                 "Empty"\r
124         };\r
125 \r
126 int CoordToNum(c)\r
127                 char c;\r
128 {\r
129         if(isdigit(c)) return c - ONE;\r
130         if(c >= 'a') return c - AAA;\r
131         return 0;\r
132 }\r
133 \r
134 char* PieceToName(p, i)\r
135         ChessSquare p;\r
136         int i;\r
137 {\r
138         if(i) return pieceToName[(int) p];\r
139                 return pieceTypeName[(int) p];\r
140 }\r
141 \r
142 char* SquareToChar(x)\r
143                         int x;\r
144 {\r
145                 return squareToChar[x - BOARD_LEFT];\r
146 }\r
147 \r
148 char* SquareToNum(y)\r
149                         int y;\r
150 {\r
151                 return squareToNum[y + (gameInfo.boardHeight < 10)];\r
152 }\r
153 \r
154 \r
155 // from winboard.c: all new routines\r
156 #define JFWAPI __declspec(dllimport)\r
157 JFWAPI BOOL WINAPI JFWSayString (LPCTSTR lpszStrinToSpeak, BOOL bInterrupt);\r
158 \r
159 typedef JFWAPI BOOL (WINAPI *PSAYSTRING)(LPCTSTR lpszStrinToSpeak, BOOL bInterrupt);\r
160 \r
161 PSAYSTRING RealSayString;\r
162 \r
163 VOID SayString(char *mess, BOOL flag)\r
164 { // for debug file\r
165         char buf[8000], *p;\r
166         if(appData.debugMode) fprintf(debugFP, "SAY '%s'\n", mess);\r
167         safeStrCpy(buf, mess, sizeof(buf)/sizeof(buf[0]));\r
168         if(p = StrCaseStr(buf, "Xboard adjudication:")) {\r
169                 int i;\r
170                 for(i=19; i>1; i--) p[i] = p[i-1];\r
171                 p[1] = ' ';\r
172         }\r
173         RealSayString(buf, flag);\r
174 }\r
175 \r
176 //static int fromX = 0, fromY = 0;\r
177 static int oldFromX, oldFromY;\r
178 static int timeflag;\r
179 static int suppressClocks = 0;\r
180 static int suppressOneKey = 0;\r
181 static HANDLE hAccelJAWS;\r
182 \r
183 typedef struct { char *name; int code; } MenuItemDesc;\r
184 \r
185 MenuItemDesc menuItemJAWS[] = {\r
186 {"Say Clock &Time\tAlt+T",      IDM_SayClockTime },\r
187 {"-", 0 },\r
188 {"Say Last &Move\tAlt+M",       IDM_SayMachineMove },\r
189 {"Say W&ho's Turn\tAlt+X",      IDM_SayWhosTurn },\r
190 {"-", 0 },\r
191 {"Say Complete &Position\tAlt+P",IDM_SayAllBoard },\r
192 {"Say &White Pieces\tAlt+W",    IDM_SayWhitePieces },\r
193 {"Say &Black Pieces\tAlt+B",    IDM_SayBlackPieces },\r
194 {"Say Board &Rank\tAlt+R",      IDM_ReadRow },\r
195 {"Say Board &File\tAlt+F",      IDM_ReadColumn },\r
196 {"-", 0 },\r
197 {"Say &Upper Diagonals\tAlt+U",  IDM_SayUpperDiagnols },\r
198 {"Say &Lower Diagonals\tAlt+L",  IDM_SayLowerDiagnols },\r
199 {"Say K&night Moves\tAlt+N",    IDM_SayKnightMoves },\r
200 {"Say Current &Square\tAlt+S",  IDM_SayCurrentPos },\r
201 {"Say &Attacks\tAlt+A",         IDM_PossibleAttackMove },\r
202 {"Say Attacke&d\tAlt+D",        IDM_PossibleAttacked },\r
203 {NULL, 0}\r
204 };\r
205 \r
206 ACCEL acceleratorsJAWS[] = {\r
207 {FVIRTKEY|FALT, 'T', IDM_SayClockTime },\r
208 {FVIRTKEY|FALT, 'M', IDM_SayMachineMove },\r
209 {FVIRTKEY|FALT, 'X', IDM_SayWhosTurn },\r
210 {FVIRTKEY|FALT, 'P', IDM_SayAllBoard },\r
211 {FVIRTKEY|FALT, 'W', IDM_SayWhitePieces },\r
212 {FVIRTKEY|FALT, 'B', IDM_SayBlackPieces },\r
213 {FVIRTKEY|FALT, 'R', IDM_ReadRow },\r
214 {FVIRTKEY|FALT, 'F', IDM_ReadColumn },\r
215 {FVIRTKEY|FALT, 'U', IDM_SayUpperDiagnols },\r
216 {FVIRTKEY|FALT, 'L', IDM_SayLowerDiagnols },\r
217 {FVIRTKEY|FALT, 'N', IDM_SayKnightMoves },\r
218 {FVIRTKEY|FALT, 'S', IDM_SayCurrentPos },\r
219 {FVIRTKEY|FALT, 'A', IDM_PossibleAttackMove },\r
220 {FVIRTKEY|FALT, 'D', IDM_PossibleAttacked }\r
221 };\r
222 \r
223 void\r
224 AdaptMenu()\r
225 {\r
226         HMENU menuMain, menuJAWS;\r
227         MENUBARINFO helpMenuInfo;\r
228         int i;\r
229 \r
230         helpMenuInfo.cbSize = sizeof(helpMenuInfo);\r
231         menuMain = GetMenu(hwndMain);\r
232         menuJAWS = CreatePopupMenu();\r
233 \r
234         for(i=0; menuItemJAWS[i].name; i++) {\r
235             if(menuItemJAWS[i].name[0] == '-')\r
236                  AppendMenu(menuJAWS, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
237             else AppendMenu(menuJAWS, MF_ENABLED|MF_STRING,\r
238                         (UINT_PTR) menuItemJAWS[i].code, (LPCTSTR) menuItemJAWS[i].name);\r
239         }\r
240         InsertMenu(menuMain, 5, MF_BYPOSITION|MF_POPUP|MF_ENABLED|MF_STRING,\r
241                 (UINT_PTR) menuJAWS, "&JAWS");\r
242         oldMenuItemState[6] = oldMenuItemState[5];\r
243         DrawMenuBar(hwndMain);\r
244 }\r
245 \r
246 BOOL\r
247 InitJAWS()\r
248 {       // to be called at beginning of WinMain, after InitApplication and InitInstance\r
249         HINSTANCE hApi = LoadLibrary("jfwapi32.dll");\r
250         if(!hApi) {\r
251                 DisplayInformation("Missing jfwapi32.dll");\r
252                 return (FALSE);\r
253         }\r
254 \r
255         RealSayString = (PSAYSTRING)GetProcAddress(hApi, "JFWSayString");\r
256         if(!RealSayString) {\r
257                 DisplayInformation("SayString returned a null pointer");\r
258                 return (FALSE);\r
259         }\r
260 \r
261         {\r
262                 // [HGM] kludge to reduce need for modification of winboard.c: make tinyLayout menu identical\r
263                 // to standard layout, so that code for switching between them does not have to be deleted\r
264                 int i;\r
265 \r
266                 AdaptMenu();\r
267                 menuBarText[0][5] = "&JAWS";\r
268                 for(i=0; i<7; i++) menuBarText[1][i] = menuBarText[0][i];\r
269         }\r
270 \r
271         hAccelJAWS = CreateAcceleratorTable(acceleratorsJAWS, 14);\r
272 \r
273         /* initialize cursor position */\r
274         fromX = fromY = 0;\r
275         SetHighlights(fromX, fromY, -1, -1);\r
276         DrawPosition(FALSE, NULL);\r
277         oldFromX = oldFromY = -1;\r
278 \r
279         if(hwndConsole) SetFocus(hwndConsole);\r
280         return TRUE;\r
281 }\r
282 \r
283 int beeps[] = { 1, 0, 0, 0, 0 };\r
284 int beepCodes[] = { 0, MB_OK, MB_ICONERROR, MB_ICONQUESTION, MB_ICONEXCLAMATION, MB_ICONASTERISK };\r
285 static int dropX = -1, dropY = -1;\r
286 \r
287 VOID\r
288 KeyboardEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
289 {\r
290         ChessSquare currentPiece;\r
291         char *piece, *xchar, *ynum ;\r
292         int n, beepType = 1; // empty beep\r
293 \r
294         if(fromX == -1 || fromY == -1) { // if we just dropped piece, stay at that square\r
295                 fromX = dropX; fromY = dropY;\r
296                 dropX = dropY = -1; // but only once\r
297         }\r
298         if(fromX == -1 || fromY == -1) {\r
299                 fromX = BOARD_LEFT; fromY = 0;\r
300         }\r
301         switch(wParam) {\r
302         case VK_LEFT:\r
303                 if(fromX == BOARD_RGHT+1) fromX -= 2; else\r
304                 if(fromX == BOARD_LEFT) { if(fromY >= BOARD_HEIGHT - gameInfo.holdingsSize) fromX -= 2; else beepType = 0; } else\r
305                 if(fromX > BOARD_LEFT) fromX--; else beepType = 0; // off-board beep\r
306                 break;\r
307         case VK_RIGHT:\r
308                 if(fromX == BOARD_LEFT-2) fromX += 2; else\r
309                 if(fromX == BOARD_RGHT-1) { if(fromY < gameInfo.holdingsSize) fromX += 2; else beepType = 0; } else\r
310                 if(fromX < BOARD_RGHT-1) fromX++; else beepType = 0;\r
311                 break;\r
312         case VK_UP:\r
313                 if(fromX == BOARD_RGHT+1) { if(fromY < gameInfo.holdingsSize - 1) fromY++; else beepType = 0; } else\r
314                 if(fromY < BOARD_HEIGHT-1) fromY++; else beepType = 0;\r
315                 break;\r
316         case VK_DOWN:\r
317                 if(fromX == BOARD_LEFT-2) { if(fromY > BOARD_HEIGHT - gameInfo.holdingsSize) fromY--; else beepType = 0; } else\r
318                 if(fromY > 0) fromY--; else beepType = 0;\r
319                 break;\r
320         }\r
321         SetHighlights(fromX, fromY, -1, -1);\r
322         DrawPosition(FALSE, NULL);\r
323         currentPiece = boards[currentMove][fromY][fromX];\r
324         piece = PieceToName(currentPiece,1);\r
325         if(beepType == 1 && currentPiece != EmptySquare) beepType = currentPiece < (int) BlackPawn ? 2 : 3; // white or black beep\r
326         if(beeps[beepType] == beeps[1] && (fromX == BOARD_RGHT+1 || fromX == BOARD_LEFT-2)) beepType = 4; // holdings beep\r
327         beepType = beeps[beepType]%6;\r
328         if(beepType) MessageBeep(beepCodes[beepType]);\r
329         if(fromX == BOARD_LEFT - 2) {\r
330                 SayString("black holdings", FALSE);\r
331                 if(currentPiece != EmptySquare) {\r
332                         char buf[MSG_SIZ];\r
333                         n = boards[currentMove][fromY][1];\r
334                         snprintf(buf, MSG_SIZ, "%d %s%s", n, PieceToName(currentPiece,0), n == 1 ? "" : "s");\r
335                         SayString(buf, TRUE);\r
336                 }\r
337         } else\r
338         if(fromX == BOARD_RGHT + 1) {\r
339                 SayString("white holdings", FALSE);\r
340                 if(currentPiece != EmptySquare) {\r
341                         char buf[MSG_SIZ];\r
342                         n = boards[currentMove][fromY][BOARD_WIDTH-2];\r
343                         snprintf(buf, MSG)SIZ,"%d %s%s", n, PieceToName(currentPiece,0), n == 1 ? "" : "s");\r
344                         SayString(buf, TRUE);\r
345                 }\r
346         } else\r
347         if(fromX >= BOARD_LEFT && fromX < BOARD_RGHT) {\r
348                 char buf[MSG_SIZ];\r
349                 xchar = SquareToChar(fromX);\r
350                 ynum = SquareToNum(fromY);\r
351                 if(currentPiece != EmptySquare) {\r
352 //                      SayString(piece[0] == 'W' ? "white" : "black", TRUE);\r
353                   snprintf(buf, MSG_SIZ, "%s %s %s", xchar, ynum, piece);\r
354                 } else snprintf(buf, MSG_SIZ, "%s %s", xchar, ynum);\r
355                 SayString(buf, TRUE);\r
356         }\r
357         return;\r
358 }\r
359 \r
360 int PosFlags(int nr);\r
361 \r
362 typedef struct {\r
363     int rf, ff, rt, ft;\r
364     int onlyCaptures;\r
365     int count;\r
366 } ReadClosure;\r
367 \r
368 extern void ReadCallback P((Board board, int flags, ChessMove kind,\r
369                                 int rf, int ff, int rt, int ft,\r
370                                 VOIDSTAR closure));\r
371 \r
372 void ReadCallback(board, flags, kind, rf, ff, rt, ft, closure)\r
373      Board board;\r
374      int flags;\r
375      ChessMove kind;\r
376      int rf, ff, rt, ft;\r
377      VOIDSTAR closure;\r
378 {\r
379     register ReadClosure *cl = (ReadClosure *) closure;\r
380     ChessSquare possiblepiece;\r
381     char *piece, *xchar, *ynum ;\r
382 \r
383 //if(appData.debugMode) fprintf(debugFP, "%c%c%c%c\n", ff+AAA, rf+ONE, ft+AAA, rt+ONE);\r
384     if(cl->ff == ff && cl->rf == rf) {\r
385         possiblepiece = board[rt][ft];\r
386         if(possiblepiece != EmptySquare) {\r
387                 piece = PieceToName(possiblepiece,1);\r
388                 xchar = SquareToChar(ft);\r
389                 ynum  = SquareToNum(rt);\r
390                 SayString(xchar , FALSE);\r
391                 SayString(ynum, FALSE);\r
392                 SayString(piece, FALSE);\r
393                 cl->count++;\r
394         }\r
395     }\r
396     if(cl->ft == ft && cl->rt == rt) {\r
397         possiblepiece = board[rf][ff];\r
398                 piece = PieceToName(possiblepiece,1);\r
399                 xchar = SquareToChar(ff);\r
400                 ynum  = SquareToNum(rf);\r
401                 SayString(xchar , FALSE);\r
402                 SayString(ynum, FALSE);\r
403                 SayString(piece, FALSE);\r
404                 cl->count++;\r
405     }\r
406 }\r
407 \r
408 VOID\r
409 PossibleAttackMove()\r
410 {\r
411         ReadClosure cl;\r
412         ChessSquare piece, victim;\r
413         int removedSelectedPiece = 0, swapColor;\r
414 \r
415 //if(appData.debugMode) fprintf(debugFP, "PossibleAttackMove %d %d %d %d\n", fromX, fromY, oldFromX, oldFromY);\r
416         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
417         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
418 \r
419         piece = boards[currentMove][fromY][fromX];\r
420         if(piece == EmptySquare) { // if square is empty, try to substitute selected piece\r
421             if(oldFromX >= 0 && oldFromY >= 0) {\r
422                 piece = boards[currentMove][oldFromY][oldFromX];\r
423                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
424                 removedSelectedPiece = 1;\r
425                 SayString("Your", FALSE);\r
426                 SayString(PieceToName(piece, 0), FALSE);\r
427                 SayString("would have", FALSE);\r
428             } else { SayString("You must select a piece first", FALSE); return; }\r
429         }\r
430 \r
431         victim = boards[currentMove][fromY][fromX];\r
432         boards[currentMove][fromY][fromX] = piece; // make sure piece is actally there\r
433         SayString("possible captures from here are", FALSE);\r
434 \r
435         swapColor = piece <  (int)BlackPawn && !WhiteOnMove(currentMove) ||\r
436                     piece >= (int)BlackPawn &&  WhiteOnMove(currentMove);\r
437         cl.count = 0; cl.rf = fromY; cl.ff = fromX; cl.rt = cl.ft = -1;\r
438         GenLegal(boards[currentMove], PosFlags(currentMove + swapColor), ReadCallback, (VOIDSTAR) &cl);\r
439         if(cl.count == 0) SayString("None", FALSE);\r
440         boards[currentMove][fromY][fromX] = victim; // repair\r
441 \r
442         if( removedSelectedPiece ) boards[currentMove][oldFromY][oldFromX] = piece;\r
443 }\r
444 \r
445 \r
446 VOID\r
447 PossibleAttacked()\r
448 {\r
449         ReadClosure cl;\r
450         ChessSquare piece = EmptySquare, victim;\r
451 \r
452         if(fromY < 0 || fromY >= BOARD_HEIGHT) return;\r
453         if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) { SayString("holdings",FALSE); return; }\r
454 \r
455         if(oldFromX >= 0 && oldFromY >= 0) { // if piece is selected, remove it\r
456                 piece = boards[currentMove][oldFromY][oldFromX];\r
457                 boards[currentMove][oldFromY][oldFromX] = EmptySquare;\r
458         }\r
459 \r
460         SayString("Pieces that can capture you are", FALSE);\r
461 \r
462         victim = boards[currentMove][fromY][fromX]; // put dummy piece on target square, to activate Pawn captures\r
463         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? WhiteQueen : BlackQueen;\r
464         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
465         GenLegal(boards[currentMove], PosFlags(currentMove+1), ReadCallback, (VOIDSTAR) &cl);\r
466         if(cl.count == 0) SayString("None", FALSE);\r
467 \r
468         SayString("You are defended by", FALSE);\r
469 \r
470         boards[currentMove][fromY][fromX] = WhiteOnMove(currentMove) ? BlackQueen : WhiteQueen;\r
471         cl.count = 0; cl.rt = fromY; cl.ft = fromX; cl.rf = cl.ff = -1;\r
472         GenLegal(boards[currentMove], PosFlags(currentMove), ReadCallback, (VOIDSTAR) &cl);\r
473         if(cl.count == 0) SayString("None", FALSE);\r
474         boards[currentMove][fromY][fromX] = victim; // put back original occupant\r
475 \r
476         if(oldFromX >= 0 && oldFromY >= 0) { // put back possibl selected piece\r
477                 boards[currentMove][oldFromY][oldFromX] = piece;\r
478         }\r
479 }\r
480 \r
481 VOID\r
482 ReadRow()\r
483 {\r
484         ChessSquare currentpiece;\r
485         char *piece, *xchar, *ynum ;\r
486         int xPos, count=0;\r
487         ynum = SquareToNum(fromY);\r
488 \r
489         if(fromY < 0) return;\r
490 \r
491         for (xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
492                 currentpiece = boards[currentMove][fromY][xPos];\r
493                 if(currentpiece != EmptySquare) {\r
494                         piece = PieceToName(currentpiece,1);\r
495                         xchar = SquareToChar(xPos);\r
496                         SayString(xchar , FALSE);\r
497                         SayString(ynum, FALSE);\r
498                         SayString(piece, FALSE);\r
499                         count++;\r
500                 }\r
501         }\r
502         if(count == 0) {\r
503                 SayString("rank", FALSE);\r
504                 SayString(ynum, FALSE);\r
505                 SayString("empty", FALSE);\r
506         }\r
507 }\r
508 \r
509 VOID\r
510 ReadColumn()\r
511 {\r
512         ChessSquare currentpiece;\r
513         char *piece, *xchar, *ynum ;\r
514         int yPos, count=0;\r
515         xchar = SquareToChar(fromX);\r
516 \r
517         if(fromX < 0) return;\r
518 \r
519         for (yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
520                 currentpiece = boards[currentMove][yPos][fromX];\r
521                 if(currentpiece != EmptySquare) {\r
522                         piece = PieceToName(currentpiece,1);\r
523                         ynum = SquareToNum(yPos);\r
524                         SayString(xchar , FALSE);\r
525                         SayString(ynum, FALSE);\r
526                         SayString(piece, FALSE);\r
527                         count++;\r
528                 }\r
529         }\r
530         if(count == 0) {\r
531                 SayString(xchar, FALSE);\r
532                 SayString("file empty", FALSE);\r
533         }\r
534 }\r
535 \r
536 VOID\r
537 SayUpperDiagnols()\r
538 {\r
539         ChessSquare currentpiece;\r
540         char *piece, *xchar, *ynum ;\r
541         int yPos, xPos;\r
542 \r
543         if(fromX < 0 || fromY < 0) return;\r
544 \r
545         if(fromX < BOARD_RGHT-1 && fromY < BOARD_HEIGHT-1) {\r
546                 SayString("The diagnol squares to your upper right contain", FALSE);\r
547                 yPos = fromY+1;\r
548                 xPos = fromX+1;\r
549                 while(yPos<BOARD_HEIGHT && xPos<BOARD_RGHT) {\r
550                         currentpiece = boards[currentMove][yPos][xPos];\r
551                         piece = PieceToName(currentpiece,1);\r
552                         xchar = SquareToChar(xPos);\r
553                         ynum = SquareToNum(yPos);\r
554                         SayString(xchar , FALSE);\r
555                         SayString(ynum, FALSE);\r
556                         SayString(piece, FALSE);\r
557                         yPos++;\r
558                         xPos++;\r
559                 }\r
560         }\r
561         else SayString("There is no squares to your upper right", FALSE);\r
562 \r
563         if(fromX > BOARD_LEFT && fromY < BOARD_HEIGHT-1) {\r
564                 SayString("The diagnol squares to your upper left contain", FALSE);\r
565                 yPos = fromY+1;\r
566                 xPos = fromX-1;\r
567                 while(yPos<BOARD_HEIGHT && xPos>=BOARD_LEFT) {\r
568                         currentpiece = boards[currentMove][yPos][xPos];\r
569                         piece = PieceToName(currentpiece,1);\r
570                         xchar = SquareToChar(xPos);\r
571                         ynum = SquareToNum(yPos);\r
572                         SayString(xchar , FALSE);\r
573                         SayString(ynum, FALSE);\r
574                         SayString(piece, FALSE);\r
575                         yPos++;\r
576                         xPos--;\r
577                 }\r
578         }\r
579         else SayString("There is no squares to your upper left", FALSE);\r
580 }\r
581 \r
582 VOID\r
583 SayLowerDiagnols()\r
584 {\r
585         ChessSquare currentpiece;\r
586         char *piece, *xchar, *ynum ;\r
587         int yPos, xPos;\r
588 \r
589         if(fromX < 0 || fromY < 0) return;\r
590 \r
591         if(fromX < BOARD_RGHT-1 && fromY > 0) {\r
592                 SayString("The diagnol squares to your lower right contain", FALSE);\r
593                 yPos = fromY-1;\r
594                 xPos = fromX+1;\r
595                 while(yPos>=0 && xPos<BOARD_RGHT) {\r
596                         currentpiece = boards[currentMove][yPos][xPos];\r
597                         piece = PieceToName(currentpiece,1);\r
598                         xchar = SquareToChar(xPos);\r
599                         ynum = SquareToNum(yPos);\r
600                         SayString(xchar , FALSE);\r
601                         SayString(ynum, FALSE);\r
602                         SayString(piece, FALSE);\r
603                         yPos--;\r
604                         xPos++;\r
605                 }\r
606         }\r
607         else SayString("There is no squares to your lower right", FALSE);\r
608 \r
609         if(fromX > BOARD_LEFT && fromY > 0) {\r
610                 SayString("The diagnol squares to your lower left contain", FALSE);\r
611                 yPos = fromY-1;\r
612                 xPos = fromX-1;\r
613                 while(yPos>=0 && xPos>=BOARD_LEFT) {\r
614                         currentpiece = boards[currentMove][yPos][xPos];\r
615                         piece = PieceToName(currentpiece,1);\r
616                         xchar = SquareToChar(xPos);\r
617                         ynum = SquareToNum(yPos);\r
618                         SayString(xchar , FALSE);\r
619                         SayString(ynum, FALSE);\r
620                         SayString(piece, FALSE);\r
621                         yPos--;\r
622                         xPos--;\r
623                 }\r
624         }\r
625         else SayString("There is no squares to your lower left", FALSE);\r
626 }\r
627 \r
628 VOID\r
629 SayKnightMoves()\r
630 {\r
631         ChessSquare currentpiece, oldpiece;\r
632         char *piece, *xchar, *ynum ;\r
633 \r
634         oldpiece = boards[currentMove][fromY][fromX];\r
635         if(oldpiece == WhiteKnight || oldpiece == BlackKnight)\r
636                 SayString("The possible squares a Knight could move to are", FALSE);\r
637         else\r
638                 SayString("The squares a Knight could possibly attack from are", FALSE);\r
639 \r
640         if (fromY+2 < BOARD_HEIGHT && fromX-1 >= BOARD_LEFT) {\r
641                 currentpiece = boards[currentMove][fromY+2][fromX-1];\r
642                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
643                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
644                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
645                 {\r
646                         piece = PieceToName(currentpiece,1);\r
647                         xchar = SquareToChar(fromX-1);\r
648                         ynum = SquareToNum(fromY+2);\r
649                         SayString(xchar , FALSE);\r
650                         SayString(ynum, FALSE);\r
651                         SayString(piece, FALSE);\r
652                 }\r
653         }\r
654 \r
655         if (fromY+2 < BOARD_HEIGHT && fromX+1 < BOARD_RGHT) {\r
656                 currentpiece = boards[currentMove][fromY+2][fromX+1];\r
657                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
658                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
659                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
660                 {\r
661                         piece = PieceToName(currentpiece,1);\r
662                         xchar = SquareToChar(fromX+1);\r
663                         ynum = SquareToNum(fromY+2);\r
664                         SayString(xchar , FALSE);\r
665                         SayString(ynum, FALSE);\r
666                         SayString(piece, FALSE);\r
667                 }\r
668         }\r
669 \r
670         if (fromY+1 < BOARD_HEIGHT && fromX+2 < BOARD_RGHT) {\r
671                 currentpiece = boards[currentMove][fromY+1][fromX+2];\r
672                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
673                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
674                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
675                 {\r
676                         piece = PieceToName(currentpiece,1);\r
677                         xchar = SquareToChar(fromX+2);\r
678                         ynum = SquareToNum(fromY+1);\r
679                         SayString(xchar , FALSE);\r
680                         SayString(ynum, FALSE);\r
681                         SayString(piece, FALSE);\r
682                 }\r
683         }\r
684 \r
685         if (fromY-1 >= 0 && fromX+2 < BOARD_RGHT) {\r
686                 currentpiece = boards[currentMove][fromY-1][fromX+2];\r
687                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
688                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
689                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
690                 {\r
691                         piece = PieceToName(currentpiece,1);\r
692                         xchar = SquareToChar(fromX+2);\r
693                         ynum = SquareToNum(fromY-1);\r
694                         SayString(xchar , FALSE);\r
695                         SayString(ynum, FALSE);\r
696                         SayString(piece, FALSE);\r
697                 }\r
698         }\r
699 \r
700         if (fromY-2 >= 0 && fromX+1 < BOARD_RGHT) {\r
701                 currentpiece = boards[currentMove][fromY-2][fromX+1];\r
702                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
703                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
704                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
705                 {\r
706                         piece = PieceToName(currentpiece,1);\r
707                         xchar = SquareToChar(fromX+1);\r
708                         ynum = SquareToNum(fromY-2);\r
709                         SayString(xchar , FALSE);\r
710                         SayString(ynum, FALSE);\r
711                         SayString(piece, FALSE);\r
712                 }\r
713         }\r
714 \r
715         if (fromY-2 >= 0 && fromX-1 >= BOARD_LEFT) {\r
716                 currentpiece = boards[currentMove][fromY-2][fromX-1];\r
717                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
718                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
719                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
720                 {\r
721                         piece = PieceToName(currentpiece,1);\r
722                         xchar = SquareToChar(fromX-1);\r
723                         ynum = SquareToNum(fromY-2);\r
724                         SayString(xchar , FALSE);\r
725                         SayString(ynum, FALSE);\r
726                         SayString(piece, FALSE);\r
727                 }\r
728         }\r
729 \r
730         if (fromY-1 >= 0 && fromX-2 >= BOARD_LEFT) {\r
731                 currentpiece = boards[currentMove][fromY-1][fromX-2];\r
732                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
733                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
734                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
735                 {\r
736                         piece = PieceToName(currentpiece,1);\r
737                         xchar = SquareToChar(fromX-2);\r
738                         ynum = SquareToNum(fromY-1);\r
739                         SayString(xchar , FALSE);\r
740                         SayString(ynum, FALSE);\r
741                         SayString(piece, FALSE);\r
742                 }\r
743         }\r
744 \r
745         if (fromY+1 < BOARD_HEIGHT && fromX-2 >= BOARD_LEFT) {\r
746                 currentpiece = boards[currentMove][fromY+1][fromX-2];\r
747                 if(((oldpiece == WhiteKnight) && (currentpiece > WhiteKing))\r
748                         || ((oldpiece == BlackKnight) && (currentpiece < BlackPawn || currentpiece == EmptySquare))\r
749                         || (oldpiece == EmptySquare) && (currentpiece == WhiteKnight || currentpiece == BlackKnight))\r
750                 {\r
751                         piece = PieceToName(currentpiece,1);\r
752                         xchar = SquareToChar(fromX-2);\r
753                         ynum = SquareToNum(fromY+1);\r
754                         SayString(xchar , FALSE);\r
755                         SayString(ynum, FALSE);\r
756                         SayString(piece, FALSE);\r
757                 }\r
758         }\r
759 }\r
760 \r
761 VOID\r
762 SayPieces(ChessSquare p)\r
763 {\r
764         ChessSquare currentpiece;\r
765         char *piece, *xchar, *ynum ;\r
766         int yPos, xPos, count = 0;\r
767         char buf[50];\r
768 \r
769         if(p == WhitePlay)   SayString("White pieces", FALSE); else\r
770         if(p == BlackPlay)   SayString("Black pieces", FALSE); else\r
771         if(p == EmptySquare) SayString("Pieces", FALSE); else {\r
772           snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%ss", PieceToName(p,1));\r
773                 SayString(buf, FALSE);\r
774         }\r
775         SayString("are located", FALSE);\r
776         for(yPos=0; yPos<BOARD_HEIGHT; yPos++) {\r
777                 for(xPos=BOARD_LEFT; xPos<BOARD_RGHT; xPos++) {\r
778                         currentpiece = boards[currentMove][yPos][xPos];\r
779                         if(p == BlackPlay && currentpiece >= BlackPawn && currentpiece <= BlackKing ||\r
780                            p == WhitePlay && currentpiece >= WhitePawn && currentpiece <= WhiteKing   )\r
781                                 piece = PieceToName(currentpiece,0);\r
782                         else if(p == EmptySquare && currentpiece != EmptySquare)\r
783                                 piece = PieceToName(currentpiece,1);\r
784                         else if(p == currentpiece)\r
785                                 piece = NULL;\r
786                         else continue;\r
787 \r
788                                 if(count == 0) SayString("at", FALSE);\r
789                                 xchar = SquareToChar(xPos);\r
790                                 ynum = SquareToNum(yPos);\r
791                                 SayString(xchar , FALSE);\r
792                                 SayString(ynum, FALSE);\r
793                                 if(piece) SayString(piece, FALSE);\r
794                                 count++;\r
795                 }\r
796         }\r
797         if(count == 0) SayString("nowhere", FALSE);\r
798 }\r
799 \r
800 VOID\r
801 SayCurrentPos()\r
802 {\r
803         ChessSquare currentpiece;\r
804         char *piece, *xchar, *ynum ;\r
805         if(fromX <  BOARD_LEFT) { SayString("You strayed into the white holdings", FALSE); return; }\r
806         if(fromX >= BOARD_RGHT) { SayString("You strayed into the black holdings", FALSE); return; }\r
807         currentpiece = boards[currentMove][fromY][fromX];\r
808         piece = PieceToName(currentpiece,1);\r
809         ynum = SquareToNum(fromY);\r
810         xchar = SquareToChar(fromX);\r
811         SayString("Your current position is", FALSE);\r
812         SayString(xchar, FALSE);\r
813         SayString(ynum, FALSE);\r
814         SayString(piece, FALSE);\r
815         if(((fromX-BOARD_LEFT) ^ fromY)&1)\r
816                 SayString("on a light square",FALSE);\r
817         else\r
818                 SayString("on a dark square",FALSE);\r
819 \r
820         PossibleAttacked();\r
821         return;\r
822 }\r
823 \r
824 VOID\r
825 SayAllBoard()\r
826 {\r
827         int Xpos, Ypos;\r
828         ChessSquare currentpiece;\r
829         char *piece, *ynum ;\r
830 \r
831         if(gameInfo.holdingsWidth) {\r
832                 int first = 0;\r
833                 for(Ypos=0; Ypos<gameInfo.holdingsSize; Ypos++) {\r
834                         int n = boards[currentMove][Ypos][BOARD_WIDTH-2];\r
835                         if(n) {\r
836                           char buf[MSG_SIZ];\r
837                           if(!first++)\r
838                             SayString("white holds", FALSE);\r
839                           currentpiece = boards[currentMove][Ypos][BOARD_WIDTH-1];\r
840                           piece = PieceToName(currentpiece,0);\r
841                           snprintf(buf, MSG_SIZ,"%d %s%s", n, piece, (n==1 ? "" : "s") );\r
842                           SayString(buf, FALSE);\r
843                         }\r
844                 }\r
845                 first = 0;\r
846                 for(Ypos=BOARD_HEIGHT-1; Ypos>=BOARD_HEIGHT - gameInfo.holdingsSize; Ypos--) {\r
847                         int n = boards[currentMove][Ypos][1];\r
848                         if(n) {\r
849                           char buf[MSG_SIZ];\r
850                           if(!first++)\r
851                             SayString("black holds", FALSE);\r
852                           currentpiece = boards[currentMove][Ypos][0];\r
853                           piece = PieceToName(currentpiece,0);\r
854                           snprintf(buf, MSG_SIZ, "%d %s%s", n, piece, (n==1 ? "" : "s") );\r
855                           SayString(buf, FALSE);\r
856                         }\r
857                 }\r
858         }\r
859 \r
860         for(Ypos=BOARD_HEIGHT-1; Ypos>=0; Ypos--) {\r
861                 ynum = ordinals[Ypos + (gameInfo.boardHeight < 10)];\r
862                 SayString(ynum, FALSE);\r
863                 SayString("rank", FALSE);\r
864                 for(Xpos=BOARD_LEFT; Xpos<BOARD_RGHT; Xpos++) {\r
865                         currentpiece = boards[currentMove][Ypos][Xpos];\r
866                         if(currentpiece != EmptySquare) {\r
867                                 int count = 0;\r
868                                 char buf[50];\r
869                                 piece = PieceToName(currentpiece,1);\r
870                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == currentpiece)\r
871                                         Xpos++, count++;\r
872                                 if(count > 1)\r
873                                   snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%d %ss", count, piece);\r
874                                 else\r
875                                   snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s", piece);\r
876                                 Xpos--;\r
877                                 SayString(buf, FALSE);\r
878                         } else {\r
879                                 int count = 0, oldX = Xpos;\r
880                                 while(Xpos < BOARD_RGHT && boards[currentMove][Ypos][Xpos] == EmptySquare)\r
881                                         Xpos++, count++;\r
882                                 if(Xpos == BOARD_RGHT && oldX == BOARD_LEFT)\r
883                                         SayString("all", FALSE);\r
884                                 else{\r
885                                     if(count > 1) {\r
886                                         char buf[10];\r
887                                         snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", count);\r
888                                         SayString(buf, FALSE);\r
889                                     }\r
890                                     Xpos--;\r
891                                 }\r
892                                 SayString("empty", FALSE);\r
893                         }\r
894                 }\r
895         }\r
896 \r
897 }\r
898 \r
899 VOID\r
900 SayWhosTurn()\r
901 {\r
902         if(gameMode == MachinePlaysBlack || gameMode == IcsPlayingWhite) {\r
903                 if(WhiteOnMove(currentMove))\r
904                         SayString("It is your turn", FALSE);\r
905                 else    SayString("It is your opponents turn", FALSE);\r
906         } else if(gameMode == MachinePlaysWhite || gameMode == IcsPlayingBlack) {\r
907                 if(WhiteOnMove(currentMove))\r
908                         SayString("It is your opponents turn", FALSE);\r
909                 else    SayString("It is your turn", FALSE);\r
910         } else {\r
911                 if(WhiteOnMove(currentMove))\r
912                         SayString("White is on move here", FALSE);\r
913                 else    SayString("Black is on move here", FALSE);\r
914         }\r
915 }\r
916 \r
917 extern char *commentList[];\r
918 \r
919 VOID\r
920 SayMachineMove(int evenIfDuplicate)\r
921 {\r
922         int len, xPos, yPos, moveNr, secondSpace = 0, castle = 0, n;\r
923         ChessSquare currentpiece;\r
924         char *piece, *xchar, *ynum, *p, checkMark = 0;\r
925         char c, buf[MSG_SIZ], comment[MSG_SIZ];\r
926         static char disambiguation[2];\r
927         static int previousMove = 0;\r
928 \r
929         if(appData.debugMode) fprintf(debugFP, "Message = '%s'\n", messageText);\r
930         if(gameMode == BeginningOfGame) return;\r
931         if(messageText[0] == '[') return;\r
932         comment[0]= 0;\r
933             if(isdigit(messageText[0])) { // message is move, possibly with thinking output\r
934                 int dotCount = 0, spaceCount = 0;\r
935                 sscanf(messageText, "%d", &moveNr);\r
936                 len = 0;\r
937                 // [HGM] show: better extraction of move\r
938                 while (messageText[len] != NULLCHAR) {\r
939                     if(messageText[len] == '.' && spaceCount == 0) dotCount++;\r
940                     if(messageText[len] == ' ') { if(++spaceCount == 2) secondSpace = len; }\r
941                     if(messageText[len] == '{') { // we detected a comment\r
942                         if(isalpha(messageText[len+1]) ) sscanf(messageText+len, "{%[^}]}", comment);\r
943                         break;\r
944                     }\r
945                     if(messageText[len] == '[') { // we detected thinking output\r
946                         int depth; float score=0; char c, lastMover = (dotCount == 3 ? 'B' : 'W');\r
947                         if(sscanf(messageText+len+1, "%d]%c%f", &depth, &c, &score) > 1) {\r
948                             if(c == ' ') { // if not explicitly specified, figure out source of thinking output\r
949                                 switch(gameMode) {\r
950                                   case MachinePlaysWhite:\r
951                                   case IcsPlayingWhite:\r
952                                     c = 'W'; break;\r
953                                   case IcsPlayingBlack:\r
954                                   case MachinePlaysBlack:\r
955                                     c = 'B';\r
956                                   default:\r
957                                     break;\r
958                                 }\r
959                             }\r
960                             if(c != lastMover && !evenIfDuplicate) return; // line is thinking output of future move, ignore.\r
961                             if(2*moveNr - (dotCount < 2) == previousMove)\r
962                                 return; // do not repeat same move; likely ponder output\r
963                             snprintf(buf, MSG_SIZ, "score %s %d at %d ply",\r
964                                         score > 0 ? "plus" : score < 0 ? "minus" : "",\r
965                                         (int) (fabs(score)*100+0.5),\r
966                                         depth );\r
967                             SayString(buf, FALSE); // move + thinking output describing it; say it.\r
968                         }\r
969                         while(messageText[len-1] == ' ') len--; // position just behind move;\r
970                         break;\r
971                     }\r
972                     if(messageText[len] == '(') { // ICS time printed behind move\r
973                         while(messageText[len+1] && messageText[len] != ')') len++; // skip it\r
974                     }\r
975                     len++;\r
976                 }\r
977                 if(secondSpace) len = secondSpace; // position behind move\r
978                 if(messageText[len-1] == '+' || messageText[len-1] == '#') {  /* you are in checkmate */\r
979                         len--; // strip off check or mate indicator\r
980                       checkMark = messageText[len]; // make sure still seen after we stip off promo piece\r
981                 }\r
982                 if(messageText[len-2] == '=') {  /* promotion */\r
983                         len-=2; // strip off promotion piece\r
984                         SayString("promotion", FALSE);\r
985                 }\r
986 \r
987                 n = 2*moveNr - (dotCount < 2);\r
988 \r
989                 if(previousMove != 2*moveNr + (dotCount > 1) || evenIfDuplicate) {\r
990                     char number[20];\r
991                     previousMove = 2*moveNr + (dotCount > 1); // remember move nr of move last spoken\r
992                     snprintf(number, sizeof(number)/sizeof(number[0]),"%d", moveNr);\r
993 \r
994                     yPos = CoordToNum(messageText[len-1]);  /* turn char coords to ints */\r
995                     xPos = CoordToNum(messageText[len-2]);\r
996                     if(xPos < 0 || xPos > 11) return; // prevent crashes if no coord string available to speak\r
997                     if(yPos < 0 || yPos > 9)  return;\r
998                     currentpiece = boards[n][yPos][xPos];\r
999                     piece = PieceToName(currentpiece,0);\r
1000                     ynum = SquareToNum(yPos);\r
1001                     xchar = SquareToChar(xPos);\r
1002                     c = messageText[len-3];\r
1003                     if(c == 'x') c = messageText[len-4];\r
1004                     if(!isdigit(c) && c < 'a' && c != '@') c = 0;\r
1005                     disambiguation[0] = c;\r
1006                     SayString(WhiteOnMove(n) ? "Black" : "White", FALSE);\r
1007                     SayString("move", FALSE);\r
1008                     SayString(number, FALSE);\r
1009 //                  if(c==0 || c=='@') SayString("a", FALSE);\r
1010                     // intercept castling moves\r
1011                     p = StrStr(messageText, "O-O-O");\r
1012                     if(p && p-messageText < len) {\r
1013                         SayString("queen side castling",FALSE);\r
1014                         castle = 1;\r
1015                     } else {\r
1016                         p = StrStr(messageText, "O-O");\r
1017                         if(p && p-messageText < len) {\r
1018                             SayString("king side castling",FALSE);\r
1019                             castle = 1;\r
1020                         }\r
1021                     }\r
1022                     if(!castle) {\r
1023                         SayString(piece, FALSE);\r
1024                         if(c == '@') SayString("dropped on", FALSE); else\r
1025                         if(c) SayString(disambiguation, FALSE);\r
1026                         SayString("to", FALSE);\r
1027                         SayString(xchar, FALSE);\r
1028                         SayString(ynum, FALSE);\r
1029                         if(messageText[len-3] == 'x') {\r
1030                                 currentpiece = boards[n-1][yPos][xPos];\r
1031                                 if(currentpiece != EmptySquare) {\r
1032                                         piece = PieceToName(currentpiece,0);\r
1033                                         SayString("Capturing a",FALSE);\r
1034                                         SayString(piece, FALSE);\r
1035                                 } else SayString("Capturing onn passann",FALSE);\r
1036                         }\r
1037                     }\r
1038                     if(checkMark == '+') SayString("check", FALSE); else\r
1039                     if(checkMark == '#') {\r
1040                                 SayString("finishing off", FALSE);\r
1041                                 SayString(WhiteOnMove(n) ? "White" : "Black", FALSE);\r
1042                     }\r
1043                 }\r
1044 \r
1045                 /* say comment after move, possibly with result */\r
1046                 p = NULL;\r
1047                 if(StrStr(messageText, " 1-0")) p = "white wins"; else\r
1048                 if(StrStr(messageText, " 0-1")) p = "black wins"; else\r
1049                 if(StrStr(messageText, " 1/2-1/2")) p = "game ends in a draw";\r
1050                 if(comment[0]) {\r
1051                     if(p) {\r
1052                         if(!StrCaseStr(comment, "draw") &&\r
1053                            !StrCaseStr(comment, "white") &&\r
1054                            !StrCaseStr(comment, "black") ) {\r
1055                                 SayString(p, FALSE);\r
1056                                 SayString("due to", FALSE);\r
1057                         }\r
1058                     }\r
1059                     SayString(comment, FALSE); // alphabetic comment (usually game end)\r
1060                 } else if(p) SayString(p, FALSE);\r
1061 \r
1062                 if(commentDialog && commentList[currentMove]) SetFocus(commentDialog);\r
1063 \r
1064             } else {\r
1065                 /* starts not with digit */\r
1066                 if(StrCaseStr(messageText, "illegal")) PlayIcsUnfinishedSound();\r
1067                 SayString(messageText, FALSE);\r
1068             }\r
1069 \r
1070 }\r
1071 \r
1072 VOID\r
1073 SayClockTime()\r
1074 {\r
1075         char buf1[50], buf2[50];\r
1076         char *str1, *str2;\r
1077         static long int lastWhiteTime, lastBlackTime;\r
1078 \r
1079         suppressClocks = 1; // if user is using alt+T command, no reason to display them\r
1080         if(abs(lastWhiteTime - whiteTimeRemaining) < 1000 && abs(lastBlackTime - blackTimeRemaining) < 1000)\r
1081                 suppressClocks = 0; // back on after two requests in rapid succession\r
1082         snprintf(buf1, sizeof(buf1)/sizeof(buf1[0]),"%s", TimeString(whiteTimeRemaining));\r
1083         str1 = buf1;\r
1084         SayString("White clock", FALSE);\r
1085         SayString(str1, FALSE);\r
1086         snprintf(buf2, sizeof(buf2)/sizeof(buf2[0]), "%s", TimeString(blackTimeRemaining));\r
1087         str2 = buf2;\r
1088         SayString("Black clock", FALSE);\r
1089         SayString(str2, FALSE);\r
1090         lastWhiteTime = whiteTimeRemaining;\r
1091         lastBlackTime = blackTimeRemaining;\r
1092 }\r
1093 \r
1094 VOID\r
1095 Toggle(Boolean *b, char *mess)\r
1096 {\r
1097         *b = !*b;\r
1098         SayString(mess, FALSE);\r
1099         SayString("is now", FALSE);\r
1100         SayString(*b ? "on" : "off", FALSE);\r
1101 }\r
1102 \r
1103 /* handles keyboard moves in a click-click fashion */\r
1104 VOID\r
1105 KeyboardMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
1106 {\r
1107         ChessSquare currentpiece;\r
1108         char *piece;\r
1109 \r
1110         static BOOLEAN sameAgain = FALSE;\r
1111         switch (message) {\r
1112         case WM_KEYDOWN:\r
1113                 sameAgain = FALSE;\r
1114                 if(oldFromX == fromX && oldFromY == fromY) {\r
1115                         sameAgain = TRUE;\r
1116                         /* click on same square */\r
1117                         break;\r
1118                 }\r
1119                 else if(oldFromX != -1) {\r
1120 \r
1121                         ChessSquare pdown, pup;\r
1122       pdown = boards[currentMove][oldFromY][oldFromX];\r
1123       pup = boards[currentMove][fromY][fromX];\r
1124 \r
1125                 if (gameMode == EditPosition ||\r
1126                         !((WhitePawn <= pdown && pdown <= WhiteKing &&\r
1127                                  WhitePawn <= pup && pup <= WhiteKing) ||\r
1128                                 (BlackPawn <= pdown && pdown <= BlackKing &&\r
1129                                  BlackPawn <= pup && pup <= BlackKing))) {\r
1130                         /* EditPosition, empty square, or different color piece;\r
1131                         click-click move is possible */\r
1132                         char promoChoice = NULLCHAR;\r
1133 \r
1134                         if (HasPromotionChoice(oldFromX, oldFromY, fromX, fromY, &promoChoice)) {\r
1135                                 if (appData.alwaysPromoteToQueen) {\r
1136                                         UserMoveEvent(oldFromX, oldFromY, fromX, fromY, 'q');\r
1137                                 }\r
1138                                 else {\r
1139                                         toX = fromX; toY = fromY; fromX = oldFromX; fromY = oldFromY;\r
1140                                         PromotionPopup(hwnd);\r
1141                                         fromX = toX; fromY = toY;\r
1142                                 }\r
1143                         }\r
1144                         else {\r
1145                                 UserMoveEvent(oldFromX, oldFromY, fromX, fromY, promoChoice);\r
1146                         }\r
1147                 oldFromX = oldFromY = -1;\r
1148                 break;\r
1149                 }\r
1150 \r
1151                 }\r
1152                 /* First downclick, or restart on a square with same color piece */\r
1153                 if (OKToStartUserMove(fromX, fromY)) {\r
1154                 oldFromX = fromX;\r
1155                 oldFromY = fromY;\r
1156                 currentpiece = boards[currentMove][fromY][fromX];\r
1157                 piece = PieceToName(currentpiece,1);\r
1158                 SayString(piece, FALSE);\r
1159                 SayString("selected", FALSE);\r
1160                 }\r
1161                 else {\r
1162                 oldFromX = oldFromY = -1;\r
1163                 }\r
1164                 break;\r
1165 \r
1166         case WM_KEYUP:\r
1167                 if (oldFromX == fromX && oldFromY == fromY) {\r
1168       /* Upclick on same square */\r
1169       if (sameAgain) {\r
1170         /* Clicked same square twice: abort click-click move */\r
1171                         oldFromX = oldFromY = -1;\r
1172                         currentpiece = boards[currentMove][fromY][fromX];\r
1173                         piece = PieceToName(currentpiece,0);\r
1174                         SayString(piece, FALSE);\r
1175                         SayString("unselected", FALSE);\r
1176                         }\r
1177                 }\r
1178         }\r
1179 }\r
1180 \r
1181 int\r
1182 NiceTime(int x)\r
1183 {       // return TRUE for times we want to announce\r
1184         if(x<0) return 0;\r
1185         x = (x+50)/100;   // tenth of seconds\r
1186         if(x <= 100) return (x%10 == 0);\r
1187         if(x <= 600) return (x%100 == 0);\r
1188         if(x <= 6000) return (x%600 == 0);\r
1189         return (x%3000 == 0);\r
1190 }\r
1191 \r
1192 #define JAWS_ARGS \\r
1193   { "beepOffBoard", ArgInt, (LPVOID) beeps, TRUE, (ArgIniType) 1 },\\r
1194   { "beepEmpty", ArgInt, (LPVOID) (beeps+1), TRUE, (ArgIniType) 0 },\\r
1195   { "beepWhite", ArgInt, (LPVOID) (beeps+2), TRUE, (ArgIniType) 0 },\\r
1196   { "beepBlack", ArgInt, (LPVOID) (beeps+3), TRUE, (ArgIniType) 0 },\\r
1197   { "beepHoldings", ArgInt, (LPVOID) (beeps+4), TRUE, (ArgIniType) 0 },\\r
1198 \r
1199 #define JAWS_ALT_INTERCEPT \\r
1200             if(suppressOneKey) {\\r
1201                 suppressOneKey = 0;\\r
1202                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) break;\\r
1203             }\\r
1204             if ((char)wParam == 022 && gameMode == EditPosition) { /* <Ctl R>. Pop up piece menu */\\r
1205                 POINT pt; int x, y;\\r
1206                 SquareToPos(fromY, fromX, &x, &y);\\r
1207                 dropX = fromX; dropY = fromY;\\r
1208                 pt.x = x; pt.y = y;\\r
1209                 if(gameInfo.variant != VariantShogi)\\r
1210                     MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\\r
1211                 else\\r
1212                     MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\\r
1213                 break;\\r
1214             }\\r
1215 \r
1216 #define JAWS_REPLAY \\r
1217     case '\020': /* ctrl P */\\r
1218       { char buf[MSG_SIZ];\\r
1219         if(GetWindowText(hwnd, buf, MSG_SIZ-1))\\r
1220                 SayString(buf, FALSE);\\r
1221       }\\r
1222       return 0;\\r
1223 \r
1224 #define JAWS_KB_NAVIGATION \\r
1225 \\r
1226         case WM_KEYDOWN:\\r
1227 \\r
1228                 if(GetKeyState(VK_MENU) < 0 && GetKeyState(VK_CONTROL) < 0) {\\r
1229                     /* Control + Alt + letter used for speaking piece positions */\\r
1230                     static int lastTime; static char lastChar;\\r
1231                     int mine = 0, time = GetTickCount(); char c;\\r
1232 \\r
1233                     if((char)wParam == lastChar && time-lastTime < 250) mine = 1;\\r
1234                     lastChar = wParam; lastTime = time;\\r
1235                     c = wParam;\\r
1236 \\r
1237                     if(gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) mine = !mine;\\r
1238 \\r
1239                     if(ToLower(c) == 'x') {\\r
1240                         SayPieces(mine ? WhitePlay : BlackPlay);\\r
1241                         suppressOneKey = 1;\\r
1242                         break;\\r
1243                     } else\\r
1244                     if(CharToPiece(c) != EmptySquare) {\\r
1245                         SayPieces(CharToPiece(mine ? ToUpper(c) : ToLower(c)));\\r
1246                         suppressOneKey = 1;\\r
1247                         break;\\r
1248                     }\\r
1249                 }\\r
1250 \\r
1251                 switch (wParam) {\\r
1252                 case VK_LEFT:\\r
1253                 case VK_RIGHT:\\r
1254                 case VK_UP:\\r
1255                 case VK_DOWN:\\r
1256                         KeyboardEvent(hwnd, message, wParam, lParam);\\r
1257                         break;\\r
1258                 case VK_SPACE:\\r
1259                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1260                         break;\\r
1261                 }\\r
1262                 break;\\r
1263         case WM_KEYUP:\\r
1264                 switch (wParam) {\\r
1265                 case VK_SPACE:\\r
1266                         KeyboardMove(hwnd, message, wParam, lParam);\\r
1267                         break;\\r
1268                 }\\r
1269                 break;\\r
1270 \r
1271 #define JAWS_MENU_ITEMS \\r
1272                 case IDM_PossibleAttackMove:  /*What can I possible attack from here */\\r
1273                         PossibleAttackMove();\\r
1274                         break;\\r
1275 \\r
1276                 case IDM_PossibleAttacked:  /*what can possible attack this square*/\\r
1277                         PossibleAttacked();\\r
1278                         break;\\r
1279 \\r
1280                 case IDM_ReadRow:   /* Read the current row of pieces */\\r
1281                         ReadRow();\\r
1282                         break;\\r
1283 \\r
1284                 case IDM_ReadColumn:   /* Read the current column of pieces */\\r
1285                         ReadColumn();\\r
1286                         break;\\r
1287 \\r
1288                 case IDM_SayCurrentPos: /* Say current position including color */\\r
1289                         SayCurrentPos();\\r
1290                         break;\\r
1291 \\r
1292                 case IDM_SayAllBoard:  /* Say the whole board from bottom to top */\\r
1293                         SayAllBoard();\\r
1294                         break;\\r
1295 \\r
1296                 case IDM_SayMachineMove:  /* Say the last move made */\\r
1297                         timeflag = 1;\\r
1298                         SayMachineMove(1);\\r
1299                         break;\\r
1300 \\r
1301                 case IDM_SayUpperDiagnols:  /* Says the diagnol positions above you */\\r
1302                         SayUpperDiagnols();\\r
1303                         break;\\r
1304 \\r
1305                 case IDM_SayLowerDiagnols:  /* Say the diagnol positions below you */\\r
1306                         SayLowerDiagnols();\\r
1307                         break;\\r
1308 \\r
1309                 case IDM_SayBlackPieces: /*Say the opponents pieces */\\r
1310                         SayPieces(BlackPlay);\\r
1311                         break;\\r
1312 \\r
1313                 case IDM_SayWhitePieces: /*Say the opponents pieces */\\r
1314                         SayPieces(WhitePlay);\\r
1315                         break;\\r
1316 \\r
1317                 case IDM_SayClockTime:  /*Say the clock time */\\r
1318                         SayClockTime();\\r
1319                         break;\\r
1320 \\r
1321                 case IDM_SayWhosTurn:   /* Say whos turn it its */\\r
1322                         SayWhosTurn();\\r
1323                         break;\\r
1324 \\r
1325                 case IDM_SayKnightMoves:  /* Say Knights (L-shaped) move */\\r
1326                         SayKnightMoves();\\r
1327                         break;\\r
1328 \\r
1329                 case OPT_PonderNextMove:  /* Toggle option setting */\\r
1330                         Toggle(&appData.ponderNextMove, "ponder");\\r
1331                         break;\\r
1332 \\r
1333                 case OPT_AnimateMoving:  /* Toggle option setting */\\r
1334                         Toggle(&appData.animate, "animate moving");\\r
1335                         break;\\r
1336 \\r
1337                 case OPT_AutoFlag:  /* Toggle option setting */\\r
1338                         Toggle(&appData.autoCallFlag, "auto flag");\\r
1339                         break;\\r
1340 \\r
1341                 case OPT_AlwaysQueen:  /* Toggle option setting */\\r
1342                         Toggle(&appData.alwaysPromoteToQueen, "always promote to queen");\\r
1343                         break;\\r
1344 \\r
1345                 case OPT_TestLegality:  /* Toggle option setting */\\r
1346                         Toggle(&appData.testLegality, "legality testing");\\r
1347                         break;\\r
1348 \\r
1349                 case OPT_HideThinkFromHuman:  /* Toggle option setting */\\r
1350                         Toggle(&appData.hideThinkingFromHuman, "hide thinking");\\r
1351                         ShowThinkingEvent();\\r
1352                         break;\\r
1353 \\r
1354                 case OPT_SaveExtPGN:  /* Toggle option setting */\\r
1355                         Toggle(&appData.saveExtendedInfoInPGN, "extended P G N info");\\r
1356                         break;\\r
1357 \\r
1358                 case OPT_ExtraInfoInMoveHistory:  /* Toggle option setting */\\r
1359                         Toggle(&appData.showEvalInMoveHistory, "extra info in move histoty");\\r
1360                         break;\\r
1361 \\r
1362 \r
1363 \r
1364 #define JAWS_ACCEL \\r
1365         !(!frozen && TranslateAccelerator(hwndMain, hAccelJAWS, &msg)) &&\r
1366 \r
1367 #define JAWS_INIT if (!InitJAWS()) return (FALSE);\r
1368 \r
1369 #define JAWS_DELETE(X)\r
1370 \r
1371 #define JAWS_SILENCE if(suppressClocks) return;\r
1372 \r
1373 #define JAWS_COPYRIGHT \\r
1374         SetDlgItemText(hDlg, OPT_MESS, "Auditory/Keyboard Enhancements  By:  Ed Rodriguez (sort of)");\r
1375 \r
1376 #define SAY(S) SayString((S), FALSE)\r
1377 \r
1378 #define SAYMACHINEMOVE() SayMachineMove(0)\r
1379 \r
1380 // After inclusion of this file somewhere early in winboard.c, the remaining part of the patch\r
1381 // is scattered over winboard.c for actually calling the routines.\r