Matthew Faltys
5 years ago
1 changed files with 0 additions and 929 deletions
@ -1,929 +0,0 @@ |
|||||||
/* 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=0; |
|
||||||
|
|
||||||
/* 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