Matthew Faltys
5 years ago
6 changed files with 984 additions and 1 deletions
@ -0,0 +1,929 @@ |
|||||||
|
/* 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); |
||||||
|
} |
Loading…
Reference in new issue