Implement Spartan Chess
[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     } else if(!strcmp(variant, "spartan")) {
727       switch(c) {
728         case 'w':
729           return B_WARLORD;
730         case 'g':
731           return B_GENERAL;
732         case 'l':
733           return B_LIEUTENANT;
734         case 'c':
735           return B_CAPTAIN;
736         case 'h':
737           return B_HOPLITE;
738       }
739     }
740   }
741   switch (c) {
742   case 'P':
743     return W_PAWN;
744   case 'p':
745     return B_PAWN;
746   case 'N':
747     return W_KNIGHT;
748   case 'n':
749     return B_KNIGHT;
750   case 'B':
751     return W_BISHOP;
752   case 'b':
753     return B_BISHOP;
754   case 'R':
755     return W_ROOK;
756   case 'r':
757     return B_ROOK;
758   case 'A':
759     return W_CARDINAL;
760   case 'a':
761     return B_CARDINAL;
762   case 'C':
763     return W_MARSHALL;
764   case 'c':
765     return B_MARSHALL;
766   case 'M':
767     return W_MAN;
768   case 'm':
769     return B_MAN;
770   case 'Q':
771     return W_QUEEN;
772   case 'q':
773     return B_QUEEN;
774   case 'E':
775     if(!strcmp(variant, "seirawan")) return W_ELEPHANT;
776     return W_ELEPHANT;
777   case 'e':
778     if(!strcmp(variant, "seirawan")) return B_ELEPHANT;
779     return B_ELEPHANT;
780   case 'H':
781     return W_HAWK;
782   case 'h':
783     return B_HAWK;
784   case 'K':
785     return W_KING;
786   case 'k':
787     return B_KING;
788   default:
789     return NOPIECE;
790   }
791 }
792
793 char PieceToChar(int piece)
794 {
795   switch (piece) {
796     case W_PAWN:return 'P';
797   case B_PAWN:
798     return 'p';
799   case W_KNIGHT:
800   case W_HONORABLEHORSE:
801     return 'N';
802   case B_KNIGHT:
803   case B_HONORABLEHORSE:
804     return 'n';
805   case W_BISHOP:
806     return 'B';
807   case B_BISHOP:
808     return 'b';
809   case W_ROOK:
810     return 'R';
811   case B_ROOK:
812     return 'r';
813   case W_CARDINAL:
814   case W_MANDARIN:
815   case W_ALFIL2:
816     return 'A';
817   case B_CARDINAL:
818   case B_MANDARIN:
819   case B_ALFIL2:
820     return 'a';
821   case W_CANNON:
822   case W_MARSHALL:
823     return 'C';
824   case B_CAPTAIN:
825   case B_CANNON:
826   case B_MARSHALL:
827     return 'c';
828   case W_MAN:
829   case W_MINISTER:
830     return 'M';
831   case B_MAN:
832   case B_MINISTER:
833     return 'm';
834   case W_QUEEN:
835     return 'Q';
836   case B_QUEEN:
837     return 'q';
838   case W_SELEPHANT:
839   case W_ELEPHANT:
840   case W_EMPRESS:
841     return 'E';
842   case B_SELEPHANT:
843   case B_ELEPHANT:
844   case B_EMPRESS:
845     return 'e';
846   case W_ALFIL:
847     return 'B';
848   case B_ALFIL:
849     return 'b';
850   case W_FERZ:
851     return 'Q';
852   case B_FERZ:
853     return 'q';
854   case W_FERZ2:
855     return 'F';
856   case B_FERZ2:
857     return 'f';
858   case W_WAZIR:
859   case W_WOODY:
860     return 'W';
861   case B_WARLORD:
862   case B_WAZIR:
863   case B_WOODY:
864     return 'w';
865   case W_HAWK:
866   case W_HORSE:
867   case W_PRIESTESS:
868   case W_NIGHTRIDER:
869     return 'H';
870   case B_HAWK:
871   case B_HORSE:
872   case B_HOPLITE:
873   case B_PRIESTESS:
874   case B_NIGHTRIDER:
875     return 'h';
876   case W_SILVER:
877   case W_PRINCESS:
878   case W_MAN2:
879     return 'S';
880   case B_SILVER:
881   case B_PRINCESS:
882   case B_MAN2:
883     return 's';
884   case W_GOLD:
885   case W_MASTODON:
886     return 'G';
887   case B_GOLD:
888   case B_GENERAL:
889   case B_MASTODON:
890     return 'g';
891   case W_AMAZON:
892     return 'Z';
893   case B_AMAZON:
894     return 'z';
895   case W_CENTAUR:
896     return 'V';
897   case B_CENTAUR:
898     return 'v';
899   case W_KING:
900     return 'K';
901   case B_KING:
902     return 'k';
903   case W_LANCE:
904     return 'L';
905   case B_LIEUTENANT:
906   case B_LANCE:
907     return 'l';
908   default:
909     return ' ';
910   }
911 }
912
913 /*
914   write a new style generic game file
915 */
916 static void WriteGameFile_v100(FILE * fp, int g)
917 {
918         struct game gg = game_globals.garray[g];
919         const char *s;
920
921         /* zero any elements we don't want to save */
922         memset(&gg, 0, offsetof(struct game, not_saved_marker));
923         gg.game_state.gameNum = 0;
924
925         /* marshall it into a string */
926         s = marshall_game(&gg);
927         if (!s) {
928                 d_printf("Unable to marshall game structure!\n");
929                 return;
930         }
931
932         /* and save it */
933         fprintf(fp, "v 100\n%s\n", s);
934         free(s);
935 }
936
937
938 /*
939   read a game file using the new generic and extensible format 
940 */
941 static int ReadGameAttrs_v100(FILE *fp, int g)
942 {
943         char *s, *s2;       
944         struct game *gg = &game_globals.garray[g];
945         struct game g1;
946
947         s = fd_load(fileno(fp), NULL);
948         if (!s) {
949                 d_printf("Error reading game file!\n");
950                 return -1;
951         }
952
953         /* skip first line */
954         s2 = strchr(s, '\n');
955
956         /* remember the game state for later */
957         g1 = *gg;
958
959         /* the marshaller doesn't save zero elements, but some elements don't
960            default to zero. Make sure they get the right value */
961         memset(&gg->not_saved_marker, 0, 
962                sizeof(struct game) - offsetof(struct game, not_saved_marker));
963         gg->game_state.gameNum = g;
964
965         if (!s2 || unmarshall_game(gg, s2) != 0) {
966                 d_printf("Error unmarshalling game data!\n");
967                 free(s);
968                 return -1;
969         }
970         free(s);
971
972         /* when examining we are not supposed to restore the game
973            state, so put it back here */
974         if (g1.status == GAME_EXAMINE || g1.status == GAME_SETUP) { 
975                 gg->game_state = g1.game_state;
976         }
977         gg->status = g1.status;
978
979         /* cope with continuing a game with timeseal that was started without it */
980         gg->wRealTime = gg->wTime * 100;
981         gg->bRealTime = gg->bTime * 100;
982
983         return 0;
984 }
985
986
987 static int ReadGameAttrs_common(FILE * fp, int g,int version)
988 {
989         if (version == 9 || version == 100) {
990                 return ReadGameAttrs_v100(fp, g);
991         }
992
993         return ReadGameAttrs_old(fp, g, version);
994 }
995
996 int ReadGameAttrs_exam(FILE * fp, int g)
997 {
998   int version = 0;
999   char line[MAX_GLINE_SIZE];
1000
1001   fgets(line, MAX_GLINE_SIZE - 1, fp);
1002
1003    if (line[0] == 'v') {
1004     sscanf(line, "%*c %d", &version);
1005
1006     if (version < 5)
1007       return -1;
1008     else
1009       return ReadGameAttrs_common(fp,g,version);
1010   }
1011
1012    else return -1;
1013 }
1014
1015 int ReadGameAttrs(FILE * fp, int g)
1016 {
1017         int version = 0;
1018         char line[MAX_GLINE_SIZE];
1019
1020         fgets(line, MAX_GLINE_SIZE - 1, fp);
1021
1022         if (line[0] == 'v') {
1023                 sscanf(line, "%*c %d", &version);
1024         }
1025         return ReadGameAttrs_common(fp,g,version);
1026 }
1027
1028 int game_read(int g, int wp, int bp)
1029 {
1030   FILE *fp;
1031   int piece;
1032
1033   game_globals.garray[g].white = wp;
1034   game_globals.garray[g].black = bp;
1035 /*  game_globals.garray[g].old_white = -1;
1036     game_globals.garray[g].old_black = -1;
1037 */
1038   game_globals.garray[g].moveListSize = 0;
1039   game_globals.garray[g].game_state.gameNum = g;
1040   strcpy(game_globals.garray[g].white_name, player_globals.parray[wp].name);
1041   strcpy(game_globals.garray[g].black_name, player_globals.parray[bp].name);
1042   if (game_globals.garray[g].type == TYPE_BLITZ) {
1043     game_globals.garray[g].white_rating = player_globals.parray[wp].b_stats.rating;
1044     game_globals.garray[g].black_rating = player_globals.parray[bp].b_stats.rating;
1045   } else if (game_globals.garray[g].type == TYPE_WILD ||
1046              game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1047              game_globals.garray[g].type == TYPE_CAPABLANCA ||
1048              game_globals.garray[g].type == TYPE_GOTHIC) {
1049     game_globals.garray[g].white_rating = player_globals.parray[wp].w_stats.rating;
1050     game_globals.garray[g].black_rating = player_globals.parray[bp].w_stats.rating;
1051   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1052     game_globals.garray[g].white_rating = player_globals.parray[wp].l_stats.rating;
1053     game_globals.garray[g].black_rating = player_globals.parray[bp].l_stats.rating;
1054   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1055     game_globals.garray[g].white_rating = player_globals.parray[wp].bug_stats.rating;
1056     game_globals.garray[g].black_rating = player_globals.parray[bp].bug_stats.rating;
1057   } else {
1058     game_globals.garray[g].white_rating = player_globals.parray[wp].s_stats.rating;
1059     game_globals.garray[g].black_rating = player_globals.parray[bp].s_stats.rating;
1060   }
1061   fp = fopen_p("%s/%c/%s-%s", "r", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1062              player_globals.parray[wp].login, player_globals.parray[bp].login);
1063   if (!fp) {
1064     return -1;
1065   }
1066   for (piece=PAWN; piece < KING; piece++) {
1067     game_globals.garray[g].game_state.holding[0][piece-PAWN]
1068       = game_globals.garray[g].game_state.holding[1][piece-PAWN] = 0;
1069   }
1070   if (ReadGameAttrs(fp, g) < 0) {
1071     fclose(fp);
1072     return -1;
1073   }
1074   fclose(fp);
1075
1076   if (game_globals.garray[g].result == END_ADJOURN
1077       || game_globals.garray[g].result == END_COURTESYADJOURN)
1078     game_globals.garray[g].result = END_NOTENDED;
1079   game_globals.garray[g].status = GAME_ACTIVE;
1080   game_globals.garray[g].startTime = tenth_secs();
1081   game_globals.garray[g].lastMoveTime = game_globals.garray[g].startTime;
1082   game_globals.garray[g].lastDecTime = game_globals.garray[g].startTime;
1083
1084   /* cope with continuing a game with timeseal that was started without it */
1085   game_globals.garray[g].wRealTime = game_globals.garray[g].wTime * 100;
1086   game_globals.garray[g].bRealTime = game_globals.garray[g].bTime * 100;
1087
1088   /* Need to do notification and pending cleanup */
1089   return 0;
1090 }
1091
1092 int game_delete(int wp, int bp)
1093 {
1094   char fname[MAX_FILENAME_SIZE];
1095   char lname[MAX_FILENAME_SIZE];
1096
1097   sprintf(fname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[wp].login[0],
1098           player_globals.parray[wp].login, player_globals.parray[bp].login);
1099   sprintf(lname, "%s/%c/%s-%s", ADJOURNED_DIR, player_globals.parray[bp].login[0],
1100           player_globals.parray[wp].login, player_globals.parray[bp].login);
1101   unlink(fname);
1102   unlink(lname);
1103   return 0;
1104 }
1105
1106 int game_save(int g)
1107 {
1108         FILE *fp;
1109         struct player *wp, *bp;
1110         struct game *gg = &game_globals.garray[g];
1111         char *fname;
1112
1113         wp = &player_globals.parray[gg->white];
1114         bp = &player_globals.parray[gg->black];
1115         asprintf(&fname, "%s/%c/%s-%s", ADJOURNED_DIR, wp->login[0],
1116                  wp->login, bp->login);
1117         fp = fopen_p("%s", "w", fname);
1118         if (!fp) {
1119                 d_printf("CHESSD: Problem opening adjourn file %s-%s for write\n", 
1120                           wp->login, bp->login);
1121                 free(fname);
1122                 return -1;
1123         }
1124         WriteGameFile_v100(fp, g);
1125         fclose(fp);
1126         /* Create link for easier stored game finding */
1127         if (bp->login[0] != wp->login[0]) {
1128                 char *lname;
1129                 asprintf(&lname, "%s/%c/%s-%s", ADJOURNED_DIR, bp->login[0],
1130                          wp->login, bp->login);
1131                 link(fname, lname);
1132                 free(lname);
1133         }
1134         free(fname);
1135         return 0;
1136 }
1137
1138 static long OldestHistGame(char *login)
1139 {
1140         FILE *fp;
1141         long when;
1142         
1143         fp = fopen_p("%s/player_data/%c/%s.%s", "r", STATS_DIR,
1144                      login[0], login, STATS_GAMES);
1145         if (fp == NULL) {
1146                 fp = fopen_p("%s/player_data/%c/.rem.%s.%s", "r", STATS_DIR,
1147                              login[0], login, STATS_GAMES);
1148         }
1149         if (!fp) return 0;
1150
1151         fscanf(fp, "%*d %*c %*d %*c %*d %*s %*s %*d %*d %*d %*d %*s %*s %ld",
1152                &when);
1153         fclose(fp);
1154         return when;
1155 }
1156
1157 static void RemoveHistGame(char *file, int maxlines)
1158 {
1159   FILE *fp;
1160   char GameFile[MAX_FILENAME_SIZE];
1161   char Opponent[MAX_LOGIN_NAME];
1162   char line[MAX_LINE_SIZE];
1163   long When, oppWhen;
1164   int count = 0;
1165
1166   fp = fopen_p("%s", "r", file);
1167   if (fp == NULL)
1168     return;
1169
1170   fgets(line, MAX_LINE_SIZE - 1, fp);
1171   sscanf(line, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1172          Opponent, &When);
1173   count++;
1174
1175   while (!feof(fp)) {
1176     fgets(line, MAX_LINE_SIZE - 1, fp);
1177     if (!feof(fp))
1178       count++;
1179   }
1180   fclose(fp);
1181
1182   stolower(Opponent);
1183   if (count > maxlines) {
1184     truncate_file(file, maxlines);
1185
1186     oppWhen = OldestHistGame(Opponent);
1187     if (oppWhen > When || oppWhen <= 0L) {
1188       sprintf(GameFile, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1189       unlink(GameFile);
1190     }
1191   }
1192 }
1193
1194 void RemHist(char *who)
1195 {
1196         FILE *fp;
1197         char Opp[MAX_LOGIN_NAME];
1198         long When, oppWhen;
1199
1200         fp = fopen_p("%s/player_data/%c/%s.%s", "r", 
1201                      STATS_DIR,
1202                      who[0], who, STATS_GAMES);
1203         if (!fp) {
1204                 return;
1205         }
1206         while (!feof(fp)) {
1207                 fscanf(fp, "%*d %*c %*d %*c %*d %s %*s %*d %*d %*d %*d %*s %*s %ld",
1208                        Opp, &When);
1209                 stolower(Opp);
1210                 oppWhen = OldestHistGame(Opp);
1211                 if (oppWhen > When || oppWhen <= 0L) {
1212                         char *fName;
1213                         asprintf(&fName, "%s/%ld/%ld", HISTORY_DIR, When % 100, When);
1214                         unlink(fName);
1215                         free(fName);
1216                 }
1217         }
1218         fclose(fp);
1219 }
1220
1221 static void write_g_out(int g, char *file, int maxlines, int isDraw,
1222                           char *EndSymbol, char *name, time_t *now)
1223 {
1224   FILE *fp;
1225   int wp, bp;
1226   int wr, br;
1227   char type[4];
1228   char tmp[2048];
1229   char *ptmp = tmp;
1230   char cResult;
1231   int count = -1;
1232   char *goteco;
1233
1234   wp = game_globals.garray[g].white;
1235   bp = game_globals.garray[g].black;
1236
1237   if (game_globals.garray[g].private) {
1238     type[0] = 'p';
1239   } else {
1240     type[0] = ' ';
1241   }
1242   if (game_globals.garray[g].type == TYPE_BLITZ) {
1243     wr = player_globals.parray[wp].b_stats.rating;
1244     br = player_globals.parray[bp].b_stats.rating;
1245     type[1] = 'b';
1246   } else if (game_globals.garray[g].type == TYPE_WILD ||
1247              game_globals.garray[g].type == TYPE_KNIGHTMATE ||
1248              game_globals.garray[g].type == TYPE_CAPABLANCA ||
1249              game_globals.garray[g].type == TYPE_GOTHIC) {
1250     wr = player_globals.parray[wp].w_stats.rating;
1251     br = player_globals.parray[bp].w_stats.rating;
1252     type[1] = 'w';
1253   } else if (game_globals.garray[g].type == TYPE_STAND) {
1254     wr = player_globals.parray[wp].s_stats.rating;
1255     br = player_globals.parray[bp].s_stats.rating;
1256     type[1] = 's';
1257   } else if (game_globals.garray[g].type == TYPE_LIGHT) {
1258     wr = player_globals.parray[wp].l_stats.rating;
1259     br = player_globals.parray[bp].l_stats.rating;
1260     type[1] = 'l';
1261   } else if (game_globals.garray[g].type == TYPE_BUGHOUSE) {
1262     wr = player_globals.parray[wp].bug_stats.rating;
1263     br = player_globals.parray[bp].bug_stats.rating;
1264     type[1] = 'B';
1265   } else {
1266     wr = 0;
1267     br = 0;
1268     if (game_globals.garray[g].type == TYPE_NONSTANDARD)
1269       type[1] = 'n';
1270     else
1271       type[1] = 'u';
1272   }
1273   if (game_globals.garray[g].rated) {
1274     type[2] = 'r';
1275   } else {
1276     type[2] = 'u';
1277   }
1278   type[3] = '\0';
1279
1280   fp = fopen_s(file, "r");
1281   if (fp) {
1282     while (!feof(fp))
1283       fgets(tmp, 1024, fp);
1284     sscanf(ptmp, "%d", &count);
1285     fclose(fp);
1286   }
1287   count = (count + 1) % 100;
1288
1289   fp = fopen_s(file, "a");
1290   if (!fp)
1291     return;
1292
1293   goteco = getECO(g);
1294
1295 /* Counter Result MyRating MyColor OppRating OppName [pbr 2 12 2 12] ECO End Date */
1296   if (name == player_globals.parray[wp].name) {
1297     if (isDraw)
1298       cResult = '=';
1299     else if (game_globals.garray[g].winner == WHITE)
1300       cResult = '+';
1301     else
1302       cResult = '-';
1303
1304     fprintf(fp, "%d %c %d W %d %s %s %d %d %d %d %s %s %ld\n",
1305             count, cResult, wr, br, player_globals.parray[bp].name, type,
1306             game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1307             game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1308             goteco,
1309             EndSymbol,
1310             (long) *now);
1311   } else {
1312     if (isDraw)
1313       cResult = '=';
1314     else if (game_globals.garray[g].winner == BLACK)
1315       cResult = '+';
1316     else
1317       cResult = '-';
1318
1319     fprintf(fp, "%d %c %d B %d %s %s %d %d %d %d %s %s %ld\n",
1320             count, cResult, br, wr, player_globals.parray[wp].name, type,
1321             game_globals.garray[g].wInitTime, game_globals.garray[g].wIncrement,
1322             game_globals.garray[g].bInitTime, game_globals.garray[g].bIncrement,
1323             goteco,
1324             EndSymbol,
1325             (long) *now);
1326   }
1327   fclose(fp);
1328
1329   RemoveHistGame(file, maxlines);
1330 }
1331
1332 /* Test if entry is present - 1 yes 0 no */
1333 /* returns -1 if an error */
1334
1335 char get_journalgame_type(int p,char* fname,char slot)
1336
1337 {
1338   struct player *pp = &player_globals.parray[p];
1339   char cur_slot;
1340   char type[4];
1341
1342   FILE* fp = fopen_s(fname,"r");
1343   if (!fp) {
1344     d_printf("Corrupt journal file! %s\n",fname);
1345     pprintf (p, "The journal file is corrupt! See an admin.\n");
1346     return 0;
1347   }
1348
1349   while (!feof(fp)) {
1350     if (fscanf(fp, "%c %*s %*d %*s %*d %s %*d %*d %*s %*s %*s\n",
1351        &cur_slot,type) != 2) {
1352       d_printf( "CHESSD: Error in journal info format for player %s.\n",
1353         pp->name);
1354       pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1355       return '\0';
1356     }
1357     if (cur_slot == slot) {
1358       fclose (fp);
1359       if (type[0] == 'p')
1360         return type[1];
1361       else
1362         return type[0];
1363     }
1364   }
1365   fclose (fp);
1366   return '0';
1367 }
1368
1369
1370 /* Returns 1 if successful */
1371
1372 int removejournalitem(int p, char slot,FILE* fp,char* fname, int* empty)
1373
1374 {
1375         FILE* fp_new;
1376         int found = 0;
1377         struct journal* j;
1378
1379         *empty = 1;
1380         fp_new = fopen_s(fname, "w");
1381         if (!fp_new) {
1382                 d_printf("Can't write to journal %s!\n",fname);
1383                 pprintf (p, "Was unable to write to the file! See an admin.\n");
1384                 return 0;
1385         }
1386         j = (struct journal*) malloc(sizeof(struct journal));
1387         while (!feof(fp)) {
1388                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1389                            &j->slot,
1390                            j->WhiteName,
1391                            &j->WhiteRating,
1392                            j->BlackName,
1393                            &j->BlackRating,
1394                            j->type,
1395                            &j->t, &j->i,
1396                            j->eco,
1397                            j->ending,
1398                            j->result) != 11) {
1399                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1400                         pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1401                         fclose(fp_new);
1402                         free (j);
1403                         return 0;
1404                 }
1405                 if (slot != j->slot) {
1406                         *empty = 0;
1407                         fprintf(fp_new, "%c %s %d %s %d %s %d %d %s %s %s\n",
1408                                 j->slot,
1409                                 j->WhiteName,
1410                                 j->WhiteRating,
1411                                 j->BlackName,
1412                                 j->BlackRating,
1413                                 j->type,
1414                                 j->t, j->i,
1415                                 j->eco,
1416                                 j->ending,
1417                                 j->result);
1418                 } else
1419                         found = 1;
1420         }
1421         fclose(fp_new);
1422         free (j);
1423         return found;
1424 }
1425
1426 /* Find from_spot in journal list - return 0 if corrupted */
1427 int journal_get_info(int p, char from_spot,struct journal* j, char *fname)
1428 {
1429         FILE *fp;
1430
1431         fp = fopen_s(fname, "r");
1432         if (!fp) {
1433                 d_printf("Corrupt journal file! %s\n",fname);
1434                 pprintf (p, "The journal file is corrupt! See an admin.\n");
1435                 return 0;
1436         }
1437         while (!feof(fp)) {
1438                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1439                            &j->slot,
1440                            j->WhiteName,
1441                            &j->WhiteRating,
1442                            j->BlackName,
1443                            &j->BlackRating,
1444                            j->type,
1445                            &j->t, &j->i,
1446                            j->eco,
1447                            j->ending,
1448                            j->result) != 11) {
1449                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1450                         pprintf(p, "The journal file is corrupt! Error in internal format.\n");
1451                         fclose(fp);
1452                         return 0;
1453                 }
1454                 if (tolower(j->slot) == from_spot) {
1455                         fclose(fp);
1456                         return 1;
1457                 }
1458         }
1459         fclose(fp);
1460         return 0;
1461 }
1462
1463 void addjournalitem(int p,struct journal* j_add, char* fname)
1464 {
1465         struct journal* j_cur;
1466         int have_output=0;
1467         char fname2[MAX_FILENAME_SIZE];
1468         FILE *fp;
1469         FILE *fp2;
1470
1471         strcpy (fname2,fname);
1472         strcat (fname2,".w");
1473         fp2 = fopen_s(fname2, "w");
1474         if (!fp2) {
1475                 d_printf( "CHESSD: Problem opening file %s for write\n", fname);
1476                 pprintf (p, "Couldn't update journal! Report this to an admin.\n");
1477                 return;
1478         } 
1479         fp = fopen_s(fname, "r");
1480         if (!fp) { /* Empty? */
1481                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1482                         j_add->slot, j_add->WhiteName, j_add->WhiteRating,
1483                         j_add->BlackName, j_add->BlackRating,
1484                         j_add->type, j_add->t, j_add->i, j_add->eco, j_add->ending,
1485                         j_add->result);
1486                 fclose (fp2);
1487                 rename (fname2, fname);
1488                 return;
1489         } else {
1490                 j_cur = (struct journal*) malloc(sizeof(struct journal));
1491                 while (!feof(fp)) {
1492                         if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1493                                    &j_cur->slot,
1494                                    j_cur->WhiteName,
1495                                    &j_cur->WhiteRating,
1496                                    j_cur->BlackName,
1497                                    &j_cur->BlackRating,
1498                                    j_cur->type,
1499                                    &j_cur->t, &j_cur->i,
1500                                    j_cur->eco,
1501                                    j_cur->ending,
1502                                    j_cur->result) != 11) {
1503                                 d_printf( "CHESSD: Error in journal info format - aborting. %s\n", fname);
1504                                 free (j_cur);
1505                                 fclose(fp);
1506                                 fclose(fp2);
1507                                 return;
1508                         }
1509                         if ((j_cur->slot >= j_add->slot) && (!have_output)) {
1510                                 
1511                                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1512                                         j_add->slot,
1513                                         j_add->WhiteName,
1514                                         j_add->WhiteRating,
1515                                         j_add->BlackName,
1516                                         j_add->BlackRating,
1517                                         j_add->type,
1518                                         j_add->t, j_add->i,
1519                                         j_add->eco,
1520                                         j_add->ending,
1521                                         j_add->result);
1522                                 have_output = 1;
1523
1524                         } 
1525                         if (j_cur->slot != j_add->slot) {
1526
1527                                 fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1528                                         j_cur->slot,
1529                                         j_cur->WhiteName,
1530                                         j_cur->WhiteRating,
1531                                         j_cur->BlackName,
1532                                         j_cur->BlackRating,
1533                                         j_cur->type,
1534                                         j_cur->t, j_cur->i,
1535                                         j_cur->eco,
1536                                         j_cur->ending,
1537                                         j_cur->result);
1538                         }
1539                 }
1540                 
1541                 if (!have_output) { /* Haven't written yet */
1542                         fprintf(fp2, "%c %s %d %s %d %s %d %d %s %s %s\n",
1543                                 j_add->slot,
1544                                 j_add->WhiteName,
1545                                 j_add->WhiteRating,
1546                                 j_add->BlackName,
1547                                 j_add->BlackRating,
1548                                 j_add->type,
1549                                 j_add->t, j_add->i,
1550                                 j_add->eco,
1551                                 j_add->ending,
1552                                 j_add->result);
1553                 }
1554                 free (j_cur);
1555                 fclose(fp);
1556                 fclose(fp2);
1557                 rename(fname2, fname);
1558                 return;
1559         }
1560
1561
1562 int pjournal(int p, int p1, char *fname)
1563 {
1564         FILE *fp;
1565         struct journal* j;
1566         
1567         fp = fopen_s(fname, "r");
1568         if (!fp) {
1569                 pprintf(p, "Sorry, no journal information available.\n");
1570                 return COM_OK;
1571         }
1572
1573         j = (struct journal*) malloc(sizeof(struct journal));
1574         pprintf(p, "Journal for %s:\n", player_globals.parray[p1].name);
1575         pprintf(p, "   White         Rating  Black         Rating  Type         ECO End Result\n");
1576         while (!feof(fp)) {
1577                 if (fscanf(fp, "%c %s %d %s %d %s %d %d %s %s %s\n",
1578                            &j->slot,
1579                            j->WhiteName,
1580                            &j->WhiteRating,
1581                            j->BlackName,
1582                            &j->BlackRating,
1583                            j->type,
1584                            &j->t, &j->i,
1585                            j->eco,
1586                            j->ending,
1587                            j->result) != 11) {
1588                         d_printf( "CHESSD: Error in journal info format. %s\n", fname);
1589                         fclose(fp);
1590                         free(j);
1591                         return COM_OK;
1592                 }
1593                 j->WhiteName[13] = '\0';         /* only first 13 chars in name */
1594                 j->BlackName[13] = '\0';
1595                 pprintf(p, "%c: %-13s %4d    %-13s %4d    [%3s%3d%4d] %s %3s %-7s\n",
1596                         j->slot, j->WhiteName, j->WhiteRating,
1597                         j->BlackName, j->BlackRating,
1598                         j->type, j->t / 600, j->i / 10, j->eco, j->ending,
1599                         j->result);
1600         }
1601         free (j);
1602         fclose(fp);
1603         return COM_OK;
1604 }
1605
1606 int pgames(int p, int p1, char *fname)
1607 {
1608         FILE *fp;
1609         time_t t;
1610         int MyRating, OppRating;
1611         int wt, wi, bt, bi;
1612         char OppName[MAX_LOGIN_NAME + 1];
1613         char type[100];
1614         char eco[100];
1615         char ending[100];
1616         char MyColor[2];
1617         int count;
1618         char result[2];
1619         
1620         fp = fopen_s(fname, "r");
1621         if (!fp) {
1622                 pprintf(p, "Sorry, no game information available.\n");
1623                 return COM_OK;
1624         }
1625         pprintf(p, "History for %s:\n", player_globals.parray[p1].name);
1626         pprintf(p, "                  Opponent      Type         ECO End Date\n");
1627         while (!feof(fp)) {
1628                 if (fscanf(fp, "%d %s %d %s %d %s %s %d %d %d %d %s %s %ld\n",
1629                            &count,
1630                            result,
1631                            &MyRating,
1632                            MyColor,
1633                            &OppRating,
1634                            OppName,
1635                            type,
1636                            &wt, &wi,
1637                            &bt, &bi,
1638                            eco,
1639                            ending,
1640                            (long *) &t) != 14) {
1641                         d_printf( "CHESSD: Error in games info format. %s\n", fname);
1642                         fclose(fp);
1643                         return COM_OK;
1644                 }
1645                 OppName[13] = '\0';             /* only first 13 chars in name */
1646                 pprintf(p, "%2d: %s %4d %s %4d %-13s [%3s%3d%4d] %s %3s %s",
1647                         count, result, MyRating, MyColor,
1648                         OppRating, OppName,
1649                         type, wt / 600, wi / 10, eco, ending,
1650                         ctime(&t));
1651         }
1652         fclose(fp);
1653         return COM_OK;
1654 }
1655
1656 void game_write_complete(int g, int isDraw, char *EndSymbol)
1657 {
1658         char fname[MAX_FILENAME_SIZE];
1659         int wp = game_globals.garray[g].white, bp = game_globals.garray[g].black;
1660         time_t now = time(NULL);
1661         int Result;
1662         FILE *fp = NULL;
1663         
1664         do {
1665                 if (fp) {
1666                         fclose(fp);
1667                         now++;
1668                 }
1669                 sprintf(fname, "%s/%ld/%ld", HISTORY_DIR, (long) now % 100, (long) now);
1670                 fp = fopen_s(fname, "r");
1671         } while (fp);   /* terminates when the file doesn't exist */
1672         
1673         fp = fopen_s(fname, "w");
1674         
1675         if (fp) {
1676                 WriteGameFile_v100(fp, g);
1677                 fclose(fp);
1678         } else {
1679                 d_printf( "Trouble writing history file %s", fname);
1680         }
1681         
1682         sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1683                 player_globals.parray[wp].login[0], player_globals.parray[wp].login, STATS_GAMES);
1684         write_g_out(g, fname, 40, isDraw, EndSymbol, player_globals.parray[wp].name, &now);
1685         sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR,
1686                 player_globals.parray[bp].login[0], player_globals.parray[bp].login, STATS_GAMES);
1687         write_g_out(g, fname, 40, isDraw, EndSymbol, player_globals.parray[bp].name, &now);
1688         
1689         if (isDraw)
1690                 Result = -1;
1691         else if (game_globals.garray[g].winner == WHITE)
1692                 Result = 1;
1693         else
1694                 Result = 0;
1695         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);
1696         if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1697                 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].black].name,Result,game_globals.garray[g].rated);
1698         if (isDraw)
1699                 Result = -1;
1700         else if (game_globals.garray[g].winner == BLACK)
1701                 Result = 1;
1702         else
1703                 Result = 0;
1704         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);
1705         if ((CheckPFlag(bp, PFLAG_REG)) && (CheckPFlag(wp, PFLAG_REG)) && (game_globals.garray[g].type != TYPE_WILD))
1706                 game_save_playerratio(fname,player_globals.parray[game_globals.garray[g].white].name,Result,game_globals.garray[g].rated);
1707 }
1708
1709 int game_count(void)
1710 {
1711         int g, count = 0;
1712         
1713         for (g = 0; g < game_globals.g_num; g++) {
1714                 if ((game_globals.garray[g].status == GAME_ACTIVE) || (game_globals.garray[g].status == GAME_EXAMINE)
1715                     || (game_globals.garray[g].status == GAME_SETUP))
1716                         count++;
1717         }
1718         if (count > command_globals.game_high)
1719                 command_globals.game_high = count;
1720         return count;
1721 }
1722
1723 static int check_kings(struct game_state_t *gs)
1724 {
1725         /* Function written 3/28/96 by Figbert to check for 1 king each side only! */
1726         int blackking = 0, whiteking = 0;
1727         int f, r;
1728         
1729         
1730         for (f = 0; f < gs->files; f++) {
1731                 for (r = 0; r < gs->ranks; r++) {
1732                         if (gs->board[f][r] == B_KING) blackking++;
1733                         if (gs->board[f][r] == W_KING) whiteking++;
1734                 }
1735         }
1736         
1737         if ((blackking == 1 || blackking == 2 && !strcmp(gs->variant, "spartan")) && whiteking == 1) return 0; /* Perfect! */
1738         
1739         return -1;
1740 }