1 /* $Id$ */
2 /* File: files.c */
3 
4 /* Purpose: code dealing with files (and death) */
5 
6 /*
7  * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
8  *
9  * This software may be copied and distributed for educational, research, and
10  * not for profit purposes provided that this copyright and statement are
11  * included in all such copies.
12  */
13 
14 #define SERVER
15 
16 #include "angband.h"
17 
18 #ifdef HANDLE_SIGNALS
19 #include <signal.h>
20 #ifndef WINDOWS
21 #include <sys/wait.h>
22 #endif
23 #endif
24 
25 /* The first x highscore entries that are displayed to players: */
26 #define SCORES_SHOWN 28
27 
28 /*
29  * You may or may not want to use the following "#undef".
30  */
31 /* #undef _POSIX_SAVED_IDS */
32 
33 /* Use first line of file as stationary title caption? If disabled,
34    'what' parm of do_cmd_help_aux() will be used instead (normal). - C. Blue */
35 //#define HELP_AUX_GRABS_TITLE
36 
37 
38 /*
39  * Extract the first few "tokens" from a buffer
40  *
41  * This function uses "colon" and "slash" as the delimeter characters.
42  *
43  * We never extract more than "num" tokens.  The "last" token may include
44  * "delimeter" characters, allowing the buffer to include a "string" token.
45  *
46  * We save pointers to the tokens in "tokens", and return the number found.
47  *
48  * Hack -- Attempt to handle the 'c' character formalism
49  *
50  * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
51  *
52  * Hack -- We will always extract at least one token
53  */
54 #if 0
55 s16b tokenize(char *buf, s16b num, char **tokens)
56 {
57 	int i = 0;
58 
59 	char *s = buf;
60 
61 
62 	/* Process */
63 	while (i < num - 1)
64 	{
65 		char *t;
66 
67 		/* Scan the string */
68 		for (t = s; *t; t++)
69 		{
70 			/* Found a delimiter */
71 			if ((*t == ':') || (*t == '/')) break;
72 
73 			/* Handle single quotes */
74 			if (*t == '\'')
75 			{
76 				/* Advance */
77 				t++;
78 
79 				/* Handle backslash */
80 				if (*t == '\\') t++;
81 
82 				/* Require a character */
83 				if (!*t) break;
84 
85 				/* Advance */
86 				t++;
87 
88 				/* Hack -- Require a close quote */
89 				if (*t != '\'') *t = '\'';
90 			}
91 
92 			/* Handle back-slash */
93 			if (*t == '\\') t++;
94 		}
95 
96 		/* Nothing left */
97 		if (!*t) break;
98 
99 		/* Nuke and advance */
100 		*t++ = '\0';
101 
102 		/* Save the token */
103 		tokens[i++] = s;
104 
105 		/* Advance */
106 		s = t;
107 	}
108 
109 	/* Save the token */
110 	tokens[i++] = s;
111 
112 	/* Number found */
113 	return (i);
114 }
115 #else	// 0
tokenize(char * buf,s16b num,char ** tokens,char delim1,char delim2)116 s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2)
117 {
118 	int i = 0;
119 
120 	char *s = buf;
121 
122 
123 	/* Process */
124 	while (i < num - 1)
125 	{
126 		char *t;
127 
128 		/* Scan the string */
129 		for (t = s; *t; t++)
130 		{
131 			/* Found a delimiter */
132 			if ((*t == delim1) || (*t == delim2)) break;
133 
134 			/* Handle single quotes */
135 			if (*t == '\'')
136 			{
137 				/* Advance */
138 				t++;
139 
140 				/* Handle backslash */
141 				if (*t == '\\') t++;
142 
143 				/* Require a character */
144 				if (!*t) break;
145 
146 				/* Advance */
147 				t++;
148 
149 				/* Hack -- Require a close quote */
150 				if (*t != '\'') *t = '\'';
151 			}
152 
153 			/* Handle back-slash */
154 			if (*t == '\\') t++;
155 		}
156 
157 		/* Nothing left */
158 		if (!*t) break;
159 
160 		/* Nuke and advance */
161 		*t++ = '\0';
162 
163 		/* Save the token */
164 		tokens[i++] = s;
165 
166 		/* Advance */
167 		s = t;
168 	}
169 
170 	/* Save the token */
171 	tokens[i++] = s;
172 
173 	/* Number found */
174 	return (i);
175 }
176 #endif	// 0
177 
178 #ifdef CHECK_TIME
179 
180 /*
181  * Operating hours for ANGBAND (defaults to non-work hours)
182  */
183 static char days[7][29] =
184 {
185 	"SUN:XXXXXXXXXXXXXXXXXXXXXXXX",
186 	"MON:XXXXXXXX.........XXXXXXX",
187 	"TUE:XXXXXXXX.........XXXXXXX",
188 	"WED:XXXXXXXX.........XXXXXXX",
189 	"THU:XXXXXXXX.........XXXXXXX",
190 	"FRI:XXXXXXXX.........XXXXXXX",
191 	"SAT:XXXXXXXXXXXXXXXXXXXXXXXX"
192 };
193 
194 /*
195  * Restict usage (defaults to no restrictions)
196  */
197 static bool check_time_flag = FALSE;
198 
199 #endif
200 
201 
202 /*
203  * Handle CHECK_TIME
204  */
check_time(void)205 errr check_time(void)
206 {
207 
208 #ifdef CHECK_TIME
209 
210 	time_t              c;
211 	struct tm               *tp;
212 
213 	/* No restrictions */
214 	if (!check_time_flag) return (0);
215 
216 	/* Check for time violation */
217 	c = time((time_t *)0);
218 	tp = localtime(&c);
219 
220 	/* Violation */
221 	if (days[tp->tm_wday][tp->tm_hour + 4] != 'X') return (1);
222 
223 #endif
224 
225 	/* Success */
226 	return (0);
227 }
228 
229 
230 
231 /*
232  * Initialize CHECK_TIME
233  */
check_time_init(void)234 errr check_time_init(void)
235 {
236 
237 #ifdef CHECK_TIME
238 
239 	FILE        *fp;
240 
241 	char    buf[1024];
242 
243 
244 	/* Build the filename */
245 	path_build(buf, 1024, ANGBAND_DIR_DATA, "time.txt");
246 
247 	/* Open the file */
248 	fp = my_fopen(buf, "r");
249 
250 	/* No file, no restrictions */
251 	if (!fp) return (0);
252 
253 	/* Assume restrictions */
254 	check_time_flag = TRUE;
255 
256 	/* Parse the file */
257 	while (0 == my_fgets(fp, buf, 80, FALSE))
258 	{
259 		/* Skip comments and blank lines */
260 		if (!buf[0] || (buf[0] == '#')) continue;
261 
262 		/* Chop the buffer */
263 		buf[29] = '\0';
264 
265 		/* Extract the info */
266 		if (prefix(buf, "SUN:")) strcpy(days[0], buf);
267 		if (prefix(buf, "MON:")) strcpy(days[1], buf);
268 		if (prefix(buf, "TUE:")) strcpy(days[2], buf);
269 		if (prefix(buf, "WED:")) strcpy(days[3], buf);
270 		if (prefix(buf, "THU:")) strcpy(days[4], buf);
271 		if (prefix(buf, "FRI:")) strcpy(days[5], buf);
272 		if (prefix(buf, "SAT:")) strcpy(days[6], buf);
273 	}
274 
275 	/* Close it */
276 	my_fclose(fp);
277 
278 #endif
279 
280 	/* Success */
281 	return (0);
282 }
283 
284 
285 
286 #ifdef CHECK_LOAD
287 
288 #ifndef MAXHOSTNAMELEN
289 # define MAXHOSTNAMELEN  64
290 #endif
291 
292 typedef struct statstime statstime;
293 
294 struct statstime
295 {
296 	int                 cp_time[4];
297 	int                 dk_xfer[4];
298 	unsigned int        v_pgpgin;
299 	unsigned int        v_pgpgout;
300 	unsigned int        v_pswpin;
301 	unsigned int        v_pswpout;
302 	unsigned int        v_intr;
303 	int                 if_ipackets;
304 	int                 if_ierrors;
305 	int                 if_opackets;
306 	int                 if_oerrors;
307 	int                 if_collisions;
308 	unsigned int        v_swtch;
309 	long                avenrun[3];
310 	struct timeval      boottime;
311 	struct timeval      curtime;
312 };
313 
314 /*
315  * Maximal load (if any).
316  */
317 static int check_load_value = 0;
318 
319 #endif
320 
321 
322 /*
323  * Handle CHECK_LOAD
324  */
check_load(void)325 errr check_load(void)
326 {
327 
328 #ifdef CHECK_LOAD
329 
330 	struct statstime    st;
331 
332 	/* Success if not checking */
333 	if (!check_load_value) return (0);
334 
335 	/* Check the load */
336 	if (0 == rstat("localhost", &st))
337 	{
338 		long val1 = (long)(st.avenrun[2]);
339 		long val2 = (long)(check_load_value) * FSCALE;
340 
341 		/* Check for violation */
342 		if (val1 >= val2) return (1);
343 	}
344 
345 #endif
346 
347 	/* Success */
348 	return (0);
349 }
350 
351 
352 /*
353  * Initialize CHECK_LOAD
354  */
check_load_init(void)355 errr check_load_init(void)
356 {
357 
358 #ifdef CHECK_LOAD
359 
360 	FILE        *fp;
361 
362 	char    buf[1024];
363 
364 	char    temphost[MAXHOSTNAMELEN+1];
365 	char    thishost[MAXHOSTNAMELEN+1];
366 
367 
368 	/* Build the filename */
369 	path_build(buf, 1024, ANGBAND_DIR_TEXT, "load.txt");
370 
371 	/* Open the "load" file */
372 	fp = my_fopen(buf, "r");
373 
374 	/* No file, no restrictions */
375 	if (!fp) return (0);
376 
377 	/* Default load */
378 	check_load_value = 100;
379 
380 	/* Get the host name */
381 	(void)gethostname(thishost, (sizeof thishost) - 1);
382 
383 	/* Parse it */
384 	while (0 == my_fgets(fp, buf, 1024, FALSE))
385 	{
386 		int value;
387 
388 		/* Skip comments and blank lines */
389 		if (!buf[0] || (buf[0] == '#')) continue;
390 
391 		/* Parse, or ignore */
392 		if (sscanf(buf, "%s%d", temphost, &value) != 2) continue;
393 
394 		/* Skip other hosts */
395 		if (!streq(temphost, thishost) &&
396 		    !streq(temphost, "localhost")) continue;
397 
398 		/* Use that value */
399 		check_load_value = value;
400 
401 		/* Done */
402 		break;
403 	}
404 
405 	/* Close the file */
406 	my_fclose(fp);
407 
408 #endif
409 
410 	/* Success */
411 	return (0);
412 }
413 
414 
415 
416 
417 #if 0
418 /*
419  * Prints the following information on the screen.
420  *
421  * For this to look right, the following should be spaced the
422  * same as in the prt_lnum code... -CFT
423  *
424  * This will send the info to the client now --KLJ--
425  *
426  * Except that this (and display_player) are never called. --KLJ--
427  */
428 static void display_player_middle(int Ind) {
429 	player_type *p_ptr = Players[Ind];
430 	int bmh = 0, bmd = 0;
431 	s32b adv_exp, adv_exp_prev;
432 
433 	int show_tohit_m = p_ptr->dis_to_h + p_ptr->to_h_melee;
434 	int show_todam_m = p_ptr->dis_to_d + p_ptr->to_d_melee;
435 /*	int show_tohit_m = p_ptr->to_h_melee;
436 	int show_todam_m = p_ptr->to_d_melee;
437 */
438 	int show_tohit_r = p_ptr->dis_to_h + p_ptr->to_h_ranged;
439 	int show_todam_r = p_ptr->to_d_ranged;
440 
441         /* well, about dual-wield.. we can only display the boni of one weapon or their average until we add another line
442            to squeeze info about the secondary weapon there too. so for now let's just stick with this. - C. Blue */
443 	object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
444 	object_type *o_ptr2 = &p_ptr->inventory[INVEN_BOW];
445 	object_type *o_ptr3 = &p_ptr->inventory[INVEN_AMMO];
446 	object_type *o_ptr4 = &p_ptr->inventory[INVEN_ARM];
447 
448 	/* Hack -- add in weapon info if known */
449 	if (object_known_p(Ind, o_ptr)) {
450 		bmh += o_ptr->to_h;
451 		bmd += o_ptr->to_d;
452 	}
453 	if (object_known_p(Ind, o_ptr2)) {
454 		show_tohit_r += o_ptr2->to_h;
455 		show_todam_r += o_ptr2->to_d;
456 	}
457 	if (object_known_p(Ind, o_ptr3)) {
458 		show_tohit_r += o_ptr3->to_h;
459 		show_todam_r += o_ptr3->to_d;
460 	}
461 	/* dual-wield..*/
462 	if (o_ptr4->k_idx && o_ptr4->tval != TV_SHIELD) {
463 		if (object_known_p(Ind, o_ptr4)) {
464 			bmh += o_ptr4->to_h;
465 			bmd += o_ptr4->to_d;
466 		}
467 		if (object_known_p(Ind, o_ptr) && object_known_p(Ind, o_ptr4)) {
468 			/* average of both */
469 			bmh /= 2;
470 			bmd /= 2;
471 		}
472 	}
473 
474 	show_tohit_m += bmh;
475 	show_todam_m += bmd;
476 
477 	/* Dump the bonuses to hit/dam */
478 //	Send_plusses(Ind, show_tohit_m, show_todam_m, show_tohit_r, show_todam_r, p_ptr->to_h_melee, p_ptr->to_d_melee);
479 	Send_plusses(Ind, 0, 0, show_tohit_r, show_todam_r, show_tohit_m, show_todam_m);
480 
481 	/* Dump the armor class bonus */
482 	Send_ac(Ind, p_ptr->dis_ac, p_ptr->dis_to_a);
483 
484 	if (p_ptr->lev >= (is_admin(p_ptr) ? PY_MAX_LEVEL : PY_MAX_PLAYER_LEVEL))
485 		adv_exp = 0;
486 	else {
487 		s64b adv_prev = 0;
488 #ifndef ALT_EXPRATIO
489 		s64b adv = ((s64b)player_exp[p_ptr->lev - 1] * (s64b)p_ptr->expfact / 100L);
490 		if (p_ptr->lev > 1) adv_prev = ((s64b)player_exp[p_ptr->lev - 2] * (s64b)p_ptr->expfact / 100L);
491 #else
492 		s64b adv = (s64b)player_exp[p_ptr->lev - 1];
493 		if (p_ptr->lev > 1) adv_prev = (s64b)player_exp[p_ptr->lev - 2];
494 #endif
495 		adv_exp = (s32b)(adv);
496 		adv_exp_prev = (s32b)(adv_prev);
497 	}
498 
499 	Send_experience(Ind, p_ptr->lev, p_ptr->max_exp, p_ptr->exp, adv_exp, adv_exp_prev);
500 	Send_gold(Ind, p_ptr->au);
501 	Send_hp(Ind, p_ptr->mhp, p_ptr->chp);
502 	Send_sp(Ind, p_ptr->msp, p_ptr->csp);
503 	Send_stamina(Ind, p_ptr->mst, p_ptr->cst);
504 }
505 
506 
507 
508 /*
509  * Display the character on the screen (with optional history)
510  *
511  * The top two and bottom two lines are left blank.
512  */
513 void display_player(int Ind)
514 {
515 	player_type *p_ptr = Players[Ind];
516 
517 	int i;
518 
519 
520 	/* Send basic information */
521 	Send_char_info(Ind, p_ptr->prace, p_ptr->pclass, p_ptr->ptrait, p_ptr->male, p_ptr->mode, p_ptr->name);
522 
523 	/* Age, Height, Weight, Social */
524 	Send_various(Ind, p_ptr->ht, p_ptr->wt, p_ptr->age, p_ptr->sc);
525 
526 	/* Send all the stats */
527 	for (i = 0; i < 6; i++)
528 	{
529 		Send_stat(Ind, i, p_ptr->stat_top[i], p_ptr->stat_use[i]);
530 	}
531 
532 	/* Extra info */
533 	display_player_middle(Ind);
534 
535 	/* Display "history" info */
536 	Send_history(Ind, i, p_ptr->history[i]);
537 }
538 #endif	// 0
539 
540 /*
541  * Recursive "help file" perusal.  Return FALSE on "ESCAPE".
542  *
543  * XXX XXX XXX Consider using a temporary file.
544  *
545  * Added 'odd_line' flag: Usually FALSE -> 20 lines available per 'page'.
546  *                        If TRUE -> 21 lines available! Added this for
547  *                        @-screen w/ COMPACT_PLAYERLIST - C. Blue
548  *             *changed it to 'div3_line', for cleaner addition of more of this type.
549  *             *changed it to 'divl' to be most flexible.
550  */
do_cmd_help_aux(int Ind,cptr name,cptr what,s32b line,int color,int divl)551 static bool do_cmd_help_aux(int Ind, cptr name, cptr what, s32b line, int color, int divl)
552 {
553 	int lines_per_page = 20 + HGT_PLUS;
554 	int i, k = 0;
555 	/* Number of "real" lines passed by */
556 	s32b next = 0;
557 	/* Number of "real" lines in the file */
558 	s32b size = 0;
559 	/* Backup value for "line" */
560 	s32b back = 0;
561 	/* This screen has sub-screens */
562 	//bool menu = FALSE;
563 	/* Current help file */
564 	FILE *fff = NULL;
565 	/* Find this string (if any) */
566 	cptr find = NULL;
567 	/* Hold a string to find */
568 	char finder[128];
569 	/* Hold a string to show */
570 	char shower[128];
571 	/* Describe this thing */
572 	char caption[128];
573 	/* Path buffer */
574 	char path[MAX_PATH_LENGTH];
575 	/* General buffer */
576 	char buf[1024];
577 	/* Sub-menu information */
578 	char hook[10][32];
579 	/* Stationary title bar, derived from 1st line of the file or from 'what' parm. */
580 #ifdef HELP_AUX_GRABS_TITLE
581 	bool use_title = FALSE;
582 #endif
583 
584 	if (is_newer_than(&Players[Ind]->version, 4, 4, 7, 0, 0, 0)) {
585 		/* use first line in the file as stationary title */
586 #ifdef HELP_AUX_GRABS_TITLE
587 		use_title = TRUE;
588 #endif
589 
590 		if (divl) {
591 			if (is_older_than(&Players[Ind]->version, 4, 4, 9, 4, 0, 0)) {
592 				/* 21-lines-per-page feature requires client 4.4.7.1 aka 4.4.7a */
593 				if (divl == 3) {
594 					lines_per_page = 21 + HGT_PLUS - ((21 + HGT_PLUS) % 3);
595 					/* hack: tell client we'd like a mod3 # of lines per page */
596 					Send_special_line(Ind, 0, 21 + HGT_PLUS, 0, "");
597 				}
598 			} else {
599 				lines_per_page = 21 + HGT_PLUS - ((21 + HGT_PLUS) % divl);
600 				/* hack: tell client we'd like a mod4 # of lines per page */
601 				Send_special_line(Ind, 0, 20 + divl + HGT_PLUS, 0, "");
602 			}
603 		}
604 	}
605 
606 	/* Wipe finder */
607 	strcpy(finder, "");
608 	/* Wipe shower */
609 	strcpy(shower, "");
610 	/* Wipe caption */
611 	strcpy(caption, "");
612 
613 	/* Wipe the hooks */
614 	for (i = 0; i < 10; i++) hook[i][0] = '\0';
615 
616 
617 	/* Hack XXX XXX XXX */
618 	if (what) {
619 		/* Caption */
620 		strcpy(caption, what);
621 		/* Access the "file" */
622 		strcpy(path, name);
623 		/* Open */
624 		fff = my_fopen(path, "rb");
625 	}
626 
627 	/* Look in "help" */
628 	if (!fff) {
629 #if 0 /* will overwrite legens-rev.log's title if the file doesn't exist yet */
630 		/* Caption */
631 		snprintf(caption, sizeof(caption), "Help file '%s'", name);
632 #endif
633 		/* Build the filename */
634 		path_build(path, MAX_PATH_LENGTH, ANGBAND_DIR_TEXT, name);
635 		/* Open the file */
636 		fff = my_fopen(path, "rb");
637 	}
638 
639 #ifndef HELP_AUX_GRABS_TITLE
640 	if (strlen(caption)) {
641 		k = (72 - strlen(caption)) / 2;
642 		strcpy(buf, "");
643 		for (i = 0; i < k; i++) strcat(buf, " ");
644 		strcat(buf, "\377W- [\377w");
645 		strcat(buf, caption);
646 		strcat(buf, "\377W] -");
647 		Send_special_line(Ind, size, -1, TERM_WHITE, buf);
648 		k = 0;
649 	}
650 #endif
651 
652 	/* Oops */
653 	if (!fff) {
654 #if 0 /* no spam -  the msg is written many times at once and also not needed */
655 		/* Message */
656 		msg_format(Ind, "Cannot open '%s'.", name);
657 		msg_print(Ind, NULL);
658 #endif
659 
660 		/* Oops */
661 		return (TRUE);
662 	}
663 
664 
665 	/* Pre-Parse the file */
666 	while (TRUE) {
667 		/* Read a line or stop */
668 		if (my_fgets(fff, buf, 1024, FALSE)) break;
669 
670 		/* XXX Parse "menu" items */
671 		if (prefix(buf, "***** ")) {
672 			char b1 = '[', b2 = ']';
673 
674 			/* Notice "menu" requests */
675 			if ((buf[6] == b1) && isdigit(buf[7]) &&
676 			    (buf[8] == b2) && (buf[9] == ' '))
677 			{
678 				/* This is a menu file */
679 				//menu = TRUE;
680 
681 				/* Extract the menu item */
682 				k = buf[7] - '0';
683 
684 				/* Extract the menu item */
685 				strcpy(hook[k], buf + 10);
686 			}
687 
688 			/* Skip this */
689 			continue;
690 		}
691 
692 		/* Count the "real" lines */
693 		next++;
694 	}
695 
696 	/* Save the number of "real" lines */
697 	size = next;
698 #ifdef HELP_AUX_GRABS_TITLE
699 	if (use_title) size--;
700 #endif
701 
702 
703 	/* Display the file */
704 	/* Restart when necessary */
705 	if (line >= size) line = 0;
706 
707 	/* (Consistent behavior with peruse_file() in c-files.c
708 	   and Receive_special_line() in nclient.c.) */
709 	if (line > size - lines_per_page) {
710 		line = size - lines_per_page;
711 		if (line < 0) line = 0;
712 	}
713 
714 
715 	/* Re-open the file if needed */
716 	if (next > line) {
717 		/* Close it */
718 		my_fclose(fff);
719 
720 		/* Hack -- Re-Open the file */
721 		fff = my_fopen(path, "rb");
722 
723 		/* Oops */
724 		if (!fff) return (FALSE);
725 
726 		/* File has been restarted */
727 		next = 0;
728 
729 #ifdef HELP_AUX_GRABS_TITLE
730 		/* Use special 'title' line? -> Skip in regular use. */
731 		if (use_title)
732 			if (!my_fgets(fff, buf, 1024, FALSE))
733 				Send_special_line(Ind, size, -1, TERM_WHITE, buf);
734 #endif
735 	}
736 
737 	/* Skip lines if needed */
738 	for (; next < line; next++) {
739 		/* Skip a line */
740 		if (my_fgets(fff, buf, 1024, FALSE)) break;
741 	}
742 
743 
744 	/* Dump the next 20 lines of the file */
745 	for (i = 0; i < lines_per_page; ) {
746 		byte attr = TERM_WHITE;
747 
748 		/* Hack -- track the "first" line */
749 		if (!i) line = next;
750 
751 		/* Get a line of the file or stop */
752 		if (my_fgets(fff, buf, 1024, FALSE)) break;
753 
754 		/* Hack -- skip "special" lines */
755 		if (prefix(buf, "***** ")) continue;
756 
757 		/* Count the "real" lines */
758 		next++;
759 
760 		/* Hack -- keep searching */
761 		if (find && !i && !strstr(buf, find)) continue;
762 
763 		/* Hack -- stop searching */
764 		find = NULL;
765 
766 		if (buf[0] == '\n') continue;
767 
768 #if 0	// This will now be done by \377? codes! - C. Blue
769 		/* Extract color */
770 		if (color) attr = color_char_to_attr(buf[0]);
771 #endif
772 
773 		/* Hack -- show matches */
774 		if (shower[0] && strstr(buf, shower)) attr = TERM_YELLOW;
775 
776 		/* Dump the line */
777 #if 0	// see above
778 		Send_special_line(Ind, size, i, attr, &buf[color]);
779 #else
780 		Send_special_line(Ind, size, i, attr, buf);
781 #endif
782 		/* Count the printed lines */
783 		i++;
784 	}
785 
786 	/* Hack -- failed search */
787 	if (find) {
788 		bell();
789 		line = back;
790 		find = NULL;
791 		my_fclose(fff);	/* The evil file that waited open? */
792 		return (TRUE);
793 	}
794 
795 	/* Close the file */
796 	my_fclose(fff);
797 
798 	/* Escape */
799 	if (k == ESCAPE) return (FALSE);
800 
801 	/* Normal return */
802 	return (TRUE);
803 }
804 
805 
806 /*
807  * Peruse the On-Line-Help, starting at the given file.
808  *
809  * Disabled --KLJ--
810  */
do_cmd_help(int Ind,int line)811 void do_cmd_help(int Ind, int line)
812 {
813 //	cptr name = "mangband.hlp";
814 	cptr name = "tomenet.hlp";
815 
816 	/* Peruse the main help file */
817 	(void)do_cmd_help_aux(Ind, name, NULL, line, FALSE, 0);
818 }
819 
820 
821 
822 
823 
824 /*
825  * Hack -- display the contents of a file on the screen
826  *
827  * XXX XXX XXX Use this function for commands such as the
828  * "examine object" command.
829  */
show_file(int Ind,cptr name,cptr what,s32b line,int color,int divl)830 errr show_file(int Ind, cptr name, cptr what, s32b line, int color, int divl)
831 {
832 	/* Peruse the requested file */
833 	(void)do_cmd_help_aux(Ind, name, what, line, color, divl);
834 
835 	/* Success */
836 	return (0);
837 }
838 
839 
840 
841 
842 /*
843  * Process the player name.
844  * Extract a clean "base name".
845  * Build the savefile name if needed.
846  */
process_player_name(int Ind,bool sf)847 bool process_player_name(int Ind, bool sf)
848 {
849 	player_type *p_ptr = Players[Ind];
850 
851 	int i, k = 0;
852 
853 
854 	/* Cannot be too long */
855 	if (strlen(p_ptr->name) > 15) //was 20 once?
856 	{
857 		/* Name too long */
858 		Destroy_connection(p_ptr->conn, "Your name is too long!");
859 
860 		/* Abort */
861 		return FALSE;
862 	}
863 
864 	/* Cannot contain "icky" characters */
865 	for (i = 0; p_ptr->name[i]; i++)
866 	{
867 		/* No control characters */
868 		if (iscntrl(p_ptr->name[i]))
869 		{
870 			/* Illegal characters */
871 			Destroy_connection(p_ptr->conn, "Your name contains control chars!");
872 
873 			/* Abort */
874 			return FALSE;
875 		}
876 	}
877 
878 
879 #ifdef MACINTOSH
880 
881 	/* Extract "useful" letters */
882 	for (i = 0; p_ptr->name[i]; i++)
883 	{
884 		char c = p_ptr->name[i];
885 
886 		/* Convert "dot" to "underscore" */
887 		if (c == '.') c = '_';
888 
889 		/* Accept all the letters */
890 		p_ptr->basename[k++] = c;
891 	}
892 
893 #else
894 
895 	/* Extract "useful" letters */
896 	for (i = 0; p_ptr->name[i]; i++)
897 	{
898 		char c = p_ptr->name[i];
899 
900 		/* Accept some letters */
901 		if (isalpha(c) || isdigit(c)) p_ptr->basename[k++] = c;
902 
903 		/* Convert space, dot, and underscore to underscore */
904 		else if (strchr(SF_BAD_CHARS, c)) p_ptr->basename[k++] = '_';
905 	}
906 
907 #endif
908 
909 
910 #if defined(WINDOWS) || defined(MSDOS)
911 
912 	/* Hack -- max length */
913 	if (k > 8) k = 8;
914 
915 #endif
916 
917 	/* Terminate */
918 	p_ptr->basename[k] = '\0';
919 
920 	/* Require a "base" name */
921 	if (!p_ptr->basename[0]) strcpy(p_ptr->basename, "PLAYER");
922 
923 
924 #ifdef SAVEFILE_MUTABLE
925 
926 	/* Accept */
927 	sf = TRUE;
928 
929 #endif
930 
931 	/* Change the savefile name */
932 	if (sf)
933 	{
934 		char temp[128];
935 
936 		/* Rename the savefile, using the player_base */
937 		(void)snprintf(temp, sizeof(temp), "%s", p_ptr->basename);
938 
939 #ifdef VM
940 		/* Hack -- support "flat directory" usage on VM/ESA */
941 		(void)snprintf(temp, sizeof(temp), "%s.sv", player_base);
942 #endif /* VM */
943 
944 		/* Build the filename */
945 		path_build(p_ptr->savefile, MAX_PATH_LENGTH, ANGBAND_DIR_SAVE, temp);
946 	}
947 
948 	/* Success */
949 	return TRUE;
950 }
951 
952 
953 /*
954  * Gets a name for the character, reacting to name changes.
955  *
956  * Assumes that "display_player()" has just been called
957  * XXX Perhaps we should NOT ask for a name (at "birth()") on Unix?
958  *
959  * The name should be sent to us from the client, so this is unnecessary --KLJ--
960  */
get_name(int Ind)961 void get_name(int Ind)
962 {
963 }
964 
965 
966 
967 /*
968  * Hack -- commit suicide
969  */
do_cmd_suicide(int Ind)970 void do_cmd_suicide(int Ind)
971 {
972 	player_type *p_ptr = Players[Ind];
973 
974 	/* Mark as suicide */
975 	p_ptr->alive = FALSE;
976 
977 	/* Hack -- set the cause of death */
978 	if (!p_ptr->ghost) {
979 		//strcpy(p_ptr->died_from, "");
980 		strcpy(p_ptr->died_from_list, "self-inflicted wounds");
981 		p_ptr->died_from_depth = getlevel(&p_ptr->wpos);
982 	}
983 
984 	/* Hack -- clear ghost */
985 	p_ptr->ghost = 0;
986 
987 	if (p_ptr->total_winner) {
988 		if (p_ptr->iron_winner) kingly(Ind, 4);
989 		else kingly(Ind, 1);
990 	} else if (p_ptr->iron_winner) kingly(Ind, 3);
991 	/* Retirement in Valinor? - C. Blue :) */
992 	if (in_valinor(&p_ptr->wpos)) kingly(Ind, 2);
993 
994 	/* Kill him */
995 	p_ptr->deathblow = 0;
996 	player_death(Ind);
997 }
998 
999 
1000 
1001 /*
1002  * Save a character
1003  */
do_cmd_save_game(int Ind)1004 void do_cmd_save_game(int Ind)
1005 {
1006 	player_type *p_ptr = Players[Ind];
1007 
1008 	/* Disturb the player */
1009 	disturb(Ind, 1, 0);
1010 
1011 	/* Clear messages */
1012 	msg_print(Ind, NULL);
1013 
1014 	/* Handle stuff */
1015 	handle_stuff(Ind);
1016 
1017 	/* Message */
1018 	msg_print(Ind, "Saving game...");
1019 
1020 	/* The player is not dead */
1021 	(void)strcpy(p_ptr->died_from, "(saved)");
1022 
1023 	/* Forbid suspend */
1024 	signals_ignore_tstp();
1025 
1026 	/* Save the player */
1027 	if (save_player(Ind))
1028 	{
1029 		msg_print(Ind, "Saving game... done.");
1030 	}
1031 
1032 	/* Save failed (oops) */
1033 	else
1034 	{
1035 		msg_print(Ind, "Saving game... failed!");
1036 	}
1037 
1038 	/* Allow suspend again */
1039 	signals_handle_tstp();
1040 
1041 	/* Note that the player is not dead */
1042 	(void)strcpy(p_ptr->died_from, "(alive and well)");
1043 }
1044 
1045 
1046 
1047 /*
1048  * Hack -- Calculates the total number of points earned         -JWT-
1049  */
1050 /* FIXME: this function returns bad value when max_exp is stupidly large
1051  * (usually admin chars) */
total_points(int Ind)1052 int total_points(int Ind)
1053 {
1054 	u32b points, tmp_base, tmp1, tmp2, tmp3, tmp3a, bonusm, bonusd;
1055 	u32b lev_factoring;
1056 	player_type *p_ptr = Players[Ind];
1057 
1058 	/* kill maggot for 100% bonus on total score? -> no
1059 	   why a little bonus for HELL mode? the honour for the player
1060 	   who chooses hell mode on his own is far greater without it. -> no
1061 	   add cash to exp? what if the player collected cool gear instead?
1062 	   make cash allow the player to skip lots of levels on the ladder?  -> no
1063 	   max_dlv? just enter the staircase in lothlorien and back up. -> no
1064 	   For now let's calc it basing on pure progress in gameplay!: */
1065 	//exp counts mainly, level factors in
1066 //	return (p_ptr->max_exp * (p_ptr->max_plv + 30) / 30);
1067 
1068 	/* Bonus */
1069 	bonusm = 100; bonusd = 100;
1070 	if (p_ptr->mode & MODE_NO_GHOST)
1071 	{
1072 		bonusm += 25;
1073 	}
1074 	if (p_ptr->mode & MODE_HARD)
1075 	{
1076 		bonusm += 25;
1077 	}
1078 
1079 #ifndef ALT_EXPRATIO
1080 	/* Bonus might cause overflow at lvl 94+ - so maybe compensate */
1081 	tmp_base = (p_ptr->max_exp * 3) / 3; //leaving this, changing lev_factoring instead..
1082 	lev_factoring = 58; //was 33 -> overflow at 94+
1083 
1084 	/* split the number against overflow bug */
1085 	tmp1 = p_ptr->lev + lev_factoring;
1086 	tmp2 = lev_factoring;
1087 	tmp3a = tmp_base % 10000000;
1088 	tmp3 = (tmp_base - tmp3a) / 10000000;
1089 	points = (((tmp3a * bonusm) / bonusd) * tmp1) / tmp2;
1090 	points += ((((10000000 * bonusm) / bonusd) * tmp1) / tmp2) * tmp3;
1091 #else
1092 	tmp_base = p_ptr->max_exp;
1093 	lev_factoring = 75; /* (yeek warrior -> tl mimic : *3) */
1094 	lev_factoring = 230; /* (yeek warrior -> tl mimic : *2) */
1095 
1096 	/* split the number against overflow bug; divide by 10 to avoid overflow again */
1097 	tmp1 = (p_ptr->expfact + lev_factoring) / 10;
1098 	tmp2 = 310 / 10; /* (230 + 80 (yeek warrior)) */
1099 	tmp3a = tmp_base % 10000000;
1100 	tmp3 = (tmp_base - tmp3a) / 10000000;
1101 	points = (((tmp3a * bonusm) / bonusd) * tmp1) / tmp2;
1102 	points += ((((10000000 * bonusm) / bonusd) * tmp1) / tmp2) * tmp3;
1103 #endif
1104 	return points;
1105 
1106 	//level counts mainly, exp factors in at higher levels
1107 	//return (p_ptr->max_plv * (300 + (p_ptr->max_exp / 100000)) / 300);
1108 #if 0
1109 	/* Maggot bonus.. beware, r_idx is hard-coded! */
1110 	int i = p_ptr->r_killed[8]? 50 : 100;
1111 	if (p_ptr->mode & MODE_HARD) i = i * 5 / 4;
1112 
1113 	if (p_ptr->mode & MODE_NO_GHOST) return (((p_ptr->max_exp + (100 * p_ptr->max_dlv)) * 4 / 3)*i/100);
1114 	else return ((p_ptr->max_exp + (100 * p_ptr->max_dlv) + p_ptr->au)*i/100);
1115 #endif //0
1116 }
1117 
1118 /*
1119  * Semi-Portable High Score List Entry (128 bytes) -- BEN
1120  *
1121  * All fields listed below are null terminated ascii strings.
1122  *
1123  * In addition, the "number" fields are right justified, and
1124  * space padded, to the full available length (minus the "null").
1125  *
1126  * Note that "string comparisons" are thus valid on "pts".
1127  */
1128 
1129 typedef struct high_score high_score;
1130 struct high_score
1131 {
1132 	char what[8];           /* Version info (string) */
1133 
1134 	char pts[11];           /* Total Score (number) */
1135 
1136 	char gold[11];          /* Total Gold (number) */
1137 
1138 	char turns[11];         /* Turns Taken (number) */
1139 
1140 	char day[11];           /* Time stamp (string) */
1141 
1142 	char who[16];           /* Player Name (string) */
1143 	char whose[16];		/* Account Name (string) */
1144 
1145 	char sex[2];            /* Player Sex (string) */
1146 	char p_r[3];            /* Player Race (number) */
1147 	char p_c[3];            /* Player Class (number) */
1148 
1149 	char cur_lev[4];                /* Current Player Level (number) */
1150 	char cur_dun[4];                /* Current Dungeon Level (number) */
1151 	char max_lev[4];                /* Max Player Level (number) */
1152 	char max_dun[4];                /* Max Dungeon Level (number) */
1153 
1154 	char how[50];           /* Method of death (string) */
1155 
1156 	char mode[1];		/* Difficulty/character mode */
1157 };
1158 
1159 #if 0 /* different format? */
1160 typedef struct high_score_old high_score_old;
1161 struct high_score_old
1162 {
1163 	char what[8];           /* Version info (string) */
1164 
1165 	char pts[11];           /* Total Score (number) */
1166 
1167 	char gold[11];          /* Total Gold (number) */
1168 
1169 	char turns[11];         /* Turns Taken (number) */
1170 
1171 	char day[10];           /* Time stamp (string) */
1172 
1173 	char who[16];           /* Player Name (string) */
1174 	char whose[16];		/* Account Name (string) */
1175 
1176 	char sex[2];            /* Player Sex (string) */
1177 	char p_r[3];            /* Player Race (number) */
1178 	char p_c[3];            /* Player Class (number) */
1179 
1180 	char cur_lev[4];                /* Current Player Level (number) */
1181 	char cur_dun[4];                /* Current Dungeon Level (number) */
1182 	char max_lev[4];                /* Max Player Level (number) */
1183 	char max_dun[4];                /* Max Dungeon Level (number) */
1184 
1185 	char how[50];           /* Method of death (string) */
1186 
1187 	char mode[1];		/* Difficulty/character mode */
1188 };
1189 #else /* same format actually */
1190 typedef struct high_score high_score_old;
1191 #endif
1192 
1193 
highscore_send(char * buffer,int max)1194 int highscore_send(char *buffer, int max) {
1195 	int len = 0, len2;
1196 	FILE *hsp;
1197 	char buf[1024];
1198 	struct high_score score;
1199 	cptr mode, race, class;
1200 
1201 	path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
1202 
1203 	hsp = fopen(buf, "rb");
1204 	if (!hsp) return(0);
1205 
1206 	while (fread(&score, sizeof(struct high_score), 1, hsp)) {
1207 		switch (score.mode[0] & MODE_MASK) {
1208 			case MODE_NORMAL:
1209 				mode = "Normal";
1210 				break;
1211 			case MODE_HARD:
1212 				mode = "Hard";
1213 				break;
1214 			case MODE_NO_GHOST:
1215 				mode = "Unworldly";
1216 				break;
1217 			case (MODE_HARD | MODE_NO_GHOST):
1218 				mode = "Hellish";
1219 				break;
1220 			case MODE_EVERLASTING:
1221 				mode = "Everlasting";
1222 				break;
1223 			case MODE_PVP:
1224 				mode = "PvP";
1225 				break;
1226 			default:
1227 				mode = "Unknown";
1228 				break;
1229 		}
1230 
1231 		race = race_info[atoi(score.p_r)].title;
1232 		class = class_info[atoi(score.p_c)].title;
1233 
1234 		len2 = snprintf(buf, 1024, "pts=%s\ngold=%s\nturns=%s\nday=%s\nwho=%s\nwhose=%s\nsex=%s\nrace=%s\nclass=%s\ncur_lev=%s\ncur_dun=%s\nmax_lev=%s\nmax_dun=%s\nhow=%s\nmode=%s\n",
1235 			score.pts, score.gold, score.turns, score.day, score.who, score.whose, score.sex, race, class, score.cur_lev, score.cur_dun, score.max_lev, score.max_dun, score.how, mode);
1236 
1237 		if (len2 + len < max) {
1238 			memcpy(&buffer[len], buf, len2);
1239 			len += len2;
1240 			buffer[len] = '\0';
1241 		}
1242 	}
1243 
1244 	fclose(hsp);
1245 
1246 	return len;
1247 }
1248 
houses_send(char * buffer,int max)1249 int houses_send(char *buffer, int max) {
1250 	int i, len = 0, len2, screen_x, screen_y;
1251 	house_type *h_ptr;
1252 	struct dna_type *dna;
1253 	char buf[1024];
1254 	s32b price;
1255 	cptr owner, wpos;
1256 
1257 	for (i = 0; i < num_houses; i++) {
1258 		h_ptr = &houses[i];
1259 		dna = h_ptr->dna;
1260 
1261 		screen_y = h_ptr->dy * 5 / MAX_HGT;
1262 		screen_x = h_ptr->dx * 5 / MAX_WID;
1263 		wpos = wpos_format(0, &h_ptr->wpos);
1264 
1265 		price = dna->price;
1266 
1267 		if (houses[i].dna->owner_type == OT_PLAYER)
1268 			owner = lookup_player_name(houses[i].dna->owner);
1269 		else if (houses[i].dna->owner_type == OT_GUILD)
1270 			owner = guilds[houses[i].dna->owner].name;
1271 		else owner = "";
1272 
1273 		if (!owner) owner = "";
1274 
1275 		if (houses[i].dna->owner_type == OT_GUILD)
1276 			len2 = snprintf(buf, 1024, "location=[%d,%d] in %s\nprice=%d\nguild=%s\n",
1277 			    screen_y, screen_x, wpos, price, owner);
1278 		else
1279 			len2 = snprintf(buf, 1024, "location=[%d,%d] in %s\nprice=%d\nowner=%s\n",
1280 			    screen_y, screen_x, wpos, price, owner);
1281 
1282 		if (len + len2 < max) {
1283 			memcpy(&buffer[len], buf, len2);
1284 			len += len2;
1285 			buffer[len] = '\0';
1286 		}
1287 	}
1288 
1289 	return len;
1290 }
1291 
1292 /*
1293  * The "highscore" file descriptor, if available.
1294  */
1295 static int highscore_fd = -1;
1296 
1297 
1298 /*
1299  * Seek score 'i' in the highscore file
1300  */
highscore_seek(int i)1301 static int highscore_seek(int i)
1302 {
1303 	/* Seek for the requested record */
1304 	return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score)));
1305 }
1306 
1307 
1308 /*
1309  * Read one score from the highscore file
1310  */
highscore_read(high_score * score)1311 static errr highscore_read(high_score *score)
1312 {
1313 	/* Read the record, note failure */
1314 	return (fd_read(highscore_fd, (char*)(score), sizeof(high_score)));
1315 }
1316 
1317 
1318 /*
1319  * Write one score to the highscore file
1320  */
highscore_write(high_score * score)1321 static int highscore_write(high_score *score)
1322 {
1323 	/* Write the record, note failure */
1324 	return (fd_write(highscore_fd, (char*)(score), sizeof(high_score)));
1325 }
1326 
highscore_seek_old(int i)1327 static int highscore_seek_old(int i)
1328 {
1329 	/* Seek for the requested record */
1330 	return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score_old)));
1331 }
highscore_read_old(high_score_old * score)1332 static errr highscore_read_old(high_score_old *score)
1333 {
1334 	/* Read the record, note failure */
1335 	return (fd_read(highscore_fd, (char*)(score), sizeof(high_score_old)));
1336 }
1337 
1338 
1339 
1340 /*
1341  * Just determine where a new score *would* be placed
1342  * Return the location (0 is best) or -1 on failure
1343  */
1344 #ifndef NEW_HISCORE
highscore_where(high_score * score,bool * move_up,bool * move_down)1345 static int highscore_where(high_score *score, bool *move_up, bool *move_down)
1346 #else
1347 static int highscore_where(high_score *score, int *erased_slot)
1348 #endif
1349 {
1350 	int             i, slot_ret = -1;
1351 	high_score      the_score;
1352 #ifndef NEW_HISCORE
1353 	int		slot_pts = -1, slot_name = -1, slot_account = -1;
1354 #else
1355 	bool		flag_char, flag_acc, flag_c, flag_r;
1356 	int		slot_old = -1;
1357 	int		entries_from_same_account = 0;
1358 #endif
1359 
1360 	/* Paranoia -- it may not have opened */
1361 	if (highscore_fd < 0) return (-1);
1362 
1363 	/* Go to the start of the highscore file */
1364 	if (highscore_seek(0)) return (-1);
1365 
1366 
1367 #ifndef NEW_HISCORE /*restructuring with new hex flags, see #else branch - C. Blue */
1368 	/* Read until we get to a higher score
1369 	   or appropriate rules are met -C. Blue */
1370 	for (i = 0; i < MAX_HISCORES; i++)
1371 	{
1372 		if (highscore_read(&the_score))
1373 		{
1374 			if (slot_pts == -1) slot_pts = i;
1375 			break;
1376 		}
1377 		if ((strcmp(the_score.pts, score->pts) < 0) && (slot_pts == -1))
1378     			slot_pts = i;
1379 		if ((!strcmp(the_score.who, score->who)) && (slot_name == -1))
1380 			slot_name = i;
1381 		if ((!strcmp(the_score.whose, score->whose)) && (slot_account == -1))
1382 			slot_account = i;
1383 	}
1384 
1385 	/* Insert score right before any higher score */
1386 	(*move_up) = FALSE; (*move_down) = FALSE;
1387 	if (slot_pts != -1) slot_ret = slot_pts;
1388 
1389 	/* If this char is already on high-score list,
1390 	   and replace_hiscore is set to 1 then replace it! */
1391 	if ((cfg.replace_hiscore == 1) && (slot_name != -1)) {
1392 		/* Replace old score of this character */
1393 		if (slot_pts != -1) {
1394 			if (slot_name > slot_pts) (*move_up) = TRUE;
1395 			if (slot_name < slot_pts) (*move_down) = TRUE;
1396 		} else {
1397 			if (slot_name < MAX_HISCORES - 1) (*move_down) = TRUE;
1398 		}
1399 	}
1400 	/* If this char is already on high-score list,
1401 	   and the new score is better than the old one
1402 	   and replace_hiscore is set to 2 then replace it! */
1403 	else if ((cfg.replace_hiscore == 2) && (slot_name != -1)) {
1404 		if ((slot_pts > slot_name) || (slot_pts == -1)) {
1405 			/* ignore the new score */
1406 			slot_ret = -2; /* hack */
1407 		} else {
1408 			/* If we just have to replace a score with a
1409 			   better one at the _same_ position, we still
1410 			   set move_up = TRUE since this will prevent
1411 			   the previous entry from sliding down and have
1412 			   it been overwritten instead */
1413 			(*move_up) = TRUE;
1414 		}
1415 	}
1416 
1417 	if (slot_ret != -1) return (slot_ret);
1418 
1419 	/* The "last" entry is always usable */
1420 	return (MAX_HISCORES - 1);
1421 #else
1422 	/* Read entries until we find a slot with lower score or arrive at the end */
1423 	for (i = 0; i < MAX_HISCORES; i++) {
1424 		if (highscore_read(&the_score)) {
1425 			/* arrived at the end of entries -> take this one then */
1426 			slot_ret = i;
1427 			break;
1428 		}
1429 		/* always valid rule: sort in after next-highest score */
1430 		if (strcmp(the_score.pts, score->pts) < 0) {
1431 			slot_ret = i;
1432 			break;
1433 		}
1434 	}
1435 	/* hack: last entry is always 'available' (effectively unused though!)  */
1436 	if (slot_ret == -1) slot_ret = MAX_HISCORES - 1;
1437 
1438  #if 0 /* don't start after the appropriate slot, but check all slots */
1439 	/* Go to next position in highscore file, if available. Otherwise, skip the for loop: */
1440 	if (!highscore_seek(slot_ret + 1))
1441 	/* now check previous entries before adding this one, if required
1442 	   according to our custom extended rule set from tomenet.cfg */
1443 	for (i = slot_ret + 1; i < MAX_HISCORES; i++) {
1444  #else
1445 	/* Go to the start of the highscore file */
1446 	if (highscore_seek(0)) return (-1);
1447 	/* now check previous entries before adding this one, if required
1448 	   according to our custom extended rule set from tomenet.cfg,
1449 	   whether we find an entry we have to actually replace with the
1450 	   new one.. */
1451 	for (i = 0; i < MAX_HISCORES; i++) {
1452  #endif
1453 		if (highscore_read(&the_score)) {
1454 			/* arrived at the end of entries -> nothing to replace */
1455 			break;
1456 		}
1457 		/* first, prepare flag test results */
1458 		flag_char = flag_acc = flag_c = flag_r = TRUE;
1459 		/* check for same char name */
1460 		if (cfg.replace_hiscore & 0x08) {
1461 			if (strcmp(the_score.who, score->who)) flag_char = FALSE;
1462 		}
1463 		/* check for same acc name */
1464 		if (strcmp(the_score.whose, score->whose)) {
1465 			if (cfg.replace_hiscore & 0x10) flag_acc = FALSE;
1466 		} else { /* for cfg.replace_hiscore & 0x7 > 2 */
1467 			entries_from_same_account++;
1468 		}
1469 		/* check for same class */
1470 		if (cfg.replace_hiscore & 0x20) {
1471 			if (strcmp(the_score.p_c, score->p_c)) flag_c = FALSE;
1472 		}
1473 		/* check for same race */
1474 		if (cfg.replace_hiscore & 0x40) {
1475 			if (strcmp(the_score.p_r, score->p_r)) flag_r = FALSE;
1476 		}
1477 
1478 		/* now test special modes in combination with flag results */
1479 		if ((cfg.replace_hiscore & 0x7) == 1) {
1480 			/* replace older entries by newer entries */
1481 			if (flag_char && flag_acc && flag_c && flag_r) {
1482 				slot_old = i;
1483 				break;
1484 			}
1485 		}
1486 		if ((cfg.replace_hiscore & 0x7) == 2) {
1487 			/* replace lower entries by higher entries */
1488 			if (flag_char && flag_acc && flag_c && flag_r) {
1489 				if (strcmp(the_score.pts, score->pts) < 0) {
1490 					slot_old = i;
1491 					break;
1492 				} else {
1493 					/* hack for display_scores_aux later:
1494 					   show that this entry is our 'goal' (use another colour) */
1495 					slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1496 				}
1497 			}
1498 		}
1499 		if ((cfg.replace_hiscore & 0x7) == 3) {
1500 			/* replace lowest entry of this account which matches the flags */
1501 			if (flag_char && flag_acc && flag_c && flag_r) {
1502 				if (strcmp(the_score.pts, score->pts) < 0) {
1503 					slot_old = i;
1504 				} else {
1505 					/* hack for display_scores_aux later:
1506 					   show that this entry is our 'goal' (use another colour) */
1507 					slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1508 				}
1509 			/* replace last entry of this account anyway due to limitation of this mode? */
1510 			} else if (entries_from_same_account == 2) {
1511 				if (strcmp(the_score.pts, score->pts) < 0) {
1512 					slot_old = i;
1513 					break;
1514 				} else {
1515 					/* hack for display_scores_aux later:
1516 					   show that this entry is our 'goal' (use another colour) */
1517 					slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1518 				}
1519 			}
1520 		}
1521 		if ((cfg.replace_hiscore & 0x7) == 4) {
1522 			/* replace lowest entry of this account which matches the flags */
1523 			if (flag_char && flag_acc && flag_c && flag_r) {
1524 				if (strcmp(the_score.pts, score->pts) < 0) {
1525 					slot_old = i;
1526 				} else {
1527 					/* hack for display_scores_aux later:
1528 					   show that this entry is our 'goal' (use another colour) */
1529 					slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1530 				}
1531 			/* replace last entry of this account anyway due to limitation of this mode? */
1532 			} else if (entries_from_same_account == 3) {
1533 				if (strcmp(the_score.pts, score->pts) < 0) {
1534 					slot_old = i;
1535 					break;
1536 				} else {
1537 					/* hack for display_scores_aux later:
1538 					   show that this entry is our 'goal' (use another colour) */
1539 					slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1540 				}
1541 			}
1542 		}
1543 	}
1544 
1545  #if 0 /* basic version without -2 -i slot_old hack for display_scores_aux */
1546 	/* If this char is already on high-score list,
1547 	   and replace_hiscore is set to values other than 0,
1548 	   - resulting in 'slot_old != -1' - then replace it! */
1549 	(*erased_slot) = -1;
1550 	/* found an old entry to remove? */
1551 	if (slot_old > -1) {
1552 		/* if the old entry is on last position, it'll be overwritten anyway */
1553 		if (slot_old < MAX_HISCORES - 1) {
1554 			(*erased_slot) = slot_old;
1555 		}
1556 	}
1557 	/* Nothing to replace, although we're in replace mode,
1558 	   ie we theoretically made it into the high score, but
1559 	   our previous entry was too good to be replaced, and
1560 	   thereby our new entry is to be discarded? */
1561 	else if ((cfg.replace_hiscore & 0x7) != 0)
1562 		slot_ret = -1; /* hack: 'ignore the new score' */
1563  #else /* hacked for display_scores_aux best possible display */
1564 	/* If this char is already on high-score list,
1565 	   and replace_hiscore is set to values other than 0,
1566 	   - resulting in 'slot_old > -1' - then replace it! */
1567 	(*erased_slot) = slot_old;
1568 	/* Nothing to replace, although we're in replace mode
1569 	   AND we found a previous matching entry (slot_old < -1):
1570 	   ie we theoretically made it into the high score, but
1571 	   our previous entry was too good to be replaced, and
1572 	   thereby our new entry is to be discarded? */
1573 	if (((cfg.replace_hiscore & 0x7) != 0) && slot_old < -1)
1574 		slot_ret = -2 - slot_ret; /* hack: 'ignore the new score'
1575 					     hack: useful display for display_scores_aux */
1576  #endif
1577 
1578 	return (slot_ret);
1579 #endif
1580 }
1581 
1582 
1583 /*
1584  * Actually place an entry into the high score file
1585  * Return the location (0 is best) or -1 on "failure"
1586  */
1587 static int highscore_add(high_score *score) {
1588 	int             i, slot;
1589 	high_score      the_score, tmpscore;
1590 	bool            done = FALSE;
1591 #ifndef NEW_HISCORE
1592 	bool            move_up, move_down;
1593 #else
1594 	int		erased_slot, cur_slots = MAX_HISCORES;
1595 #endif
1596 
1597 	/* Paranoia -- it may not have opened */
1598 	if (highscore_fd < 0) return (-1);
1599 
1600 	/* Determine where the score should go */
1601 #ifndef NEW_HISCORE
1602 	slot = highscore_where(score, &move_up, &move_down);
1603 #else
1604 	slot = highscore_where(score, &erased_slot);
1605 #endif
1606 	/* Hack -- Not on the list
1607 	   (ie didn't match conditions to replace previous score) */
1608 	if (slot < 0) return (-1);
1609 
1610 	/* Hack -- prepare to dump the new score */
1611 	the_score = (*score);
1612 
1613 #ifndef NEW_HISCORE
1614 	if (!move_down) {
1615 		/* Slide all the scores down one */
1616 		for (i = slot; !done && (i < MAX_HISCORES); i++) {
1617 			/* Read the old guy, note errors */
1618 			if (highscore_seek(i)) return (-1);
1619 			if (highscore_read(&tmpscore)) done = TRUE;
1620 
1621 			/* Back up and dump the score we were holding */
1622 			if (highscore_seek(i)) return (-1);
1623 			if (highscore_write(&the_score)) return (-1);
1624 
1625 			if (move_up && !strcmp(score->who, tmpscore.who)) {
1626 				/* If older score is to be removed, we can stop here */
1627 				return(slot);
1628 			}
1629 
1630 			/* Hack -- Save the old score, for the next pass */
1631 			the_score = tmpscore;
1632 		}
1633 	} else {
1634 		/* Move upwards through the score board */
1635 		for (i = slot; !done && (i >= 0); i--) {
1636 			/* Read the old guy, note errors */
1637 			if (highscore_seek(i)) return (-1);
1638 			if (highscore_read(&tmpscore)) done = TRUE;
1639 
1640 			/* Back up and dump the score we were holding */
1641 			if (highscore_seek(i)) return (-1);
1642 			if (highscore_write(&the_score)) return (-1);
1643 
1644 			if (!strcmp(score->who, tmpscore.who)) {
1645 				/* If older score is to be removed, we can stop here */
1646 				return(slot);
1647 			}
1648 
1649 			/* Hack -- Save the old score, for the next pass */
1650 			the_score = tmpscore;
1651 		}
1652 	}
1653 #else
1654 	/* Erase one old entry first? */
1655 	if (erased_slot > -1) {
1656 		cur_slots = erased_slot + 1; /* how many used slots does the high score table currently have? */
1657 		for (i = erased_slot; i < MAX_HISCORES; i++) {
1658 			/* Read the following entry, if any */
1659 			if (highscore_seek(i + 1)) break;
1660 			if (highscore_read(&tmpscore)) break;
1661 			/* do log file entry: */
1662 //			if (i == erased_slot) s_printf("HISCORE_DEL: #%d %s\n", i, tmpscore.who);
1663 			/* hack: count high score table slots in use: */
1664 			cur_slots++;
1665 			/* Overwrite current entry with it */
1666 			if (highscore_seek(i)) return(-1);
1667 			if (highscore_write(&tmpscore)) return(-1);
1668 		}
1669 		/* take note if the newly inserted slot has to be moved
1670 		   because its actually below the erased slot */
1671 		if (slot > erased_slot) slot--;
1672 	}
1673 	/* Insert new entry - Just slide all the scores down one */
1674 	/* note ('for' destination: if we previously erased one entry,
1675 	   then it means the last entry has become obsolete, so we
1676 	   skip it when we shift downwards again, otherwise we'd
1677 	   duplicate it. */
1678 //	for (i = slot; !done && (i < (erased_slot > -1 ? MAX_HISCORES - 1 : MAX_HISCORES)); i++) {
1679 	for (i = slot; !done && (i < cur_slots); i++) {
1680 		/* Read the old guy, note errors */
1681 		if (highscore_seek(i)) return (-1);
1682 		if (highscore_read(&tmpscore)) done = TRUE;
1683 		/* Back up and dump the score we were holding */
1684 		if (highscore_seek(i)) return (-1);
1685 		if (highscore_write(&the_score)) return (-1);
1686 		/* Hack -- Save the old score, for the next pass */
1687 		the_score = tmpscore;
1688 	}
1689 #endif
1690 
1691 	/* Return location used */
1692 	return (slot);
1693 }
1694 
1695 
1696 
1697 /*
1698  * Display the scores in a given range.
1699  * Assumes the high score list is already open.
1700  * Only five entries per line, too much info.
1701  *
1702  * Mega-Hack -- allow "fake" entry at the given position.
1703  */
1704 #ifndef NEW_HISCORE
1705 static void display_scores_aux(int Ind, int line, int note, high_score *score)
1706 #else
1707 static void display_scores_aux(int Ind, int line, int note, int erased_slot, high_score *score)
1708 #endif
1709 {
1710 	int             i, j, from, to, place;//, attr
1711 	char attrc[3];
1712 
1713 	high_score      the_score;
1714 
1715 	char    out_val[256];
1716 
1717 	FILE *fff;
1718 	char file_name[MAX_PATH_LENGTH];
1719 
1720 	/* Hack - distinguish between wilderness and dungeon */
1721 	bool wilderness;
1722 
1723 	/* Hack - display if the player was a former winner */
1724 	char extra_info[40];
1725 
1726 	/* Paranoia -- it may not have opened */
1727 	if (highscore_fd < 0) return;
1728 
1729 	/* Temporary file */
1730 	if (path_temp(file_name, MAX_PATH_LENGTH)) return;
1731 
1732 	/* Open the temp file */
1733 	fff = my_fopen(file_name, "wb");
1734 	if (fff == (FILE*)NULL) return;
1735 
1736 	/* Assume we will show the first 20 */
1737 	from = 0;
1738 	to = SCORES_SHOWN;
1739 	if (to > MAX_HISCORES) to = MAX_HISCORES;
1740 
1741 
1742 	/* Seek to the beginning */
1743 	if (highscore_seek(0)){
1744 		my_fclose(fff);
1745 		return;
1746 	}
1747 
1748 	/* Hack -- Count the high scores */
1749 	for (i = 0; i < MAX_HISCORES; i++)
1750 		if (highscore_read(&the_score)) break;
1751 
1752 #ifndef NEW_HISCORE
1753 	/* Hack -- allow "fake" entry to be last */
1754 	if ((note == i) && score) i++;
1755 #else
1756 	if (note < -1) note = -(note + 2); /* unwrap display hack */
1757 	if (score) i++; /* hack: insert fake entry */
1758 #endif
1759 
1760 	/* Forget about the last entries */
1761 	if (i > to) i = to;
1762 
1763 	/* Show 5 per page, until "done" */
1764 	for (j = from, place = j+1; j < i; j++, place++) {
1765 		int pr, pc, clev, mlev, cdun, mdun;
1766 		byte modebuf;
1767 		char modestr[20], modecol[5];
1768 		cptr gold, when, aged;
1769 
1770 
1771 		/* Hack -- indicate death in yellow */
1772 		//attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
1773 
1774 
1775 		/* Mega-Hack -- insert a "fake" record */
1776 		if ((note == j) && score) {
1777 			sprintf(attrc, "\377G");
1778 			the_score = (*score);
1779 			//attr = TERM_L_GREEN;
1780 			score = NULL;
1781 			note = -1;
1782 			j--;
1783 			i--;
1784 		}
1785 
1786 		/* Read a normal record */
1787 		else {
1788 			sprintf(attrc, "\377w");
1789 #ifdef NEW_HISCORE
1790 			/* unwrap display hack part 2 */
1791 			if (erased_slot < -1 && -(erased_slot + 2) == j)
1792 				/* indicate our 'goal' character we're chasing, score-wise, for replacing him */
1793 				sprintf(attrc, "\377B");
1794 			else if (erased_slot > -1 && erased_slot == j)
1795 				/* indicate our ex-goal which we now seem to have reached, just for fun */
1796 				sprintf(attrc, "\377B");
1797 #endif
1798 			/* Read the proper record */
1799 			if (highscore_seek(j)) break;
1800 			if (highscore_read(&the_score)) break;
1801 		}
1802 
1803 
1804 		strcpy(extra_info, ".");
1805 		wilderness = FALSE;
1806 
1807 		/* Extract the race/class */
1808 		pr = atoi(the_score.p_r);
1809 		pc = atoi(the_score.p_c);
1810 
1811 		/* Extract the level info */
1812 		clev = atoi(the_score.cur_lev);
1813 		mlev = atoi(the_score.max_lev);
1814 #if 0
1815 		if (the_score.cur_dun[strlen(the_score.cur_dun) - 1] == '\001') {
1816 			wilderness = TRUE;
1817 			the_score.cur_dun[strlen(the_score.cur_dun) - 1] = '\0';
1818 		}
1819 #endif
1820 		cdun = atoi(the_score.cur_dun);
1821 		mdun = atoi(the_score.max_dun);
1822 
1823 		/* Hack -- extract the gold and such */
1824 		for (when = the_score.day; isspace(*when); when++) /* loop */;
1825 		for (gold = the_score.gold; isspace(*gold); gold++) /* loop */;
1826 		for (aged = the_score.turns; isspace(*aged); aged++) /* loop */;
1827 
1828 		modebuf = (the_score.mode[0] & MODE_MASK);
1829 		strcpy(modestr, "");
1830 		strcpy(modecol, "");
1831 		switch (modebuf) {
1832                 case MODE_HARD:
1833 			strcpy(modestr, "purgatorial ");
1834 //			strcpy(modecol, "\377s");
1835 	    	        break;
1836                 case MODE_NO_GHOST:
1837 			strcpy(modestr, "unworldly ");
1838 			strcpy(modecol, "\377D");
1839 	                break;
1840 		case (MODE_HARD + MODE_NO_GHOST):
1841 			strcpy(modestr, "hellish ");
1842 			strcpy(modecol, "\377D");
1843 			break;
1844                 case MODE_NORMAL:
1845 //			strcpy(modecol, "\377w");
1846                         break;
1847 		}
1848 
1849 		/* Hack ;) Remember if the player was a former winner */
1850 		if (the_score.how[strlen(the_score.how) - 1] == '\001')
1851 		{
1852 			strcpy(extra_info, ". (Defeated Morgoth)");
1853 			the_score.how[strlen(the_score.how) - 1] = '\0';
1854 		}
1855 
1856 		/* Dump some info */
1857 		snprintf(out_val, sizeof(out_val), "%2s%3d.%10s %s%s the %s%s %s, Lv.%d",
1858 			attrc, place, the_score.pts, modecol, the_score.who, modestr,
1859 			race_info[pr].title, class_info[pc].title,
1860 			clev);
1861 
1862 		/* Append a "maximum level" */
1863 		if (mlev > clev) strcat(out_val, format(" (max %d)", mlev));
1864 
1865 		/* Dump the first line */
1866 		fprintf(fff, "%s\n", out_val);
1867 
1868 		/* Another line of info */
1869 		if (strcmp(the_score.how, "winner") && strcmp(the_score.how, "*winner*") && strcmp(the_score.how, "iron champion") && strcmp(the_score.how, "iron emperor"))
1870 			snprintf(out_val, sizeof(out_val),
1871 				"               Killed by %s\n"
1872 				"               on %s %d%s%s",
1873 				the_score.how, wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1874 		else if (!strcmp(the_score.how, "winner"))
1875 			snprintf(out_val, sizeof(out_val),
1876 				"               \377vRetired after a legendary career\n"
1877 				"               on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1878 		else if (!strcmp(the_score.how, "*winner*"))
1879 			snprintf(out_val, sizeof(out_val),
1880 				"               \377vRetired on the shores of Valinor\n"
1881 				"               on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1882 		else if (!strcmp(the_score.how, "iron champion"))
1883 			snprintf(out_val, sizeof(out_val),
1884 				"               \377sRetired iron champion\n"
1885 				"               on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1886 		else if (!strcmp(the_score.how, "iron emperor"))
1887 			snprintf(out_val, sizeof(out_val),
1888 				"               \377vRetired from the iron throne\n"
1889 				"               on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1890 
1891 		/* Hack -- some people die in the town */
1892 		if (!cdun)
1893 		{
1894 			/* (can't be in Valinor while we're in town, can we) */
1895 			if (strcmp(the_score.how, "winner"))
1896 				snprintf(out_val, sizeof(out_val),
1897 					"               Killed by %s\n"
1898 					"               in town%s",
1899 					the_score.how, mdun > cdun ? format(" (max %d)", mdun) : "");
1900 			else
1901 				snprintf(out_val, sizeof(out_val),
1902 					"               \377vRetired after a legendary career\n"
1903 					"               in town%s", mdun > cdun ? format(" (max %d)", mdun) : "");
1904 		}
1905 
1906 		/* Append a "maximum level" */
1907 //		if (mdun > cdun) strcat(out_val, format(" (max %d)", mdun));
1908 
1909 		/* Dump the info */
1910 		fprintf(fff, "%s\n", out_val);
1911 
1912 		/* And still another line of info */
1913 		sprintf(out_val,
1914 			"               (Date %s, Gold %s, Turn %s).",
1915 			when, gold, aged);
1916 		fprintf(fff, "%s\n", out_val);
1917 
1918 #if 0 /* bad formatting in > 4.4.1.7, better without this */
1919 		/* Print newline if this isn't the last one */
1920 		if (j < i - 1)
1921 #endif
1922 			fprintf(fff, "\n");
1923 	}
1924 
1925 	/* Close the file */
1926 	my_fclose(fff);
1927 
1928 	/* Display the file contents */
1929 	show_file(Ind, file_name, "High Scores", line, 0, 5);
1930 
1931 	/* Remove the file */
1932 	fd_kill(file_name);
1933 }
1934 
1935 
1936 
1937 
1938 /*
1939  * Enters a players name on a hi-score table, if "legal", and in any
1940  * case, displays some relevant portion of the high score list.
1941  *
1942  * Assumes "signals_ignore_tstp()" has been called.
1943  */
1944 static errr top_twenty(int Ind) {
1945 	player_type *p_ptr = Players[Ind];
1946 	//int          j;
1947 
1948 	high_score   the_score;
1949 
1950 	time_t ct = time((time_t*)0);
1951 
1952 	if (is_admin(p_ptr)) return 0;
1953 
1954 	/* No score file */
1955 	if (highscore_fd < 0) {
1956 		s_printf("Score file unavailable.\n");
1957 		return (0);
1958 	}
1959 
1960 	/* Interupted */
1961 	if (!p_ptr->total_winner && streq(p_ptr->died_from, "Interrupting")) {
1962 		msg_print(Ind, "Score not registered due to interruption.");
1963 		/* display_scores_aux(0, 10, -1, NULL); */
1964 		return (0);
1965 	}
1966 
1967 	/* Quitter */
1968 	if (!p_ptr->total_winner && streq(p_ptr->died_from, "Quitting")) {
1969 		msg_print(Ind, "Score not registered due to quitting.");
1970 		/* display_scores_aux(0, 10, -1, NULL); */
1971 		return (0);
1972 	}
1973 
1974 
1975 	/* Clear the record */
1976 	WIPE(&the_score, high_score);
1977 
1978 	/* Save the version */
1979 	sprintf(the_score.what, "%u.%u.%u",
1980 		VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
1981 
1982 	/* Calculate and save the points */
1983 	sprintf(the_score.pts, "%10u", total_points(Ind));
1984 	the_score.pts[10] = '\0';
1985 
1986 	/* Save the current gold */
1987 	sprintf(the_score.gold, "%10u", p_ptr->au);
1988 	the_score.gold[10] = '\0';
1989 
1990 	/* Save the current turn */
1991 	sprintf(the_score.turns, "%10u", turn);
1992 	the_score.turns[10] = '\0';
1993 
1994 #ifdef HIGHSCORE_DATE_HACK
1995 	/* Save the date in a hacked up form (9 chars) */
1996 	sprintf(the_score.day, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22);
1997 #else
1998 	/* Save the date in standard form (8 chars) */
1999 //	strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct));
2000 	/* Save the date in real standard form (10 chars) */
2001 	strftime(the_score.day, 11, "%Y/%m/%d", localtime(&ct));
2002 #endif
2003 
2004 	/* Save the player name (15 chars) */
2005 	sprintf(the_score.who, "%-.15s", p_ptr->name);
2006 	/* Save the player account name (15 chars) */
2007 	sprintf(the_score.whose, "%-.15s", p_ptr->accountname);
2008 
2009 	/* Save the player info */
2010 	sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2011 	sprintf(the_score.p_r, "%2d", p_ptr->prace);
2012 	sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2013 
2014 	/* Save the level and such */
2015 	sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2016 	sprintf(the_score.cur_dun, "%3d", p_ptr->died_from_depth);
2017 	sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2018 	sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2019 
2020 	/* Save the cause of death (31 chars) */
2021 	/* HACKED to take the saved cause of death of the character, not the ghost */
2022 	sprintf(the_score.how, "%-.49s", p_ptr->died_from_list);
2023 
2024 	/* Save the modus (hellish, bat..) */
2025 //	sprintf(the_score.mode, "%c", p_ptr->mode);
2026 	the_score.mode[0] = p_ptr->mode;
2027 
2028 	/* Lock (for writing) the highscore file, or fail */
2029 	if (fd_lock(highscore_fd, F_WRLCK)) return (1);
2030 
2031 	/* Add a new entry to the score list, see where it went */
2032 	//j = highscore_add(&the_score);
2033 	highscore_add(&the_score);
2034 
2035 	/* Unlock the highscore file, or fail */
2036 	if (fd_lock(highscore_fd, F_UNLCK)) return (1);
2037 
2038 
2039 #if 0
2040 	/* Hack -- Display the top fifteen scores */
2041 	if (j < 10) display_scores_aux(0, 15, j, NULL);
2042 	/* Display the scores surrounding the player */
2043 	else {
2044 		display_scores_aux(0, 5, j, NULL);
2045 		display_scores_aux(j - 2, j + 7, j, NULL);
2046 	}
2047 #endif
2048 
2049 
2050 	/* Success */
2051 	return (0);
2052 }
2053 
2054 
2055 /*
2056  * Predict the players location, and display it.
2057  */
2058 static errr predict_score(int Ind, int line) {
2059 	player_type *p_ptr = Players[Ind];
2060 	int		j;
2061 	high_score	the_score;
2062 #ifndef NEW_HISCORE
2063 	bool		move_up, move_down;
2064 #else
2065 	int		erased_slot;
2066 #endif
2067 
2068 	/* No score file */
2069 	if (highscore_fd < 0) {
2070 		s_printf("Score file unavailable.\n");
2071 		return (0);
2072 	}
2073 
2074 
2075 	/* Save the version */
2076 	sprintf(the_score.what, "%u.%u.%u",
2077 		VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
2078 
2079 	/* Calculate and save the points */
2080 	sprintf(the_score.pts, "%10u", total_points(Ind));
2081 
2082 	/* Save the current gold */
2083 	sprintf(the_score.gold, "%10u", p_ptr->au);
2084 
2085 	/* Save the current turn */
2086 	sprintf(the_score.turns, "%10u", turn);
2087 
2088 	/* Hack -- no time needed */
2089 	strcpy(the_score.day, "TODAY");
2090 
2091 	/* Save the player name (15 chars) */
2092 	sprintf(the_score.who, "%-.15s", p_ptr->name);
2093 	/* Save the player name (15 chars) */
2094 	sprintf(the_score.whose, "%-.15s", p_ptr->accountname);
2095 
2096 	/* Save the player info */
2097 	sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2098 	sprintf(the_score.p_r, "%2d", p_ptr->prace);
2099 	sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2100 
2101 	/* Save the level and such */
2102 	sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2103 	sprintf(the_score.cur_dun, "%3d", getlevel(&p_ptr->wpos));
2104 	sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2105 	sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2106 
2107 	/* Hack -- no cause of death */
2108 	strcpy(the_score.how, "nobody (yet!)");
2109 
2110 	/* Modus.. */
2111 	the_score.mode[0] = p_ptr->mode;
2112 
2113 	/* See where the entry would be placed */
2114 #ifndef NEW_HISCORE
2115 	j = highscore_where(&the_score, &move_up, &move_down);
2116 	/* Hack -- Display the top n scores */
2117 	if (is_admin(p_ptr))
2118 		display_scores_aux(Ind, line, -1, NULL);
2119 	else if (j < (SCORES_SHOWN - 1))
2120 		display_scores_aux(Ind, line, j, &the_score);
2121 	/* Display some "useful" scores */
2122 	else
2123 		display_scores_aux(Ind, line, SCORES_SHOWN - 1, &the_score);  /* -1, NULL */
2124 #else
2125 	j = highscore_where(&the_score, &erased_slot);
2126 	if (is_admin(p_ptr))
2127 		display_scores_aux(Ind, line, -1, -1, NULL);
2128 	else
2129 		display_scores_aux(Ind, line, j, erased_slot, &the_score);
2130 #endif
2131 
2132 
2133 	/* Success */
2134 	return (0);
2135 }
2136 
2137 
2138 
2139 
2140 /*
2141  * Change a player into a King!                 -RAK-
2142  */
2143 void kingly(int Ind, int type) {
2144 	player_type *p_ptr = Players[Ind];
2145 
2146 #if 0	// No, this makes Delete_player fail!
2147 	/* Hack -- retire in town */
2148 	p_ptr->wpos.wx = 0;	// pfft, not 0 maybe
2149 	p_ptr->wpos.wy = 0;
2150 	p_ptr->wpos.wz = 0;
2151 #endif	// 0
2152 
2153 	/* Fake death */
2154 	//(void)strcpy(p_ptr->died_from_list, "Ripe Old Age");
2155 	switch (type) {
2156 	case 1: strcpy(p_ptr->died_from_list, "winner"); break; //retirement
2157 	case 2: strcpy(p_ptr->died_from_list, "*winner*"); break; //valinor retirement
2158 	case 3: strcpy(p_ptr->died_from_list, "iron champion"); break; //made it through IDDC
2159 	case 4: strcpy(p_ptr->died_from_list, "iron emperor"); break; //made it through IDDC and killed Morgoth
2160 	}
2161 
2162 	/* Restore the experience */
2163 	p_ptr->exp = p_ptr->max_exp;
2164 
2165 	/* Restore the level */
2166 	p_ptr->lev = p_ptr->max_plv;
2167 
2168 	/* Hack -- Player gets an XP bonus for beating the game */
2169 	/* p_ptr->exp = p_ptr->max_exp += 10000000L; */
2170 }
2171 
2172 
2173 /*
2174  * Add a player to the high score list.
2175  */
2176 void add_high_score(int Ind) {
2177 	char buf[1024];
2178 
2179 	/* Build the filename */
2180 	path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
2181 
2182 	/* Open the high score file, for reading/writing */
2183 	highscore_fd = fd_open(buf, O_RDWR);
2184 
2185 	/* Add them */
2186 	top_twenty(Ind);
2187 
2188 	/* Shut the high score file */
2189 	(void)fd_close(highscore_fd);
2190 
2191 	/* Forget the high score fd */
2192 	highscore_fd = -1;
2193 }
2194 
2195 
2196 /*
2197  * Close up the current game (player may or may not be dead)
2198  *
2199  * This function is called only from "main.c" and "signals.c".
2200  *
2201  * In here we try to save everybody's game, as well as save the server state.
2202  */
2203 void close_game(void) {
2204 	int i;
2205 
2206 	/* No suspending now */
2207 	signals_ignore_tstp();
2208 
2209 	for (i = 1; i <= NumPlayers; i++) {
2210 		player_type *p_ptr = Players[i];
2211 
2212 		/* Make sure the player is connected */
2213 		if (p_ptr->conn == NOT_CONNECTED)
2214 			continue;
2215 
2216 		/* Handle stuff */
2217 		handle_stuff(i);
2218 
2219 		/* Flush the messages */
2220 		msg_print(i, NULL);
2221 
2222 		/* Build the filename */
2223 		/*path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");*/
2224 
2225 		/* Open the high score file, for reading/writing */
2226 		/*highscore_fd = fd_open(buf, O_RDWR);*/
2227 
2228 
2229 		/* Handle death */
2230 		if (p_ptr->death) {
2231 			/* Handle retirement */
2232 		        /* Retirement in Valinor? - C. Blue :) */
2233 	    		if (in_valinor(&p_ptr->wpos)) kingly(i, 2);
2234 			else if (p_ptr->total_winner) kingly(i, 1);
2235 
2236 			/* Save memories */
2237 			if (!save_player(i)) msg_print(i, "death save failed!");
2238 
2239 			/* Handle score, show Top scores */
2240 			top_twenty(i);
2241 		}
2242 
2243 		/* Still alive */
2244 		else {
2245 			/* Save the game */
2246 			do_cmd_save_game(i);
2247 
2248 			/* Prompt for scores XXX XXX XXX */
2249 			/*prt("Press Return (or Escape).", 0, 40);*/
2250 		}
2251 
2252 
2253 		/* Shut the high score file */
2254 		/*(void)fd_close(highscore_fd);*/
2255 
2256 		/* Forget the high score fd */
2257 		/*highscore_fd = -1;*/
2258 	}
2259 
2260 	/* Save list of banned players */
2261 	save_banlist();
2262 
2263 	/* Save dynamic quest info */
2264 	save_quests();
2265 
2266 	/* Try to save the server information */
2267 	save_server_info();
2268 
2269 	/* Allow suspending now */
2270 	signals_handle_tstp();
2271 }
2272 
2273 
2274 /*
2275  * Hack -- Display the scores in a given range and quit.
2276  *
2277  * This function is only called from "main.c" when the user asks
2278  * to see the "high scores".
2279  */
2280 void display_scores(int Ind, int line) {
2281 	char buf[1024];
2282 
2283 	/* Build the filename */
2284 	path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
2285 
2286 	/* Open the binary high score file, for reading */
2287 	highscore_fd = fd_open(buf, O_RDONLY);
2288 
2289 	/* Paranoia -- No score file */
2290 	if (highscore_fd < 0) {
2291 		/* Message to server admin */
2292 		s_printf("Score file unavailable.\n");
2293 
2294 		/* Quit */
2295 		return;
2296 	}
2297 
2298 	/* Display the scores */
2299 	predict_score(Ind, line);
2300 
2301 	/* Shut the high score file */
2302 	(void)fd_close(highscore_fd);
2303 
2304 	/* Forget the high score fd */
2305 	highscore_fd = -1;
2306 
2307 	/* Quit */
2308 	/* quit(NULL); */
2309 }
2310 
2311 
2312 /*
2313  * Get a random line from a file
2314  * Based on the monster speech patch by Matt Graham,
2315  */
2316 errr get_rnd_line(cptr file_name, int entry, char *output, int max_len) {
2317 	FILE    *fp;
2318 	char    buf[1024];
2319 	int     line = 0, counter, test, numentries;
2320 	int     line_num = 0;
2321 	//bool    found = FALSE;
2322 
2323 
2324 	/* Build the filename */
2325 	path_build(buf, 1024, ANGBAND_DIR_TEXT, file_name);
2326 
2327 	/* Open the file */
2328 	fp = my_fopen(buf, "r");
2329 
2330 	/* Failed */
2331 	if (!fp) return (-1);
2332 
2333 	/* Find the entry of the monster */
2334 	while (TRUE) {
2335 		/* Get a line from the file */
2336 		if (my_fgets(fp, buf, 1024, FALSE) == 0) {
2337 			/* Count the lines */
2338 			line_num++;
2339 
2340 			/* Look for lines starting with 'N:' */
2341 			if ((buf[0] == 'N') && (buf[1] == ':')) {
2342 				/* Allow default lines */
2343 				if (buf[2] == '*') {
2344 					/* Default lines */
2345 					//found = TRUE;
2346 					break;
2347 				}
2348 				/* Get the monster number */
2349 				else if (sscanf(&(buf[2]), "%d", &test) != EOF) {
2350 					/* Is it the right monster? */
2351 					if (test == entry) {
2352 						//found = TRUE;
2353 						break;
2354 					}
2355 				} else {
2356 					my_fclose(fp);
2357 					return (-1);
2358 				}
2359 			}
2360 		} else {
2361 			/* Reached end of file */
2362 			my_fclose(fp);
2363 			return (-1);
2364 		}
2365 
2366 	}
2367 
2368 	/* Get the number of entries */
2369 	while (TRUE) {
2370 		/* Get the line */
2371 		if (my_fgets(fp, buf, 1024, FALSE) == 0) {
2372 			/* Count the lines */
2373 			line_num++;
2374 
2375 			/* Look for the number of entries */
2376 			if (isdigit(buf[0])) {
2377 				/* Get the number of entries */
2378 				numentries = atoi(buf);
2379 				break;
2380 			}
2381 		} else {
2382 			/* Count the lines */
2383 			line_num++;
2384 
2385 			my_fclose(fp);
2386 			return (-1);
2387 		}
2388 	}
2389 
2390 	if (numentries > 0) {
2391 		/* Grab an appropriate line number */
2392 		line = rand_int(numentries);
2393 
2394 		/* Get the random line */
2395 		for (counter = 0; counter <= line; counter++) {
2396 			/* Count the lines */
2397 			line_num++;
2398 
2399 			/* Try to read the line */
2400 			if (my_fgets(fp, buf, 1024, FALSE) == 0) {
2401 				/* Found the line */
2402 				if (counter == line) break;
2403 			} else {
2404 				my_fclose(fp);
2405 				return (-1);
2406 			}
2407 		}
2408 
2409 		/* Copy the line */
2410 		strncpy(output, buf, max_len);
2411 	}
2412 
2413 	/* Close the file */
2414 	my_fclose(fp);
2415 
2416 	/* Success */
2417 	return (line);
2418 }
2419 
2420 /* Clear objects so that artifacts get saved.
2421  * This probably isn't neccecary, as all the objects on each of
2422  * these levels should have been cleared by now. However, paranoia
2423  * can't hurt all that much... -APD
2424  */
2425 /* totally inefficient replacement - sorry. */
2426 /* it also doesnt respect non existent world positions */
2427 /* rewrite */
2428 /* k rewritten.. but not so better?	- Jir - */
2429 void wipeout_needless_objects() {
2430 #if 0	// exit_game_panic version
2431 	int i = 1;
2432 	int j,k;
2433 	struct worldpos wpos;
2434 	struct wilderness_type *wild;
2435 
2436 	for (i = 0; i < MAX_WILD_X; i++) {
2437 		wpos.wx = i;
2438 		for (j = 0; j < MAX_WILD_Y; j++) {
2439 			wpos.wy = j;
2440 			wild = &wild_info[j][i];
2441 
2442 			wpos.wz = 0;
2443 			if(getcave(&wpos) && !players_on_depth(&wpos)) wipe_o_list(&wpos);
2444 
2445 			if (wild->flags&WILD_F_UP)
2446 				for (k = 0;k < wild->tower->maxdepth; k++) {
2447 					wpos.wz = k;
2448 					if((getcave(&wpos)) && (!players_on_depth(&wpos))) wipe_o_list(&wpos);
2449 				}
2450 
2451 			if (wild->flags&WILD_F_DOWN)
2452 				for (k = 0;k < wild->dungeon->maxdepth; k++) {
2453 					wpos.wz = -k;
2454 					if((getcave(&wpos)) && (!players_on_depth(&wpos))) wipe_o_list(&wpos);
2455 				}
2456 		}
2457 	}
2458 #else	// 0
2459 	struct worldpos cwpos;
2460 	struct dungeon_type *d_ptr;
2461 	wilderness_type *w_ptr;
2462 	int x,y,z;
2463 
2464 	for (y = 0; y < MAX_WILD_Y; y++) {
2465 		cwpos.wy = y;
2466 		for (x = 0; x < MAX_WILD_X; x++) {
2467 			cwpos.wx = x;
2468 			cwpos.wz = 0;
2469 			w_ptr = &wild_info[y][x];
2470 //			if(getcave(&cwpos) && !players_on_depth(&cwpos)) wipe_o_list(&cwpos);
2471 			if(w_ptr->flags & WILD_F_DOWN){
2472 				d_ptr = w_ptr->dungeon;
2473 				for (z = 1; z <= d_ptr->maxdepth; z++) {
2474 					cwpos.wz = -z;
2475 					if(d_ptr->level[z-1].ondepth && d_ptr->level[z-1].cave)
2476 						wipe_o_list(&cwpos);
2477 				}
2478 			}
2479 			if(w_ptr->flags & WILD_F_UP){
2480 				d_ptr = w_ptr->tower;
2481 				for (z = 1; z <= d_ptr->maxdepth; z++) {
2482 					cwpos.wz = z;
2483 					if(d_ptr->level[z-1].ondepth && d_ptr->level[z-1].cave)
2484 						wipe_o_list(&cwpos);
2485 				}
2486 			}
2487 		}
2488 	}
2489 #endif	// 0
2490 }
2491 
2492 /*
2493  * Handle a fatal crash.
2494  *
2495  * Here we try to save every player's state, and the state of the server
2496  * in general.  Note that we must be extremely careful not to have a crash
2497  * in this function, or some things may not get saved.  Also, this function
2498  * may get called because some data structures are not in a "correct" state.
2499  * For this reason many paranoia checks are done to prevent bad pointer
2500  * dereferences.
2501  *
2502  * Note that this function would not be needed at all if there were no bugs.
2503  */
2504 void exit_game_panic(void) {
2505 	int i = 1;
2506 
2507 #ifndef WINDOWS
2508 	int dumppid, dumpstatus;
2509 
2510 	/* fork() a child process that will abort() - mikaelh */
2511 	dumppid = fork();
2512 	if (dumppid == 0) {
2513 		/* child process */
2514 
2515 #ifdef HANDLE_SIGNALS
2516 		signal(SIGABRT, SIG_IGN);
2517 #endif
2518 
2519 		/* dump core */
2520 		abort();
2521 	} else if (dumppid != -1) {
2522 		/* wait for the child */
2523 		waitpid(dumppid, &dumpstatus, 0);
2524 	}
2525 #endif
2526 
2527 	/* If nothing important has happened, just quit */
2528 	if (!server_generated || server_saved) quit("panic");
2529 
2530 	while (NumPlayers > (i - 1)) {
2531 		player_type *p_ptr = Players[i];
2532 
2533 		/* Don't dereference bad pointers */
2534 		if (!p_ptr) {
2535 			/* Skip to next player */
2536 			i++;
2537 
2538 			continue;
2539 		}
2540 
2541 		/* Hack -- turn off some things */
2542 		disturb(i, 1, 0);
2543 
2544 		/* Mega-Hack -- Delay death */
2545 		if (p_ptr->chp < 0) p_ptr->death = FALSE;
2546 
2547 		/* Hardcode panic save */
2548 		panic_save = 1;
2549 
2550 		/* Forbid suspend */
2551 		signals_ignore_tstp();
2552 
2553 		/* Indicate panic save */
2554 		(void)strcpy(p_ptr->died_from, "(panic save)");
2555 
2556 		/* Remember depth in the log files */
2557 		s_printf("Trying panic saving %s on %d %d %d..\n", p_ptr->name, p_ptr->wpos.wx, p_ptr->wpos.wy, p_ptr->wpos.wz);
2558 
2559 		/* Panic save, or get worried */
2560 		if (!save_player(i)) {
2561 			if (!Destroy_connection(p_ptr->conn, "panic save failed!")) {
2562 				/* Something extremely bad is going on, try to recover anyway */
2563 				i++;
2564 			}
2565 		}
2566 
2567 		/* Successful panic save */
2568 		if (!Destroy_connection(p_ptr->conn, "panic save succeeded!")) {
2569 			/* Something very bad happened, skip to next player */
2570 			i++;
2571 		}
2572 	}
2573 
2574 //	wipeout_needless_objects();
2575 
2576 	/* Save dynamic quest info */
2577 	save_quests();
2578 
2579 	/* Save list of banned players */
2580 	save_banlist();
2581 
2582 	if (!save_server_info()) quit("server panic info save failed!");
2583 
2584 #if 0 /* abort() done above in a child process */
2585 	/* make a core dump using abort() - mikaelh */
2586 # ifdef HANDLE_SIGNALS
2587 	signal(SIGABRT, SIG_IGN);
2588 # endif
2589 	abort();
2590 #endif
2591 
2592 	/* Successful panic save of server info */
2593 	quit("server panic info save succeeded!");
2594 }
2595 
2596 
2597 /* Save all characters and server info with 'panic save' flag set,
2598    to prevent non-autorecalled players after a flush error restart - C. Blue */
2599 void save_game_panic(void) {
2600 	int i = 1;
2601 
2602 	/* If nothing important has happened, just quit */
2603 //	if (!server_generated || server_saved) return;
2604 
2605 	while (NumPlayers > (i - 1)) {
2606 		player_type *p_ptr = Players[i];
2607 
2608 		/* Don't dereference bad pointers */
2609 		if (!p_ptr) {
2610 			/* Skip to next player */
2611 			i++;
2612 
2613 			continue;
2614 		}
2615 
2616 		/* Hack -- turn off some things */
2617 		disturb(i, 1, 0);
2618 
2619 		/* Mega-Hack -- Delay death */
2620 //		if (p_ptr->chp < 0) p_ptr->death = FALSE;
2621 
2622 		/* Hardcode panic save */
2623 		panic_save = 1;
2624 
2625 		/* Forbid suspend */
2626 //		signals_ignore_tstp();
2627 
2628 		/* Indicate panic save */
2629 //		(void)strcpy(p_ptr->died_from, "(panic save)");
2630 
2631 		/* Remember depth in the log files */
2632 		s_printf("Trying panic saving %s on %d %d %d..\n", p_ptr->name, p_ptr->wpos.wx, p_ptr->wpos.wy, p_ptr->wpos.wz);
2633 
2634 		/* Panic save */
2635 		save_player(i);
2636 	    	i++;
2637 	}
2638 
2639 //	wipeout_needless_objects();
2640 
2641 	/* Save dynamic quest info */
2642 	save_quests();
2643 
2644 	/* Save list of banned players */
2645 	save_banlist();
2646 
2647 	save_server_info();
2648 
2649 	/* No more panicking */
2650 	panic_save = 0;
2651 }
2652 
2653 
2654 /*
2655  * Windows specific replacement for signal handling [grk]
2656  */
2657 #ifdef WINDOWS
2658 #ifndef HANDLE_SIGNALS
2659 
2660 LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
2661 
2662 /* Callback to be called by Windows when our term closes, the user
2663  * logs off, the system is shutdown, etc.
2664  */
2665 BOOL ctrl_handler( DWORD fdwCtrlType ) {
2666 	/* Save everything and quit the game */
2667 	shutdown_server();
2668 
2669 	return TRUE;
2670 }
2671 
2672 /* Global unhandled exception handler */
2673 /* If the server crashes under Windows, this is where we end up */
2674 LONG WINAPI myUnhandledExceptionFilter(
2675   struct _EXCEPTION_POINTERS* ExceptionInfo) {
2676 	/* We don't report to the meta server in this case, the meta
2677 	 * server will detect that we've gone anyway
2678 	 */
2679 
2680 	/* Call the previous exception handler, which we are assuming
2681 	 * is the MinGW exception handler which should have been implicitly
2682 	 * setup when we loaded the exchndl.dll library.
2683 	 */
2684 	if(old_handler != NULL)
2685 	{
2686 	  old_handler(ExceptionInfo);
2687 	}
2688 
2689 	/* Save everything and quit the game */
2690 	exit_game_panic();
2691 
2692 	/* We don't expect to ever get here... but for what it's worth... */
2693 	return(EXCEPTION_EXECUTE_HANDLER);
2694 
2695 }
2696 
2697 
2698 void setup_exit_handler(void) {
2699 	/* Trap CTRL+C, Logoff, Shutdown, etc */
2700 	if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ctrl_handler, TRUE ) )
2701 	{
2702 		plog("Initialised exit save handler.");
2703 	}else{
2704 		plog("ERROR: Could not set panic save handler!");
2705 	}
2706 	/* Trap unhandled exceptions, i.e. server crashes */
2707 	old_handler = SetUnhandledExceptionFilter( myUnhandledExceptionFilter );
2708 }
2709 #endif
2710 #endif
2711 
2712 
2713 #ifdef HANDLE_SIGNALS
2714 
2715 
2716 /*
2717  * Handle signals -- suspend
2718  *
2719  * Actually suspend the game, and then resume cleanly
2720  *
2721  * This will probably inflict much anger upon the suspender, but it is still
2722  * allowed (for now) --KLJ--
2723  */
2724 static void handle_signal_suspend(int sig) {
2725 	/* Disable handler */
2726 	(void)signal(sig, SIG_IGN);
2727 
2728 #ifdef SIGSTOP
2729 
2730 	/* Suspend ourself */
2731 #ifndef WINDOWS
2732 	(void)kill(0, SIGSTOP);
2733 #else
2734 	raise(SIGSTOP);
2735 #endif
2736 
2737 #endif
2738 
2739 	/* Restore handler */
2740 	(void)signal(sig, handle_signal_suspend);
2741 }
2742 
2743 
2744 /*
2745  * Handle signals -- simple (interrupt and quit)
2746  *
2747  * This function was causing a *huge* number of problems, so it has
2748  * been simplified greatly.  We keep a global variable which counts
2749  * the number of times the user attempts to kill the process, and
2750  * we commit suicide if the user does this a certain number of times.
2751  *
2752  * We attempt to give "feedback" to the user as he approaches the
2753  * suicide thresh-hold, but without penalizing accidental keypresses.
2754  *
2755  * To prevent messy accidents, we should reset this global variable
2756  * whenever the user enters a keypress, or something like that.
2757  *
2758  * This simply calls "exit_game_panic()", which should try to save
2759  * everyone's character and the server info, which is probably nicer
2760  * than killing everybody. --KLJ--
2761  */
2762 static void handle_signal_simple(int sig) {
2763 	/* Disable handler */
2764 	(void)signal(sig, SIG_IGN);
2765 
2766 
2767 	/* Nothing to save, just quit */
2768 	if (!server_generated || server_saved) quit(NULL);
2769 
2770 
2771 	/* Count the signals */
2772 	signal_count++;
2773 
2774 
2775 	/* Allow suicide (after 5) */
2776 	if (signal_count >= 5) {
2777 		/* Tell the metaserver that we've quit */
2778 		Report_to_meta(META_DIE);
2779 
2780 		/* Save everything and quit the game */
2781 //              exit_game_panic();
2782 		shutdown_server();
2783 	}
2784 
2785 	/* Give warning (after 4) */
2786 	else if (signal_count >= 4) {
2787 		s_printf("Warning: Next signal kills server!\n");
2788 	}
2789 
2790 	/* Restore handler */
2791 	(void)signal(sig, handle_signal_simple);
2792 }
2793 
2794 static void handle_signal_bpipe(int sig){
2795 	(void)signal(sig, SIG_IGN);	/* This should not happen, but for the sake of convention... */
2796 	s_printf("SIGPIPE received\n");
2797 	(void)signal(sig, handle_signal_bpipe);
2798 }
2799 
2800 /*
2801  * Handle signal -- abort, kill, etc
2802  *
2803  * This one also calls exit_game_panic() --KLJ--
2804  */
2805 static void handle_signal_abort(int sig) {
2806 	/* Disable handler */
2807 	(void)signal(sig, SIG_IGN);
2808 
2809 	s_printf("Received signal %d.\n", sig);
2810 
2811 	/* Nothing to save, just quit */
2812 	if (!server_generated || server_saved) quit(NULL);
2813 
2814 	/* Tell the metaserver that we're going down */
2815 	Report_to_meta(META_DIE);
2816 
2817 	/* Save everybody and quit */
2818 	exit_game_panic();
2819 }
2820 
2821 
2822 
2823 
2824 /*
2825  * Ignore SIGTSTP signals (keyboard suspend)
2826  */
2827 void signals_ignore_tstp(void) {
2828 #ifdef SIGTSTP
2829 	(void)signal(SIGTSTP, SIG_IGN);
2830 #endif
2831 
2832 }
2833 
2834 /*
2835  * Handle SIGTSTP signals (keyboard suspend)
2836  */
2837 void signals_handle_tstp(void) {
2838 #ifdef SIGTSTP
2839 	(void)signal(SIGTSTP, handle_signal_suspend);
2840 #endif
2841 
2842 }
2843 
2844 
2845 /*
2846  * Prepare to handle the relevant signals
2847  */
2848 void signals_init(void) {
2849 #ifdef SIGHUP
2850 	(void)signal(SIGHUP, SIG_IGN);
2851 #endif
2852 
2853 #ifdef SIGTSTP
2854 	(void)signal(SIGTSTP, handle_signal_suspend);
2855 #endif
2856 
2857 #ifdef SIGINT
2858 	(void)signal(SIGINT, handle_signal_simple);
2859 #endif
2860 
2861 #ifdef SIGQUIT
2862 	(void)signal(SIGQUIT, handle_signal_simple);
2863 #endif
2864 
2865 #ifdef SIGFPE
2866 	(void)signal(SIGFPE, handle_signal_abort);
2867 #endif
2868 
2869 #ifdef SIGILL
2870 	(void)signal(SIGILL, handle_signal_abort);
2871 #endif
2872 
2873 #ifdef SIGTRAP
2874 	(void)signal(SIGTRAP, handle_signal_abort);
2875 #endif
2876 
2877 #ifdef SIGIOT
2878 	(void)signal(SIGIOT, handle_signal_abort);
2879 #endif
2880 
2881 #ifdef SIGKILL
2882 	(void)signal(SIGKILL, handle_signal_abort);
2883 #endif
2884 
2885 #ifdef SIGBUS
2886 	(void)signal(SIGBUS, handle_signal_abort);
2887 #endif
2888 
2889 #ifdef SIGSEGV
2890 	(void)signal(SIGSEGV, handle_signal_abort);
2891 #endif
2892 
2893 #ifdef SIGTERM
2894 	(void)signal(SIGTERM, handle_signal_abort);
2895 #endif
2896 
2897 #ifdef SIGPIPE
2898 	(void)signal(SIGPIPE, handle_signal_bpipe);
2899 #endif
2900 
2901 #ifdef SIGEMT
2902 	(void)signal(SIGEMT, handle_signal_abort);
2903 #endif
2904 
2905 #ifdef SIGDANGER
2906 	(void)signal(SIGDANGER, handle_signal_abort);
2907 #endif
2908 
2909 #ifdef SIGSYS
2910 	(void)signal(SIGSYS, handle_signal_abort);
2911 #endif
2912 
2913 #ifdef SIGXCPU
2914 	(void)signal(SIGXCPU, handle_signal_abort);
2915 #endif
2916 
2917 #ifdef SIGPWR
2918 	(void)signal(SIGPWR, handle_signal_abort);
2919 #endif
2920 }
2921 
2922 
2923 #else   /* HANDLE_SIGNALS */
2924 
2925 
2926 /*
2927  * Do nothing
2928  */
2929 void signals_ignore_tstp(void) { }
2930 
2931 /*
2932  * Do nothing
2933  */
2934 void signals_handle_tstp(void) { }
2935 
2936 /*
2937  * Do nothing
2938  */
2939 void signals_init(void) { }
2940 
2941 
2942 #endif  /* HANDLE_SIGNALS */
2943 
2944 
2945 #if 0
2946 
2947 /*
2948  * New v* functions for accessing files in memory
2949  * Uses non-blocking fds to read the files
2950  *  - mikaelh
2951  */
2952 
2953 /* Same as in nserver.c */
2954 #define MAX_FD 1023
2955 
2956 #define VFILE_INPUT_INSTALLED	1
2957 
2958 struct vfile {
2959 	char *name;
2960 	char *data;
2961 	size_t alloc;
2962 	size_t len;
2963 	off_t pos;
2964 	int flags;
2965 	int vflags;
2966 	int (*callback)(int, int);
2967 };
2968 
2969 typedef struct vfile vfile;
2970 
2971 struct vfile vfiles[MAX_FD];
2972 
2973 void vfile_receive_input(int fd, int arg);
2974 int vopen(const char *pathname, int flags, int (*callback)(int, int));
2975 int vclose(int fd);
2976 ssize_t vread(int fd, char *buf, size_t len);
2977 off_t vseek(int fd, off_t offset, int whence);
2978 
2979 void vfile_receive_input(int fd, int arg) {
2980 	vfile *vf = &vfiles[fd];
2981 	ssize_t read_len;
2982 
2983 	if (vf->alloc - vf->len < 4096) {
2984 		vf->alloc += 4096;
2985 		vf->data = realloc(vf->data, vf->alloc);
2986 	}
2987 
2988 	read_len = read(fd, vf->data + vf->len, vf->alloc - vf->len);
2989 	if (read_len > 0) {
2990 		vf->len += read_len;
2991 	}
2992 	else if (read_len == 0) { /* end of file */
2993 		remove_input(fd);
2994 		vf->vflags &= ~VFILE_INPUT_INSTALLED;
2995 		(*vf->callback)(fd, 0);
2996 	}
2997 	else if (errno != EAGAIN) {
2998 		/* fatal error, close the file and notify callback */
2999 		vclose(fd);
3000 		(*vf->callback)(fd, -1);
3001 	}
3002 }
3003 
3004 int vopen(const char *pathname, int flags, int (*callback)(int, int)) {
3005 	int fd;
3006 	size_t len;
3007 	vfile *vf;
3008 
3009 	flags |= O_NONBLOCK;
3010 
3011 	fd = open(pathname, flags);
3012 
3013 	if (fd >= 0) {
3014 		vf = &vfiles[fd];
3015 		memset(vf, 0, sizeof(struct vfile));
3016 		len = strlen(pathname);
3017 		vf->name = malloc(len + 1);
3018 		strcpy(vf->name, pathname);
3019 		vf->flags = flags;
3020 		vf->callback = callback;
3021 		vf->alloc = 4096;
3022 		vf->data = malloc(vf->alloc);
3023 
3024 		install_input(vfile_receive_input, fd, 0);
3025 		vf->vflags |= VFILE_INPUT_INSTALLED;
3026 	}
3027 
3028 	return fd;
3029 }
3030 
3031 int vclose(int fd) {
3032 	vfile *vf = &vfiles[fd];
3033 	int n;
3034 
3035 	if (vf->vflags & VFILE_INPUT_INSTALLED) {
3036 		remove_input(fd);
3037 		vf->vflags &= ~VFILE_INPUT_INSTALLED;
3038 	}
3039 
3040 	n = close(fd);
3041 
3042 	if (!n) {
3043 		free(vf->name);
3044 		free(vf->data);
3045 		memset(vf, 0, sizeof(struct vfile));
3046 	}
3047 
3048 	return n;
3049 }
3050 
3051 ssize_t vread(int fd, char *buf, size_t len) {
3052 	vfile *vf = &vfiles[fd];
3053 
3054 	if (!vf->data) {
3055 		return -1;
3056 	}
3057 
3058 	if (vf->pos + len > vf->len) {
3059 		len = vf->len - vf->pos;
3060 	}
3061 
3062 	if (len < 0) return 0;
3063 
3064 	memcpy(buf, &vf->data[vf->pos], len);
3065 	vf->pos += len;
3066 
3067 	return len;
3068 }
3069 
3070 off_t vseek(int fd, off_t offset, int whence) {
3071 	vfile *vf = &vfiles[fd];
3072 
3073 	if (whence == SEEK_SET) {
3074 		vf->pos = offset;
3075 	} else if (whence == SEEK_CUR) {
3076 		vf->pos += offset;
3077 	} else if (whence == SEEK_END) {
3078 		vf->pos = vf->len + offset;
3079 	} else {
3080 		return -1;
3081 	}
3082 
3083 	return vf->pos;
3084 }
3085 
3086 #endif // 0
3087 
3088 /* Erase the current highscore completely - C. Blue */
3089 bool highscore_reset(int Ind) {
3090         char buf[1024];
3091 
3092         /* Build the filename */
3093         path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3094 
3095 	/* bam (delete file, simply) */
3096         highscore_fd = fd_open(buf, O_TRUNC);
3097         (void)fd_close(highscore_fd);
3098 
3099         /* Forget the high score fd */
3100 	highscore_fd = -1;
3101         return(TRUE);
3102 }
3103 
3104 /* remove one specific entry from the highscore - C. Blue */
3105 bool highscore_remove(int Ind, int slot) {
3106 	int	i;
3107 	high_score	tmpscore;
3108         char buf[1024];
3109 
3110         /* Build the filename */
3111         path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3112 
3113         /* Open the binary high score file, for reading */
3114         highscore_fd = fd_open(buf, O_RDWR);
3115         /* Paranoia -- No score file */
3116         if (highscore_fd < 0) {
3117                 if (Ind) msg_print(Ind, "Score file unavailable!");
3118                 return(FALSE);
3119         }
3120         /* Lock (for writing) the highscore file, or fail */
3121         if (fd_lock(highscore_fd, F_WRLCK)) {
3122                 if (Ind) msg_print(Ind, "Couldn't lock highscore file for writing!");
3123                 return(FALSE);
3124         }
3125 
3126 	for (i = slot; i < MAX_HISCORES; i++) {
3127 		/* Read the following entry, if any */
3128 		if (highscore_seek(i + 1)) break;
3129 		if (highscore_read(&tmpscore)) break;
3130 		/* Overwrite current entry with it */
3131 		if (highscore_seek(i)) return(FALSE);
3132 		if (highscore_write(&tmpscore)) return(FALSE);
3133 	}
3134 
3135 	/* zero final entry */
3136 	WIPE(&tmpscore, sizeof(high_score));
3137 	strcpy(tmpscore.who, "(nobody)"); /* hack: erase name instead of 0 */
3138 	if (highscore_seek(i)) return(FALSE);
3139 	if (highscore_write(&tmpscore)) return(FALSE);
3140 
3141         /* Unlock the highscore file, or fail */
3142         if (fd_lock(highscore_fd, F_UNLCK)) {
3143                 if (Ind) msg_print(Ind, "Couldn't unlock highscore file from writing!");
3144                 return(FALSE);
3145         }
3146         /* Shut the high score file */
3147         (void)fd_close(highscore_fd);
3148 
3149         /* Forget the high score fd */
3150 	highscore_fd = -1;
3151 
3152 	return(TRUE);
3153 }
3154 
3155 /* Hack: Update old high score file to new format - C. Blue */
3156 bool highscore_file_convert(int Ind) {
3157 	int	i, entries;
3158 	high_score_old	oldscore[MAX_HISCORES];
3159 	high_score	newscore[MAX_HISCORES];
3160         char buf[1024];
3161 
3162         /* Build the filename */
3163         path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3164 
3165         /* Open the binary high score file, for reading */
3166         highscore_fd = fd_open(buf, O_RDWR);
3167         /* Paranoia -- No score file */
3168         if (highscore_fd < 0) {
3169                 if (Ind) msg_print(Ind, "Score file unavailable!");
3170                 return(FALSE);
3171         }
3172 
3173 	for (i = 0; i < MAX_HISCORES; i++) {
3174 		/* Read old entries */
3175 		if (highscore_seek_old(i)) break;
3176 		if (highscore_read_old(&oldscore[i])) break;
3177 	}
3178 	entries = i;
3179 
3180         /* Shut the high score file */
3181         (void)fd_close(highscore_fd);
3182 
3183 #if 0 /* old conversion done once, for example */
3184 	/* convert entries */
3185 	for (i = 0; i < entries; i++) {
3186 		char mod[80];
3187 
3188 		strcpy(newscore[i].what, oldscore[i].what);
3189 		strcpy(newscore[i].pts, oldscore[i].pts);
3190 		strcpy(newscore[i].gold, oldscore[i].gold);
3191 		strcpy(newscore[i].turns, oldscore[i].turns);
3192 
3193 		/* convert date format */
3194 		mod[0] = '2';
3195 		mod[1] = '0';
3196 		mod[2] = oldscore[i].day[6];
3197 		mod[3] = oldscore[i].day[7];
3198 		mod[4] = '/';
3199 		mod[5] = oldscore[i].day[0];
3200 		mod[6] = oldscore[i].day[1];
3201 		mod[7] = '/';
3202 		mod[8] = oldscore[i].day[3];
3203 		mod[9] = oldscore[i].day[4];
3204 		mod[10] = '\0';
3205 
3206 		strcpy(newscore[i].day, mod);
3207 
3208 		strcpy(newscore[i].who, oldscore[i].who);
3209 		strcpy(newscore[i].whose, oldscore[i].whose);
3210 
3211 		strcpy(newscore[i].sex, oldscore[i].sex);
3212 		strcpy(newscore[i].p_r, oldscore[i].p_r);
3213 		strcpy(newscore[i].p_c, oldscore[i].p_c);
3214 
3215 		strcpy(newscore[i].cur_lev, oldscore[i].cur_lev);
3216 		strcpy(newscore[i].cur_dun, oldscore[i].cur_dun);
3217 		strcpy(newscore[i].max_lev, oldscore[i].max_lev);
3218 		strcpy(newscore[i].max_dun, oldscore[i].max_dun);
3219 
3220 		strcpy(newscore[i].how, oldscore[i].how);
3221 		strcpy(newscore[i].mode, oldscore[i].mode);
3222 	}
3223 #else /* new conversion: inserting RACE_KOBOLD */
3224 	for (i = 0; i < entries; i++) {
3225 		int r;
3226 		strcpy(newscore[i].what, oldscore[i].what);
3227 		strcpy(newscore[i].pts, oldscore[i].pts);
3228 		strcpy(newscore[i].gold, oldscore[i].gold);
3229 		strcpy(newscore[i].turns, oldscore[i].turns);
3230 
3231 		strcpy(newscore[i].day, oldscore[i].day);
3232 
3233 		strcpy(newscore[i].who, oldscore[i].who);
3234 		strcpy(newscore[i].whose, oldscore[i].whose);
3235 
3236 		strcpy(newscore[i].sex, oldscore[i].sex);
3237 
3238 		r = atoi(oldscore[i].p_r);
3239 		if (r >= RACE_KOBOLD) {
3240 			r++;
3241 			sprintf(newscore[i].p_r, "%2d", r);
3242 		} else strcpy(newscore[i].p_r, oldscore[i].p_r);
3243 
3244 		strcpy(newscore[i].p_c, oldscore[i].p_c);
3245 
3246 		strcpy(newscore[i].cur_lev, oldscore[i].cur_lev);
3247 		strcpy(newscore[i].cur_dun, oldscore[i].cur_dun);
3248 		strcpy(newscore[i].max_lev, oldscore[i].max_lev);
3249 		strcpy(newscore[i].max_dun, oldscore[i].max_dun);
3250 
3251 		strcpy(newscore[i].how, oldscore[i].how);
3252 		strcpy(newscore[i].mode, oldscore[i].mode);
3253 	}
3254 #endif
3255 	/* bam (delete file, simply) */
3256         highscore_fd = fd_open(buf, O_TRUNC);
3257         (void)fd_close(highscore_fd);
3258 
3259         /* Open the binary high score file, for writing */
3260         highscore_fd = fd_open(buf, O_RDWR);
3261         /* Paranoia -- No score file */
3262         if (highscore_fd < 0) {
3263                 if (Ind) msg_print(Ind, "Score file unavailable!");
3264                 return(FALSE);
3265         }
3266 
3267         /* Lock (for writing) the highscore file, or fail */
3268         if (fd_lock(highscore_fd, F_WRLCK)) {
3269                 if (Ind) msg_print(Ind, "Couldn't lock highscore file for writing!");
3270                 return(FALSE);
3271         }
3272 
3273 	for (i = 0; i < entries; i++) {
3274 		/* Skip to end */
3275 		if (highscore_seek(i)) return (-1);
3276 		/* add new entry */
3277 		if (highscore_write(&newscore[i])) return (-1);
3278 	}
3279 
3280         /* Unlock the highscore file, or fail */
3281         if (fd_lock(highscore_fd, F_UNLCK)) {
3282                 if (Ind) msg_print(Ind, "Couldn't unlock highscore file from writing!");
3283                 return(FALSE);
3284         }
3285         /* Shut the high score file */
3286         (void)fd_close(highscore_fd);
3287 
3288         /* Forget the high score fd */
3289 	highscore_fd = -1;
3290 
3291 	return(TRUE);
3292 }
3293