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