You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
930 lines
29 KiB
930 lines
29 KiB
5 years ago
|
/* List the Nethack record file and the average score.
|
||
|
Nethack 3.4-3.6, 3.3, 3.2 and 3.1 RECORD file formats supported
|
||
|
Output: stdout
|
||
|
|
||
|
RECLIST [-f filename] [-u playername] [-p profession] [-r race]
|
||
|
[-n number] [-ns score] [-np interval] [-d]
|
||
|
[-ds date since] [-du date until]
|
||
|
[-g version]
|
||
|
|
||
|
Check out the comments in the #defines and #includes before compiling
|
||
|
*/
|
||
|
|
||
|
/* J. Lahtinen 8/1993 - 12/2015 internet: jslahtin@gmail.com
|
||
|
Some parts of this program are copied from the Nethack 3.3.0 sources,
|
||
|
the rest I wrote myself.
|
||
|
Tell me if you find any bugs.. including the program telling the death
|
||
|
reason differently from "nethack -s".
|
||
|
When reporting bugs, please include the line in your record file that
|
||
|
reclist doesn't treat properly (copy & paste is just fine).
|
||
|
|
||
|
2015-12-12 - confirmed compatibility to Nethack 3.6.0
|
||
|
- removed the warning of "too" modern game version
|
||
|
2015-02-21 confirmed the compatibility to the leaked Nethack 3.5.0 version
|
||
|
2003-01-20 changed the parameters -ds and -du from long to char* to allow
|
||
|
comparisons of partial date (for example -ds 2003 to list games
|
||
|
finished after the beginning of year 2003)
|
||
|
2002-03-29 v3.4.0
|
||
|
2001-04-07 increased the size of the line buffer to fit Lethe patch scores
|
||
|
2000-09-06 removed the level number from escaping scores
|
||
|
2000-08-21 removed the level number from ascensions
|
||
|
anything in curly braces shown even if the "death" string isn't
|
||
|
(for the move counter added with my moves patch)
|
||
|
2000-08-12 v3.3.1
|
||
|
2000-07-30 added option -ns to show only scores above some limit
|
||
|
2000-01-23 added option -a to select by alignment
|
||
|
2000-01-02 v3.3
|
||
|
updated to understand the file format of game version 3.3.0
|
||
|
removed old compile options NHPLUS and ENDGAME
|
||
|
980731 v3.2.2
|
||
|
removed false append of "with the Amulet" on escaped games
|
||
|
980108 options -ds and -du to delimit game ending dates
|
||
|
971130 v3.2.1
|
||
|
changed the endgame level names to those used by the game
|
||
|
version 3.2.2
|
||
|
970525 don't split lines between [] (the game version 3.2 itself doesn't).
|
||
|
give an error message from parameter -f without a filename..
|
||
|
961124 v3.2, fixed the endgame level indexes changed from game ver.3.1
|
||
|
960628 option -g to select scores of a certain game version
|
||
|
960505 Both 3.1 and 3.2 game versions handled
|
||
|
960421 v3.0: File format changed for game version 3.2
|
||
|
950524 don't print "in the endgame" when the exact end level is shown
|
||
|
I noticed the NhPlus class U missing from the helps, added it there..
|
||
|
950523 2.3
|
||
|
fixed a difference between this and nethack -s in splitting the line
|
||
|
when the death string is 55 characters
|
||
|
made the more accurate endgame level names optional (#define ENDGAME)
|
||
|
950521 alternative definition of getch for unix users
|
||
|
950228 2.2c
|
||
|
"Escaped with the Amulet" added
|
||
|
950117 a negative number in -npX stops display only at the end
|
||
|
-npX always stops at the end
|
||
|
941117 option -npX pauses after about every X lines
|
||
|
940601 -l option reads the logfile.
|
||
|
Option -help works like -?
|
||
|
940517 added the missing "." in the ascensions
|
||
|
940509 "Crushed" and "escaped" ends specified
|
||
|
940406 Make the difference between demigod and demigoddess
|
||
|
940322 2.2a
|
||
|
Endgame- levels specified
|
||
|
940224 the parameter -d displays the date also with the scores on a single
|
||
|
display line
|
||
|
940110 2.2
|
||
|
the parameter -fs may be used to read stdin
|
||
|
931203 2.1b
|
||
|
conio.h headers removed for better operating system compatibility
|
||
|
extra space on "quit" scores removed
|
||
|
uid longer than 1 character caused some errors in unix, fixed
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <ctype.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#if defined(__MSDOS__)||defined(__OS2__)
|
||
|
#include <conio.h>
|
||
|
#else
|
||
|
#define getch() getc(stdin)
|
||
|
/* getch() reads one character without waiting for CR in MSDOS and OS/2
|
||
|
and it's currently the only thing in conio.h I use */
|
||
|
#endif
|
||
|
|
||
|
/* The functions strnicmp and stricmp are not ANSI, according to the BorlandC
|
||
|
help files. If your compiler doesn't understand them, you can replace them
|
||
|
with the standard functions (and lose the case insensitivity..) with these
|
||
|
following defines (or use suitable case-insensitive functions that your
|
||
|
compiler understands):
|
||
|
|
||
|
#define strnicmp strncmp
|
||
|
#define stricmp strcmp
|
||
|
*/
|
||
|
/* NOTE: With unix you may prefer defining them as strncasecmp and strcasecmp
|
||
|
as below..
|
||
|
*/
|
||
|
|
||
|
#if defined(__unix__)
|
||
|
#define RECORD "/usr/games/lib/nethackdir/record"
|
||
|
#define LOGFILE "/usr/games/lib/nethackdir/logfile"
|
||
|
#ifndef stricmp
|
||
|
#define strnicmp strncasecmp
|
||
|
#define stricmp strcasecmp
|
||
|
#endif
|
||
|
#else
|
||
|
#define RECORD "record"
|
||
|
#define LOGFILE "logfile"
|
||
|
#endif
|
||
|
|
||
|
/* Some defines taken from game sources (src/topten.c) */
|
||
|
#define NAMSZ 10 /* max length of player name */
|
||
|
#define DTHSZ 100 /* max length of "killer" string */
|
||
|
#define ROLESZ 3 /* role length */
|
||
|
#define BUFSZ 256 /* text buffer size */
|
||
|
|
||
|
char *eos(char *p);
|
||
|
|
||
|
char *ver="3.6.0"; /* version number */
|
||
|
|
||
|
/* command line parameters */
|
||
|
int pause=0, /* pause interval */
|
||
|
plin=0, /* lines printed without pause */
|
||
|
pnum=0, /* how many scores to show */
|
||
|
day=0; /* !0 : show dying dates */
|
||
|
long low=0; /* minimum score to show */
|
||
|
char *begdate=NULL, /* date boundaries */
|
||
|
*enddate=NULL,
|
||
|
*name=NULL, /* if only one username is to be listed */
|
||
|
*prole=NULL, /* character class */
|
||
|
*prace=NULL,
|
||
|
*pver=NULL, /* if only scores of a certain version are to be listed */
|
||
|
*pgend=NULL, /* list only this gender */
|
||
|
*palign=NULL; /* only this alignment */
|
||
|
|
||
|
/* other globals used everywhere */
|
||
|
int num=0; /* number of scores listed */
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
FILE *inp;
|
||
|
|
||
|
int i;
|
||
|
long sum=0,
|
||
|
score=0;
|
||
|
|
||
|
char line[BUFSZ],
|
||
|
fver[6];
|
||
|
char *filename=RECORD;
|
||
|
|
||
|
long chkline(char *line);
|
||
|
void help(char *prog,char *par,char *explain);
|
||
|
|
||
|
/* Command line parameters */
|
||
|
for (i=1; i<argc; i++) {
|
||
|
if (argv[i][0]=='/')
|
||
|
argv[i][0]='-';
|
||
|
if ((argv[i][0]=='?') || (argv[i][1]=='?') ||
|
||
|
(!stricmp(argv[i],"-help")) || (!stricmp(argv[i],"--help")))
|
||
|
help(argv[0],NULL,NULL);
|
||
|
else if (!(strnicmp(argv[i],"-f",2))) { /* filename */
|
||
|
if (strlen(argv[i])==2) {
|
||
|
i++;
|
||
|
if (i==argc) {
|
||
|
printf("The -f parameter requires a filename.\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
filename=argv[i]; }
|
||
|
else
|
||
|
filename=argv[i]+2;
|
||
|
if (!stricmp(filename,"s"))
|
||
|
filename=NULL; /* stdio */
|
||
|
}
|
||
|
else if (!(stricmp(argv[i],"-l"))) /* logfile */
|
||
|
filename=LOGFILE;
|
||
|
else if (!(strnicmp(argv[i],"-u",2))) /* player name */
|
||
|
if (strlen(argv[i])==2) {
|
||
|
i++;
|
||
|
name=argv[i]; }
|
||
|
else
|
||
|
name=argv[i]+2;
|
||
|
else if (!(strnicmp(argv[i],"-g",2))) { /* game version */
|
||
|
if (strlen(argv[i])==2) {
|
||
|
i++;
|
||
|
pver=argv[i]; }
|
||
|
else
|
||
|
pver=argv[i]+2;
|
||
|
if (!isdigit(pver[0]) || pver[0]>'3') {
|
||
|
sprintf(line,"-g %s",pver);
|
||
|
help(argv[0],line,"Versions later than 3.x.x not supported yet");
|
||
|
}
|
||
|
}
|
||
|
else if (!(strnicmp(argv[i],"-np",3))) /* pause interval, MUST be */
|
||
|
if (strlen(argv[i])==3) { /* checked before -n */
|
||
|
i++;
|
||
|
pause=atoi(argv[i]); }
|
||
|
else
|
||
|
pause=atoi(argv[i]+3);
|
||
|
else if (!(strnicmp(argv[i],"-ns",3))) /* minimum score to list */
|
||
|
if (strlen(argv[i])==3) { /* MUST be checked before -n */
|
||
|
i++;
|
||
|
low=atol(argv[i]); }
|
||
|
else
|
||
|
low=atol(argv[i]+3);
|
||
|
else if (!(strnicmp(argv[i],"-n",2))) /* number */
|
||
|
if (strlen(argv[i])==2) {
|
||
|
i++;
|
||
|
pnum=atoi(argv[i]); }
|
||
|
else
|
||
|
pnum=atoi(argv[i]+2);
|
||
|
else if (!(strnicmp(argv[i],"-ds",3))) /* dates since.. */
|
||
|
if (strlen(argv[i])==3) { /* MUST be checked before -d */
|
||
|
i++;
|
||
|
begdate=argv[i]; }
|
||
|
else
|
||
|
begdate=argv[i]+3;
|
||
|
else if (!(strnicmp(argv[i],"-du",3))) /* dates until.. */
|
||
|
if (strlen(argv[i])==3) { /* MUST be checked before -d */
|
||
|
i++;
|
||
|
enddate=argv[i]; }
|
||
|
else
|
||
|
enddate=argv[i]+3;
|
||
|
else if (argv[i][1]=='d') /* display the dates */
|
||
|
day=1;
|
||
|
else if (!(strnicmp(argv[i],"-p",2))) /* role */
|
||
|
if (strlen(argv[i])==2) {
|
||
|
i++;
|
||
|
prole=argv[i]; }
|
||
|
else
|
||
|
prole=argv[i]+2;
|
||
|
else if (!(strnicmp(argv[i],"-r",2))) { /* race */
|
||
|
if (strlen(argv[i])==2) {
|
||
|
i++;
|
||
|
prace=argv[i]; }
|
||
|
else
|
||
|
prace=argv[i]+2;
|
||
|
}
|
||
|
else if (!(strnicmp(argv[i],"-a",2))) { /* alignment */
|
||
|
if (strlen(argv[i])==2) {
|
||
|
i++;
|
||
|
palign=argv[i]; }
|
||
|
else
|
||
|
palign=argv[i]+2;
|
||
|
if (strlen(palign)>3) {
|
||
|
sprintf(line,"-a %s",palign);
|
||
|
help(argv[0],line,"Alignment should be one of law/neu/cha/l/n/c");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Command line ok, let's process the file */
|
||
|
|
||
|
if (!filename)
|
||
|
inp=stdin; /* I don't know if this works on all compilers.. */
|
||
|
else
|
||
|
if (!(inp=fopen(filename,"r"))) {
|
||
|
printf ("File %s not found\n",filename);
|
||
|
exit (2);
|
||
|
}
|
||
|
|
||
|
/* Actually I now think there's no point in this.
|
||
|
* You'll probably see if the output looks funny anyway,
|
||
|
* so you might consider the warning more annoying than helpful. 12.12.2015
|
||
|
fgets(line,sizeof(line),inp);
|
||
|
sscanf(line,"%5s",fver);
|
||
|
if (fver[1]=='.' && fver[3]=='.' && strncmp(fver,"3.6",3) > 0) {
|
||
|
printf ("The file seems to be from a game version newer than me.\n");
|
||
|
printf ("The output may be incorrect.\n\n");
|
||
|
} else if (fver[1] != '.') {
|
||
|
printf ("Not sure about which game version the file is from.\n");
|
||
|
printf ("Version 3.1 assumed for unknown lines.\n\n");
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
printf("%-71s%s"," No Points Name","Hp [max]\n");
|
||
|
/* HERE is the main loop */
|
||
|
while ((!(feof(inp))) && ((!pnum) || (num<pnum))) {
|
||
|
|
||
|
score = chkline(line);
|
||
|
if (pause>0 && plin >= pause) {
|
||
|
printf ("--more--\r");
|
||
|
if (tolower(getch()=='n'))
|
||
|
pnum = num;
|
||
|
printf (" \r");
|
||
|
plin = 0;
|
||
|
}
|
||
|
if (score == -9) {
|
||
|
printf ("\nInvalid file format!\n");
|
||
|
return 1;
|
||
|
}
|
||
|
sum += score;
|
||
|
fgets(line,sizeof(line),inp);
|
||
|
}
|
||
|
|
||
|
if (num)
|
||
|
printf("\n%d scores. Average score %ld\n",num,sum/(long)num);
|
||
|
else
|
||
|
printf("\nNo games found.\n");
|
||
|
if (inp!=stdin)
|
||
|
fclose (inp);
|
||
|
if (pause) getch();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Interpret one line of the file. Check for the game version. */
|
||
|
|
||
|
long chkline
|
||
|
(char *line)
|
||
|
{
|
||
|
long line33 (char *line,int rank);
|
||
|
long line32 (char *line);
|
||
|
long line31 (char *line);
|
||
|
|
||
|
int ver_major, ver_minor, patchlevel,
|
||
|
n;
|
||
|
static int rank=-1;
|
||
|
|
||
|
/* Which game version did this score come from? Are we interested in
|
||
|
scores of that version? */
|
||
|
n = sscanf (line, "%d.%d.%d", &ver_major, &ver_minor, &patchlevel);
|
||
|
if (!pver || !strncmp(pver,line,strlen(pver)) || !strncmp(pver,"3.1",3)) {
|
||
|
rank++;
|
||
|
if ((ver_major == 3 && ver_minor >= 3) || ver_major > 3)
|
||
|
return line33(line,rank);
|
||
|
else if (ver_major == 3 && ver_minor == 2)
|
||
|
return line32(line);
|
||
|
else
|
||
|
return line31(line);
|
||
|
}
|
||
|
else if (pver) /* We didn't care about that version */
|
||
|
return 0;
|
||
|
else
|
||
|
return -9; /* I don't understand this version. Newer than 3.3 */
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Interpret a line of game version 3.3 format */
|
||
|
long line33
|
||
|
(char *line, int rank)
|
||
|
{
|
||
|
#define ENDGAMBR 7
|
||
|
#define EGMIN -5 /* last endgame level */
|
||
|
#define EGMAX -1 /* first endgame level */
|
||
|
#define DUNMIN 0 /* minimum dungeonbranch number */
|
||
|
#define DUNMAX 7 /* maximum dungeonbranch number */
|
||
|
long score;
|
||
|
int dtype, dlev, maxlev, hp, maxhp;
|
||
|
char bdate[BUFSZ], /* 9 should be enough in VALID files, but we don't */
|
||
|
ddate[BUFSZ], /* want to crash on invalid entries either.. */
|
||
|
role[ROLESZ+1],
|
||
|
race[ROLESZ+1],
|
||
|
gender[ROLESZ+1],
|
||
|
align[ROLESZ+1],
|
||
|
plname[NAMSZ+1],
|
||
|
death[DTHSZ+1];
|
||
|
char linebuf[BUFSZ],
|
||
|
prtbuf[81],
|
||
|
hpbuf[6];
|
||
|
char *start, *end;
|
||
|
int n,
|
||
|
secline=1,
|
||
|
dayshown=0,
|
||
|
par=0,
|
||
|
hppos;
|
||
|
static char *eglev[] ={"Plane of Earth", /* -1 */
|
||
|
"Plane of Air", /* -2 */
|
||
|
"Plane of Fire", /* -3 */
|
||
|
"Plane of Water", /* -4 */
|
||
|
"Astral Plane"}; /* -5 */
|
||
|
static char *dungeon[] ={"The Dungeons of Doom", /* 0 */
|
||
|
"Gehennom", /* 1 */
|
||
|
"The Gnomish Mines", /* 2 */
|
||
|
"The Quest", /* 3 */
|
||
|
"Sokoban", /* 4 */
|
||
|
"Fort Ludios", /* 5 */
|
||
|
"Vlad's Tower", /* 6 */
|
||
|
"the endgame"}; /* 7 */
|
||
|
|
||
|
/* Fields in the line: version(3.3.n, ignored here), score, dungeon branch,
|
||
|
dlevel, max dlevel, hp, max hp, number of deaths (ignored), birthdate,
|
||
|
death date, uid (ignored), role, race, gender, alignment, name, reason */
|
||
|
n = sscanf(line,
|
||
|
"%*d.%*d.%*d %ld %d %d %d %d %d %*d %s %s %*d %s %s %s %s %[^,],%[^\n\r]",
|
||
|
&score,&dtype,&dlev,&maxlev,&hp,&maxhp,&ddate,&bdate,
|
||
|
role, race, gender, align, plname, death);
|
||
|
if (n<14) /* Invalid line, not all fields found */
|
||
|
return 0L;
|
||
|
|
||
|
/* Are we interested in this playername, role, alignment and deathdate? */
|
||
|
if (role[0]<'A')
|
||
|
return 0L;
|
||
|
if ((prole) && (strnicmp(prole,role,strlen(prole))))
|
||
|
return 0L;
|
||
|
if ((name) && (strcmp(name,plname)))
|
||
|
return 0L;
|
||
|
if ((prace) && (strnicmp(prace,race,strlen(prace))))
|
||
|
return 0L;
|
||
|
if ((palign) && (strnicmp(palign,align,strlen(palign))))
|
||
|
return 0L;
|
||
|
if ((begdate != NULL && strcmp(ddate,begdate) < 0) ||
|
||
|
(enddate != NULL && strcmp(ddate,enddate) > 0))
|
||
|
return 0L;
|
||
|
if (score<low)
|
||
|
return 0L;
|
||
|
|
||
|
sprintf(linebuf,"%3d %10ld %.10s-%s-%s-%s-%s ",
|
||
|
rank,score,plname,role,race,gender,align);
|
||
|
if (!strncmp(death,"escaped",7)) {
|
||
|
strcat(linebuf,"escaped the dungeon");
|
||
|
if (strstr(death,"Amulet"))
|
||
|
strcat(linebuf," with the Amulet");
|
||
|
if (dtype!=ENDGAMBR) /* not in the Endgame */
|
||
|
sprintf(eos(linebuf)," [max level %d]",maxlev);
|
||
|
secline=0;
|
||
|
}
|
||
|
else if (!strncmp(death,"ascended",8)) {
|
||
|
sprintf(eos(linebuf),"ascended to demigod%s-hood",
|
||
|
gender[0] == 'F'? "dess": "");
|
||
|
secline=0;
|
||
|
}
|
||
|
else if (!strncmp(death,"quit",4)) {
|
||
|
strcat(linebuf,"quit");
|
||
|
secline=0;
|
||
|
}
|
||
|
else if (!strncmp(death,"starv",5)) {
|
||
|
strcat(linebuf,"starved to death");
|
||
|
secline=0;
|
||
|
}
|
||
|
else if (!strncmp(death,"choked",6)) {
|
||
|
sprintf(eos(linebuf),"choked on h%s food",
|
||
|
gender[0] == 'F'? "er": "is");
|
||
|
secline=0;
|
||
|
}
|
||
|
else if (!strncmp(death,"poisoned",8))
|
||
|
strcat(linebuf,"was poisoned");
|
||
|
else if (!strncmp(death,"crushed",7))
|
||
|
strcat(linebuf,"was crushed to death");
|
||
|
else if (!strncmp(death,"petrified",9))
|
||
|
strcat(linebuf,"turned to stone");
|
||
|
else
|
||
|
strcat(linebuf,"died");
|
||
|
|
||
|
if (dtype == ENDGAMBR) { /* Endgame levels */
|
||
|
if (strncmp(death,"ascend",6)) { /* No level number for ascensions */
|
||
|
if (dlev >= EGMIN && dlev <= EGMAX) {
|
||
|
sprintf(eos(linebuf)," on the %s",eglev[-dlev - 1]);
|
||
|
} else {
|
||
|
strcat(linebuf," on the Plane of Void");
|
||
|
}
|
||
|
}
|
||
|
} else if (strncmp(death,"escape",6)) {
|
||
|
sprintf(eos(linebuf), " in %s on level %d",
|
||
|
(dtype >= DUNMIN && dtype < DUNMAX)? dungeon[dtype]:
|
||
|
"an unknown dungeon branch",
|
||
|
dlev);
|
||
|
if (dlev != maxlev)
|
||
|
sprintf(eos(linebuf), " [max %d]", maxlev);
|
||
|
}
|
||
|
|
||
|
strcat(linebuf,".");
|
||
|
|
||
|
if (secline)
|
||
|
sprintf(eos(linebuf)," %c%s.",toupper(*death),death+1);
|
||
|
else if (start=strchr(death,'{'))
|
||
|
/* Anyway, print what may be in curly braces. I have the game duration
|
||
|
* appended there with my topten patch
|
||
|
* (http://www.clinet.fi/~walker/nethack.html#moves) */
|
||
|
sprintf(eos(linebuf)," %s",start);
|
||
|
|
||
|
start=linebuf;
|
||
|
if (hp>0)
|
||
|
sprintf(hpbuf,"%d",hp);
|
||
|
else
|
||
|
sprintf(hpbuf,"-");
|
||
|
hppos=70;
|
||
|
while (strlen(start)>=hppos) { /* split the line as necessary */
|
||
|
|
||
|
for (end=start; end<start+hppos-1; end++) {
|
||
|
if (*end == '[')
|
||
|
par=1;
|
||
|
else if (*end == ']')
|
||
|
par=0;
|
||
|
}
|
||
|
while (end > start && (*end != ' ' || par)) {
|
||
|
if (*end == '[')
|
||
|
par=0;
|
||
|
else if (*end == ']')
|
||
|
par=1;
|
||
|
end--;
|
||
|
}
|
||
|
if (end == start) /* There was no good split position, shouldn't happen */
|
||
|
end=start+hppos;
|
||
|
|
||
|
strncpy(prtbuf, start, end-start);
|
||
|
prtbuf[end-start]=0;
|
||
|
printf("%s\n",prtbuf); /* This display line */
|
||
|
plin++;
|
||
|
|
||
|
if (day && !dayshown) {
|
||
|
printf(" %-15s", ddate);
|
||
|
dayshown=1;
|
||
|
} else
|
||
|
printf("%16s"," ");
|
||
|
start=end+1; /* Start position of the NEXT line */
|
||
|
hppos= 57 - strlen(hpbuf);
|
||
|
}
|
||
|
end=eos(start);
|
||
|
while (end < start+hppos && end < linebuf+sizeof(linebuf))
|
||
|
*end++=' ';
|
||
|
sprintf(end,hpbuf);
|
||
|
printf("%s",start);
|
||
|
sprintf(hpbuf,"[%d]",maxhp);
|
||
|
printf("%6s\n",hpbuf);
|
||
|
plin++;
|
||
|
if (day && !dayshown) {
|
||
|
printf(" %-15s\n",ddate);
|
||
|
plin++;
|
||
|
}
|
||
|
num++;
|
||
|
return score;
|
||
|
}
|
||
|
|
||
|
/* Interpret a line of game version 3.2 format */
|
||
|
long line32
|
||
|
(char *line)
|
||
|
{
|
||
|
int n,
|
||
|
pos,
|
||
|
pos2,
|
||
|
dtype,
|
||
|
dlev,
|
||
|
maxlev,
|
||
|
hp,
|
||
|
maxhp,
|
||
|
par=0,
|
||
|
dayshown=0;
|
||
|
static int nro=0;
|
||
|
long score;
|
||
|
char pvm1[BUFSZ],
|
||
|
pvm2[BUFSZ],
|
||
|
chclass,
|
||
|
sex,
|
||
|
sver[10],
|
||
|
death[150]={""},
|
||
|
ascend=0;
|
||
|
char *reason;
|
||
|
|
||
|
long line31(char *line);
|
||
|
|
||
|
static char *dungeon[]={"The Dungeons of Doom",/* 0 */
|
||
|
"Gehennom", /* 1 */
|
||
|
"The Gnomish Mines", /* 2 */
|
||
|
"The Quest", /* 3 */
|
||
|
"Fort Ludios", /* 4 */
|
||
|
"Vlad's Tower", /* 5 */
|
||
|
"the endgame"}; /* 6 */
|
||
|
|
||
|
static char *eglev[]={"Plane of Earth", /* -1 */
|
||
|
"Plane of Air", /* -2 */
|
||
|
"Plane of Fire", /* -3 */
|
||
|
"Plane of Water", /* -4 */
|
||
|
"Astral Plane"}; /* -5 */
|
||
|
nro++;
|
||
|
/* First there is the game version (3.2.something), then comes the actual
|
||
|
score. Then the dungeon branch of death and dlevel, maxdlevel, hp, maxhp,
|
||
|
number of deaths(ignored), dates when the game ended and started,
|
||
|
userid(ignored, character class&sex, name and reason for game ending
|
||
|
*/
|
||
|
|
||
|
n = sscanf (line,"%9s %ld %d %d %d %d %d %*d %s %s %*d %c%c",
|
||
|
&sver,&score,&dtype,&dlev,&maxlev,&hp,&maxhp,
|
||
|
&pvm1,&pvm2,&chclass,&sex);
|
||
|
|
||
|
if (chclass<'A')
|
||
|
return (0L); /* The VMS version may have end line with 'class' < A */
|
||
|
if ((prole) && (prole[0]!=chclass))
|
||
|
return (0L); /* Some other class specified on the command line */
|
||
|
if (score<low)
|
||
|
return 0L;
|
||
|
|
||
|
/* Find the player name. I didn't know how to use sscanf for it without
|
||
|
stopping in the comma after the name when I wrote this.. Anyway,
|
||
|
the name is after the character class and sex, which are the first
|
||
|
alphabets on the line and can never be before position 38.
|
||
|
*/
|
||
|
for (pos=38;!isalpha(line[pos]);pos++); /* pos points to class now */
|
||
|
if (pos>strlen(line)) /* Never is in a valid file. But we don't want */
|
||
|
return (0L); /* to crash just if the file is invalid, do we.. */
|
||
|
pos += 3;
|
||
|
for (pos2=0;(line[pos]!=',')&&(pos2<sizeof(death));pos2++,pos++)
|
||
|
death[pos2]=line[pos];
|
||
|
death[pos2]=0;
|
||
|
|
||
|
if ((name) && (strcmp(name,death)))
|
||
|
return (0L); /* We are only interested in somebody else's scores */
|
||
|
|
||
|
if ((begdate != NULL && strcmp(pvm1,begdate) < 0) ||
|
||
|
(enddate != NULL && strcmp(pvm1,enddate) > 0))
|
||
|
return (0L); /* We're not intrested in dates this early /late */
|
||
|
|
||
|
printf("%3d%11ld ",nro,score);
|
||
|
reason= &line[pos+1];
|
||
|
strcat(death,"-"); /* The name is already there in the beginnin of string */
|
||
|
pos=strlen(death);
|
||
|
death[pos]=chclass; death[pos+1]=0;
|
||
|
/* Here is the part where I'm not yet totally sure everything still works
|
||
|
as it should.. but this worked on the 3.1 files */
|
||
|
if (!(strncmp(reason,"poisoned",8)))
|
||
|
strcat(death," was poisoned");
|
||
|
else if (!(strncmp(reason,"petrified",9)))
|
||
|
strcat(death," turned to stone");
|
||
|
else if (!(strncmp(reason,"starv",5)))
|
||
|
strcat(death," starved to death");
|
||
|
else if (!(strncmp(reason,"quit",4)))
|
||
|
strcat(death," quit");
|
||
|
else if (!(strncmp(reason,"crushed",7)))
|
||
|
strcat(death," was crushed to death");
|
||
|
else if (!(strncmp(reason,"choked",6)))
|
||
|
if (sex=='M')
|
||
|
strcat(death," choked on his food");
|
||
|
else
|
||
|
strcat(death," choked on her food");
|
||
|
else if (!(strncmp(reason,"ascended",8))) {
|
||
|
if (sex=='F')
|
||
|
strcat(death," ascended to demigoddess-hood.");
|
||
|
else
|
||
|
strcat(death," ascended to demigod-hood.");
|
||
|
ascend=1;
|
||
|
}
|
||
|
else if (!(strncmp(reason,"escaped",7))) {
|
||
|
sprintf (death+strlen(death)," escaped the dungeon [max level %d].",maxlev);
|
||
|
ascend=1; /* Not an ascension, but also listed in a special way. */
|
||
|
}
|
||
|
else
|
||
|
strcat(death," died");
|
||
|
if (!ascend) {
|
||
|
pos=strlen(death);
|
||
|
if (dtype==6) /* The endgame */
|
||
|
{
|
||
|
if (dlev < -5 || dlev > -1) {
|
||
|
printf ("Invalid endgame level number %d in file!\n", dlev);
|
||
|
return (0L);
|
||
|
}
|
||
|
sprintf (death+pos," on the %s. ",eglev[-1-dlev]);
|
||
|
/* dlev is negative if you die in the endgame, from -1 to -5 */
|
||
|
}
|
||
|
else {
|
||
|
sprintf (death+pos," in %s",dungeon[dtype]);
|
||
|
if (dtype!=6)
|
||
|
sprintf(death+strlen(death)," on level %d",dlev);
|
||
|
if (maxlev!=dlev)
|
||
|
sprintf(death+strlen(death)," [max %d]. ",maxlev);
|
||
|
else strcat(death,". ");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((strncmp(reason,"quit",4)) && (strncmp(reason,"starvation",10))
|
||
|
&& (!ascend))
|
||
|
{ reason[0]=toupper(reason[0]);
|
||
|
for (pos2=0; reason[pos2]>=' '; pos2++);
|
||
|
reason[pos2++]='.';
|
||
|
reason[pos2]=0;
|
||
|
pos=strlen(death);
|
||
|
strcat(death,reason);
|
||
|
}
|
||
|
|
||
|
pos=strlen(death);
|
||
|
if (pos>54) { /* split line at the last space before position 54 */
|
||
|
while (pos>54 || death[pos]!=' ' || (par>0 && pos>30)) {
|
||
|
if(death[pos]==']') /* but we don't split between [] */
|
||
|
par++;
|
||
|
if(death[pos]=='[')
|
||
|
par--;
|
||
|
pos--;
|
||
|
}
|
||
|
death[pos]=0;
|
||
|
if (day && !dayshown) {
|
||
|
printf ("%s\n %s\t\t",death,pvm1);
|
||
|
dayshown=1;
|
||
|
}
|
||
|
else {
|
||
|
printf("%s\n\t\t",death);
|
||
|
}
|
||
|
plin++;
|
||
|
pos++;
|
||
|
pos2=0;
|
||
|
while (death[pos]) {
|
||
|
death[pos2]=death[pos];
|
||
|
pos++;
|
||
|
pos2++; }
|
||
|
death[pos2]=0;
|
||
|
}
|
||
|
|
||
|
printf("%-54s",death);
|
||
|
pos=0;
|
||
|
|
||
|
if (hp>0)
|
||
|
printf("%3d",hp);
|
||
|
else
|
||
|
printf(" -");
|
||
|
sprintf(death,"[%d]\n",maxhp);
|
||
|
printf("%7s",death);
|
||
|
plin++;
|
||
|
|
||
|
if (day && !dayshown) {
|
||
|
printf (" %s\n",pvm1);
|
||
|
plin++;
|
||
|
}
|
||
|
num++;
|
||
|
|
||
|
return (score);
|
||
|
}
|
||
|
|
||
|
/* Show some help about the arguments */
|
||
|
void help(char *prog, char *par, char *explain)
|
||
|
{
|
||
|
printf ("RECLIST, version %s\n",ver);
|
||
|
printf ("List Nethack 3.1 - 3.5 record file and calculate the average score.\n");
|
||
|
printf("\nUsage: %s [-f file / -fs / -l] [-u name] [-p role] [-r race]\n",
|
||
|
prog);
|
||
|
printf(" [-a alignment] [-g ver] [-nX] [-nsX] [-npX] [-d]");
|
||
|
printf(" [-ds date] [-du date]\n\n");
|
||
|
printf("The options are:\n");
|
||
|
printf("\t-u name list only the scores of player 'name'\n");
|
||
|
printf("\t-p profession list only the scores for one character class\n");
|
||
|
printf("\t-r race race to be listed (since 3.3: hum/dwa/elf/gno/orc)\n");
|
||
|
printf("\t-a alignment list only this alignment (since 3.3: l/n/c)\n");
|
||
|
printf("\t-f filename read the scores from the file 'filename'\n");
|
||
|
printf("\t-fs read the scorelist from stdin\n");
|
||
|
printf("\t-l read the logfile from %s\n", LOGFILE);
|
||
|
printf("\t-g ver list only scores of game version ver\n");
|
||
|
printf("\t-n X list only X scores\n");
|
||
|
printf("\t-np X pause after every X lines\n");
|
||
|
printf("\t-ns X don't list scores below X points\n");
|
||
|
printf("\t-d show also the dates\n");
|
||
|
printf("\t-ds date games ended at or after date\n");
|
||
|
printf("\t-du date games ended at or before date\n");
|
||
|
printf("\nYou may specify the options in any order.\n");
|
||
|
printf("The space after -f, -g, -u, -r, -a, -n, -ns or -np is optional.\n");
|
||
|
if (par)
|
||
|
printf ("\nUnknown parameter %s\n",par);
|
||
|
if (explain)
|
||
|
printf("%s\n", explain);
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* If it wasn't a 3.2 record file, we may still check if it makes sense
|
||
|
in the 3.1 format */
|
||
|
|
||
|
long line31 (char *line)
|
||
|
{
|
||
|
int pos, pos2,
|
||
|
dtype, dlev, maxlev,
|
||
|
hp, maxhp,
|
||
|
dayshown=0;
|
||
|
static int nro=0,
|
||
|
plin=1;
|
||
|
long score,
|
||
|
pvm;
|
||
|
char chclass, sex,
|
||
|
death[70]={""},
|
||
|
ascend=0;
|
||
|
char *reason;
|
||
|
|
||
|
static char *dungeon[]={"The Dungeons of Doom",/* 0 */
|
||
|
"Gehennom", /* 1 */
|
||
|
"The Gnomish Mines", /* 2 */
|
||
|
"The Quest", /* 3 */
|
||
|
"Fort Ludios", /* 4 */
|
||
|
"Vlad's Tower", /* 5 */
|
||
|
"the endgame"}; /* 6 */
|
||
|
|
||
|
static char *eglev[]={"Earth", /* 0 */
|
||
|
"Air", /* -1*/
|
||
|
"Fire", /* -2*/
|
||
|
"Water", /* -3*/
|
||
|
"Astral"}; /* -4*/
|
||
|
nro++;
|
||
|
/* The date uses positions 0-5 (YYMMDD), uid (starts) in 7.
|
||
|
After that, the dungeon branch of death and dlevel, maxdlevel, hp, maxhp,
|
||
|
score, character class&sex, name and reason for game ending
|
||
|
*/
|
||
|
sscanf (line,"%ld %*d %d %d %d %d %d %ld %c%c",
|
||
|
&pvm,&dtype,&dlev,&maxlev,&hp,&maxhp,&score,&chclass,&sex);
|
||
|
|
||
|
if (chclass<'A')
|
||
|
return (0); /* The VMS version may have end line with class < A */
|
||
|
if ((prole) && (prole[0]!=chclass))
|
||
|
return (0);
|
||
|
if (score<low)
|
||
|
return 0L;
|
||
|
/* Player name */
|
||
|
for (pos=20;!isalpha(line[pos]);pos++); /* pos points to class now */
|
||
|
if (pos>strlen(line)) /* Never is in a valid file. But we don't want */
|
||
|
return (0); /* to crash just if the file is invalid.. */
|
||
|
pos += 3;
|
||
|
for (pos2=0;(line[pos]!=',')&&(pos2<sizeof(death));pos2++,pos++)
|
||
|
death[pos2]=line[pos];
|
||
|
death[pos2]=0;
|
||
|
|
||
|
if ((name) && (strcmp(name,death)))
|
||
|
return (0);
|
||
|
|
||
|
printf("%3d%11ld ",nro,score);
|
||
|
reason= &line[pos+1];
|
||
|
strcat(death,"-"); /* The name is already there in the beginnin of string */
|
||
|
pos=strlen(death);
|
||
|
death[pos]=chclass; death[pos+1]=0;
|
||
|
if (!(strncmp(reason,"poisoned",8)))
|
||
|
strcat(death," was poisoned");
|
||
|
else if (!(strncmp(reason,"petrified",9)))
|
||
|
strcat(death," turned to stone");
|
||
|
else if (!(strncmp(reason,"starv",5)))
|
||
|
strcat(death," starved to death");
|
||
|
else if (!(strncmp(reason,"quit",4)))
|
||
|
strcat(death," quit");
|
||
|
else if (!(strncmp(reason,"crushed",7)))
|
||
|
strcat(death," was crushed to death");
|
||
|
else if (!(strncmp(reason,"choked",6)))
|
||
|
if (sex=='M')
|
||
|
strcat(death," choked on his food");
|
||
|
else
|
||
|
strcat(death," choked on her food");
|
||
|
else if (!(strncmp(reason,"ascended",8))) {
|
||
|
if (sex=='F')
|
||
|
strcat(death," ascended to demigoddess-hood.");
|
||
|
else
|
||
|
strcat(death," ascended to demigod-hood.");
|
||
|
ascend=1;
|
||
|
}
|
||
|
else if (!(strncmp(reason,"escaped",7))) {
|
||
|
strcat (death+strlen(death)," escaped the dungeon ");
|
||
|
if (!strcmp(reason+7," (with the Amulet)"))
|
||
|
sprintf (death+strlen(death),"[max level %d].",maxlev);
|
||
|
else
|
||
|
strcat (death,"with the Amulet");
|
||
|
ascend=1; /* Not an ascension, but also listed in a special way. */
|
||
|
}
|
||
|
else
|
||
|
strcat(death," died");
|
||
|
if (!ascend) {
|
||
|
pos=strlen(death);
|
||
|
|
||
|
if (dtype==6) /* The endgame */
|
||
|
sprintf (death+pos," in %s on the %s Plane.",
|
||
|
dungeon[dtype],eglev[-dlev]);
|
||
|
/* dlev is negative if you die in the endgame */
|
||
|
else {
|
||
|
sprintf (death+pos," in %s",dungeon[dtype]);
|
||
|
if (dtype!=6)
|
||
|
sprintf(death+strlen(death)," on level %d",dlev);
|
||
|
if (maxlev!=dlev)
|
||
|
sprintf(death+strlen(death)," [max %d].",maxlev);
|
||
|
else strcat(death,".");
|
||
|
}
|
||
|
if (strlen(death)>54) { /* split the line before the death reason */
|
||
|
death[pos]=0;
|
||
|
if (day && !dayshown) {
|
||
|
printf ("%s\n %ld\t\t",death,pvm);
|
||
|
plin++;
|
||
|
dayshown=0;
|
||
|
}
|
||
|
else {
|
||
|
printf("%s\n\t\t",death);
|
||
|
plin++;
|
||
|
}
|
||
|
pos++;
|
||
|
pos2=0;
|
||
|
while (death[pos]) {
|
||
|
death[pos2]=death[pos];
|
||
|
pos++;
|
||
|
pos2++; }
|
||
|
death[pos2]=0;
|
||
|
}
|
||
|
/* if (strlen(death)==55)
|
||
|
death[54]=0; */
|
||
|
}
|
||
|
printf("%-54s",death);
|
||
|
|
||
|
if (hp>0)
|
||
|
printf("%3d",hp);
|
||
|
else
|
||
|
printf(" -");
|
||
|
sprintf(death,"[%d]",maxhp);
|
||
|
printf("%6s\n",death);
|
||
|
plin++;
|
||
|
|
||
|
if ((strncmp(reason,"quit",4)) && (strncmp(reason,"starvation",10)) && (!ascend))
|
||
|
{ reason[0]=toupper(reason[0]);
|
||
|
reason[strlen(reason)-1]=0; /* remove the linefeed at the end */
|
||
|
if (day && !dayshown) {
|
||
|
printf(" %ld",pvm);
|
||
|
dayshown=0;
|
||
|
}
|
||
|
printf("\t\t%s.\n",reason);
|
||
|
plin++;
|
||
|
}
|
||
|
if (day && !dayshown) {
|
||
|
printf (" %ld\n",pvm);
|
||
|
plin++;
|
||
|
}
|
||
|
num++;
|
||
|
|
||
|
if (pause>0 && plin >= pause) {
|
||
|
printf ("--more--\r");
|
||
|
if (tolower(getch()=='n'))
|
||
|
pnum = num;
|
||
|
printf (" \r");
|
||
|
plin = 0;
|
||
|
}
|
||
|
|
||
|
return (score);
|
||
|
}
|
||
|
|
||
|
char *eos(char *p)
|
||
|
{
|
||
|
while (*p)
|
||
|
p++;
|
||
|
return (p);
|
||
|
}
|