Implement test for perpetual checking
[capablanca.git] / lasker-2.2.3 / src / gamedb.c
1 /*
2    Copyright (c) 1993 Richard V. Nash.
3    Copyright (c) 2000 Dan Papasian
4    Copyright (C) Andrew Tridgell 2002
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 static int check_kings(struct game_state_t *gs);
24 static int get_empty_slot(void);
25 static int ReadGameAttrs_common(FILE *fp, int g, int version);
26 static long OldestHistGame(char *login);
27 static void RemoveHistGame(char *file, int maxlines);
28 static void write_g_out(int g, char *file, int maxlines, int isDraw, char *EndSymbol, char *name, time_t *now);
29 static int game_zero(int g);
30  
31 const char *TypeStrings[NUM_GAMETYPES] = {"untimed", "blitz", "standard", 
32                                           "nonstandard", "wild", "lightning", 
33                                           "bughouse", "gothic", "knightmate", 
34                                           "capablanca"};
35
36 /* this method is awful! how about allocation as we need it and freeing
37     afterwards! */
38 static int get_empty_slot(void)
39 {
40         int i;
41
42         for (i = 0; i < game_globals.g_num; i++) {
43                 if (game_globals.garray[i].status == GAME_EMPTY)
44                         return i;
45         }
46         game_globals.g_num++;
47         game_globals.garray = (struct game *)realloc(game_globals.garray, sizeof(struct game) * game_globals.g_num);
48         /* yeah great, bet this causes lag!  - DAV*/
49         /* I have serious doubt of the truth to the above client- bugg */
50         game_globals.garray[game_globals.g_num - 1].status = GAME_EMPTY;
51         return game_globals.g_num - 1;
52 }
53
54 int game_new(void)
55 {
56         int new = get_empty_slot();
57         game_zero(new);
58         return new;
59 }
60
61 static int game_zero(int g)
62 {
63         ZERO_STRUCT(game_globals.garray[g]);
64
65         game_globals.garray[g].white = -1;
66         game_globals.garray[g].black = -1;
67
68         game_globals.garray[g].status = GAME_NEW;
69         game_globals.garray[g].link = -1;
70         game_globals.garray[g].result = END_NOTENDED;
71         game_globals.garray[g].type = TYPE_UNTIMED;
72         game_globals.garray[g].game_state.gameNum = g;
73         game_globals.garray[g].wInitTime = 300; /* 5 minutes */
74         game_globals.garray[g].wIncrement = 0;
75         game_globals.garray[g].bInitTime = 300; /* 5 minutes */
76         game_globals.garray[g].bIncrement = 0;
77         game_globals.garray[g].flag_pending = FLAG_NONE;
78         strcpy(game_globals.garray[g].FENstartPos,INITIAL_FEN);
79         return 0;
80 }
81
82 int game_free(int g)
83 {
84         FREE(game_globals.garray[g].moveList);
85         FREE(game_globals.garray[g].examMoveList);
86         game_globals.garray[g].moveList = NULL;
87         game_globals.garray[g].examMoveList = NULL;
88         game_globals.garray[g].moveListSize = 0;
89         game_globals.garray[g].examMoveListSize = 0;
90         return 0;
91 }
92
93 int game_remove(int g)
94 {
95         /* Should remove game from players observation list */
96         game_free(g);
97         game_zero(g);
98         game_globals.garray[g].status = GAME_EMPTY;
99         return 0;
100 }
101
102 /* old moves not stored now - uses smoves */
103 int game_finish(int g)
104 {
105         player_game_ended(g);           /* Alert playerdb that game ended */
106         game_remove(g);
107         return 0;
108 }
109
110 void MakeFENpos (int g, char *FEN)
111 {
112         strcpy(FEN, boardToFEN(g));
113 }
114
115 static char *game_time_str(int wt, int winc, int bt, int binc)
116 {
117   static char tstr[50];
118
119   if ((!wt) && (!winc)) {                       /* Untimed */
120     strcpy(tstr, "");
121     return tstr;
122   }
123   if ((wt == bt) && (winc == binc)) {
124     sprintf(tstr, " %d %d", wt, winc);
125   } else {
126     sprintf(tstr, " %d %d : %d %d", wt, winc, bt, binc);
127   }
128   return tstr;
129 }
130
131 const char *bstr[] = {"untimed", "blitz", "standard", "non-standard", "wild", "lightning", "Bughouse", "Gothic", "Knightmate", "Capablanca"};
132
133 const char *rstr[] = {"unrated", "rated"};
134
135 char *game_str(int rated, int wt, int winc, int bt, int binc,
136                        char *cat, char *board)
137 {
138   static char tstr[200];
139
140   if (cat && cat[0] && board && board[0] &&
141       (strcmp(cat, "standard") || strcmp(board, "standard"))) {
142     sprintf(tstr, "%s %s%s Loaded from %s/%s",
143             rstr[rated],
144             bstr[game_isblitz(wt / 60, winc, bt / 60, binc, cat, board)],
145             game_time_str(wt / 60, winc, bt / 60, binc),
146             cat, board);
147   } else {
148     sprintf(tstr, "%s %s%s",
149             rstr[rated],
150             bstr[game_isblitz(wt / 60, winc, bt / 60, binc, cat, board)],
151             game_time_str(wt / 60, winc, bt / 60, binc));
152   }
153   return tstr;
154 }
155
156 int game_isblitz(int wt, int winc, int bt, int binc,
157                          char *cat, char *board)
158 {
159   int total;
160
161   if(cat && cat[0]) {
162     if (!strcmp(cat, "bughouse"))
163       return TYPE_BUGHOUSE;
164     if (!strcmp(cat, "gothic"))
165       return TYPE_GOTHIC;
166     if (!strcmp(cat, "knightmate"))
167       return TYPE_KNIGHTMATE;
168     if (!strcmp(cat, "capablanca"))
169       return TYPE_CAPABLANCA;
170     if (board && board[0]) {
171       if (!strcmp(cat, "wild"))
172         return TYPE_WILD;
173       if (strcmp(cat, "standard") || strcmp(board, "standard"))
174         return TYPE_NONSTANDARD;
175     }
176   }
177
178   if ((wt == 0) || (bt == 0))
179                         /* nonsense if one is timed and one is not */
180     return TYPE_UNTIMED;
181
182   if ((wt != bt) || (winc != binc))
183     return TYPE_NONSTANDARD;
184   total = wt * 60 + winc * 40;
185   if (total < 180)              /* 3 minute */
186     return TYPE_LIGHT;
187   if (total >= 900)             /* 15 minutes */
188     return TYPE_STAND;
189   else
190     return TYPE_BLITZ;
191 }
192
193 void send_board_to(int g, int p)
194 {
195   struct player *pp = &player_globals.parray[p];
196   char *b;
197   int side;
198   int relation;
199
200 /* since we know g and p, figure out our relationship to this game */
201
202   side = WHITE;
203   if ((game_globals.garray[g].status == GAME_EXAMINE) || (game_globals.garray[g].status == GAME_SETUP)) {
204     if (pp->game == g) {
205       relation = 2;
206     } else {
207       relation = -2;
208     }
209   } else {
210     if (pp->game == g) {
211       side = pp->side;
212       relation = ((side == game_globals.garray[g].game_state.onMove) ? 1 : -1);
213     } else {
214       relation = 0;
215     }
216   }
217
218   if (CheckPFlag(p, PFLAG_FLIP)) {              /* flip board? */
219     if (side == WHITE)
220       side = BLACK;
221     else
222       side = WHITE;
223   }
224   game_update_time(g);
225   b = board_to_string(game_globals.garray[g].white_name,
226                       game_globals.garray[g].black_name,
227                       game_globals.garray[g].wTime,
228                       game_globals.garray[g].bTime,
229                       &game_globals.garray[g].game_state,
230                       (game_globals.garray[g].status == GAME_EXAMINE || game_globals.garray[g].status == GAME_SETUP) ?
231                       game_globals.garray[g].examMoveList : game_globals.garray[g].moveList,
232                       pp->style,
233                       side, relation, p);
234   Bell(p);
235
236   if (pp->game == g && net_globals.con[pp->socket]->timeseal) {
237     pprintf_noformat(p, "\n%s\n[G]\n", b);
238   } else {
239     pprintf_noformat(p, "\n%s", b);
240   }
241
242   if (p != command_globals.commanding_player) {
243           send_prompt(p);
244   }
245 }
246
247 void send_boards(int g)
248 {
249         int p,p1;
250         struct simul_info_t *simInfo = player_globals.parray[game_globals.garray[g].white].simul_info;
251         int which_board = -1;
252
253         /* Begin code added 3/28/96 - Figbert */
254
255         if ((game_globals.garray[g].status != GAME_SETUP) && 
256             (check_kings(&game_globals.garray[g].game_state))) {
257                 d_printf( "Game has invalid number of kings.  Aborting...\n");
258 { int f, r;
259 d_printf("files = %d, ranks = %d\n", game_globals.garray[g].game_state.files, game_globals.garray[g].game_state.ranks);
260 for(r=0; r<game_globals.garray[g].game_state.ranks; r++) {
261   for(f=0; f<game_globals.garray[g].game_state.files; f++)
262         d_printf("b[%d][%d]=%d\n",f,r,game_globals.garray[g].game_state.board[f][r]);
263 }
264 }
265                 game_finish(g);
266                 
267                 for (p = 0; p < player_globals.p_num; p++) {
268                         struct player *pp = &player_globals.parray[p];
269                         if (pp->status == PLAYER_EMPTY)
270                                 continue;
271                         if (pp->game == g) {
272                                 
273                                 p1 = pp->opponent;
274                                 
275                                 if (p1 >= 0 && player_globals.parray[p1].game == g) {
276                                         pprintf (p1, "Disconnecting you from game number %d.\n", g+1);
277                                         player_globals.parray[p1].game = -1;
278                                 }
279                                 
280                                 pprintf (p, "Disconnecting you from game number %d.\n", g+1);
281                                 pp->game = -1;
282                         }
283                 }
284                 return;
285         }
286         
287         /* End code added 3/28/96 - Figbert */
288         
289         if (simInfo != NULL)
290                 which_board = simInfo->boards[simInfo->onBoard];
291         
292         if ((simInfo == NULL) || (which_board == g)) {
293                 for (p = 0; p < player_globals.p_num; p++) {
294                         struct player *pp = &player_globals.parray[p];
295                         if (pp->status == PLAYER_EMPTY)
296                                 continue;
297                         if (player_is_observe(p, g) || (pp->game == g))
298                                 send_board_to(g, p);
299                 }
300         }
301 }
302
303 void game_update_time(int g)
304 {
305         struct game *gg;
306         unsigned now, timesince;
307         
308         if (g == -1) {
309                 return;
310         }
311
312         gg = &game_globals.garray[g];
313
314         /* no update on first move */
315         if (gg->game_state.moveNum == 1) 
316                 return;
317         if (gg->clockStopped)
318                 return;
319         if (gg->type == TYPE_UNTIMED)
320                 return;
321         now = tenth_secs();
322         timesince = now - gg->lastDecTime;
323         if (gg->game_state.onMove == WHITE) {
324                 gg->wTime -= timesince;
325         } else {
326                 gg->bTime -= timesince;
327         }
328         gg->lastDecTime = now;
329 }
330
331 #if 0
332 static void game_update_times(void)
333 {
334         int g;
335         
336         for (g = 0; g < game_globals.g_num; g++) {
337                 if (game_globals.garray[g].status != GAME_ACTIVE)
338                         continue;
339                 if (game_globals.garray[g].clockStopped)
340                         continue;
341                 game_update_time(g);
342         }
343 }
344 #endif
345
346 char *EndString(int g, int personal)
347 {
348   static char endstr[200];
349   char *blackguy, *whiteguy;
350   static char blackstr[] = "Black";
351   static char whitestr[] = "White";
352
353   blackguy = (personal ? game_globals.garray[g].black_name : blackstr);
354   whiteguy = (personal ? game_globals.garray[g].white_name : whitestr);
355
356   switch (game_globals.garray[g].result) {
357   case END_CHECKMATE:
358     sprintf(endstr, "%s checkmated",
359             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
360     break;
361   case END_BARE:
362     sprintf(endstr, "%s bared",
363             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
364     break;
365   case END_PERPETUAL:
366     sprintf(endstr, "%s perpetually checking",
367             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
368     break;
369   case END_RESIGN:
370     sprintf(endstr, "%s resigned",
371             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
372     break;
373   case END_FLAG:
374     sprintf(endstr, "%s ran out of time",
375             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
376     break;
377   case END_AGREEDDRAW:
378     sprintf(endstr, "Game drawn by mutual agreement");
379     break;
380   case END_BOTHFLAG:
381     sprintf(endstr, "Game drawn because both players ran out of time");
382     break;
383   case END_REPETITION:
384     sprintf(endstr, "Game drawn by repetition");
385     break;
386   case END_50MOVERULE:
387     sprintf(endstr, "Draw by the 50 move rule");
388     break;
389   case END_ADJOURN:
390     sprintf(endstr, "Game adjourned by mutual agreement");
391     break;
392   case END_LOSTCONNECTION:
393     sprintf(endstr, "%s lost connection, game adjourned",
394             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
395     break;
396   case END_ABORT:
397     sprintf(endstr, "Game aborted by mutual agreement");
398     break;
399   case END_STALEMATE:
400     sprintf(endstr, "Stalemate.");
401     break;
402   case END_NOTENDED:
403     sprintf(endstr, "Still in progress");
404     break;
405   case END_COURTESY:
406     sprintf(endstr, "Game courtesyaborted by %s",
407             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
408     break;
409   case END_COURTESYADJOURN:
410     sprintf(endstr, "Game courtesyadjourned by %s",
411             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
412     break;
413   case END_NOMATERIAL:
414     sprintf(endstr, "Game drawn because neither player has mating material");
415     break;
416   case END_FLAGNOMATERIAL:
417     sprintf(endstr, "%s ran out of time and %s has no material to mate",
418             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy,
419             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
420     break;
421   case END_ADJDRAW:
422     sprintf(endstr, "Game drawn by adjudication");
423     break;
424   case END_ADJWIN:
425     sprintf(endstr, "%s wins by adjudication",
426             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
427     break;
428   case END_ADJABORT:
429     sprintf(endstr, "Game aborted by adjudication");
430     break;
431   default:
432     sprintf(endstr, "???????");
433     break;
434   }
435
436   return (endstr);
437 }
438
439 const char *EndSym(int g)
440 {
441         static const char *symbols[] = {"1-0", "0-1", "1/2-1/2", "*"};
442
443         switch (game_globals.garray[g].result) {
444         case END_CHECKMATE:
445         case END_RESIGN:
446         case END_FLAG:
447         case END_ADJWIN:
448         case END_BARE:
449         case END_PERPETUAL:
450                 return ((game_globals.garray[g].winner == WHITE) ? symbols[0] : symbols[1]);
451                 break;
452         case END_AGREEDDRAW:
453         case END_BOTHFLAG:
454         case END_REPETITION:
455         case END_50MOVERULE:
456         case END_STALEMATE:
457         case END_NOMATERIAL:
458         case END_FLAGNOMATERIAL:
459         case END_ADJDRAW:
460                 return (symbols[2]);
461                 break;
462         default:
463                 break;
464         }
465
466         return (symbols[3]);
467 }
468
469 /* This should be enough to hold any game up to at least 8000 moves
470  * If we overwrite this, the server will crash :-).
471  */
472 /* 8000? who you trying to kid? this is awful - enough for 600 halfs :) -DAV*/
473 #define GAME_STRING_LEN 19000
474 static char gameString[GAME_STRING_LEN];
475 char *movesToString(int g, int pgn)
476 {
477   char tmp[160];
478   int wr, br;
479   int i, col;
480   time_t curTime;
481   struct move_t *moves;
482
483   if (game_globals.garray[g].status == GAME_EXAMINE || game_globals.garray[g].status == GAME_SETUP)
484     moves = game_globals.garray[g].examMoveList;
485   else moves = game_globals.garray[g].moveList;
486
487   wr = game_globals.garray[g].white_rating;
488   br = game_globals.garray[g].black_rating;
489   
490
491   curTime = untenths(game_globals.garray[g].timeOfStart);
492
493   if (pgn) {
494     sprintf(gameString,
495             "\n[Event \"%s %s %s game\"]\n"
496             "[Site \"%s, %s\"]\n",
497             config_get_tmp("SERVER_NAME"),
498             rstr[game_globals.garray[g].rated], /*bstr[game_globals.garray[g].type],*/ 
499             game_globals.garray[g].variant, // [HGM] allow more variation in game_types
500             config_get_tmp("SERVER_NAME"),
501             config_get_tmp("SERVER_LOCATION"));
502     strftime(tmp, sizeof(tmp),
503              "[Date \"%Y.%m.%d\"]\n"
504              "[Time \"%H:%M:%S\"]\n",
505              localtime((time_t *) &curTime));
506     strcat(gameString, tmp);
507     sprintf(tmp,
508             "[Round \"-\"]\n"
509             "[White \"%s\"]\n"
510             "[Black \"%s\"]\n"
511             "[WhiteElo \"%d\"]\n"
512             "[BlackElo \"%d\"]\n",
513             game_globals.garray[g].white_name, game_globals.garray[g].black_name, wr, br);
514     strcat(gameString, tmp);
515     if(game_globals.garray[g].game_state.variant[0]) { // [HGM] variant: print variant tag
516         sprintf(tmp,
517             "[Variant \"%s\"]\n",
518             game_globals.garray[g].game_state.variant);
519         strcat(gameString, tmp);
520     }
521     if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the FEN
522         sprintf(tmp,
523             "[Setup \"1\"]\n"
524             "[FEN \"%s\"]\n",
525             game_globals.garray[g].FENstartPos);
526         strcat(gameString, tmp);
527     }
528     sprintf(tmp,
529             "[TimeControl \"%d+%d\"]\n"
530             "[Mode \"ICS\"]\n"
531             "[Result \"%s\"]\n\n",
532             game_globals.garray[g].wInitTime / 10, game_globals.garray[g].wIncrement / 10, EndSym(g));
533     strcat(gameString, tmp);
534
535     col = 0;
536     for (i = 0; i < game_globals.garray[g].numHalfMoves; i++) {
537       if (moves[i].color == WHITE) {
538         if ((col += sprintf(tmp, "%d. ", (i+1) / 2 + 1)) > 70) {
539           strcat(gameString, "\n");
540           col = 0;
541         }
542         strcat(gameString, tmp);
543       } else if (i==0) {
544         strcat (tmp, "1. ... ");
545         col += 7;
546       }
547       if ((col += sprintf(tmp, "%s ", moves[i].algString)) > 70) {
548         strcat(gameString, "\n");
549         col = 0;
550       }
551       strcat(gameString, tmp);
552       if(moves[i].depth > 0) { // [HGM] computer game, add {score/depth} comment
553         if ((col += sprintf(tmp, "{%s%.2f/%d} ", moves[i].score > 0 ? "+" : "",
554                                 moves[i].score, moves[i].depth)) > 70) {
555           strcat(gameString, "\n");
556           col = 0;
557         }
558         strcat(gameString, tmp);
559       }
560     }
561     strcat(gameString, "\n");
562
563   } else {
564
565     sprintf(gameString, "\n%s ", game_globals.garray[g].white_name);
566     if (wr > 0) {
567       sprintf(tmp, "(%d) ", wr);
568     } else {
569       sprintf(tmp, "(UNR) ");
570     }
571     strcat(gameString, tmp);
572     sprintf(tmp, "vs. %s ", game_globals.garray[g].black_name);
573     strcat(gameString, tmp);
574     if (br > 0) {
575       sprintf(tmp, "(%d) ", br);
576     } else {
577       sprintf(tmp, "(UNR) ");
578     }
579     strcat(gameString, tmp);
580     strcat(gameString, "--- ");
581     strcat(gameString, strltime(&curTime));
582     if (game_globals.garray[g].rated) {
583       strcat(gameString, "\nRated ");
584     } else {
585       strcat(gameString, "\nUnrated ");
586     }
587 //    strcat (gameString, TypeStrings[game_globals.garray[g].type]);
588     strcat (gameString, game_globals.garray[g].variant);
589     strcat(gameString, " match, initial time: ");
590     if ((game_globals.garray[g].bInitTime != game_globals.garray[g].wInitTime) || (game_globals.garray[g].wIncrement != game_globals.garray[g].bIncrement)) { /* different starting times */ 
591       sprintf(tmp, "%d minutes, increment: %d seconds AND %d minutes, increment: %d seconds.\n\n", game_globals.garray[g].wInitTime / 600, game_globals.garray[g].wIncrement / 10, game_globals.garray[g].bInitTime / 600, game_globals.garray[g].bIncrement / 10);
592     } else {
593       sprintf(tmp, "%d minutes, increment: %d seconds.\n\n", game_globals.garray[g].wInitTime / 600, game_globals.garray[g].wIncrement / 10);
594     }
595     strcat(gameString, tmp);
596
597     if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the initial position board
598         char *q; struct game_state_t initial_gs; struct move_t ml[600]; int r, f;
599
600         initial_gs.gameNum = g;
601         initial_gs.wkrmoved = 0; // [HGM] for some reason calling reset_board_vars() does not work here
602         initial_gs.wqrmoved = 0; //       so I just duplicated the code and pasted it here...
603         initial_gs.wkmoved = 0;
604         initial_gs.bkrmoved = 0;
605         initial_gs.bqrmoved = 0;
606         initial_gs.bkmoved = 0;
607         initial_gs.onMove = WHITE;
608         initial_gs.lastIrreversable = -1;
609         initial_gs.files = game_globals.garray[g].game_state.files;
610         initial_gs.ranks = game_globals.garray[g].game_state.ranks;
611         initial_gs.holdings = game_globals.garray[g].game_state.holdings;
612         strcpy(initial_gs.variant, game_globals.garray[g].game_state.variant);
613         for (f = 0; f < 2; f++) {
614           for (r = 0; r < initial_gs.files; r++)
615              initial_gs.ep_possible[f][r] = 0;
616           for (r = PAWN; r <= PIECES-1; r++)
617             initial_gs.holding[f][r-PAWN] = 0;
618         }
619         FEN_to_board(game_globals.garray[g].FENstartPos ,&initial_gs);
620
621         kludgeFlag = 1; // [HGM] setup: this is not thread safe. Must it be???
622         q = board_to_string(
623                       game_globals.garray[g].white_name,
624                       game_globals.garray[g].black_name,
625                       game_globals.garray[g].wTime,
626                       game_globals.garray[g].bTime,
627                       &initial_gs,
628                       ml,
629                       11,
630                       WHITE, 0, 0);
631         kludgeFlag = 0;
632
633         strcat(gameString, q);
634         strcat(gameString, "\n");
635     }
636     sprintf(tmp, "Move  %-19s%-19s\n", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
637     strcat(gameString, tmp);
638     strcat(gameString, "----  ----------------   ----------------\n");
639
640     for (i = 0; i < game_globals.garray[g].numHalfMoves; i += 2) {
641       if (i==0 && (moves[i].color == BLACK)) {
642         sprintf(tmp, "%3d.  %-16s   %-16s\n", (i+1)/2 + 1, "...",
643                      move_and_time(&moves[i]));
644         i--;
645       } else if (i + 1 < game_globals.garray[g].numHalfMoves) {
646         sprintf(tmp, "%3d.  %-16s   ", (i+1)/2 + 1, move_and_time(&moves[i]));
647         strcat(gameString, tmp);
648         sprintf(tmp, "%-16s\n", move_and_time(&moves[i+1]));
649       } else {
650         sprintf(tmp, "%3d.  %-16s\n", (i+1)/2 + 1, move_and_time(&moves[i]));
651       }
652       strcat(gameString, tmp);
653       if (strlen(gameString) > GAME_STRING_LEN - 100) { /* Bug out if getting
654                                                            close to filling this
655                                                            string */
656         return gameString;
657       }
658     }
659
660     strcat(gameString, "      ");
661   }
662
663   sprintf(tmp, "{%s} %s\n", EndString(g, 0), EndSym(g));
664   strcat(gameString, tmp);
665
666   return gameString;
667 }
668
669 void game_disconnect(int g, int p)
670 {
671   game_ended(g, (game_globals.garray[g].white == p) ? WHITE : BLACK, END_LOSTCONNECTION);
672 }
673
674 int CharToPiece(char c, char *variant)
675 {
676
677   if(variant) {
678     if(!strcmp(variant, "shogi")) {
679       switch(c) {
680         case 'N':
681           return W_HONORABLEHORSE;
682         case 'n':
683           return B_HONORABLEHORSE;
684         case 'L':
685           return W_LANCE;
686         case 'l':
687           return B_LANCE;
688         case 'S':
689           return W_SILVER;
690         case 's':
691           return B_SILVER;
692         case 'G':
693           return W_GOLD;
694         case 'g':
695           return B_GOLD;
696       }
697     } else if(!strcmp(variant, "xiangqi")) {
698       switch(c) {
699         case 'A':
700           return W_MANDARIN;
701         case 'a':
702           return B_MANDARIN;
703         case 'H':
704           return W_HORSE;
705         case 'h':
706           return B_HORSE;
707         case 'C':
708           return W_CANNON;
709         case 'c':
710           return B_CANNON;
711       }
712     } else if(!strcmp(variant, "super")) {
713       switch(c) {
714         case 'E':
715           return W_EMPRESS;
716         case 'e':
717           return B_EMPRESS;
718         case 'S':
719           return W_PRINCESS;
720         case 's':
721           return B_PRINCESS;
722         case 'Z':
723           return W_AMAZON;
724         case 'z':
725           return B_AMAZON;
726         case 'V':
727           return W_CENTAUR;
728         case 'v':
729           return B_CENTAUR;
730       }
731     } else if(!strcmp(variant, "spartan")) {
732       switch(c) {
733         case 'w':
734           return B_WARLORD;
735         case 'g':
736           return B_GENERAL;
737         case 'l':
738           return B_LIEUTENANT;
739         case 'c':
740           return B_CAPTAIN;
741         case 'h':
742           return B_HOPLITE;
743       }
744     }
745   }
746   switch (c) {
747   case 'P':
748     return W_PAWN;
749   case 'p':
750     return B_PAWN;
751   case 'N':
752     return W_KNIGHT;
753   case 'n':
754     return B_KNIGHT;
755   case 'B':
756     return W_BISHOP;
757   case 'b':
758     return B_BISHOP;
759   case 'R':
760     return W_ROOK;
761   case 'r':
762     return B_ROOK;
763   case 'A':
764     return W_CARDINAL;
765   case 'a':
766     return B_CARDINAL;
767   case 'C':
768     return W_MARSHALL;
769   case 'c':
770     return B_MARSHALL;
771   case 'M':
772     return W_MAN;
773   case 'm':
774     return B_MAN;
775   case 'Q':
776     return W_QUEEN;
777   case 'q':
778     return B_QUEEN;
779   case 'E':
780     if(!strcmp(variant, "seirawan")) return W_ELEPHANT;
781     return W_ELEPHANT;
782   case 'e':
783     if(!strcmp(variant, "seirawan")) return B_ELEPHANT;
784     return B_ELEPHANT;
785   case 'H':
786     return W_HAWK;
787   case 'h':
788     return B_HAWK;
789   case 'K':
790     return W_KING;
791   case 'k':
792     return B_KING;
793   default:
794     return NOPIECE;
795   }
796 }
797
798 char PieceToChar(int piece)
799 {
800   switch (piece) {
801     case W_PAWN:return 'P';
802   case B_PAWN:
803     return 'p';
804   case W_KNIGHT:
805   case W_HONORABLEHORSE:
806     return 'N';
807   case B_KNIGHT:
808   case B_HONORABLEHORSE:
809     return 'n';
810   case W_BISHOP:
811     return 'B';
812   case B_BISHOP:
813     return 'b';
814   case W_ROOK:
815     return 'R';
816   case B_ROOK:
817     return 'r';
818   case W_CARDINAL:
819   case W_MANDARIN:
820   case W_ALFIL2:
821     return 'A';
822   case B_CARDINAL:
823   case B_MANDARIN:
824   case B_ALFIL2:
825     return 'a';
826   case W_CANNON:
827   case W_MARSHALL:
828     return 'C';
829   case B_CAPTAIN:
830   case B_CANNON:
831   case B_MARSHALL:
832     return 'c';
833   case W_MAN:
834   case W_MINISTER:
835     return 'M';
836   case B_MAN:
837   case B_MINISTER:
838     return 'm';
839   case W_QUEEN:
840     return 'Q';
841   case B_QUEEN:
842     return 'q';
843   case W_SELEPHANT:
844   case W_ELEPHANT:
845   case W_EMPRESS:
846     return 'E';
847   case B_SELEPHANT:
848   case B_ELEPHANT:
849   case B_EMPRESS:
850     return 'e';
851   case W_ALFIL:
852     return 'B';
853   case B_ALFIL:
854     return 'b';
855   case W_FERZ:
856     return 'Q';
857   case B_FERZ:
858     return 'q';
859   case W_FERZ2:
860     return 'F';
861   case B_FERZ2:
862     return 'f';
863   case W_WAZIR:
864   case W_WOODY:
865     return 'W';
866   case B_WARLORD:
867   case B_WAZIR:
868   case B_WOODY:
869     return 'w';
870   case W_HAWK:
871   case W_HORSE:
872   case W_PRIESTESS:
873   case W_NIGHTRIDER:
874     return 'H';
875   case B_HAWK:
876   case B_HORSE:
877   case B_HOPLITE:
878   case B_PRIESTESS:
879   case B_NIGHTRIDER:
880     return 'h';
881   case W_SILVER:
882   case W_PRINCESS:
883   case W_MAN2:
884     return 'S';
885   case B_SILVER:
886   case B_PRINCESS:
887   case B_MAN2:
888     return 's';
889   case W_GOLD:
890   case W_MASTODON:
891     return 'G';
892   case B_GOLD:
893   case B_GENERAL:
894   case B_MASTODON:
895     return 'g';
896   case W_AMAZON:
897     return 'Z';
898   case B_AMAZON:
899     return 'z';
900   case W_CENTAUR:
901     return 'V';
902   case B_CENTAUR:
903     return 'v';
904   case W_KING:
905     return 'K';
906   case B_KING:
907     return 'k';
908   case W_LANCE:
909     return 'L';
910   case B_LIEUTENANT:
911   case B_LANCE:
912     return 'l';
913   default:
914     return ' ';
915   }
916 }
917
918 /*
919   write a new style generic game file
920 */
921 static void WriteGameFile_v100(FILE * fp, int g)
922 {
923         struct game gg = game_globals.garray[g];
924         const char *s;
925
926         /* zero any elements we don't want to save */
927         memset(&gg, 0, offsetof(struct game, not_saved_marker));
928         gg.game_state.gameNum = 0;
929
930         /* marshall it into a string */
931         s = marshall_game(&gg);
932         if (!s) {
933                 d_printf("Unable to marshall game structure!\n");
934                 return;
935         }
936
937         /* and save it */
938         fprintf(fp, "v 100\n%s\n", s);
939         free(s);
940 }
941
942
943 /*
944   read a game file using the new generic and extensible format 
945 */
946 static int ReadGameAttrs_v100(FILE *fp, int g)
947 {
948         char *s, *s2;       
949         struct game *gg = &game_globals.garray[g];
950         struct game g1;
951
952         s = fd_load(fileno(fp), NULL);
953         if (!s) {
954                 d_printf("Error reading game file!\n");
955                 return -1;
956         }
957
958         /* skip first line */
959         s2 = strchr(s, '\n');
960
961         /* remember the game state for later */
962         g1 = *gg;
963
964         /* the marshaller doesn't save zero elements, but some elements don't
965            default to zero. Make sure they get the right value */
966         memset(&gg->not_saved_marker, 0, 
967                sizeof(struct game) - offsetof(struct game, not_saved_marker));
968         gg->game_state.gameNum = g;
969
970         if (!s2 || unmarshall_game(gg, s2) != 0) {
971                 d_printf("Error unmarshalling game data!\n");
972                 free(s);
973                 return -1;
974         }
975         free(s);
976
977         /* when examining we are not supposed to restore the game
978            state, so put it back here */
979         if (g1.status == GAME_EXAMINE || g1.status == GAME_SETUP) { 
980                 gg->game_state = g1.game_state;
981         }
982         gg->status = g1.status;
983
984         /* cope with continuing a game with timeseal that was started without it */
985         gg->wRealTime = gg->wTime * 100;
986         gg->bRealTime = gg->bTime * 100;
987
988         return 0;
989 }
990
991
992 static int ReadGameAttrs_common(FILE * fp, int g,int version)
993 {
994         if (version == 9 || version == 100) {
995                 return ReadGameAttrs_v100(fp, g);
996         }
997
998         return ReadGameAttrs_old(fp, g, version);
999 }
1000
1001 int ReadGameAttrs_exam(FILE * fp, int g)
1002 {
1003   int version = 0;
1004   char line[MAX_GLINE_SIZE];
1005
1006   fgets(line, MAX_GLINE_SIZE - 1, fp);
1007
1008    if (line[0] == 'v') {
1009     sscanf(line, "%*c %d", &version);
1010
1011     if (version < 5)
1012       return -1;
1013     else
1014       return ReadGameAttrs_common(fp,g,version);
1015   }
1016
1017    else return -1;
1018 }
1019
1020 int ReadGameAttrs(FILE * fp, int g)
1021 {
1022         int version = 0;
1023         char line[MAX_GLINE_SIZE];
1024
1025         fgets(line, MAX_GLINE_SIZE - 1, fp);
1026
1027         if (line[0] == 'v') {
1028                 sscanf(line, "%*c %d", &version);
1029         }
1030         return ReadGameAttrs_common(fp,g,version);
1031 }
1032
1033 int game_read(int g, int wp, int bp)
1034 {
1035   FILE *fp;
1036   int piece;
1037
1038   game_globals.garray[g].white = wp;
1039   game_globals.garray[g].black = bp;
1040 /*  game_globals.garray[g].old_white = -1;
1041     game_globals.garray[g].old_black = -1;
1042 */
1043   game_globals.garray[g].moveListSize = 0;
1044   game_globals.garray[g].game_state.gameNum = g;
1045   strcpy(game_globals.garray[g].white_name, player_globals.parray[wp].name);
1046   strcpy(game_globals.garray[g].black_name, player_globals.parray[bp].name);
1047   if (game_globals.garray[g].type == TYPE_BLITZ) {
1048     game_globals.garray[g].white_rating = player_globals.parray[wp].b_stats.rating;
1049     game_globals.garray[g].black_rating = player_globals.parray[bp].b_stats.rating;
1050   } else if (game_globals.garray[g].type == TYPE_WILD ||
1051              game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1052              game_globals.garray[g].type == TYPE_CAPABLANCA ||
1053              game_globals.garray[g].type == TYPE_GOTHIC) {
1054     game_globals.garray[g].white_rating = player_globals.parray[wp].w_stats.rating;
1055     game_globals.garray[g].black_rating = player_globals.parray[bp].w_stats.rating;
1056   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1057     game_globals.garray[g].white_rating = player_globals.parray[wp].l_stats.rating;
1058     game_globals.garray[g].black_rating = player_globals.parray[bp].l_stats.rating;
1059   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1060     game_globals.garray[g].white_rating = player_globals.parray[wp].bug_stats.rating;
1061     game_globals.garray[g].black_rating = player_globals.parray[bp].bug_stats.rating;
1062   } else {
1063     game_globals.garray[g].white_rating = player_globals.parray[wp].s_stats.rating;
1064     game_globals.garray[g].black_rating = player_globals.parray[bp].s_stats.rating;
1065   }
1066   fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1067              player_globals.parray[wp].login, player_globals.parray[bp].login);
1068   if (!fp) {
1069     return -1;
1070   }
1071   for (piece=PAWN; piece < KING; piece++) {
1072     game_globals.garray[g].game_state.holding[0][piece-PAWN]
1073       = game_globals.garray[g].game_state.holding[1][piece-PAWN] = 0;
1074   }
1075   if (ReadGameAttrs(fp, g) < 0) {
1076     fclose(fp);
1077     return -1;
1078   }
1079   fclose(fp);
1080
1081   if (game_globals.garray[g].result == END_ADJOURN
1082       || game_globals.garray[g].result == END_COURTESYADJOURN)
1083     game_globals.garray[g].result = END_NOTENDED;
1084   game_globals.garray[g].status = GAME_ACTIVE;
1085   game_globals.garray[g].startTime = tenth_secs();
1086   game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1087   game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1088
1089   /* cope with continuing a game with timeseal that was started without it */
1090   game_globals.garray[g].wRealTime = game_globals.garray[g].wTime * 100;
1091   game_globals.garray[g].bRealTime = game_globals.garray[g].bTime * 100;
1092
1093   /* Need to do notification and pending cleanup */
1094   return 0;
1095 }
1096
1097 int game_delete(int wp, int bp)
1098 {
1099   char fname[MAX_FILENAME_SIZE];
1100   char lname[MAX_FILENAME_SIZE];
1101
1102   sprintf(fname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1103           player_globals.parray[wp].login, player_globals.parray[bp].login);
1104   sprintf(lname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[bp].login[0],
1105           player_globals.parray[wp].login, player_globals.parray[bp].login);
1106   unlink(fname);
1107   unlink(lname);
1108   return 0;
1109 }
1110
1111 int game_save(int g)
1112 {
1113         FILE *fp;
1114         struct player *wp, *bp;
1115         struct game *gg = &game_globals.garray[g];
1116         char *fname;
1117
1118         wp = &player_globals.parray[gg->white];
1119         bp = &player_globals.parray[gg->black];
1120         asprintf(&fname, "%s/%c/%s-%s", ADJOURNED_DIR, wp->login[0],
1121                  wp->login, bp->login);
1122         fp = fopen_p("%s", "w", fname);
1123         if (!fp) {
1124                 d_printf("CHESSD: Problem opening adjourn file %s-%s for write\n", 
1125                           wp->login, bp->login);
1126                 free(fname);
1127                 return -1;
1128         }
1129         WriteGameFile_v100(fp, g);
1130         fclose(fp);
1131         /* Create link for easier stored game finding */
1132         if (bp->login[0] != wp->login[0]) {
1133                 char *lname;
1134                 asprintf(&lname, "%s/%c/%s-%s", ADJOURNED_DIR, bp->login[0],
1135                          wp->login, bp->login);
1136                 link(fname, lname);
1137                 free(lname);
1138         }
1139         free(fname);
1140         return 0;
1141 }
1142
1143 static long OldestHistGame(char *login)
1144 {
1145         FILE *fp;
1146         long when;
1147         
1148         fp = fopen_p("%s/player_data/%c/%s.%s", "r", STATS_DIR,
1149                      login[0], login, STATS_GAMES);
1150         if (fp == NULL) {
1151                 fp = fopen_p("%s/player_data/%c/.rem.%s.%s", "r", STATS_DIR,
1152                              login[0], login, STATS_GAMES);
1153         }
1154         if (!fp) return 0;
1155
1156         fscanf(fp, "%*d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %*s %ld",
1157                &when);
1158         fclose(fp);
1159         return when;
1160 }
1161
1162 static void RemoveHistGame(char *file, int maxlines)
1163 {
1164   FILE *fp;
1165   char GameFile[MAX_FILENAME_SIZE];
1166   char Opponent[MAX_LOGIN_NAME];
1167   char line[MAX_LINE_SIZE];
1168   long When, oppWhen;
1169   int count = 0;
1170
1171   fp = fopen_p("%s", "r", file);
1172   if (fp == NULL)
1173     return;
1174
1175   fgets(line, MAX_LINE_SIZE - 1, fp);
1176   sscanf(line, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1177          Opponent, &When);
1178   count++;
1179
1180   while (!feof(fp)) {
1181     fgets(line, MAX_LINE_SIZE - 1, fp);
1182     if (!feof(fp))
1183       count++;
1184   }
1185   fclose(fp);
1186
1187   stolower(Opponent);
1188   if (count > maxlines) {
1189     truncate_file(file, maxlines);
1190
1191     oppWhen = OldestHistGame(Opponent);
1192     if (oppWhen > When || oppWhen <= 0L) {
1193       sprintf(GameFile, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1194       unlink(GameFile);
1195     }
1196   }
1197 }
1198
1199 void RemHist(char *who)
1200 {
1201         FILE *fp;
1202         char Opp[MAX_LOGIN_NAME];
1203         long When, oppWhen;
1204
1205         fp = fopen_p("%s/player_data/%c/%s.%s", "r", 
1206                      STATS_DIR,
1207                      who[0], who, STATS_GAMES);
1208         if (!fp) {
1209                 return;
1210         }
1211         while (!feof(fp)) {
1212                 fscanf(fp, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1213                        Opp, &When);
1214                 stolower(Opp);
1215                 oppWhen = OldestHistGame(Opp);
1216                 if (oppWhen > When || oppWhen <= 0L) {
1217                         char *fName;
1218                         asprintf(&fName, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1219                         unlink(fName);
1220                         free(fName);
1221                 }
1222         }
1223         fclose(fp);
1224 }
1225
1226 static void write_g_out(int g, char *file, int maxlines, int isDraw,
1227                           char *EndSymbol, char *name, time_t *now)
1228 {
1229   FILE *fp;
1230   int wp, bp;
1231   int wr, br;
1232   char type[4];
1233   char tmp[2048];
1234   char *ptmp = tmp;
1235   char cResult;
1236   int count = -1;
1237   char *goteco;
1238
1239   wp = game_globals.garray[g].white;
1240   bp = game_globals.garray[g].black;
1241
1242   if (game_globals.garray[g].private) {
1243     type[0] = 'p';
1244   } else {
1245     type[0] = ' ';
1246   }
1247   if (game_globals.garray[g].type == TYPE_BLITZ) {
1248     wr = player_globals.parray[wp].b_stats.rating;
1249     br = player_globals.parray[bp].b_stats.rating;
1250     type[1] = 'b';
1251   } else if (game_globals.garray[g].type == TYPE_WILD ||
1252              game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1253              game_globals.garray[g].type == TYPE_CAPABLANCA ||
1254              game_globals.garray[g].type == TYPE_GOTHIC) {
1255     wr = player_globals.parray[wp].w_stats.rating;
1256     br = player_globals.parray[bp].w_stats.rating;
1257     type[1] = 'w';
1258   } else if (game_globals.garray[g].type == TYPE_STAND) {
1259     wr = player_globals.parray[wp].s_stats.rating;
1260     br = player_globals.parray[bp].s_stats.rating;
1261     type[1] = 's';
1262   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1263     wr = player_globals.parray[wp].l_stats.rating;
1264     br = player_globals.parray[bp].l_stats.rating;
1265     type[1] = 'l';
1266   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1267     wr = player_globals.parray[wp].bug_stats.rating;
1268     br = player_globals.parray[bp].bug_stats.rating;
1269     type[1] = 'B';
1270   } else {
1271     wr = 0;
1272     br = 0;
1273     if (game_globals.garray[g].type == TYPE_NONSTANDARD)
1274       type[1] = 'n';
1275     else
1276       type[1] = 'u';
1277   }
1278   if (game_globals.garray[g].rated) {
1279     type[2] = 'r';
1280   } else {
1281     type[2] = 'u';
1282   }
1283   type[3] = '\0';
1284
1285   fp = fopen_s(file, "r");
1286   if (fp) {
1287     while (!feof(fp))
1288       fgets(tmp, 1024, fp);
1289     sscanf(ptmp, "%d", &count);
1290     fclose(fp);
1291   }
1292   count = (count + 1) % 100;
1293
1294   fp = fopen_s(file, "a");
1295   if (!fp)
1296     return;
1297
1298   goteco = getECO(g);
1299
1300 /* Counter Result MyRating MyColor OppRating OppName [pbr 2 12 2 12] ECO End Date */
1301   if (name == player_globals.parray[wp].name) {
1302     if (isDraw)
1303       cResult = '=';
1304     else if (game_globals.garray[g].winner == WHITE)
1305       cResult = '+';
1306     else
1307       cResult = '-';
1308
1309     fprintf(fp, "%d %c %d W %d %s %s %d %d %d %d %s %s %ld\n",
1310             count, cResult, wr, br, player_globals.parray[bp].name, type,
1311             game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1312             game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1313             goteco,
1314             EndSymbol,
1315             (long) *now);
1316   } else {
1317     if (isDraw)
1318       cResult = '=';
1319     else if (game_globals.garray[g].winner == BLACK)
1320       cResult = '+';
1321     else
1322       cResult = '-';
1323
1324     fprintf(fp, "%d %c %d B %d %s %s %d %d %d %d %s %s %ld\n",
1325             count, cResult, br, wr, player_globals.parray[wp].name, type,
1326             game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1327             game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1328             goteco,
1329             EndSymbol,
1330             (long) *now);
1331   }
1332   fclose(fp);
1333
1334   RemoveHistGame(file, maxlines);
1335 }
1336
1337 /* Test if entry is present - 1 yes 0 no */
1338 /* returns -1 if an error */
1339
1340 char get_journalgame_type(int p,char* fname,char slot)
1341
1342 {
1343   struct player *pp = &player_globals.parray[p];
1344   char cur_slot;
1345   char type[4];
1346
1347   FILE* fp = fopen_s(fname,"r");
1348   if (!fp) {
1349     d_printf("Corrupt journal file! %s\n",fname);
1350     pprintf (p, "The journal file is corrupt! See an admin.\n");
1351     return 0;
1352   }
1353
1354   while (!feof(fp)) {
1355     if (fscanf(fp, "%c %*s %*d %*s %*d %s %*d %*d %*s %*s %*s\n",
1356        &cur_slot,type) != 2) {
1357       d_printf( "CHESSD: Error in journal info format for player %s.\n",
1358         pp->name);
1359       pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1360       return '\0';
1361     }
1362     if (cur_slot == slot) {
1363       fclose (fp);
1364       if (type[0] == 'p')
1365         return type[1];
1366       else
1367         return type[0];
1368     }
1369   }
1370   fclose (fp);
1371   return '0';
1372 }
1373
1374
1375 /* Returns 1 if successful */
1376
1377 int removejournalitem(int p, char slot,FILE* fp,char* fname, int* empty)
1378
1379 {
1380         FILE* fp_new;
1381         int found = 0;
1382         struct journal* j;
1383
1384         *empty = 1;
1385         fp_new = fopen_s(fname, "w");
1386         if (!fp_new) {
1387                 d_printf("Can't write to journal %s!\n",fname);
1388                 pprintf (p, "Was unable to write to the file! See an admin.\n");
1389                 return 0;
1390         }
1391         j = (struct journal*) malloc(sizeof(struct journal));
1392         while (!feof(fp)) {
1393                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1394                            &j->slot,
1395                            j->WhiteName,
1396                            &j->WhiteRating,
1397                            j->BlackName,
1398                            &j->BlackRating,
1399                            j->type,
1400                            &j->t, &j->i,
1401                            j->eco,
1402                            j->ending,
1403                            j->result) != 11) {
1404                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1405                         pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1406                         fclose(fp_new);
1407                         free (j);
1408                         return 0;
1409                 }
1410                 if (slot != j->slot) {
1411                         *empty = 0;
1412                         fprintf(fp_new, "%c %s %d %s %d %s %d %d %s %s %s\n",
1413                                 j->slot,
1414                                 j->WhiteName,
1415                                 j->WhiteRating,
1416                                 j->BlackName,
1417                                 j->BlackRating,
1418                                 j->type,
1419                                 j->t, j->i,
1420                                 j->eco,
1421                                 j->ending,
1422                                 j->result);
1423                 } else
1424                         found = 1;
1425         }
1426         fclose(fp_new);
1427         free (j);
1428         return found;
1429 }
1430
1431 /* Find from_spot in journal list - return 0 if corrupted */
1432 int journal_get_info(int p, char from_spot,struct journal* j, char *fname)
1433 {
1434         FILE *fp;
1435
1436         fp = fopen_s(fname, "r");
1437         if (!fp) {
1438                 d_printf("Corrupt journal file! %s\n",fname);
1439                 pprintf (p, "The journal file is corrupt! See an admin.\n");
1440                 return 0;
1441         }
1442         while (!feof(fp)) {
1443                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1444                            &j->slot,
1445                            j->WhiteName,
1446                            &j->WhiteRating,
1447                            j->BlackName,
1448                            &j->BlackRating,
1449                            j->type,
1450                            &j->t, &j->i,
1451                            j->eco,
1452                            j->ending,
1453                            j->result) != 11) {
1454                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1455                         pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1456                         fclose(fp);
1457                         return 0;
1458                 }
1459                 if (tolower(j->slot) == from_spot) {
1460                         fclose(fp);
1461                         return 1;
1462                 }
1463         }
1464         fclose(fp);
1465         return 0;
1466 }
1467
1468 void addjournalitem(int p,struct journal* j_add, char* fname)
1469 {
1470         struct journal* j_cur;
1471         int have_output=0;
1472         char fname2[MAX_FILENAME_SIZE];
1473         FILE *fp;
1474         FILE *fp2;
1475
1476         strcpy (fname2,fname);
1477         strcat (fname2,".w");
1478         fp2 = fopen_s(fname2, "w");
1479         if (!fp2) {
1480                 d_printf( "CHESSD: Problem opening file %s for write\n", fname);
1481                 pprintf (p, "Couldn't update journal! Report this to an admin.\n");
1482                 return;
1483         } 
1484         fp = fopen_s(fname, "r");
1485         if (!fp) { /* Empty? */
1486                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1487                         j_add->slot, j_add->WhiteName, j_add->WhiteRating,
1488                         j_add->BlackName, j_add->BlackRating,
1489                         j_add->type, j_add->t, j_add->i, j_add->eco, j_add->ending,
1490                         j_add->result);
1491                 fclose (fp2);
1492                 rename (fname2, fname);
1493                 return;
1494         } else {
1495                 j_cur = (struct journal*) malloc(sizeof(struct journal));
1496                 while (!feof(fp)) {
1497                         if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1498                                    &j_cur->slot,
1499                                    j_cur->WhiteName,
1500                                    &j_cur->WhiteRating,
1501                                    j_cur->BlackName,
1502                                    &j_cur->BlackRating,
1503                                    j_cur->type,
1504                                    &j_cur->t, &j_cur->i,
1505                                    j_cur->eco,
1506                                    j_cur->ending,
1507                                    j_cur->result) != 11) {
1508                                 d_printf( "CHESSD: Error in journal info format - aborting. %s\n", fname);
1509                                 free (j_cur);
1510                                 fclose(fp);
1511                                 fclose(fp2);
1512                                 return;
1513                         }
1514                         if ((j_cur->slot >= j_add->slot) && (!have_output)) {
1515                                 
1516                                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1517                                         j_add->slot,
1518                                         j_add->WhiteName,
1519                                         j_add->WhiteRating,
1520                                         j_add->BlackName,
1521                                         j_add->BlackRating,
1522                                         j_add->type,
1523                                         j_add->t, j_add->i,
1524                                         j_add->eco,
1525                                         j_add->ending,
1526                                         j_add->result);
1527                                 have_output = 1;
1528
1529                         } 
1530                         if (j_cur->slot != j_add->slot) {
1531
1532                                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1533                                         j_cur->slot,
1534                                         j_cur->WhiteName,
1535                                         j_cur->WhiteRating,
1536                                         j_cur->BlackName,
1537                                         j_cur->BlackRating,
1538                                         j_cur->type,
1539                                         j_cur->t, j_cur->i,
1540                                         j_cur->eco,
1541                                         j_cur->ending,
1542                                         j_cur->result);
1543                         }
1544                 }
1545                 
1546                 if (!have_output) { /* Haven't written yet */
1547                         fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1548                                 j_add->slot,
1549                                 j_add->WhiteName,
1550                                 j_add->WhiteRating,
1551                                 j_add->BlackName,
1552                                 j_add->BlackRating,
1553                                 j_add->type,
1554                                 j_add->t, j_add->i,
1555                                 j_add->eco,
1556                                 j_add->ending,
1557                                 j_add->result);
1558                 }
1559                 free (j_cur);
1560                 fclose(fp);
1561                 fclose(fp2);
1562                 rename(fname2, fname);
1563                 return;
1564         }
1565
1566
1567 int pjournal(int p, int p1, char *fname)
1568 {
1569         FILE *fp;
1570         struct journal* j;
1571         
1572         fp = fopen_s(fname, "r");
1573         if (!fp) {
1574                 pprintf(p, "Sorry, no journal information available.\n");
1575                 return COM_OK;
1576         }
1577
1578         j = (struct journal*) malloc(sizeof(struct journal));
1579         pprintf(p, "Journal for %s:\n", player_globals.parray[p1].name);
1580         pprintf(p, "   White         Rating  Black         Rating  Type         ECO End Result\n");
1581         while (!feof(fp)) {
1582                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1583                            &j->slot,
1584                            j->WhiteName,
1585                            &j->WhiteRating,
1586                            j->BlackName,
1587                            &j->BlackRating,
1588                            j->type,
1589                            &j->t, &j->i,
1590                            j->eco,
1591                            j->ending,
1592                            j->result) != 11) {
1593                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1594                         fclose(fp);
1595                         free(j);
1596                         return COM_OK;
1597                 }
1598                 j->WhiteName[13] = '\0';         /* only first 13 chars in name */
1599                 j->BlackName[13] = '\0';
1600                 pprintf(p, "%c: %-13s %4d    %-13s %4d    [%3s%3d%4d] %s %3s %-7s\n",
1601                         j->slot, j->WhiteName, j->WhiteRating,
1602                         j->BlackName, j->BlackRating,
1603                         j->type, j->t / 600, j->i / 10, j->eco, j->ending,
1604                         j->result);
1605         }
1606         free (j);
1607         fclose(fp);
1608         return COM_OK;
1609 }
1610
1611 int pgames(int p, int p1, char *fname)
1612 {
1613         FILE *fp;
1614         time_t t;
1615         int MyRating, OppRating;
1616         int wt, wi, bt, bi;
1617         char OppName[MAX_LOGIN_NAME + 1];
1618         char type[100];
1619         char eco[100];
1620         char ending[100];
1621         char MyColor[2];
1622         int count;
1623         char result[2];
1624         
1625         fp = fopen_s(fname, "r");
1626         if (!fp) {
1627                 pprintf(p, "Sorry, no game information available.\n");
1628                 return COM_OK;
1629         }
1630         pprintf(p, "History for %s:\n", player_globals.parray[p1].name);
1631         pprintf(p, "                  Opponent      Type         ECO End Date\n");
1632         while (!feof(fp)) {
1633                 if (fscanf(fp, "%d %s %d %s %d %s %s %d %d %d %d %s %s %ld\n",
1634                            &count,
1635                            result,
1636                            &MyRating,
1637                            MyColor,
1638                            &OppRating,
1639                            OppName,
1640                            type,
1641                            &wt, &wi,
1642                            &bt, &bi,
1643                            eco,
1644                            ending,
1645                            (long *) &t) != 14) {
1646                         d_printf( "CHESSD: Error in games info format. %s\n", fname);
1647                         fclose(fp);
1648                         return COM_OK;
1649                 }
1650                 OppName[13] = '\0';             /* only first 13 chars in name */
1651                 pprintf(p, "%2d: %s %4d %s %4d %-13s [%3s%3d%4d] %s %3s %s",
1652                         count, result, MyRating, MyColor,
1653                         OppRating, OppName,
1654                         type, wt / 600, wi / 10, eco, ending,
1655                         ctime(&t));
1656         }
1657         fclose(fp);
1658         return COM_OK;
1659 }
1660
1661 void game_write_complete(int g, int isDraw, char *EndSymbol)
1662 {
1663         char fname[MAX_FILENAME_SIZE];
1664         int wp = game_globals.garray[g].white, bp = game_globals.garray[g].black;
1665         time_t now = time(NULL);
1666         int Result;
1667         FILE *fp = NULL;
1668         
1669         do {
1670                 if (fp) {
1671                         fclose(fp);
1672                         now++;
1673                 }
1674                 sprintf(fname, "%s/%ld/%ld", HISTORY_DIR, (long) now % 100, (long) now);
1675                 fp = fopen_s(fname, "r");
1676         } while (fp);   /* terminates when the file doesn't exist */
1677         
1678         fp = fopen_s(fname, "w");
1679         
1680         if (fp) {
1681                 WriteGameFile_v100(fp, g);
1682                 fclose(fp);
1683         } else {
1684                 d_printf( "Trouble writing history file %s", fname);
1685         }
1686         
1687         sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1688                 player_globals.parray[wp].login[0], player_globals.parray[wp].login, STATS_GAMES);
1689         write_g_out(g, fname, 40, isDraw, EndSymbol, player_globals.parray[wp].name, &now);
1690         sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1691                 player_globals.parray[bp].login[0], player_globals.parray[bp].login, STATS_GAMES);
1692         write_g_out(g, fname, 40, isDraw, EndSymbol, player_globals.parray[bp].name, &now);
1693         
1694         if (isDraw)
1695                 Result = -1;
1696         else if (game_globals.garray[g].winner == WHITE)
1697                 Result = 1;
1698         else
1699                 Result = 0;
1700         sprintf(fname,"%s/player_data/%c/%s.gstats", STATS_DIR, player_globals.parray[game_globals.garray[g].white].login[0], player_globals.parray[game_globals.garray[g].white].login);
1701         if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1702                 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].black].name,Result,game_globals.garray[g].rated);
1703         if (isDraw)
1704                 Result = -1;
1705         else if (game_globals.garray[g].winner == BLACK)
1706                 Result = 1;
1707         else
1708                 Result = 0;
1709         sprintf(fname,"%s/player_data/%c/%s.gstats", STATS_DIR, player_globals.parray[game_globals.garray[g].black].login[0], player_globals.parray[game_globals.garray[g].black].login);
1710         if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1711                 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].white].name,Result,game_globals.garray[g].rated);
1712 }
1713
1714 int game_count(void)
1715 {
1716         int g, count = 0;
1717         
1718         for (g = 0; g < game_globals.g_num; g++) {
1719                 if ((game_globals.garray[g].status == GAME_ACTIVE) || (game_globals.garray[g].status == GAME_EXAMINE)
1720                     || (game_globals.garray[g].status == GAME_SETUP))
1721                         count++;
1722         }
1723         if (count > command_globals.game_high)
1724                 command_globals.game_high = count;
1725         return count;
1726 }
1727
1728 static int check_kings(struct game_state_t *gs)
1729 {
1730         /* Function written 3/28/96 by Figbert to check for 1 king each side only! */
1731         int blackking = 0, whiteking = 0;
1732         int f, r;
1733         
1734         
1735         for (f = 0; f < gs->files; f++) {
1736                 for (r = 0; r < gs->ranks; r++) {
1737                         if (gs->board[f][r] == B_KING) blackking++;
1738                         if (gs->board[f][r] == W_KING) whiteking++;
1739                 }
1740         }
1741         
1742         if ((blackking == 1 || blackking == 2 && !strcmp(gs->variant, "spartan")) && whiteking == 1) return 0; /* Perfect! */
1743         
1744         return -1;
1745 }