OSDN Git Service

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