OSDN Git Service

fix #36659
[jnethack/source.git] / src / topten.c
1 /* NetHack 3.6  topten.c        $NHDT-Date: 1448117546 2015/11/21 14:52:26 $  $NHDT-Branch: master $:$NHDT-Revision: 1.40 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 #include "hack.h"
11 #include "dlb.h"
12 #ifdef SHORT_FILENAMES
13 #include "patchlev.h"
14 #else
15 #include "patchlevel.h"
16 #endif
17
18 #ifdef VMS
19 /* We don't want to rewrite the whole file, because that entails
20    creating a new version which requires that the old one be deletable. */
21 #define UPDATE_RECORD_IN_PLACE
22 #endif
23
24 /*
25  * Updating in place can leave junk at the end of the file in some
26  * circumstances (if it shrinks and the O.S. doesn't have a straightforward
27  * way to truncate it).  The trailing junk is harmless and the code
28  * which reads the scores will ignore it.
29  */
30 #ifdef UPDATE_RECORD_IN_PLACE
31 static long final_fpos;
32 #endif
33
34 #define done_stopprint program_state.stopprint
35
36 #define newttentry() (struct toptenentry *) alloc(sizeof (struct toptenentry))
37 #define dealloc_ttentry(ttent) free((genericptr_t) (ttent))
38 #define NAMSZ 10
39 #define DTHSZ 100
40 #define ROLESZ 3
41
42 struct toptenentry {
43     struct toptenentry *tt_next;
44 #ifdef UPDATE_RECORD_IN_PLACE
45     long fpos;
46 #endif
47     long points;
48     int deathdnum, deathlev;
49     int maxlvl, hp, maxhp, deaths;
50     int ver_major, ver_minor, patchlevel;
51     long deathdate, birthdate;
52     int uid;
53     char plrole[ROLESZ + 1];
54     char plrace[ROLESZ + 1];
55     char plgend[ROLESZ + 1];
56     char plalign[ROLESZ + 1];
57     char name[NAMSZ + 1];
58     char death[DTHSZ + 1];
59 } * tt_head;
60 /* size big enough to read in all the string fields at once; includes
61    room for separating space or trailing newline plus string terminator */
62 #define SCANBUFSZ (4 * (ROLESZ + 1) + (NAMSZ + 1) + (DTHSZ + 1) + 1)
63
64 STATIC_DCL void FDECL(topten_print, (const char *));
65 STATIC_DCL void FDECL(topten_print_bold, (const char *));
66 STATIC_DCL xchar FDECL(observable_depth, (d_level *));
67 STATIC_DCL void NDECL(outheader);
68 STATIC_DCL void FDECL(outentry, (int, struct toptenentry *, BOOLEAN_P));
69 STATIC_DCL void FDECL(discardexcess, (FILE *));
70 STATIC_DCL void FDECL(readentry, (FILE *, struct toptenentry *));
71 STATIC_DCL void FDECL(writeentry, (FILE *, struct toptenentry *));
72 STATIC_DCL void FDECL(writexlentry, (FILE *, struct toptenentry *));
73 STATIC_DCL long NDECL(encodexlogflags);
74 STATIC_DCL long NDECL(encodeconduct);
75 STATIC_DCL long NDECL(encodeachieve);
76 STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *));
77 STATIC_DCL int FDECL(classmon, (char *, BOOLEAN_P));
78 STATIC_DCL int FDECL(score_wanted, (BOOLEAN_P, int, struct toptenentry *, int,
79                                     const char **, int));
80 #ifdef NO_SCAN_BRACK
81 STATIC_DCL void FDECL(nsb_mung_line, (char *));
82 STATIC_DCL void FDECL(nsb_unmung_line, (char *));
83 #endif
84
85 static winid toptenwin = WIN_ERR;
86
87 /* "killed by",&c ["an"] 'killer.name' */
88 void
89 formatkiller(buf, siz, how)
90 char *buf;
91 unsigned siz;
92 int how;
93 {
94     static NEARDATA const char *const killed_by_prefix[] = {
95         /* DIED, CHOKING, POISONING, STARVING, */
96 /*JP
97         "killed by ", "choked on ", "poisoned by ", "died of ",
98 */
99         "\8e\80\82ñ\82¾", "\82Å\92\82\91§\82µ\82½", "\82Ì\93Å\82Å\8e\80\82ñ\82¾", "",
100         /* DROWNING, BURNING, DISSOLVED, CRUSHING, */
101 /*JP
102         "drowned in ", "burned by ", "dissolved in ", "crushed to death by ",
103 */
104          "\93M\8e\80\82µ\82½","\8fÄ\8e\80\82µ\82½", "\97n\8aâ\82É\97n\82¯\82½", "\89\9f\82µ\92×\82³\82ê\82½",
105         /* STONING, TURNED_SLIME, GENOCIDED, */
106 /*JP
107         "petrified by ", "turned to slime by ", "killed by ",
108 */
109          "\90Î\82É\82È\82Á\82½", "\82É\83X\83\89\83C\83\80\82É\82³\82ê\82½", "\8bs\8eE\82³\82ê\82½",
110         /* PANICKED, TRICKED, QUIT, ESCAPED, ASCENDED */
111         "", "", "", "", ""
112     };
113     unsigned l;
114     char *kname = killer.name;
115
116     buf[0] = '\0'; /* so strncat() can find the end */
117 #if 1 /*JP*//*\90æ\82É\91Î\8fÛ\82ð\83R\83s\81[*/
118     strncat(buf, kname, siz - 1);
119     siz -= strlen(buf);
120 #endif
121     switch (killer.format) {
122     default:
123         impossible("bad killer format? (%d)", killer.format);
124         /*FALLTHRU*/
125     case NO_KILLER_PREFIX:
126         break;
127     case KILLED_BY_AN:
128 #if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\95s\97v*/
129         kname = an(kname);
130 #endif
131         /*FALLTHRU*/
132     case KILLED_BY:
133 #if 0 /*JP*/
134         (void) strncat(buf, killed_by_prefix[how], siz - 1);
135         l = strlen(buf);
136         buf += l, siz -= l;
137 #else /*JP:\8aù\82É\91Î\8fÛ\82ð\83R\83s\81[\82µ\82Ä\82¢\82é\82Ì\82Å\92P\82É\92Ç\89Á*/
138         (void) strncat(buf, killed_by_prefix[how], siz - 1);
139 #endif
140         break;
141 #if 1 /*JP*/
142       case KILLED_SUFFIX:
143         (void) strncat(buf, "\82É\8eE\82³\82ê\82½", siz - 1);
144 #endif
145     }
146 #if 0 /*JP*//*\8aù\82É\83R\83s\81[\8dÏ\82Ý*/
147     /* we're writing into buf[0] (after possibly advancing buf) rather than
148        appending, but strncat() appends a terminator and strncpy() doesn't */
149     (void) strncat(buf, kname, siz - 1);
150 #endif
151 }
152
153 STATIC_OVL void
154 topten_print(x)
155 const char *x;
156 {
157     if (toptenwin == WIN_ERR)
158         raw_print(x);
159     else
160         putstr(toptenwin, ATR_NONE, x);
161 }
162
163 STATIC_OVL void
164 topten_print_bold(x)
165 const char *x;
166 {
167     if (toptenwin == WIN_ERR)
168         raw_print_bold(x);
169     else
170         putstr(toptenwin, ATR_BOLD, x);
171 }
172
173 STATIC_OVL xchar
174 observable_depth(lev)
175 d_level *lev;
176 {
177 #if 0
178     /* if we ever randomize the order of the elemental planes, we
179        must use a constant external representation in the record file */
180     if (In_endgame(lev)) {
181         if (Is_astralevel(lev))
182             return -5;
183         else if (Is_waterlevel(lev))
184             return -4;
185         else if (Is_firelevel(lev))
186             return -3;
187         else if (Is_airlevel(lev))
188             return -2;
189         else if (Is_earthlevel(lev))
190             return -1;
191         else
192             return 0; /* ? */
193     } else
194 #endif
195     return depth(lev);
196 }
197
198 /* throw away characters until current record has been entirely consumed */
199 STATIC_OVL void
200 discardexcess(rfile)
201 FILE *rfile;
202 {
203     int c;
204
205     do {
206         c = fgetc(rfile);
207     } while (c != '\n' && c != EOF);
208 }
209
210 STATIC_OVL void
211 readentry(rfile, tt)
212 FILE *rfile;
213 struct toptenentry *tt;
214 {
215     char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ],
216         s4[SCANBUFSZ], s5[SCANBUFSZ], s6[SCANBUFSZ];
217
218 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
219     static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
220     static const char fmt32[] = "%c%c %s %s%*c";
221     static const char fmt33[] = "%s %s %s %s %s %s%*c";
222 #else
223     static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
224     static const char fmt32[] = "%c%c %[^,],%[^\n]%*c";
225     static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
226 #endif
227
228 #ifdef UPDATE_RECORD_IN_PLACE
229     /* note: input below must read the record's terminating newline */
230     final_fpos = tt->fpos = ftell(rfile);
231 #endif
232 #define TTFIELDS 13
233     if (fscanf(rfile, fmt, &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
234                &tt->points, &tt->deathdnum, &tt->deathlev, &tt->maxlvl,
235                &tt->hp, &tt->maxhp, &tt->deaths, &tt->deathdate,
236                &tt->birthdate, &tt->uid) != TTFIELDS) {
237 #undef TTFIELDS
238         tt->points = 0;
239         discardexcess(rfile);
240     } else {
241         /* load remainder of record into a local buffer;
242            this imposes an implicit length limit of SCANBUFSZ
243            on every string field extracted from the buffer */
244         if (!fgets(inbuf, sizeof inbuf, rfile)) {
245             /* sscanf will fail and tt->points will be set to 0 */
246             *inbuf = '\0';
247         } else if (!index(inbuf, '\n')) {
248             Strcpy(&inbuf[sizeof inbuf - 2], "\n");
249             discardexcess(rfile);
250         }
251         /* Check for backwards compatibility */
252         if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3)) {
253             int i;
254
255             if (sscanf(inbuf, fmt32, tt->plrole, tt->plgend, s1, s2) == 4) {
256                 tt->plrole[1] = tt->plgend[1] = '\0'; /* read via %c */
257                 copynchars(tt->name, s1, (int) (sizeof tt->name) - 1);
258                 copynchars(tt->death, s2, (int) (sizeof tt->death) - 1);
259             } else
260                 tt->points = 0;
261             tt->plrole[1] = '\0';
262             if ((i = str2role(tt->plrole)) >= 0)
263                 Strcpy(tt->plrole, roles[i].filecode);
264             Strcpy(tt->plrace, "?");
265             Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
266             Strcpy(tt->plalign, "?");
267         } else if (sscanf(inbuf, fmt33, s1, s2, s3, s4, s5, s6) == 6) {
268             copynchars(tt->plrole, s1, (int) (sizeof tt->plrole) - 1);
269             copynchars(tt->plrace, s2, (int) (sizeof tt->plrace) - 1);
270             copynchars(tt->plgend, s3, (int) (sizeof tt->plgend) - 1);
271             copynchars(tt->plalign, s4, (int) (sizeof tt->plalign) - 1);
272             copynchars(tt->name, s5, (int) (sizeof tt->name) - 1);
273             copynchars(tt->death, s6, (int) (sizeof tt->death) - 1);
274         } else
275             tt->points = 0;
276 #ifdef NO_SCAN_BRACK
277         if (tt->points > 0) {
278             nsb_unmung_line(tt->name);
279             nsb_unmung_line(tt->death);
280         }
281 #endif
282     }
283
284     /* check old score entries for Y2K problem and fix whenever found */
285     if (tt->points > 0) {
286         if (tt->birthdate < 19000000L)
287             tt->birthdate += 19000000L;
288         if (tt->deathdate < 19000000L)
289             tt->deathdate += 19000000L;
290     }
291 }
292
293 STATIC_OVL void
294 writeentry(rfile, tt)
295 FILE *rfile;
296 struct toptenentry *tt;
297 {
298     static const char fmt32[] = "%c%c ";        /* role,gender */
299     static const char fmt33[] = "%s %s %s %s "; /* role,race,gndr,algn */
300 #ifndef NO_SCAN_BRACK
301     static const char fmt0[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
302     static const char fmtX[] = "%s,%s%s%s\n";
303 #else /* NO_SCAN_BRACK */
304     static const char fmt0[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ";
305     static const char fmtX[] = "%s %s%s%s\n";
306
307     nsb_mung_line(tt->name);
308     nsb_mung_line(tt->death);
309 #endif
310
311     (void) fprintf(rfile, fmt0, tt->ver_major, tt->ver_minor, tt->patchlevel,
312                    tt->points, tt->deathdnum, tt->deathlev, tt->maxlvl,
313                    tt->hp, tt->maxhp, tt->deaths, tt->deathdate,
314                    tt->birthdate, tt->uid);
315     if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3))
316         (void) fprintf(rfile, fmt32, tt->plrole[0], tt->plgend[0]);
317     else
318         (void) fprintf(rfile, fmt33, tt->plrole, tt->plrace, tt->plgend,
319                        tt->plalign);
320     (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
321                    tt->death,
322                    (multi ? ", while " : ""),
323                    (multi ? (multi_reason ? multi_reason : "helpless") : ""));
324
325 #ifdef NO_SCAN_BRACK
326     nsb_unmung_line(tt->name);
327     nsb_unmung_line(tt->death);
328 #endif
329 }
330
331 /* as tab is never used in eg. plname or death, no need to mangle those. */
332 STATIC_OVL void
333 writexlentry(rfile, tt)
334 FILE *rfile;
335 struct toptenentry *tt;
336 {
337 #define Fprintf (void) fprintf
338 #define XLOG_SEP '\t' /* xlogfile field separator. */
339     char buf[BUFSZ];
340
341     Sprintf(buf, "version=%d.%d.%d", tt->ver_major, tt->ver_minor,
342             tt->patchlevel);
343     Sprintf(eos(buf), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP,
344             tt->points, XLOG_SEP, tt->deathdnum, XLOG_SEP, tt->deathlev);
345     Sprintf(eos(buf), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP, tt->maxlvl,
346             XLOG_SEP, tt->hp, XLOG_SEP, tt->maxhp);
347     Sprintf(eos(buf), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
348             XLOG_SEP, tt->deaths, XLOG_SEP, tt->deathdate, XLOG_SEP,
349             tt->birthdate, XLOG_SEP, tt->uid);
350     Fprintf(rfile, "%s", buf);
351     Sprintf(buf, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP,
352             tt->plrole, XLOG_SEP, tt->plrace, XLOG_SEP, tt->plgend, XLOG_SEP,
353             tt->plalign);
354     Fprintf(rfile, "%s%cname=%s%cdeath=%s",
355             buf, /* (already includes separator) */
356             XLOG_SEP, plname, XLOG_SEP, tt->death);
357     if (multi)
358         Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
359                 multi_reason ? multi_reason : "helpless");
360     Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP,
361             encodeconduct(), XLOG_SEP, moves, XLOG_SEP, encodeachieve());
362     Fprintf(rfile, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP,
363             (long) urealtime.realtime, XLOG_SEP,
364             (long) ubirthday, XLOG_SEP, (long) urealtime.finish_time);
365     Fprintf(rfile, "%cgender0=%s%calign0=%s", XLOG_SEP,
366             genders[flags.initgend].filecode, XLOG_SEP,
367             aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
368     Fprintf(rfile, "%cflags=0x%lx", XLOG_SEP, encodexlogflags());
369     Fprintf(rfile, "\n");
370 #undef XLOG_SEP
371 }
372
373 STATIC_OVL long
374 encodexlogflags()
375 {
376     long e = 0L;
377
378     if (wizard)
379         e |= 1L << 0;
380     if (discover)
381         e |= 1L << 1;
382     if (!u.uroleplay.numbones)
383         e |= 1L << 2;
384
385     return e;
386 }
387
388 STATIC_OVL long
389 encodeconduct()
390 {
391     long e = 0L;
392
393     if (!u.uconduct.food)
394         e |= 1L << 0;
395     if (!u.uconduct.unvegan)
396         e |= 1L << 1;
397     if (!u.uconduct.unvegetarian)
398         e |= 1L << 2;
399     if (!u.uconduct.gnostic)
400         e |= 1L << 3;
401     if (!u.uconduct.weaphit)
402         e |= 1L << 4;
403     if (!u.uconduct.killer)
404         e |= 1L << 5;
405     if (!u.uconduct.literate)
406         e |= 1L << 6;
407     if (!u.uconduct.polypiles)
408         e |= 1L << 7;
409     if (!u.uconduct.polyselfs)
410         e |= 1L << 8;
411     if (!u.uconduct.wishes)
412         e |= 1L << 9;
413     if (!u.uconduct.wisharti)
414         e |= 1L << 10;
415     if (!num_genocides())
416         e |= 1L << 11;
417
418     return e;
419 }
420
421 STATIC_OVL long
422 encodeachieve()
423 {
424     long r = 0L;
425
426     if (u.uachieve.bell)
427         r |= 1L << 0;
428     if (u.uachieve.enter_gehennom)
429         r |= 1L << 1;
430     if (u.uachieve.menorah)
431         r |= 1L << 2;
432     if (u.uachieve.book)
433         r |= 1L << 3;
434     if (u.uevent.invoked)
435         r |= 1L << 4;
436     if (u.uachieve.amulet)
437         r |= 1L << 5;
438     if (In_endgame(&u.uz))
439         r |= 1L << 6;
440     if (Is_astralevel(&u.uz))
441         r |= 1L << 7;
442     if (u.uachieve.ascended)
443         r |= 1L << 8;
444     if (u.uachieve.mines_luckstone)
445         r |= 1L << 9;
446     if (u.uachieve.finish_sokoban)
447         r |= 1L << 10;
448     if (u.uachieve.killed_medusa)
449         r |= 1L << 11;
450     if (u.uroleplay.blind)
451         r |= 1L << 12;
452     if (u.uroleplay.nudist)
453         r |= 1L << 13;
454
455     return r;
456 }
457
458 STATIC_OVL void
459 free_ttlist(tt)
460 struct toptenentry *tt;
461 {
462     struct toptenentry *ttnext;
463
464     while (tt->points > 0) {
465         ttnext = tt->tt_next;
466         dealloc_ttentry(tt);
467         tt = ttnext;
468     }
469     dealloc_ttentry(tt);
470 }
471
472 void
473 topten(how, when)
474 int how;
475 time_t when;
476 {
477     int uid = getuid();
478     int rank, rank0 = -1, rank1 = 0;
479     int occ_cnt = sysopt.persmax;
480     register struct toptenentry *t0, *tprev;
481     struct toptenentry *t1;
482     FILE *rfile;
483     register int flg = 0;
484     boolean t0_used;
485 #ifdef LOGFILE
486     FILE *lfile;
487 #endif /* LOGFILE */
488 #ifdef XLOGFILE
489     FILE *xlfile;
490 #endif /* XLOGFILE */
491
492 #ifdef _DCC
493     /* Under DICE 3.0, this crashes the system consistently, apparently due to
494      * corruption of *rfile somewhere.  Until I figure this out, just cut out
495      * topten support entirely - at least then the game exits cleanly.  --AC
496      */
497     return;
498 #endif
499
500     /* If we are in the midst of a panic, cut out topten entirely.
501      * topten uses alloc() several times, which will lead to
502      * problems if the panic was the result of an alloc() failure.
503      */
504     if (program_state.panicking)
505         return;
506
507     if (iflags.toptenwin) {
508         toptenwin = create_nhwindow(NHW_TEXT);
509     }
510
511 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
512 #define HUP if (!program_state.done_hup)
513 #else
514 #define HUP
515 #endif
516
517 #ifdef TOS
518     restore_colors(); /* make sure the screen is black on white */
519 #endif
520     /* create a new 'topten' entry */
521     t0_used = FALSE;
522     t0 = newttentry();
523     t0->ver_major = VERSION_MAJOR;
524     t0->ver_minor = VERSION_MINOR;
525     t0->patchlevel = PATCHLEVEL;
526     t0->points = u.urexp;
527     t0->deathdnum = u.uz.dnum;
528     /* deepest_lev_reached() is in terms of depth(), and reporting the
529      * deepest level reached in the dungeon death occurred in doesn't
530      * seem right, so we have to report the death level in depth() terms
531      * as well (which also seems reasonable since that's all the player
532      * sees on the screen anyway)
533      */
534     t0->deathlev = observable_depth(&u.uz);
535     t0->maxlvl = deepest_lev_reached(TRUE);
536     t0->hp = u.uhp;
537     t0->maxhp = u.uhpmax;
538     t0->deaths = u.umortality;
539     t0->uid = uid;
540     copynchars(t0->plrole, urole.filecode, ROLESZ);
541     copynchars(t0->plrace, urace.filecode, ROLESZ);
542     copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
543     copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
544     copynchars(t0->name, plname, NAMSZ);
545     formatkiller(t0->death, sizeof t0->death, how);
546     t0->birthdate = yyyymmdd(ubirthday);
547     t0->deathdate = yyyymmdd(when);
548     t0->tt_next = 0;
549 #ifdef UPDATE_RECORD_IN_PLACE
550     t0->fpos = -1L;
551 #endif
552
553 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
554     if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
555         if (!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) {
556             HUP raw_print("Cannot open log file!");
557         } else {
558             writeentry(lfile, t0);
559             (void) fclose(lfile);
560         }
561         unlock_file(LOGFILE);
562     }
563 #endif /* LOGFILE */
564 #ifdef XLOGFILE
565     if (lock_file(XLOGFILE, SCOREPREFIX, 10)) {
566         if (!(xlfile = fopen_datafile(XLOGFILE, "a", SCOREPREFIX))) {
567             HUP raw_print("Cannot open extended log file!");
568         } else {
569             writexlentry(xlfile, t0);
570             (void) fclose(xlfile);
571         }
572         unlock_file(XLOGFILE);
573     }
574 #endif /* XLOGFILE */
575
576     if (wizard || discover) {
577         if (how != PANICKED)
578             HUP {
579                 char pbuf[BUFSZ];
580                 topten_print("");
581                 Sprintf(pbuf,
582 #if 0 /*JP*/
583              "Since you were in %s mode, the score list will not be checked.",
584                         wizard ? "wizard" : "discover");
585 #else
586              "%s\83\82\81[\83h\82Å\83v\83\8c\83C\82µ\82½\82Ì\82Å\83X\83R\83A\83\8a\83X\83g\82É\82Í\8dÚ\82ç\82È\82¢\81D",
587                         wizard ? "\83E\83B\83U\81[\83h" : "\94­\8c©");
588 #endif
589                 topten_print(pbuf);
590             }
591         goto showwin;
592     }
593
594     if (!lock_file(RECORD, SCOREPREFIX, 60))
595         goto destroywin;
596
597 #ifdef UPDATE_RECORD_IN_PLACE
598     rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX);
599 #else
600     rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
601 #endif
602
603     if (!rfile) {
604         HUP raw_print("Cannot open record file!");
605         unlock_file(RECORD);
606         goto destroywin;
607     }
608
609     HUP topten_print("");
610
611     /* assure minimum number of points */
612     if (t0->points < sysopt.pointsmin)
613         t0->points = 0;
614
615     t1 = tt_head = newttentry();
616     tprev = 0;
617     /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
618     for (rank = 1;;) {
619         readentry(rfile, t1);
620         if (t1->points < sysopt.pointsmin)
621             t1->points = 0;
622         if (rank0 < 0 && t1->points < t0->points) {
623             rank0 = rank++;
624             if (tprev == 0)
625                 tt_head = t0;
626             else
627                 tprev->tt_next = t0;
628             t0->tt_next = t1;
629 #ifdef UPDATE_RECORD_IN_PLACE
630             t0->fpos = t1->fpos; /* insert here */
631 #endif
632             t0_used = TRUE;
633             occ_cnt--;
634             flg++; /* ask for a rewrite */
635         } else
636             tprev = t1;
637
638         if (t1->points == 0)
639             break;
640         if ((sysopt.pers_is_uid ? t1->uid == t0->uid
641                                 : strncmp(t1->name, t0->name, NAMSZ) == 0)
642             && !strncmp(t1->plrole, t0->plrole, ROLESZ) && --occ_cnt <= 0) {
643             if (rank0 < 0) {
644                 rank0 = 0;
645                 rank1 = rank;
646                 HUP {
647                     char pbuf[BUFSZ];
648
649                     Sprintf(pbuf,
650 /*JP
651                         "You didn't beat your previous score of %ld points.",
652 */
653                         "\82 \82È\82½\82Í\88È\91O\82Ì%ld\83|\83C\83\93\83g\82Ì\83X\83R\83A\82É\93Í\82©\82È\82©\82Á\82½\81D",
654                             t1->points);
655                     topten_print(pbuf);
656                     topten_print("");
657                 }
658             }
659             if (occ_cnt < 0) {
660                 flg++;
661                 continue;
662             }
663         }
664         if (rank <= sysopt.entrymax) {
665             t1->tt_next = newttentry();
666             t1 = t1->tt_next;
667             rank++;
668         }
669         if (rank > sysopt.entrymax) {
670             t1->points = 0;
671             break;
672         }
673     }
674     if (flg) { /* rewrite record file */
675 #ifdef UPDATE_RECORD_IN_PLACE
676         (void) fseek(rfile, (t0->fpos >= 0 ? t0->fpos : final_fpos),
677                      SEEK_SET);
678 #else
679         (void) fclose(rfile);
680         if (!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))) {
681             HUP raw_print("Cannot write record file");
682             unlock_file(RECORD);
683             free_ttlist(tt_head);
684             goto destroywin;
685         }
686 #endif /* UPDATE_RECORD_IN_PLACE */
687         if (!done_stopprint)
688             if (rank0 > 0) {
689                 if (rank0 <= 10) {
690 /*JP
691                     topten_print("You made the top ten list!");
692 */
693                     topten_print("\82 \82È\82½\82Í\83g\83b\83v10\83\8a\83X\83g\82É\8dÚ\82Á\82½\81I");
694                 } else {
695                     char pbuf[BUFSZ];
696
697 #if 0 /*JP*/
698                     Sprintf(pbuf,
699                             "You reached the %d%s place on the top %d list.",
700                             rank0, ordin(rank0), sysopt.entrymax);
701 #else
702                     Sprintf(pbuf,
703                             "\82 \82È\82½\82Í\81C\83g\83b\83v%d\83\8a\83X\83g\82Ì%d\88Ê\82É\8dÚ\82Á\82½\81D",
704                             sysopt.entrymax, rank0);
705 #endif
706                     topten_print(pbuf);
707                 }
708                 topten_print("");
709             }
710     }
711     if (rank0 == 0)
712         rank0 = rank1;
713     if (rank0 <= 0)
714         rank0 = rank;
715     if (!done_stopprint)
716         outheader();
717     t1 = tt_head;
718     for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
719         if (flg
720 #ifdef UPDATE_RECORD_IN_PLACE
721             && rank >= rank0
722 #endif
723             )
724             writeentry(rfile, t1);
725         if (done_stopprint)
726             continue;
727         if (rank > flags.end_top && (rank < rank0 - flags.end_around
728                                      || rank > rank0 + flags.end_around)
729             && (!flags.end_own
730                 || (sysopt.pers_is_uid
731                         ? t1->uid == t0->uid
732                         : strncmp(t1->name, t0->name, NAMSZ) == 0)))
733             continue;
734         if (rank == rank0 - flags.end_around
735             && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own)
736             topten_print("");
737         if (rank != rank0)
738             outentry(rank, t1, FALSE);
739         else if (!rank1)
740             outentry(rank, t1, TRUE);
741         else {
742             outentry(rank, t1, TRUE);
743             outentry(0, t0, TRUE);
744         }
745     }
746     if (rank0 >= rank)
747         if (!done_stopprint)
748             outentry(0, t0, TRUE);
749 #ifdef UPDATE_RECORD_IN_PLACE
750     if (flg) {
751 #ifdef TRUNCATE_FILE
752         /* if a reasonable way to truncate a file exists, use it */
753         truncate_file(rfile);
754 #else
755         /* use sentinel record rather than relying on truncation */
756         t1->points = 0L; /* terminates file when read back in */
757         t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
758         t1->uid = t1->deathdnum = t1->deathlev = 0;
759         t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
760         t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
761         t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
762         t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L);
763         Strcpy(t1->name, "@");
764         Strcpy(t1->death, "<eod>\n");
765         writeentry(rfile, t1);
766         (void) fflush(rfile);
767 #endif /* TRUNCATE_FILE */
768     }
769 #endif /* UPDATE_RECORD_IN_PLACE */
770     (void) fclose(rfile);
771     unlock_file(RECORD);
772     free_ttlist(tt_head);
773
774 showwin:
775     if (iflags.toptenwin && !done_stopprint)
776         display_nhwindow(toptenwin, 1);
777 destroywin:
778     if (!t0_used)
779         dealloc_ttentry(t0);
780     if (iflags.toptenwin) {
781         destroy_nhwindow(toptenwin);
782         toptenwin = WIN_ERR;
783     }
784 }
785
786 STATIC_OVL void
787 outheader()
788 {
789     char linebuf[BUFSZ];
790     register char *bp;
791
792     Strcpy(linebuf, " No  Points     Name");
793     bp = eos(linebuf);
794     while (bp < linebuf + COLNO - 9)
795         *bp++ = ' ';
796     Strcpy(bp, "Hp [max]");
797     topten_print(linebuf);
798 }
799
800 /* so>0: standout line; so=0: ordinary line */
801 STATIC_OVL void
802 outentry(rank, t1, so)
803 struct toptenentry *t1;
804 int rank;
805 boolean so;
806 {
807     boolean second_line = TRUE;
808     char linebuf[BUFSZ];
809     char *bp, hpbuf[24], linebuf3[BUFSZ];
810     int hppos, lngr;
811 #if 1 /*JP*/
812     char who[BUFSZ];
813     char where[BUFSZ];
814     char action[BUFSZ];
815     char car[BUFSZ];
816     char cdr[BUFSZ];
817     const char *jdeath;
818 #endif
819
820 #if 1 /*JP*/
821     who[0] = '\0';
822     where[0] = '\0';
823     action[0] = '\0';
824 #endif
825     linebuf[0] = '\0';
826     if (rank)
827         Sprintf(eos(linebuf), "%3d", rank);
828     else
829         Strcat(linebuf, "   ");
830
831     Sprintf(eos(linebuf), " %10ld  %.10s", t1->points ? t1->points : u.urexp,
832             t1->name);
833     Sprintf(eos(linebuf), "-%s", t1->plrole);
834     if (t1->plrace[0] != '?')
835         Sprintf(eos(linebuf), "-%s", t1->plrace);
836     /* Printing of gender and alignment is intentional.  It has been
837      * part of the NetHack Geek Code, and illustrates a proper way to
838      * specify a character from the command line.
839      */
840     Sprintf(eos(linebuf), "-%s", t1->plgend);
841     if (t1->plalign[0] != '?')
842 /*JP
843         Sprintf(eos(linebuf), "-%s ", t1->plalign);
844 */
845         Sprintf(eos(linebuf), "-%s", t1->plalign);
846     else
847 /*JP
848         Strcat(linebuf, " ");
849 */
850         Strcat(linebuf, "");
851 #if 1 /*JP*/
852         Strcat(linebuf, "\82Í");
853 /*JP: \93ú\96{\8cê\82Å\82Í\81u\81\9b\81\9b\82ð\8eè\82É\81v\82ð\90æ\82É\92Ç\89Á\82µ\82È\82¢\82Æ\95s\8e©\91R */
854         jdeath = t1->death;
855         if (!strncmp(jdeath, "\96\82\8f\9c\82¯\82ð\8eè\82É", 12))
856             jdeath += 12;
857         else if (!strncmp(jdeath, "\93V\8fã\82Å\92p\90J\82ð\8eó\82¯", 16))
858             jdeath += 16;
859         else if (!strncmp(jdeath, "\8bU\95¨\82Ì\96\82\8f\9c\82¯\82ð\92Í\82Ü\82³\82ê", 24))
860             jdeath += 24;
861 #endif
862 #if 0 /*JP*/
863     if (!strncmp("escaped", t1->death, 7)) {
864 #else
865     if (!strncmp("\92E\8fo\82µ\82½", jdeath, 8)
866         || !strncmp("escaped", jdeath, 7)) {
867 #endif
868 #if 0 /*JP*/
869         Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
870                 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
871                 t1->maxlvl);
872         /* fixup for closing paren in "escaped... with...Amulet)[max..." */
873         if ((bp = index(linebuf, ')')) != 0)
874             *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
875 #else
876         char jbuf[BUFSZ];
877         strncpy(jbuf, t1->death, jdeath - t1->death);
878         jbuf[jdeath - t1->death] = '\0';
879         Sprintf(action, "%s\96À\8b{\82©\82ç\92E\8fo\82µ\82½[\8dÅ\91å\92n\89º%d\8aK]",
880                 jbuf, t1->maxlvl);
881 #endif
882         second_line = FALSE;
883 #if 0 /*JP*/
884     } else if (!strncmp("ascended", t1->death, 8)) {
885 #else
886     } else if (!strncmp("\8f¸\93V\82µ\82½", jdeath, 8)
887                || !strncmp("ascended", jdeath, 8)) {
888 #endif
889 #if 0 /*JP:T*/
890         Sprintf(eos(linebuf), "ascended to demigod%s-hood",
891                 (t1->plgend[0] == 'F') ? "dess" : "");
892 #else
893         Sprintf(action, "\8f¸\93V\82µ%s\90_\82Æ\82È\82Á\82½",
894                 (t1->plgend[0] == 'F') ? "\8f\97" : "");
895 #endif
896         second_line = FALSE;
897     } else {
898 /*JP
899         if (!strncmp(t1->death, "quit", 4)) {
900 */
901         if (!strncmp(jdeath, "\94²\82¯\82½", 4)) {
902 #if 0 /*JP*/
903             Strcat(linebuf, "quit");
904 #else
905             Strcat(action, t1->death);
906 #endif
907             second_line = FALSE;
908 #if 1 /*JP*/
909         }
910 #else
911         } else if (!strncmp(t1->death, "died of st", 10)) {
912             Strcat(linebuf, "starved to death");
913             second_line = FALSE;
914         } else if (!strncmp(t1->death, "choked", 6)) {
915             Sprintf(eos(linebuf), "choked on h%s food",
916                     (t1->plgend[0] == 'F') ? "er" : "is");
917         } else if (!strncmp(t1->death, "poisoned", 8)) {
918             Strcat(linebuf, "was poisoned");
919         } else if (!strncmp(t1->death, "crushed", 7)) {
920             Strcat(linebuf, "was crushed to death");
921         } else if (!strncmp(t1->death, "petrified by ", 13)) {
922             Strcat(linebuf, "turned to stone");
923         } else
924             Strcat(linebuf, "died");
925 #endif /*JP*/
926
927         if (t1->deathdnum == astral_level.dnum) {
928             const char *arg, *fmt = " on the Plane of %s";
929
930             switch (t1->deathlev) {
931             case -5:
932 #if 0 /*JP*/
933                 fmt = " on the %s Plane";
934 #endif
935 /*JP
936                 arg = "Astral";
937 */
938                 arg = "\93V\8fã\8aE";
939                 break;
940             case -4:
941 /*JP
942                 arg = "Water";
943 */
944                 arg = "\90\85\82Ì\90¸\97ì\8aE";
945                 break;
946             case -3:
947 /*JP
948                 arg = "Fire";
949 */
950                 arg = "\89Î\82Ì\90¸\97ì\8aE";
951                 break;
952             case -2:
953 /*JP
954                 arg = "Air";
955 */
956                 arg = "\95\97\82Ì\90¸\97ì\8aE";
957                 break;
958             case -1:
959 /*JP
960                 arg = "Earth";
961 */
962                 arg = "\92n\82Ì\90¸\97ì\8aE";
963                 break;
964             default:
965                 arg = "Void";
966                 break;
967             }
968 #if 0 /*JP*/
969             Sprintf(eos(linebuf), fmt, arg);
970 #else
971             Sprintf(where, "%s\82É\82Ä", arg);
972 #endif
973         } else {
974 /*JP
975             Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
976 */
977             Sprintf(eos(linebuf), "%s", dungeons[t1->deathdnum].dname);
978             if (t1->deathdnum != knox_level.dnum)
979 /*JP
980                 Sprintf(eos(linebuf), " on level %d", t1->deathlev);
981 */
982                 Sprintf(eos(linebuf), "\82Ì\92n\89º%d\8aK\82É\82Ä", t1->deathlev);
983             if (t1->deathlev != t1->maxlvl)
984 /*JP
985                 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
986 */
987                 Sprintf(eos(where), "[\8dÅ\91å\92n\89º%d\8aK]", t1->maxlvl);
988         }
989
990         /* kludge for "quit while already on Charon's boat" */
991         if (!strncmp(t1->death, "quit ", 5))
992             Strcat(linebuf, t1->death + 4);
993     }
994 #if 0 /*JP*/
995     Strcat(linebuf, ".");
996 #endif
997
998     /* Quit, starved, ascended, and escaped contain no second line */
999     if (second_line)
1000 /*JP
1001         Sprintf(eos(linebuf), "  %c%s.", highc(*(t1->death)), t1->death + 1);
1002 */
1003         Sprintf(action, "%s", t1->death);
1004
1005 #if 1 /*JP*/
1006     Sprintf(eos(linebuf), "%s%s%s\81D", who, where, action);
1007 #endif
1008     lngr = (int) strlen(linebuf);
1009     if (t1->hp <= 0)
1010         hpbuf[0] = '-', hpbuf[1] = '\0';
1011     else
1012         Sprintf(hpbuf, "%d", t1->hp);
1013     /* beginning of hp column after padding (not actually padded yet) */
1014     hppos = COLNO - (sizeof("  Hp [max]") - 1); /* sizeof(str) includes \0 */
1015 #if 1 /*JP*/
1016     while(lngr >= hppos ){
1017 /*JP hppos\82æ\82è\91O\82Ì\93K\93\96\82È\88Ê\92u\82Å\95ª\8a\84\82·\82é\81D*/
1018         car[0] = '\0';
1019         cdr[0] = '\0';
1020         split_japanese(linebuf, car, cdr, hppos);
1021         
1022         bp = eos(car);
1023         if (so) {
1024             while (bp < car + (COLNO-1)) *bp++ = ' ';
1025             *bp = 0;
1026             topten_print_bold(car);
1027         } else
1028             topten_print(car);
1029         
1030             Sprintf(linebuf, "%15s %s", "", cdr);
1031         lngr = (int)strlen(linebuf);
1032     }
1033 /*JP: \93ú\96{\8cê\82ª\93ü\82é\82Æ\95\8e\9a\97ñ\82ð\8cã\82©\82ç\8c©\82Ä\82¢\82­\82±\82Æ\82Í\82Å\82«\82È\82¢\82½\82ß\83R\83\81\83\93\83g\83A\83E\83g*/
1034 #else
1035     while (lngr >= hppos) {
1036         for (bp = eos(linebuf); !(*bp == ' ' && (bp - linebuf < hppos)); bp--)
1037             ;
1038         /* special case: word is too long, wrap in the middle */
1039         if (linebuf + 15 >= bp)
1040             bp = linebuf + hppos - 1;
1041         /* special case: if about to wrap in the middle of maximum
1042            dungeon depth reached, wrap in front of it instead */
1043         if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5))
1044             bp -= 5;
1045         if (*bp != ' ')
1046             Strcpy(linebuf3, bp);
1047         else
1048             Strcpy(linebuf3, bp + 1);
1049         *bp = 0;
1050         if (so) {
1051             while (bp < linebuf + (COLNO - 1))
1052                 *bp++ = ' ';
1053             *bp = 0;
1054             topten_print_bold(linebuf);
1055         } else
1056             topten_print(linebuf);
1057         Sprintf(linebuf, "%15s %s", "", linebuf3);
1058         lngr = strlen(linebuf);
1059     }
1060 #endif /*JP*/
1061     /* beginning of hp column not including padding */
1062     hppos = COLNO - 7 - (int) strlen(hpbuf);
1063     bp = eos(linebuf);
1064
1065     if (bp <= linebuf + hppos) {
1066         /* pad any necessary blanks to the hit point entry */
1067         while (bp < linebuf + hppos)
1068             *bp++ = ' ';
1069         Strcpy(bp, hpbuf);
1070         Sprintf(eos(bp), " %s[%d]",
1071                 (t1->maxhp < 10) ? "  " : (t1->maxhp < 100) ? " " : "",
1072                 t1->maxhp);
1073     }
1074
1075     if (so) {
1076         bp = eos(linebuf);
1077         if (so >= COLNO)
1078             so = COLNO - 1;
1079         while (bp < linebuf + so)
1080             *bp++ = ' ';
1081         *bp = 0;
1082         topten_print_bold(linebuf);
1083     } else
1084         topten_print(linebuf);
1085 }
1086
1087 STATIC_OVL int
1088 score_wanted(current_ver, rank, t1, playerct, players, uid)
1089 boolean current_ver;
1090 int rank;
1091 struct toptenentry *t1;
1092 int playerct;
1093 const char **players;
1094 int uid;
1095 {
1096     int i;
1097
1098     if (current_ver
1099         && (t1->ver_major != VERSION_MAJOR || t1->ver_minor != VERSION_MINOR
1100             || t1->patchlevel != PATCHLEVEL))
1101         return 0;
1102
1103     if (sysopt.pers_is_uid && !playerct && t1->uid == uid)
1104         return 1;
1105
1106     for (i = 0; i < playerct; i++) {
1107         if (players[i][0] == '-' && index("pr", players[i][1])
1108             && players[i][2] == 0 && i + 1 < playerct) {
1109             const char *arg = players[i + 1];
1110             if ((players[i][1] == 'p'
1111                  && str2role(arg) == str2role(t1->plrole))
1112                 || (players[i][1] == 'r'
1113                     && str2race(arg) == str2race(t1->plrace)))
1114                 return 1;
1115             i++;
1116         } else if (strcmp(players[i], "all") == 0
1117                    || strncmp(t1->name, players[i], NAMSZ) == 0
1118                    || (players[i][0] == '-' && players[i][1] == t1->plrole[0]
1119                        && players[i][2] == 0)
1120                    || (digit(players[i][0]) && rank <= atoi(players[i])))
1121             return 1;
1122     }
1123     return 0;
1124 }
1125
1126 /*
1127  * print selected parts of score list.
1128  * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1129  * and argv[1] starting with "-s".
1130  */
1131 void
1132 prscore(argc, argv)
1133 int argc;
1134 char **argv;
1135 {
1136     const char **players;
1137     int playerct, rank;
1138     boolean current_ver = TRUE, init_done = FALSE;
1139     register struct toptenentry *t1;
1140     FILE *rfile;
1141     boolean match_found = FALSE;
1142     register int i;
1143     char pbuf[BUFSZ];
1144     int uid = -1;
1145     const char *player0;
1146
1147     if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1148         raw_printf("prscore: bad arguments (%d)", argc);
1149         return;
1150     }
1151
1152     rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1153     if (!rfile) {
1154         raw_print("Cannot open record file!");
1155         return;
1156     }
1157
1158 #ifdef AMIGA
1159     {
1160         extern winid amii_rawprwin;
1161
1162         init_nhwindows(&argc, argv);
1163         amii_rawprwin = create_nhwindow(NHW_TEXT);
1164     }
1165 #endif
1166
1167     /* If the score list isn't after a game, we never went through
1168      * initialization. */
1169     if (wiz1_level.dlevel == 0) {
1170         dlb_init();
1171         init_dungeons();
1172         init_done = TRUE;
1173     }
1174
1175     if (!argv[1][2]) { /* plain "-s" */
1176         argc--;
1177         argv++;
1178     } else
1179         argv[1] += 2;
1180
1181     if (argc > 1 && !strcmp(argv[1], "-v")) {
1182         current_ver = FALSE;
1183         argc--;
1184         argv++;
1185     }
1186
1187     if (argc <= 1) {
1188         if (sysopt.pers_is_uid) {
1189             uid = getuid();
1190             playerct = 0;
1191             players = (const char **) 0;
1192         } else {
1193             player0 = plname;
1194             if (!*player0)
1195 #ifdef AMIGA
1196                 player0 = "all"; /* single user system */
1197 #else
1198                 player0 = "hackplayer";
1199 #endif
1200             playerct = 1;
1201             players = &player0;
1202         }
1203     } else {
1204         playerct = --argc;
1205         players = (const char **) ++argv;
1206     }
1207     raw_print("");
1208
1209     t1 = tt_head = newttentry();
1210     for (rank = 1;; rank++) {
1211         readentry(rfile, t1);
1212         if (t1->points == 0)
1213             break;
1214         if (!match_found
1215             && score_wanted(current_ver, rank, t1, playerct, players, uid))
1216             match_found = TRUE;
1217         t1->tt_next = newttentry();
1218         t1 = t1->tt_next;
1219     }
1220
1221     (void) fclose(rfile);
1222     if (init_done) {
1223         free_dungeons();
1224         dlb_cleanup();
1225     }
1226
1227     if (match_found) {
1228         outheader();
1229         t1 = tt_head;
1230         for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1231             if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1232                 (void) outentry(rank, t1, FALSE);
1233         }
1234     } else {
1235         Sprintf(pbuf, "Cannot find any %sentries for ",
1236                 current_ver ? "current " : "");
1237         if (playerct < 1)
1238             Strcat(pbuf, "you.");
1239         else {
1240             if (playerct > 1)
1241                 Strcat(pbuf, "any of ");
1242             for (i = 0; i < playerct; i++) {
1243                 /* stop printing players if there are too many to fit */
1244                 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1245                     if (strlen(pbuf) < BUFSZ - 4)
1246                         Strcat(pbuf, "...");
1247                     else
1248                         Strcpy(pbuf + strlen(pbuf) - 4, "...");
1249                     break;
1250                 }
1251                 Strcat(pbuf, players[i]);
1252                 if (i < playerct - 1) {
1253                     if (players[i][0] == '-' && index("pr", players[i][1])
1254                         && players[i][2] == 0)
1255                         Strcat(pbuf, " ");
1256                     else
1257                         Strcat(pbuf, ":");
1258                 }
1259             }
1260         }
1261         raw_print(pbuf);
1262         raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1263
1264                    hname);
1265         raw_printf("Player types are: [-p role] [-r race]");
1266     }
1267     free_ttlist(tt_head);
1268 #ifdef AMIGA
1269     {
1270         extern winid amii_rawprwin;
1271
1272         display_nhwindow(amii_rawprwin, 1);
1273         destroy_nhwindow(amii_rawprwin);
1274         amii_rawprwin = WIN_ERR;
1275     }
1276 #endif
1277 }
1278
1279 STATIC_OVL int
1280 classmon(plch, fem)
1281 char *plch;
1282 boolean fem;
1283 {
1284     int i;
1285
1286     /* Look for this role in the role table */
1287     for (i = 0; roles[i].name.m; i++)
1288         if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1289             if (fem && roles[i].femalenum != NON_PM)
1290                 return roles[i].femalenum;
1291             else if (roles[i].malenum != NON_PM)
1292                 return roles[i].malenum;
1293             else
1294                 return PM_HUMAN;
1295         }
1296     /* this might be from a 3.2.x score for former Elf class */
1297     if (!strcmp(plch, "E"))
1298         return PM_RANGER;
1299
1300     impossible("What weird role is this? (%s)", plch);
1301     return  PM_HUMAN_MUMMY;
1302 }
1303
1304 /*
1305  * Get a random player name and class from the high score list,
1306  * and attach them to an object (for statues or morgue corpses).
1307  */
1308 struct obj *
1309 tt_oname(otmp)
1310 struct obj *otmp;
1311 {
1312     int rank;
1313     register int i;
1314     register struct toptenentry *tt;
1315     FILE *rfile;
1316     struct toptenentry tt_buf;
1317
1318     if (!otmp)
1319         return (struct obj *) 0;
1320
1321     rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1322     if (!rfile) {
1323         impossible("Cannot open record file!");
1324         return (struct obj *) 0;
1325     }
1326
1327     tt = &tt_buf;
1328     rank = rnd(sysopt.tt_oname_maxrank);
1329 pickentry:
1330     for (i = rank; i; i--) {
1331         readentry(rfile, tt);
1332         if (tt->points == 0)
1333             break;
1334     }
1335
1336     if (tt->points == 0) {
1337         if (rank > 1) {
1338             rank = 1;
1339             rewind(rfile);
1340             goto pickentry;
1341         }
1342         otmp = (struct obj *) 0;
1343     } else {
1344         set_corpsenm(otmp, classmon(tt->plrole, (tt->plgend[0] == 'F')));
1345         otmp = oname(otmp, tt->name);
1346     }
1347
1348     (void) fclose(rfile);
1349     return otmp;
1350 }
1351
1352 #ifdef NO_SCAN_BRACK
1353 /* Lattice scanf isn't up to reading the scorefile.  What */
1354 /* follows deals with that; I admit it's ugly. (KL) */
1355 /* Now generally available (KL) */
1356 STATIC_OVL void
1357 nsb_mung_line(p)
1358 char *p;
1359 {
1360     while ((p = index(p, ' ')) != 0)
1361         *p = '|';
1362 }
1363
1364 STATIC_OVL void
1365 nsb_unmung_line(p)
1366 char *p;
1367 {
1368     while ((p = index(p, '|')) != 0)
1369         *p = ' ';
1370 }
1371 #endif /* NO_SCAN_BRACK */
1372
1373 /*topten.c*/