Matthew Faltys
5 years ago
commit
783ceac78a
4 changed files with 1089 additions and 0 deletions
@ -0,0 +1,24 @@
|
||||
GOC=go build
|
||||
GOFLAGS=-a -ldflags '-s'
|
||||
CGOR=CGO_ENABLED=0
|
||||
|
||||
|
||||
all: stat reclist |
||||
|
||||
run: |
||||
go run \
|
||||
nethack-launcher.go
|
||||
|
||||
stat: |
||||
mkdir -p bin/
|
||||
$(CGOR) $(GOC) $(GOFLAGS) -o bin/nethack-launcher nethack-launcher.go
|
||||
|
||||
reclist: |
||||
mkdir -p bin/
|
||||
gcc -static deps/reclist.c -o bin/reclist
|
||||
|
||||
dependencies: |
||||
go get github.com/gorilla/mux
|
||||
|
||||
clean: |
||||
rm -rf bin/
|
@ -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=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); |
||||
} |
@ -0,0 +1,13 @@
|
||||
3.4.3 15420 2 10 10 -2 109 1 20191104 20191103 5 Val Dwa Fem Law squid,killed by a Green-elf, while frozen by a monster's gaze |
||||
3.4.3 9966 0 8 8 -1 63 1 20191101 20191101 5 Ran Elf Mal Cha jonathon,killed by a lizard, while frozen by a potion |
||||
3.4.3 5770 2 8 8 -1 77 1 20191105 20191105 5 Val Dwa Fem Law squid,killed by a fall onto poison spikes |
||||
3.4.3 4856 2 5 5 0 55 1 20191025 20191025 5 Ran Hum Mal Cha jonathon,killed by a killer bee, while praying |
||||
3.4.3 2469 0 4 5 -4 63 1 20191107 20191105 5 Val Dwa Fem Law squid,killed by a water moccasin, while helpless |
||||
3.4.3 1847 0 4 4 0 27 1 20191101 20191101 5 Pri Elf Mal Cha squid,killed by a falling rock |
||||
3.4.3 1526 2 7 7 0 43 1 20191101 20191101 5 Mon Hum Fem Neu jonathon,killed by a giant spider |
||||
3.4.3 1332 0 3 4 -2 40 1 20191106 20191106 5 Ran Elf Mal Cha jonathon,killed by an incubus |
||||
3.4.3 1132 2 5 6 -2 57 1 20191106 20191106 5 Val Dwa Fem Law jonathon,killed by a rothe, while helpless |
||||
3.4.3 1020 2 4 4 0 47 1 20191101 20191101 5 Cav Hum Fem Law squid,killed by a gnome lord, while praying |
||||
3.4.3 968 0 4 4 47 47 0 20191105 20191105 5 Sam Hum Mal Law squid,quit |
||||
3.4.3 901 0 4 4 -1 25 1 20191025 20191025 5 Tou Hum Mal Neu jonathon,killed by a kitten |
||||
3.4.3 852 2 4 4 -3 25 1 20191105 20191105 5 Arc Gno Mal Neu squid,killed by a werejackal |
@ -0,0 +1,123 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"flag" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"os" |
||||
"os/exec" |
||||
|
||||
"github.com/unixvoid/glogger" |
||||
) |
||||
|
||||
var ( |
||||
loglevel string |
||||
recordlocation string |
||||
reclistlocation string |
||||
port = 8080 |
||||
) |
||||
|
||||
func main() { |
||||
// init runtime args
|
||||
flag.StringVar(&loglevel, "loglevel", "debug", loglevel) |
||||
flag.StringVar(&recordlocation, "recordlocation", "record", recordlocation) |
||||
flag.StringVar(&reclistlocation, "reclistlocation", "reclist", reclistlocation) |
||||
flag.Parse() |
||||
|
||||
// init config file and logger
|
||||
initLogger(loglevel) |
||||
|
||||
//// // make sure record file exists
|
||||
//// if _, err := os.Stat(recordlocation); os.IsNotExist(err) {
|
||||
//// glogger.Info.Printf("record file not found in %s\n", recordlocation)
|
||||
//// fmt.Printf("%s\n", err)
|
||||
//// os.Exit(1)
|
||||
//// }
|
||||
//// // make sure reclist bin exists
|
||||
//// if _, err := os.Stat(reclistlocation); os.IsNotExist(err) {
|
||||
//// glogger.Info.Printf("reclist binary not found in %s\n", reclistlocation)
|
||||
//// fmt.Printf("%s\n", err)
|
||||
//// os.Exit(1)
|
||||
//// }
|
||||
|
||||
clearScreen() |
||||
printWelcomeScreen() |
||||
|
||||
// disable input buffering
|
||||
exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run() |
||||
// do not display entered characters on the screen
|
||||
exec.Command("stty", "-F", "/dev/tty", "-echo").Run() |
||||
|
||||
var b []byte = make([]byte, 1) |
||||
for { |
||||
os.Stdin.Read(b) |
||||
//fmt.Println("I got the byte", b, "("+string(b)+")")
|
||||
switch string(b) { |
||||
case "q": |
||||
clearScreen() |
||||
os.Exit(0) |
||||
default: |
||||
//fmt.Printf("command '%s' not implimented\n", string(b))
|
||||
//fmt.Printf(">> ")
|
||||
} |
||||
} |
||||
//scanner := bufio.NewScanner(os.Stdin)
|
||||
//for scanner.Scan() {
|
||||
// //fmt.Println(scanner.Text())
|
||||
// switch scanner.Text() {
|
||||
// case "q":
|
||||
// os.Exit(0)
|
||||
// default:
|
||||
// fmt.Printf("command '%s' not implimented\n", scanner.Text())
|
||||
// fmt.Printf(">> ")
|
||||
// }
|
||||
//}
|
||||
//if err := scanner.Err(); err != nil {
|
||||
// log.Println(err)
|
||||
//}
|
||||
} |
||||
|
||||
func initLogger(loglevel string) { |
||||
// init logger
|
||||
if loglevel == "debug" { |
||||
glogger.LogInit(os.Stdout, os.Stdout, os.Stdout, os.Stderr) |
||||
} else if loglevel == "cluster" { |
||||
glogger.LogInit(os.Stdout, os.Stdout, ioutil.Discard, os.Stderr) |
||||
} else if loglevel == "info" { |
||||
glogger.LogInit(os.Stdout, ioutil.Discard, ioutil.Discard, os.Stderr) |
||||
} else { |
||||
glogger.LogInit(ioutil.Discard, ioutil.Discard, ioutil.Discard, os.Stderr) |
||||
} |
||||
} |
||||
|
||||
func clearScreen() { |
||||
cmd := exec.Command("clear") |
||||
cmd.Stdout = os.Stdout |
||||
cmd.Run() |
||||
} |
||||
|
||||
func printWelcomeScreen() { |
||||
println("unixvoid.com underground nethack server") |
||||
println("") |
||||
println(" Not logged in.") |
||||
println("") |
||||
println(" l) Login") |
||||
println(" r) Register new user") |
||||
println(" w) Watch games in progress") |
||||
println(" q) Quit") |
||||
println("") |
||||
fmt.Printf(">> ") |
||||
} |
||||
|
||||
func gethighscore(w http.ResponseWriter, r *http.Request) { |
||||
// run script
|
||||
output, err := exec.Command(reclistlocation, |
||||
"-f", |
||||
recordlocation).CombinedOutput() |
||||
if err != nil { |
||||
fmt.Printf("%s\n", err) |
||||
} |
||||
|
||||
fmt.Fprintf(w, "%s\n", output) |
||||
} |
Loading…
Reference in new issue