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