0d1223fd050367d15567bbd6120c8c1362b884ae
[capablanca.git] / 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_RESIGN:
366     sprintf(endstr, "%s resigned",
367             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
368     break;
369   case END_FLAG:
370     sprintf(endstr, "%s ran out of time",
371             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy);
372     break;
373   case END_AGREEDDRAW:
374     sprintf(endstr, "Game drawn by mutual agreement");
375     break;
376   case END_BOTHFLAG:
377     sprintf(endstr, "Game drawn because both players ran out of time");
378     break;
379   case END_REPETITION:
380     sprintf(endstr, "Game drawn by repetition");
381     break;
382   case END_50MOVERULE:
383     sprintf(endstr, "Draw by the 50 move rule");
384     break;
385   case END_ADJOURN:
386     sprintf(endstr, "Game adjourned by mutual agreement");
387     break;
388   case END_LOSTCONNECTION:
389     sprintf(endstr, "%s lost connection, game adjourned",
390             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
391     break;
392   case END_ABORT:
393     sprintf(endstr, "Game aborted by mutual agreement");
394     break;
395   case END_STALEMATE:
396     sprintf(endstr, "Stalemate.");
397     break;
398   case END_NOTENDED:
399     sprintf(endstr, "Still in progress");
400     break;
401   case END_COURTESY:
402     sprintf(endstr, "Game courtesyaborted by %s",
403             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
404     break;
405   case END_COURTESYADJOURN:
406     sprintf(endstr, "Game courtesyadjourned by %s",
407             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
408     break;
409   case END_NOMATERIAL:
410     sprintf(endstr, "Game drawn because neither player has mating material");
411     break;
412   case END_FLAGNOMATERIAL:
413     sprintf(endstr, "%s ran out of time and %s has no material to mate",
414             game_globals.garray[g].winner == WHITE ? blackguy : whiteguy,
415             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
416     break;
417   case END_ADJDRAW:
418     sprintf(endstr, "Game drawn by adjudication");
419     break;
420   case END_ADJWIN:
421     sprintf(endstr, "%s wins by adjudication",
422             game_globals.garray[g].winner == WHITE ? whiteguy : blackguy);
423     break;
424   case END_ADJABORT:
425     sprintf(endstr, "Game aborted by adjudication");
426     break;
427   default:
428     sprintf(endstr, "???????");
429     break;
430   }
431
432   return (endstr);
433 }
434
435 const char *EndSym(int g)
436 {
437         static const char *symbols[] = {"1-0", "0-1", "1/2-1/2", "*"};
438
439         switch (game_globals.garray[g].result) {
440         case END_CHECKMATE:
441         case END_RESIGN:
442         case END_FLAG:
443         case END_ADJWIN:
444         case END_BARE:
445                 return ((game_globals.garray[g].winner == WHITE) ? symbols[0] : symbols[1]);
446                 break;
447         case END_AGREEDDRAW:
448         case END_BOTHFLAG:
449         case END_REPETITION:
450         case END_50MOVERULE:
451         case END_STALEMATE:
452         case END_NOMATERIAL:
453         case END_FLAGNOMATERIAL:
454         case END_ADJDRAW:
455                 return (symbols[2]);
456                 break;
457         default:
458                 break;
459         }
460
461         return (symbols[3]);
462 }
463
464 /* This should be enough to hold any game up to at least 8000 moves
465  * If we overwrite this, the server will crash :-).
466  */
467 /* 8000? who you trying to kid? this is awful - enough for 600 halfs :) -DAV*/
468 #define GAME_STRING_LEN 19000
469 static char gameString[GAME_STRING_LEN];
470 char *movesToString(int g, int pgn)
471 {
472   char tmp[160];
473   int wr, br;
474   int i, col;
475   time_t curTime;
476   struct move_t *moves;
477
478   if (game_globals.garray[g].status == GAME_EXAMINE || game_globals.garray[g].status == GAME_SETUP)
479     moves = game_globals.garray[g].examMoveList;
480   else moves = game_globals.garray[g].moveList;
481
482   wr = game_globals.garray[g].white_rating;
483   br = game_globals.garray[g].black_rating;
484   
485
486   curTime = untenths(game_globals.garray[g].timeOfStart);
487
488   if (pgn) {
489     sprintf(gameString,
490             "\n[Event \"%s %s %s game\"]\n"
491             "[Site \"%s, %s\"]\n",
492             config_get_tmp("SERVER_NAME"),
493             rstr[game_globals.garray[g].rated], /*bstr[game_globals.garray[g].type],*/ 
494             game_globals.garray[g].variant, // [HGM] allow more variation in game_types
495             config_get_tmp("SERVER_NAME"),
496             config_get_tmp("SERVER_LOCATION"));
497     strftime(tmp, sizeof(tmp),
498              "[Date \"%Y.%m.%d\"]\n"
499              "[Time \"%H:%M:%S\"]\n",
500              localtime((time_t *) &curTime));
501     strcat(gameString, tmp);
502     sprintf(tmp,
503             "[Round \"-\"]\n"
504             "[White \"%s\"]\n"
505             "[Black \"%s\"]\n"
506             "[WhiteElo \"%d\"]\n"
507             "[BlackElo \"%d\"]\n",
508             game_globals.garray[g].white_name, game_globals.garray[g].black_name, wr, br);
509     strcat(gameString, tmp);
510     if(game_globals.garray[g].game_state.variant[0]) { // [HGM] variant: print variant tag
511         sprintf(tmp,
512             "[Variant \"%s\"]\n",
513             game_globals.garray[g].game_state.variant);
514         strcat(gameString, tmp);
515     }
516     if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the FEN
517         sprintf(tmp,
518             "[Setup \"1\"]\n"
519             "[FEN \"%s\"]\n",
520             game_globals.garray[g].FENstartPos);
521         strcat(gameString, tmp);
522     }
523     sprintf(tmp,
524             "[TimeControl \"%d+%d\"]\n"
525             "[Mode \"ICS\"]\n"
526             "[Result \"%s\"]\n\n",
527             game_globals.garray[g].wInitTime / 10, game_globals.garray[g].wIncrement / 10, EndSym(g));
528     strcat(gameString, tmp);
529
530     col = 0;
531     for (i = 0; i < game_globals.garray[g].numHalfMoves; i++) {
532       if (moves[i].color == WHITE) {
533         if ((col += sprintf(tmp, "%d. ", (i+1) / 2 + 1)) > 70) {
534           strcat(gameString, "\n");
535           col = 0;
536         }
537         strcat(gameString, tmp);
538       } else if (i==0) {
539         strcat (tmp, "1. ... ");
540         col += 7;
541       }
542       if ((col += sprintf(tmp, "%s ", moves[i].algString)) > 70) {
543         strcat(gameString, "\n");
544         col = 0;
545       }
546       strcat(gameString, tmp);
547       if(moves[i].depth > 0) { // [HGM] computer game, add {score/depth} comment
548         if ((col += sprintf(tmp, "{%s%.2f/%d} ", moves[i].score > 0 ? "+" : "",
549                                 moves[i].score, moves[i].depth)) > 70) {
550           strcat(gameString, "\n");
551           col = 0;
552         }
553         strcat(gameString, tmp);
554       }
555     }
556     strcat(gameString, "\n");
557
558   } else {
559
560     sprintf(gameString, "\n%s ", game_globals.garray[g].white_name);
561     if (wr > 0) {
562       sprintf(tmp, "(%d) ", wr);
563     } else {
564       sprintf(tmp, "(UNR) ");
565     }
566     strcat(gameString, tmp);
567     sprintf(tmp, "vs. %s ", game_globals.garray[g].black_name);
568     strcat(gameString, tmp);
569     if (br > 0) {
570       sprintf(tmp, "(%d) ", br);
571     } else {
572       sprintf(tmp, "(UNR) ");
573     }
574     strcat(gameString, tmp);
575     strcat(gameString, "--- ");
576     strcat(gameString, strltime(&curTime));
577     if (game_globals.garray[g].rated) {
578       strcat(gameString, "\nRated ");
579     } else {
580       strcat(gameString, "\nUnrated ");
581     }
582 //    strcat (gameString, TypeStrings[game_globals.garray[g].type]);
583     strcat (gameString, game_globals.garray[g].variant);
584     strcat(gameString, " match, initial time: ");
585     if ((game_globals.garray[g].bInitTime != game_globals.garray[g].wInitTime) || (game_globals.garray[g].wIncrement != game_globals.garray[g].bIncrement)) { /* different starting times */ 
586       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);
587     } else {
588       sprintf(tmp, "%d minutes, increment: %d seconds.\n\n", game_globals.garray[g].wInitTime / 600, game_globals.garray[g].wIncrement / 10);
589     }
590     strcat(gameString, tmp);
591     if(game_globals.garray[g].game_state.setup) { // [HGM] setup: print the initial position board
592         char *q; struct game_state_t initial_gs; struct move_t ml[600]; int r, f;
593
594         initial_gs.gameNum = g;
595         initial_gs.wkrmoved = 0; // [HGM] for some reason calling reset_board_vars() does not work here
596         initial_gs.wqrmoved = 0; //       so I just duplicated the code and pasted it here...
597         initial_gs.wkmoved = 0;
598         initial_gs.bkrmoved = 0;
599         initial_gs.bqrmoved = 0;
600         initial_gs.bkmoved = 0;
601         initial_gs.onMove = WHITE;
602         initial_gs.lastIrreversable = -1;
603         initial_gs.files = game_globals.garray[g].game_state.files;
604         initial_gs.ranks = game_globals.garray[g].game_state.ranks;
605         initial_gs.holdings = game_globals.garray[g].game_state.holdings;
606         strcpy(initial_gs.variant, game_globals.garray[g].game_state.variant);
607         for (f = 0; f < 2; f++) {
608           for (r = 0; r < initial_gs.files; r++)
609              initial_gs.ep_possible[f][r] = 0;
610           for (r = PAWN; r <= QUEEN; r++)
611             initial_gs.holding[f][r-PAWN] = 0;
612         }
613         FEN_to_board(game_globals.garray[g].FENstartPos ,&initial_gs);
614
615         kludgeFlag = 1; // [HGM] setup: this is not thread safe. Must it be???
616         q = board_to_string(
617                       game_globals.garray[g].white_name,
618                       game_globals.garray[g].black_name,
619                       game_globals.garray[g].wTime,
620                       game_globals.garray[g].bTime,
621                       &initial_gs,
622                       ml,
623                       11,
624                       WHITE, 0, 0);
625         kludgeFlag = 0;
626
627         strcat(gameString, q);
628         strcat(gameString, "\n");
629     }
630     sprintf(tmp, "Move  %-19s%-19s\n", game_globals.garray[g].white_name, game_globals.garray[g].black_name);
631     strcat(gameString, tmp);
632     strcat(gameString, "----  ----------------   ----------------\n");
633
634     for (i = 0; i < game_globals.garray[g].numHalfMoves; i += 2) {
635       if (i==0 && (moves[i].color == BLACK)) {
636         sprintf(tmp, "%3d.  %-16s   %-16s\n", (i+1)/2 + 1, "...",
637                      move_and_time(&moves[i]));
638         i--;
639       } else if (i + 1 < game_globals.garray[g].numHalfMoves) {
640         sprintf(tmp, "%3d.  %-16s   ", (i+1)/2 + 1, move_and_time(&moves[i]));
641         strcat(gameString, tmp);
642         sprintf(tmp, "%-16s\n", move_and_time(&moves[i+1]));
643       } else {
644         sprintf(tmp, "%3d.  %-16s\n", (i+1)/2 + 1, move_and_time(&moves[i]));
645       }
646       strcat(gameString, tmp);
647       if (strlen(gameString) > GAME_STRING_LEN - 100) { /* Bug out if getting
648                                                            close to filling this
649                                                            string */
650         return gameString;
651       }
652     }
653
654     strcat(gameString, "      ");
655   }
656
657   sprintf(tmp, "{%s} %s\n", EndString(g, 0), EndSym(g));
658   strcat(gameString, tmp);
659
660   return gameString;
661 }
662
663 void game_disconnect(int g, int p)
664 {
665   game_ended(g, (game_globals.garray[g].white == p) ? WHITE : BLACK, END_LOSTCONNECTION);
666 }
667
668 int CharToPiece(char c, char *variant)
669 {
670
671   if(variant) {
672     if(!strcmp(variant, "shogi")) {
673       switch(c) {
674         case 'N':
675           return W_HONORABLEHORSE;
676         case 'n':
677           return B_HONORABLEHORSE;
678         case 'L':
679           return W_LANCE;
680         case 'l':
681           return B_LANCE;
682         case 'S':
683           return W_SILVER;
684         case 's':
685           return B_SILVER;
686         case 'G':
687           return W_GOLD;
688         case 'g':
689           return B_GOLD;
690       }
691     } else if(!strcmp(variant, "xiangqi")) {
692       switch(c) {
693         case 'A':
694           return W_MANDARIN;
695         case 'a':
696           return B_MANDARIN;
697         case 'H':
698           return W_HORSE;
699         case 'h':
700           return B_HORSE;
701         case 'C':
702           return W_CANNON;
703         case 'c':
704           return B_CANNON;
705       }
706     } else if(!strcmp(variant, "super")) {
707       switch(c) {
708         case 'E':
709           return W_EMPRESS;
710         case 'e':
711           return B_EMPRESS;
712         case 'S':
713           return W_PRINCESS;
714         case 's':
715           return B_PRINCESS;
716         case 'Z':
717           return W_AMAZON;
718         case 'z':
719           return B_AMAZON;
720         case 'V':
721           return W_CENTAUR;
722         case 'v':
723           return B_CENTAUR;
724       }
725     }
726   }
727   switch (c) {
728   case 'P':
729     return W_PAWN;
730   case 'p':
731     return B_PAWN;
732   case 'N':
733     return W_KNIGHT;
734   case 'n':
735     return B_KNIGHT;
736   case 'B':
737     return W_BISHOP;
738   case 'b':
739     return B_BISHOP;
740   case 'R':
741     return W_ROOK;
742   case 'r':
743     return B_ROOK;
744   case 'A':
745     return W_CARDINAL;
746   case 'a':
747     return B_CARDINAL;
748   case 'C':
749     return W_MARSHALL;
750   case 'c':
751     return B_MARSHALL;
752   case 'M':
753     return W_MAN;
754   case 'm':
755     return B_MAN;
756   case 'Q':
757     return W_QUEEN;
758   case 'q':
759     return B_QUEEN;
760   case 'E':
761     return W_ELEPHANT;
762   case 'e':
763     return B_ELEPHANT;
764   case 'K':
765     return W_KING;
766   case 'k':
767     return B_KING;
768   default:
769     return NOPIECE;
770   }
771 }
772
773 char PieceToChar(int piece)
774 {
775   switch (piece) {
776     case W_PAWN:return 'P';
777   case B_PAWN:
778     return 'p';
779   case W_KNIGHT:
780   case W_HONORABLEHORSE:
781     return 'N';
782   case B_KNIGHT:
783   case B_HONORABLEHORSE:
784     return 'n';
785   case W_BISHOP:
786     return 'B';
787   case B_BISHOP:
788     return 'b';
789   case W_ROOK:
790     return 'R';
791   case B_ROOK:
792     return 'r';
793   case W_CARDINAL:
794   case W_MANDARIN:
795   case W_ALFIL2:
796     return 'A';
797   case B_CARDINAL:
798   case B_MANDARIN:
799   case B_ALFIL2:
800     return 'a';
801   case W_CANNON:
802   case W_MARSHALL:
803     return 'C';
804   case B_CANNON:
805   case B_MARSHALL:
806     return 'c';
807   case W_MAN:
808   case W_MINISTER:
809     return 'M';
810   case B_MAN:
811   case B_MINISTER:
812     return 'm';
813   case W_QUEEN:
814     return 'Q';
815   case B_QUEEN:
816     return 'q';
817   case W_ELEPHANT:
818   case W_EMPRESS:
819     return 'E';
820   case B_ELEPHANT:
821   case B_EMPRESS:
822     return 'e';
823   case W_ALFIL:
824     return 'B';
825   case B_ALFIL:
826     return 'b';
827   case W_FERZ:
828     return 'Q';
829   case B_FERZ:
830     return 'q';
831   case W_FERZ2:
832     return 'F';
833   case B_FERZ2:
834     return 'f';
835   case W_WAZIR:
836   case W_WOODY:
837     return 'W';
838   case B_WAZIR:
839   case B_WOODY:
840     return 'w';
841   case W_HORSE:
842   case W_PRIESTESS:
843   case W_NIGHTRIDER:
844     return 'H';
845   case B_HORSE:
846   case B_PRIESTESS:
847   case B_NIGHTRIDER:
848     return 'h';
849   case W_SILVER:
850   case W_PRINCESS:
851   case W_MAN2:
852     return 'S';
853   case B_SILVER:
854   case B_PRINCESS:
855   case B_MAN2:
856     return 's';
857   case W_GOLD:
858   case W_MASTODON:
859     return 'G';
860   case B_GOLD:
861   case B_MASTODON:
862     return 'g';
863   case W_AMAZON:
864     return 'Z';
865   case B_AMAZON:
866     return 'z';
867   case W_CENTAUR:
868     return 'V';
869   case B_CENTAUR:
870     return 'v';
871   case W_KING:
872     return 'K';
873   case B_KING:
874     return 'k';
875   case W_LANCE:
876     return 'L';
877   case B_LANCE:
878     return 'l';
879   default:
880     return ' ';
881   }
882 }
883
884 /*
885   write a new style generic game file
886 */
887 static void WriteGameFile_v100(FILE * fp, int g)
888 {
889         struct game gg = game_globals.garray[g];
890         const char *s;
891
892         /* zero any elements we don't want to save */
893         memset(&gg, 0, offsetof(struct game, not_saved_marker));
894         gg.game_state.gameNum = 0;
895
896         /* marshall it into a string */
897         s = marshall_game(&gg);
898         if (!s) {
899                 d_printf("Unable to marshall game structure!\n");
900                 return;
901         }
902
903         /* and save it */
904         fprintf(fp, "v 100\n%s\n", s);
905         free(s);
906 }
907
908
909 /*
910   read a game file using the new generic and extensible format 
911 */
912 static int ReadGameAttrs_v100(FILE *fp, int g)
913 {
914         char *s, *s2;       
915         struct game *gg = &game_globals.garray[g];
916         struct game g1;
917
918         s = fd_load(fileno(fp), NULL);
919         if (!s) {
920                 d_printf("Error reading game file!\n");
921                 return -1;
922         }
923
924         /* skip first line */
925         s2 = strchr(s, '\n');
926
927         /* remember the game state for later */
928         g1 = *gg;
929
930         /* the marshaller doesn't save zero elements, but some elements don't
931            default to zero. Make sure they get the right value */
932         memset(&gg->not_saved_marker, 0, 
933                sizeof(struct game) - offsetof(struct game, not_saved_marker));
934         gg->game_state.gameNum = g;
935
936         if (!s2 || unmarshall_game(gg, s2) != 0) {
937                 d_printf("Error unmarshalling game data!\n");
938                 free(s);
939                 return -1;
940         }
941         free(s);
942
943         /* when examining we are not supposed to restore the game
944            state, so put it back here */
945         if (g1.status == GAME_EXAMINE || g1.status == GAME_SETUP) { 
946                 gg->game_state = g1.game_state;
947         }
948         gg->status = g1.status;
949
950         /* cope with continuing a game with timeseal that was started without it */
951         gg->wRealTime = gg->wTime * 100;
952         gg->bRealTime = gg->bTime * 100;
953
954         return 0;
955 }
956
957
958 static int ReadGameAttrs_common(FILE * fp, int g,int version)
959 {
960         if (version == 9 || version == 100) {
961                 return ReadGameAttrs_v100(fp, g);
962         }
963
964         return ReadGameAttrs_old(fp, g, version);
965 }
966
967 int ReadGameAttrs_exam(FILE * fp, int g)
968 {
969   int version = 0;
970   char line[MAX_GLINE_SIZE];
971
972   fgets(line, MAX_GLINE_SIZE - 1, fp);
973
974    if (line[0] == 'v') {
975     sscanf(line, "%*c %d", &version);
976
977     if (version < 5)
978       return -1;
979     else
980       return ReadGameAttrs_common(fp,g,version);
981   }
982
983    else return -1;
984 }
985
986 int ReadGameAttrs(FILE * fp, int g)
987 {
988         int version = 0;
989         char line[MAX_GLINE_SIZE];
990
991         fgets(line, MAX_GLINE_SIZE - 1, fp);
992
993         if (line[0] == 'v') {
994                 sscanf(line, "%*c %d", &version);
995         }
996         return ReadGameAttrs_common(fp,g,version);
997 }
998
999 int game_read(int g, int wp, int bp)
1000 {
1001   FILE *fp;
1002   int piece;
1003
1004   game_globals.garray[g].white = wp;
1005   game_globals.garray[g].black = bp;
1006 /*  game_globals.garray[g].old_white = -1;
1007     game_globals.garray[g].old_black = -1;
1008 */
1009   game_globals.garray[g].moveListSize = 0;
1010   game_globals.garray[g].game_state.gameNum = g;
1011   strcpy(game_globals.garray[g].white_name, player_globals.parray[wp].name);
1012   strcpy(game_globals.garray[g].black_name, player_globals.parray[bp].name);
1013   if (game_globals.garray[g].type == TYPE_BLITZ) {
1014     game_globals.garray[g].white_rating = player_globals.parray[wp].b_stats.rating;
1015     game_globals.garray[g].black_rating = player_globals.parray[bp].b_stats.rating;
1016   } else if (game_globals.garray[g].type == TYPE_WILD ||
1017              game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1018              game_globals.garray[g].type == TYPE_CAPABLANCA ||
1019              game_globals.garray[g].type == TYPE_GOTHIC) {
1020     game_globals.garray[g].white_rating = player_globals.parray[wp].w_stats.rating;
1021     game_globals.garray[g].black_rating = player_globals.parray[bp].w_stats.rating;
1022   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1023     game_globals.garray[g].white_rating = player_globals.parray[wp].l_stats.rating;
1024     game_globals.garray[g].black_rating = player_globals.parray[bp].l_stats.rating;
1025   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1026     game_globals.garray[g].white_rating = player_globals.parray[wp].bug_stats.rating;
1027     game_globals.garray[g].black_rating = player_globals.parray[bp].bug_stats.rating;
1028   } else {
1029     game_globals.garray[g].white_rating = player_globals.parray[wp].s_stats.rating;
1030     game_globals.garray[g].black_rating = player_globals.parray[bp].s_stats.rating;
1031   }
1032   fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1033              player_globals.parray[wp].login, player_globals.parray[bp].login);
1034   if (!fp) {
1035     return -1;
1036   }
1037   for (piece=PAWN; piece < KING; piece++) {
1038     game_globals.garray[g].game_state.holding[0][piece-PAWN]
1039       = game_globals.garray[g].game_state.holding[1][piece-PAWN] = 0;
1040   }
1041   if (ReadGameAttrs(fp, g) < 0) {
1042     fclose(fp);
1043     return -1;
1044   }
1045   fclose(fp);
1046
1047   if (game_globals.garray[g].result == END_ADJOURN
1048       || game_globals.garray[g].result == END_COURTESYADJOURN)
1049     game_globals.garray[g].result = END_NOTENDED;
1050   game_globals.garray[g].status = GAME_ACTIVE;
1051   game_globals.garray[g].startTime = tenth_secs();
1052   game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1053   game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1054
1055   /* cope with continuing a game with timeseal that was started without it */
1056   game_globals.garray[g].wRealTime = game_globals.garray[g].wTime * 100;
1057   game_globals.garray[g].bRealTime = game_globals.garray[g].bTime * 100;
1058
1059   /* Need to do notification and pending cleanup */
1060   return 0;
1061 }
1062
1063 int game_delete(int wp, int bp)
1064 {
1065   char fname[MAX_FILENAME_SIZE];
1066   char lname[MAX_FILENAME_SIZE];
1067
1068   sprintf(fname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1069           player_globals.parray[wp].login, player_globals.parray[bp].login);
1070   sprintf(lname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[bp].login[0],
1071           player_globals.parray[wp].login, player_globals.parray[bp].login);
1072   unlink(fname);
1073   unlink(lname);
1074   return 0;
1075 }
1076
1077 int game_save(int g)
1078 {
1079         FILE *fp;
1080         struct player *wp, *bp;
1081         struct game *gg = &game_globals.garray[g];
1082         char *fname;
1083
1084         wp = &player_globals.parray[gg->white];
1085         bp = &player_globals.parray[gg->black];
1086         asprintf(&fname, "%s/%c/%s-%s", ADJOURNED_DIR, wp->login[0],
1087                  wp->login, bp->login);
1088         fp = fopen_p("%s", "w", fname);
1089         if (!fp) {
1090                 d_printf("CHESSD: Problem opening adjourn file %s-%s for write\n", 
1091                           wp->login, bp->login);
1092                 free(fname);
1093                 return -1;
1094         }
1095         WriteGameFile_v100(fp, g);
1096         fclose(fp);
1097         /* Create link for easier stored game finding */
1098         if (bp->login[0] != wp->login[0]) {
1099                 char *lname;
1100                 asprintf(&lname, "%s/%c/%s-%s", ADJOURNED_DIR, bp->login[0],
1101                          wp->login, bp->login);
1102                 link(fname, lname);
1103                 free(lname);
1104         }
1105         free(fname);
1106         return 0;
1107 }
1108
1109 static long OldestHistGame(char *login)
1110 {
1111         FILE *fp;
1112         long when;
1113         
1114         fp = fopen_p("%s/player_data/%c/%s.%s", "r", STATS_DIR,
1115                      login[0], login, STATS_GAMES);
1116         if (fp == NULL) {
1117                 fp = fopen_p("%s/player_data/%c/.rem.%s.%s", "r", STATS_DIR,
1118                              login[0], login, STATS_GAMES);
1119         }
1120         if (!fp) return 0;
1121
1122         fscanf(fp, "%*d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %*s %ld",
1123                &when);
1124         fclose(fp);
1125         return when;
1126 }
1127
1128 static void RemoveHistGame(char *file, int maxlines)
1129 {
1130   FILE *fp;
1131   char GameFile[MAX_FILENAME_SIZE];
1132   char Opponent[MAX_LOGIN_NAME];
1133   char line[MAX_LINE_SIZE];
1134   long When, oppWhen;
1135   int count = 0;
1136
1137   fp = fopen_p("%s", "r", file);
1138   if (fp == NULL)
1139     return;
1140
1141   fgets(line, MAX_LINE_SIZE - 1, fp);
1142   sscanf(line, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1143          Opponent, &When);
1144   count++;
1145
1146   while (!feof(fp)) {
1147     fgets(line, MAX_LINE_SIZE - 1, fp);
1148     if (!feof(fp))
1149       count++;
1150   }
1151   fclose(fp);
1152
1153   stolower(Opponent);
1154   if (count > maxlines) {
1155     truncate_file(file, maxlines);
1156
1157     oppWhen = OldestHistGame(Opponent);
1158     if (oppWhen > When || oppWhen <= 0L) {
1159       sprintf(GameFile, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1160       unlink(GameFile);
1161     }
1162   }
1163 }
1164
1165 void RemHist(char *who)
1166 {
1167         FILE *fp;
1168         char Opp[MAX_LOGIN_NAME];
1169         long When, oppWhen;
1170
1171         fp = fopen_p("%s/player_data/%c/%s.%s", "r", 
1172                      STATS_DIR,
1173                      who[0], who, STATS_GAMES);
1174         if (!fp) {
1175                 return;
1176         }
1177         while (!feof(fp)) {
1178                 fscanf(fp, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1179                        Opp, &When);
1180                 stolower(Opp);
1181                 oppWhen = OldestHistGame(Opp);
1182                 if (oppWhen > When || oppWhen <= 0L) {
1183                         char *fName;
1184                         asprintf(&fName, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1185                         unlink(fName);
1186                         free(fName);
1187                 }
1188         }
1189         fclose(fp);
1190 }
1191
1192 static void write_g_out(int g, char *file, int maxlines, int isDraw,
1193                           char *EndSymbol, char *name, time_t *now)
1194 {
1195   FILE *fp;
1196   int wp, bp;
1197   int wr, br;
1198   char type[4];
1199   char tmp[2048];
1200   char *ptmp = tmp;
1201   char cResult;
1202   int count = -1;
1203   char *goteco;
1204
1205   wp = game_globals.garray[g].white;
1206   bp = game_globals.garray[g].black;
1207
1208   if (game_globals.garray[g].private) {
1209     type[0] = 'p';
1210   } else {
1211     type[0] = ' ';
1212   }
1213   if (game_globals.garray[g].type == TYPE_BLITZ) {
1214     wr = player_globals.parray[wp].b_stats.rating;
1215     br = player_globals.parray[bp].b_stats.rating;
1216     type[1] = 'b';
1217   } else if (game_globals.garray[g].type == TYPE_WILD ||
1218              game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1219              game_globals.garray[g].type == TYPE_CAPABLANCA ||
1220              game_globals.garray[g].type == TYPE_GOTHIC) {
1221     wr = player_globals.parray[wp].w_stats.rating;
1222     br = player_globals.parray[bp].w_stats.rating;
1223     type[1] = 'w';
1224   } else if (game_globals.garray[g].type == TYPE_STAND) {
1225     wr = player_globals.parray[wp].s_stats.rating;
1226     br = player_globals.parray[bp].s_stats.rating;
1227     type[1] = 's';
1228   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1229     wr = player_globals.parray[wp].l_stats.rating;
1230     br = player_globals.parray[bp].l_stats.rating;
1231     type[1] = 'l';
1232   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1233     wr = player_globals.parray[wp].bug_stats.rating;
1234     br = player_globals.parray[bp].bug_stats.rating;
1235     type[1] = 'B';
1236   } else {
1237     wr = 0;
1238     br = 0;
1239     if (game_globals.garray[g].type == TYPE_NONSTANDARD)
1240       type[1] = 'n';
1241     else
1242       type[1] = 'u';
1243   }
1244   if (game_globals.garray[g].rated) {
1245     type[2] = 'r';
1246   } else {
1247     type[2] = 'u';
1248   }
1249   type[3] = '\0';
1250
1251   fp = fopen_s(file, "r");
1252   if (fp) {
1253     while (!feof(fp))
1254       fgets(tmp, 1024, fp);
1255     sscanf(ptmp, "%d", &count);
1256     fclose(fp);
1257   }
1258   count = (count + 1) % 100;
1259
1260   fp = fopen_s(file, "a");
1261   if (!fp)
1262     return;
1263
1264   goteco = getECO(g);
1265
1266 /* Counter Result MyRating MyColor OppRating OppName [pbr 2 12 2 12] ECO End Date */
1267   if (name == player_globals.parray[wp].name) {
1268     if (isDraw)
1269       cResult = '=';
1270     else if (game_globals.garray[g].winner == WHITE)
1271       cResult = '+';
1272     else
1273       cResult = '-';
1274
1275     fprintf(fp, "%d %c %d W %d %s %s %d %d %d %d %s %s %ld\n",
1276             count, cResult, wr, br, player_globals.parray[bp].name, type,
1277             game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1278             game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1279             goteco,
1280             EndSymbol,
1281             (long) *now);
1282   } else {
1283     if (isDraw)
1284       cResult = '=';
1285     else if (game_globals.garray[g].winner == BLACK)
1286       cResult = '+';
1287     else
1288       cResult = '-';
1289
1290     fprintf(fp, "%d %c %d B %d %s %s %d %d %d %d %s %s %ld\n",
1291             count, cResult, br, wr, player_globals.parray[wp].name, type,
1292             game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1293             game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1294             goteco,
1295             EndSymbol,
1296             (long) *now);
1297   }
1298   fclose(fp);
1299
1300   RemoveHistGame(file, maxlines);
1301 }
1302
1303 /* Test if entry is present - 1 yes 0 no */
1304 /* returns -1 if an error */
1305
1306 char get_journalgame_type(int p,char* fname,char slot)
1307
1308 {
1309   struct player *pp = &player_globals.parray[p];
1310   char cur_slot;
1311   char type[4];
1312
1313   FILE* fp = fopen_s(fname,"r");
1314   if (!fp) {
1315     d_printf("Corrupt journal file! %s\n",fname);
1316     pprintf (p, "The journal file is corrupt! See an admin.\n");
1317     return 0;
1318   }
1319
1320   while (!feof(fp)) {
1321     if (fscanf(fp, "%c %*s %*d %*s %*d %s %*d %*d %*s %*s %*s\n",
1322        &cur_slot,type) != 2) {
1323       d_printf( "CHESSD: Error in journal info format for player %s.\n",
1324         pp->name);
1325       pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1326       return '\0';
1327     }
1328     if (cur_slot == slot) {
1329       fclose (fp);
1330       if (type[0] == 'p')
1331         return type[1];
1332       else
1333         return type[0];
1334     }
1335   }
1336   fclose (fp);
1337   return '0';
1338 }
1339
1340
1341 /* Returns 1 if successful */
1342
1343 int removejournalitem(int p, char slot,FILE* fp,char* fname, int* empty)
1344
1345 {
1346         FILE* fp_new;
1347         int found = 0;
1348         struct journal* j;
1349
1350         *empty = 1;
1351         fp_new = fopen_s(fname, "w");
1352         if (!fp_new) {
1353                 d_printf("Can't write to journal %s!\n",fname);
1354                 pprintf (p, "Was unable to write to the file! See an admin.\n");
1355                 return 0;
1356         }
1357         j = (struct journal*) malloc(sizeof(struct journal));
1358         while (!feof(fp)) {
1359                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1360                            &j->slot,
1361                            j->WhiteName,
1362                            &j->WhiteRating,
1363                            j->BlackName,
1364                            &j->BlackRating,
1365                            j->type,
1366                            &j->t, &j->i,
1367                            j->eco,
1368                            j->ending,
1369                            j->result) != 11) {
1370                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1371                         pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1372                         fclose(fp_new);
1373                         free (j);
1374                         return 0;
1375                 }
1376                 if (slot != j->slot) {
1377                         *empty = 0;
1378                         fprintf(fp_new, "%c %s %d %s %d %s %d %d %s %s %s\n",
1379                                 j->slot,
1380                                 j->WhiteName,
1381                                 j->WhiteRating,
1382                                 j->BlackName,
1383                                 j->BlackRating,
1384                                 j->type,
1385                                 j->t, j->i,
1386                                 j->eco,
1387                                 j->ending,
1388                                 j->result);
1389                 } else
1390                         found = 1;
1391         }
1392         fclose(fp_new);
1393         free (j);
1394         return found;
1395 }
1396
1397 /* Find from_spot in journal list - return 0 if corrupted */
1398 int journal_get_info(int p, char from_spot,struct journal* j, char *fname)
1399 {
1400         FILE *fp;
1401
1402         fp = fopen_s(fname, "r");
1403         if (!fp) {
1404                 d_printf("Corrupt journal file! %s\n",fname);
1405                 pprintf (p, "The journal file is corrupt! See an admin.\n");
1406                 return 0;
1407         }
1408         while (!feof(fp)) {
1409                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1410                            &j->slot,
1411                            j->WhiteName,
1412                            &j->WhiteRating,
1413                            j->BlackName,
1414                            &j->BlackRating,
1415                            j->type,
1416                            &j->t, &j->i,
1417                            j->eco,
1418                            j->ending,
1419                            j->result) != 11) {
1420                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1421                         pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1422                         fclose(fp);
1423                         return 0;
1424                 }
1425                 if (tolower(j->slot) == from_spot) {
1426                         fclose(fp);
1427                         return 1;
1428                 }
1429         }
1430         fclose(fp);
1431         return 0;
1432 }
1433
1434 void addjournalitem(int p,struct journal* j_add, char* fname)
1435 {
1436         struct journal* j_cur;
1437         int have_output=0;
1438         char fname2[MAX_FILENAME_SIZE];
1439         FILE *fp;
1440         FILE *fp2;
1441
1442         strcpy (fname2,fname);
1443         strcat (fname2,".w");
1444         fp2 = fopen_s(fname2, "w");
1445         if (!fp2) {
1446                 d_printf( "CHESSD: Problem opening file %s for write\n", fname);
1447                 pprintf (p, "Couldn't update journal! Report this to an admin.\n");
1448                 return;
1449         } 
1450         fp = fopen_s(fname, "r");
1451         if (!fp) { /* Empty? */
1452                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1453                         j_add->slot, j_add->WhiteName, j_add->WhiteRating,
1454                         j_add->BlackName, j_add->BlackRating,
1455                         j_add->type, j_add->t, j_add->i, j_add->eco, j_add->ending,
1456                         j_add->result);
1457                 fclose (fp2);
1458                 rename (fname2, fname);
1459                 return;
1460         } else {
1461                 j_cur = (struct journal*) malloc(sizeof(struct journal));
1462                 while (!feof(fp)) {
1463                         if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1464                                    &j_cur->slot,
1465                                    j_cur->WhiteName,
1466                                    &j_cur->WhiteRating,
1467                                    j_cur->BlackName,
1468                                    &j_cur->BlackRating,
1469                                    j_cur->type,
1470                                    &j_cur->t, &j_cur->i,
1471                                    j_cur->eco,
1472                                    j_cur->ending,
1473                                    j_cur->result) != 11) {
1474                                 d_printf( "CHESSD: Error in journal info format - aborting. %s\n", fname);
1475                                 free (j_cur);
1476                                 fclose(fp);
1477                                 fclose(fp2);
1478                                 return;
1479                         }
1480                         if ((j_cur->slot >= j_add->slot) && (!have_output)) {
1481                                 
1482                                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1483                                         j_add->slot,
1484                                         j_add->WhiteName,
1485                                         j_add->WhiteRating,
1486                                         j_add->BlackName,
1487                                         j_add->BlackRating,
1488                                         j_add->type,
1489                                         j_add->t, j_add->i,
1490                                         j_add->eco,
1491                                         j_add->ending,
1492                                         j_add->result);
1493                                 have_output = 1;
1494
1495                         } 
1496                         if (j_cur->slot != j_add->slot) {
1497
1498                                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1499                                         j_cur->slot,
1500                                         j_cur->WhiteName,
1501                                         j_cur->WhiteRating,
1502                                         j_cur->BlackName,
1503                                         j_cur->BlackRating,
1504                                         j_cur->type,
1505                                         j_cur->t, j_cur->i,
1506                                         j_cur->eco,
1507                                         j_cur->ending,
1508                                         j_cur->result);
1509                         }
1510                 }
1511                 
1512                 if (!have_output) { /* Haven't written yet */
1513                         fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1514                                 j_add->slot,
1515                                 j_add->WhiteName,
1516                                 j_add->WhiteRating,
1517                                 j_add->BlackName,
1518                                 j_add->BlackRating,
1519                                 j_add->type,
1520                                 j_add->t, j_add->i,
1521                                 j_add->eco,
1522                                 j_add->ending,
1523                                 j_add->result);
1524                 }
1525                 free (j_cur);
1526                 fclose(fp);
1527                 fclose(fp2);
1528                 rename(fname2, fname);
1529                 return;
1530         }
1531
1532
1533 int pjournal(int p, int p1, char *fname)
1534 {
1535         FILE *fp;
1536         struct journal* j;
1537         
1538         fp = fopen_s(fname, "r");
1539         if (!fp) {
1540                 pprintf(p, "Sorry, no journal information available.\n");
1541                 return COM_OK;
1542         }
1543
1544         j = (struct journal*) malloc(sizeof(struct journal));
1545         pprintf(p, "Journal for %s:\n", player_globals.parray[p1].name);
1546         pprintf(p, "   White         Rating  Black         Rating  Type         ECO End Result\n");
1547         while (!feof(fp)) {
1548                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1549                            &j->slot,
1550                            j->WhiteName,
1551                            &j->WhiteRating,
1552                            j->BlackName,
1553                            &j->BlackRating,
1554                            j->type,
1555                            &j->t, &j->i,
1556                            j->eco,
1557                            j->ending,
1558                            j->result) != 11) {
1559                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1560                         fclose(fp);
1561                         free(j);
1562                         return COM_OK;
1563                 }
1564                 j->WhiteName[13] = '\0';         /* only first 13 chars in name */
1565                 j->BlackName[13] = '\0';
1566                 pprintf(p, "%c: %-13s %4d    %-13s %4d    [%3s%3d%4d] %s %3s %-7s\n",
1567                         j->slot, j->WhiteName, j->WhiteRating,
1568                         j->BlackName, j->BlackRating,
1569                         j->type, j->t / 600, j->i / 10, j->eco, j->ending,
1570                         j->result);
1571         }
1572         free (j);
1573         fclose(fp);
1574         return COM_OK;
1575 }
1576
1577 int pgames(int p, int p1, char *fname)
1578 {
1579         FILE *fp;
1580         time_t t;
1581         int MyRating, OppRating;
1582         int wt, wi, bt, bi;
1583         char OppName[MAX_LOGIN_NAME + 1];
1584         char type[100];
1585         char eco[100];
1586         char ending[100];
1587         char MyColor[2];
1588         int count;
1589         char result[2];
1590         
1591         fp = fopen_s(fname, "r");
1592         if (!fp) {
1593                 pprintf(p, "Sorry, no game information available.\n");
1594                 return COM_OK;
1595         }
1596         pprintf(p, "History for %s:\n", player_globals.parray[p1].name);
1597         pprintf(p, "                  Opponent      Type         ECO End Date\n");
1598         while (!feof(fp)) {
1599                 if (fscanf(fp, "%d %s %d %s %d %s %s %d %d %d %d %s %s %ld\n",
1600                            &count,
1601                            result,
1602                            &MyRating,
1603                            MyColor,
1604                            &OppRating,
1605                            OppName,
1606                            type,
1607                            &wt, &wi,
1608                            &bt, &bi,
1609                            eco,
1610                            ending,
1611                            (long *) &t) != 14) {
1612                         d_printf( "CHESSD: Error in games info format. %s\n", fname);
1613                         fclose(fp);
1614                         return COM_OK;
1615                 }
1616                 OppName[13] = '\0';             /* only first 13 chars in name */
1617                 pprintf(p, "%2d: %s %4d %s %4d %-13s [%3s%3d%4d] %s %3s %s",
1618                         count, result, MyRating, MyColor,
1619                         OppRating, OppName,
1620                         type, wt / 600, wi / 10, eco, ending,
1621                         ctime(&t));
1622         }
1623         fclose(fp);
1624         return COM_OK;
1625 }
1626
1627 void game_write_complete(int g, int isDraw, char *EndSymbol)
1628 {
1629         char fname[MAX_FILENAME_SIZE];
1630         int wp = game_globals.garray[g].white, bp = game_globals.garray[g].black;
1631         time_t now = time(NULL);
1632         int Result;
1633         FILE *fp = NULL;
1634         
1635         do {
1636                 if (fp) {
1637                         fclose(fp);
1638                         now++;
1639                 }
1640                 sprintf(fname, "%s/%ld/%ld", HISTORY_DIR, (long) now % 100, (long) now);
1641                 fp = fopen_s(fname, "r");
1642         } while (fp);   /* terminates when the file doesn't exist */
1643         
1644         fp = fopen_s(fname, "w");
1645         
1646         if (fp) {
1647                 WriteGameFile_v100(fp, g);
1648                 fclose(fp);
1649         } else {
1650                 d_printf( "Trouble writing history file %s", fname);
1651         }
1652         
1653         sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1654                 player_globals.parray[wp].login[0], player_globals.parray[wp].login, STATS_GAMES);
1655         write_g_out(g, fname, 10, isDraw, EndSymbol, player_globals.parray[wp].name, &now);
1656         sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1657                 player_globals.parray[bp].login[0], player_globals.parray[bp].login, STATS_GAMES);
1658         write_g_out(g, fname, 10, isDraw, EndSymbol, player_globals.parray[bp].name, &now);
1659         
1660         if (isDraw)
1661                 Result = -1;
1662         else if (game_globals.garray[g].winner == WHITE)
1663                 Result = 1;
1664         else
1665                 Result = 0;
1666         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);
1667         if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1668                 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].black].name,Result,game_globals.garray[g].rated);
1669         if (isDraw)
1670                 Result = -1;
1671         else if (game_globals.garray[g].winner == BLACK)
1672                 Result = 1;
1673         else
1674                 Result = 0;
1675         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);
1676         if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1677                 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].white].name,Result,game_globals.garray[g].rated);
1678 }
1679
1680 int game_count(void)
1681 {
1682         int g, count = 0;
1683         
1684         for (g = 0; g < game_globals.g_num; g++) {
1685                 if ((game_globals.garray[g].status == GAME_ACTIVE) || (game_globals.garray[g].status == GAME_EXAMINE)
1686                     || (game_globals.garray[g].status == GAME_SETUP))
1687                         count++;
1688         }
1689         if (count > command_globals.game_high)
1690                 command_globals.game_high = count;
1691         return count;
1692 }
1693
1694 static int check_kings(struct game_state_t *gs)
1695 {
1696         /* Function written 3/28/96 by Figbert to check for 1 king each side only! */
1697         int blackking = 0, whiteking = 0;
1698         int f, r;
1699         
1700         
1701         for (f = 0; f < gs->files; f++) {
1702                 for (r = 0; r < gs->ranks; r++) {
1703                         if (gs->board[f][r] == B_KING) blackking++;
1704                         if (gs->board[f][r] == W_KING) whiteking++;
1705                 }
1706         }
1707         
1708         if (blackking == 1 && whiteking == 1) return 0; /* Perfect! */
1709         
1710         return -1;
1711 }