1 /* File: files.c */
2 
3 /* Purpose: code dealing with files (and death) */
4 
5 /*
6  * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
7  *
8  * This software may be copied and distributed for educational, research, and
9  * not for profit purposes provided that this copyright and statement are
10  * included in all such copies.
11  */
12 
13 #include "mangband.h"
14 
15 #ifdef HANDLE_SIGNALS
16 #include <signal.h>
17 
18 volatile sig_atomic_t signalbusy = 0;
19 
20 #endif
21 
22 
23 
24 /*
25  * You may or may not want to use the following "#undef".
26  */
27 /* #undef _POSIX_SAVED_IDS */
28 
29 
30 /*
31  * Extract the first few "tokens" from a buffer
32  *
33  * This function uses "colon" and "slash" as the delimeter characters.
34  *
35  * We never extract more than "num" tokens.  The "last" token may include
36  * "delimeter" characters, allowing the buffer to include a "string" token.
37  *
38  * We save pointers to the tokens in "tokens", and return the number found.
39  *
40  * Hack -- Attempt to handle the 'c' character formalism
41  *
42  * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
43  *
44  * Hack -- We will always extract at least one token
45  */
tokenize(char * buf,s16b num,char ** tokens)46 s16b tokenize(char *buf, s16b num, char **tokens)
47 {
48 	int i = 0;
49 
50 	char *s = buf;
51 
52 
53 	/* Process */
54 	while (i < num - 1)
55 	{
56 		char *t;
57 
58 		/* Scan the string */
59 		for (t = s; *t; t++)
60 		{
61 			/* Found a delimiter */
62 			if ((*t == ':') || (*t == '/')) break;
63 
64 			/* Handle single quotes */
65 			if (*t == '\'') /* ' */
66 			{
67 				/* Advance */
68 				t++;
69 
70 				/* Handle backslash */
71 				if (*t == '\\') t++;
72 
73 				/* Require a character */
74 				if (!*t) break;
75 
76 				/* Advance */
77 				t++;
78 
79 				/* Hack -- Require a close quote */
80 				if (*t != '\'') *t = '\'';
81 			}
82 
83 			/* Handle back-slash */
84 			if (*t == '\\') t++;
85 		}
86 
87 		/* Nothing left */
88 		if (!*t) break;
89 
90 		/* Nuke and advance */
91 		*t++ = '\0';
92 
93 		/* Save the token */
94 		tokens[i++] = s;
95 
96 		/* Advance */
97 		s = t;
98 	}
99 
100 	/* Save the token */
101 	tokens[i++] = s;
102 
103 	/* Number found */
104 	return (i);
105 }
106 
107 
108 bool skip_next_line = FALSE;
109 /*
110  * Parse a sub-file of the "extra info" (format shown below)
111  *
112  * Each "action" line has an "action symbol" in the first column,
113  * followed by a colon, followed by some command specific info,
114  * usually in the form of "tokens" separated by colons or slashes.
115  *
116  * Blank lines, lines starting with white space, and lines starting
117  * with pound signs ("#") are ignored (as comments).
118  *
119  * Note the use of "tokenize()" to allow the use of both colons and
120  * slashes as delimeters, while still allowing final tokens which
121  * may contain any characters including "delimiters".
122  *
123  * Note the use of "strtol()" to allow all "integers" to be encoded
124  * in decimal, hexidecimal, or octal form.
125  *
126  * Note that "monster zero" is used for the "player" attr/char, "object
127  * zero" will be used for the "stack" attr/char, and "feature zero" is
128  * used for the "nothing" attr/char.
129  *
130  * Parse another file recursively, see below for details
131  *   %:<filename>
132  *
133  * Specify the attr/char values for "monsters" by race index
134  *   R:<num>:<a>:<c>
135  *
136  * Specify the attr/char values for "objects" by kind index
137  *   K:<num>:<a>:<c>
138  *
139  * Specify the attr/char values for "features" by feature index
140  *   F:<num>:<a>:<c>
141  *
142  * Specify the attr/char values for unaware "objects" by kind tval
143  *   U:<tv>:<a>:<c>
144  *
145  * Specify the attr/char values for inventory "objects" by kind tval
146  *   E:<tv>:<a>:<c>
147  *
148  * Define a macro action, given an encoded macro action
149  *   A:<str>
150  *
151  * Create a normal macro, given an encoded macro trigger
152  *   P:<str>
153  *
154  * Create a command macro, given an encoded macro trigger
155  *   C:<str>
156  *
157  * Create a keyset mapping
158  *   S:<key>:<key>:<dir>
159  *
160  * Turn an option off, given its name
161  *   X:<str>
162  *
163  * Turn an option on, given its name
164  *   Y:<str>
165  *
166  * Specify visual information, given an index, and some data
167  *   V:<num>:<kv>:<rv>:<gv>:<bv>
168  */
process_pref_file_aux(char * buf)169 errr process_pref_file_aux(char *buf)
170 {
171 	int i, j, k, n1, n2;
172 
173 	char *zz[16];
174 
175 	/* Skip "empty" && "blank" lines */
176 	if (!buf[0] || isspace(buf[0]))
177 	{
178 			skip_next_line = FALSE;
179 			return (0);
180 	}
181 
182 	/* Skip comments */
183 	if (buf[0] == '#') return (0);
184 
185 
186 	/* Require "?:*" format */
187 	if (buf[1] != ':') return (1);
188 
189 	/* Hack - Do not load any Evaluated Expressions */
190 	if (skip_next_line)		return(0);
191 
192 	/* Process "%:<fname>" */
193 	if (buf[0] == '%')
194 	{
195 		/* Attempt to Process the given file */
196 		return (process_pref_file(buf + 2));
197 	}
198 
199 
200 	/* Process "R:<num>:<a>/<c>" -- attr/char for monster races */
201 	if (buf[0] == 'R')
202 	{
203 		if (tokenize(buf+2, 3, zz) == 3)
204 		{
205 			monster_race *r_ptr;
206 			i = (huge)strtol(zz[0], NULL, 0);
207 			n1 = strtol(zz[1], NULL, 0);
208 			n2 = strtol(zz[2], NULL, 0);
209 			if (i >= z_info->r_max) return (1);
210 			/*r_ptr = &r_info[i];
211 			if (n1) r_ptr->x_attr = n1;
212 			if (n2) r_ptr->x_char = n2;*/
213 			if (n1) r_attr_s[i] = n1;
214 			if (n2) r_char_s[i] = n2;
215 			return (0);
216 		}
217 	}
218 
219 
220 	/* Process "K:<num>:<a>/<c>"  -- attr/char for object kinds */
221 	else if (buf[0] == 'K')
222 	{
223 		if (tokenize(buf+2, 3, zz) == 3)
224 		{
225 			object_kind *k_ptr;
226 			i = (huge)strtol(zz[0], NULL, 0);
227 			n1 = strtol(zz[1], NULL, 0);
228 			n2 = strtol(zz[2], NULL, 0);
229 			if (i >= z_info->k_max) return (1);
230 			/*k_ptr = &k_info[i];
231 			if (n1) k_ptr->x_attr = n1;
232 			if (n2) k_ptr->x_char = n2;*/
233 			if (n1) k_attr_s[i] = n1;
234 			if (n2) k_char_s[i] = n1;
235 			return (0);
236 		}
237 	}
238 
239 
240 	/* Process "F:<num>:<a>/<c>" -- attr/char for terrain features */
241 	else if (buf[0] == 'F')
242 	{
243 		if (tokenize(buf+2, 3, zz) == 3)
244 		{
245 			feature_type *f_ptr;
246 			i = (huge)strtol(zz[0], NULL, 0);
247 			n1 = strtol(zz[1], NULL, 0);
248 			n2 = strtol(zz[2], NULL, 0);
249 			if (i >= z_info->f_max) return (1);
250 			/*f_ptr = &f_info[i];
251 			if (n1) f_ptr->x_attr = n1;
252 			if (n2) f_ptr->x_char = n2;*/
253 			if (n1) f_attr_s[i] = n1;
254 			if (n2) f_char_s[i] = n2;
255 			return (0);
256 		}
257 	}
258 
259 
260 	/* Process "U:<tv>:<a>/<c>" -- attr/char for unaware items */
261 	else if (buf[0] == 'U')
262 	{
263 		if (tokenize(buf+2, 3, zz) == 3)
264 		{
265 			j = (huge)strtol(zz[0], NULL, 0);
266 			n1 = strtol(zz[1], NULL, 0);
267 			n2 = strtol(zz[2], NULL, 0);
268 			for (i = 1; i < z_info->k_max; i++)
269 			{
270 				object_kind *k_ptr = &k_info[i];
271 				if (k_ptr->tval == j)
272 				{
273 					if (n1) k_ptr->d_attr = n1;
274 					if (n2) k_ptr->d_char = n2;
275 				}
276 			}
277 			return (0);
278 		}
279 	}
280 
281 
282 	/* Process "E:<tv>:<a>/<c>" -- attr/char for equippy chars */
283 	else if (buf[0] == 'E')
284 	{
285 		if (tokenize(buf+2, 2, zz) == 2)
286 		{
287 			j = (byte)strtol(zz[0], NULL, 0) % 128;
288 			n1 = strtol(zz[1], NULL, 0);
289 			if (n1) tval_to_attr[j] = n1;
290 			return (0);
291 		}
292 		if (tokenize(buf+2, 3, zz) == 3)
293 		{
294 			j = (byte)strtol(zz[0], NULL, 0) % 128;
295 			n1 = strtol(zz[1], NULL, 0);
296 			n2 = strtol(zz[2], NULL, 0);
297 			if (n1) tval_to_attr[j] = n1;
298 			if (n2) tval_to_char[j] = n2;
299 			return (0);
300 		}
301 	}
302 
303 
304 	/* Process "A:<str>" -- save an "action" for later */
305 	else if (buf[0] == 'A')
306 	{
307 		text_to_ascii(macro__buf, buf+2);
308 		return (0);
309 	}
310 
311 	/* Process "P:<str>" -- create normal macro */
312 	else if (buf[0] == 'P')
313 	{
314 		char tmp[1024];
315 		text_to_ascii(tmp, buf+2);
316 		macro_add(tmp, macro__buf, FALSE);
317 		return (0);
318 	}
319 
320 	/* Process "C:<str>" -- create command macro */
321 	else if (buf[0] == 'C')
322 	{
323 		char tmp[1024];
324 		text_to_ascii(tmp, buf+2);
325 		macro_add(tmp, macro__buf, TRUE);
326 		return (0);
327 	}
328 
329 
330 	/* Process "S:<num>:<a>/<c>" -- attr/char for special things */
331 	else if (buf[0] == 'S')
332 	{
333 		if (tokenize(buf+2, 3, zz) == 3)
334 		{
335 			i = strtol(zz[0], NULL, 0);
336 			n1 = strtol(zz[1], NULL, 0);
337 			n2 = strtol(zz[2], NULL, 0);
338 			if ((i < 0) || (i >= (long)N_ELEMENTS(misc_to_attr))) return (1);
339 			misc_to_attr[i] = (byte)n1;
340 			misc_to_char[i] = (char)n2;
341 			return (0);
342 		}
343 
344 	}
345 	/* Process "S:<key>:<key>:<dir>" -- keymap */
346 	else if (buf[0] == 'S')
347 	{
348 		if (tokenize(buf+2, 3, zz) == 3)
349 		{
350 			i = strtol(zz[0], NULL, 0) & 0x7F;
351 			j = strtol(zz[0], NULL, 0) & 0x7F;
352 			k = strtol(zz[0], NULL, 0) & 0x7F;
353 			if ((k > 9) || (k == 5)) k = 0;
354 			keymap_cmds[i] = j;
355 			keymap_dirs[i] = k;
356 			return (0);
357 		}
358 	}
359 
360 
361 	/* Process "V:<num>:<kv>:<rv>:<gv>:<bv>" -- visual info */
362 	else if (buf[0] == 'V')
363 	{
364 		if (tokenize(buf+2, 5, zz) == 5)
365 		{
366 			i = (byte)strtol(zz[0], NULL, 0);
367 			color_table[i][0] = (byte)strtol(zz[1], NULL, 0);
368 			color_table[i][1] = (byte)strtol(zz[2], NULL, 0);
369 			color_table[i][2] = (byte)strtol(zz[3], NULL, 0);
370 			color_table[i][3] = (byte)strtol(zz[4], NULL, 0);
371 			return (0);
372 		}
373 	}
374 
375 
376 	/* Process "X:<str>" -- turn option off */
377 	else if (buf[0] == 'X')
378 	{
379 		return (0);
380 	}
381 
382 	/* Process "Y:<str>" -- turn option on */
383 	else if (buf[0] == 'Y')
384 	{
385 		return (0);
386 	}
387 
388 	/* Process "W:<num>:<use> -- set window use */
389 	else if (buf[0] == 'W')
390 	{
391 		return (0);
392 	}
393 
394 
395 	/* Process "?: -- expression */
396 	else if (buf[0] == '?')
397 	{
398 		skip_next_line = TRUE;
399 		return (0);
400 	}
401 
402 	/* Failure */
403 	return (1);
404 }
405 
406 
407 /*
408  * Process the "user pref file" with the given name
409  *
410  * See the function above for a list of legal "commands".
411  */
process_pref_file(cptr name)412 errr process_pref_file(cptr name)
413 {
414 	ang_file* fp;
415 
416 	char buf[1024];
417 
418 
419 	/* Build the filename */
420 	path_build(buf, 1024, ANGBAND_DIR_PREF, name);
421 
422 	/* Open the file */
423 	fp = file_open(buf, MODE_READ, -1);
424 
425 	/* Catch errors */
426 	if (!fp) return (-1);
427 
428 	/* Process the file */
429 	while (file_getl(fp, buf, 1024))
430 	{
431 		/* Process the line */
432 		if (process_pref_file_aux(buf))
433 		{
434 			/* Useful error message */
435 			plog(format("Error in '%s' parsing '%s'.", buf, name));
436 		}
437 	}
438 
439 	/* Close the file */
440 	file_close(fp);
441 
442 	/* Success */
443 	return (0);
444 }
445 
446 
447 
448 
449 
450 
451 
452 #ifdef CHECK_TIME
453 
454 /*
455  * Operating hours for ANGBAND (defaults to non-work hours)
456  */
457 static char days[7][29] =
458 {
459 	"SUN:XXXXXXXXXXXXXXXXXXXXXXXX",
460 	"MON:XXXXXXXX.........XXXXXXX",
461 	"TUE:XXXXXXXX.........XXXXXXX",
462 	"WED:XXXXXXXX.........XXXXXXX",
463 	"THU:XXXXXXXX.........XXXXXXX",
464 	"FRI:XXXXXXXX.........XXXXXXX",
465 	"SAT:XXXXXXXXXXXXXXXXXXXXXXXX"
466 };
467 
468 /*
469  * Restict usage (defaults to no restrictions)
470  */
471 static bool check_time_flag = FALSE;
472 
473 #endif
474 
475 
476 /*
477  * Handle CHECK_TIME
478  */
check_time(void)479 errr check_time(void)
480 {
481 
482 #ifdef CHECK_TIME
483 
484 	time_t              c;
485 	struct tm		*tp;
486 
487 	/* No restrictions */
488 	if (!check_time_flag) return (0);
489 
490 	/* Check for time violation */
491 	c = time((time_t *)0);
492 	tp = localtime(&c);
493 
494 	/* Violation */
495 	if (days[tp->tm_wday][tp->tm_hour + 4] != 'X') return (1);
496 
497 #endif
498 
499 	/* Success */
500 	return (0);
501 }
502 
503 
504 
505 /*
506  * Initialize CHECK_TIME
507  */
check_time_init(void)508 errr check_time_init(void)
509 {
510 
511 #ifdef CHECK_TIME
512 
513 	ang_file* fp;
514 
515 	char	buf[1024];
516 
517 
518 	/* Build the filename */
519 	path_build(buf, 1024, ANGBAND_DIR_DATA, "time.txt");
520 
521 	/* Open the file */
522 	fp = file_open(buf, MODE_READ, -1);
523 
524 	/* No file, no restrictions */
525 	if (!fp) return (0);
526 
527 	/* Assume restrictions */
528 	check_time_flag = TRUE;
529 
530 	/* Parse the file */
531 	while (file_getl(fp, buf, 80))
532 	{
533 		/* Skip comments and blank lines */
534 		if (!buf[0] || (buf[0] == '#')) continue;
535 
536 		/* Chop the buffer */
537 		buf[29] = '\0';
538 
539 		/* Extract the info */
540 		if (prefix(buf, "SUN:")) strcpy(days[0], buf);
541 		if (prefix(buf, "MON:")) strcpy(days[1], buf);
542 		if (prefix(buf, "TUE:")) strcpy(days[2], buf);
543 		if (prefix(buf, "WED:")) strcpy(days[3], buf);
544 		if (prefix(buf, "THU:")) strcpy(days[4], buf);
545 		if (prefix(buf, "FRI:")) strcpy(days[5], buf);
546 		if (prefix(buf, "SAT:")) strcpy(days[6], buf);
547 	}
548 
549 	/* Close it */
550 	file_close(fp);
551 
552 #endif
553 
554 	/* Success */
555 	return (0);
556 }
557 
558 
559 
560 #ifdef CHECK_LOAD
561 
562 #ifndef MAXHOSTNAMELEN
563 # define MAXHOSTNAMELEN  64
564 #endif
565 
566 typedef struct statstime statstime;
567 
568 struct statstime
569 {
570 	int                 cp_time[4];
571 	int                 dk_xfer[4];
572 	unsigned int        v_pgpgin;
573 	unsigned int        v_pgpgout;
574 	unsigned int        v_pswpin;
575 	unsigned int        v_pswpout;
576 	unsigned int        v_intr;
577 	int                 if_ipackets;
578 	int                 if_ierrors;
579 	int                 if_opackets;
580 	int                 if_oerrors;
581 	int                 if_collisions;
582 	unsigned int        v_swtch;
583 	long                avenrun[3];
584 	struct timeval      boottime;
585 	struct timeval      curtime;
586 };
587 
588 /*
589  * Maximal load (if any).
590  */
591 static int check_load_value = 0;
592 
593 #endif
594 
595 
596 /*
597  * Handle CHECK_LOAD
598  */
check_load(void)599 errr check_load(void)
600 {
601 
602 #ifdef CHECK_LOAD
603 
604 	struct statstime    st;
605 
606 	/* Success if not checking */
607 	if (!check_load_value) return (0);
608 
609 	/* Check the load */
610 	if (0 == rstat("localhost", &st))
611 	{
612 		long val1 = (long)(st.avenrun[2]);
613 		long val2 = (long)(check_load_value) * FSCALE;
614 
615 		/* Check for violation */
616 		if (val1 >= val2) return (1);
617 	}
618 
619 #endif
620 
621 	/* Success */
622 	return (0);
623 }
624 
625 
626 /*
627  * Initialize CHECK_LOAD
628  */
check_load_init(void)629 errr check_load_init(void)
630 {
631 
632 #ifdef CHECK_LOAD
633 
634 	ang_file* fp;
635 
636 	char	buf[1024];
637 
638 	char	temphost[MAXHOSTNAMELEN+1];
639 	char	thishost[MAXHOSTNAMELEN+1];
640 
641 
642 	/* Build the filename */
643 	path_build(buf, 1024, ANGBAND_DIR_TEXT, "load.txt");
644 
645 	/* Open the "load" file */
646 	fp = file_open(buf, MODE_READ, -1);
647 
648 	/* No file, no restrictions */
649 	if (!fp) return (0);
650 
651 	/* Default load */
652 	check_load_value = 100;
653 
654 	/* Get the host name */
655 	(void)gethostname(thishost, (sizeof thishost) - 1);
656 
657 	/* Parse it */
658 	while (file_getl(fp, buf, 1024))
659 	{
660 		int value;
661 
662 		/* Skip comments and blank lines */
663 		if (!buf[0] || (buf[0] == '#')) continue;
664 
665 		/* Parse, or ignore */
666 		if (sscanf(buf, "%s%d", temphost, &value) != 2) continue;
667 
668 		/* Skip other hosts */
669 		if (!streq(temphost, thishost) &&
670 		    !streq(temphost, "localhost")) continue;
671 
672 		/* Use that value */
673 		check_load_value = value;
674 
675 		/* Done */
676 		break;
677 	}
678 
679 	/* Close the file */
680 	file_close(fp);
681 
682 #endif
683 
684 	/* Success */
685 	return (0);
686 }
687 
688 
689 
690 
691 
692 /*
693  * Prints the following information on the screen.
694  *
695  * For this to look right, the following should be spaced the
696  * same as in the prt_lnum code... -CFT
697  *
698  * This will send the info to the client now --KLJ--
699  *
700  * Except that this (and display_player) are never called. --KLJ--
701  */
display_player_middle(player_type * p_ptr)702 static void display_player_middle(player_type *p_ptr)
703 {
704 	int show_tohit = p_ptr->dis_to_h;
705 	int show_todam = p_ptr->dis_to_d;
706 
707 	s32b adv_exp;
708 
709 	object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
710 
711 	/* Hack -- add in weapon info if known */
712 	if (object_known_p(p_ptr, o_ptr)) show_tohit += o_ptr->to_h;
713 	if (object_known_p(p_ptr, o_ptr)) show_todam += o_ptr->to_d;
714 
715 	/* Dump the bonuses to hit/dam */
716 	//Send_plusses(Ind, show_tohit, show_todam);
717 
718 	/* Dump the armor class bonus */
719 	//Send_ac(Ind, p_ptr->dis_ac, p_ptr->dis_to_a);
720 
721 	if (p_ptr->lev >= PY_MAX_LEVEL)
722 		adv_exp = 0;
723 	else adv_exp = (s32b)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L);
724 
725 	//Send_experience(Ind, p_ptr->lev, p_ptr->max_exp, p_ptr->exp, adv_exp);
726 
727 	//Send_gold(Ind, p_ptr->au);
728 
729 	//Send_hp(Ind, p_ptr->mhp, p_ptr->chp);
730 
731 	//Send_sp(Ind, p_ptr->msp, p_ptr->csp);
732 }
733 
734 
735 
736 /*
737  * Display the character on the screen (with optional history)
738  *
739  * The top two and bottom two lines are left blank.
740  */
display_player(player_type * p_ptr)741 void display_player(player_type *p_ptr)
742 {
743 	int i;
744 
745 
746 	/* Send basic information */
747 	//Send_char_info(Ind, p_ptr->prace, p_ptr->pclass, p_ptr->male);
748 
749 	/* Age, Height, Weight, Social */
750 	//Send_various(Ind, p_ptr->ht, p_ptr->wt, p_ptr->age, p_ptr->sc);
751 
752 	/* Send all the stats */
753 	for (i = 0; i < A_MAX; i++)
754 	{
755 		//Send_stat(Ind, i, p_ptr->stat_top[i], p_ptr->stat_use[i]);
756 		//Send_maxstat(Ind, i, p_ptr->stat_max[i]);
757 	}
758 
759 	/* Extra info */
760 	display_player_middle(p_ptr);
761 
762 	/* Display "history" info */
763 	//Send_history(Ind, i, p_ptr->history[i]);
764 }
765 
766 
c_put_str_b(char buffer[100][82],byte attr,cptr str,int row,int col)767 void c_put_str_b(char buffer[100][82], byte attr, cptr str, int row, int col)
768 {
769 	/* Position cursor, Dump the attr/text */
770 	char* s;
771 	int i;
772 	s = buffer[row-1];
773 	i = col;
774 	while(*str)
775 	{
776 		s[i++] = (*str);
777 		str++;
778 	}
779 }
put_str_b(char buffer[100][82],cptr str,int row,int col)780 void put_str_b(char buffer[100][82], cptr str, int row, int col)
781 {
782 	c_put_str_b(buffer,0,str,row,col);
783 }
prt_num_b(char buffer[100][82],cptr header,int num,int row,int col,byte color)784 void prt_num_b(char buffer[100][82], cptr header, int num, int row, int col, byte color)
785 {
786 	int len = strlen(header);
787 	char out_val[32];
788 	put_str_b(buffer,header, row, col);
789 	put_str_b(buffer,"   ", row, col + len);
790 	(void)sprintf(out_val, "%6ld", (long)num);
791 	c_put_str_b(buffer,color, out_val, row, col + len + 3);
792 }
793 
prt_lnum_b(char buffer[100][82],cptr header,s32b num,int row,int col,byte color)794 void prt_lnum_b(char buffer[100][82], cptr header, s32b num, int row, int col, byte color)
795 {
796 	int len = strlen(header);
797 	char out_val[32];
798 	put_str_b(buffer,header, row, col);
799 	(void)sprintf(out_val, "%9ld", (long)num);
800 	c_put_str_b(buffer,color, out_val, row, col + len);
801 }
802 
803 /*
804  * Returns a "rating" of x depending on y
805  */
likert(int x,int y)806 static cptr likert(int x, int y)
807 {
808 	/* Paranoia */
809 	if (y <= 0) y = 1;
810 
811 	/* Negative values */
812 	if (x < 0)
813 	{
814 		return ("Very Bad");
815 	}
816 
817 	/* Analyze the value */
818 	switch ((x / y))
819 	{
820 		case 0:
821 		case 1:
822 		{
823 			return ("Bad");
824 		}
825 		case 2:
826 		{
827 			return ("Poor");
828 		}
829 		case 3:
830 		case 4:
831 		{
832 			return ("Fair");
833 		}
834 		case 5:
835 		{
836 			return ("Good");
837 		}
838 		case 6:
839 		{
840 			return ("Very Good");
841 		}
842 		case 7:
843 		case 8:
844 		{
845 			return ("Excellent");
846 		}
847 		case 9:
848 		case 10:
849 		case 11:
850 		case 12:
851 		case 13:
852 		{
853 			return ("Superb");
854 		}
855 		case 14:
856 		case 15:
857 		case 16:
858 		case 17:
859 		{
860 			return ("Heroic");
861 		}
862 		default:
863 		{
864 			return ("Legendary");
865 		}
866 	}
867 }
868 
869 
870 /*
871  * Similar to the function in c-xtra but modified to work server-side.
872  * We print text into a buffer rather than to a term.
873  * This is used for serverside character dumps.
874  */
display_player_server(player_type * p_ptr,char buffer[100][82])875 void display_player_server(player_type *p_ptr, char buffer[100][82])
876 {
877 	int i;
878 	char buf[80];
879 	cptr desc;
880 /*	bool hist = FALSE;*/
881 
882 	int show_tohit = p_ptr->dis_to_h;
883 	int show_todam = p_ptr->dis_to_d;
884 	object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
885 
886 	/* Name, Sex, Race, Class */
887 	put_str_b(buffer,"Name        :", 2, 1);
888 	put_str_b(buffer,"Sex         :", 3, 1);
889 	put_str_b(buffer,"Race        :", 4, 1);
890 	put_str_b(buffer,"Class       :", 5, 1);
891 
892 	c_put_str_b(buffer,TERM_L_BLUE, p_ptr->name, 2, 15);
893 	c_put_str_b(buffer,TERM_L_BLUE, (p_ptr->male ? "Male" : "Female"), 3, 15);
894 	c_put_str_b(buffer,TERM_L_BLUE, p_name + p_info[p_ptr->prace].name, 4, 15);
895 	c_put_str_b(buffer,TERM_L_BLUE, c_name + c_info[p_ptr->pclass].name, 5, 15);
896 
897 	/* Age, Height, Weight, Social */
898 	prt_num_b(buffer,"Age          ", (int)p_ptr->age, 2, 32, TERM_L_BLUE);
899 	prt_num_b(buffer,"Height       ", (int)p_ptr->ht, 3, 32, TERM_L_BLUE);
900 	prt_num_b(buffer,"Weight       ", (int)p_ptr->wt, 4, 32, TERM_L_BLUE);
901 	prt_num_b(buffer,"Social Class ", (int)p_ptr->sc, 5, 32, TERM_L_BLUE);
902 
903 	/* Display the stats */
904 	for (i = 0; i < A_MAX; i++)
905 	{
906 		/* Special treatment of "injured" stats */
907 		if (p_ptr->stat_use[i] < p_ptr->stat_top[i])
908 		{
909 			int value;
910 
911 			/* Use lowercase stat name */
912 			put_str_b(buffer,stat_names_reduced[i], 2 + i, 61);
913 
914 			/* Get the current stat */
915 			value = p_ptr->stat_use[i];
916 
917 			/* Obtain the current stat (modified) */
918 			cnv_stat(value, buf);
919 
920 			/* Display the current stat (modified) */
921 			c_put_str_b(buffer,TERM_YELLOW, buf, 2 + i, 66);
922 
923 			/* Acquire the max stat */
924 			value = p_ptr->stat_top[i];
925 
926 			/* Obtain the maximum stat (modified) */
927 			cnv_stat(value, buf);
928 
929 			/* Display the maximum stat (modified) */
930 			if (p_ptr->stat_max[i] == 18+100)
931 				c_put_str_b(buffer,TERM_L_UMBER, buf, 2 + i, 73);
932 			else
933 				c_put_str_b(buffer,TERM_L_GREEN, buf, 2 + i, 73);
934 		}
935 
936 		/* Normal treatment of "normal" stats */
937 		else
938 		{
939 			/* Assume uppercase stat name */
940 			put_str_b(buffer,stat_names[i], 2 + i, 61);
941 
942 			/* Obtain the current stat (modified) */
943 			cnv_stat(p_ptr->stat_use[i], buf);
944 
945 			/* Display the current stat (modified) */
946 			if (p_ptr->stat_max[i] == 18+100)
947 				c_put_str_b(buffer,TERM_L_UMBER, buf, 2 + i, 66);
948 			else
949 				c_put_str_b(buffer,TERM_L_GREEN, buf, 2 + i, 66);
950 		}
951 	}
952 
953 	put_str_b(buffer,"(Miscellaneous Abilities)", 15, 25);
954 
955 	/* Display "skills" */
956 	put_str_b(buffer,"Fighting    :", 16, 1);
957 	desc = likert(p_ptr->skill_thn, 12);
958 	c_put_str_b(buffer,0, desc, 16, 15);
959 
960 	put_str_b(buffer,"Bows/Throw  :", 17, 1);
961 	desc = likert(p_ptr->skill_thb, 12);
962 	c_put_str_b(buffer,0, desc, 17, 15);
963 
964 	put_str_b(buffer,"Saving Throw:", 18, 1);
965 	desc = likert(p_ptr->skill_sav, 6);
966 	c_put_str_b(buffer,0, desc, 18, 15);
967 
968 	put_str_b(buffer,"Stealth     :", 19, 1);
969 	desc = likert(p_ptr->skill_stl, 1);
970 	c_put_str_b(buffer,0, desc, 19, 15);
971 
972 
973 	put_str_b(buffer,"Perception  :", 16, 28);
974 	desc = likert(p_ptr->skill_fos, 6);
975 	c_put_str_b(buffer,0, desc, 16, 42);
976 
977 	put_str_b(buffer,"Searching   :", 17, 28);
978 	desc = likert(p_ptr->skill_srh, 6);
979 	c_put_str_b(buffer,0, desc, 17, 42);
980 
981 	put_str_b(buffer,"Disarming   :", 18, 28);
982 	desc = likert(p_ptr->skill_dis, 8);
983 	c_put_str_b(buffer,0, desc, 18, 42);
984 
985 	put_str_b(buffer,"Magic Device:", 19, 28);
986 	desc = likert(p_ptr->skill_dev, 6);
987 	c_put_str_b(buffer,0, desc, 19, 42);
988 
989 
990 	put_str_b(buffer,"Blows/Round:", 16, 55);
991 	put_str_b(buffer,format("%d", p_ptr->num_blow), 16, 69);
992 
993 	put_str_b(buffer,"Shots/Round:", 17, 55);
994 	put_str_b(buffer,format("%d", p_ptr->num_fire), 17, 69);
995 
996 	put_str_b(buffer,"Infra-Vision:", 19, 55);
997 	put_str_b(buffer,format("%d feet", p_ptr->see_infra * 10), 19, 69);
998 
999 	/* Dump the bonuses to hit/dam */
1000 	if (o_ptr->k_idx)
1001 	{
1002 		show_tohit += o_ptr->to_h;
1003 		show_todam += o_ptr->to_d;
1004 	}
1005 	prt_num_b(buffer,"+ To Hit    ", show_tohit, 9, 1, TERM_L_BLUE);
1006 	prt_num_b(buffer,"+ To Damage ", show_todam, 10, 1, TERM_L_BLUE);
1007 
1008 	/* Dump the armor class bonus */
1009 	prt_num_b(buffer,"+ To AC     ", p_ptr->dis_to_a, 11, 1, TERM_L_BLUE);
1010 
1011 	/* Dump the total armor class */
1012 	prt_num_b(buffer,"  Base AC   ", p_ptr->dis_ac, 12, 1, TERM_L_BLUE);
1013 
1014 	prt_num_b(buffer,"Level      ", (int)p_ptr->lev, 9, 28, TERM_L_GREEN);
1015 
1016 	if (p_ptr->exp >= p_ptr->max_exp)
1017 	{
1018 		prt_lnum_b(buffer,"Experience ", p_ptr->exp, 10, 28, TERM_L_GREEN);
1019 	}
1020 	else
1021 	{
1022 		prt_lnum_b(buffer,"Experience ", p_ptr->exp, 10, 28, TERM_YELLOW);
1023 	}
1024 
1025 	prt_lnum_b(buffer,"Max Exp    ", p_ptr->max_exp, 11, 28, TERM_L_GREEN);
1026 
1027 	if (p_ptr->lev >= PY_MAX_LEVEL)
1028 	{
1029 		put_str_b(buffer,"Exp to Adv.", 12, 28);
1030 		c_put_str_b(buffer,TERM_L_GREEN, "    *****", 12, 28+11);
1031 	}
1032 	else
1033 	{
1034 		/* prt_lnum_b(buffer,"Exp to Adv.", p_ptr->exp_adv, 12, 28, TERM_L_GREEN); */
1035 	}
1036 
1037 	prt_lnum_b(buffer,"Gold       ", p_ptr->au, 13, 28, TERM_L_GREEN);
1038 
1039 	prt_num_b(buffer,"Max Hit Points ", p_ptr->mhp, 9, 52, TERM_L_GREEN);
1040 
1041 	if (p_ptr->chp >= p_ptr->mhp)
1042 	{
1043 		prt_num_b(buffer,"Cur Hit Points ", p_ptr->chp, 10, 52, TERM_L_GREEN);
1044 	}
1045 	else if (p_ptr->chp > (p_ptr->mhp) / 10)
1046 	{
1047 		prt_num_b(buffer,"Cur Hit Points ", p_ptr->chp, 10, 52, TERM_YELLOW);
1048 	}
1049 	else
1050 	{
1051 		prt_num_b(buffer,"Cur Hit Points ", p_ptr->chp, 10, 52, TERM_RED);
1052 	}
1053 
1054 	prt_num_b(buffer,"Max SP (Mana)  ", p_ptr->msp, 11, 52, TERM_L_GREEN);
1055 
1056 	if (p_ptr->csp >= p_ptr->msp)
1057 	{
1058 		prt_num_b(buffer,"Cur SP (Mana)  ", p_ptr->csp, 12, 52, TERM_L_GREEN);
1059 	}
1060 	else if (p_ptr->csp > (p_ptr->msp) / 10)
1061 	{
1062 		prt_num_b(buffer,"Cur SP (Mana)  ", p_ptr->csp, 12, 52, TERM_YELLOW);
1063 	}
1064 	else
1065 	{
1066 		prt_num_b(buffer,"Cur SP (Mana)  ", p_ptr->csp, 12, 52, TERM_RED);
1067 	}
1068 
1069 	/* Check for history */
1070 	put_str_b(buffer, "(Character Background)", 21, 25);
1071 
1072 	for (i = 0; i < 4; i++)
1073 	{
1074 		put_str_b(buffer,p_ptr->history[i], i + 22, 10);
1075 	}
1076 }
1077 
1078 
1079 /*
1080  * Hack -- Dump a character description file
1081  * This is for server-side character dumps
1082  */
file_character_server(player_type * p_ptr,cptr name)1083 errr file_character_server(player_type *p_ptr, cptr name)
1084 {
1085 	int		i, j, x, y, x1, x2, y1, y2;
1086 	byte		a;
1087 	char		c, attr;
1088 	cptr		paren = ")";
1089 	ang_file*	fff = NULL;
1090 	char		o_name[80];
1091 	char		today[10];
1092 	char		buf[1024];
1093 	cave_view_type status[80];
1094 	char		buffer[100][82];
1095 	time_t ct = time((time_t*)0);
1096 
1097 	// Init buffer...
1098 	for(i=0;i<100;i++)
1099 	{
1100 		for(x=0;x<80;x++)
1101 			buffer[i][x] = ' ';
1102 		buffer[i][80] = '\0';
1103 	}
1104 
1105 	/* Drop priv's
1106 	safe_setuid_drop(); */
1107 
1108 	/* Build the filename */
1109 	path_build(buf, 1024, ANGBAND_DIR_BONE, name);
1110 
1111 	/* Grab priv's
1112 	safe_setuid_grab(); */
1113 
1114 	/* Open the non-existing file */
1115 	if (!file_exists(buf))
1116 	{
1117 		fff = file_open(buf, MODE_WRITE, FTYPE_TEXT);
1118 	}
1119 
1120 	/* Invalid file */
1121 	if (!fff)
1122 	{
1123 		/* Error */
1124 		return (-1);
1125 	}
1126 
1127 	/* Add ladder information, this line is used by the online ladder and
1128 	 * not displayed when viewing a character dump online.
1129 	 */
1130 	strftime(today, 9, "%m/%d/%y", localtime(&ct));
1131 	file_putf(fff, "# %lu|%lu|%-.8s|%-.25s|%c|%2d|%2d|%3d|%3d|%3d|%3d|%-.31s|%d.%d.%d\n",
1132 		(long)total_points(p_ptr),
1133 		(long)p_ptr->au,
1134 		today,
1135 		p_ptr->name,
1136 		p_ptr->male ? 'm' : 'f',
1137 		p_ptr->prace,
1138 		p_ptr->pclass,
1139 		p_ptr->lev,
1140 		p_ptr->died_from_depth,
1141 		p_ptr->max_plv,
1142 		p_ptr->max_dlv,
1143 		p_ptr->died_from_list,
1144 		SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
1145 
1146 #ifndef DEBUG
1147 	/* Leave it at that for characters lower than level 20 */
1148 	if( p_ptr->lev < 20 )
1149 	{
1150 		/* Close dump file */
1151 		file_close(fff);
1152 
1153 		/* Success */
1154 		return (0);
1155 	}
1156 #endif
1157 
1158 	/* Begin dump */
1159 	if (cfg_ironman)
1160 		file_putf(fff, "  [Ironman Mangband %d.%d.%d Character Dump]\n\n",
1161 		        SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
1162 	else
1163 		file_putf(fff, "  [Mangband %d.%d.%d Character Dump]\n\n",
1164 		        SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
1165 
1166 	/* Display the player info */
1167 	display_player_server(p_ptr, buffer);
1168 
1169 	/* Dump the buffer */
1170 	for(i=0;i<26;i++)
1171 	{
1172 		file_putf(fff,"%s\n",buffer[i]);
1173 	}
1174 
1175 	/* Dump the equipment */
1176 	file_putf(fff, "%s", "  [Character Equipment]\n\n");
1177 	for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
1178 	{
1179 		object_desc(0, o_name, sizeof(o_name), &p_ptr->inventory[i], TRUE, 3);
1180 		file_putf(fff, "%c%s %s\n",
1181 		        index_to_label(i), paren, o_name);
1182 	}
1183 	file_putf(fff, "%s", "\n\n");
1184 
1185 	/* Dump the inventory */
1186 	file_putf(fff, "%s", "  [Character Inventory]\n\n");
1187 	for (i = 0; i < INVEN_PACK; i++)
1188 	{
1189 		object_desc(0, o_name, sizeof(o_name), &p_ptr->inventory[i], TRUE, 3);
1190 		file_putf(fff, "%c%s %s\n",
1191 		        index_to_label(i), paren, o_name);
1192 	}
1193 	file_putf(fff, "%s", "\n\n");
1194 
1195 	/* Dump house inventory */
1196 	file_putf(fff, "%s", "  [Home Inventory]\n");
1197 	for (i = 0; i < num_houses; i++)
1198 	{
1199 		if (house_owned_by(p_ptr, i))
1200 		{
1201 			int Depth = houses[i].depth;
1202 			cave_type *c_ptr;
1203 
1204 			file_putf(fff, "%s", "\n"); j = 0;
1205 			for(y=houses[i].y_1; y<=houses[i].y_2;y++)
1206 			{
1207 				for(x=houses[i].x_1; x<=houses[i].x_2;x++)
1208 				{
1209 					/* Paranoia -- unallocated Depth */
1210 					if (!cave[Depth]) continue;
1211 					c_ptr = &cave[Depth][y][x];
1212 					if (c_ptr->o_idx)
1213 					{
1214 						if (j > 12) { file_putf(fff, "%s", "\n"); j = 0; }
1215 						object_desc(0, o_name, sizeof(o_name), &o_list[c_ptr->o_idx], TRUE, 3);
1216 						file_putf(fff, "%c%s %s\n",
1217 			        		index_to_label(j), paren, o_name);
1218 						j++;
1219 					}
1220 				}
1221 			}
1222 		}
1223 	}
1224 	file_putf(fff, "%s", "\n\n");
1225 
1226 	/* Dump character history */
1227 	if(p_ptr->birth_turn.turn || p_ptr->birth_turn.era)
1228 	{
1229 		history_event *evt;
1230 		file_putf(fff, "%s", "  [Character History]\n\n");
1231 		file_putf(fff, "%s", "Time       Dungeon Char  Event\n");
1232 		file_putf(fff, "%s", "           Level   Level\n\n");
1233 		for(evt = p_ptr->charhist; evt; evt = evt->next)
1234 		{
1235 			file_putf(fff, "%s\n", format_history_event(evt));
1236 		}
1237 		file_putf(fff, "%s", "\n\n");
1238 	}
1239 
1240 	/* Dump last messages */
1241 	file_putf(fff, "%s", "  [Last Messages]\n\n");
1242 	i = p_ptr->msg_hist_ptr;
1243 	for(j=0;j<MAX_MSG_HIST;j++)
1244 	{
1245 		if(i >= MAX_MSG_HIST) i = 0;
1246 		if(!STRZERO(p_ptr->msg_log[i]))
1247 			file_putf(fff, "%s\n",p_ptr->msg_log[i]);
1248 		i++;
1249 	}
1250 	file_putf(fff, "%s", "\n\n");
1251 
1252 	/* Dump the scene of death */
1253 	file_putf(fff, "%s", "  [Scene of Death]\n\n");
1254 	/* Get an in bounds area */
1255 	x1 = p_ptr->px - 39;
1256 	x2 = p_ptr->px + 39;
1257 	y1 = p_ptr->py - 10;
1258 	y2 = p_ptr->py + 10;
1259 	if (y1 < 0)
1260 	{
1261 		y2 = y2-y1;
1262 		y1 = 0;
1263 	}
1264 	if (x1 < 0)
1265 	{
1266 		x2 = x2-x1;
1267 		x1 = 0;
1268 	}
1269 	if (y2 > MAX_HGT-1)
1270 	{
1271 		y1 = y1 - (y2-(MAX_HGT-1));
1272 		y2 = MAX_HGT-1;
1273 	}
1274 	if (x2 > MAX_WID-1)
1275 	{
1276 		x1 = x1 - (x2-(MAX_WID-1));
1277 		x2 = MAX_WID-1;
1278 	}
1279 	/* Prepare status line */
1280 	c_prt_status_line(p_ptr, &status[0], 80);
1281 
1282 	/* Describe each row */
1283 	for(y=y1;y<=y2+1;y++)
1284 	{
1285 		for(x=x1;x<=x2;x++)
1286 		{
1287 			/* Get the features */
1288 			if (y > y2)
1289 			{
1290 				/* Hack -- read from status line */
1291 				if (x-x1 < 80) {
1292 					a = status[x-x1].a;
1293 					c = status[x-x1].c;
1294 				} else {
1295 					a = TERM_WHITE; c = ' ';
1296 				}
1297 			}
1298 			else
1299 				map_info(p_ptr, y, x, &a, &c, &a, &c, TRUE);
1300 			/* Hack for the player who is already dead and gone */
1301 			if( (x == p_ptr->px) && (y == p_ptr->py))
1302 			{
1303 				c = '@';
1304 				a = 'W';
1305 			}
1306 			/* translate the attr */
1307 			attr = 'w';
1308 			switch (a)
1309 			{
1310 				case TERM_DARK: attr = 'd'; break;
1311 				case TERM_WHITE: attr = 'w'; break;
1312 				case TERM_SLATE: attr = 's'; break;
1313 				case TERM_ORANGE: attr = 'o'; break;
1314 				case TERM_RED: attr = 'r'; break;
1315 				case TERM_GREEN: attr = 'g'; break;
1316 				case TERM_BLUE: attr = 'b'; break;
1317 				case TERM_UMBER: attr = 'u'; break;
1318 				case TERM_L_DARK: attr = 'D'; break;
1319 				case TERM_L_WHITE: attr = 'W'; break;
1320 				case TERM_VIOLET: attr = 'v'; break;
1321 				case TERM_YELLOW: attr = 'y'; break;
1322 				case TERM_L_RED: attr = 'R'; break;
1323 				case TERM_L_GREEN: attr = 'G'; break;
1324 				case TERM_L_BLUE: attr = 'B'; break;
1325 				case TERM_L_UMBER: attr = 'U'; break;
1326 			}
1327 			/* Config file controls if we output with color codes */
1328 			if(cfg_chardump_color)
1329 			{
1330 				/* Output with attr colour code */
1331 				file_putf(fff,"%c%c",attr,c);
1332 			}
1333 			else
1334 			{
1335 				/* Output plain ASCII */
1336 				file_putf(fff,"%c",c);
1337 			}
1338 		}
1339 		file_putf(fff, "%s", "\n");
1340 	}
1341 	file_putf(fff, "%s", "\n\n");
1342 
1343 
1344 	/* Close it */
1345 	file_close(fff);
1346 
1347 	/* Success */
1348 	return (0);
1349 }
1350 
1351 
1352 
1353 
file_peruse_next(player_type * p_ptr,char query,int next)1354 int file_peruse_next(player_type *p_ptr, char query, int next)
1355 {
1356 	/* Process query */
1357 	if (query)
1358 	{
1359 		if (query == '1') /* 'End' */
1360 			p_ptr->interactive_line = p_ptr->interactive_size-20;
1361 		else /* Other keys */
1362 			common_peruse(p_ptr, query);
1363 
1364 		/* Adjust viewport boundaries */
1365 		if (p_ptr->interactive_line > p_ptr->interactive_size-20)
1366 			p_ptr->interactive_line = p_ptr->interactive_size-20;
1367 		if (p_ptr->interactive_line < 0)
1368 			p_ptr->interactive_line = 0;
1369 
1370 		/* Shift window! */
1371 		if ((p_ptr->interactive_line+20 > p_ptr->interactive_next+MAX_TXT_INFO)
1372 			|| (p_ptr->interactive_line < p_ptr->interactive_next))
1373 		{
1374 			next = p_ptr->interactive_line - MAX_TXT_INFO / 2;
1375 		}
1376 
1377 		/* Adjust window boundaries */
1378 		if (next > p_ptr->interactive_size - MAX_TXT_INFO)
1379 			next = p_ptr->interactive_size - MAX_TXT_INFO;
1380 		if (next < 0) next = 0;
1381 	}
1382 	return next;
1383 }
1384 
1385 
1386 /*
1387  * On-Line help.
1388  *
1389  * Process user commands, access sub-menu entries and browse files.
1390  * This function manages a virtual 'window' which buffers file
1391  * contents using "copy_file_info" function.
1392  */
common_file_peruse(player_type * p_ptr,char query)1393 void common_file_peruse(player_type *p_ptr, char query)
1394 {
1395 	int next = p_ptr->interactive_next;
1396 
1397 	/* Enter sub-menu */
1398 	if (isalpha(query))
1399 	{
1400 		/* Extract the requested menu item */
1401 		int k = A2I(query);
1402 
1403 		/* Verify the menu item */
1404 		if ((k >= 0) && (k <= 25) && !STRZERO(p_ptr->interactive_hook[k]))
1405 		{
1406 			/* Paranoia -- free string */
1407 			string_free(p_ptr->interactive_file);
1408 			/* Select that file */
1409 			p_ptr->interactive_file = string_make(p_ptr->interactive_hook[k]);
1410 			/* Hack: enforce update */
1411 			p_ptr->interactive_next = -1;
1412 			next = 0;
1413 			/* Query processed */
1414 			query = 0;
1415 		}
1416 	}
1417 
1418 	/* Use default file */
1419 	if (!p_ptr->interactive_file)
1420 	{
1421 		/* Paranoia -- free string */
1422 		string_free(p_ptr->interactive_file);
1423 		/* Select default file */
1424 		p_ptr->interactive_file = string_make("help.hlp");
1425 		/* Hack: enforce update */
1426 		p_ptr->interactive_next = -1;
1427 		next = 0;
1428 	}
1429 
1430 	/* We're just starting. Reset counter */
1431 	if (!query)
1432 	{
1433 		p_ptr->interactive_line = 0;
1434 	}
1435 
1436 	/* We're done. Clear file, exit */
1437 	if (query == ESCAPE)
1438 	{
1439 		if (p_ptr->interactive_file)
1440 		{
1441 			string_free(p_ptr->interactive_file);
1442 			p_ptr->interactive_file = NULL;
1443 		}
1444 		p_ptr->special_file_type = 0;
1445 		return;
1446 	}
1447 
1448 	/* Process query */
1449 	if (query)
1450 	{
1451 		next = file_peruse_next(p_ptr, query, next);
1452 	}
1453 
1454 	/* Hack -- something overwrote "info" */
1455 	if (!p_ptr->last_info_line)
1456 	{
1457 		p_ptr->interactive_next = -1;
1458 	}
1459 
1460 	/* Update file */
1461 	if (next != p_ptr->interactive_next)
1462 	{
1463 		p_ptr->interactive_next = next;
1464 		copy_file_info(p_ptr, p_ptr->interactive_file, next, 0);
1465 	}
1466 }
1467 
1468 /*
1469  * Read a file and copy a portion of it into player's "info[]" array.
1470  *
1471  * TODO: Add 'search' from do_cmd_help_aux()
1472  *
1473  */
copy_file_info(player_type * p_ptr,cptr name,int line,int color)1474 void copy_file_info(player_type *p_ptr, cptr name, int line, int color)
1475 {
1476 	int i = 0, k;
1477 
1478 	/* Current help file */
1479 	ang_file* fff = NULL;
1480 
1481 	/* Number of "real" lines passed by */
1482 	int		next = 0;
1483 
1484 	/* Path buffer */
1485 	char	path[1024];
1486 
1487 	/* General buffer */
1488 	char	buf[1024];
1489 
1490 	/* Strlen */
1491 	int 	len;
1492 
1493 	/* Build the filename */
1494 	path_build(path, 1024, ANGBAND_DIR_HELP, name);
1495 
1496 	/* Open the file */
1497 	fff = file_open(path, MODE_READ, -1);
1498 
1499 	/* Oops */
1500 	if (!fff)
1501 	{
1502 		/* Message */
1503 		msg_format(p_ptr, "Cannot open '%s'.", name);
1504 		msg_print(p_ptr, NULL);
1505 
1506 		/* Oops */
1507 		return;
1508 	}
1509 
1510 	/* Wipe the hooks */
1511 	for (k = 0; k < 26; k++) p_ptr->interactive_hook[k][0] = '\0';
1512 
1513 	/* Parse the file */
1514 	while (TRUE)
1515 	{
1516 		byte attr = TERM_WHITE;
1517 
1518 		/* Read a line or stop */
1519 		if (!file_getl(fff, buf, 1024)) break;
1520 
1521 		/* XXX Parse "menu" items */
1522 		if (prefix(buf, "***** "))
1523 		{
1524 			char b1 = '[', b2 = ']';
1525 
1526 			/* Notice "menu" requests */
1527 			if ((buf[6] == b1) && isalpha(buf[7]) &&
1528 			    (buf[8] == b2) && (buf[9] == ' '))
1529 			{
1530 				/* Extract the menu item */
1531 				k = A2I(buf[7]);
1532 
1533 				/* Store the menu item (if valid) */
1534 				if ((k >= 0) && (k < 26))
1535 					my_strcpy(p_ptr->interactive_hook[k], buf + 10, sizeof(p_ptr->interactive_hook[0]));
1536 			}
1537 
1538 			/* Skip this */
1539 			continue;
1540 		}
1541 
1542 		/* Get length */
1543 		len = strlen(buf);
1544 
1545 		/* Count the "real" lines */
1546 		next++;
1547 
1548 		/* Wait for needed one */
1549 		if (next <= line) continue;
1550 
1551 		/* Too much */
1552 		if (i >= MAX_TXT_INFO) continue;
1553 
1554 		/* Extract color */
1555 		if (color) attr = color_char_to_attr(buf[0]);
1556 
1557 		/* Clear rest of line with spaces */
1558 		for (k = len; k < 80 + color; k++)
1559 		{
1560 			buf[k] = ' ';
1561 		}
1562 
1563 		/* Dump the line */
1564 		for (k = 0; k < 80; k++)
1565 		{
1566 			p_ptr->info[i][k].a = attr;
1567 			p_ptr->info[i][k].c = buf[k+color];
1568 		}
1569 
1570 		/* Count the "info[]" lines */
1571 		i++;
1572 	}
1573 
1574 	/* Save last "real" line */
1575 	p_ptr->interactive_size = next;
1576 
1577 	/* Save last dumped line */
1578 	p_ptr->last_info_line = i - 1;
1579 
1580 	/* Close the file */
1581 	file_close(fff);
1582 }
1583 
1584 #if 0
1585 /*
1586  * Recursive "help file" perusal.  Return FALSE on "ESCAPE".
1587  *
1588  * XXX XXX XXX Consider using a temporary file.
1589  *
1590  */
1591 static bool do_cmd_help_aux(player_type *p_ptr, cptr name, cptr what, int line, int color)
1592 {
1593 	int		i, k;
1594 
1595 	/* Number of "real" lines passed by */
1596 	int		next = 0;
1597 
1598 	/* Number of "real" lines in the file */
1599 	int		size = 0;
1600 
1601 	/* Backup value for "line" */
1602 	int		back = 0;
1603 
1604 	/* This screen has sub-screens */
1605 	bool	menu = FALSE;
1606 
1607 	/* Current help file */
1608 	ang_file* fff = NULL;
1609 
1610 	/* Find this string (if any) */
1611 	cptr	find = NULL;
1612 
1613 	/* Hold a string to find */
1614 	char	finder[128];
1615 
1616 	/* Hold a string to show */
1617 	char	shower[128];
1618 
1619 	/* Describe this thing */
1620 	char	caption[128];
1621 
1622 	/* Path buffer */
1623 	char	path[1024];
1624 
1625 	/* General buffer */
1626 	char	buf[1024];
1627 
1628 	/* Sub-menu information */
1629 	char	hook[10][32];
1630 
1631 
1632 	/* Wipe finder */
1633 	strcpy(finder, "");
1634 
1635 	/* Wipe shower */
1636 	strcpy(shower, "");
1637 
1638 	/* Wipe caption */
1639 	strcpy(caption, "");
1640 
1641 	/* Wipe the hooks */
1642 	for (i = 0; i < 10; i++) hook[i][0] = '\0';
1643 
1644 
1645 	/* Hack XXX XXX XXX */
1646 	if (what)
1647 	{
1648 		/* Caption */
1649 		strcpy(caption, what);
1650 
1651 		/* Access the "file" */
1652 		strcpy(path, name);
1653 
1654 		/* Open */
1655 		fff = file_open(path, MODE_READ, -1);
1656 	}
1657 
1658 	/* Look in "help" */
1659 	if (!fff)
1660 	{
1661 		/* Caption */
1662 		sprintf(caption, "Help file '%s'", name);
1663 
1664 		/* Build the filename */
1665 		path_build(path, 1024, ANGBAND_DIR_HELP, name);
1666 
1667 		/* Open the file */
1668 		fff = file_open(path, MODE_READ, -1);
1669 	}
1670 
1671 	/* Oops */
1672 	if (!fff)
1673 	{
1674 		/* Message */
1675 		msg_format(p_ptr, "Cannot open '%s'.", name);
1676 		msg_print(Ind, NULL);
1677 
1678 		/* Oops */
1679 		return (TRUE);
1680 	}
1681 
1682 
1683 	/* Pre-Parse the file */
1684 	while (TRUE)
1685 	{
1686 		/* Read a line or stop */
1687 		if (!file_getl(fff, buf, 1024)) break;
1688 
1689 		/* XXX Parse "menu" items */
1690 		if (prefix(buf, "***** "))
1691 		{
1692 			char b1 = '[', b2 = ']';
1693 
1694 			/* Notice "menu" requests */
1695 			if ((buf[6] == b1) && isdigit(buf[7]) &&
1696 			    (buf[8] == b2) && (buf[9] == ' '))
1697 			{
1698 				/* This is a menu file */
1699 				menu = TRUE;
1700 
1701 				/* Extract the menu item */
1702 				k = buf[7] - '0';
1703 
1704 				/* Extract the menu item */
1705 				strcpy(hook[k], buf + 10);
1706 			}
1707 
1708 			/* Skip this */
1709 			continue;
1710 		}
1711 
1712 		/* Count the "real" lines */
1713 		next++;
1714 	}
1715 
1716 	/* Save the number of "real" lines */
1717 	size = next;
1718 
1719 
1720 
1721 	/* Display the file */
1722 #if 0
1723 	while (TRUE)
1724 	{
1725 		/* Clear screen */
1726 		Term_clear();
1727 
1728 #endif
1729 
1730 		/* Restart when necessary */
1731 		if (line >= size) Players[Ind]->interactive_line = line = 0;
1732 
1733 
1734 		/* Re-open the file if needed */
1735 		if (next > line)
1736 		{
1737 			/* Close it */
1738 			file_close(fff);
1739 
1740 			/* Hack -- Re-Open the file */
1741 			fff = file_open(path, MODE_READ, -1);
1742 
1743 			/* Oops */
1744 			if (!fff) return (FALSE);
1745 
1746 			/* File has been restarted */
1747 			next = 0;
1748 		}
1749 
1750 		/* Skip lines if needed */
1751 		for (; next < line; next++)
1752 		{
1753 			/* Skip a line */
1754 			if (!file_getl(fff, buf, 1024)) break;
1755 		}
1756 
1757 
1758 		/* Dump the next 20 lines of the file */
1759 		for (i = 0; i < 20; )
1760 		{
1761 			byte attr = TERM_WHITE;
1762 
1763 			/* Hack -- track the "first" line */
1764 			if (!i) line = next;
1765 
1766 			/* Get a line of the file or stop */
1767 			if (!file_getl(fff, buf, 1024)) break;
1768 
1769 			/* Hack -- skip "special" lines */
1770 			if (prefix(buf, "***** ")) continue;
1771 
1772 			/* Count the "real" lines */
1773 			next++;
1774 
1775 			/* Hack -- keep searching */
1776 			if (find && !i && !strstr(buf, find)) continue;
1777 
1778 			/* Hack -- stop searching */
1779 			find = NULL;
1780 
1781 			/* Extract color */
1782 			if (color) attr = color_char_to_attr(buf[0]);
1783 
1784 			/* Hack -- show matches */
1785 			if (shower[0] && strstr(buf, shower)) attr = TERM_YELLOW;
1786 
1787 			/* Dump the line */
1788 			Send_special_line(Ind, size, i, attr, &buf[color]);
1789 
1790 			/* Count the printed lines */
1791 			i++;
1792 		}
1793 
1794 		/* Hack -- failed search */
1795 		if (find)
1796 		{
1797 			bell();
1798 			line = back;
1799 			find = NULL;
1800 			return (TRUE);
1801 		}
1802 
1803 		/* Inform about empty file/list */
1804 		if (!i)
1805 			Send_special_line(Ind, 1, 0, TERM_WHITE, "  (nothing)");
1806 
1807 #if 0
1808 		/* Show a general "title" */
1809 		prt(format("[MAngband %d.%d.%d, %s, Line %d/%d]",
1810 		           SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH,
1811 		           caption, line, size), 0, 0);
1812 
1813 
1814 		/* Prompt -- menu screen */
1815 		if (menu)
1816 		{
1817 			/* Wait for it */
1818 			prt("[Press a Number, or ESC to exit.]", 23, 0);
1819 		}
1820 
1821 		/* Prompt -- small files */
1822 		else if (size <= 20)
1823 		{
1824 			/* Wait for it */
1825 			prt("[Press ESC to exit.]", 23, 0);
1826 		}
1827 
1828 		/* Prompt -- large files */
1829 		else
1830 		{
1831 			/* Wait for it */
1832 			prt("[Press Return, Space, -, =, /, or ESC to exit.]", 23, 0);
1833 		}
1834 
1835 		/* Get a keypress */
1836 		k = inkey();
1837 
1838 		/* Hack -- return to last screen */
1839 		if (k == '?') break;
1840 
1841 		/* Hack -- try showing */
1842 		if (k == '=')
1843 		{
1844 			prt("Show: ", 23, 0);
1845 			(void)askfor_aux(shower, 80);
1846 		}
1847 
1848 		/* Hack -- try finding */
1849 		if (k == '/')
1850 		{
1851 			prt("Find: ", 23, 0);
1852 			if (askfor_aux(finder, 80))
1853 			{
1854 				find = finder;
1855 				back = line;
1856 				line = line + 1;
1857 			}
1858 		}
1859 
1860 		/* Hack -- go to a specific line */
1861 		if (k == '#')
1862 		{
1863 			char tmp[80];
1864 			prt("Goto Line: ", 23, 0);
1865 			strcpy(tmp, "0");
1866 			if (askfor_aux(tmp, 80))
1867 			{
1868 				line = atoi(tmp);
1869 			}
1870 		}
1871 
1872 		/* Hack -- go to a specific file */
1873 		if (k == '%')
1874 		{
1875 			char tmp[80];
1876 			prt("Goto File: ", 23, 0);
1877 			strcpy(tmp, "help.hlp");
1878 			if (askfor_aux(tmp, 80))
1879 			{
1880 				if (!do_cmd_help_aux(tmp, NULL, 0)) k = ESCAPE;
1881 			}
1882 		}
1883 
1884 		/* Hack -- Allow backing up */
1885 		if (k == '-')
1886 		{
1887 			line = line - 10;
1888 			if (line < 0) line = 0;
1889 		}
1890 
1891 		/* Hack -- Advance a single line */
1892 		if ((k == '\n') || (k == '\r'))
1893 		{
1894 			line = line + 1;
1895 		}
1896 
1897 		/* Advance one page */
1898 		if (k == ' ')
1899 		{
1900 			line = line + 20;
1901 		}
1902 
1903 		/* Recurse on numbers */
1904 		if (menu && isdigit(k) && hook[k-'0'][0])
1905 		{
1906 			/* Recurse on that file */
1907 			if (!do_cmd_help_aux(hook[k-'0'], NULL, 0)) k = ESCAPE;
1908 		}
1909 
1910 		/* Exit on escape */
1911 		if (k == ESCAPE) break;
1912 	}
1913 
1914 #endif
1915 
1916 	/* Close the file */
1917 	file_close(fff);
1918 
1919 	/* Escape */
1920 	if (k == ESCAPE) return (FALSE);
1921 
1922 	/* Normal return */
1923 	return (TRUE);
1924 }
1925 
1926 
1927 /*
1928  * Peruse the On-Line-Help, starting at the given file.
1929  *
1930  * Disabled --KLJ--
1931  */
1932 void do_cmd_help(player_type *p_ptr, int line)
1933 {
1934 	cptr name = "help.hlp";
1935 
1936 	/* Peruse the main help file */
1937 	(void)do_cmd_help_aux(p_ptr, name, NULL, line, FALSE);
1938 }
1939 #endif
1940 
1941 
1942 /*
1943  * Hack -- display the contents of a file on the screen
1944  *
1945  * XXX XXX XXX Use this function for commands such as the
1946  * "examine object" command.
1947  */
show_file(player_type * p_ptr,cptr name,cptr what,int line,int color)1948 errr show_file(player_type *p_ptr, cptr name, cptr what, int line, int color)
1949 {
1950 	/* Prepare */
1951 	clear_from(p_ptr, 0);
1952 
1953 	/* Peruse the requested file */
1954 	copy_file_info(p_ptr, name, line, color);
1955 
1956 	/* Send header */
1957 	Send_special_other(p_ptr, (char*)what);
1958 
1959 	/* Success */
1960 	return (0);
1961 }
1962 
1963 /* Given a player name in "nick_name", write out a new version to "wptr",
1964  * where a) all words are capitalized b) double-spaces are removed.
1965  * For example, "juG  tHe brAve" should become "Jug The Brave".
1966  * If "bptr" is not NULL, the "base" version will be written to
1967  * it, replacing all spaces with underscore (-> "Jug_The_Brave").
1968  * It is assumed that the only characters that exist in "nick_name" are
1969  *  letters (a-zA-Z), digits (0-9) and space ( ).
1970  * Some other function should've taken care of that.
1971  * It is assumed that "wptr" is at least of MAX_CHARS length.
1972  * It is assumed that "bptr" is at least of MAX_CHARS length.
1973  *
1974  * Note: because we actually want to allow names that do not follow
1975  * this format (i.e. "ZalPriest", "Master_of_Puppets"), this function
1976  * SHOULD NOT be called! However, if you're on an machine with
1977  * case-insensitive filesystem (looking at you, Win32), this function
1978  * MUST be called, as early as possible, to prevent savefile name collision.
1979  */
rewrite_player_name(char * wptr,char * bptr,const char * nick_name)1980 int rewrite_player_name(char *wptr, char *bptr, const char *nick_name)
1981 {
1982 	/* Re-write nick, removing double spaces */
1983 	const char *p;
1984 	bool recap;
1985 	const char *wptr_start;
1986 	const char *bptr_start;
1987 
1988 	/* Nothing to do */
1989 	if (!wptr && !bptr) return 0;
1990 
1991 	wptr_start = wptr;
1992 	bptr_start = bptr;
1993 
1994 	if (wptr) *wptr = '\0';
1995 	if (bptr) *bptr = '\0';
1996 	recap = TRUE;
1997 	for (p = nick_name; *p; p++)
1998 	{
1999 		char c = *p;
2000 		if (c == ' ') /* A space */
2001 		{
2002 			if (recap) continue; /* Skip 2nd, 3rd, Nth space */
2003 			recap = TRUE;
2004 		}
2005 		else /* It's a symbol or digit */
2006 		{
2007 			if (isalpha(c))
2008 			{
2009 				c = recap ? toupper(c) : tolower(c);
2010 			}
2011 			recap = FALSE;
2012 		}
2013 		/* Save one char */
2014 		if (wptr) *wptr++ = c;
2015 
2016 		/* Save "base" version */
2017 		if (bptr) *bptr++ = (c == ' ' ? '_': c);
2018 	}
2019 	/* Terminate strings */
2020 	if (wptr) *wptr = '\0';
2021 	if (bptr) *bptr = '\0';
2022 
2023 	/* Right-trim spaces */
2024 	if (wptr) for ( ; wptr-- > wptr_start; )
2025 	{
2026 		if (*wptr == ' ') *wptr = '\0';
2027 		else break;
2028 	}
2029 	if (bptr) for ( ; bptr-- > bptr_start; )
2030 	{
2031 		if (*bptr == '_') *bptr = '\0';
2032 		else break;
2033 	}
2034 
2035 	if (!wptr) return bptr - bptr_start;
2036 	return wptr - wptr_start;
2037 }
2038 
2039 /*
2040  * XXXXXXXXX
2041  */
process_player_name_aux(cptr name,cptr base,bool sf)2042 int process_player_name_aux(cptr name, cptr base, bool sf)
2043 {
2044 	int i, k = 0;
2045 	char local_base[MAX_CHARS];
2046 	char *basename = (char*) base;
2047 
2048 
2049 	/* Cannot be too long */
2050 	if (strlen(name) > MAX_NAME_LEN)
2051 	{
2052 		/* Abort */
2053 		return -1;
2054 	}
2055 
2056 	/* Cannot contain "icky" characters */
2057 	for (i = 0; name[i]; i++)
2058 	{
2059 		/* No control characters */
2060 		if (iscntrl(name[i]))
2061 		{
2062 			/* Abort */
2063 			return -2;
2064 		}
2065 	}
2066 
2067 	if (base == NULL) basename = &local_base[0];
2068 
2069 #ifdef MACINTOSH
2070 
2071 	/* Extract "useful" letters */
2072 	for (i = 0; name[i]; i++)
2073 	{
2074 		char c = name[i];
2075 
2076 		/* Convert "dot" to "underscore" */
2077 		if (c == '.') c = '_';
2078 
2079 		/* Accept all the letters */
2080 		basename[k++] = c;
2081 	}
2082 
2083 #else
2084 
2085 	/* Extract "useful" letters */
2086 	for (i = 0; name[i]; i++)
2087 	{
2088 		char c = name[i];
2089 
2090 		/* Accept some letters */
2091 		if (isalpha(c) || isdigit(c)) basename[k++] = c;
2092 
2093 		/* Convert space, dot, and underscore to underscore */
2094 		else if (strchr(". _", c)) basename[k++] = '_';
2095 	}
2096 
2097 #endif
2098 
2099 	/* Terminate */
2100 	basename[k] = '\0';
2101 
2102 	/* Require a "base" name */
2103 	if (!basename[0]) strcpy(basename, "PLAYER");
2104 
2105 	/* Change the savefile name */
2106 	if (sf)
2107 	{
2108 		char temp[128];
2109 
2110 		/* Rename the savefile, using the player_base */
2111 		(void)sprintf(temp, "%s", basename);
2112 
2113 #ifdef VM
2114 		/* Hack -- support "flat directory" usage on VM/ESA */
2115 		(void)sprintf(temp, "%s.sv", player_base);
2116 #endif /* VM */
2117 
2118 		/* Build the filename */
2119 		path_build((char*)name, 1024, ANGBAND_DIR_SAVE, temp);
2120 	}
2121 
2122 	/* Success */
2123 	return 0;
2124 }
2125 
2126 
2127 
2128 /*
2129  * Process the player name.
2130  * Extract a clean "base name".
2131  * Build the savefile name if needed.
2132  */
process_player_name(player_type * p_ptr,bool sf)2133 bool process_player_name(player_type *p_ptr, bool sf)
2134 {
2135 	int ret;
2136 
2137 #ifdef SAVEFILE_MUTABLE
2138 
2139 	/* Accept */
2140 	sf = TRUE;
2141 
2142 #endif
2143 
2144 	ret = process_player_name_aux(p_ptr->name, p_ptr->basename, FALSE);
2145 
2146 	/* Name is too long or contained illegal characters */
2147 	if (ret < 0)
2148 	{
2149 		/* Abort */
2150 		return FALSE;
2151 	}
2152 
2153 	/* Change the savefile name */
2154 	if (sf)
2155 	{
2156 		char temp[128];
2157 
2158 		/* Rename the savefile, using the player_base */
2159 		(void)sprintf(temp, "%s", p_ptr->basename);
2160 
2161 #ifdef VM
2162 		/* Hack -- support "flat directory" usage on VM/ESA */
2163 		(void)sprintf(temp, "%s.sv", player_base);
2164 #endif /* VM */
2165 
2166 		/* Build the filename */
2167 		path_build(p_ptr->savefile, 1024, ANGBAND_DIR_SAVE, temp);
2168 	}
2169 
2170 	/* Success */
2171 	return TRUE;
2172 }
2173 
2174 
2175 /*
2176  * Gets a name for the character, reacting to name changes.
2177  *
2178  * Assumes that "display_player()" has just been called
2179  * XXX Perhaps we should NOT ask for a name (at "birth()") on Unix?
2180  *
2181  * The name should be sent to us from the client, so this is unnecessary --KLJ--
2182  */
get_name(player_type * p_ptr)2183 void get_name(player_type *p_ptr)
2184 {
2185 }
2186 
2187 
2188 
2189 /*
2190  * Hack -- commit suicide
2191  */
do_cmd_suicide(player_type * p_ptr)2192 void do_cmd_suicide(player_type *p_ptr)
2193 {
2194 	/* Mark as suicide */
2195 	p_ptr->alive = FALSE;
2196 
2197 	/* Hack -- set the cause of death */
2198 	if (!p_ptr->ghost)
2199 	{
2200 		strcpy(p_ptr->died_from_list, "self-inflicted wounds");
2201 		p_ptr->died_from_depth = p_ptr->dun_depth;
2202 	}
2203 
2204 	/* Hack -- clear ghost */
2205 	p_ptr->ghost = FALSE;
2206 
2207 	if (p_ptr->total_winner) kingly(p_ptr);
2208 
2209 	/* Queue "death", to be handled in dungeon() tick, so it happens inside game turns */
2210 	p_ptr->death = TRUE;
2211 }
2212 
2213 
2214 
2215 /*
2216  * Save a character
2217  */
do_cmd_save_game(player_type * p_ptr)2218 void do_cmd_save_game(player_type *p_ptr)
2219 {
2220 	/* Disturb the player */
2221 	disturb(p_ptr, 1, 0);
2222 
2223 	/* Clear messages */
2224 	msg_print(p_ptr, NULL);
2225 
2226 	/* Handle stuff */
2227 	handle_stuff(p_ptr);
2228 
2229 	/* Message */
2230 	msg_print(p_ptr, "Saving game...");
2231 
2232 	/* Refresh */
2233 	/*Term_fresh();*/
2234 
2235 	/* The player is not dead */
2236 	(void)strcpy(p_ptr->died_from, "(saved)");
2237 
2238 	/* Forbid suspend */
2239 	signals_ignore_tstp();
2240 
2241 	/* Save the player */
2242 	if (save_player(p_ptr))
2243 	{
2244 		msg_print(p_ptr, "Saving game... done.");
2245 	}
2246 
2247 	/* Save failed (oops) */
2248 	else
2249 	{
2250 		msg_print(p_ptr, "Saving game... failed!");
2251 	}
2252 
2253 	/* Allow suspend again */
2254 	signals_handle_tstp();
2255 
2256 	/* Refresh */
2257 	/*Term_fresh();*/
2258 
2259 	/* Note that the player is not dead */
2260 	(void)strcpy(p_ptr->died_from, "(alive and well)");
2261 }
2262 
2263 
2264 
2265 /*
2266  * Hack -- Calculates the total number of points earned		-JWT-
2267  */
total_points(player_type * p_ptr)2268 long total_points(player_type *p_ptr)
2269 {
2270 	/* Standard scoring */
2271 	long score = (p_ptr->max_exp + (100 * p_ptr->max_dlv));
2272 
2273 	/* Remove 10% for energy_buildup */
2274 	if (option_p(p_ptr,ENERGY_BUILDUP))
2275 	{
2276 		score = (long)(score * 0.9);
2277 	}
2278 
2279 	/* Add 50% for unburdened monsters */
2280 	if (!option_p(p_ptr,MONSTER_RECOIL))
2281 	{
2282 		score = (long)(score * 1.5);
2283 	}
2284 
2285 	/* We award a 50% score bonus for bravery with no_ghost characters */
2286 	if (option_p(p_ptr, NO_GHOST) && !cfg_ironman)
2287 	{
2288 		score = (long)(score * 1.5);
2289 	}
2290 
2291 	/* Standard scoring */
2292 	return score;
2293 }
2294 
2295 
2296 
2297 
2298 /*
2299  * Display some character info
2300  *
2301  * FIXME -- This is very broken.  There is no home.  There is no way to get
2302  *   most of this information transferred to the client.  This isn't used all
2303  *   that often.  I'll worry about it later.  --KLJ--
2304  */
show_info(player_type * p_ptr)2305 static void show_info(player_type *p_ptr)
2306 {
2307 #if 0
2308 	int			i, j, k;
2309 
2310 	object_type		*o_ptr;
2311 
2312 	store_type		*st_ptr = &store[7];
2313 
2314 
2315 	/* Hack -- Know everything in the inven/equip */
2316 	for (i = 0; i < INVEN_TOTAL; i++)
2317 	{
2318 		o_ptr = &inventory[i];
2319 		if (o_ptr->k_idx)
2320 		{
2321 			object_aware(p_ptr, o_ptr);
2322 			object_known(o_ptr);
2323 		}
2324 	}
2325 
2326 	/* Hack -- Know everything in the home */
2327 	for (i = 0; i < st_ptr->stock_num; i++)
2328 	{
2329 		o_ptr = &st_ptr->stock[i];
2330 		if (o_ptr->k_idx)
2331 		{
2332 			object_aware(p_ptr, o_ptr);
2333 			object_known(o_ptr);
2334 		}
2335 	}
2336 
2337 	/* Hack -- Recalculate bonuses */
2338 	p_ptr->update |= (PU_BONUS);
2339 
2340 	/* Handle stuff */
2341 	handle_stuff();
2342 
2343 	/* Flush all input keys */
2344 	flush();
2345 
2346 	/* Flush messages */
2347 	msg_print(NULL);
2348 
2349 
2350 	/* Describe options */
2351 	prt("You may now dump a character record to one or more files.", 21, 0);
2352 	prt("Then, hit RETURN to see the character, or ESC to abort.", 22, 0);
2353 
2354 	/* Dump character records as requested */
2355 	while (TRUE)
2356 	{
2357 		char out_val[160];
2358 
2359 		/* Prompt */
2360 		put_str("Filename: ", 23, 0);
2361 
2362 		/* Default */
2363 		strcpy(out_val, "");
2364 
2365 		/* Ask for filename (or abort) */
2366 		if (!askfor_aux(out_val, 60)) return;
2367 
2368 		/* Return means "show on screen" */
2369 		if (!out_val[0]) break;
2370 
2371 		/* Dump a character file */
2372 		(void)file_character(out_val, FALSE);
2373 	}
2374 
2375 
2376 	/* Show player on screen */
2377 	display_player(FALSE);
2378 
2379 	/* Prompt for inventory */
2380 	prt("Hit any key to see more information (ESC to abort): ", 23, 0);
2381 
2382 	/* Allow abort at this point */
2383 	if (inkey() == ESCAPE) return;
2384 
2385 
2386 	/* Show equipment and inventory */
2387 
2388 	/* Equipment -- if any */
2389 	if (equip_cnt)
2390 	{
2391 		Term_clear();
2392 		item_tester_full = TRUE;
2393 		show_equip();
2394 		prt("You are using: -more-", 0, 0);
2395 		if (inkey() == ESCAPE) return;
2396 	}
2397 
2398 	/* Inventory -- if any */
2399 	if (inven_cnt)
2400 	{
2401 		Term_clear();
2402 		item_tester_full = TRUE;
2403 		show_inven();
2404 		prt("You are carrying: -more-", 0, 0);
2405 		if (inkey() == ESCAPE) return;
2406 	}
2407 
2408 
2409 
2410 	/* Home -- if anything there */
2411 	if (st_ptr->stock_num)
2412 	{
2413 		/* Display contents of the home */
2414 		for (k = 0, i = 0; i < st_ptr->stock_num; k++)
2415 		{
2416 			/* Clear screen */
2417 			Term_clear();
2418 
2419 			/* Show 12 items */
2420 			for (j = 0; (j < 12) && (i < st_ptr->stock_num); j++, i++)
2421 			{
2422 				char o_name[80];
2423 				char tmp_val[80];
2424 
2425 				/* Acquire item */
2426 				o_ptr = &st_ptr->stock[i];
2427 
2428 				/* Print header, clear line */
2429 				sprintf(tmp_val, "%c) ", I2A(j));
2430 				prt(tmp_val, j+2, 4);
2431 
2432 				/* Display object description */
2433 				object_desc(o_name, o_ptr, TRUE, 3);
2434 				c_put_str(tval_to_attr[o_ptr->tval], o_name, j+2, 7);
2435 			}
2436 
2437 			/* Caption */
2438 			prt(format("Your home contains (page %d): -more-", k+1), 0, 0);
2439 
2440 			/* Wait for it */
2441 			if (inkey() == ESCAPE) return;
2442 		}
2443 	}
2444 #endif
2445 }
2446 
2447 
2448 
2449 
2450 
2451 /*
2452  * Semi-Portable High Score List Entry (128 bytes) -- BEN
2453  *
2454  * All fields listed below are null terminated ascii strings.
2455  *
2456  * In addition, the "number" fields are right justified, and
2457  * space padded, to the full available length (minus the "null").
2458  *
2459  * Note that "string comparisons" are thus valid on "pts".
2460  */
2461 
2462 typedef struct high_score high_score;
2463 
2464 struct high_score
2465 {
2466 	char what[8];		/* Version info (string) */
2467 
2468 	char pts[10];		/* Total Score (number) */
2469 
2470 	char gold[10];		/* Total Gold (number) */
2471 
2472 	char turns[20];		/* Turns Taken (number) */
2473 
2474 	char day[10];		/* Time stamp (string) */
2475 
2476 	char who[16];		/* Player Name (string) */
2477 
2478 	char uid[8];		/* Player UID (number) */
2479 
2480 	char sex[2];		/* Player Sex (string) */
2481 	char p_r[3];		/* Player Race (number) */
2482 	char p_c[3];		/* Player Class (number) */
2483 
2484 	char cur_lev[4];		/* Current Player Level (number) */
2485 	char cur_dun[4];		/* Current Dungeon Level (number) */
2486 	char max_lev[4];		/* Max Player Level (number) */
2487 	char max_dun[4];		/* Max Dungeon Level (number) */
2488 
2489 	char how[32];		/* Method of death (string) */
2490 };
2491 
2492 
2493 
2494 /*
2495  * The "highscore" file descriptor, if available.
2496  */
2497 static ang_file* highscore_fd = NULL;
2498 
2499 
2500 /*
2501  * Seek score 'i' in the highscore file
2502  */
highscore_seek(int i)2503 static int highscore_seek(int i)
2504 {
2505 	/* Seek for the requested record */
2506 	return (file_seek(highscore_fd, (huge)(i) * sizeof(high_score))) ? 0 : -1;
2507 }
2508 
2509 
2510 /*
2511  * Read one score from the highscore file
2512  */
highscore_read(high_score * score)2513 static errr highscore_read(high_score *score)
2514 {
2515 	/* Read the record, note failure */
2516 	return (file_read(highscore_fd, (char*)(score), sizeof(high_score))) > 0 ? 0 : -1;
2517 }
2518 
2519 
2520 /*
2521  * Write one score to the highscore file
2522  */
highscore_write(high_score * score)2523 static int highscore_write(high_score *score)
2524 {
2525 	/* Write the record, note failure */
2526 	return (file_write(highscore_fd, (char*)(score), sizeof(high_score))) ? 0 : -1;
2527 }
2528 
2529 
2530 
2531 
2532 /*
2533  * Just determine where a new score *would* be placed
2534  * Return the location (0 is best) or -1 on failure
2535  */
highscore_where(high_score * score)2536 static int highscore_where(high_score *score)
2537 {
2538 	int			i;
2539 
2540 	high_score		the_score;
2541 
2542 	/* Paranoia -- it may not have opened */
2543 	if (highscore_fd == NULL) return (-1);
2544 
2545 	/* Go to the start of the highscore file */
2546 	if (highscore_seek(0)) return (-1);
2547 
2548 	/* Read until we get to a higher score */
2549 	for (i = 0; i < MAX_HISCORES; i++)
2550 	{
2551 		if (highscore_read(&the_score)) return (i);
2552 		if (strcmp(the_score.pts, score->pts) < 0) return (i);
2553 	}
2554 
2555 	/* The "last" entry is always usable */
2556 	return (MAX_HISCORES - 1);
2557 }
2558 
2559 
2560 /*
2561  * Actually place an entry into the high score file
2562  * Return the location (0 is best) or -1 on "failure"
2563  */
highscore_add(high_score * score)2564 static int highscore_add(high_score *score)
2565 {
2566 	int			i, slot;
2567 	bool		done = FALSE;
2568 
2569 	high_score		the_score, tmpscore;
2570 
2571 
2572 	/* Paranoia -- it may not have opened */
2573 	if (highscore_fd == NULL) return (-1);
2574 
2575 	/* Determine where the score should go */
2576 	slot = highscore_where(score);
2577 
2578 	/* Hack -- Not on the list */
2579 	if (slot < 0) return (-1);
2580 
2581 	/* Hack -- prepare to dump the new score */
2582 	the_score = (*score);
2583 
2584 	/* Slide all the scores down one */
2585 	for (i = slot; !done && (i < MAX_HISCORES); i++)
2586 	{
2587 		/* Read the old guy, note errors */
2588 		if (highscore_seek(i)) return (-1);
2589 		if (highscore_read(&tmpscore)) done = TRUE;
2590 
2591 		/* Back up and dump the score we were holding */
2592 		if (highscore_seek(i)) return (-1);
2593 		if (highscore_write(&the_score)) return (-1);
2594 
2595 		/* Hack -- Save the old score, for the next pass */
2596 		the_score = tmpscore;
2597 	}
2598 
2599 	/* Return location used */
2600 	return (slot);
2601 }
2602 
2603 
2604 
2605 /*
2606  * Display the scores in a given range.
2607  * Assumes the high score list is already open.
2608  * Only five entries per line, too much info.
2609  *
2610  * Mega-Hack -- allow "fake" entry at the given position.
2611  */
display_scores_aux(player_type * p_ptr,int line,int note,high_score * score)2612 static void display_scores_aux(player_type *p_ptr, int line, int note, high_score *score)
2613 {
2614 	int		i, j, from, to, attr, place;
2615 
2616 	high_score	the_score;
2617 
2618 	char	out_val[256];
2619 
2620 	ang_file* fff;
2621 	char file_name[1024];
2622 
2623 	/* Paranoia -- it may not have opened */
2624 	if (highscore_fd < 0) return;
2625 
2626 	/* Seek to the beginning (or abort) */
2627 	if (highscore_seek(0)) return;
2628 
2629 	/* Temporary file */
2630 	if (path_temp(file_name, 1024)) return;
2631 
2632 	/* Open the temp file */
2633 	fff = file_open(file_name, MODE_WRITE, FTYPE_TEXT);
2634 
2635 	/* Paranoia */
2636 	if (!fff)
2637 	{
2638 		plog(format("ERROR! %s (writing %s)", strerror(errno), file_name));
2639 		return;
2640 	}
2641 
2642 	/* Assume we will show the first 20 */
2643 	from = 0;
2644 	to = 20;
2645 	if (to > MAX_HISCORES) to = MAX_HISCORES;
2646 
2647 
2648 	/* Hack -- Count the high scores */
2649 	for (i = 0; i < MAX_HISCORES; i++)
2650 	{
2651 		if (highscore_read(&the_score)) break;
2652 	}
2653 
2654 	/* Hack -- allow "fake" entry to be last */
2655 	if ((note == i) && score) i++;
2656 
2657 	/* Forget about the last entries */
2658 	if (i > to) i = to;
2659 
2660 
2661 	/* Show 5 per page, until "done" */
2662 	for (j = from, place = j+1; j < i; j++, place++)
2663 	{
2664 		int pr, pc, clev, mlev, cdun, mdun;
2665 
2666 		cptr user, gold, when, aged;
2667 
2668 
2669 		/* Hack -- indicate death in yellow */
2670 		attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
2671 
2672 
2673 		/* Mega-Hack -- insert a "fake" record */
2674 		if ((note == j) && score)
2675 		{
2676 			the_score = (*score);
2677 			attr = TERM_L_GREEN;
2678 			score = NULL;
2679 			note = -1;
2680 			j--;
2681 		}
2682 
2683 		/* Read a normal record */
2684 		else
2685 		{
2686 			/* Read the proper record */
2687 			if (highscore_seek(j)) break;
2688 			if (highscore_read(&the_score)) break;
2689 		}
2690 
2691 		/* Extract the race/class */
2692 		pr = atoi(the_score.p_r);
2693 		pc = atoi(the_score.p_c);
2694 
2695 		/* Extract the level info */
2696 		clev = atoi(the_score.cur_lev);
2697 		mlev = atoi(the_score.max_lev);
2698 		cdun = atoi(the_score.cur_dun);
2699 		mdun = atoi(the_score.max_dun);
2700 
2701 		/* Hack -- extract the gold and such */
2702 		for (user = the_score.uid; isspace(*user); user++) /* loop */;
2703 		for (when = the_score.day; isspace(*when); when++) /* loop */;
2704 		for (gold = the_score.gold; isspace(*gold); gold++) /* loop */;
2705 		for (aged = the_score.turns; isspace(*aged); aged++) /* loop */;
2706 
2707 		/* Dump some info */
2708 		sprintf(out_val, "%3d.%9s  %s the %s %s, Level %d",
2709 			place, the_score.pts, the_score.who,
2710 			p_name + p_info[pr].name, c_name + c_info[pc].name,
2711 			clev);
2712 
2713 		/* Append a "maximum level" */
2714 		if (mlev > clev) my_strcat(out_val, format(" (Max %d)", mlev), sizeof(out_val));
2715 
2716 		/* Dump the first line */
2717 		file_putf(fff, "%s\n", out_val);
2718 
2719 		/* Another line of info */
2720 		if (strcmp(the_score.how, "winner"))
2721 			sprintf(out_val, "               Killed by %s on %s %d",
2722 			the_score.how, "Dungeon Level", cdun);
2723 		else
2724 			sprintf(out_val, "               Retired after a legendary career");
2725 
2726 		/* Hack -- some people die in the town */
2727 		if ((!cdun) && (strcmp(the_score.how, "winner")))
2728 		{
2729 			sprintf(out_val, "               Killed by %s in the Town",
2730 				the_score.how);
2731 		}
2732 
2733 		/* Append a "maximum level" */
2734 		if (mdun > cdun) strcat(out_val, format(" (Max %d)", mdun));
2735 
2736 		/* Dump the info */
2737 		file_putf(fff, "%s\n", out_val);
2738 
2739 		/* And still another line of info */
2740 		sprintf(out_val,
2741 			"               (User %s, Date %s, Gold %s, Turn %s).",
2742 			user, when, gold, aged);
2743 		file_putf(fff, "%s\n", out_val);
2744 
2745 		/* Print newline if this isn't the last one */
2746 		if (j < i - 1)
2747 			file_putf(fff, "%s", "\n");
2748 	}
2749 
2750 	/* Close the file */
2751 	file_close(fff);
2752 
2753 	/* Display the file contents */
2754 	show_file(p_ptr, file_name, "High Scores", line, 0);
2755 
2756 	/* Remove the file */
2757 	file_delete(file_name);
2758 }
2759 
2760 
2761 
2762 
2763 /*
2764  * Enters a players name on a hi-score table, if "legal", and in any
2765  * case, displays some relevant portion of the high score list.
2766  *
2767  * Assumes "signals_ignore_tstp()" has been called.
2768  */
top_twenty(player_type * p_ptr)2769 static errr top_twenty(player_type *p_ptr)
2770 {
2771 	int          j;
2772 
2773 	high_score   the_score;
2774 
2775 	time_t ct = time((time_t*)0);
2776 
2777 
2778 	/* Clear screen */
2779 	/*Term_clear();*/
2780 
2781 	/* No score file */
2782 	if (highscore_fd == NULL)
2783 	{
2784 		plog("Score file unavailable.");
2785 		return (0);
2786 	}
2787 
2788 #ifndef SCORE_WIZARDS
2789 	/* Wizard-mode pre-empts scoring */
2790 	if (p_ptr->noscore & 0x000F)
2791 	{
2792 		msg_print(p_ptr, "Score not registered for wizards.");
2793 		/*display_scores_aux(0, 10, -1, NULL);*/
2794 		return (0);
2795 	}
2796 #endif
2797 
2798 #ifndef SCORE_BORGS
2799 	/* Borg-mode pre-empts scoring */
2800 	if (p_ptr->noscore & 0x00F0)
2801 	{
2802 		msg_print(p_ptr, "Score not registered for borgs.");
2803 		/*display_scores_aux(0, 10, -1, NULL);*/
2804 		return (0);
2805 	}
2806 #endif
2807 
2808 #ifndef SCORE_CHEATERS
2809 	/* Cheaters are not scored */
2810 	if (p_ptr->noscore & 0xFF00)
2811 	{
2812 		msg_print(p_ptr, "Score not registered for cheaters.");
2813 		/*display_scores_aux(0, 10, -1, NULL);*/
2814 		return (0);
2815 	}
2816 #endif
2817 
2818 	/* Interupted */
2819 	if (!p_ptr->total_winner && streq(p_ptr->died_from, "Interrupting"))
2820 	{
2821 		msg_print(p_ptr, "Score not registered due to interruption.");
2822 		/* display_scores_aux(0, 10, -1, NULL); */
2823 		return (0);
2824 	}
2825 
2826 	/* Quitter */
2827 	if (!p_ptr->total_winner && streq(p_ptr->died_from, "Quitting"))
2828 	{
2829 		msg_print(p_ptr, "Score not registered due to quitting.");
2830 		/* display_scores_aux(0, 10, -1, NULL); */
2831 		return (0);
2832 	}
2833 
2834 
2835 	/* Clear the record */
2836 	WIPE(&the_score, high_score);
2837 
2838 	/* Save the version */
2839 	sprintf(the_score.what, "%u.%u.%u",
2840 	        SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
2841 
2842 	/* Calculate and save the points */
2843 	sprintf(the_score.pts, "%9lu", (long)total_points(p_ptr));
2844 	the_score.pts[9] = '\0';
2845 
2846 	/* Save the current gold */
2847 	sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
2848 	the_score.gold[9] = '\0';
2849 
2850 	/* Save the current turn */
2851 	my_strcpy(the_score.turns, ht_show(&turn,0), sizeof(the_score.turns));
2852 
2853 #ifdef HIGHSCORE_DATE_HACK
2854 	/* Save the date in a hacked up form (9 chars) */
2855 	sprintf(the_score.day, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22);
2856 #else
2857 	/* Save the date in standard form (8 chars) */
2858 	strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct));
2859 #endif
2860 
2861 	/* Save the player name (15 chars) */
2862 	sprintf(the_score.who, "%-.15s", p_ptr->name);
2863 
2864 	/* Save the player info */
2865 	sprintf(the_score.uid, "%7u", 0 /*player_uid*/);
2866 	sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2867 	sprintf(the_score.p_r, "%2d", p_ptr->prace);
2868 	sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2869 
2870 	/* Save the level and such */
2871 	sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2872 	sprintf(the_score.cur_dun, "%3d", p_ptr->died_from_depth);
2873 	sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2874 	sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2875 
2876 	/* Save the cause of death (31 chars) */
2877 	/* HACKED to take the saved cause of death of the character, not the ghost */
2878 	sprintf(the_score.how, "%-.31s", p_ptr->died_from_list);
2879 
2880 
2881 	/* Lock (for writing) the highscore file, or fail */
2882 	if (!file_lock(highscore_fd)) return (1);
2883 
2884 	/* Add a new entry to the score list, see where it went */
2885 	j = highscore_add(&the_score);
2886 
2887 	/* Unlock the highscore file, or fail */
2888 	if (!file_unlock(highscore_fd)) return (1);
2889 
2890 
2891 #if 0
2892 	/* Hack -- Display the top fifteen scores */
2893 	if (j < 10)
2894 	{
2895 		display_scores_aux(0, 15, j, NULL);
2896 	}
2897 
2898 	/* Display the scores surrounding the player */
2899 	else
2900 	{
2901 		display_scores_aux(0, 5, j, NULL);
2902 		display_scores_aux(j - 2, j + 7, j, NULL);
2903 	}
2904 #endif
2905 
2906 
2907 	/* Success */
2908 	return (0);
2909 }
2910 
2911 
2912 /*
2913  * Predict the players location, and display it.
2914  */
predict_score(player_type * p_ptr,int line)2915 static errr predict_score(player_type *p_ptr, int line)
2916 {
2917 	int          j;
2918 
2919 	high_score   the_score;
2920 
2921 
2922 	/* No score file */
2923 	if (highscore_fd < 0)
2924 	{
2925 		plog("Score file unavailable.");
2926 		return (0);
2927 	}
2928 
2929 
2930 	/* Save the version */
2931 	sprintf(the_score.what, "%u.%u.%u",
2932 	        SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
2933 
2934 	/* Calculate and save the points */
2935 	sprintf(the_score.pts, "%9lu", (long)total_points(p_ptr));
2936 
2937 	/* Save the current gold */
2938 	sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
2939 
2940 	/* Save the current turn */
2941 	my_strcpy(the_score.turns, ht_show(&turn,0), sizeof(the_score.turns));
2942 
2943 	/* Hack -- no time needed */
2944 	strcpy(the_score.day, "TODAY");
2945 
2946 	/* Save the player name (15 chars) */
2947 	sprintf(the_score.who, "%-.15s", p_ptr->name);
2948 
2949 	/* Save the player info */
2950 	sprintf(the_score.uid, "%7u", 0 /*player_uid*/);
2951 	sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2952 	sprintf(the_score.p_r, "%2d", p_ptr->prace);
2953 	sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2954 
2955 	/* Save the level and such */
2956 	sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2957 	sprintf(the_score.cur_dun, "%3d", p_ptr->dun_depth);
2958 	sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2959 	sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2960 
2961 	/* Hack -- no cause of death */
2962 	strcpy(the_score.how, "nobody (yet!)");
2963 
2964 
2965 	/* See where the entry would be placed */
2966 	j = highscore_where(&the_score);
2967 
2968 
2969 	/* Hack -- Display the top fifteen scores */
2970 	if (j < 10)
2971 	{
2972 		display_scores_aux(p_ptr, line, j, &the_score);
2973 	}
2974 
2975 	/* Display some "useful" scores */
2976 	else
2977 	{
2978 		display_scores_aux(p_ptr, line, -1, NULL);
2979 	}
2980 
2981 
2982 	/* Success */
2983 	return (0);
2984 }
2985 
2986 
2987 
2988 
2989 /*
2990  * Change a player into a King!			-RAK-
2991  */
kingly(player_type * p_ptr)2992 void kingly(player_type *p_ptr)
2993 {
2994 	/* Fake death */
2995 	//(void)strcpy(p_ptr->died_from_list, "Ripe Old Age");
2996 	(void)strcpy(p_ptr->died_from_list, "winner");
2997 
2998 	/* Restore the experience */
2999 	p_ptr->exp = p_ptr->max_exp;
3000 
3001 	/* Restore the level */
3002 	p_ptr->lev = p_ptr->max_plv;
3003 
3004 	/* Hack -- Player gets an XP bonus for beating the game */
3005 	p_ptr->exp = p_ptr->max_exp += 10000000L;
3006 
3007 	/* Hack -- Ensure we are retired */
3008 	p_ptr->retire_timer = 0;
3009 }
3010 
3011 
3012 /*
3013  * Add a player to the high score list.
3014  */
add_high_score(player_type * p_ptr)3015 void add_high_score(player_type *p_ptr)
3016 {
3017 	char buf[1024];
3018 
3019 	/* Build the filename */
3020 	path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3021 
3022 	/* Open the high score file, for reading/writing */
3023 	highscore_fd = file_open(buf, MODE_READWRITE, FTYPE_RAW);
3024 
3025 	/* Add them */
3026 	top_twenty(p_ptr);
3027 
3028 	/* Shut the high score file */
3029 	(void)file_close(highscore_fd);
3030 
3031 	/* Forget the high score fd */
3032 	highscore_fd = NULL;
3033 }
3034 
3035 
3036 /*
3037  * Close up the current game (player may or may not be dead)
3038  *
3039  * This function is called only from "main.c" and "signals.c".
3040  *
3041  * In here we try to save everybody's game, as well as save the server state.
3042  */
close_game(void)3043 void close_game(void)
3044 {
3045 	int i;
3046 
3047 	/* No suspending now */
3048 	signals_ignore_tstp();
3049 
3050 	for (i = 0; i <= NumPlayers; i++)
3051 	{
3052 		player_type *p_ptr = Players[i];
3053 
3054 		/* Handle stuff */
3055 		handle_stuff(p_ptr);
3056 
3057 		/* Flush the messages */
3058 		msg_print(p_ptr, NULL);
3059 
3060 		/* Flush the input */
3061 		/*flush();*/
3062 
3063 
3064 		/* Hack -- Character is now "icky" */
3065 		/*character_icky = TRUE;*/
3066 
3067 
3068 		/* Build the filename */
3069 		/*path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");*/
3070 
3071 		/* Open the high score file, for reading/writing */
3072 		/*highscore_fd = fd_open(buf, O_RDWR);*/
3073 
3074 
3075 		/* Handle death */
3076 		if (p_ptr->death)
3077 		{
3078 			/* Handle retirement */
3079 			if (p_ptr->total_winner) kingly(p_ptr);
3080 
3081 			/* Save memories */
3082 			if (!save_player(p_ptr)) msg_print(p_ptr, "death save failed!");
3083 
3084 			/* Dump bones file
3085 			make_bones(i);
3086 			*/
3087 
3088 			/* Show more info */
3089 			show_info(p_ptr);
3090 
3091 			/* Handle score, show Top scores */
3092 			top_twenty(p_ptr);
3093 		}
3094 
3095 		/* Still alive */
3096 		else
3097 		{
3098 			/* Save the game */
3099 			do_cmd_save_game(p_ptr);
3100 
3101 			/* Prompt for scores XXX XXX XXX */
3102 			/*prt("Press Return (or Escape).", 0, 40);*/
3103 
3104 			/* Predict score (or ESCAPE) */
3105 			/*if (inkey() != ESCAPE) predict_score();*/
3106 		}
3107 
3108 
3109 		/* Shut the high score file */
3110 		/*(void)file_close(highscore_fd);*/
3111 
3112 		/* Forget the high score fd */
3113 		/*highscore_fd = NULL;*/
3114 	}
3115 
3116 	/* Try to save the server information */
3117 	save_server_info();
3118 
3119 	/* Allow suspending now */
3120 	signals_handle_tstp();
3121 }
3122 
3123 
3124 /*
3125  * Hack -- Display the scores in a given range and quit.
3126  *
3127  * This function is only called from "main.c" when the user asks
3128  * to see the "high scores".
3129  */
display_scores(player_type * p_ptr,int line)3130 void display_scores(player_type *p_ptr, int line)
3131 {
3132 	char buf[1024];
3133 
3134 	/* Build the filename */
3135 	path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3136 
3137 	/* Open the binary high score file, for reading */
3138 	highscore_fd = file_open(buf, MODE_READ, -1);
3139 
3140 	/* Paranoia -- No score file */
3141 	if (highscore_fd < 0)
3142 	{
3143 		/* Message to server admin */
3144 		plog("Score file unavailable.");
3145 
3146 		/* Quit */
3147 		return;
3148 	}
3149 
3150 	/* Clear screen */
3151 	/* Term_clear(); */
3152 
3153 	/* Display the scores */
3154 	predict_score(p_ptr, line);
3155 
3156 	/* Shut the high score file */
3157 	(void)file_close(highscore_fd);
3158 
3159 	/* Forget the high score fd */
3160 	highscore_fd = NULL;
3161 
3162 	/* Quit */
3163 	/* quit(NULL); */
3164 }
3165 
3166 
3167 /*
3168  * Get a random line from a file
3169  * Based on the monster speech patch by Matt Graham,
3170  */
get_rnd_line(cptr file_name,int entry,char * output)3171 errr get_rnd_line(cptr file_name, int entry, char *output)
3172 {
3173 	ang_file* fp;
3174 	char    buf[1024];
3175 	int     line, counter, test, numentries;
3176 	int     line_num = 0;
3177 	bool    found = FALSE;
3178 
3179 
3180 	/* Build the filename */
3181 	path_build(buf, 1024, ANGBAND_DIR_EDIT, file_name);
3182 
3183 	/* Open the file */
3184 	fp = file_open(buf, MODE_READ, -1);
3185 
3186 	/* Failed */
3187 	if (!fp) return (-1);
3188 
3189 	/* Find the entry of the monster */
3190 	while (TRUE)
3191 	{
3192 		/* Get a line from the file */
3193 		if (file_getl(fp, buf, 1024))
3194 		{
3195 			/* Count the lines */
3196 			line_num++;
3197 
3198 			/* Look for lines starting with 'N:' */
3199 			if ((buf[0] == 'N') && (buf[1] == ':'))
3200 			{
3201 				/* Allow default lines */
3202 				if (buf[2] == '*')
3203 				{
3204 					/* Default lines */
3205 					found = TRUE;
3206 					break;
3207 				}
3208 				/* Get the monster number */
3209 				else if (sscanf(&(buf[2]), "%d", &test) != EOF)
3210 				{
3211 					/* Is it the right monster? */
3212 					if (test == entry)
3213 					{
3214 						found = TRUE;
3215 						break;
3216 					}
3217 				}
3218 				else
3219 				{
3220 					file_close(fp);
3221 					return (-1);
3222 				}
3223 			}
3224 		}
3225 		else
3226 		{
3227 			/* Reached end of file */
3228 			file_close(fp);
3229 			return (-1);
3230 		}
3231 
3232 	}
3233 
3234 	/* Get the number of entries */
3235 	while (TRUE)
3236 	{
3237 		/* Get the line */
3238 		if (file_getl(fp, buf, 1024))
3239 		{
3240 			/* Count the lines */
3241 			line_num++;
3242 
3243 			/* Look for the number of entries */
3244 			if (isdigit(buf[0]))
3245 			{
3246 				/* Get the number of entries */
3247 				numentries = atoi(buf);
3248 				break;
3249 			}
3250 		}
3251 		else
3252 		{
3253 			/* Count the lines */
3254 			line_num++;
3255 
3256 			file_close(fp);
3257 			return (-1);
3258 		}
3259 	}
3260 
3261 	if (numentries > 0)
3262 	{
3263 		/* Grab an appropriate line number */
3264 		line = randint0(numentries);
3265 
3266 		/* Get the random line */
3267 		for (counter = 0; counter <= line; counter++)
3268 		{
3269 			/* Count the lines */
3270 			line_num++;
3271 
3272 			/* Try to read the line */
3273 			if (file_getl(fp, buf, 1024))
3274 			{
3275 				/* Found the line */
3276 				if (counter == line) break;
3277 			}
3278 			else
3279 			{
3280 				file_close(fp);
3281 				return (-1);
3282 			}
3283 		}
3284 
3285 		/* Copy the line */
3286 		strcpy(output, buf);
3287 	}
3288 
3289 	/* Close the file */
3290 	file_close(fp);
3291 
3292 	/* Success */
3293 	return (0);
3294 }
3295 
3296 
3297 /*
3298  * Handle a fatal crash.
3299  *
3300  * Here we try to save every player's state, and the state of the server
3301  * in general.  Note that we must be extremely careful not to have a crash
3302  * in this function, or some things may not get saved.  Also, this function
3303  * may get called because some data structures are not in a "correct" state.
3304  * For this reason many paranoia checks are done to prevent bad pointer
3305  * dereferences.
3306  *
3307  * Note that this function would not be needed at all if there were no bugs.
3308  */
exit_game_panic()3309 void exit_game_panic()
3310 {
3311 	int i = 1;
3312 
3313 	/* If nothing important has happened, just return */
3314 	if (!server_generated || server_saved) return;
3315 
3316 	/* Mega-Hack -- see "msg_print()" */
3317 	msg_flag = FALSE;
3318 
3319 	while (NumPlayers > (i - 1))
3320 	{
3321 		player_type *p_ptr = Players[i];
3322 
3323 		/* Don't dereference bad pointers */
3324 		if (!p_ptr)
3325 		{
3326 			/* Skip to next player */
3327 			i++;
3328 
3329 			continue;
3330 		}
3331 
3332 		/* Hack -- turn off some things */
3333 		disturb(p_ptr, 1, 0);
3334 
3335 		/* Mega-Hack -- Delay death */
3336 		if (p_ptr->chp < 0) p_ptr->death = FALSE;
3337 
3338 		/* Hardcode panic save */
3339 		panic_save = 1;
3340 
3341 		/* Forbid suspend */
3342 		signals_ignore_tstp();
3343 
3344 		/* Indicate panic save */
3345 		(void)strcpy(p_ptr->died_from, "(panic save)");
3346 
3347 		/* Try to save the player, don't worry if this fails because there
3348 		 * is nothing we can do now anyway */
3349 		save_player(p_ptr);
3350 		i++;
3351 
3352 	}
3353 
3354 	/* Clear objects so that artifacts get saved.
3355 	 * This probably isn't neccecary, as all the objects on each of
3356 	 * these levels should have been cleared by now. However, paranoia
3357 	 * can't hurt all that much... -APD
3358 	 */
3359 	for (i = 1; i < MAX_DEPTH; i++)
3360 	{
3361 
3362 		/* Paranoia -- wipe this depth's objects if no players are on it*/
3363 		if (!players_on_depth[i]) wipe_o_list(i);
3364 	}
3365 
3366 	if (!save_server_info()) plog("server panic info save failed!");
3367 
3368 	/* Successful panic save of server info */
3369 	plog("server panic info save succeeded!");
3370 
3371 }
3372 
3373 /*
3374  * Windows specific replacement for signal handling [grk]
3375  */
3376 #ifdef WINDOWS
3377 #ifndef HANDLE_SIGNALS
3378 
3379 LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
3380 
3381 /* Callback to be called by Windows when our term closes, the user
3382  * logs off, the system is shutdown, etc.
3383  */
ctrl_handler(DWORD fdwCtrlType)3384 BOOL ctrl_handler( DWORD fdwCtrlType )
3385 {
3386 	/* Save everything and quit the game */
3387 	shutdown_server();
3388 
3389 	return TRUE;
3390 }
3391 
3392 /* Global unhandled exception handler */
3393 /* If the server crashes under Windows, this is where we end up */
myUnhandledExceptionFilter(struct _EXCEPTION_POINTERS * ExceptionInfo)3394 LONG WINAPI myUnhandledExceptionFilter(
3395   struct _EXCEPTION_POINTERS* ExceptionInfo)
3396 {
3397 	/* We don't report to the meta server in this case, the meta
3398 	 * server will detect that we've gone anyway
3399 	 */
3400 
3401 	/* Call the previous exception handler, which we are assuming
3402 	 * is the MinGW exception handler which should have been implicitly
3403 	 * setup when we loaded the exchndl.dll library.
3404 	 */
3405 	if(old_handler != NULL)
3406 	{
3407 	  old_handler(ExceptionInfo);
3408 	}
3409 
3410 	/* Save everything and quit the game */
3411 	exit_game_panic();
3412 
3413 	/* We don't expect to ever get here... but for what it's worth... */
3414 	return(EXCEPTION_EXECUTE_HANDLER);
3415 
3416 }
3417 
3418 
setup_exit_handler(void)3419 void setup_exit_handler(void)
3420 {
3421 	/* Trap CTRL+C, Logoff, Shutdown, etc */
3422 	if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ctrl_handler, TRUE ) )
3423 	{
3424 		plog("Initialised exit save handler.");
3425 	}else{
3426 		plog("ERROR: Could not set panic save handler!");
3427 	}
3428 	/* Trap unhandled exceptions, i.e. server crashes */
3429 	old_handler = SetUnhandledExceptionFilter( myUnhandledExceptionFilter );
3430 }
3431 #endif
3432 #endif
3433 
3434 #ifdef HANDLE_SIGNALS
3435 
3436 
3437 /*
3438  * Handle signals -- suspend
3439  *
3440  * Actually suspend the game, and then resume cleanly
3441  *
3442  * This will probably inflict much anger upon the suspender, but it is still
3443  * allowed (for now) --KLJ--
3444  */
handle_signal_suspend(int sig)3445 static void handle_signal_suspend(int sig)
3446 {
3447 	/* Disable handler */
3448 	(void)signal(sig, SIG_IGN);
3449 
3450 #ifdef SIGSTOP
3451 
3452 	/* Flush output */
3453 	/*Term_fresh();*/
3454 
3455 	/* Suspend the "Term" */
3456 	/*Term_xtra(TERM_XTRA_ALIVE, 0);*/
3457 
3458 	/* Suspend ourself */
3459 	(void)kill(0, SIGSTOP);
3460 
3461 	/* Resume the "Term" */
3462 	/*Term_xtra(TERM_XTRA_ALIVE, 1);*/
3463 
3464 	/* Redraw the term */
3465 	/*Term_redraw();*/
3466 
3467 	/* Flush the term */
3468 	/*Term_fresh();*/
3469 
3470 #endif
3471 
3472 	/* Restore handler */
3473 	(void)signal(sig, handle_signal_suspend);
3474 }
3475 
3476 
3477 /*
3478  * Handle signals -- simple (interrupt and quit)
3479  *
3480  * This function was causing a *huge* number of problems, so it has
3481  * been simplified greatly.  We keep a global variable which counts
3482  * the number of times the user attempts to kill the process, and
3483  * we commit suicide if the user does this a certain number of times.
3484  *
3485  * We attempt to give "feedback" to the user as he approaches the
3486  * suicide thresh-hold, but without penalizing accidental keypresses.
3487  *
3488  * To prevent messy accidents, we should reset this global variable
3489  * whenever the user enters a keypress, or something like that.
3490  *
3491  * This simply calls "exit_game_panic()", which should try to save
3492  * everyone's character and the server info, which is probably nicer
3493  * than killing everybody. --KLJ--
3494  */
handle_signal_simple(int sig)3495 static void handle_signal_simple(int sig)
3496 {
3497 	/* Disable handler */
3498 	(void)signal(sig, SIG_IGN);
3499 
3500 
3501 	/* Nothing to save, just quit */
3502 	if (!server_generated || server_saved) quit(NULL);
3503 
3504 	/* Hack -- on SIGTERM, quit right away */
3505 	if (sig == SIGTERM)
3506 	{
3507 		signal_count = 5;
3508 	}
3509 
3510 	/* Count the signals */
3511 	signal_count++;
3512 
3513 
3514 	/* Allow suicide (after 5) */
3515 	if (signal_count >= 5)
3516 	{
3517 		/* Perform a "clean" shutdown */
3518 		shutdown_server();
3519 	}
3520 
3521 	/* Give warning (after 4) */
3522 	else if (signal_count >= 4)
3523 	{
3524 		plog("Warning: Next signal kills server!");
3525 
3526 		/* Make a noise */
3527 		/*Term_xtra(TERM_XTRA_NOISE, 0);*/
3528 
3529 		/* Clear the top line */
3530 		/*Term_erase(0, 0, 255);*/
3531 
3532 		/* Display the cause */
3533 		/*Term_putstr(0, 0, -1, TERM_WHITE, "Contemplating suicide!");*/
3534 
3535 		/* Flush */
3536 		/*Term_fresh();*/
3537 	}
3538 
3539 	/* Give warning (after 2) */
3540 	else if (signal_count >= 2)
3541 	{
3542 		/* Make a noise */
3543 		/*Term_xtra(TERM_XTRA_NOISE, 0);*/
3544 	}
3545 
3546 	/* Restore handler */
3547 	(void)signal(sig, handle_signal_simple);
3548 }
3549 
3550 
3551 /*
3552  * Handle signal -- abort, kill, etc
3553  *
3554  * This one also calls exit_game_panic() --KLJ--
3555  */
handle_signal_abort(int sig)3556 static void handle_signal_abort(int sig)
3557 {
3558 	/* We are *not* reentrant */
3559 	if (signalbusy) raise(sig);
3560 	signalbusy = 1;
3561 
3562     plog("Unexpected signal, panic saving.");
3563 
3564 	/* Nothing to save, just quit */
3565 	if (!server_generated || server_saved) quit(NULL);
3566 
3567 	/* Save everybody */
3568     exit_game_panic();
3569 
3570 	/* Enable default handler */
3571 	(void)signal(sig, SIG_DFL);
3572 
3573 	/* Reraise */
3574 	raise(sig);
3575 }
3576 
3577 
3578 
3579 
3580 /*
3581  * Ignore SIGTSTP signals (keyboard suspend)
3582  */
signals_ignore_tstp(void)3583 void signals_ignore_tstp(void)
3584 {
3585 
3586 #ifdef SIGTSTP
3587 	(void)signal(SIGTSTP, SIG_IGN);
3588 #endif
3589 
3590 }
3591 
3592 /*
3593  * Handle SIGTSTP signals (keyboard suspend)
3594  */
signals_handle_tstp(void)3595 void signals_handle_tstp(void)
3596 {
3597 
3598 #ifdef SIGTSTP
3599 	(void)signal(SIGTSTP, handle_signal_suspend);
3600 #endif
3601 
3602 }
3603 
3604 
3605 /*
3606  * Prepare to handle the relevant signals
3607  */
signals_init(void)3608 void signals_init(void)
3609 {
3610 
3611 #ifdef SIGHUP
3612 	(void)signal(SIGHUP, SIG_IGN);
3613 
3614 #ifdef TARGET_OS_OSX
3615 	/* Closing Terminal.app on OSX sends SIGHUP, let's not ignore it! */
3616 	(void)signal(SIGHUP, handle_signal_abort);
3617 #endif
3618 
3619 #endif
3620 
3621 #ifdef SIGTSTP
3622 	(void)signal(SIGTSTP, handle_signal_suspend);
3623 #endif
3624 
3625 
3626 #ifdef SIGINT
3627 	(void)signal(SIGINT, handle_signal_simple);
3628 #endif
3629 
3630 #ifdef SIGQUIT
3631 	(void)signal(SIGQUIT, handle_signal_simple);
3632 #endif
3633 
3634 
3635 #ifdef SIGFPE
3636 	(void)signal(SIGFPE, handle_signal_abort);
3637 #endif
3638 
3639 #ifdef SIGILL
3640 	(void)signal(SIGILL, handle_signal_abort);
3641 #endif
3642 
3643 #ifdef SIGTRAP
3644 	(void)signal(SIGTRAP, handle_signal_abort);
3645 #endif
3646 
3647 #ifdef SIGIOT
3648 	(void)signal(SIGIOT, handle_signal_abort);
3649 #endif
3650 
3651 #ifdef SIGKILL
3652 	(void)signal(SIGKILL, handle_signal_abort);
3653 #endif
3654 
3655 #ifdef SIGBUS
3656 	(void)signal(SIGBUS, handle_signal_abort);
3657 #endif
3658 
3659 #ifdef SIGSEGV
3660 	(void)signal(SIGSEGV, handle_signal_abort);
3661 #endif
3662 
3663 #ifdef SIGTERM
3664 	(void)signal(SIGTERM, handle_signal_simple);
3665 #endif
3666 
3667 #ifdef SIGPIPE
3668 	(void)signal(SIGPIPE, SIG_IGN);
3669 #endif
3670 
3671 #ifdef SIGEMT
3672 	(void)signal(SIGEMT, handle_signal_abort);
3673 #endif
3674 
3675 #ifdef SIGDANGER
3676 	(void)signal(SIGDANGER, handle_signal_abort);
3677 #endif
3678 
3679 #ifdef SIGSYS
3680 	(void)signal(SIGSYS, handle_signal_abort);
3681 #endif
3682 
3683 #ifdef SIGXCPU
3684 	(void)signal(SIGXCPU, handle_signal_abort);
3685 #endif
3686 
3687 #ifdef SIGPWR
3688 	(void)signal(SIGPWR, handle_signal_abort);
3689 #endif
3690 
3691 }
3692 
3693 
3694 #else	/* HANDLE_SIGNALS */
3695 
3696 
3697 /*
3698  * Do nothing
3699  */
signals_ignore_tstp(void)3700 void signals_ignore_tstp(void)
3701 {
3702 }
3703 
3704 /*
3705  * Do nothing
3706  */
signals_handle_tstp(void)3707 void signals_handle_tstp(void)
3708 {
3709 }
3710 
3711 /*
3712  * Do nothing
3713  */
signals_init(void)3714 void signals_init(void)
3715 {
3716 }
3717 
3718 
3719 #endif	/* HANDLE_SIGNALS */
3720