This is based on the "new" dumplog patch for 3.6.0, by Maxime Bacoux.
Define DUMPLOG to enable. By default only enabled for the TTY linux.
.lp
MAX_STATUENAME_RANK\ =\ Maximum number of score file entries to use for
random statue names (default is 10).
+.lp
+DUMPLOGFILE\ =\ A filename where the end-of-game dumplog is saved.
+Not defining this will prevent dumplog from being created. Only available
+if your game is compiled with DUMPLOG. Allows the following placeholders:
+.sd
+.si
+%% - literal '%'
+%v - version (eg. "3.6.1-0")
+%u - game UID
+%t - game start time, UNIX timestamp format
+%T - current time, UNIX timestamp format
+%d - game start time, YYYYMMDDhhmmss format
+%D - current time, YYYYMMDDhhmmss format
+%n - player name
+%N - first character of player name
+.ei
+.ed
+
.hn 1
Scoring
.pg
\item[\ib{PERS\verb+_+IS\verb+_+UID}]
0 or 1 to use user names or numeric userids, respectively, to identify
unique people for the score file
+%.lp
+\item[\ib{DUMPLOGFILE}]
+A filename where the end-of-game dumplog is saved.
+Not defining this will prevent dumplog from being created. Only available
+if your game is compiled with DUMPLOG. Allows the following placeholders:
+%.sd
+%.si
+{\tt \%\%} --- literal `{\tt \%}'\\
+{\tt \%v} --- version (eg. "3.6.1-0")\\
+{\tt \%u} --- game UID\\
+{\tt \%t} --- game start time, UNIX timestamp format\\
+{\tt \%T} --- current time, UNIX timestamp format\\
+{\tt \%d} --- game start time, YYYYMMDDhhmmss format\\
+{\tt \%D} --- current time, YYYYMMDDhhmmss format\\
+{\tt \%n} --- player name\\
+{\tt \%N} --- first character of player name
+%.ei
+%.ed
\elist
%.hn 1
Ray Chason's MSDOS port support for some VESA modes
Darshan Shaligram's pet ranged attack
Jason Dorje Short's key rebinding
+Maxime Bacoux's new dumplog
Code Cleanup and Reorganization
but it isn't necessary for successful operation of the program */
#define FREE_ALL_MEMORY /* free all memory at exit */
+/* #define DUMPLOG */ /* End-of-game dump logs */
+#ifdef DUMPLOG
+
+#ifndef DUMPLOG_MSG_COUNT
+#define DUMPLOG_MSG_COUNT 50
+#endif
+
+#ifndef DUMPLOG_FILE
+#define DUMPLOG_FILE "/tmp/nethack.%n.%d.log"
+/* DUMPLOG_FILE allows following placeholders:
+ %% literal '%'
+ %v version (eg. "3.6.1-0")
+ %u game UID
+ %t game start time, UNIX timestamp format
+ %T current time, UNIX timestamp format
+ %d game start time, YYYYMMDDhhmmss format
+ %D current time, YYYYMMDDhhmmss format
+ %n player name
+ %N first character of player name
+ DUMPLOG_FILE is not used if SYSCF is defined
+*/
+#endif
+
+#endif
+
+
/* End of Section 4 */
#ifdef TTY_TILES_ESCCODES
/* ### botl.c ### */
+E char *NDECL(do_statusline1);
+E char *NDECL(do_statusline2);
E int FDECL(xlev_to_rank, (int));
E int FDECL(title_to_mon, (const char *, int *, int *));
E void NDECL(max_rank_sz);
E int FDECL(dosearch0, (int));
E int NDECL(dosearch);
E void NDECL(sokoban_detect);
+E void NDECL(dump_map);
E void FDECL(reveal_terrain, (int, int));
/* ### dig.c ### */
#endif
#endif
+E void FDECL(dump_open_log, (time_t));
+E void NDECL(dump_close_log);
+E void FDECL(dump_redirect, (boolean));
+E void FDECL(dump_forward_putstr, (winid, int, const char*, int));
+
/* ### wizard.c ### */
E void NDECL(amulet);
boolean vision_inited; /* true if vision is ready */
boolean sanity_check; /* run sanity checks */
boolean mon_polycontrol; /* debug: control monster polymorphs */
+ boolean in_dumplog; /* doing the dumplog right now? */
/* stuff that is related to options and/or user or platform preferences
*/
char *shellers; /* like wizards, for ! command (-DSHELL); also ^Z */
char *genericusers; /* usernames that prompt for user name */
char *debugfiles; /* files to show debugplines in. '*' is all. */
+#ifdef DUMPLOG
+ char *dumplogfile; /* where the dump file is saved */
+#endif
int env_dbgfl; /* 1: debugfiles comes from getenv("DEBUGFILES")
* so sysconf's DEBUGFILES shouldn't override it;
* 0: getenv() hasn't been attempted yet;
STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */
STATIC_DCL const char *NDECL(rank);
-#ifndef STATUS_VIA_WINDOWPORT
-
-STATIC_DCL void NDECL(bot1);
-STATIC_DCL void NDECL(bot2);
+#if !defined(STATUS_VIA_WINDOWPORT) || defined(DUMPLOG)
-STATIC_OVL void
-bot1()
+char *
+do_statusline1()
{
- char newbot1[MAXCO];
+ static char newbot1[BUFSZ];
register char *nb;
register int i, j;
if (flags.showscore)
Sprintf(nb = eos(nb), " S:%ld", botl_score());
#endif
- curs(WIN_STATUS, 1, 0);
- putstr(WIN_STATUS, 0, newbot1);
+ return newbot1;
}
-STATIC_OVL void
-bot2()
+char *
+do_statusline2()
{
- char newbot2[MAXCO], /* MAXCO: botl.h */
+ static char newbot2[BUFSZ], /* MAXCO: botl.h */
/* dungeon location (and gold), hero health (HP, PW, AC),
experience (HD if poly'd, else Exp level and maybe Exp points),
time (in moves), varying number of status conditions */
if ((money = money_cnt(invent)) < 0L)
money = 0L; /* ought to issue impossible() and then discard gold */
Sprintf(eos(dloc), "%s:%-2ld", /* strongest hero can lift ~300000 gold */
- encglyph(objnum_to_glyph(GOLD_PIECE)), min(money, 999999L));
+ iflags.in_dumplog ? "$" : encglyph(objnum_to_glyph(GOLD_PIECE)),
+ min(money, 999999L));
dln = strlen(dloc);
/* '$' encoded as \GXXXXNNNN is 9 chars longer than display will need */
dx = strstri(dloc, "\\G") ? 9 : 0;
/* only two or three consecutive spaces available to squeeze out */
mungspaces(newbot2);
}
-
- curs(WIN_STATUS, 1, 1);
- putmixed(WIN_STATUS, 0, newbot2);
+ return newbot2;
}
+#ifndef STATUS_VIA_WINDOWPORT
void
bot()
{
if (youmonst.data && iflags.status_updates) {
- bot1();
- bot2();
+ curs(WIN_STATUS, 1, 0);
+ putstr(WIN_STATUS, 0, do_statusline1());
+ curs(WIN_STATUS, 1, 1);
+ putmixed(WIN_STATUS, 0, do_statusline2());
}
context.botl = context.botlx = 0;
}
-
#endif /* !STATUS_VIA_WINDOWPORT */
+#endif /* !STATUS_VIA_WINDOWPORT || DUMPLOG */
+
/* convert experience level (1..30) to rank index (0..8) */
int
xlev_to_rank(xlev)
STATIC_PTR void FDECL(findone, (int, int, genericptr_t));
STATIC_PTR void FDECL(openone, (int, int, genericptr_t));
STATIC_DCL int FDECL(mfind0, (struct monst *, BOOLEAN_P));
+STATIC_DCL int FDECL(reveal_terrain_getglyph, (int, int, int, unsigned, int, int));
/* bring hero out from underwater or underground or being engulfed;
return True iff any change occurred */
}
}
+STATIC_DCL int
+reveal_terrain_getglyph(x,y, full, swallowed, default_glyph, which_subset)
+int x,y, full;
+unsigned swallowed;
+int default_glyph, which_subset;
+{
+ int glyph, levl_glyph;
+ uchar seenv;
+ boolean keep_traps = (which_subset & TER_TRP) !=0,
+ keep_objs = (which_subset & TER_OBJ) != 0,
+ keep_mons = (which_subset & TER_MON) != 0;
+ struct monst *mtmp;
+ struct trap *t;
+
+ /* for 'full', show the actual terrain for the entire level,
+ otherwise what the hero remembers for seen locations with
+ monsters, objects, and/or traps removed as caller dictates */
+ seenv = (full || level.flags.hero_memory)
+ ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
+ if (full) {
+ levl[x][y].seenv = SVALL;
+ glyph = back_to_glyph(x, y);
+ levl[x][y].seenv = seenv;
+ } else {
+ levl_glyph = level.flags.hero_memory
+ ? levl[x][y].glyph
+ : seenv
+ ? back_to_glyph(x, y)
+ : default_glyph;
+ /* glyph_at() returns the displayed glyph, which might
+ be a monster. levl[][].glyph contains the remembered
+ glyph, which will never be a monster (unless it is
+ the invisible monster glyph, which is handled like
+ an object, replacing any object or trap at its spot) */
+ glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
+ if (keep_mons && x == u.ux && y == u.uy && swallowed)
+ glyph = mon_to_glyph(u.ustuck);
+ else if (((glyph_is_monster(glyph)
+ || glyph_is_warning(glyph)) && !keep_mons)
+ || glyph_is_swallow(glyph))
+ glyph = levl_glyph;
+ if (((glyph_is_object(glyph) && !keep_objs)
+ || glyph_is_invisible(glyph))
+ && keep_traps && !covers_traps(x, y)) {
+ if ((t = t_at(x, y)) != 0 && t->tseen)
+ glyph = trap_to_glyph(t);
+ }
+ if ((glyph_is_object(glyph) && !keep_objs)
+ || (glyph_is_trap(glyph) && !keep_traps)
+ || glyph_is_invisible(glyph)) {
+ if (!seenv) {
+ glyph = default_glyph;
+ } else if (lastseentyp[x][y] == levl[x][y].typ) {
+ glyph = back_to_glyph(x, y);
+ } else {
+ /* look for a mimic here posing as furniture;
+ if we don't find one, we'll have to fake it */
+ if ((mtmp = m_at(x, y)) != 0
+ && mtmp->m_ap_type == M_AP_FURNITURE) {
+ glyph = cmap_to_glyph(mtmp->mappearance);
+ } else {
+ /* we have a topology type but we want a
+ screen symbol in order to derive a glyph;
+ some screen symbols need the flags field
+ of levl[][] in addition to the type
+ (to disambiguate STAIRS to S_upstair or
+ S_dnstair, for example; current flags
+ might not be intended for remembered
+ type, but we've got no other choice) */
+ schar save_typ = levl[x][y].typ;
+
+ levl[x][y].typ = lastseentyp[x][y];
+ glyph = back_to_glyph(x, y);
+ levl[x][y].typ = save_typ;
+ }
+ }
+ }
+ }
+ if (glyph == cmap_to_glyph(S_darkroom))
+ glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
+ return glyph;
+}
+
+void
+dump_map()
+{
+ int x, y, glyph;
+ int subset = TER_MAP|TER_TRP|TER_OBJ|TER_MON;
+ int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
+ char buf[BUFSZ];
+
+ for (y = 0; y < ROWNO; y++) {
+ for (x = 1; x < COLNO; x++) {
+ int ch, color;
+ unsigned special;
+ glyph = reveal_terrain_getglyph(x,y, FALSE, u.uswallow,
+ default_glyph, subset);
+ (void) mapglyph(glyph, &ch, &color, &special, x, y);
+ buf[x-1] = ch;
+ }
+ buf[x-2] = '\0';
+ putstr(0,0, buf);
+ }
+}
+
/* idea from crawl; show known portion of map without any monsters,
objects, or traps occluding the view of the underlying terrain */
void
if ((Hallucination || Stunned || Confusion) && !full) {
You("are too disoriented for this.");
} else {
- int x, y, glyph, levl_glyph, default_glyph;
- uchar seenv;
- struct monst *mtmp;
- struct trap *t;
+ int x, y, glyph, default_glyph;
char buf[BUFSZ];
/* there is a TER_MAP bit too; we always show map regardless of it */
boolean keep_traps = (which_subset & TER_TRP) !=0,
if (unconstrain_map())
docrt();
default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
- /* for 'full', show the actual terrain for the entire level,
- otherwise what the hero remembers for seen locations with
- monsters, objects, and/or traps removed as caller dictates */
+
for (x = 1; x < COLNO; x++)
for (y = 0; y < ROWNO; y++) {
- seenv = (full || level.flags.hero_memory)
- ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
- if (full) {
- levl[x][y].seenv = SVALL;
- glyph = back_to_glyph(x, y);
- levl[x][y].seenv = seenv;
- } else {
- levl_glyph = level.flags.hero_memory
- ? levl[x][y].glyph
- : seenv
- ? back_to_glyph(x, y)
- : default_glyph;
- /* glyph_at() returns the displayed glyph, which might
- be a monster. levl[][].glyph contains the remembered
- glyph, which will never be a monster (unless it is
- the invisible monster glyph, which is handled like
- an object, replacing any object or trap at its spot) */
- glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
- if (keep_mons && x == u.ux && y == u.uy && swallowed)
- glyph = mon_to_glyph(u.ustuck);
- else if (((glyph_is_monster(glyph)
- || glyph_is_warning(glyph)) && !keep_mons)
- || glyph_is_swallow(glyph))
- glyph = levl_glyph;
- if (((glyph_is_object(glyph) && !keep_objs)
- || glyph_is_invisible(glyph))
- && keep_traps && !covers_traps(x, y)) {
- if ((t = t_at(x, y)) != 0 && t->tseen)
- glyph = trap_to_glyph(t);
- }
- if ((glyph_is_object(glyph) && !keep_objs)
- || (glyph_is_trap(glyph) && !keep_traps)
- || glyph_is_invisible(glyph)) {
- if (!seenv) {
- glyph = default_glyph;
- } else if (lastseentyp[x][y] == levl[x][y].typ) {
- glyph = back_to_glyph(x, y);
- } else {
- /* look for a mimic here posing as furniture;
- if we don't find one, we'll have to fake it */
- if ((mtmp = m_at(x, y)) != 0
- && mtmp->m_ap_type == M_AP_FURNITURE) {
- glyph = cmap_to_glyph(mtmp->mappearance);
- } else {
- /* we have a topology type but we want a
- screen symbol in order to derive a glyph;
- some screen symbols need the flags field
- of levl[][] in addition to the type
- (to disambiguate STAIRS to S_upstair or
- S_dnstair, for example; current flags
- might not be intended for remembered
- type, but we've got no other choice) */
- schar save_typ = levl[x][y].typ;
-
- levl[x][y].typ = lastseentyp[x][y];
- glyph = back_to_glyph(x, y);
- levl[x][y].typ = save_typ;
- }
- }
- }
- }
- if (glyph == cmap_to_glyph(S_darkroom))
- glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
+ glyph = reveal_terrain_getglyph(x,y, full, swallowed,
+ default_glyph, which_subset);
show_glyph(x, y, glyph);
}
return TRUE;
}
+#ifdef DUMPLOG
+STATIC_OVL void
+dump_plines()
+{
+ int i;
+ char* str;
+ extern char* saved_plines[];
+
+ putstr(0, 0, "");
+ putstr(0, 0, "Latest messages:");
+ for (i = 0; i < DUMPLOG_MSG_COUNT; ++i)
+ {
+ str = saved_plines[DUMPLOG_MSG_COUNT - 1 - i];
+ if (str) {
+ char buf[BUFSZ];
+ Sprintf(buf, " %s", str);
+ putstr(0, 0, buf);
+ }
+#ifdef FREE_ALL_MEMORY
+ free(str);
+#endif
+ }
+}
+#endif
+
+STATIC_OVL void
+dump_everything(how, taken)
+int how;
+boolean taken;
+{
+#ifdef DUMPLOG
+ struct obj* obj;
+ struct topl* topl;
+ char pbuf[BUFSZ];
+
+ dump_redirect(TRUE);
+ if (!iflags.in_dumplog)
+ return;
+
+ init_symbols();
+
+ for (obj = invent; obj; obj = obj->nobj) {
+ makeknown(obj->otyp);
+ obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
+ if (Is_container(obj) || obj->otyp == STATUE)
+ obj->cknown = obj->lknown = 1;
+ }
+
+ Sprintf(pbuf, "%s, %s %s %s %s", plname,
+ aligns[1 - u.ualign.type].adj,
+ genders[flags.female].adj,
+ urace.adj,
+ (flags.female && urole.name.f) ? urole.name.f : urole.name.m);
+ putstr(0, 0, pbuf);
+ putstr(0, 0, "");
+
+ dump_map();
+ putstr(0, 0, do_statusline1());
+ putstr(0, 0, do_statusline2());
+ putstr(0, 0, "");
+
+ dump_plines();
+ putstr(0, 0, "");
+ putstr(0, 0, "Inventory:");
+ display_inventory((char *) 0, TRUE);
+ container_contents(invent, TRUE, TRUE, FALSE);
+ enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
+ (how >= PANICKED) ? ENL_GAMEOVERALIVE
+ : ENL_GAMEOVERDEAD);
+ putstr(0, 0, "");
+ list_vanquished('y', FALSE);
+ putstr(0, 0, "");
+ list_genocided('a', FALSE);
+ putstr(0, 0, "");
+ show_conduct((how >= PANICKED) ? 1 : 2);
+ putstr(0, 0, "");
+ show_overview((how >= PANICKED) ? 1 : 2, how);
+ putstr(0, 0, "");
+ dump_redirect(FALSE);
+#endif
+}
+
STATIC_OVL void
disclose(how, taken)
int how;
urealtime.finish_time = endtime = getnow();
urealtime.realtime += (long) (endtime - urealtime.start_timing);
+ dump_open_log(endtime);
/* Sometimes you die on the first move. Life's not fair.
* On those rare occasions you get hosed immediately, go out
* smiling... :-) -3.
if (strcmp(flags.end_disclose, "none") && how != PANICKED)
disclose(how, taken);
+ dump_everything(how, taken);
/* finish_paybill should be called after disclosure but before bones */
if (bones_ok && taken)
} else
done_stopprint = 1; /* just avoid any more output */
+#ifdef DUMPLOG
+ dump_redirect(TRUE);
+ genl_outrip(0, how, endtime);
+ dump_redirect(FALSE);
+#endif
if (u.uhave.amulet) {
Strcat(killer.name, " (with the Amulet)");
} else if (how == ESCAPED) {
/* don't bother counting to see whether it should be plural */
}
- if (!done_stopprint) {
- Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
- (how != ASCENDED)
- ? (const char *) ((flags.female && urole.name.f)
- ? urole.name.f
- : urole.name.m)
- : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
- putstr(endwin, 0, pbuf);
- putstr(endwin, 0, "");
- }
+ Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
+ (how != ASCENDED)
+ ? (const char *) ((flags.female && urole.name.f)
+ ? urole.name.f
+ : urole.name.m)
+ : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
+ dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
+ dump_forward_putstr(endwin, 0, "", done_stopprint);
if (how == ESCAPED || how == ASCENDED) {
struct monst *mtmp;
/* count the points for artifacts */
artifact_score(invent, TRUE, endwin);
+#ifdef DUMPLOG
+ dump_redirect(TRUE);
+ artifact_score(invent, TRUE, endwin);
+ dump_redirect(FALSE);
+#endif
viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
mtmp = mydogs;
- if (!done_stopprint)
- Strcpy(pbuf, "You");
+ Strcpy(pbuf, "You");
if (!Schroedingers_cat) /* check here in case disclosure was off */
Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
if (Schroedingers_cat) {
int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
mhp = d(m_lev, 8);
nowrap_add(u.urexp, mhp);
- if (!done_stopprint)
- Strcat(eos(pbuf), " and Schroedinger's cat");
+ Strcat(eos(pbuf), " and Schroedinger's cat");
}
if (mtmp) {
while (mtmp) {
- if (!done_stopprint)
- Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
+ Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
if (mtmp->mtame)
nowrap_add(u.urexp, mtmp->mhp);
mtmp = mtmp->nmon;
}
- if (!done_stopprint)
- putstr(endwin, 0, pbuf);
+ dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
pbuf[0] = '\0';
} else {
- if (!done_stopprint)
- Strcat(pbuf, " ");
- }
- if (!done_stopprint) {
- Sprintf(eos(pbuf), "%s with %ld point%s,",
- how == ASCENDED ? "went to your reward"
- : "escaped from the dungeon",
- u.urexp, plur(u.urexp));
- putstr(endwin, 0, pbuf);
+ Strcat(pbuf, " ");
}
+ Sprintf(eos(pbuf), "%s with %ld point%s,",
+ how == ASCENDED ? "went to your reward"
+ : "escaped from the dungeon",
+ u.urexp, plur(u.urexp));
+ dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
if (!done_stopprint)
artifact_score(invent, FALSE, endwin); /* list artifacts */
+#if DUMPLOG
+ dump_redirect(TRUE);
+ artifact_score(invent, FALSE, 0);
+ dump_redirect(FALSE);
+#endif
/* list valuables here */
for (val = valuables; val->list; val++) {
Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
count, plur(count));
}
- putstr(endwin, 0, pbuf);
+ dump_forward_putstr(endwin, 0, pbuf, 0);
}
}
- } else if (!done_stopprint) {
+ } else {
/* did not escape or ascend */
if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
/* level teleported out of the dungeon; `how' is DIED,
}
Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
- putstr(endwin, 0, pbuf);
+ dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
}
- if (!done_stopprint) {
- Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
- plur(umoney), moves, plur(moves));
- putstr(endwin, 0, pbuf);
- }
- if (!done_stopprint) {
- Sprintf(pbuf,
- "You were level %d with a maximum of %d hit point%s when you %s.",
- u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
- putstr(endwin, 0, pbuf);
- putstr(endwin, 0, "");
- }
+ Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
+ plur(umoney), moves, plur(moves));
+ dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
+ Sprintf(pbuf,
+ "You were level %d with a maximum of %d hit point%s when you %s.",
+ u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
+ dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
+ dump_forward_putstr(endwin, 0, "", done_stopprint);
if (!done_stopprint)
display_nhwindow(endwin, TRUE);
if (endwin != WIN_ERR)
destroy_nhwindow(endwin);
+ dump_close_log();
/* "So when I die, the first thing I will see in Heaven is a
* score list?" */
if (have_windows && !iflags.toptenwin)
free((genericptr_t) sysopt.debugfiles);
sysopt.debugfiles = dupstr(bufp);
}
+ } else if (src == SET_IN_SYS && match_varname(buf, "DUMPLOGFILE", 7)) {
+#ifdef DUMPLOG
+ if (sysopt.dumplogfile)
+ free((genericptr_t) sysopt.dumplogfile);
+ sysopt.dumplogfile = dupstr(bufp);
+#endif
} else if (src == SET_IN_SYS && match_varname(buf, "GENERICUSERS", 12)) {
- if (sysopt.genericusers) free(sysopt.genericusers);
+ if (sysopt.genericusers)
+ free((genericptr_t) sysopt.genericusers);
sysopt.genericusers = dupstr(bufp);
} else if (src == SET_IN_SYS && match_varname(buf, "SUPPORT", 7)) {
if (sysopt.support)
static void FDECL(execplinehandler, (const char *));
#endif
+#ifdef DUMPLOG
+char* saved_plines[DUMPLOG_MSG_COUNT] = {0};
+#endif
+
/*VARARGS1*/
/* Note that these declarations rely on knowledge of the internals
* of the variable argument handling stuff in "tradstdc.h"
return;
}
+#ifdef DUMPLOG
+ /* We hook here early to have options-agnostic output. */
+ free(saved_plines[DUMPLOG_MSG_COUNT - 1]);
+ for (ln = 0; ln < DUMPLOG_MSG_COUNT - 1; ++ln)
+ saved_plines[DUMPLOG_MSG_COUNT - ln - 1] = saved_plines[DUMPLOG_MSG_COUNT - ln - 2];
+ saved_plines[0] = malloc(strlen(line) + 1);
+ (void) strcpy(saved_plines[0], line);
+#endif
+
msgtyp = msgtype_type(line, no_repeat);
if (msgtyp == MSGTYP_NOSHOW
|| (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg)))
sysopt.debugfiles = (char *) 0;
#else
sysopt.debugfiles = dupstr(DEBUGFILES);
+#endif
+#ifdef DUMPLOG
+ sysopt.dumplogfile = (char *) 0;
#endif
sysopt.env_dbgfl = 0; /* haven't checked getenv("DEBUGFILES") yet */
sysopt.shellers = (char *) 0;
if (sysopt.debugfiles)
free((genericptr_t) sysopt.debugfiles),
sysopt.debugfiles = (char *) 0;
+#ifdef DUMPLOG
+ if (sysopt.dumplogfile)
+ free((genericptr_t)sysopt.dumplogfile), sysopt.dumplogfile=(char *)0;
+#endif
if (sysopt.genericusers)
free((genericptr_t) sysopt.genericusers),
sysopt.genericusers = (char *) 0;
#endif /* STATUS_HILITES */
#endif /* STATUS_VIA_WINDOWPORT */
+STATIC_VAR struct window_procs dumplog_windowprocs_backup;
+STATIC_PTR FILE* dumplog_file;
+
+#ifdef DUMPLOG
+char *
+dump_fmtstr(fmt, buf)
+char *fmt;
+char *buf;
+{
+ char *fp = fmt, *bp = buf;
+ int slen, len = 0;
+ char tmpbuf[BUFSZ];
+ char verbuf[BUFSZ];
+
+ time_t now = getnow();
+ int uid = getuid();
+
+ while (fp && *fp && len < BUFSZ-1) {
+ if (*fp == '%') {
+ fp++;
+ switch (*fp) {
+ default: goto finish;
+ case '\0': /* fallthrough */
+ case '%': /* literal % */
+ Sprintf(tmpbuf,"%%");
+ break;
+ case 't': /* game start, timestamp */
+ Sprintf(tmpbuf, "%ld", ubirthday);
+ break;
+ case 'T': /* current time, timestamp */
+ Sprintf(tmpbuf, "%ld", now);
+ break;
+ case 'd': /* game start, YYYYMMDDhhmmss */
+ Sprintf(tmpbuf, "%08ld%06ld",
+ yyyymmdd(ubirthday), hhmmss(ubirthday));
+ break;
+ case 'D': /* current time, YYYYMMDDhhmmss */
+ Sprintf(tmpbuf, "%08ld%06ld", yyyymmdd(now), hhmmss(now));
+ break;
+ case 'v': /* version, eg. "3.6.1-0" */
+ Sprintf(tmpbuf, "%s", version_string(verbuf));
+ break;
+ case 'u': /* UID */
+ Sprintf(tmpbuf, "%d", uid);
+ break;
+ case 'n': /* player name */
+ Sprintf(tmpbuf, "%s", (plname ? plname : "unknown"));
+ break;
+ case 'N': /* first character of player name */
+ Sprintf(tmpbuf, "%c", (plname ? *plname : 'u'));
+ break;
+ }
+
+ slen = strlen(tmpbuf);
+ if (len + slen < BUFSZ-1) {
+ len += slen;
+ Sprintf(bp, "%s", tmpbuf);
+ bp += slen;
+ if (*fp) fp++;
+ } else
+ break;
+ } else {
+ *bp = *fp;
+ bp++;
+ fp++;
+ len++;
+ }
+ }
+ finish:
+ *bp = '\0';
+ return buf;
+}
+#endif /* DUMPLOG */
+
+
+void
+dump_open_log(now)
+time_t now;
+{
+#ifdef DUMPLOG
+ char buf[BUFSZ];
+ char *fname;
+
+#ifdef SYSCF
+ if (!sysopt.dumplogfile)
+ return;
+ fname = dump_fmtstr(sysopt.dumplogfile, buf);
+#else
+ fname = dump_fmtstr(DUMPLOG_FILE, buf);
+#endif
+
+ dumplog_file = fopen(fname, "w");
+ dumplog_windowprocs_backup = windowprocs;
+#endif
+}
+
+void
+dump_close_log()
+{
+ if (dumplog_file) {
+ fclose(dumplog_file);
+ dumplog_file = NULL;
+ }
+}
+
+void
+dump_putc(ch)
+int ch;
+{
+ /* Not very efficient, but we mostly don't care. */
+ if (dumplog_file)
+ putc(ch, dumplog_file);
+}
+
+void
+dump_forward_putstr(win, attr, str, no_forward)
+winid win;
+int attr;
+const char* str;
+int no_forward;
+{
+ if (dumplog_file)
+ fprintf(dumplog_file, "%s\n", str);
+ if (!no_forward)
+ putstr(win, attr, str);
+}
+
+STATIC_OVL void
+dump_putstr(win, attr, str)
+winid win;
+int attr;
+const char* str;
+{
+ if (dumplog_file)
+ fprintf(dumplog_file, "%s\n", str);
+}
+
+STATIC_OVL winid
+dump_create_nhwindow(dummy)
+int dummy;
+{
+ return dummy;
+}
+
+STATIC_OVL void
+dump_clear_nhwindow(win)
+winid win;
+{
+
+}
+
+STATIC_OVL void
+dump_display_nhwindow(win, p)
+winid win;
+BOOLEAN_P p;
+{
+
+}
+
+STATIC_OVL void
+dump_destroy_nhwindow(win)
+winid win;
+{
+
+}
+
+STATIC_OVL void
+dump_start_menu(win)
+winid win;
+{
+
+}
+
+STATIC_OVL void
+dump_add_menu(win, glyph, identifier, ch, gch, attr, str, preselected)
+winid win;
+int glyph;
+const ANY_P* identifier;
+CHAR_P ch;
+CHAR_P gch;
+int attr;
+const char* str;
+BOOLEAN_P preselected;
+{
+ if (dumplog_file) {
+ if (glyph == NO_GLYPH)
+ fprintf(dumplog_file, " %s\n", str);
+ else
+ fprintf(dumplog_file, " %c - %s\n", ch, str);
+ }
+}
+
+STATIC_OVL void
+dump_end_menu(win, str)
+winid win;
+const char* str;
+{
+ if (dumplog_file) {
+ if (str)
+ fprintf(dumplog_file, "%s\n", str);
+ else
+ fputs("\n", dumplog_file);
+ }
+}
+
+STATIC_OVL int
+dump_select_menu(win, index, item)
+winid win;
+int index;
+MENU_ITEM_P** item;
+{
+ *item = NULL;
+ return 0;
+}
+
+void
+dump_redirect(flag)
+boolean flag;
+{
+ if (dumplog_file) {
+ if (flag) {
+ windowprocs.win_create_nhwindow = dump_create_nhwindow;
+ windowprocs.win_clear_nhwindow = dump_clear_nhwindow;
+ windowprocs.win_display_nhwindow = dump_display_nhwindow;
+ windowprocs.win_destroy_nhwindow = dump_destroy_nhwindow;
+ windowprocs.win_start_menu = dump_start_menu;
+ windowprocs.win_add_menu = dump_add_menu;
+ windowprocs.win_end_menu = dump_end_menu;
+ windowprocs.win_select_menu = dump_select_menu;
+ windowprocs.win_putstr = dump_putstr;
+ } else {
+ windowprocs = dumplog_windowprocs_backup;
+ }
+ iflags.in_dumplog = flag;
+ } else {
+ iflags.in_dumplog = FALSE;
+ }
+}
+
/*windows.c*/
CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE
CFLAGS+=-DTIMED_DELAY
CFLAGS+=-DHACKDIR=\"$(HACKDIR)\"
+CFLAGS+=-DDUMPLOG
LINK=$(CC)
# Only needed for GLIBC stack trace:
# overridden via DEBUGFILES environment variable.
#DEBUGFILES=*
+# Save end of game dump log to this file.
+# Only available if NetHack was compiled with DUMPLOG
+# Allows following placeholders:
+# %% literal '%'
+# %v version (eg. "3.6.1-0")
+# %u game UID
+# %t game start time, UNIX timestamp format
+# %T current time, UNIX timestamp format
+# %d game start time, YYYYMMDDhhmmss format
+# %D current time, YYYYMMDDhhmmss format
+# %n player name
+# %N first character of player name
+#DUMPLOGFILE=/tmp/nethack.%n.%d.log
+
# Try to get more info in case of a program bug or crash. Only used
# if the program is built with the PANICTRACE compile-time option enabled.
# By default PANICTRACE is enabled if BETA is defined, otherwise disabled.
# Only available if game has been compiled with DEBUG.
#DEBUGFILES=*
+# Save end of game dump log to this file.
+# Only available if NetHack was compiled with DUMPLOG
+# Allows following placeholders:
+# %% literal '%'
+# %v version (eg. "3.6.1-0")
+# %u game UID
+# %t game start time, UNIX timestamp format
+# %T current time, UNIX timestamp format
+# %d game start time, YYYYMMDDhhmmss format
+# %D current time, YYYYMMDDhhmmss format
+# %n player name
+# %N first character of player name
+#DUMPLOGFILE=nethack-%n-%d.log
+
# Limit the number of simultaneous games (see also nethack.sh).
#MAXPLAYERS=10
#ifdef DLB
"data librarian",
#endif
+#ifdef DUMPLOG
+ "end-of-game dumplogs",
+#endif
#ifdef MFLOPPY
"floppy drive support",
#endif