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