1    | /*
2    |  * $Log: summary.c,v $
3    |  * Revision 1.17  2003/07/15 22:47:07  millis
4    |  * Fix Bug 185 (call smail for each email individually)
5    |  *
6    |  * Revision 1.16  2003/02/12 07:46:07  nzmb
7    |  * Fixed several bugs in the concession handling code, including a severe one
8    |  *
9    |  * that could, in rare circumstances, lead to an undeserved concession being
10   |  * granted
11   |  *
12   |  * Revision 1.15  2003/01/18 23:57:43  millis
13   |  * Updated from USTV
14   |  *
15   |  * Revision 1.14  2002/08/27 22:27:58  millis
16   |  * Updated for automake/autoconf functionality
17   |  *
18   |  * Revision 1.13  2002/06/12 21:08:30  millis
19   |  * second value in player record is hex, thus us %x and not %d
20   |  *
21   |  * Revision 1.12  2002/04/23 11:08:35  miller
22   |  * Spelling change "Svmebody" -> "Somebody"
23   |  *
24   |  * Revision 1.11  2002/04/18 04:44:34  greg
25   |  * Added the following commands:
26   |  * - unstart
27   |  * - set secret
28   |  * - set [prflist|prfrand|prfboth]
29   |  *
30   |  * Fixed Set Absence so that "to" is not case sensitive
31   |  *
32   |  * Fixed Quiet games so that new players are announced
33   |  * before the game starts
34   |  *
35   |  * Fixed ascii_to_ded.c so thatit no longer generates an
36   |  * error when compiled
37   |  *
38   |  * Revision 1.10  2002/03/05 23:38:50  miller
39   |  * Replaced lost changes revison 1.8
40   |  *
41   |  * Revision 1.9  2002/03/05 23:05:47  miller
42   |  * Fixed Machiavelli summary problem
43   |  *
44   |  * Revision 1.8  2002/02/03 03:45:43  nzmb
45   |  * Fixed bug in plyrdata.c and readded some concession
46   |  *  handler code to summary.c
47   |  *
48   |  * Revision 1.7  2001/10/20 12:11:16  miller
49   |  * Merged in changes from DEMA and USTV CVS
50   |  *
51   |  * Revision 1.6.2.1  2001/10/15 22:30:45  ustv
52   |  * Added display of duality stuff
53   |  *
54   |  * Revision 1.6  2001/08/18 07:11:36  nzmb
55   |  * Show concessions if applicable
56   |  * 
57   |  * Revision 1.5  2001/07/15 09:20:27  greg
58   |  * added support for game directories in a sub directory
59   |  *
60   |  * Revision 1.4  2001/07/01 23:19:29  miller
61   |  * Add storm table
62   |  *
63   |  * Revision 1.3  2001/03/16 00:06:17  miller
64   |  * Fix 'C' parameter so that next one is not squashed
65   |  *
66   |  * Revision 1.2  2000/11/14 14:27:37  miller
67   |  * Accept -C option to specify config_dir
68   |  * Handle blind summaries differently (if not master)
69   |  *
70   |  * Revision 1.1  1998/02/28 17:49:42  david
71   |  * Initial revision
72   |  *
73   |  * Revision 1.1  1996/10/20 12:29:45  rpaar
74   |  * Morrolan v9.0
75   |  */
76   | 
77   | /*  summary.c -- Generate a game summary file
78   |  *  Copyright 1987, Lowe.
79   |  *
80   |  *  Diplomacy is a trademark of the Avalon Hill Game Company, Baltimore,
81   |  *  Maryland, all rights reserved; used with permission.
82   |  *
83   |  *  Redistribution and use in source and binary forms are permitted
84   |  *  provided that it is for non-profit purposes, that this and the 
85   |  *  above notices are preserved and that due credit is given to Mr.
86   |  *  Lowe.
87   |  */
88   | 
89   | #include <stdlib.h>
90   | #include <string.h>
91   | 
92   | #include "config.h"
93   | #include "dip.h"
94   | #include "porder.h"
95   | #include "mach.h"
96   | #include "conf.h"
97   | #include "functions.h"
98   | #include "diplog.h"
99   | 
100  | static int variant = 0;		/* The currently loaded variant */
101  | 
102  | #define MAXADDR 100
103  | #define MAXNAME 80
104  | #define MAXTURN 200
105  | #define MAXPHASE 10
106  | #define YPL 10			/* Ten years per line */
107  | #define CPL 36			/* 36 supply centers per line */
108  | 
109  | static char addrs[MAXUSER][MAXADDR], addr[MAXADDR];
110  | static char names[MAXUSER][MAXNAME];
111  | /* this was called "phase", but that conflicted with a function of
112  |  * the same name, so i renamed it -- nw Sun Jun  8 00:57:19 GMT 1997 */
113  | static char phaze[MAXTURN][MAXPHASE];
114  | static char owner[MAXTURN][NPROV];
115  | static char cownr[MAXTURN][NPROV];
116  | static char homes[MAXTURN][NPROV];
117  | static int units[MAXTURN][MASTER + 1];
118  | static int centers[MAXTURN][MASTER + 1];
119  | static int player[MAXTURN][MASTER + 1];
120  | static short prov[NPROV], city[NPROV];
121  | static int sqrs[YPL];
122  | 
123  | static FILE *tfp;
124  | 
125  | 
126  | static char outc[] =
127  | {' ', '*', '&', '+', '%', '^', '$', '#', '!', '~', '@', '(',
128  |  ')', '=', '-', '_', '|', '\\', '/', '?', '>', '<', '[', ']',
129  |  '{', '}', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
130  |  'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
131  |  'w', 'x', 'y', 'z'};
132  | static int outn[sizeof(outc)];
133  | 
134  | /* todo: move these globals to global.c */
135  | FILE *mfp;			/* Master File pointer */
136  | int gflg = 0;			/* Nonzero indicates gunboat mode */
137  | int mflg = 0;			/* Nonzero indicates master mode */
138  | int bflg = 0;			/* Nonzero indicates blind mode  */
139  | int main(int argc, char **argv)
140  | {
141  | 
142  | 	/*
143  | 	 *  Initialize, read the data structures off the input files.
144  | 	 */
145  | 
146  | 	int i, j, k, n, m, p;
147  | 	int nprov, cs, ce, ncity, lname = 0, turn, years = 0;
148  | 	int ix[60], sy, gotent;
149  | 	char string[30], *s, *t, *name;
150  |         char exe_name[100];
151  | 	int lflg=0;
152  | 	int tooMany = 0; /* Set to 1 if too many builds were made */
153  | 
154  | 	variant = 1;
155  | 	name = NULL;
156  | 	log_fp = stderr;
157  | 	gotent = 0;
158  | 
159  | 	for (i = 1; i < argc; i++) {
160  | 		if (*argv[i] == '-') {
161  | 			for (s = argv[i] + 1; *s; s++) {
162  | 				switch (*s) {
163  | 				case 'g':
164  | 					gflg++;
165  | 					lname = mflg ? 0 : sizeof("Somebody #00");
166  | 					break;
167  | 
168  | 				case 'l':
169  | 					lflg++;
170  | 					break;
171  | 
172  | 				case 'm':
173  | 					mflg++;
174  | 					lname = 0;
175  | 					break;
176  | 
177  | 				case 'q':
178  | 					qflg++;
179  | 					break;
180  | 	
181  | 				 case 'b':
182  |                                         bflg++;
183  |                                         break;
184  | 
185  | 				case 'C':
186  |                                   if (*++s)
187  |                                         CONFIG_DIR = s;
188  |                                   else if (i+1 < argc)
189  |                                         CONFIG_DIR = argv[i+1];
190  |                                   else {
191  |                                         fprintf(stderr, "Directory must follow C option.\n");
192  |                                         goto usage;
193  |                                   }
194  |                                   i++;
195  |                                   s = " ";
196  |                                 break;
197  | 
198  | 
199  | 				case 'c':
200  | 					/* ignore 'c' options */
201  | 					break;
202  | 
203  | 
204  | 				case 'v':
205  | 					if (isdigit(*(s + 1))) {
206  | 						variant = atoi(++s);
207  | 						while (isdigit(*s))
208  | 							s++;
209  | 						s--;
210  | 					} else {
211  | 						if (++i < argc) {
212  | 							variant = atoi(argv[i]);
213  | 						} else {
214  | 							fprintf(log_fp, "sum: Missing variant number.\n");
215  | 							goto usage;
216  | 						}
217  | 					}
218  | 					break;
219  | 
220  | 				default:
221  | 					fprintf(log_fp, "sum: Unknown option '%c'.\n", *s);
222  | 					goto usage;
223  | 				}
224  | 			}
225  | 		} else {
226  | 			if (name)
227  | 				goto usage;
228  | 			name = argv[i];
229  | 		}
230  | 	}
231  | 
232  |        /*
233  |          * read in the configuration, first from config file,
234  |          * finally from the command line
235  |          * TODO: fold in command line processing for configuration
236  |          * variables with regular argument processing.
237  |          */
238  |         conf_init();
239  |         conf_readfile(CONFIG_DIR, CONFIG_FILE);
240  |         conf_cmdline(argc, argv);
241  | 
242  |         if (lflg) {
243  | 	    if (!(log_fp = fopen(LOG_FILE, "a"))) {
244  |                     fprintf(stderr, "sum: Unable to open log file.\n");
245  |                     exit(1);
246  | 	    }
247  |         }
248  | 
249  |         mfp = fopen(MASTER_FILE, "r");
250  |         sprintf(exe_name,"%s-%s", JUDGE_CODE, "dip");
251  | 
252  |         OPENDIPLOG(exe_name);
253  |         DIPINFO("Started summary");
254  | 
255  | 
256  | 	/*
257  | 	 *  We only want to generate the master summary if the gunboat flag
258  | 	 *  is enabled.  Otherwise the master can look at the regular players'
259  | 	 *  summary.  
260  | 	 */
261  | 
262  | 	if (mflg) bflg = 0; /* Master is never blind! */
263  | 
264  | 	if (mflg && gflg) {
265  | 		gflg = 0;
266  | 	} else {
267  | 		mflg = 0;
268  | 	}
269  | 
270  | 	if (!variant) {
271  | 		fprintf(log_fp, "sum: Invalid numeric variant specified.\n");
272  | 		goto usage;
273  | 	}
274  | 	if (!name) {
275  | 	      usage:
276  | 		fprintf(log_fp, "sum: Usage: %s [-c<CONFIG>=<value>] [-C<directory>] [-mbglv#] game\n", argv[0]);
277  | 		fprintf(log_fp, "    -c<CONFIG>=<value>: Set <CONFIG> setting to <value>.\n");
278  |                 fprintf(log_fp, "    C: Set directory for dip.conf file.\n");
279  | 		fprintf(log_fp, "    m: Master, don't hide identities in msummary\n");
280  | 	        fprintf(log_fp, "    b: Blind game, limits output\n");
281  | 		fprintf(log_fp, "    g: Gunboat, hide identities.\n");
282  | 		fprintf(log_fp, "    l: Send errors to log file.\n");
283  | 		fprintf(log_fp, "    v: Specify alternate variant.\n");
284  | 		exit(1);
285  | 	}
286  | 
287  | 
288  | 	/*
289  | 	 *  Spin through the Gxxx files to find variant 
290  | 	 */
291  | 
292  | 	for (turn = 1; turn < MAXTURN; turn++) {
293  | 		/* TODO: 0 flag for printf(3) is not POSIX, once someone confirms that
294  | 		 * '0' is the default pad for integers, the 0 should be gotten rid of
295  | 		 */
296  | 		sprintf(line, "%s%s/G%03d", GAME_DIR, name, turn);
297  | 		if (!(ifp = fopen(line, "r")))
298  | 			break;
299  | 		fclose(ifp);
300  | 	}
301  | 	if (turn <= 2) {
302  | 		fprintf(log_fp, "sum: Attempt to obtain summary before 1st turn: %s\n", name);
303  | 		exit(1);
304  | 	}
305  | 	/* TODO: 0 flag for printf(3) is not POSIX, once someone confirms that
306  | 	 * '0' is the default pad for integers, the 0 should be gotten rid of */
307  | 	sprintf(line, "%s%s/G%03d", GAME_DIR, name, turn - 1);
308  | 	if ((ifp = fopen(line, "r"))) {
309  | 		/*
310  | 		 *  Skip to dipent
311  | 		 */
312  | 		while (fgets(line, sizeof(line), ifp) && !(line[0] == '-' && line[1] == '2'));
313  | 		getdipent(ifp);
314  | 		fclose(ifp);
315  | 	}
316  | 	variant = dipent.variant;
317  | 
318  | 	newdipent(name, variant);
319  | 
320  | 	variant = dipent.variant;
321  | 
322  | /*** TODO: Make summary.c and po-init.c use the same code!!! 
323  |      I am fixing a bug due only to this now! MLM 05/03/2002 ***/
324  | 
325  | 	sprintf(line, "map.%d", variant);
326  | 	if ((ifp = fopen(line, "r")) == NULL) {
327  | 		fprintf(log_fp, "sum: Error opening map data file %s.\n", line);
328  | 		exit(1);
329  | 	}
330  | 	if (fread(&npr, sizeof(npr), 1, ifp) != 1 ||
331  | 	    fread(&hp, sizeof(hp), 1, ifp) != 1 ||
332  | 	    fread(&nv, sizeof(nv), 1, ifp) != 1) {
333  | 		fprintf(log_fp, "sum: Error reading map file npr/hp/nv, %s.\n", line);
334  | 		return E_FATAL;
335  | 	}
336  | 	maxheap = hp + 2048;
337  | 	if (!(heap = (unsigned char *) malloc(maxheap + 10))) {
338  | 		fprintf(log_fp, "sum: Unable to allocate heap.\n");
339  | 		return E_FATAL;
340  | 	}
341  | 
342  | 	for (i = 1; i <= npr; i++) {
343  | 		if (fread(cmap, sizeof(cmap[0]), CMAP_SIZE, ifp) != CMAP_SIZE) {
344  | 			fprintf(log_fp, "sum: cmap read error city = %d, %s.\n", i, line);
345  | 			return E_FATAL;
346  | 		}
347  | 		pr[i].name = (char *) &heap[cmap[CMAP_NAME]];
348  | 		pr[i].move = (unsigned char *) &heap[cmap[CMAP_MOVE]];
349  | 		pr[i].type = cmap[CMAP_TYPE];
350  | 		pr[i].flags = cmap[CMAP_FLAG];
351  | 		pr[i].type2 = cmap[CMAP_TYPE2];
352  | 		n = islower(pr[i].type) ? 0 : power(pr[i].type);
353  | 		pr[i].owner = n;
354  | 		pr[i].cown = n;
355  | 		pr[i].home = n;
356  | 		pr[i].unit = 0;
357  | 		pr[i].gunit = 0;
358  | 	}
359  | 
360  | 	if ((i = fread(heap, sizeof(unsigned char), hp, ifp)) != hp) {
361  | 		fprintf(log_fp, "sum: cmap heap read error, %d of %d, %s.\n", i, hp, line);
362  | 		return E_FATAL;
363  | 	}
364  | 	if (nv > MAXVINC) {
365  | 		fprintf(log_fp, "sum: Maximum variable income exceeded.\n");
366  | 		return E_FATAL;
367  | 	}
368  | 	if (nv > 0) {
369  | 		if ((i = fread(vincome, sizeof(vincome[0]), nv, ifp)) != nv) {
370  | 			fprintf(log_fp, "sum: cmap income read error, %d of %d, %s.\n", i, nv, line);
371  | 			return E_FATAL;
372  | 		}
373  | 		if ((i = fread(ftab, sizeof(ftab), 1, ifp)) != 1) {
374  | 			fprintf(log_fp, "sum: cmap ftab read error, %d. %s\n", i, line);
375  | 			return E_FATAL;
376  | 		}
377  | 		if ((i = fread(ptab, sizeof(ptab), 1, ifp)) != 1) {
378  | 			fprintf(log_fp, "sum: cmap ptab read error, %d. %s\n", i, line);
379  | 			return E_FATAL;
380  | 		}
381  | 		if ((i = fread(stab, sizeof(stab), 1, ifp)) != 1) {
382  |                         fprintf(log_fp, "sum: cmap stab read error, %d. %s\n", i, line);
383  |                         return E_FATAL;
384  |                 }
385  | 		if ((i = fread(permitted_units, sizeof(permitted_units), 1, ifp)) != 1) {
386  |                                 fprintf(rfp, "cmap permitted_units read error, %d. %s\n", i, line);
387  |                                 return E_FATAL;
388  |                         }
389  |                         if ((i = fread(&initial_money, sizeof(initial_money), 1, ifp)) != 1) {
390  |                                 fprintf(rfp, "cmap initial_money read error, %d. %s\n", i, line);
391  |                                 return E_FATAL;
392  |                         }                                                                                              
393  | 	}
394  | /* Now read colonial hk, gw and rw settings */	
395  |         if (fread(&nhk, sizeof(nhk),1, ifp) != 1) {
396  |             /* Serious error, what to do? */
397  |             nhk = 0;
398  |         }
399  |         if (nhk > 0) {
400  |             if(fread(hk, sizeof(hk[0]), nhk, ifp) != nhk) {
401  |                 fprintf(ifp, "Mismatch on number of hk records.\n");
402  |                 exit(1);
403  |             }
404  |         }
405  |         if (fread(&ngw, sizeof(ngw),1, ifp) != 1) {
406  |             /* Serious error, what to do? */
407  |             ngw = 0;
408  |         }
409  |         if (ngw > 0) {
410  |             if(fread(gw, sizeof(gw[0]), ngw, ifp) != ngw) {
411  |                 fprintf(ifp, "Mismatch on number of gw records.\n");
412  |                 exit(1);
413  |             }
414  |         }
415  | 	if (fread(&nrw, sizeof(nrw),1, ifp) != 1) {
416  | 	    /* Serious error, what to do? */
417  | 	    nrw = 0;
418  | 	}
419  | 	if (nrw > 0) {
420  | 	    if(fread(rw, sizeof(rw[0]), nrw, ifp) != nrw) {
421  | 		fprintf(ifp, "Mismatch on number of rw records.\n");
422  | 		exit(1);
423  | 	    }
424  | 	}
425  | 
426  | 	/*
427  | 	 *  Get the province/supply center ordering.
428  | 	 */
429  | 
430  | 	if ((i = fread(&nprov, sizeof(nprov), 1, ifp)) != 1 || nprov == 0) {
431  | 		fclose(ifp);
432  | 		sprintf(line, "%s%s/%ssummary", GAME_DIR, dipent.name, mflg ? "m" : "");
433  | 		if (!(ifp = fopen(line, "w"))) {
434  | 			perror(line);
435  | 			exit(1);
436  | 		}
437  | 		fprintf(ifp, "Sorry, summary not supported for the %s variant.\n",
438  | 			variants[variant]);
439  | 		exit(0);
440  | 	}
441  | 
442  | 	if (nprov > NPROV) {
443  | 		fprintf(log_fp, "sum: Too many prov records: %d of %d.\n", nprov, NPROV);
444  | 		exit(1);
445  | 	}
446  | 	if ((i = fread(prov, sizeof(prov[0]), nprov, ifp)) != nprov) {
447  | 		fprintf(log_fp, "sum: prov read error, %d of %d.\n", i, nprov);
448  | 		exit(1);
449  | 	}
450  | 	/*
451  | 	 *  Get the city ordering.
452  | 	 */
453  | 
454  | 	if ((i = fread(&ncity, sizeof(ncity), 1, ifp)) != 1) {
455  | 		ncity = 0;
456  | 	}
457  | 	if (ncity > NPROV) {
458  | 		fprintf(log_fp, "sum: Too many city records: %d of %d.\n", ncity, NPROV);
459  | 		exit(1);
460  | 	}
461  | 	if ((i = fread(city, sizeof(city[0]), ncity, ifp)) != ncity) {
462  | 		fprintf(log_fp, "sum: city read error, %d of %d.\n", i, ncity);
463  | 		exit(1);
464  | 	}
465  | 	fclose(ifp);
466  | 
467  | 	/*
468  | 	 * Get the real names for the players.
469  | 	 */
470  | 
471  | 	if ((ifp = fopen("dip.whois", "r"))) {
472  | 		while (fgets(line, sizeof(line), ifp)) {
473  | 			for (s = line; isspace(*s); s++);	/* skip any leading whitespace */
474  | 			if (!strncmp(s, "User:", 5)) {
475  | 				sscanf(s, "User: %d", &i);
476  | 			} else if (!strncasecmp(s, "Name:", 5)) {
477  | 				for (s += 5; isspace(*s); s++);
478  | 				for (t = names[i]; t < names[i] + MAXNAME && *s && *s != '\n';
479  | 				     *t++ = *s++);
480  | 				*t = '\0';
481  | 			}
482  | 		}
483  | 	}
484  | 	/*
485  | 	 *  Spin through the Gxxx files and gather up stats.
486  | 	 */
487  | 
488  | 	for (turn = 1; turn < MAXTURN; turn++) {
489  | 		/* TODO remove non-posix 0 flag */
490  | 		sprintf(line, "%s%s/G%03d", GAME_DIR, dipent.name, turn);
491  | 		if (!(ifp = fopen(line, "r")))
492  | 			break;
493  | 
494  | 		/*
495  | 		 *  Get the phase.
496  | 		 */
497  | 
498  | 		fgets(phaze[turn], sizeof(phaze[turn]), ifp);
499  | 		if ((s = strchr(phaze[turn], '\n')))
500  | 			*s = '\0';
501  | 
502  | 		/*
503  | 		 *  Skip unit locations.
504  | 		 */
505  | 
506  | 		while (fgets(line, sizeof(line), ifp) && *line != '-');
507  | 
508  | 		/*
509  | 		 *  Get the supply center ownerships.
510  | 		 */
511  | 
512  | 		fgets(line, sizeof(line), ifp);
513  | 		if (*line != '-') {
514  | 			if (line[1] == ':') {
515  | 				j = 1;
516  | 				while (fgets(line, sizeof(line), ifp) && line[1] == ':') {
517  | 					switch (*line) {
518  | 
519  | 						/*
520  | 						 *  C: City ownership
521  | 						 */
522  | 
523  | 					case 'C':
524  | 						for (i = j, t = line + 2; *t && !isspace(*t); i++, t++)
525  | 							if (!isalnum(*t))
526  | 								*t = '.';
527  | 						strcpy(cownr[turn] + j - 1, line + 2);
528  | 						break;
529  | 
530  | 						/*
531  | 						 *  H: Province home ownership
532  | 						 */
533  | 
534  | 					case 'H':
535  | 						for (i = j, t = line + 2; *t && !isspace(*t); i++, t++)
536  | 							if (!isalnum(*t))
537  | 								*t = '.';
538  | 						strcpy(homes[turn] + j - 1, line + 2);
539  | 						break;
540  | 
541  | 						/*
542  | 						 *  N: Comment, province name.  Note: OCHF must follow N.
543  | 						 */
544  | 
545  | 					case 'N':
546  | 						j = i;
547  | 						break;
548  | 
549  | 						/*
550  | 						 *  O: Province ownership.
551  | 						 */
552  | 
553  | 					case 'O':
554  | 						for (i = j, t = line + 2; *t && !isspace(*t); i++, t++)
555  | 							if (!isalnum(*t))
556  | 								*t = '.';
557  | 						strcpy(owner[turn] + j - 1, line + 2);
558  | 						break;
559  | 					}
560  | 				}
561  | 
562  | 			} else {	/* Old pre-Machiavelli format */
563  | 
564  | 				strcpy(owner[turn], line);
565  | 				fgets(line, sizeof(line), ifp);
566  | 
567  | 			}
568  | 
569  | 			/*
570  | 			 *  Read in player list.
571  | 			 */
572  | 
573  | 			n = 0;
574  | 			while (*line != '-') {
575  | 				get_power(line, &p);
576  | 				if (!p && *line == 'M')
577  | 					p = MASTER;
578  | 				if (p) {
579  | 					n++;
580  | 					j = sscanf(line, "%s %x %d %d %d %d %d %s", string, &i,
581  | 						   &units[turn][p], &centers[turn][p], &player[turn][p], &i, &i, addr);
582  | 
583  | 					if (j != 8) {
584  | 						fprintf(log_fp, "sum: Bad G%3.3d line, %d: %s", turn - 1, j, line);
585  | 					}
586  | 					if ((i = player[turn][p]) < MAXUSER) {
587  | 						if (*addr != '*')
588  | 							strcpy(addrs[i], addr);
589  | 						if ((s = strchr(addrs[i], '\n')))
590  | 							*s = '\0';
591  | 						if (!gflg || p == MASTER) {
592  | 							if ((j = strlen(names[i])) > lname)
593  | 								lname = j;
594  | 						}
595  | 					} else {
596  | 						player[turn][p] = MAXUSER - 1;
597  | 						strcpy(names[MAXUSER - 1], "Unknown");
598  | 					}
599  | 
600  | 				}
601  | 				if (!fgets(line, sizeof(line), ifp))
602  | 					break;
603  | 			}
604  | 
605  | 			if (!n) {
606  | 				for (i = 0; i <= npr; i++) {
607  | 					if ((p = power(owner[turn][i]))) {
608  | 						centers[turn][p]++;
609  | 					}
610  | 				}
611  | 			}
612  | 			/*
613  | 			 * Get any dipent entry we run across. (only last is retained)
614  | 			 */
615  | 
616  | 			if (line[0] == '-' && line[1] == '2') {
617  | 				gotent = 1;
618  | 				getdipent(ifp);
619  | 			}
620  | 		} else {
621  | 
622  | 			/*
623  | 			 *  Use default supply center ownerships--'x' = unowned center, 'l' =
624  | 			 *  land non-center, & 'w' = water non-center, so don't count those.
625  | 			 */
626  | 
627  | 			for (i = 0; i <= npr; i++) {
628  | 				if ((pr[i + 1].type != 'x') && (pr[i + 1].type != 'l') &&
629  | 				    (pr[i + 1].type != 'w')) {
630  | 					owner[turn][i] = cownr[turn][i] = homes[turn][i] = pr[i + 1].type;
631  | 				} else {
632  | 					owner[turn][i] = cownr[turn][i] = homes[turn][i] = '.';
633  | 				}
634  | 				centers[turn][power(owner[turn][i])]++;
635  | 				units[turn][power(owner[turn][i])]++;
636  | 			}
637  | 			owner[turn][i] = cownr[turn][i] = homes[turn][i] = '\n';
638  | 
639  | 		}
640  | 
641  | 		fclose(ifp);
642  | 
643  | 	}
644  | 
645  | 	if (phaze[1][0] == 'S' && phaze[1][5] == 'M') {
646  | 		sprintf(phaze[0], "x%4.4dB", atoi(phaze[1] + 1) - 1);
647  | 	} else {
648  | 		sprintf(phaze[0], "x%4.4dx", atoi(phaze[1] + 1));
649  | 	}
650  | 	memcpy(owner[0], owner[1], sizeof(owner[0]));
651  | 	memcpy(cownr[0], cownr[1], sizeof(cownr[0]));
652  | 	memcpy(homes[0], homes[1], sizeof(homes[0]));
653  | 	memcpy(centers[0], centers[1], sizeof(centers[0]));
654  | 	memcpy(units[0], units[1], sizeof(units[0]));
655  | 
656  | 	if (dipent.flags & F_MACH) {
657  | 		for (p = 1; p < WILD_PLAYER; p++) {
658  | 			if (dipent.pl[p] == 'x')
659  | 				continue;
660  | 			centers[1][p] = centers[2][p] + 3 * units[2][p];
661  | 		}
662  | 	}
663  | 	/*
664  | 	 *  Oh boy, now we write out the report.  First the player list.
665  | 	 */
666  | 
667  | 	sprintf(line, "%s%s/%ssummary", GAME_DIR, dipent.name, mflg ? "m" : "");
668  | 	if (!(ifp = fopen(line, "w"))) {
669  | 		perror(line);
670  | 		exit(1);
671  | 	}
672  | 	fprintf(ifp, "Summary of game '%s' through %s.\n\n", dipent.name, phaze[turn - 2]);
673  | 
674  | 	k = 1;
675  | 	for (p = MASTER; p < WILD_PLAYER || p == MASTER; p++) {
676  | 		if (dipent.pl[p] == 'x')
677  | 			continue;
678  | 		i = 0;
679  | 		for (j = 1; j < turn; j++) {
680  | 			if (qflg && gflg && !mflg) {	/* Don't show power transfer if quiet */
681  | 				if (i)
682  | 					player[j][p] = i;
683  | 			}
684  | 			if ((n = player[j][p]) && i != n) {
685  | 				if (!i) {
686  | 					while (i < j)
687  | 						player[i++][p] = n;
688  | 					fprintf(ifp, "  %s:%*s", powers[p], (int) (14 - strlen(powers[p])), "");
689  | 				} else {
690  | 					fprintf(ifp, "   from %6.6s:  ", phaze[j - 1]);
691  | 				}
692  | 				i = n;
693  | 				if (gflg && p != MASTER) {
694  | 					if (strncmp(names[i], "Somebody #", 10)) {
695  | 						strcpy(addrs[i], "someone@somewhere");
696  | 						if (dipent.x2flags & X2F_SECRET) {
697  | 							sprintf(names[i], "Somebody   ");
698  | 						} else {
699  | 							sprintf(names[i], "Somebody #%d", k++);
700  | 						}
701  | 					}
702  | 				}
703  | 				addrs[i][120 - 14 - 1 - 2 - lname - 3] = '\0';
704  | 				fprintf(ifp, "%-*s  %s\n", lname, names[i], addrs[i]);
705  | 			}
706  | 		}
707  | 
708  | 		if (p == MASTER)
709  | 			p = 0;
710  | 	}
711  | 
712  | 	/*
713  | 	   **  We'll output the parameters if we managed to find a dipent entry.
714  | 	   (this routine is not used at the moment, it gets the dipent from
715  | 	   the dip.master file.  The current assumption is that the most
716  | 	   recent dipent in the data files is better)
717  | 	   strcpy(name, dipent.name);
718  | 	   while (getdipent(mfp)) {
719  | 	   if (strcmp(dipent.name,name) == 0)
720  | 	   break;
721  | 	   };
722  | 	 */
723  | 
724  | 	if (gotent) {
725  | 		fprintf(ifp, "\n\nGame parameters are/were as follows:\n");
726  | 		params(ifp);
727  | 	}
728  | 	fprintf(ifp, "  Judge: %s.\n", JUDGE_CODE);
729  | 
730  | 	/*
731  | 	 *  Game start date
732  | 	 */
733  | 
734  | 	sprintf(line, "%s%s/start", GAME_DIR, dipent.name);
735  | 	if ((tfp = fopen(line, "r"))) {
736  | 		fputs("\n", ifp);
737  | 		while (fgets(line, sizeof(line), tfp))
738  | 			fputs(line, ifp);
739  | 		fclose(tfp);
740  | 	}
741  | 	/*
742  | 	 *  Draw if there is one, is next
743  | 	 */
744  | 
745  | 	sprintf(line, "%s%s/draw", GAME_DIR, dipent.name);
746  | 	if ((tfp = fopen(line, "r"))) {
747  | 		while (fgets(line, sizeof(line), tfp))
748  | 			fputs(line, ifp);
749  | 		fclose(tfp);
750  | 	}
751  |     /*
752  | 
753  |      * or concession ...
754  |  
755  |      */
756  |  
757  |      sprintf(line, "%s%s/conc", GAME_DIR, dipent.name);
758  |  
759  |      if((tfp = fopen(line, "r"))) {
760  |  
761  |          while(fgets(line, sizeof(line), tfp))
762  |  
763  |              fputs(line, ifp);
764  |  
765  |          fclose(tfp);
766  |  
767  |      }
768  | 	/*
769  | 	 *  Comments if any are next.
770  | 	 */
771  | 
772  | 	sprintf(line, "%s%s/info", GAME_DIR, dipent.name);
773  | 	if ((tfp = fopen(line, "r"))) {
774  | 		fputs("\n", ifp);
775  | 		while (fgets(line, sizeof(line), tfp))
776  | 			fputs(line, ifp);
777  | 		fclose(tfp);
778  | 	}
779  | 	/*
780  | 	 *  Next we take care of the supply center ownership.
781  | 	 */
782  | 
783  | 	/* Only display this section if not blind or for master */
784  | 
785  | 	if (!bflg) for (cs = 0; cs < nprov; cs += CPL) {
786  | 		ce = nprov < cs + CPL ? nprov : cs + CPL;
787  | 
788  | 		if (dipent.flags & F_MACH) {
789  | 			fprintf(ifp, "\n\nHistorical Province Summary%s\n", cs ? " (cont)" : "");
790  | 			fprintf(ifp, "---------------------------%s\n", cs ? "-------" : "");
791  | 		} else {
792  | 			fprintf(ifp, "\n\nHistorical Supply Center Summary%s\n", cs ? " (cont)" : "");
793  | 			fprintf(ifp, "--------------------------------%s\n", cs ? "-------" : "");
794  | 		}
795  | 		fprintf(ifp, "   ");
796  | 		for (i = cs; i < ce; i += 2) {
797  | 			s = pr[prov[i]].name;
798  | 			while (*s)
799  | 				s++;
800  | 			s++;
801  | 			s++;
802  | 			if (!*s)
803  | 				s = pr[prov[i]].name;
804  | 			if (islower(*s))
805  | 				*s = toupper(*s);
806  | 			fprintf(ifp, " %-3.3s", s);
807  | 		}
808  | 
809  | 		fprintf(ifp, "\nYear ");
810  | 		for (i = cs + 1; i < ce; i += 2) {
811  | 			s = pr[prov[i]].name;
812  | 			while (*s)
813  | 				s++;
814  | 			s++;
815  | 			s++;
816  | 			if (!*s)
817  | 				s = pr[prov[i]].name;
818  | 			if (islower(*s))
819  | 				*s = toupper(*s);
820  | 			fprintf(ifp, " %-3.3s", s);
821  | 		}
822  | 		fputc('\n', ifp);
823  | 
824  | 		years = 0;
825  | 		for (i = 0; i < turn; i++) {
826  | 			if (phaze[i][5] == 'B' || phaze[i][5] == 'A' ||
827  | 			    ((i == (turn - 1)) && (phaze[i][6] == 'X')) ||
828  | 			    ((i != 0) && (i < turn - 1) && (phaze[i + 1][4] != phaze[i][4]))) {
829  | 				fprintf(ifp, "%4.4d", atoi(phaze[i] + 1));
830  | 				for (p = cs; p < ce; p++)
831  | 					fprintf(ifp, " %c", owner[i][prov[p] - 1]);
832  | 				fputc('\n', ifp);
833  | 				ix[years++] = i;
834  | 			}
835  | 		}
836  | 	}
837  | 
838  | 	/*
839  | 	 *  Similarly the city ownerships.
840  | 	 */
841  | 
842  | 	if (!bflg) for (cs = 0; cs < ncity; cs += CPL) {
843  | 		ce = ncity < cs + CPL ? ncity : cs + CPL;
844  | 
845  | 		fprintf(ifp, "\n\nHistorical City Summary%s\n", cs ? " (cont)" : "");
846  | 		fprintf(ifp, "-----------------------%s\n", cs ? "-------" : "");
847  | 		fprintf(ifp, "   ");
848  | 		for (i = cs; i < ce; i += 2) {
849  | 			s = pr[city[i]].name;
850  | 			while (*s)
851  | 				s++;
852  | 			s++;
853  | 			s++;
854  | 			if (!*s)
855  | 				s = pr[city[i]].name;
856  | 			if (islower(*s))
857  | 				*s = toupper(*s);
858  | 			fprintf(ifp, " %-3.3s", s);
859  | 		}
860  | 
861  | 		fprintf(ifp, "\nYear ");
862  | 		for (i = cs + 1; i < ce; i += 2) {
863  | 			s = pr[city[i]].name;
864  | 			while (*s)
865  | 				s++;
866  | 			s++;
867  | 			s++;
868  | 			if (!*s)
869  | 				s = pr[city[i]].name;
870  | 			if (islower(*s))
871  | 				*s = toupper(*s);
872  | 			fprintf(ifp, " %-3.3s", s);
873  | 		}
874  | 		fputc('\n', ifp);
875  | 
876  | 		for (i = 0; i < years; i++) {
877  | 			fprintf(ifp, "%4.4d", atoi(phaze[ix[i]] + 1));
878  | 			for (p = cs; p < ce; p++)
879  | 				fprintf(ifp, " %c", cownr[ix[i]][city[p] - 1]);
880  | 			fputc('\n', ifp);
881  | 		}
882  | 	}
883  | 
884  | 	/*
885  | 	 *  Next we take care of supply center counts..
886  | 	 */
887  | 
888  | 	if (dipent.flags & F_MACH) {
889  | 		fprintf(ifp, "\n\nHistory of Treasury for Adjustments\n");
890  | 		fprintf(ifp, "-----------------------------------\n");
891  | 	} else {
892  | 		fprintf(ifp, "\n\nHistory of Supply Center Counts\n");
893  | 		fprintf(ifp, "-------------------------------\n");
894  | 	}
895  | 
896  | 	for (sy = 0; sy < years; sy += YPL) {
897  | 
898  | 		fprintf(ifp, "Power    %4.4s", phaze[ix[sy]] + 1);
899  | 		for (i = 1; i < YPL && i + sy < years; i++)
900  | 			fprintf(ifp, " '%2.2s", phaze[ix[i + sy]] + 3);
901  | 		fprintf(ifp, "%*sPlayer\n", 4 * (YPL - i) + 3, "");
902  | 
903  | 		for (p = 1; p < WILD_PLAYER; p++) {
904  | 			if (dipent.pl[p] == 'x' || !centers[ix[sy]][p])
905  | 				continue;
906  | 			fprintf(ifp, "%-10.10s", powers[p]);
907  | 			j = 0;
908  | 			for (i = 0; i < YPL && i + sy < years; i++) {
909  | 				if (!qflg && player[ix[i + sy]][p] != player[ix[j + sy]][p]) {
910  | 					if (player[ix[j + sy]][p]) {
911  | 						fprintf(ifp, "\\%*s%s\n%*s", 4 * (YPL - i) - 1 + 2, "",
912  | 							names[player[ix[j + sy]][p]], 10 + 4 * i, "");
913  | 					}
914  | 					j = i;
915  | 				}
916  | 				if (!centers[ix[i + sy]][p])
917  | 					break;
918  | 				m = centers[ix[i + sy]][p];
919  | 				n = (ix[i + sy] == turn - 1) || (dipent.flags & F_MACH) ?
920  | 				    m : units[ix[i + sy] + 1][p];
921  | 				if (!bflg) {
922  | 					
923  | 					if (n > m) {
924  | 					    fprintf(ifp, "%3dX", m); 
925  | 					    tooMany = 1;
926  | 					}
927  | 					else 
928  | 					    fprintf(ifp, "%3d%c", m, outc[m - n]);
929  | 				} else {
930  | 					fprintf(ifp, "%3d ", m);
931  | 				}
932  | 				if (n <= m) outn[m - n]++;
933  | 				sqrs[i] += m * m;
934  | 			}
935  | 			fprintf(ifp, "%*s%s\n", 4 * (YPL - i) + 2, "", names[player[ix[j + sy]][p]]);
936  | 		}
937  | 
938  | 		if (!(dipent.flags & F_MACH)) {
939  | 			fprintf(ifp, "Index:   ");
940  | 			for (i = 0; i < YPL && i + sy < years; i++) {
941  | 				fprintf(ifp, "%4d", sqrs[i] / dipent.np);
942  | 				sqrs[i] = 0;
943  | 			}
944  | 		}
945  | 		fprintf(ifp, "\n\n");
946  | 
947  | 	}
948  | 
949  | 	if (!bflg) {
950  | 		if (tooMany) 
951  | 			fprintf(ifp, "X = too many build(s).\n");
952  | 		
953  | 		for (i = 1; i < sizeof(outc); i++) {
954  | 			if (outn[i]) {
955  | 				fprintf(ifp, "%c = %d unused build%s.\n", outc[i], i, i == 1 ? "" : "s");
956  | 			}
957  | 		}
958  | 	}
959  | 	/*
960  | 	 *  Next we take care of the rest of the info...
961  | 	 */
962  | 
963  | 	if (!(dipent.flags & F_MACH)) {
964  | 		fprintf(ifp, "\nIndex is the sum of squares of the number of supply centers divided by the\n");
965  | 		fprintf(ifp, "number of players.  It is a measure of how far the game has progressed.\n");
966  | 	}
967  | 	fclose(ifp);
968  | 	return 0;
969  | }