From 285023acf65e36e3ca419a0ebea036323d082725 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 3 Dec 2018 18:57:01 -0800 Subject: [PATCH 01/43] fix #H7659 - accessing freed memory by cutworm() hmon() can destroy the weapon being used, and known_hitum() would still pass the pointer to the freed object to cutworm(). Remember the relevant weapon attribute before using and maybe freeing the object, then pass that attribute instead of the whole weapon. Also pass 'more-likely-to-cut' for axes in addition to blades. thimonst() behaved similarly, although due to much different code paths none of the objects that might get to hmon() were then passed to cutworm(), so it wasn't vulnerable. But pass 'more-likely-to-cut' for axes instead of for blades when thrown. --- doc/fixes36.2 | 2 ++ include/extern.h | 4 ++-- src/dothrow.c | 14 +++++++++----- src/uhitm.c | 8 +++++--- src/worm.c | 16 +++++++--------- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 53ba866ea..07d1fe756 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -236,6 +236,8 @@ to emphasize that it's not a light source, change description of wielded Sting from "(glowing)" to nothing (not warm enough to feel) when blind glowing Sting quivers if hero becomes blind and quivering Sting glows if blindness ends; it worked for timed blindness but not for blindfold +weapon (wielded pie, egg, potion, boomerang) might be destroyed when hitting a + long worm, then freed memory was accessed to decide whether to cut it Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 16b10859b..4f1c8d6fa 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1543745352 2018/12/02 10:09:12 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.664 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1543892214 2018/12/04 02:56:54 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.665 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2834,7 +2834,7 @@ E void FDECL(worm_move, (struct monst *)); E void FDECL(worm_nomove, (struct monst *)); E void FDECL(wormgone, (struct monst *)); E void FDECL(wormhitu, (struct monst *)); -E void FDECL(cutworm, (struct monst *, XCHAR_P, XCHAR_P, struct obj *)); +E void FDECL(cutworm, (struct monst *, XCHAR_P, XCHAR_P, BOOLEAN_P)); E void FDECL(see_wsegs, (struct monst *)); E void FDECL(detect_wsegs, (struct monst *, BOOLEAN_P)); E void FDECL(save_worm, (int, int)); diff --git a/src/dothrow.c b/src/dothrow.c index d40857447..e55c9b7d6 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 dothrow.c $NHDT-Date: 1525012611 2018/04/29 14:36:51 $ $NHDT-Branch: master $:$NHDT-Revision: 1.137 $ */ +/* NetHack 3.6 dothrow.c $NHDT-Date: 1543892215 2018/12/04 02:56:55 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.152 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1652,13 +1652,16 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ } if (tmp >= dieroll) { - boolean wasthrown = (thrownobj != 0); + boolean wasthrown = (thrownobj != 0), + /* remember weapon attribute; hmon() might destroy obj */ + chopper = is_axe(obj); /* attack hits mon */ if (hmode == HMON_APPLIED) u.uconduct.weaphit++; if (hmon(mon, obj, hmode, dieroll)) { /* mon still alive */ - cutworm(mon, bhitpos.x, bhitpos.y, obj); + if (mon->wormno) + cutworm(mon, bhitpos.x, bhitpos.y, chopper); } exercise(A_DEX, TRUE); /* if hero was swallowed and projectile killed the engulfer, @@ -1668,8 +1671,9 @@ register struct obj *obj; /* thrownobj or kickedobj or uwep */ if (wasthrown && !thrownobj) return 1; - /* projectiles other than magic stones - sometimes disappear when thrown */ + /* projectiles other than magic stones sometimes disappear + when thrown; projectiles aren't among the types of weapon + that hmon() might have destroyed so obj is intact */ if (objects[otyp].oc_skill < P_NONE && objects[otyp].oc_skill > -P_BOOMERANG && !objects[otyp].oc_magic) { diff --git a/src/uhitm.c b/src/uhitm.c index a14218543..0412bb599 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 uhitm.c $NHDT-Date: 1542765366 2018/11/21 01:56:06 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.185 $ */ +/* NetHack 3.6 uhitm.c $NHDT-Date: 1543892215 2018/12/04 02:56:55 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.195 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -449,7 +449,9 @@ int rollneeded, armorpenalty; /* for monks */ struct attack *uattk; int dieroll; { - register boolean malive = TRUE; + boolean malive = TRUE, + /* hmon() might destroy weapon; remember aspect for cutworm */ + slice_or_chop = (weapon && (is_blade(weapon) || is_axe(weapon))); if (override_confirmation) { /* this may need to be generalized if weapons other than @@ -490,7 +492,7 @@ int dieroll; u.uconduct.weaphit = oldweaphit; } if (mon->wormno && *mhit) - cutworm(mon, x, y, weapon); + cutworm(mon, x, y, slice_or_chop); } } return malive; diff --git a/src/worm.c b/src/worm.c index 4e9d144b1..faf257710 100644 --- a/src/worm.c +++ b/src/worm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 worm.c $NHDT-Date: 1456528599 2016/02/26 23:16:39 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.20 $ */ +/* NetHack 3.6 worm.c $NHDT-Date: 1543892216 2018/12/04 02:56:56 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.28 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -314,10 +314,10 @@ register struct monst *worm; * that both halves will survive. */ void -cutworm(worm, x, y, weap) +cutworm(worm, x, y, cuttier) struct monst *worm; xchar x, y; -struct obj *weap; +boolean cuttier; /* hit is by wielded blade or axe or by thrown axe */ { register struct wseg *curr, *new_tail; register struct monst *new_worm; @@ -330,12 +330,10 @@ struct obj *weap; if (x == worm->mx && y == worm->my) return; /* hit on head */ - /* cutting goes best with a bladed weapon */ - cut_chance = rnd(20); /* Normally 1-16 does not cut */ - /* Normally 17-20 does */ - - if (weap && is_blade(weap)) /* With a blade 1- 6 does not cut */ - cut_chance += 10; /* 7-20 does */ + /* cutting goes best with a cuttier weapon */ + cut_chance = rnd(20); /* Normally 1-16 does not cut, 17-20 does, */ + if (cuttier) + cut_chance += 10; /* with a blade 1- 6 does not cut, 7-20 does. */ if (cut_chance < 17) return; /* not good enough */ From ec8f002d810aa39aab04a104909ebb30f0423ebe Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 4 Dec 2018 11:45:41 -0500 Subject: [PATCH 02/43] bit-use collision on WC2_TERM_SIZE, WC2_RESET_STATUS --- doc/fixes36.2 | 2 ++ include/winprocs.h | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 53ba866ea..ad513eb17 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -258,6 +258,8 @@ buliding with EXTRA_SANITY_CHECKS enabled would issue "no monster to remove" changing Sting's description to be "(weapon in hand) (light blue aura)" was too close to feedback when objects become blessed; change it again, to "(weapon in hand, flickering/glimmering/gleaming light blue)" +fix bit-use collision between WC2_TERM_SIZE and WC2_RESET_STATUS in + include/winprocs.h following a recent merge tty: turn off an optimization that is the suspected cause of Windows reported partial status lines following level changes tty: ensure that current status fields are always copied to prior status diff --git a/include/winprocs.h b/include/winprocs.h index 316af5171..ddfef409d 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -216,10 +216,10 @@ extern after updating status window fields */ #define WC2_RESET_STATUS 0x0100L /* 09 call status_update(BL_RESET) to indicate draw everything */ -#define WC2_TERM_SIZE 0x0100L /* 10 support setting terminal size */ -#define WC2_WINDOWBORDERS 0x0200L /* 11 display borders on nh windows */ -#define WC2_PETATTR 0x0400L /* 12 attributes for hilite_pet */ -#define WC2_GUICOLOR 0x0800L /* 13 display colours outside map win */ +#define WC2_TERM_SIZE 0x0200L /* 10 support setting terminal size */ +#define WC2_WINDOWBORDERS 0x0400L /* 11 display borders on nh windows */ +#define WC2_PETATTR 0x0800L /* 12 attributes for hilite_pet */ +#define WC2_GUICOLOR 0x1000L /* 13 display colours outside map win */ /* 19 free bits */ #define ALIGN_LEFT 1 From ab9e28d1aaa7a009e5051dcbee6bbbccf62804e5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 4 Dec 2018 11:58:05 -0500 Subject: [PATCH 03/43] some fixes provided from Hardfought --- include/flag.h | 3 +- include/wincurs.h | 3 -- src/files.c | 9 +++++- src/options.c | 67 ++++++++++++++++++++++++++++++++++++++++++- sys/unix/Makefile.src | 8 ++++++ 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/include/flag.h b/include/flag.h index 0623ec526..e3d0a5143 100644 --- a/include/flag.h +++ b/include/flag.h @@ -322,12 +322,13 @@ struct instance_flags { #endif #endif uchar bouldersym; /* symbol for boulder display */ -#ifdef TTY_GRAPHICS +#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) char prevmsg_window; /* type of old message window to use */ #endif #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) boolean extmenu; /* extended commands use menu interface */ #endif + #ifdef MFLOPPY boolean checkspace; /* check disk space before writing files */ /* (in iflags to allow restore after moving diff --git a/include/wincurs.h b/include/wincurs.h index 55e7f1d22..be79256b7 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -259,9 +259,6 @@ extern void curses_del_menu(winid wid); extern void curses_status_init(void); extern void curses_status_update(int, genericptr_t, int, int, int, unsigned long *); -/* extern attr_t curses_color_attr(int nh_color, int bg_color); */ -/* extern void curses_update_stats(void); */ -/* extern void curses_decrement_highlight(void); */ /* cursinvt.c */ diff --git a/src/files.c b/src/files.c index 3dd137743..2fd0f89c1 100644 --- a/src/files.c +++ b/src/files.c @@ -3185,7 +3185,14 @@ int which_set; if (symset[which_set].name && (fuzzymatch(symset[which_set].name, "Default symbols", " -_", TRUE) - || !strcmpi(symset[which_set].name, "default"))) + || !strcmpi(symset[which_set].name, "default") +#ifdef CURSES_GRAPHICS + /* we don't maintain static symbols for curses + * the system defines these at runtime + */ + || !strcmpi(symset[which_set].name, "curses") +#endif + )) clear_symsetentry(which_set, TRUE); config_error_done(); return (symset[which_set].name == 0) ? 1 : 0; diff --git a/src/options.c b/src/options.c index 8be8cfcd3..51f0d6d8f 100644 --- a/src/options.c +++ b/src/options.c @@ -106,7 +106,7 @@ static struct Bool_Opt { #endif { "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME }, { "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME }, -#if defined(MICRO) || defined(WIN32) +#if defined(MICRO) || defined(WIN32) || defined(CURSES_GRAPHICS) { "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /*WC*/ #else /* systems that support multiple terminals, many monochrome */ { "color", &iflags.wc_color, FALSE, SET_IN_GAME }, /*WC*/ @@ -3626,6 +3626,71 @@ boolean tinitial, tfrom_file; } #endif + /* WINCAP2 + * term_cols:amount */ + fullname = "term_cols"; + if (match_optname(opts, fullname, sizeof("term_cols")-1, TRUE)) { + op = string_for_opt(opts, negated); + iflags.wc2_term_cols = atoi(op); + if (negated) bad_negation(fullname, FALSE); + return retval; + } + + /* WINCAP2 + * term_rows:amount */ + fullname = "term_rows"; + if (match_optname(opts, fullname, sizeof("term_rows")-1, TRUE)) { + op = string_for_opt(opts, negated); + iflags.wc2_term_rows = atoi(op); + if (negated) bad_negation(fullname, FALSE); + return retval; + } + + /* WINCAP2 + * petattr:string */ + fullname = "petattr"; + if (match_optname(opts, fullname, sizeof("petattr")-1, TRUE)) { + op = string_for_opt(opts, negated); + if (op && !negated) { +#ifdef CURSES_GRAPHICS + iflags.wc2_petattr = curses_read_attrs(op); + if (!curses_read_attrs(op)) + config_error_add("Unknown %s parameter '%s'", fullname, opts); + return FALSE; +#else + /* non-curses windowports will not use this flag anyway + * but the above will not compile if we don't have curses. + * Just set it to a sensible default: */ + iflags.wc2_petattr = ATR_INVERSE +#endif + } else if (negated) bad_negation(fullname, TRUE); + return retval; + } + + /* WINCAP2 + * windowborders:n */ + fullname = "windowborders"; + if (match_optname(opts, fullname, sizeof("windowborders")-1, TRUE)) { + op = string_for_opt(opts, negated); + if (negated && op) bad_negation(fullname, TRUE); + else { + if (negated) + iflags.wc2_windowborders = 2; /* Off */ + else if (!op) + iflags.wc2_windowborders = 1; /* On */ + else /* Value supplied */ + iflags.wc2_windowborders = atoi(op); + if ((iflags.wc2_windowborders > 3) || + (iflags.wc2_windowborders < 1)) { + iflags.wc2_windowborders = 0; + config_error_add("Unknown %s parameter '%s'", fullname, opts); + return FALSE; + + } + } + return retval; + } + /* menustyle:traditional or combination or full or partial */ fullname = "menustyle"; if (match_optname(opts, fullname, 4, TRUE)) { diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 07d5d5920..38523fd36 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -198,6 +198,14 @@ REGEXOBJ = posixregex.o WINTTYSRC = ../win/tty/getline.c ../win/tty/termcap.c ../win/tty/topl.c \ ../win/tty/wintty.c WINTTYOBJ = getline.o termcap.o topl.o wintty.o +# Files for curses interface +WINCURSESSRC = ../win/curses/cursmain.c ../win/curses/curswins.c \ + ../win/curses/cursmisc.c ../win/curses/cursdial.c \ + ../win/curses/cursstat.c ../win/curses/cursinit.c \ + ../win/curses/cursmesg.c ../win/curses/cursinvt.c +WINCURSESOBJ = cursmain.o curswins.o cursmisc.o cursdial.o cursstat.o \ + cursinit.o cursmesg.o cursinvt.o + # # Files for curses interface WINCURSESSRC = ../win/curses/cursmain.c ../win/curses/curswins.c \ From dc2539701a05e1cb90362dd45e9f576eee4a21c2 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 4 Dec 2018 11:59:41 -0500 Subject: [PATCH 04/43] Revert "some fixes provided from Hardfought" This reverts commit ab9e28d1aaa7a009e5051dcbee6bbbccf62804e5. --- include/flag.h | 3 +- include/wincurs.h | 3 ++ src/files.c | 9 +----- src/options.c | 67 +------------------------------------------ sys/unix/Makefile.src | 8 ------ 5 files changed, 6 insertions(+), 84 deletions(-) diff --git a/include/flag.h b/include/flag.h index e3d0a5143..0623ec526 100644 --- a/include/flag.h +++ b/include/flag.h @@ -322,13 +322,12 @@ struct instance_flags { #endif #endif uchar bouldersym; /* symbol for boulder display */ -#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) +#ifdef TTY_GRAPHICS char prevmsg_window; /* type of old message window to use */ #endif #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) boolean extmenu; /* extended commands use menu interface */ #endif - #ifdef MFLOPPY boolean checkspace; /* check disk space before writing files */ /* (in iflags to allow restore after moving diff --git a/include/wincurs.h b/include/wincurs.h index be79256b7..55e7f1d22 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -259,6 +259,9 @@ extern void curses_del_menu(winid wid); extern void curses_status_init(void); extern void curses_status_update(int, genericptr_t, int, int, int, unsigned long *); +/* extern attr_t curses_color_attr(int nh_color, int bg_color); */ +/* extern void curses_update_stats(void); */ +/* extern void curses_decrement_highlight(void); */ /* cursinvt.c */ diff --git a/src/files.c b/src/files.c index 2fd0f89c1..3dd137743 100644 --- a/src/files.c +++ b/src/files.c @@ -3185,14 +3185,7 @@ int which_set; if (symset[which_set].name && (fuzzymatch(symset[which_set].name, "Default symbols", " -_", TRUE) - || !strcmpi(symset[which_set].name, "default") -#ifdef CURSES_GRAPHICS - /* we don't maintain static symbols for curses - * the system defines these at runtime - */ - || !strcmpi(symset[which_set].name, "curses") -#endif - )) + || !strcmpi(symset[which_set].name, "default"))) clear_symsetentry(which_set, TRUE); config_error_done(); return (symset[which_set].name == 0) ? 1 : 0; diff --git a/src/options.c b/src/options.c index 51f0d6d8f..8be8cfcd3 100644 --- a/src/options.c +++ b/src/options.c @@ -106,7 +106,7 @@ static struct Bool_Opt { #endif { "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME }, { "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME }, -#if defined(MICRO) || defined(WIN32) || defined(CURSES_GRAPHICS) +#if defined(MICRO) || defined(WIN32) { "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /*WC*/ #else /* systems that support multiple terminals, many monochrome */ { "color", &iflags.wc_color, FALSE, SET_IN_GAME }, /*WC*/ @@ -3626,71 +3626,6 @@ boolean tinitial, tfrom_file; } #endif - /* WINCAP2 - * term_cols:amount */ - fullname = "term_cols"; - if (match_optname(opts, fullname, sizeof("term_cols")-1, TRUE)) { - op = string_for_opt(opts, negated); - iflags.wc2_term_cols = atoi(op); - if (negated) bad_negation(fullname, FALSE); - return retval; - } - - /* WINCAP2 - * term_rows:amount */ - fullname = "term_rows"; - if (match_optname(opts, fullname, sizeof("term_rows")-1, TRUE)) { - op = string_for_opt(opts, negated); - iflags.wc2_term_rows = atoi(op); - if (negated) bad_negation(fullname, FALSE); - return retval; - } - - /* WINCAP2 - * petattr:string */ - fullname = "petattr"; - if (match_optname(opts, fullname, sizeof("petattr")-1, TRUE)) { - op = string_for_opt(opts, negated); - if (op && !negated) { -#ifdef CURSES_GRAPHICS - iflags.wc2_petattr = curses_read_attrs(op); - if (!curses_read_attrs(op)) - config_error_add("Unknown %s parameter '%s'", fullname, opts); - return FALSE; -#else - /* non-curses windowports will not use this flag anyway - * but the above will not compile if we don't have curses. - * Just set it to a sensible default: */ - iflags.wc2_petattr = ATR_INVERSE -#endif - } else if (negated) bad_negation(fullname, TRUE); - return retval; - } - - /* WINCAP2 - * windowborders:n */ - fullname = "windowborders"; - if (match_optname(opts, fullname, sizeof("windowborders")-1, TRUE)) { - op = string_for_opt(opts, negated); - if (negated && op) bad_negation(fullname, TRUE); - else { - if (negated) - iflags.wc2_windowborders = 2; /* Off */ - else if (!op) - iflags.wc2_windowborders = 1; /* On */ - else /* Value supplied */ - iflags.wc2_windowborders = atoi(op); - if ((iflags.wc2_windowborders > 3) || - (iflags.wc2_windowborders < 1)) { - iflags.wc2_windowborders = 0; - config_error_add("Unknown %s parameter '%s'", fullname, opts); - return FALSE; - - } - } - return retval; - } - /* menustyle:traditional or combination or full or partial */ fullname = "menustyle"; if (match_optname(opts, fullname, 4, TRUE)) { diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 38523fd36..07d5d5920 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -198,14 +198,6 @@ REGEXOBJ = posixregex.o WINTTYSRC = ../win/tty/getline.c ../win/tty/termcap.c ../win/tty/topl.c \ ../win/tty/wintty.c WINTTYOBJ = getline.o termcap.o topl.o wintty.o -# Files for curses interface -WINCURSESSRC = ../win/curses/cursmain.c ../win/curses/curswins.c \ - ../win/curses/cursmisc.c ../win/curses/cursdial.c \ - ../win/curses/cursstat.c ../win/curses/cursinit.c \ - ../win/curses/cursmesg.c ../win/curses/cursinvt.c -WINCURSESOBJ = cursmain.o curswins.o cursmisc.o cursdial.o cursstat.o \ - cursinit.o cursmesg.o cursinvt.o - # # Files for curses interface WINCURSESSRC = ../win/curses/cursmain.c ../win/curses/curswins.c \ From 36c2aec2ffe413141dd402f51ee7516b6980cc57 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 4 Dec 2018 17:10:15 -0800 Subject: [PATCH 05/43] fix #H7667 - maybe_reset_pick(), other bad context When deciding whether to discard interrupted lock/unlock context while changing levels, maybe_reset_pick() checks whether xlock.box is being carried. But it was doing so after the old level had been saved and memory for non-carried container there had been freed. That led to a couple of other issues. context.travelcc was using -1 for 'no cached value', but the fields of travelcc have type 'xchar' and shouldn't be given negative values. 0 should be fine for 'no cache'. Failed partial restore which occurred after old game's context had been loaded would begin a new game with old game's stale context. Restoring goes out of its way to avoid that for 'flags' but didn't for 'context'. --- doc/fixes36.2 | 4 ++++ src/cmd.c | 4 ++-- src/do.c | 21 ++++++++++++--------- src/hack.c | 6 +++--- src/options.c | 4 +--- src/restore.c | 14 +++++++++++--- src/save.c | 27 ++++++--------------------- 7 files changed, 39 insertions(+), 41 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index f5649056c..4073cef20 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -238,6 +238,10 @@ glowing Sting quivers if hero becomes blind and quivering Sting glows if blindness ends; it worked for timed blindness but not for blindfold weapon (wielded pie, egg, potion, boomerang) might be destroyed when hitting a long worm, then freed memory was accessed to decide whether to cut it +level change after being interruped locking or unlocking a container might + access freed memory +if a restore attempt failed and a new game was started instead, it would use + stale context from old game if restoration got far enough to load that Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/cmd.c b/src/cmd.c index 92b2fc0d2..8924923da 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 cmd.c $NHDT-Date: 1543797825 2018/12/03 00:43:45 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.312 $ */ +/* NetHack 3.6 cmd.c $NHDT-Date: 1543972186 2018/12/05 01:09:46 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.313 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -5694,7 +5694,7 @@ dotravel(VOID_ARGS) cmd[1] = 0; cc.x = iflags.travelcc.x; cc.y = iflags.travelcc.y; - if (cc.x == -1 && cc.y == -1) { + if (cc.x == 0 && cc.y == 0) { /* No cached destination, start attempt from current position */ cc.x = u.ux; cc.y = u.uy; diff --git a/src/do.c b/src/do.c index 4817d150a..febfcf9b5 100644 --- a/src/do.c +++ b/src/do.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do.c $NHDT-Date: 1543052696 2018/11/24 09:44:56 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.175 $ */ +/* NetHack 3.6 do.c $NHDT-Date: 1543972190 2018/12/05 01:09:50 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.176 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1237,6 +1237,17 @@ boolean at_stairs, falling, portal; if (fd < 0) return; + /* discard context which applies to the level we're leaving; + for lock-picking, container may be carried, in which case we + keep context; if on the floor, it's about to be saved+freed and + maybe_reset_pick() needs to do its carried() check before that */ + maybe_reset_pick(); + reset_trapset(); /* even if to-be-armed trap obj is accompanying hero */ + iflags.travelcc.x = iflags.travelcc.y = 0; /* travel destination cache */ + context.polearm.hitmon = (struct monst *) 0; /* polearm target */ + /* digging context is level-aware and can actually be resumed if + hero returns to the previous level without any intervening dig */ + if (falling) /* assuming this is only trap door or hole */ impact_drop((struct obj *) 0, u.ux, u.uy, newlevel->dlevel); @@ -1576,14 +1587,6 @@ boolean at_stairs, falling, portal; /* assume this will always return TRUE when changing level */ (void) in_out_region(u.ux, u.uy); (void) pickup(1); - - /* discard context which applied to previous level */ - maybe_reset_pick(); /* for door or for box not accompanying hero */ - reset_trapset(); /* even if to-be-armed trap obj is accompanying hero */ - iflags.travelcc.x = iflags.travelcc.y = -1; /* travel destination cache */ - context.polearm.hitmon = (struct monst *) 0; /* polearm target */ - /* digging context is level-aware and can actually be resumed if - hero returns to the previous level without any intervening dig */ } STATIC_OVL void diff --git a/src/hack.c b/src/hack.c index 7a5e48116..c0c0313f2 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 hack.c $NHDT-Date: 1540591769 2018/10/26 22:09:29 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.194 $ */ +/* NetHack 3.6 hack.c $NHDT-Date: 1543972190 2018/12/05 01:09:50 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.200 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -923,7 +923,7 @@ int mode; u.dx = u.tx - u.ux; u.dy = u.ty - u.uy; nomul(0); - iflags.travelcc.x = iflags.travelcc.y = -1; + iflags.travelcc.x = iflags.travelcc.y = 0; } return TRUE; } @@ -1045,7 +1045,7 @@ int mode; nomul(0); /* reset run so domove run checks work */ context.run = 8; - iflags.travelcc.x = iflags.travelcc.y = -1; + iflags.travelcc.x = iflags.travelcc.y = 0; } return TRUE; } diff --git a/src/options.c b/src/options.c index 8be8cfcd3..d4b7daf4d 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1543395749 2018/11/28 09:02:29 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.334 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1543972192 2018/12/05 01:09:52 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.335 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -747,8 +747,6 @@ initoptions_init() warnsyms[i] = def_warnsyms[i].sym; iflags.bouldersym = 0; - iflags.travelcc.x = iflags.travelcc.y = -1; - /* for "special achievement" tracking (see obj.h, create_object(sp_lev.c), addinv_core1(invent.c) */ iflags.mines_prize_type = LUCKSTONE; diff --git a/src/restore.c b/src/restore.c index 25604f10d..e9f25d72c 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 restore.c $NHDT-Date: 1542798626 2018/11/21 11:10:26 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.109 $ */ +/* NetHack 3.6 restore.c $NHDT-Date: 1543972193 2018/12/05 01:09:53 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.128 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -539,6 +539,7 @@ unsigned int *stuckid, *steedid; #ifdef SYSFLAGS struct sysflag newgamesysflags; #endif + struct context_info newgamecontext; /* all 0, but has some pointers */ struct obj *otmp, *tmp_bc; char timebuf[15]; unsigned long uid; @@ -553,9 +554,15 @@ unsigned int *stuckid, *steedid; if (!wizard) return FALSE; } + + newgamecontext = context; /* copy statically init'd context */ mread(fd, (genericptr_t) &context, sizeof (struct context_info)); - if (context.warntype.speciesidx >= LOW_PM) - context.warntype.species = &mons[context.warntype.speciesidx]; + context.warntype.species = (context.warntype.speciesidx >= LOW_PM) + ? &mons[context.warntype.speciesidx] + : (struct permonst *) 0; + /* context.victual.piece, .tin.tin, .spellbook.book, and .polearm.hitmon + are pointers which get set to Null during save and will be recovered + via corresponding o_id or m_id while objs or mons are being restored */ /* we want to be able to revert to command line/environment/config file option values instead of keeping old save file option values @@ -625,6 +632,7 @@ unsigned int *stuckid, *steedid; #ifdef SYSFLAGS sysflags = newgamesysflags; #endif + context = newgamecontext; return FALSE; } /* in case hangup save occurred in midst of level change */ diff --git a/src/save.c b/src/save.c index 095890f05..18465966d 100644 --- a/src/save.c +++ b/src/save.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 save.c $NHDT-Date: 1489192905 2017/03/11 00:41:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.101 $ */ +/* NetHack 3.6 save.c $NHDT-Date: 1543972194 2018/12/05 01:09:54 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.115 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1035,37 +1035,22 @@ register struct obj *otmp; if (Has_contents(otmp)) saveobjchn(fd, otmp->cobj, mode); if (release_data(mode)) { - /* if (otmp->oclass == FOOD_CLASS) - * food_disappears(otmp); - */ /* - * If these are on the floor, the discarding could - * be because of a game save, or we could just be changing levels. + * If these are on the floor, the discarding could be + * due to game save, or we could just be changing levels. * Always invalidate the pointer, but ensure that we have * the o_id in order to restore the pointer on reload. */ if (otmp == context.victual.piece) { - /* Store the o_id of the victual if mismatched */ - if (context.victual.o_id != otmp->o_id) - context.victual.o_id = otmp->o_id; - /* invalidate the pointer; on reload it will get restored */ + context.victual.o_id = otmp->o_id; context.victual.piece = (struct obj *) 0; } if (otmp == context.tin.tin) { - /* Store the o_id of your tin */ - if (context.tin.o_id != otmp->o_id) - context.tin.o_id = otmp->o_id; - /* invalidate the pointer; on reload it will get restored */ + context.tin.o_id = otmp->o_id; context.tin.tin = (struct obj *) 0; } - /* if (otmp->oclass == SPBOOK_CLASS) - * book_disappears(otmp); - */ if (otmp == context.spbook.book) { - /* Store the o_id of your spellbook */ - if (context.spbook.o_id != otmp->o_id) - context.spbook.o_id = otmp->o_id; - /* invalidate the pointer; on reload it will get restored */ + context.spbook.o_id = otmp->o_id; context.spbook.book = (struct obj *) 0; } otmp->where = OBJ_FREE; /* set to free so dealloc will work */ From 37b7243c57af7465f8526bf1bf9d85b9b7f5875b Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 4 Dec 2018 22:22:43 -0500 Subject: [PATCH 06/43] some updates to curses Tangles updates --- include/flag.h | 4 +--- include/wincurs.h | 3 --- src/options.c | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/include/flag.h b/include/flag.h index 0623ec526..a459c5bb9 100644 --- a/include/flag.h +++ b/include/flag.h @@ -322,10 +322,8 @@ struct instance_flags { #endif #endif uchar bouldersym; /* symbol for boulder display */ -#ifdef TTY_GRAPHICS - char prevmsg_window; /* type of old message window to use */ -#endif #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) + char prevmsg_window; /* type of old message window to use */ boolean extmenu; /* extended commands use menu interface */ #endif #ifdef MFLOPPY diff --git a/include/wincurs.h b/include/wincurs.h index 55e7f1d22..be79256b7 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -259,9 +259,6 @@ extern void curses_del_menu(winid wid); extern void curses_status_init(void); extern void curses_status_update(int, genericptr_t, int, int, int, unsigned long *); -/* extern attr_t curses_color_attr(int nh_color, int bg_color); */ -/* extern void curses_update_stats(void); */ -/* extern void curses_decrement_highlight(void); */ /* cursinvt.c */ diff --git a/src/options.c b/src/options.c index d4b7daf4d..7dc622687 100644 --- a/src/options.c +++ b/src/options.c @@ -106,7 +106,7 @@ static struct Bool_Opt { #endif { "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME }, { "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME }, -#if defined(MICRO) || defined(WIN32) +#if defined(MICRO) || defined(WIN32) || defined(CURSES_GRAPHICS) { "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /*WC*/ #else /* systems that support multiple terminals, many monochrome */ { "color", &iflags.wc_color, FALSE, SET_IN_GAME }, /*WC*/ From 95a6c458bc9bbc2b76d6700f1f2269b4eb4f811e Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 5 Dec 2018 01:45:16 -0800 Subject: [PATCH 07/43] fix #H6292 - green slime & genocide Report suggested that if hero is turning into green slime, genociding green slime should cure it. I went another direction: if life-saved while dying due to turning into green slime, you survive polymorphed into green slime form. If green slimes have been genocided (probably after becoming infected with slime or hero wouldn't have faced any slimes to cause infection, but that could be from eating a glob of green slime created prior to genocide, or from #wizintrinsic), you'll immediately die again, this time from genocide. --- doc/fixes36.2 | 1 + src/end.c | 16 +++++++++---- src/timeout.c | 66 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 4073cef20..f87856d44 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -242,6 +242,7 @@ level change after being interruped locking or unlocking a container might access freed memory if a restore attempt failed and a new game was started instead, it would use stale context from old game if restoration got far enough to load that +if hero survives turning into slime (life-saving), survive as a green slime Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/end.c b/src/end.c index a433acc07..cbc312598 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1542798619 2018/11/21 11:10:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.152 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1544003110 2018/12/05 09:45:10 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.156 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1184,7 +1184,12 @@ int how; u.ugrave_arise = (NON_PM - 2); /* leave no corpse */ else if (how == STONING) u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */ - else if (how == TURNED_SLIME) + else if (how == TURNED_SLIME + /* it's possible to turn into slime even though green slimes + have been genocided: genocide could occur after hero is + already infected or hero could eat a glob of one created + before genocide; don't try to arise as one if they're gone */ + && !(mvitals[PM_GREEN_SLIME].mvflags & G_GENOD)) u.ugrave_arise = PM_GREEN_SLIME; if (how == QUIT) { @@ -1310,11 +1315,14 @@ int how; } } - if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) { + if (u.ugrave_arise >= LOW_PM) { /* give this feedback even if bones aren't going to be created, so that its presence or absence doesn't tip off the player to new bones or their lack; it might be a lie if makemon fails */ - Your("body rises from the dead as %s...", + Your("%s as %s...", + (u.ugrave_arise != PM_GREEN_SLIME) + ? "body rises from the dead" + : "revenant persists", an(mons[u.ugrave_arise].mname)); display_nhwindow(WIN_MESSAGE, FALSE); } diff --git a/src/timeout.c b/src/timeout.c index 110aa03ad..e4347c5b3 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 timeout.c $NHDT-Date: 1541902953 2018/11/11 02:22:33 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.84 $ */ +/* NetHack 3.6 timeout.c $NHDT-Date: 1544003111 2018/12/05 09:45:11 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.87 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -11,6 +11,7 @@ STATIC_DCL void NDECL(vomiting_dialogue); STATIC_DCL void NDECL(choke_dialogue); STATIC_DCL void NDECL(levitation_dialogue); STATIC_DCL void NDECL(slime_dialogue); +STATIC_DCL void FDECL(slimed_to_death, (struct kinfo *)); STATIC_DCL void NDECL(slip_or_trip); STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *)); STATIC_DCL void FDECL(lantern_message, (struct obj *)); @@ -357,6 +358,57 @@ burn_away_slime() } } +/* countdown timer for turning into green slime has run out; kill our hero */ +STATIC_OVL void +slimed_to_death(kptr) +struct kinfo *kptr; +{ + /* redundant: polymon() cures sliming when polying into green slime */ + if (Upolyd && youmonst.data == &mons[PM_GREEN_SLIME]) { + dealloc_killer(kptr); + return; + } + /* more sure killer reason is set up */ + if (kptr && kptr->name[0]) { + killer.format = kptr->format; + Strcpy(killer.name, kptr->name); + } else { + killer.format = NO_KILLER_PREFIX; + Strcpy(killer.name, "turned into green slime"); + } + dealloc_killer(kptr); + /* involuntarily break "never changed form" conduct */ + u.uconduct.polyselfs++; + done(TURNED_SLIME); + + /* life-saved; even so, hero still has turned into green slime; + player may have genocided green slimes after being infected */ + if ((mvitals[PM_GREEN_SLIME].mvflags & G_GENOD) != 0) { + killer.format = KILLED_BY; + Strcpy(killer.name, "slimicide"); + /* immediately follows "OK, so you don't die." */ + pline("Yes, you do. Green slime has been genocided..."); + done(GENOCIDED); + /* could be life-saved again (only in explore or wizard mode) + but green slimes are gone; just stay in current form */ + + /* not geno'd; survive as a green slime */ + } else { + /* this part of polyself() isn't in polymon(); + we assume that green slimes don't emit light */ + if (emits_light(youmonst.data)) + del_light_source(LS_MONSTER, monst_to_any(&youmonst)); + /* undo the 'involuntarily break "never changed form"' + increment so that this change isn't counted twice */ + u.uconduct.polyselfs--; + /* can't be Unchanging even if life-saving wasn't due to amulet; + hero infected with slime wouldn't have turned into green slime + to get here if Unchanging */ + (void) polymon(PM_GREEN_SLIME); + } + return; +} + /* Intrinsic Passes_walls is temporary when your god is trying to fix all troubles and then TROUBLE_STUCK_IN_WALL calls safe_teleds() but it can't find anywhere to place you. If that happens you get a small @@ -469,17 +521,7 @@ nh_timeout() done(STONING); break; case SLIMED: - if (kptr && kptr->name[0]) { - killer.format = kptr->format; - Strcpy(killer.name, kptr->name); - } else { - killer.format = NO_KILLER_PREFIX; - Strcpy(killer.name, "turned into green slime"); - } - dealloc_killer(kptr); - /* involuntarily break "never changed form" conduct */ - u.uconduct.polyselfs++; - done(TURNED_SLIME); + slimed_to_death(kptr); /* done(TURNED_SLIME) */ break; case VOMITING: make_vomiting(0L, TRUE); From 6e0a9cad5a5b46f5eaf98a68362af06a9e81c5fc Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 5 Dec 2018 22:35:59 +0200 Subject: [PATCH 08/43] Fake leash object is not attached When farlooking at a fake leash object, make it explicitly not be attached to a monster. --- src/pager.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pager.c b/src/pager.c index e9fe016ed..22df977e4 100644 --- a/src/pager.c +++ b/src/pager.c @@ -187,6 +187,8 @@ struct obj **obj_p; otmp->quan = 2L; /* to force pluralization */ else if (otmp->otyp == SLIME_MOLD) otmp->spe = context.current_fruit; /* give it a type */ + else if (otmp->otyp == LEASH) + otmp->leashmon = 0; if (mtmp && has_mcorpsenm(mtmp)) /* mimic as corpse/statue */ otmp->corpsenm = MCORPSENM(mtmp); else if (otmp->otyp == CORPSE && glyph_is_body(glyph)) From f020595fb7cf993067047a43c528e1709f571c52 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 5 Dec 2018 22:39:11 +0200 Subject: [PATCH 09/43] Valid cursor locations showed null When getting a cursor location, and there was no "valid" location function defined, trying to go to the next or previous valid location showed null. Fix this by using the "interesting" locations if no valid ones. --- src/do_name.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/do_name.c b/src/do_name.c index c1210bbd9..38665135e 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -378,6 +378,10 @@ int x, y, gloc; || IS_UNEXPLORED_LOC(x - 1, y) || IS_UNEXPLORED_LOC(x, y + 1) || IS_UNEXPLORED_LOC(x, y - 1))); + case GLOC_VALID: + if (getpos_getvalid) + return (getpos_getvalid(x,y)); + /*FALLTHRU*/ case GLOC_INTERESTING: return gather_locs_interesting(x,y, GLOC_DOOR) || !(glyph_is_cmap(glyph) @@ -395,8 +399,6 @@ int x, y, gloc; || glyph_to_cmap(glyph) == S_darkroom || glyph_to_cmap(glyph) == S_corr || glyph_to_cmap(glyph) == S_litcorr)); - case GLOC_VALID: - return (getpos_getvalid && getpos_getvalid(x,y)); } /*NOTREACHED*/ return FALSE; From 857b8af2eec85ae7fccbdc1bf9cd637ce7b36f09 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 5 Dec 2018 23:15:14 +0200 Subject: [PATCH 10/43] Add missing valid location descriptors --- src/do_name.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/do_name.c b/src/do_name.c index 38665135e..01feca7e7 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -57,7 +57,9 @@ static const char *const gloc_descr[NUM_GLOCS][4] = { { "any unexplored areas", "unexplored area", "unexplored location", "unexplored locations" }, { "anything interesting", "interesting thing", "anything interesting", - "anything interesting" } + "anything interesting" }, + { "any valid locations", "valid location", "valid location", + "valid locations" } }; static const char *const gloc_filtertxt[NUM_GFILTER] = { From c258f9c3f83008ca46e55ce343ff44c660d6a26a Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:40:33 -0500 Subject: [PATCH 11/43] update header on cursdial --- win/curses/cursdial.c | 2 +- win/curses/cursdial.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index 99e5eb830..2e63b6f82 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursdial.c */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #include "curses.h" #include "hack.h" diff --git a/win/curses/cursdial.h b/win/curses/cursdial.h index dea396178..9c15a0775 100644 --- a/win/curses/cursdial.h +++ b/win/curses/cursdial.h @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursdial.h */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #ifndef CURSDIAL_H # define CURSDIAL_H From 14335ea90c66609fc9a0555d577a3e309af51baa Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:41:30 -0500 Subject: [PATCH 12/43] update header on cursinit --- win/curses/cursinit.c | 2 +- win/curses/cursinit.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/win/curses/cursinit.c b/win/curses/cursinit.c index f787bf1a0..04adbb853 100644 --- a/win/curses/cursinit.c +++ b/win/curses/cursinit.c @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursinit.c */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #include "curses.h" #include "hack.h" diff --git a/win/curses/cursinit.h b/win/curses/cursinit.h index ef4e232fb..c611d59f6 100644 --- a/win/curses/cursinit.h +++ b/win/curses/cursinit.h @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursinit.h */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #ifndef CURSINIT_H # define CURSINIT_H From 86281c5555d91e94d32f6b9c12301de5a1cdd5c8 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:42:11 -0500 Subject: [PATCH 13/43] update header on cursinvt --- win/curses/cursinvt.c | 6 +++--- win/curses/cursinvt.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c index aa9970caf..ebf04a4f4 100644 --- a/win/curses/cursinvt.c +++ b/win/curses/cursinvt.c @@ -1,7 +1,7 @@ -/* NetHack 3.6 cursinvt.c */ -/* Copyright (c) Karl Garrison, 2010. */ -/* NetHack may be freely redistributed. See license for details. */ /* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ +/* NetHack 3.6 cursinvt.c */ +/* Copyright (c) Fredrik Ljungdahl, 2017. */ +/* NetHack may be freely redistributed. See license for details. */ #include "curses.h" #include "hack.h" diff --git a/win/curses/cursinvt.h b/win/curses/cursinvt.h index 9d9d65cb2..db2df6586 100644 --- a/win/curses/cursinvt.h +++ b/win/curses/cursinvt.h @@ -1,7 +1,7 @@ -/* NetHack 3.6 cursinvt.h */ -/* Copyright (c) Karl Garrison, 2010. */ -/* NetHack may be freely redistributed. See license for details. */ /* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ +/* NetHack 3.6 cursinvt.h */ +/* Copyright (c) Fredrik Ljungdahl, 2017. */ +/* NetHack may be freely redistributed. See license for details. */ #ifndef CURSINVT_H # define CURSINVT_H From 661ffb0133924f0777e1a97b47d60b0be95d5f81 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:42:49 -0500 Subject: [PATCH 14/43] update header on cursmain --- win/curses/cursmain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 2412def4b..62abc71f1 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursmain.c */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #include "curses.h" #include "hack.h" From c260911f38deaa1bb7c966579234acf81a6d7293 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:43:33 -0500 Subject: [PATCH 15/43] update header on cursmesg --- win/curses/cursmesg.c | 2 +- win/curses/cursmesg.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index ef351a27a..dd0f6e349 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursmesg.c */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #include "curses.h" #include "hack.h" diff --git a/win/curses/cursmesg.h b/win/curses/cursmesg.h index 588bf883b..f6002356d 100644 --- a/win/curses/cursmesg.h +++ b/win/curses/cursmesg.h @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursmesg.h */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #ifndef CURSMESG_H # define CURSMESG_H From 2458281d425d4c25a521c07958397ad8a0c13235 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:44:08 -0500 Subject: [PATCH 16/43] update header on cursmisc --- win/curses/cursmisc.c | 2 +- win/curses/cursmisc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/win/curses/cursmisc.c b/win/curses/cursmisc.c index 8ee1f68fe..0c47d775d 100644 --- a/win/curses/cursmisc.c +++ b/win/curses/cursmisc.c @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursmisc.c */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #include "curses.h" #include "hack.h" diff --git a/win/curses/cursmisc.h b/win/curses/cursmisc.h index 4d0f78003..364901267 100644 --- a/win/curses/cursmisc.h +++ b/win/curses/cursmisc.h @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursmisc.h */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #ifndef CURSMISC_H # define CURSMISC_H From 9c873712b6ebc5a40826d76781cd94c16700978f Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:45:15 -0500 Subject: [PATCH 17/43] update header on cursstat --- win/curses/cursstat.c | 6 +++--- win/curses/cursstat.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index b2e45f28b..4c901736f 100644 --- a/win/curses/cursstat.c +++ b/win/curses/cursstat.c @@ -1,7 +1,7 @@ -/* NetHack 3.6 cursstat.c */ -/* Copyright (c) Karl Garrison, 2010. */ -/* NetHack may be freely redistributed. See license for details. */ /* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ +/* NetHack 3.6 cursstat.c */ +/* Copyright (c) Karl Garrison, 2010. */ +/* NetHack may be freely redistributed. See license for details. */ #include /* toupper() */ #include "curses.h" diff --git a/win/curses/cursstat.h b/win/curses/cursstat.h index 8b8120545..e10977bb2 100644 --- a/win/curses/cursstat.h +++ b/win/curses/cursstat.h @@ -1,7 +1,7 @@ -/* NetHack 3.6 cursstat.c */ -/* Copyright (c) Karl Garrison, 2010. */ -/* NetHack may be freely redistributed. See license for details. */ /* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ +/* NetHack 3.6 cursstat.h */ +/* Copyright (c) Karl Garrison, 2010. */ +/* NetHack may be freely redistributed. See license for details. */ #ifndef CURSSTAT_H # define CURSSTAT_H From c4930c678207372bb036a2cb9b2c906828b03104 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 17:45:40 -0500 Subject: [PATCH 18/43] update header on curswins --- win/curses/curswins.c | 2 +- win/curses/curswins.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/win/curses/curswins.c b/win/curses/curswins.c index 017450830..c74a39871 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 curswins.c */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #include "curses.h" #include "hack.h" diff --git a/win/curses/curswins.h b/win/curses/curswins.h index 8a5c00fd6..dfa7b7a1e 100644 --- a/win/curses/curswins.h +++ b/win/curses/curswins.h @@ -1,7 +1,7 @@ +/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 curswins.h */ /* Copyright (c) Karl Garrison, 2010. */ /* NetHack may be freely redistributed. See license for details. */ -/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ #ifndef CURSWIN_H # define CURSWIN_H From e5b232104eff331136382509acd061c0ad2f846e Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 5 Dec 2018 14:56:03 -0800 Subject: [PATCH 19/43] more green slime When a hero dies due to turning into green slime, actually polymorph him into a green slime monster before killing him off. That way he'll show as a green 'P' on the map instead of white '@' during final disclosure. Also, armor that gets destroyed by polymorphing into that form will be absent from resulting bones file. --- src/cmd.c | 9 +++++++-- src/timeout.c | 46 +++++++++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index 8924923da..67170cec7 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 cmd.c $NHDT-Date: 1543972186 2018/12/05 01:09:46 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.313 $ */ +/* NetHack 3.6 cmd.c $NHDT-Date: 1544050555 2018/12/05 22:55:55 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.314 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2740,7 +2740,12 @@ int final; } if (Polymorph_control) you_have("polymorph control", from_what(POLYMORPH_CONTROL)); - if (Upolyd && u.umonnum != u.ulycn) { + if (Upolyd && u.umonnum != u.ulycn + /* if we've died from turning into slime, we're polymorphed + right now but don't want to list it as a temporary attribute + [we need a more reliable way to detect this situation] */ + && !(final == ENL_GAMEOVERDEAD + && u.umonnum == PM_GREEN_SLIME && !Unchanging)) { /* foreign shape (except were-form which is handled below) */ Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname)); if (wizard) diff --git a/src/timeout.c b/src/timeout.c index e4347c5b3..29b740fcc 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 timeout.c $NHDT-Date: 1544003111 2018/12/05 09:45:11 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.87 $ */ +/* NetHack 3.6 timeout.c $NHDT-Date: 1544050558 2018/12/05 22:55:58 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.88 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -311,6 +311,13 @@ slime_dialogue() { register long i = (Slimed & TIMEOUT) / 2L; + if (i == 1L) { + /* display as green slime during "You have become green slime." + but don't worry about not being able to see self; if already + mimicking something else at the time, implicitly be revealed */ + youmonst.m_ap_type = M_AP_MONSTER; + youmonst.mappearance = PM_GREEN_SLIME; + } if (((Slimed & TIMEOUT) % 2L) && i >= 0L && i < SIZE(slime_texts)) { char buf[BUFSZ]; @@ -363,6 +370,8 @@ STATIC_OVL void slimed_to_death(kptr) struct kinfo *kptr; { + uchar save_mvflags; + /* redundant: polymon() cures sliming when polying into green slime */ if (Upolyd && youmonst.data == &mons[PM_GREEN_SLIME]) { dealloc_killer(kptr); @@ -377,8 +386,25 @@ struct kinfo *kptr; Strcpy(killer.name, "turned into green slime"); } dealloc_killer(kptr); - /* involuntarily break "never changed form" conduct */ - u.uconduct.polyselfs++; + + /* + * Polymorph into a green slime, which might destroy some worn armor + * (potentially affecting bones) and dismount from steed. + * Can't be Unchanging; wouldn't have turned into slime if we were. + * Despite lack of Unchanging, neither done() nor savelife() calls + * rehumanize() if hero dies while polymorphed. + * polymon() undoes the slime countdown's mimick-green-slime hack + * but does not perform polyself()'s light source bookkeeping. + * No longer need to manually increment uconduct.polyselfs to reflect + * [formerly implicit] change of form; polymon() takes care of that. + * Temporarily ungenocide if necessary. + */ + if (emits_light(youmonst.data)) + del_light_source(LS_MONSTER, monst_to_any(&youmonst)); + save_mvflags = mvitals[PM_GREEN_SLIME].mvflags; + mvitals[PM_GREEN_SLIME].mvflags = save_mvflags & ~G_GENOD; + (void) polymon(PM_GREEN_SLIME); + mvitals[PM_GREEN_SLIME].mvflags = save_mvflags; done(TURNED_SLIME); /* life-saved; even so, hero still has turned into green slime; @@ -391,20 +417,6 @@ struct kinfo *kptr; done(GENOCIDED); /* could be life-saved again (only in explore or wizard mode) but green slimes are gone; just stay in current form */ - - /* not geno'd; survive as a green slime */ - } else { - /* this part of polyself() isn't in polymon(); - we assume that green slimes don't emit light */ - if (emits_light(youmonst.data)) - del_light_source(LS_MONSTER, monst_to_any(&youmonst)); - /* undo the 'involuntarily break "never changed form"' - increment so that this change isn't counted twice */ - u.uconduct.polyselfs--; - /* can't be Unchanging even if life-saving wasn't due to amulet; - hero infected with slime wouldn't have turned into green slime - to get here if Unchanging */ - (void) polymon(PM_GREEN_SLIME); } return; } From 1baa20dfc5fbbfc2c16e11d947794129639a4a1f Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 5 Dec 2018 23:43:59 -0500 Subject: [PATCH 20/43] header update on cursstat.c --- win/curses/cursstat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index 4c901736f..a4f708163 100644 --- a/win/curses/cursstat.c +++ b/win/curses/cursstat.c @@ -1,6 +1,6 @@ /* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/ /* NetHack 3.6 cursstat.c */ -/* Copyright (c) Karl Garrison, 2010. */ +/* Copyright (c) Andy Thomson, 2018. */ /* NetHack may be freely redistributed. See license for details. */ #include /* toupper() */ From e9624f25837e1d6c4ee6b0e0b873b0b3db14b71b Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 6 Dec 2018 17:27:36 -0800 Subject: [PATCH 21/43] fix #H7686 - destroy_item()'s inventory traversal Inventory traversal can be disrupted when items being traversed are able to change inventory. I've lost track of how many times this sort of thing has been discovered. Report claimed that boiled potion of polymorph caused transformation which resulted in dropped weapon and dropped or destroyed worn armor. That was evidently a guess; potionbreathe() for that potion only abuses constitution. The traceback showed 'you_were()' was involved. Boiled potion of unholy water triggers human-to-beast transformation of hero inflicted with lycanthropy, yielding similar situation. I didn't notice anything unusual when reproducing this but inventory was definitely vulnerable. My 'one line' fixes entries are steadily getting to be more verbose; I may have to go back to 'fix bug'. :-} --- doc/fixes36.2 | 8 ++++++++ src/zap.c | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index f87856d44..7f0673f39 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -243,6 +243,14 @@ level change after being interruped locking or unlocking a container might if a restore attempt failed and a new game was started instead, it would use stale context from old game if restoration got far enough to load that if hero survives turning into slime (life-saving), survive as a green slime +hero hit by something that causes inventory items to be destroyed with loss of + any of those causing other inventory items to be dropped or destroyed, + inventory traversal became unreliable (known sequence: potions hit by + fire then breahing vapor from boiled unholy water triggering were + transformation to beast form; possible sequence: ring of levitation + blasted by lightning and dropping hero onto fire trap); [3.6.1 fixed a + similar problem with more obvious symptom, an "object lost" panic when + the unholy water was wielded; the fix for that wasn't general enough] Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/zap.c b/src/zap.c index 5a5a67c34..8f7adf338 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 zap.c $NHDT-Date: 1543744276 2018/12/02 09:51:16 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.299 $ */ +/* NetHack 3.6 zap.c $NHDT-Date: 1544146046 2018/12/07 01:27:26 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.300 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -4719,15 +4719,32 @@ void destroy_item(osym, dmgtyp) register int osym, dmgtyp; { - register struct obj *obj, *obj2; + register struct obj *obj; int dmg, xresist, skip; long i, cnt, quan; int dindx; const char *mult; boolean physical_damage; - for (obj = invent; obj; obj = obj2) { - obj2 = obj->nobj; + /* + * Sometimes destroying an item can change inventory aside from the + * item itself (cited case was a potion of polymorph; when destroyed, + * potion_breathe() caused hero to transform and that resulted in + * destruction of some worn armor). Unlike other uses of the object + * bybass mechanism, destroy_item() can be called multiple times for + * same event. So we have to explicitly clear it before each use and + * hope no other section of code expects it to retain previous value. + * + * FIXME? Destruction of a ring of levitation could drop hero onto + * a fire trap which could destroy other items and we'll get called + * recursively. This should still work, but items beyond the ring + * which survive the fire will be marked as already processed by the + * inner call, so will always survive the remainder of the outer call + * instead of being subjected to original chance of destruction. + */ + bypass_objlist(invent, FALSE); /* clear bypass bit for invent */ + + while ((obj = nxt_unbypassed_obj(invent)) != 0) { physical_damage = FALSE; if (obj->oclass != osym) continue; /* test only objs of type osym */ @@ -4820,6 +4837,7 @@ register int osym, dmgtyp; skip++; break; } + if (!skip) { if (obj->in_use) --quan; /* one will be used up elsewhere */ @@ -4852,9 +4870,9 @@ register int osym, dmgtyp; for (i = 0; i < cnt; i++) useup(obj); if (dmg) { - if (xresist) + if (xresist) { You("aren't hurt!"); - else { + } else { const char *how = destroy_strings[dindx][2]; boolean one = (cnt == 1L); @@ -4877,7 +4895,7 @@ destroy_mitem(mtmp, osym, dmgtyp) struct monst *mtmp; int osym, dmgtyp; { - struct obj *obj, *obj2; + struct obj *obj; int skip, tmp = 0; long i, cnt, quan; int dindx; @@ -4889,8 +4907,11 @@ int osym, dmgtyp; } vis = canseemon(mtmp); - for (obj = mtmp->minvent; obj; obj = obj2) { - obj2 = obj->nobj; + + /* see destroy_item(); object destruction could disrupt inventory list */ + bypass_objlist(mtmp->minvent, FALSE); /* clear bypass bit for invent */ + + while ((obj = nxt_unbypassed_obj(mtmp->minvent)) != 0) { if (obj->oclass != osym) continue; /* test only objs of type osym */ skip = 0; From 150d01a9651e0f0c0f26aa8ecf40c553ce0d5ebe Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 6 Dec 2018 22:30:15 -0500 Subject: [PATCH 22/43] add MM_ASLEEP makemon() flag and honor it for fill_zoo --- doc/fixes36.2 | 1 + include/hack.h | 13 +++++++------ src/makemon.c | 23 +++++++++++++---------- src/mkroom.c | 2 +- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 7f0673f39..421792d38 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -251,6 +251,7 @@ hero hit by something that causes inventory items to be destroyed with loss of blasted by lightning and dropping hero onto fire trap); [3.6.1 fixed a similar problem with more obvious symptom, an "object lost" panic when the unholy water was wielded; the fix for that wasn't general enough] +add MM_ASLEEP makemon() flag and honor it when creating group for fill_zoo Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/hack.h b/include/hack.h index 5785b8590..beab7da8a 100644 --- a/include/hack.h +++ b/include/hack.h @@ -253,13 +253,14 @@ typedef struct sortloot_item Loot; #define MM_IGNOREWATER 0x00008 /* ignore water when positioning */ #define MM_ADJACENTOK \ 0x00010 /* it is acceptable to use adjacent coordinates */ -#define MM_ANGRY 0x00020 /* monster is created angry */ +#define MM_ANGRY 0x00020 /* monster is created angry */ #define MM_NONAME 0x00040 /* monster is not christened */ -#define MM_EGD 0x00100 /* add egd structure */ -#define MM_EPRI 0x00200 /* add epri structure */ -#define MM_ESHK 0x00400 /* add eshk structure */ -#define MM_EMIN 0x00800 /* add emin structure */ -#define MM_EDOG 0x01000 /* add edog structure */ +#define MM_EGD 0x00100 /* add egd structure */ +#define MM_EPRI 0x00200 /* add epri structure */ +#define MM_ESHK 0x00400 /* add eshk structure */ +#define MM_EMIN 0x00800 /* add emin structure */ +#define MM_EDOG 0x01000 /* add edog structure */ +#define MM_ASLEEP 0x02000 /* monsters should be generated asleep */ /* flags for make_corpse() and mkcorpstat() */ #define CORPSTAT_NONE 0x00 diff --git a/src/makemon.c b/src/makemon.c index 33cc7709c..e4d799e9b 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -18,15 +18,15 @@ STATIC_DCL boolean FDECL(uncommon, (int)); STATIC_DCL int FDECL(align_shift, (struct permonst *)); STATIC_DCL boolean FDECL(mk_gen_ok, (int, int, int)); STATIC_DCL boolean FDECL(wrong_elem_type, (struct permonst *)); -STATIC_DCL void FDECL(m_initgrp, (struct monst *, int, int, int)); +STATIC_DCL void FDECL(m_initgrp, (struct monst *, int, int, int, int)); STATIC_DCL void FDECL(m_initthrow, (struct monst *, int, int)); STATIC_DCL void FDECL(m_initweap, (struct monst *)); STATIC_DCL void FDECL(m_initinv, (struct monst *)); STATIC_DCL boolean FDECL(makemon_rnd_goodpos, (struct monst *, unsigned, coord *)); -#define m_initsgrp(mtmp, x, y) m_initgrp(mtmp, x, y, 3) -#define m_initlgrp(mtmp, x, y) m_initgrp(mtmp, x, y, 10) +#define m_initsgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 3, mmf) +#define m_initlgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 10, mmf) #define toostrong(monindx, lev) (mons[monindx].difficulty > lev) #define tooweak(monindx, lev) (mons[monindx].difficulty < lev) @@ -76,9 +76,9 @@ struct permonst *ptr; /* make a group just like mtmp */ STATIC_OVL void -m_initgrp(mtmp, x, y, n) -register struct monst *mtmp; -register int x, y, n; +m_initgrp(mtmp, x, y, n, mmflags) +struct monst *mtmp; +int x, y, n, mmflags; { coord mm; register int cnt = rnd(n); @@ -131,6 +131,8 @@ register int x, y, n; if (enexto(&mm, mm.x, mm.y, mtmp->data)) { mon = makemon(mtmp->data, mm.x, mm.y, NO_MM_FLAGS); if (mon) { + if (mmflags & MM_ASLEEP) + mon->msleeping = 1; mon->mpeaceful = FALSE; mon->mavenge = 0; set_malign(mon); @@ -1193,7 +1195,8 @@ int mmflags; newemin(mtmp); if (mmflags & MM_EDOG) newedog(mtmp); - + if (mmflags & MM_ASLEEP) + mtmp->msleeping = 1; mtmp->nmon = fmon; fmon = mtmp; mtmp->m_id = context.ident++; @@ -1366,12 +1369,12 @@ int mmflags; set_malign(mtmp); /* having finished peaceful changes */ if (anymon) { if ((ptr->geno & G_SGROUP) && rn2(2)) { - m_initsgrp(mtmp, mtmp->mx, mtmp->my); + m_initsgrp(mtmp, mtmp->mx, mtmp->my, mmflags); } else if (ptr->geno & G_LGROUP) { if (rn2(3)) - m_initlgrp(mtmp, mtmp->mx, mtmp->my); + m_initlgrp(mtmp, mtmp->mx, mtmp->my, mmflags); else - m_initsgrp(mtmp, mtmp->mx, mtmp->my); + m_initsgrp(mtmp, mtmp->mx, mtmp->my, mmflags); } } diff --git a/src/mkroom.c b/src/mkroom.c index 15cffdc7f..d0995c7f1 100644 --- a/src/mkroom.c +++ b/src/mkroom.c @@ -341,7 +341,7 @@ struct mkroom *sroom; : (type == ANTHOLE) ? antholemon() : (struct permonst *) 0, - sx, sy, NO_MM_FLAGS); + sx, sy, MM_ASLEEP); if (mon) { mon->msleeping = 1; if (type == COURT && mon->mpeaceful) { From 9e170baecc766553a89ff413f95161a572881892 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 7 Dec 2018 00:36:38 -0800 Subject: [PATCH 23/43] fix #H7655 - highlighting gold With options along the line of OPTIONS=statushilites:4 HILITE_STATUS=gold/always/yellow gold started out unhighlighted (unhighlit?). I didn't try to figure out why, just changed things to force a full status update when gold requires internal changes (different \G encoding or different glyph) which is something that happens when session first enters moveloop(). --- doc/fixes36.2 | 1 + src/botl.c | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 421792d38..68d6c3229 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -252,6 +252,7 @@ hero hit by something that causes inventory items to be destroyed with loss of similar problem with more obvious symptom, an "object lost" panic when the unholy water was wielded; the fix for that wasn't general enough] add MM_ASLEEP makemon() flag and honor it when creating group for fill_zoo +at start of session (new game or restore), HILITE_STATUS for gold was ignored Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/botl.c b/src/botl.c index 4af749105..e0008d2b5 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1527042178 2018/05/23 02:22:58 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.101 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1544171789 2018/12/07 08:36:29 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.128 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -732,15 +732,14 @@ boolean *valsetlist; * NNNN = the glyph portion * 25 = the gold amount * + * Setting 'chg = 2' is enough to render the field properly, but + * not to honor an initial highlight, so force 'update_all = 1'. */ - if (fld == BL_GOLD) { - if (context.rndencode != oldrndencode) { - chg = 2; + if (context.rndencode != oldrndencode + || showsyms[COIN_CLASS + SYM_OFF_O] != oldgoldsym) { + update_all = 1; /* chg = 2; */ oldrndencode = context.rndencode; - } - if (oldgoldsym != showsyms[COIN_CLASS + SYM_OFF_O]) { - chg = 2; oldgoldsym = showsyms[COIN_CLASS + SYM_OFF_O]; } } From f3c4a52e36ffb4736aac198964fabf575a00d921 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 7 Dec 2018 01:20:20 -0800 Subject: [PATCH 24/43] menucolors In the 'special options' section at the end of 'O's menu, change the spelling for the menucolors entry to "menu colors" so that it isn't spelled exactly the same as the 'menucolors' boolean option. Only affects what the player sees when reading that menu. If player uses 'O' to add any menu colors and 'menucolors' boolean is Off at the time, give a reminder to toggle it to On in order to have those menu colorings become active. (Adding hilite_status entries has a similar reminder for 'statushilites' if done while that is 0.) --- doc/fixes36.2 | 2 ++ src/options.c | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 68d6c3229..aaff66ca0 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -253,6 +253,8 @@ hero hit by something that causes inventory items to be destroyed with loss of the unholy water was wielded; the fix for that wasn't general enough] add MM_ASLEEP makemon() flag and honor it when creating group for fill_zoo at start of session (new game or restore), HILITE_STATUS for gold was ignored +if player creates any menu colors via 'O' while menucolors is off, issue a + reminder that it needs to be on in order for those to become effective Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/options.c b/src/options.c index 7dc622687..b3595a6ad 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1543972192 2018/12/05 01:09:52 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.335 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1544174413 2018/12/07 09:20:13 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.339 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -4283,7 +4283,7 @@ static struct other_opts { int NDECL((*othr_count_func)); } othropt[] = { { "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC, count_apes }, - { "menucolors", SET_IN_GAME, OPT_OTHER_MENUCOLOR, count_menucolors }, + { "menu colors", SET_IN_GAME, OPT_OTHER_MENUCOLOR, count_menucolors }, { "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE, msgtype_count }, #ifdef STATUS_HILITES { "status hilite rules", SET_IN_GAME, OPT_OTHER_STATHILITE, @@ -4457,7 +4457,7 @@ doset() /* changing options via menu by Per Liboriussen */ } #endif } else if (opt_indx == OPT_OTHER_MENUCOLOR) { - (void) special_handling("menucolors", setinitial, + (void) special_handling("menu_colors", setinitial, fromfile); } else if (opt_indx == OPT_OTHER_MSGTYPE) { (void) special_handling("msgtype", setinitial, fromfile); @@ -5036,7 +5036,7 @@ boolean setinitial, setfromfile; if (pick_cnt >= 0) goto msgtypes_again; } - } else if (!strcmp("menucolors", optname)) { + } else if (!strcmp("menu_colors", optname)) { int opt_idx, nmc, mcclr, mcattr; char mcbuf[BUFSZ] = DUMMY; @@ -5044,11 +5044,15 @@ boolean setinitial, setfromfile; nmc = count_menucolors(); opt_idx = handle_add_list_remove("menucolor", nmc); if (opt_idx == 3) { /* done */ + menucolors_done: + if (nmc > 0 && !iflags.use_menu_color) + pline( + "To have menu colors become active, toggle 'menucolors' option to True."); return TRUE; } else if (opt_idx == 0) { /* add new */ getlin("What new menucolor pattern?", mcbuf); if (*mcbuf == '\033') - return TRUE; + goto menucolors_done; if (*mcbuf && test_regex_pattern(mcbuf, (const char *)0) && (mcclr = query_color((char *) 0)) != -1 From a1c1acdd2423300a7771a2ab3001ffb51c0ce861 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 7 Dec 2018 09:29:01 -0500 Subject: [PATCH 25/43] add MM_NOGRP makemon() flag add MM_NOGRP makemon() flag as a means of suppressing groups of monsters in a couple places that warrant it when a specific monster type isn't specified on the call to makemon() --- doc/fixes36.2 | 3 +++ include/hack.h | 1 + src/makemon.c | 6 ++---- src/mklev.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 421792d38..538c632ca 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -252,6 +252,9 @@ hero hit by something that causes inventory items to be destroyed with loss of similar problem with more obvious symptom, an "object lost" panic when the unholy water was wielded; the fix for that wasn't general enough] add MM_ASLEEP makemon() flag and honor it when creating group for fill_zoo +add MM_NOGRP makemon() flag as a means of suppressing groups of monsters in + a couple places that warrant it when a specific monster type isn't + specified on the call to makemon() Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/hack.h b/include/hack.h index beab7da8a..e59693d5f 100644 --- a/include/hack.h +++ b/include/hack.h @@ -261,6 +261,7 @@ typedef struct sortloot_item Loot; #define MM_EMIN 0x00800 /* add emin structure */ #define MM_EDOG 0x01000 /* add edog structure */ #define MM_ASLEEP 0x02000 /* monsters should be generated asleep */ +#define MM_NOGRP 0x04000 /* suppress creation of monster groups */ /* flags for make_corpse() and mkcorpstat() */ #define CORPSTAT_NONE 0x00 diff --git a/src/makemon.c b/src/makemon.c index e4d799e9b..20912b8da 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -129,10 +129,8 @@ int x, y, n, mmflags; * smaller group. */ if (enexto(&mm, mm.x, mm.y, mtmp->data)) { - mon = makemon(mtmp->data, mm.x, mm.y, NO_MM_FLAGS); + mon = makemon(mtmp->data, mm.x, mm.y, (mmflags | MM_NOGRP)); if (mon) { - if (mmflags & MM_ASLEEP) - mon->msleeping = 1; mon->mpeaceful = FALSE; mon->mavenge = 0; set_malign(mon); @@ -1367,7 +1365,7 @@ int mmflags; : eminp->renegade; } set_malign(mtmp); /* having finished peaceful changes */ - if (anymon) { + if (anymon && !(mmflags & MM_NOGRP)) { if ((ptr->geno & G_SGROUP) && rn2(2)) { m_initsgrp(mtmp, mtmp->mx, mtmp->my, mmflags); } else if (ptr->geno & G_LGROUP) { diff --git a/src/mklev.c b/src/mklev.c index 11067b931..35b0d9378 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -803,7 +803,7 @@ skip0: if (u.uhave.amulet || !rn2(3)) { x = somex(croom); y = somey(croom); - tmonst = makemon((struct permonst *) 0, x, y, NO_MM_FLAGS); + tmonst = makemon((struct permonst *) 0, x, y, (MM_NOGRP | MM_ASLEEP)); if (tmonst && tmonst->data == &mons[PM_GIANT_SPIDER] && !occupied(x, y)) (void) maketrap(x, y, WEB); From 8319227b557c98db99776f2a3ea1fe3022643497 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 7 Dec 2018 22:18:50 +0200 Subject: [PATCH 26/43] Clear the getlin buffer Recent change to this bit of code didn't clear the buffer, which causes garbage in the string if compiled with EDIT_GETLIN --- win/tty/wintty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 47bac195b..b5c4451df 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -2046,7 +2046,7 @@ struct WinDesc *cw; tty_nhbell(); break; } else { - char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ]; + char searchbuf[BUFSZ + 2], tmpbuf[BUFSZ] = DUMMY; boolean on_curr_page = FALSE; int lineno = 0; From 6ff9753029ab187ca238169d68beb9313efea031 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 7 Dec 2018 19:12:56 -0500 Subject: [PATCH 27/43] back out some spider changes After consultation with the original committer, this is being pulled out, possibly revisited later. There was originally meant to be a follow-up piece to this that he never had a chance to integrate for various reasons. --- doc/fixes36.2 | 1 - src/monmove.c | 40 ---------------------------------------- 2 files changed, 41 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 11aa182ff..b4403f0fe 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -384,7 +384,6 @@ make it clear when a leprechaun dodges your attack wizard mode #wizidentify can now select individual items for permanent identification and don't display the selection to permanently identify everything if everything is already fully identified -spiders will occasionally spin webs when moving around make mine town "orctown" variation a multiple level feature of the mines replace #monpolycontrol command with monpolycontrol boolean option replace #wizdebug_traveldisplay command with travel_debug boolean option diff --git a/src/monmove.c b/src/monmove.c index 5694a31b8..c501cf421 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -14,7 +14,6 @@ STATIC_DCL int FDECL(disturb, (struct monst *)); STATIC_DCL void FDECL(release_hero, (struct monst *)); STATIC_DCL void FDECL(distfleeck, (struct monst *, int *, int *, int *)); STATIC_DCL int FDECL(m_arrival, (struct monst *)); -STATIC_DCL int FDECL(count_webbing_walls, (XCHAR_P, XCHAR_P)); STATIC_DCL boolean FDECL(stuff_prevents_passage, (struct monst *)); STATIC_DCL int FDECL(vamp_shift, (struct monst *, struct permonst *, BOOLEAN_P)); @@ -759,24 +758,6 @@ xchar nix,niy; return FALSE; } -/* returns the number of walls in the four cardinal directions that could - hold up a web */ -STATIC_OVL int -count_webbing_walls(x, y) -xchar x, y; -{ -#define holds_up_web(X, Y) ((!isok((X), (Y)) \ - || IS_ROCK(levl[X][Y].typ) \ - || (levl[X][Y].typ == STAIRS \ - && (X) == xupstair && (Y) == yupstair) \ - || (levl[X][Y].typ == LADDER \ - && (X) == xupladder && (Y) == yupladder) \ - || levl[X][Y].typ == IRONBARS) ? 1 : 0) - return (holds_up_web(x, y - 1) + holds_up_web(x + 1, y) - + holds_up_web(x, y + 1) + holds_up_web(x - 1, y)); -#undef holds_up_web -} - /* Return values: * 0: did not move, but can still attack and do other stuff. * 1: moved, possibly can attack. @@ -1472,27 +1453,6 @@ postmov: } } - /* maybe spin a web -- this needs work; if the spider is far away, - it might spin a lot of webs before hero encounters it */ - if (webmaker(ptr) && !mtmp->mspec_used && !t_at(mtmp->mx, mtmp->my)) { - struct trap *trap; - int prob = ((ptr == &mons[PM_GIANT_SPIDER]) ? 15 : 5) - * (count_webbing_walls(mtmp->mx, mtmp->my) + 1); - - if (rn2(1000) < prob - && (trap = maketrap(mtmp->mx, mtmp->my, WEB)) != 0) { - mtmp->mspec_used = d(4, 4); /* 4..16 */ - if (cansee(mtmp->mx, mtmp->my)) { - char mbuf[BUFSZ]; - - Strcpy(mbuf, - canspotmon(mtmp) ? y_monnam(mtmp) : something); - pline("%s spins a web.", upstart(mbuf)); - trap->tseen = 1; - } - } - } - if (hides_under(ptr) || ptr->mlet == S_EEL) { /* Always set--or reset--mundetected if it's already hidden (just in case the object it was hiding under went away); From e8a5ff6dffd2b98bdc29bab208f6630389c1ece6 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 7 Dec 2018 16:37:24 -0800 Subject: [PATCH 28/43] gold highlight bit Keep type/value straight: 'update_all' is boolean, not ordinary int. --- src/botl.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/botl.c b/src/botl.c index e0008d2b5..2fa84afe0 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1544171789 2018/12/07 08:36:29 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.128 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1544229439 2018/12/08 00:37:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.129 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -733,15 +733,14 @@ boolean *valsetlist; * 25 = the gold amount * * Setting 'chg = 2' is enough to render the field properly, but - * not to honor an initial highlight, so force 'update_all = 1'. + * not to honor an initial highlight, so force 'update_all = TRUE'. */ - if (fld == BL_GOLD) { - if (context.rndencode != oldrndencode - || showsyms[COIN_CLASS + SYM_OFF_O] != oldgoldsym) { - update_all = 1; /* chg = 2; */ - oldrndencode = context.rndencode; - oldgoldsym = showsyms[COIN_CLASS + SYM_OFF_O]; - } + if (fld == BL_GOLD + && (context.rndencode != oldrndencode + || showsyms[COIN_CLASS + SYM_OFF_O] != oldgoldsym)) { + update_all = TRUE; /* chg = 2; */ + oldrndencode = context.rndencode; + oldgoldsym = showsyms[COIN_CLASS + SYM_OFF_O]; } reset = FALSE; From 22555438004cc173da19a32eafdb61c28335dd12 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 7 Dec 2018 16:51:18 -0800 Subject: [PATCH 29/43] recursive destroy_item() Make the sequence: be zapped by lightning, have worn ring of levitation be destroyed, fall onto fire trap work better. The fire trap handling will mark everything in inventory as already processed; anything vulnerable to lightning past the destroyed ring would not be checked. So delay destroying such a ring until after all of inventory has been subjected to lightning. --- src/zap.c | 369 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 215 insertions(+), 154 deletions(-) diff --git a/src/zap.c b/src/zap.c index 8f7adf338..1e6e3b1b3 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 zap.c $NHDT-Date: 1544146046 2018/12/07 01:27:26 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.300 $ */ +/* NetHack 3.6 zap.c $NHDT-Date: 1544230271 2018/12/08 00:51:11 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.301 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -28,11 +28,12 @@ STATIC_DCL void FDECL(zhitu, (int, int, const char *, XCHAR_P, XCHAR_P)); STATIC_DCL void FDECL(revive_egg, (struct obj *)); STATIC_DCL boolean FDECL(zap_steed, (struct obj *)); STATIC_DCL void FDECL(skiprange, (int, int *, int *)); - STATIC_DCL int FDECL(zap_hit, (int, int)); STATIC_OVL void FDECL(disintegrate_mon, (struct monst *, int, const char *)); STATIC_DCL void FDECL(backfire, (struct obj *)); STATIC_DCL int FDECL(spell_hit_bonus, (int)); +STATIC_DCL void FDECL(destroy_one_item, (struct obj *, int, int)); +STATIC_DCL void FDECL(wishcmdassist, (int)); #define ZT_MAGIC_MISSILE (AD_MAGM - 1) #define ZT_FIRE (AD_FIRE - 1) @@ -1846,6 +1847,10 @@ struct obj *obj, *otmp; * menu_drop(), askchain() - inventory traversal where multiple * Drop can alter the invent chain while traversal * is in progress (bhito isn't involved). + * destroy_item(), destroy_mitem() - inventory traversal where + * item destruction can trigger drop or destruction of + * other item(s) and alter the invent or mon->minvent + * chain, possibly recursively. * * The bypass bit on all objects is reset each turn, whenever * context.bypasses is set. @@ -4715,178 +4720,234 @@ const char *const destroy_strings[][3] = { { "breaks apart and explodes", "", "exploding wand" }, }; -void -destroy_item(osym, dmgtyp) -register int osym, dmgtyp; +/* guts of destroy_item(), which ought to be called maybe_destroy_items(); + caller must decide whether obj is eligible */ +STATIC_OVL void +destroy_one_item(obj, osym, dmgtyp) +struct obj *obj; +int osym, dmgtyp; { - register struct obj *obj; - int dmg, xresist, skip; long i, cnt, quan; - int dindx; + int dmg, xresist, skip, dindx; const char *mult; boolean physical_damage; - /* - * Sometimes destroying an item can change inventory aside from the - * item itself (cited case was a potion of polymorph; when destroyed, - * potion_breathe() caused hero to transform and that resulted in - * destruction of some worn armor). Unlike other uses of the object - * bybass mechanism, destroy_item() can be called multiple times for - * same event. So we have to explicitly clear it before each use and - * hope no other section of code expects it to retain previous value. - * - * FIXME? Destruction of a ring of levitation could drop hero onto - * a fire trap which could destroy other items and we'll get called - * recursively. This should still work, but items beyond the ring - * which survive the fire will be marked as already processed by the - * inner call, so will always survive the remainder of the outer call - * instead of being subjected to original chance of destruction. - */ - bypass_objlist(invent, FALSE); /* clear bypass bit for invent */ + physical_damage = FALSE; + xresist = skip = 0; + /* lint suppression */ + dmg = dindx = 0; + quan = 0L; - while ((obj = nxt_unbypassed_obj(invent)) != 0) { - physical_damage = FALSE; - if (obj->oclass != osym) - continue; /* test only objs of type osym */ - if (obj->oartifact) - continue; /* don't destroy artifacts */ - if (obj->in_use && obj->quan == 1L) - continue; /* not available */ - xresist = skip = 0; - /* lint suppression */ - dmg = dindx = 0; - quan = 0L; - - switch (dmgtyp) { - case AD_COLD: - if (osym == POTION_CLASS && obj->otyp != POT_OIL) { - quan = obj->quan; - dindx = 0; - dmg = rnd(4); - } else - skip++; - break; - case AD_FIRE: - xresist = (Fire_resistance && obj->oclass != POTION_CLASS - && obj->otyp != GLOB_OF_GREEN_SLIME); - - if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) - skip++; - if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { - skip++; - if (!Blind) - pline("%s glows a strange %s, but remains intact.", - The(xname(obj)), hcolor("dark red")); - } + switch (dmgtyp) { + case AD_COLD: + if (osym == POTION_CLASS && obj->otyp != POT_OIL) { quan = obj->quan; - switch (osym) { - case POTION_CLASS: - dindx = (obj->otyp != POT_OIL) ? 1 : 2; - dmg = rnd(6); - break; - case SCROLL_CLASS: - dindx = 3; - dmg = 1; - break; - case SPBOOK_CLASS: - dindx = 4; - dmg = 1; - break; - case FOOD_CLASS: - if (obj->otyp == GLOB_OF_GREEN_SLIME) { - dindx = 1; /* boil and explode */ - dmg = (obj->owt + 19) / 20; - } else { - skip++; - } - break; - default: - skip++; - break; - } + dindx = 0; + dmg = rnd(4); + } else + skip++; + break; + case AD_FIRE: + xresist = (Fire_resistance && obj->oclass != POTION_CLASS + && obj->otyp != GLOB_OF_GREEN_SLIME); + if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) + skip++; + if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + skip++; + if (!Blind) + pline("%s glows a strange %s, but remains intact.", + The(xname(obj)), hcolor("dark red")); + } + quan = obj->quan; + switch (osym) { + case POTION_CLASS: + dindx = (obj->otyp != POT_OIL) ? 1 : 2; + dmg = rnd(6); break; - case AD_ELEC: - xresist = (Shock_resistance && obj->oclass != RING_CLASS); - quan = obj->quan; - switch (osym) { - case RING_CLASS: - if (obj->otyp == RIN_SHOCK_RESISTANCE) { - skip++; - break; - } - dindx = 5; - dmg = 0; - break; - case WAND_CLASS: - if (obj->otyp == WAN_LIGHTNING) { - skip++; - break; - } -#if 0 - if (obj == current_wand) { skip++; break; } -#endif - dindx = 6; - dmg = rnd(10); - break; - default: + case SCROLL_CLASS: + dindx = 3; + dmg = 1; + break; + case SPBOOK_CLASS: + dindx = 4; + dmg = 1; + break; + case FOOD_CLASS: + if (obj->otyp == GLOB_OF_GREEN_SLIME) { + dindx = 1; /* boil and explode */ + dmg = (obj->owt + 19) / 20; + } else { skip++; - break; } break; default: skip++; break; } - - if (!skip) { - if (obj->in_use) - --quan; /* one will be used up elsewhere */ - for (i = cnt = 0L; i < quan; i++) - if (!rn2(3)) - cnt++; - - if (!cnt) - continue; - mult = (cnt == 1L) - ? (quan == 1L) ? "Your" /* 1 of 1 */ - : "One of your" /* 1 of N */ - : (cnt < quan) ? "Some of your" /* n of N */ - : (quan == 2L) ? "Both of your" /* 2 of 2 */ - : "All of your"; /* N of N */ - pline("%s %s %s!", mult, xname(obj), - destroy_strings[dindx][(cnt > 1L)]); - if (osym == POTION_CLASS && dmgtyp != AD_COLD) { - if (!breathless(youmonst.data) || haseyes(youmonst.data)) - potionbreathe(obj); + break; + case AD_ELEC: + xresist = (Shock_resistance && obj->oclass != RING_CLASS); + quan = obj->quan; + switch (osym) { + case RING_CLASS: + if (obj->otyp == RIN_SHOCK_RESISTANCE) { + skip++; + break; } - if (obj->owornmask) { - if (obj->owornmask & W_RING) /* ring being worn */ - Ring_gone(obj); - else - setnotworn(obj); + dindx = 5; + dmg = 0; + break; + case WAND_CLASS: + if (obj->otyp == WAN_LIGHTNING) { + skip++; + break; } - if (obj == current_wand) - current_wand = 0; /* destroyed */ - for (i = 0; i < cnt; i++) - useup(obj); - if (dmg) { - if (xresist) { - You("aren't hurt!"); - } else { - const char *how = destroy_strings[dindx][2]; - boolean one = (cnt == 1L); +#if 0 + if (obj == current_wand) { skip++; break; } +#endif + dindx = 6; + dmg = rnd(10); + break; + default: + skip++; + break; + } + break; + default: + skip++; + break; + } - if (dmgtyp == AD_FIRE && osym == FOOD_CLASS) - how = "exploding glob of slime"; - if (physical_damage) - dmg = Maybe_Half_Phys(dmg); - losehp(dmg, one ? how : (const char *) makeplural(how), - one ? KILLED_BY_AN : KILLED_BY); - exercise(A_STR, FALSE); - } + if (!skip) { + if (obj->in_use) + --quan; /* one will be used up elsewhere */ + for (i = cnt = 0L; i < quan; i++) + if (!rn2(3)) + cnt++; + + if (!cnt) + return; + mult = (cnt == 1L) + ? ((quan == 1L) ? "Your" /* 1 of 1 */ + : "One of your") /* 1 of N */ + : ((cnt < quan) ? "Some of your" /* n of N */ + : (quan == 2L) ? "Both of your" /* 2 of 2 */ + : "All of your"); /* N of N */ + pline("%s %s %s!", mult, xname(obj), + destroy_strings[dindx][(cnt > 1L)]); + if (osym == POTION_CLASS && dmgtyp != AD_COLD) { + if (!breathless(youmonst.data) || haseyes(youmonst.data)) + potionbreathe(obj); + } + if (obj->owornmask) { + if (obj->owornmask & W_RING) /* ring being worn */ + Ring_gone(obj); + else + setnotworn(obj); + } + if (obj == current_wand) + current_wand = 0; /* destroyed */ + for (i = 0; i < cnt; i++) + useup(obj); + if (dmg) { + if (xresist) { + You("aren't hurt!"); + } else { + const char *how = destroy_strings[dindx][2]; + boolean one = (cnt == 1L); + + if (dmgtyp == AD_FIRE && osym == FOOD_CLASS) + how = "exploding glob of slime"; + if (physical_damage) + dmg = Maybe_Half_Phys(dmg); + losehp(dmg, one ? how : (const char *) makeplural(how), + one ? KILLED_BY_AN : KILLED_BY); + exercise(A_STR, FALSE); } } } +} + +/* target items of specified class for possible destruction */ +void +destroy_item(osym, dmgtyp) +int osym, dmgtyp; +{ + register struct obj *obj; + int i, deferral_indx = 0; + /* 1+52+1: try to handle a full inventory; it doesn't matter if + inventory actually has more, even if everything should be deferred */ + unsigned short deferrals[1 + 52 + 1]; /* +1: gold, overflow */ + + (void) memset((genericptr_t) deferrals, 0, sizeof deferrals); + /* + * Sometimes destroying an item can change inventory aside from + * the item itself (cited case was a potion of unholy water; when + * boiled, potionbreathe() caused hero to transform into were-beast + * form and that resulted in dropping or destroying some worn armor). + * + * Unlike other uses of the object bybass mechanism, destroy_item() + * can be called multiple times for the same event. So we have to + * explicitly clear it before each use and hope no other section of + * code expects it to retain previous value. + * + * Destruction of a ring of levitation or form change which pushes + * off levitation boots could drop hero onto a fire trap that + * could destroy other items and we'll get called recursively. Or + * onto a trap which transports hero elsewhere, which won't disrupt + * traversal but could yield message sequencing issues. So we + * defer handling such things until after rest of inventory has + * been processed. If some other combination of items and events + * triggers a recursive call, rest of inventory after the triggering + * item will be skipped by the outer call since the inner one will + * have set the bypass bits of the whole list. + * + * [Unfortunately, death while poly'd into flyer and subsequent + * rehumanization could also drop hero onto a trap, and there's no + * straightforward way to defer that. Things could be improved by + * redoing this to use two passes, first to collect a list or array + * of o_id and quantity of what is targetted for destruction, + * second pass to handle the destruction.] + */ + bypass_objlist(invent, FALSE); /* clear bypass bit for invent */ + + while ((obj = nxt_unbypassed_obj(invent)) != 0) { + if (obj->oclass != osym) + continue; /* test only objs of type osym */ + if (obj->oartifact) + continue; /* don't destroy artifacts */ + if (obj->in_use && obj->quan == 1L) + continue; /* not available */ + + /* if loss of this item might dump us onto a trap, hold off + until later because potential recursive destroy_item() will + result in setting bypass bits on whole chain--we would skip + the rest as already processed once control returns here */ + if (deferral_indx < SIZE(deferrals) + && ((obj->owornmask != 0L + && (objects[obj->otyp].oc_oprop == LEVITATION + || objects[obj->otyp].oc_oprop == FLYING)) + /* destroyed wands and potions of polymorph don't trigger + polymorph so don't need to be deferred */ + || (obj->otyp == POT_WATER && u.ulycn >= LOW_PM + && (Upolyd ? obj->blessed : obj->cursed)))) { + deferrals[deferral_indx++] = obj->o_id; + continue; + } + /* obj is eligible; maybe destroy it */ + destroy_one_item(obj, osym, dmgtyp); + } + /* if we saved some items for later (most likely just a worn ring + of levitation) and they're still in inventory, handle them now */ + for (i = 0; i < deferral_indx; ++i) { + /* note: obj->nobj is only referenced when obj is skipped; + having obj be dropped or destroyed won't affect traversal */ + for (obj = invent; obj; obj = obj->nobj) + if (obj->o_id == deferrals[i]) { + destroy_one_item(obj, osym, dmgtyp); + break; + } + } return; } @@ -4909,7 +4970,7 @@ int osym, dmgtyp; vis = canseemon(mtmp); /* see destroy_item(); object destruction could disrupt inventory list */ - bypass_objlist(mtmp->minvent, FALSE); /* clear bypass bit for invent */ + bypass_objlist(mtmp->minvent, FALSE); /* clear bypass bit for minvent */ while ((obj = nxt_unbypassed_obj(mtmp->minvent)) != 0) { if (obj->oclass != osym) From dc1e2da35995ac8a8a008cbcbd7b66e60ded5e9b Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 17:56:20 -0500 Subject: [PATCH 30/43] Windows startup modifications Changes to be committed: modified: include/winprocs.h modified: src/options.c modified: sys/share/pcmain.c new file: sys/share/safeproc.c modified: sys/winnt/Makefile.msc modified: sys/winnt/stubs.c new file: sys/winnt/windmain.c modified: sys/winnt/winnt.c modified: win/win32/vs2017/NetHack.vcxproj modified: win/win32/vs2017/NetHackW.vcxproj modified: win/win32/winhack.c Because multiple window ports are supported on Windows now, even in the same executable and selectable via config file in some cases, some adjustments became necessary. There will likely be some further refining of this over the next day or two. List of changes: Move Windows startup from sys/share/pcmain.c and into its own sys/winnt/windmain.c so that it can be modified to fix some current breakage, and allow altering the order of some things. There is startup processing code that is common to all of the Windows WindowPorts, but that startup processing code needs to have no dependency on any one of those WindowPorts. Yet, during startup processing, some of the initialization routines can end up calling NetHack functions that expect an active Window port underneath, and if there isn't one, routines like pline, impossible, panic can end up invoking null function pointers. Place a new file sys/share/safeproc.c, in which a complete window port is available for early startup processing purposes. It's WindowPort name field is set to "safe-startup" just for reference. The prototypes in include/winprocs.h require that SAFEPROCS be Usage: windowprocs = get_safe_procs(0); initializes a set of winprocs function pointers that ensure none of the function pointers are left null, but that's all it does. windowprocs = get_safe_procs(1); initializes a set of winprocs functions pointers that ensure none of the function pointers are left null, but also provides some basic output and input functionality using nothing other than C stdio routines (no platform or OS specific code). The conditional code related to WIN32 has been removed from sys/share/pcmain.c The code common to all of the Windows WindowPorts calls get_safe_procs() almost immediately to ensure that there is a set of WindowPort winprocs available. --- include/winprocs.h | 73 +++ src/options.c | 13 +- sys/share/pcmain.c | 270 +---------- sys/share/safeproc.c | 550 ++++++++++++++++++++++ sys/winnt/Makefile.msc | 14 +- sys/winnt/stubs.c | 2 - sys/winnt/windmain.c | 757 ++++++++++++++++++++++++++++++ sys/winnt/winnt.c | 3 + win/win32/vs2017/NetHack.vcxproj | 5 +- win/win32/vs2017/NetHackW.vcxproj | 5 +- win/win32/winhack.c | 39 +- 11 files changed, 1447 insertions(+), 284 deletions(-) create mode 100644 sys/share/safeproc.c create mode 100644 sys/winnt/windmain.c diff --git a/include/winprocs.h b/include/winprocs.h index ddfef409d..85ece4a7d 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -375,4 +375,77 @@ struct chain_procs { }; #endif /* WINCHAIN */ +#ifdef SAFEPROCS +/* + * window port routines available in sys/share/safeproc.c + */ +extern struct window_procs *FDECL(get_safe_procs, (int)); +extern void FDECL(safe_init_nhwindows, (int *, char **)); +extern void NDECL(safe_player_selection); +extern void NDECL(safe_askname); +extern void NDECL(safe_get_nh_event); +extern void FDECL(safe_exit_nhwindows, (const char *)); +extern void FDECL(safe_suspend_nhwindows, (const char *)); +extern void NDECL(safe_resume_nhwindows); +extern winid FDECL(safe_create_nhwindow, (int)); +extern void FDECL(safe_clear_nhwindow, (winid)); +extern void FDECL(safe_display_nhwindow, (winid, BOOLEAN_P)); +extern void FDECL(safe_destroy_nhwindow, (winid)); +extern void FDECL(safe_curs, (winid, int, int)); +extern void FDECL(safe_putstr, (winid, int, const char *)); +extern void FDECL(safe_putmixed, (winid, int, const char *)); +extern void FDECL(safe_display_file, (const char *, BOOLEAN_P)); +extern void FDECL(safe_start_menu, (winid)); +extern void FDECL(safe_add_menu, (winid, int, const ANY_P *, CHAR_P, CHAR_P, + int, const char *, BOOLEAN_P)); +extern void FDECL(safe_end_menu, (winid, const char *)); +extern int FDECL(safe_select_menu, (winid, int, MENU_ITEM_P **)); +extern char FDECL(safe_message_menu, (CHAR_P, int, const char *)); +extern void NDECL(safe_update_inventory); +extern void NDECL(safe_mark_synch); +extern void NDECL(safe_wait_synch); +#ifdef CLIPPING +extern void FDECL(safe_cliparound, (int, int)); +#endif +#ifdef POSITIONBAR +extern void FDECL(safe_update_positionbar, (char *)); +#endif +extern void FDECL(safe_print_glyph, (winid, XCHAR_P, XCHAR_P, int, int)); +extern void FDECL(safe_raw_print, (const char *)); +extern void FDECL(safe_raw_print_bold, (const char *)); +extern int NDECL(safe_nhgetch); +extern int FDECL(safe_nh_poskey, (int *, int *, int *)); +extern void NDECL(safe_nhbell); +extern int NDECL(safe_doprev_message); +extern char FDECL(safe_yn_function, (const char *, const char *, CHAR_P)); +extern void FDECL(safe_getlin, (const char *, char *)); +extern int NDECL(safe_get_ext_cmd); +extern void FDECL(safe_number_pad, (int)); +extern void NDECL(safe_delay_output); +#ifdef CHANGE_COLOR +extern void FDECL(safe_change_color, (int, long, int)); +#ifdef MAC +extern void FDECL(safe_change_background, (int)); +extern short FDECL(safe_set_font_name, (winid, char *)); +#endif +extern char *NDECL(safe_get_color_string); +#endif +extern void NDECL(safe_start_screen); +extern void NDECL(safe_end_screen); +extern void FDECL(safe_outrip, (winid, int, time_t)); +extern void FDECL(safe_preference_update, (const char *)); +extern char *FDECL(safe_getmsghistory, (BOOLEAN_P)); +extern void FDECL(safe_putmsghistory, (const char *, BOOLEAN_P)); +extern void NDECL(safe_status_init); +extern void NDECL(safe_status_finish); +extern void FDECL(safe_status_enablefield, + (int, const char *, const char *, BOOLEAN_P)); +extern void FDECL(safe_status_update, (int, genericptr_t, int, int, int, unsigned long *)); +extern boolean NDECL(safe_can_suspend); +extern void FDECL(stdio_raw_print, (const char *)); +extern void FDECL(stdio_raw_print_bold, (const char *)); +extern void NDECL(stdio_wait_synch); +extern int NDECL(stdio_nhgetch); +#endif /* SAFEPROCS */ #endif /* WINPROCS_H */ + diff --git a/src/options.c b/src/options.c index b3595a6ad..9dafa31bf 100644 --- a/src/options.c +++ b/src/options.c @@ -873,11 +873,16 @@ initoptions_finish() * A multi-interface binary might only support status highlighting * for some of the interfaces; check whether we asked for it but are * using one which doesn't. + * + * Option processing can take place before a user-decided WindowPort + * is even initialized, so check for that too. */ - if (iflags.hilite_delta && !wc2_supported("statushilites")) { - raw_printf("Status highlighting not supported for %s interface.", - windowprocs.name); - iflags.hilite_delta = 0; + if (!WINDOWPORT("safe-startup")) { + if (iflags.hilite_delta && !wc2_supported("statushilites")) { + raw_printf("Status highlighting not supported for %s interface.", + windowprocs.name); + iflags.hilite_delta = 0; + } } #endif return; diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 8651ae8fe..1f67b0efb 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -3,11 +3,7 @@ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ -/* main.c - MSDOS, OS/2, ST, Amiga, and Windows NetHack */ - -#ifdef WIN32 -#include "win32api.h" /* for GetModuleFileName */ -#endif +/* main.c - MSDOS, OS/2, ST, Amiga NetHack */ #include "hack.h" #include "dlb.h" @@ -47,29 +43,12 @@ void NDECL(preserve_icon); STATIC_DCL void FDECL(process_options, (int argc, char **argv)); STATIC_DCL void NDECL(nhusage); -#if defined(MICRO) || defined(WIN32) || defined(OS2) +#if defined(MICRO) || defined(OS2) extern void FDECL(nethack_exit, (int)); #else #define nethack_exit exit #endif -#ifdef WIN32 -extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ -extern int redirect_stdout; /* from sys/share/pcsys.c */ -extern int GUILaunched; -char *NDECL(exename); -char default_window_sys[] = "mswin"; -#ifndef WIN32CON -HANDLE hStdOut; -boolean NDECL(fakeconsole); -void NDECL(freefakeconsole); -#endif -#endif - -#if defined(MSWIN_GRAPHICS) -extern void NDECL(mswin_destroy_reg); -#endif - #ifdef EXEPATH STATIC_DCL char *FDECL(exepath, (char *)); #endif @@ -78,7 +57,7 @@ int FDECL(main, (int, char **)); extern boolean FDECL(pcmain, (int, char **)); -#if defined(__BORLANDC__) && !defined(_WIN32) +#if defined(__BORLANDC__) void NDECL(startup); unsigned _stklen = STKSIZ; #endif @@ -98,21 +77,8 @@ char *argv[]; { boolean resuming; - nethack_enter(argc, argv); - sys_early_init(); -#if defined(WIN32) && defined(TTY_GRAPHICS) - Strcpy(default_window_sys, "tty"); -#else -#if defined(CURSES_GRAPHICS) - Strcpy(default_window_sys, "curses"); -#endif -#endif - resuming = pcmain(argc, argv); -#ifdef LAN_FEATURES - init_lan_features(); -#endif moveloop(resuming); nethack_exit(EXIT_SUCCESS); /*NOTREACHED*/ @@ -126,14 +92,10 @@ char *argv[]; { register int fd; register char *dir; -#if defined(WIN32) || defined(MSDOS) +#if defined(MSDOS) char *envp = NULL; char *sptr = NULL; #endif -#if defined(WIN32) - char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; - boolean save_getreturn_status = getreturn_enabled; -#endif #ifdef NOCWD_ASSUMPTIONS char failbuf[BUFSZ]; #endif @@ -162,7 +124,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ # endif #endif -#if defined(__BORLANDC__) && !defined(_WIN32) +#if defined(__BORLANDC__) startup(); #endif @@ -175,15 +137,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif hname = "NetHack"; /* used for syntax messages */ -#ifndef WIN32 choose_windows(DEFAULT_WINDOW_SYS); -#else - choose_windows(default_window_sys); - if (argc >= 1 - && !strcmpi(default_window_sys, "mswin") - && strstri(argv[0], "nethackw.exe")) - iflags.windowtype_locked = TRUE; -#endif #if !defined(AMIGA) && !defined(GNUDOS) /* Save current directory and make sure it gets restored when @@ -243,7 +197,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) fqn_prefix[prefcnt] = fqn_prefix[0]; -#if defined(WIN32) || defined(MSDOS) +#if defined(MSDOS) /* sysconf should be searched for in this location */ envp = nh_getenv("COMMONPROGRAMFILES"); if (envp) { @@ -317,14 +271,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ chdirx(HACKDIR, 1); #endif ami_wininit_data(); -#endif -#ifdef WIN32 - save_getreturn_status = getreturn_enabled; -#ifdef TTY_GRAPHICS - raw_clear_screen(); -#endif - getreturn_enabled = TRUE; - check_recordfile((char *) 0); #endif initoptions(); @@ -355,13 +301,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ argv++; } -#ifdef WIN32 - if (argcheck(argc, argv, ARG_WINDOWS) == 1) { - argc--; - argv++; - } -#endif - if (argc > 1 && !strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { /* avoid matching "-dec" for DECgraphics; since the man page * says -d directory, hope nobody's using -desomething_else @@ -381,36 +320,11 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ Strcpy(hackdir, dir); } if (argc > 1) { -#if defined(WIN32) && !defined(WIN32CON) - int sfd = 0; - boolean tmpconsole = FALSE; - hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); -#endif /* * Now we know the directory containing 'record' and * may do a prscore(). */ if (!strncmp(argv[1], "-s", 2)) { -#if defined(WIN32) && !defined(WIN32CON) - -#if 0 - if (!hStdOut) { - tmpconsole = fakeconsole(); - } -#endif - /* - * Check to see if we're redirecting to a file. - */ - sfd = (int) _fileno(stdout); - redirect_stdout = (sfd >= 0) ? !isatty(sfd) : 0; - - if (!redirect_stdout && !hStdOut) { - raw_printf( - "-s is not supported for the Graphical Interface\n"); - nethack_exit(EXIT_SUCCESS); - } -#endif - #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) chdirx(hackdir, 0); #endif @@ -418,47 +332,16 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ initoptions(); #endif prscore(argc, argv); -#if defined(WIN32) && !defined(WIN32CON) - if (tmpconsole) { - getreturn("to exit"); - freefakeconsole(); - tmpconsole = FALSE; - } -#endif nethack_exit(EXIT_SUCCESS); } -#ifdef MSWIN_GRAPHICS - if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */ - mswin_destroy_reg(); - nethack_exit(EXIT_SUCCESS); - } -#endif /* Don't initialize the window system just to print usage */ if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) { -#if 0 - if (!hStdOut) { - GUILaunched = 0; - tmpconsole = fakeconsole(); - } -#endif nhusage(); - -#if defined(WIN32) && !defined(WIN32CON) - if (tmpconsole) { - getreturn("to exit"); - freefakeconsole(); - tmpconsole = FALSE; - } -#endif nethack_exit(EXIT_SUCCESS); } } } - -#ifdef WIN32 - getreturn_enabled = save_getreturn_status; -#endif /* * It seems you really want to play. */ @@ -472,11 +355,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ "\"%s\".%s", copyright_banner_line(1), copyright_banner_line(2), copyright_banner_line(3), copyright_banner_line(4), DLBFILE, -#ifdef WIN32 - "\nAre you perhaps trying to run NetHack within a zip utility?"); -#else ""); -#endif error("dlb_init failure."); } @@ -490,41 +369,22 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ chdirx(hackdir, 1); #endif -#if defined(MSDOS) || defined(WIN32) +#if defined(MSDOS) /* In 3.6.0, several ports process options before they init * the window port. This allows settings that impact window * ports to be specified or read from the sys or user config files. */ process_options(argc, argv); -#ifdef WIN32 - /* - if (WINDOWPORT("mswin")) - NHWinMainInit(); - else - */ -#ifdef TTY_GRAPHICS - if (WINDOWPORT("tty")) { - iflags.use_background_glyph = FALSE; - nttty_open(1); - } else { - iflags.use_background_glyph = TRUE; - } -#endif /* TTY_GRAPHICS */ -#endif /* WIN32 */ -#endif /* MSDOS || WIN32 */ +#endif /* MSDOS */ -#if defined(MSDOS) || defined(WIN32) +#if defined(MSDOS) init_nhwindows(&argc, argv); #else init_nhwindows(&argc, argv); process_options(argc, argv); #endif -#if defined(WIN32) && defined(TTY_GRAPHICS) - toggle_mouse_support(); /* must come after process_options */ -#endif - #ifdef MFLOPPY set_lock_and_bones(); #ifndef AMIGA @@ -561,19 +421,8 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ * overwritten without confirmation when a user starts up * another game with the same player name. */ -#if defined(WIN32) - /* Obtain the name of the logged on user and incorporate - * it into the name. */ - Sprintf(fnamebuf, "%s-%s", get_username(0), plname); - (void) fname_encode( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", '%', - fnamebuf, encodedfnamebuf, BUFSZ); - Sprintf(lock, "%s", encodedfnamebuf); - /* regularize(lock); */ /* we encode now, rather than substitute */ -#else Strcpy(lock, plname); regularize(lock); -#endif getlock(); #else /* What follows is !PC_LOCKING */ #ifdef AMIGA /* We'll put the bones & levels in the user specified directory \ @@ -599,11 +448,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (fd < 0) { raw_print("Cannot create lock file"); } else { -#ifdef WIN32 - hackpid = GetCurrentProcessId(); -#else hackpid = 1; -#endif write(fd, (genericptr_t) &hackpid, sizeof(hackpid)); nhclose(fd); } @@ -618,10 +463,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ vision_init(); display_gamewindows(); -#ifdef WIN32 - getreturn_enabled = TRUE; -#endif - /* * First, try to find and restore a save file for specified character. * We'll return here if new game player_selection() renames the hero. @@ -799,18 +640,6 @@ char *argv[]; case 'l': bigscreen = -1; break; -#endif -#ifdef WIN32 - case 'w': /* windowtype */ -#ifdef TTY_GRAPHICS - if (strncmpi(&argv[0][2], "tty", 3)) { - nttty_open(1); - } -#endif - config_error_init(FALSE, "command line", FALSE); - choose_windows(&argv[0][2]); - config_error_done(); - break; #endif case '@': flags.randomall = 1; @@ -904,14 +733,14 @@ boolean wr; #endif /* CHDIR */ #ifdef PORT_HELP -#if defined(MSDOS) || defined(WIN32) +#if defined(MSDOS) void port_help() { /* display port specific help file */ display_file(PORT_HELP, 1); } -#endif /* MSDOS || WIN32 */ +#endif /* MSDOS */ #endif /* PORT_HELP */ /* validate wizard mode if player has requested access to it */ @@ -930,71 +759,6 @@ authorize_wizard_mode() #define PATH_SEPARATOR '\\' #endif -#if defined(WIN32) && !defined(WIN32CON) -static char exenamebuf[PATHLEN]; -extern HANDLE hConIn; -extern HANDLE hConOut; -boolean has_fakeconsole; - -char * -exename() -{ - int bsize = PATHLEN; - char *tmp = exenamebuf, *tmp2; - -#ifdef UNICODE - { - TCHAR wbuf[PATHLEN * 4]; - GetModuleFileName((HANDLE) 0, wbuf, PATHLEN * 4); - WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL); - } -#else - *(tmp + GetModuleFileName((HANDLE) 0, tmp, bsize)) = '\0'; -#endif - tmp2 = strrchr(tmp, PATH_SEPARATOR); - if (tmp2) - *tmp2 = '\0'; - tmp2++; - return tmp2; -} - -boolean -fakeconsole(void) -{ - if (!hStdOut) { - HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); - - if (!hStdOut && !hStdIn) { - /* Bool rval; */ - AllocConsole(); - AttachConsole(GetCurrentProcessId()); - /* rval = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); */ - freopen("CON", "w", stdout); - freopen("CON", "r", stdin); - } - has_fakeconsole = TRUE; - } - - /* Obtain handles for the standard Console I/O devices */ - hConIn = GetStdHandle(STD_INPUT_HANDLE); - hConOut = GetStdHandle(STD_OUTPUT_HANDLE); -#if 0 - if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) { - /* Unable to set control handler */ - cmode = 0; /* just to have a statement to break on for debugger */ - } -#endif - return has_fakeconsole; -} -void freefakeconsole() -{ - if (has_fakeconsole) { - FreeConsole(); - } -} -#endif - #define EXEPATHBUFSZ 256 char exepathbuf[EXEPATHBUFSZ]; @@ -1009,19 +773,7 @@ char *str; return (char *) 0; bsize = EXEPATHBUFSZ; tmp = exepathbuf; -#ifndef WIN32 Strcpy(tmp, str); -#else -#ifdef UNICODE - { - TCHAR wbuf[BUFSZ]; - GetModuleFileName((HANDLE) 0, wbuf, BUFSZ); - WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL); - } -#else - *(tmp + GetModuleFileName((HANDLE) 0, tmp, bsize)) = '\0'; -#endif -#endif tmp2 = strrchr(tmp, PATH_SEPARATOR); if (tmp2) *tmp2 = '\0'; diff --git a/sys/share/safeproc.c b/sys/share/safeproc.c new file mode 100644 index 000000000..b3b991a7c --- /dev/null +++ b/sys/share/safeproc.c @@ -0,0 +1,550 @@ +/* NetHack 3.6 safeproc.c */ +/* Copyright (c) Michael Allison, 2018 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include + +/* + * *********************************************************** + * This is a complete WindowPort implementation that can be + * assigned to the windowproc function pointers very early + * in the startup initialization, perhaps immediately even. + * It requires only the following call: + * windowprocs = get_safe_procs(0); + * + * The game startup can trigger functions in other modules + * that make assumptions on a WindowPort being available + * and bad things can happen if any function pointers are + * null at that time. + * + * Some ports prior to 3.6.2 made attempts to early init + * various pieces of one of their WindowPorts, but that + * caused conflicts if that particular WindowPort wasn't + * the one that the user ended up selecting in their + * config file later. The WindowPort interfaced was designed + * to allow multiple WindowPorts to be linked into the same + * game binary. + * + * The base functions established by a call to get_safe_procs() + * accomplish the goal of preventing crashes, but not much + * else. + * + * There are also a few additional functions provided in here + * that can be selected optionally to provide some startup + * functionality for getting messages out to the user about + * issues that are being experienced during startup in + * general or during options parsing. The ones in here are + * deliberately free from any platforms or OS specific code. + * Please leave them using stdio C routines as much as + * possible. That isn't to say you can't do fancier functions + * prior to initialization of the primary WindowPort, but you + * can provide those platform-specific functions elsewhere, + * and assign them the same way that these more generic versions + * are assigned. + * + * The additional platform-independent, but more functional + * routines provided in here should be assigned after the + * windowprocs = get_safe_procs() + * call. Here's a list of them: + * + * + * + * + * *********************************************************** + */ + +struct window_procs safe_procs = { + "safe-startup", 0L, 0L, + safe_init_nhwindows, safe_player_selection, safe_askname, safe_get_nh_event, + safe_exit_nhwindows, safe_suspend_nhwindows, safe_resume_nhwindows, + safe_create_nhwindow, safe_clear_nhwindow, safe_display_nhwindow, + safe_destroy_nhwindow, safe_curs, safe_putstr, genl_putmixed, + safe_display_file, safe_start_menu, safe_add_menu, safe_end_menu, + safe_select_menu, safe_message_menu, safe_update_inventory, safe_mark_synch, + safe_wait_synch, +#ifdef CLIPPING + safe_cliparound, +#endif +#ifdef POSITIONBAR + safe_update_positionbar, +#endif + safe_print_glyph, safe_raw_print, safe_raw_print_bold, safe_nhgetch, + safe_nh_poskey, safe_nhbell, safe_doprev_message, safe_yn_function, + safe_getlin, safe_get_ext_cmd, safe_number_pad, safe_delay_output, +#ifdef CHANGE_COLOR /* the Mac uses a palette device */ + safe_change_color, +#ifdef MAC + safe_change_background, set_safe_font_name, +#endif + safe_get_color_string, +#endif + safe_start_screen, safe_end_screen, genl_outrip, + safe_preference_update, + safe_getmsghistory, safe_putmsghistory, + safe_status_init, + safe_status_finish, safe_status_enablefield, +#ifdef STATUS_HILITES + safe_status_update, +#else + safe_status_update, +#endif + safe_can_suspend, +}; + +struct window_procs * +get_safe_procs(optn) +int optn; +{ + if (optn) { + /* include the slightly more functional stdc versions */ + safe_procs.win_raw_print = stdio_raw_print; + safe_procs.win_raw_print_bold = stdio_raw_print_bold; + safe_procs.win_nhgetch = stdio_nhgetch; + safe_procs.win_wait_synch = stdio_wait_synch; + } + return &safe_procs; +} + +/*ARGSUSED*/ +void +safe_init_nhwindows(argcp, argv) +int *argcp UNUSED; +char **argv UNUSED; +{ + return; +} + +void +safe_player_selection() +{ + return; +} + +void +safe_askname() +{ + return; +} + +void +safe_get_nh_event() +{ + return; +} + +void +safe_suspend_nhwindows(str) +const char *str; +{ + return; +} + +void +safe_resume_nhwindows() +{ + return; +} + +void +safe_exit_nhwindows(str) +const char *str; +{ + return; +} + +winid +safe_create_nhwindow(type) +int type; +{ + return WIN_ERR; +} + +void +safe_clear_nhwindow(window) +winid window; +{ + return; +} + +/*ARGSUSED*/ +void +safe_display_nhwindow(window, blocking) +winid window; +boolean blocking; +{ + return; +} + +void +safe_dismiss_nhwindow(window) +winid window; +{ + return; +} + +void +safe_destroy_nhwindow(window) +winid window; +{ + return; +} + +void +safe_curs(window, x, y) +winid window; +int x, y; +{ + return; +} + +void +safe_putstr(window, attr, str) +winid window; +int attr; +const char *str; +{ + return; +} + +void +safe_display_file(fname, complain) +const char *fname; +boolean complain; +{ + return; +} + +void +safe_start_menu(window) +winid window; +{ + return; +} + +/*ARGSUSED*/ +/* + * Add a menu item to the beginning of the menu list. This list is reversed + * later. + */ +void +safe_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected) +winid window; /* window to use, must be of type NHW_MENU */ +int glyph UNUSED; /* glyph to display with item (not used) */ +const anything *identifier; /* what to return if selected */ +char ch; /* keyboard accelerator (0 = pick our own) */ +char gch; /* group accelerator (0 = no group) */ +int attr; /* attribute for string (like safe_putstr()) */ +const char *str; /* menu string */ +boolean preselected; /* item is marked as selected */ +{ + return; +} + +/* + * End a menu in this window, window must a type NHW_MENU. + */ +void +safe_end_menu(window, prompt) +winid window; /* menu to use */ +const char *prompt; /* prompt to for menu */ +{ + return; +} + +int +safe_select_menu(window, how, menu_list) +winid window; +int how; +menu_item **menu_list; +{ + return 0; +} + +/* special hack for treating top line --More-- as a one item menu */ +char +safe_message_menu(let, how, mesg) +char let; +int how; +const char *mesg; +{ + return '\033'; +} + +void +safe_update_inventory() +{ + return; +} + +void +safe_mark_synch() +{ +} + +void +safe_wait_synch() +{ +} + +#ifdef CLIPPING +void +safe_cliparound(x, y) +int x, y; +{ +} +#endif /* CLIPPING */ + +/* + * safe_print_glyph + * + * Print the glyph to the output device. Don't flush the output device. + */ +void +safe_print_glyph(window, x, y, glyph, bkglyph) +winid window; +xchar x, y; +int glyph; +int bkglyph UNUSED; +{ + return; +} + +void +safe_raw_print(str) +const char *str; +{ + return; +} + +void +safe_raw_print_bold(str) +const char *str; +{ + return; +} + +int +safe_nhgetch() +{ + return '\033'; +} + +/* + * return a key, or 0, in which case a mouse button was pressed + * mouse events should be returned as character postitions in the map window. + * Since normal tty's don't have mice, just return a key. + */ +/*ARGSUSED*/ +int +safe_nh_poskey(x, y, mod) +int *x, *y, *mod; +{ + return '\033'; +} + +void +win_safe_init(dir) +int dir; +{ + return; +} + +#ifdef POSITIONBAR +void +safe_update_positionbar(posbar) +char *posbar; +{ + return; +} +#endif /* POSITIONBAR */ + +/* + * safe_status_init() + * -- initialize the port-specific data structures. + */ +void +safe_status_init() +{ + return; +} + +void +safe_can_suspend() +{ + return; +} + +void +safe_nhbell() +{ + return; +} + +int +safe_doprev_message() +{ + return 0; +} + +char +safe_yn_function(query, resp, def) +const char *query; +const char *resp; +char def; +{ + return '\033'; +} + +/*ARGSUSED*/ +static void +safe_getlin(prompt, outbuf) +const char *prompt UNUSED; +char *outbuf; +{ + Strcpy(outbuf, "\033"); +} + +int +safe_get_ext_cmd() +{ + return '\033'; +} + +void +safe_number_pad(mode) +int mode; +{ + return; +} + +void +safe_delay_output() +{ + return; +} + +void +safe_start_screen() +{ + return; +} + +void +safe_end_screen() +{ + return; +} + +void +safe_outrip(tmpwin, how, when) +winid tmpwin; +int how; +time_t when; +{ + return; +} + +/*ARGSUSED*/ +void +safe_preference_update(pref) +const char *pref UNUSED; +{ + return; +} + +char * +safe_getmsghistory(init) +boolean init UNUSED; +{ + return (char *) 0; +} + +void +safe_putmsghistory(msg, is_restoring) +const char *msg; +boolean is_restoring; +{ +} + +void +safe_status_finish() +{ +} + +void +safe_status_enablefield(fieldidx, nm, fmt, enable) +int fieldidx; +const char *nm; +const char *fmt; +boolean enable; +{ +} + +#ifdef STATUS_HILITES +/* call once for each field, then call with BL_FLUSH to output the result */ +void +safe_status_update(idx, ptr, chg, percent, color, colormasks) +int idx; +genericptr_t ptr; +int chg UNUSED, percent UNUSED, color UNUSED; +unsigned long *colormasks UNUSED; +{ +} +#endif /* STATUS_HILITES */ + +/************************************************************** + * These are some optionally selectable routines that add + * some base functionality over the safe_* versions above. + * The safe_* versions are primarily designed to ensure that + * there are no null function pointers remaining at early + * game startup/initialization time. + * + * The slightly more functional versions in here should be kept + * free of platform-specific code or OS-specific code. If you + * want to use versions that involve platform-specific or + * OS-specific code, go right ahead but use your own replacement + * version of the functions in a platform-specific or + * OS-specific source file, not in here. + ***************************************************************/ + +/* Add to your code: windowprocs.win_raw_print = stdio_wait_synch; */ +void +stdio_wait_synch() +{ + char valid[] = {' ', '\n', '\r', '\033', '\0'}; + + fprintf(stdout, "--More--"); + (void) fflush(stdout); + while (!index(valid, stdio_nhgetch())) + ; +} + +/* Add to your code: windowprocs.win_raw_print = stdio_raw_print; */ +void +stdio_raw_print(str) +const char *str; +{ + if (str) + fprintf(stdout, "%s\n", str); + return; +} + +/* Add to your code: windowprocs.win_raw_print_bold = stdio_raw_print_bold; */ +void +stdio_raw_print_bold(str) +const char *str; +{ + stdio_raw_print(str); + return; +} + +/* Add to your code: windowprocs.win_nhgetch = stdio_nhgetch; */ +int +stdio_nhgetch() +{ + return getchar(); +} + + +/* safeprocs.c */ diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 3bc520282..e8b4e22d3 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -215,7 +215,7 @@ VOBJ06 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o VOBJ07 = $(O)end.o $(O)engrave.o $(O)exper.o $(O)explode.o VOBJ08 = $(O)extralev.o $(O)files.o $(O)fountain.o $(O)hack.o VOBJ09 = $(O)hacklib.o $(O)invent.o $(O)light.o $(O)lock.o -VOBJ10 = $(O)mail.o $(O)pcmain.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o +VOBJ10 = $(O)mail.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o VOBJ11 = $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mklev.o VOBJ12 = $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o VOBJ13 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o @@ -233,7 +233,7 @@ VOBJ24 = $(O)track.o $(O)trap.o $(O)u_init.o $(O)uhitm.o VOBJ25 = $(O)vault.o $(O)vis_tab.o $(O)vision.o $(O)weapon.o VOBJ26 = $(O)were.o $(O)wield.o $(O)windows.o $(O)wizard.o VOBJ27 = $(O)worm.o $(O)worn.o $(O)write.o $(O)zap.o -VOBJ28 = $(O)win10.o +VOBJ28 = $(O)win10.o $(O)safeproc.o $(O)windmain.o DLBOBJ = $(O)dlb.o @@ -535,7 +535,7 @@ INCLDIR= /I..\include /I..\sys\winnt # Util builds #========================================== -cflagsBuild = $(cflags) $(INCLDIR) $(WINPFLAG) $(DLBFLG) +cflagsBuild = $(cflags) $(INCLDIR) $(WINPFLAG) $(DLBFLG) -DSAFEPROCS lflagsBuild = $(lflags) $(conlibs) $(MACHINE) #========================================== @@ -1461,7 +1461,7 @@ $(O)guistub.o: $(HACK_H) $(MSWSYS)\stubs.c # $(O)winhack.o: $(HACK_H) $(MSWIN)\winhack.c - @$(cc) $(cflagsBuild) -I$(MSWSYS) -I$(MSWIN) -Fo$@ $(MSWIN)\winhack.c + @$(cc) $(cflagsBuild) -DSAFEPROCS -I$(MSWSYS) -I$(MSWIN) -Fo$@ $(MSWIN)\winhack.c #if you aren't linking in the full tty then #include the following stub for proper linkage. @@ -1511,9 +1511,9 @@ $(O)\curswins.c: $(WCURSES)\curswins.c $(WCURSES)\curswins.h $(INCL)\wincurs.h $(O)tos.o: ..\sys\atari\tos.c $(HACK_H) $(INCL)\tcap.h @$(CC) $(cflagsBuild) -Fo$@ ..\sys\atari\tos.c -$(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h \ - $(MSWSYS)\win32api.h - @$(CC) $(cflagsBuild) -Fo$@ ..\sys\share\pcmain.c +$(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h +$(O)safeprocs.o: ..\sys\share\safeprocs.c $(HACK_H) $(MSWSYS)\win32api.h + @$(CC) $(cflagsBuild) -Fo$@ ..\sys\share\safeprocs.c $(O)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) @$(CC) $(cflagsBuild) -Fo$@ ..\sys\share\pcsys.c $(O)pctty.o: ..\sys\share\pctty.c $(HACK_H) diff --git a/sys/winnt/stubs.c b/sys/winnt/stubs.c index 3e7fa4c20..af10a7fc0 100644 --- a/sys/winnt/stubs.c +++ b/sys/winnt/stubs.c @@ -96,13 +96,11 @@ clear_screen() return; } -#ifdef TTY_GRAPHICS void backsp() { return; } -#endif int has_color(int color) diff --git a/sys/winnt/windmain.c b/sys/winnt/windmain.c new file mode 100644 index 000000000..9768a8e6f --- /dev/null +++ b/sys/winnt/windmain.c @@ -0,0 +1,757 @@ +/* NetHack 3.6 windmain.c $NHDT-Date: 1543465755 2018/11/29 04:29:15 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.101 $ */ +/* Copyright (c) Derek S. Ray, 2015. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* main.c - Windows */ + +#include "win32api.h" /* for GetModuleFileName */ +#include "hack.h" +#include "dlb.h" +#include +#include + +#if !defined(SAFEPROCS) +#error You must #define SAFEPROCS to build windmain.c +#endif + +#define E extern +static void FDECL(process_options, (int argc, char **argv)); +static void NDECL(nhusage); +static char *FDECL(exepath, (char *)); +char *NDECL(exename); +boolean NDECL(fakeconsole); +void NDECL(freefakeconsole); +E void FDECL(nethack_exit, (int)); +#if defined(MSWIN_GRAPHICS) +E void NDECL(mswin_destroy_reg); +#endif +#undef E + +int NDECL(windows_nhgetch); +void NDECL(windows_nhbell); +int FDECL(windows_nh_poskey, (int *, int *, int *)); +void FDECL(windows_raw_print, (const char *)); +char FDECL(windows_yn_function, (const char *, const char *, CHAR_P)); +void FDECL(windows_getlin, (const char *, char *)); + +char orgdir[PATHLEN]; +extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ +extern int redirect_stdout; /* from sys/share/pcsys.c */ +extern int GUILaunched; +HANDLE hStdOut; +#if defined(MSWIN_GRAPHICS) +char default_window_sys[] = "mswin"; +#endif + +/* + * __MINGW32__ Note + * If the graphics version is built, we don't need a main; it is skipped + * to help MinGW decide which entry point to choose. If both main and + * WinMain exist, the resulting executable won't work correctly. + */ +int +#ifndef __MINGW32__ +main(argc, argv) +#else +mingw_main(argc, argv) +#endif +int argc; +char *argv[]; +{ + boolean save_getreturn_status = getreturn_enabled; + boolean resuming = FALSE; /* assume new game */ + register int fd; + register char *dir; + char *envp = NULL; + char *sptr = NULL; + char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; + char failbuf[BUFSZ]; + + /* + * Get a set of valid safe windowport function + * pointers during early startup initialization. + */ + if (!WINDOWPORT("safe-startup")) + windowprocs = *get_safe_procs(1); + sys_early_init(); +#ifdef _MSC_VER +# ifdef DEBUG + /* set these appropriately for VS debugging */ + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, + _CRTDBG_MODE_DEBUG); /* | _CRTDBG_MODE_FILE);*/ + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); +/*| _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW);*/ +/* use STDERR by default +_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); +_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ +/* Heap Debugging + _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) + | _CRTDBG_ALLOC_MEM_DF + | _CRTDBG_CHECK_ALWAYS_DF + | _CRTDBG_CHECK_CRT_DF + | _CRTDBG_DELAY_FREE_MEM_DF + | _CRTDBG_LEAK_CHECK_DF); + _CrtSetBreakAlloc(1423); +*/ +# endif +#endif + + hname = "NetHack"; /* used for syntax messages */ + /* Save current directory and make sure it gets restored when + * the game is exited. + */ + if (getcwd(orgdir, sizeof orgdir) == (char *) 0) + error("NetHack: current directory path too long"); + dir = nh_getenv("NETHACKDIR"); + if (dir == (char *) 0) + dir = nh_getenv("HACKDIR"); + if (dir == (char *) 0) + dir = exepath(argv[0]); +#ifdef _MSC_VER + if (IsDebuggerPresent()) { + static char exepath[_MAX_PATH]; + /* check if we're running under the debugger so we can get to the right folder anyway */ + if (dir != (char *)0) { + char *top = (char *)0; + + if (strlen(dir) < (_MAX_PATH - 1)) + strcpy(exepath, dir); + top = strstr(exepath, "\\build\\.\\Debug"); + if (!top) top = strstr(exepath, "\\build\\.\\Release"); + if (top) { + *top = '\0'; + if (strlen(exepath) < (_MAX_PATH - (strlen("\\binary\\") + 1))) { + Strcat(exepath, "\\binary\\"); + if (strlen(exepath) < (PATHLEN - 1)) { + dir = exepath; + } + } + } + } + } +#endif + if (dir != (char *)0) { + int prefcnt; + int fd; + boolean have_syscf = FALSE; + + (void) strncpy(hackdir, dir, PATHLEN - 1); + hackdir[PATHLEN - 1] = '\0'; + fqn_prefix[0] = (char *) alloc(strlen(hackdir) + 2); + Strcpy(fqn_prefix[0], hackdir); + append_slash(fqn_prefix[0]); + for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) + fqn_prefix[prefcnt] = fqn_prefix[0]; + /* sysconf should be searched for in this location */ + envp = nh_getenv("COMMONPROGRAMFILES"); + if (envp) { + if ((sptr = index(envp, ';')) != 0) + *sptr = '\0'; + if (strlen(envp) > 0) { + fqn_prefix[SYSCONFPREFIX] = + (char *) alloc(strlen(envp) + 10); + Strcpy(fqn_prefix[SYSCONFPREFIX], envp); + append_slash(fqn_prefix[SYSCONFPREFIX]); + Strcat(fqn_prefix[SYSCONFPREFIX], "NetHack\\"); + } + } + + /* okay so we have the overriding and definitive locaton + for sysconf, but only in the event that there is not a + sysconf file there (for whatever reason), check a secondary + location rather than abort. */ + + /* Is there a SYSCF_FILE there? */ + fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY); + if (fd >= 0) { + /* readable */ + close(fd); + have_syscf = TRUE; + } + + if (!have_syscf) { + /* No SYSCF_FILE where there should be one, and + without an installer, a user may not be able + to place one there. So, let's try somewhere else... */ + fqn_prefix[SYSCONFPREFIX] = fqn_prefix[0]; + + /* Is there a SYSCF_FILE there? */ + fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY); + if (fd >= 0) { + /* readable */ + close(fd); + have_syscf = TRUE; + } + } + + /* user's home directory should default to this - unless + * overridden */ + envp = nh_getenv("USERPROFILE"); + if (envp) { + if ((sptr = index(envp, ';')) != 0) + *sptr = '\0'; + if (strlen(envp) > 0) { + fqn_prefix[CONFIGPREFIX] = + (char *) alloc(strlen(envp) + 2); + Strcpy(fqn_prefix[CONFIGPREFIX], envp); + append_slash(fqn_prefix[CONFIGPREFIX]); + } + } + } + save_getreturn_status = getreturn_enabled; + getreturn_enabled = TRUE; + check_recordfile((char *) 0); + initoptions(); + if (!validate_prefix_locations(failbuf)) { + raw_printf("Some invalid directory locations were specified:\n\t%s\n", + failbuf); + nethack_exit(EXIT_FAILURE); + } + if (!hackdir[0]) + Strcpy(hackdir, orgdir); + if (argc > 1) { + if (argcheck(argc, argv, ARG_VERSION) == 2) + nethack_exit(EXIT_SUCCESS); + + if (argcheck(argc, argv, ARG_DEBUG) == 1) { + argc--; + argv++; + } + if (argcheck(argc, argv, ARG_WINDOWS) == 1) { + argc--; + argv++; + } + if (argc > 1 && !strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { + /* avoid matching "-dec" for DECgraphics; since the man page + * says -d directory, hope nobody's using -desomething_else + */ + argc--; + argv++; + dir = argv[0] + 2; + if (*dir == '=' || *dir == ':') + dir++; + if (!*dir && argc > 1) { + argc--; + argv++; + dir = argv[0]; + } + if (!*dir) + error("Flag -d must be followed by a directory name."); + Strcpy(hackdir, dir); + } + if (argc > 1) { +#if !defined(TTY_GRAPHICS) + int sfd = 0; + boolean tmpconsole = FALSE; + hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); +#endif + /* + * Now we know the directory containing 'record' and + * may do a prscore(). + */ + if (!strncmp(argv[1], "-s", 2)) { +#if !defined(TTY_GRAPHICS) + /* + * Check to see if we're redirecting to a file. + */ + sfd = (int) _fileno(stdout); + redirect_stdout = (sfd >= 0) ? !isatty(sfd) : 0; + + if (!redirect_stdout && !hStdOut) { + raw_printf( + "-s is not supported for the Graphical Interface\n"); + nethack_exit(EXIT_SUCCESS); + } +#endif +#ifdef SYSCF + initoptions(); +#endif + prscore(argc, argv); + +#if !defined(TTY_GRAPHICS) + if (tmpconsole) { + getreturn("to exit"); + freefakeconsole(); + tmpconsole = FALSE; + } +#endif + nethack_exit(EXIT_SUCCESS); + } + +#if defined(MSWIN_GRAPHICS) + if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */ + mswin_destroy_reg(); + nethack_exit(EXIT_SUCCESS); + } +#endif + /* Don't initialize the window system just to print usage */ + if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) { + nhusage(); + +#if !defined(TTY_GRAPHICS) + if (tmpconsole) { + getreturn("to exit"); + freefakeconsole(); + tmpconsole = FALSE; + } +#endif + nethack_exit(EXIT_SUCCESS); + } + } + } + getreturn_enabled = save_getreturn_status; + +/* + * It seems you really want to play. + */ + /* In 3.6.0, several ports process options before they init + * the window port. This allows settings that impact window + * ports to be specified or read from the sys or user config files. + */ + process_options(argc, argv); + + if (argc >= 1 + && !strcmpi(default_window_sys, "mswin") + && strstri(argv[0], "nethackw.exe")) + iflags.windowtype_locked = TRUE; + +#if defined(TTY_GRAPHICS) + if (!iflags.windowtype_locked) + Strcpy(default_window_sys, "tty"); +#else +#if defined(CURSES_GRAPHICS) + if (!iflags.windowtype_locked) + Strcpy(default_window_sys, "curses"); +#endif +#endif + choose_windows(default_window_sys); + if (!dlb_init()) { + pline( + "%s\n%s\n%s\n%s\n\nNetHack was unable to open the required file " + "\"%s\".%s", + copyright_banner_line(1), copyright_banner_line(2), + copyright_banner_line(3), copyright_banner_line(4), DLBFILE, + "\nAre you perhaps trying to run NetHack within a zip utility?"); + error("dlb_init failure."); + } + + u.uhp = 1; /* prevent RIP on early quits */ + u.ux = 0; /* prevent flush_screen() */ + + nethack_enter(argc, argv); + iflags.use_background_glyph = FALSE; + if (WINDOWPORT("mswin")) + iflags.use_background_glyph = TRUE; + if (WINDOWPORT("tty")) + nttty_open(1); + + init_nhwindows(&argc, argv); + + if (WINDOWPORT("tty")) + toggle_mouse_support(); + + /* strip role,race,&c suffix; calls askname() if plname[] is empty + or holds a generic user name like "player" or "games" */ + plnamesuffix(); + set_playmode(); /* sets plname to "wizard" for wizard mode */ + /* until the getlock code is resolved, override askname()'s + setting of renameallowed; when False, player_selection() + won't resent renaming as an option */ + iflags.renameallowed = FALSE; + /* Obtain the name of the logged on user and incorporate + * it into the name. */ + Sprintf(fnamebuf, "%s-%s", get_username(0), plname); + (void) fname_encode( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", '%', + fnamebuf, encodedfnamebuf, BUFSZ); + Sprintf(lock, "%s", encodedfnamebuf); + /* regularize(lock); */ /* we encode now, rather than substitute */ + getlock(); + + /* Set up level 0 file to keep the game state. + */ + fd = create_levelfile(0, (char *) 0); + if (fd < 0) { + raw_print("Cannot create lock file"); + } else { + hackpid = GetCurrentProcessId(); + write(fd, (genericptr_t) &hackpid, sizeof(hackpid)); + nhclose(fd); + } + /* + * Initialize the vision system. This must be before mklev() on a + * new game or before a level restore on a saved game. + */ + vision_init(); + display_gamewindows(); + getreturn_enabled = TRUE; + /* + * First, try to find and restore a save file for specified character. + * We'll return here if new game player_selection() renames the hero. + */ +attempt_restore: + if ((fd = restore_saved_game()) >= 0) { +#ifdef NEWS + if (iflags.news) { + display_file(NEWS, FALSE); + iflags.news = FALSE; + } +#endif + pline("Restoring save file..."); + mark_synch(); /* flush output */ + if (dorecover(fd)) { + resuming = TRUE; /* not starting new game */ + if (discover) + You("are in non-scoring discovery mode."); + if (discover || wizard) { + if (yn("Do you want to keep the save file?") == 'n') + (void) delete_savefile(); + else { + nh_compress(fqname(SAVEF, SAVEPREFIX, 0)); + } + } + } + } + + if (!resuming) { + /* new game: start by choosing role, race, etc; + player might change the hero's name while doing that, + in which case we try to restore under the new name + and skip selection this time if that didn't succeed */ + if (!iflags.renameinprogress) { + player_selection(); + if (iflags.renameinprogress) { + /* player has renamed the hero while selecting role; + discard current lock file and create another for + the new character name */ + goto attempt_restore; + } + } + newgame(); + if (discover) + You("are in non-scoring discovery mode."); + } + moveloop(resuming); + nethack_exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +STATIC_OVL void +process_options(argc, argv) +int argc; +char *argv[]; +{ + int i; + + /* + * Process options. + */ + while (argc > 1 && argv[1][0] == '-') { + argv++; + argc--; + switch (argv[0][1]) { + case 'a': + if (argv[0][2]) { + if ((i = str2align(&argv[0][2])) >= 0) + flags.initalign = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2align(argv[0])) >= 0) + flags.initalign = i; + } + break; + case 'D': + wizard = TRUE, discover = FALSE; + break; + case 'X': + discover = TRUE, wizard = FALSE; + break; +#ifdef NEWS + case 'n': + iflags.news = FALSE; + break; +#endif + case 'u': + if (argv[0][2]) + (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1); + else if (argc > 1) { + argc--; + argv++; + (void) strncpy(plname, argv[0], sizeof(plname) - 1); + } else + raw_print("Player name expected after -u"); + break; + case 'g': + if (argv[0][2]) { + if ((i = str2gend(&argv[0][2])) >= 0) + flags.initgend = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2gend(argv[0])) >= 0) + flags.initgend = i; + } + break; + case 'p': /* profession (role) */ + if (argv[0][2]) { + if ((i = str2role(&argv[0][2])) >= 0) + flags.initrole = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2role(argv[0])) >= 0) + flags.initrole = i; + } + break; + case 'r': /* race */ + if (argv[0][2]) { + if ((i = str2race(&argv[0][2])) >= 0) + flags.initrace = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2race(argv[0])) >= 0) + flags.initrace = i; + } + break; +#if 0 + case 'w': /* windowtype */ +#if defined(TTY_GRAPHICS) + if (!strncmpi(&argv[0][2], "tty", 3)) { + nttty_open(1); + } +#endif + config_error_init(FALSE, "command line", FALSE); + choose_windows(&argv[0][2]); + config_error_done(); + break; +#endif + case '@': + flags.randomall = 1; + break; + default: + if ((i = str2role(&argv[0][1])) >= 0) { + flags.initrole = i; + break; + } else + raw_printf("\nUnknown switch: %s", argv[0]); + /* FALL THROUGH */ + case '?': + nhusage(); + nethack_exit(EXIT_SUCCESS); + } + } +} + +STATIC_OVL void +nhusage() +{ + char buf1[BUFSZ], buf2[BUFSZ], *bufptr; + + buf1[0] = '\0'; + bufptr = buf1; + +#define ADD_USAGE(s) \ + if ((strlen(buf1) + strlen(s)) < (BUFSZ - 1)) \ + Strcat(bufptr, s); + + /* -role still works for those cases which aren't already taken, but + * is deprecated and will not be listed here. + */ + (void) Sprintf(buf2, "\nUsage:\n%s [-d dir] -s [-r race] [-p profession] " + "[maxrank] [name]...\n or", + hname); + ADD_USAGE(buf2); + + (void) Sprintf( + buf2, "\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]", + hname); + ADD_USAGE(buf2); +#ifdef NEWS + ADD_USAGE(" [-n]"); +#endif + if (!iflags.window_inited) + raw_printf("%s\n", buf1); + else + (void) printf("%s\n", buf1); +#undef ADD_USAGE +} + +#ifdef PORT_HELP +void +port_help() +{ + /* display port specific help file */ + display_file(PORT_HELP, 1); +} +#endif /* PORT_HELP */ + +/* validate wizard mode if player has requested access to it */ +boolean +authorize_wizard_mode() +{ + if (!strcmp(plname, WIZARD_NAME)) + return TRUE; + return FALSE; +} + +#define PATH_SEPARATOR '\\' + +#if defined(WIN32) && !defined(WIN32CON) +static char exenamebuf[PATHLEN]; +extern HANDLE hConIn; +extern HANDLE hConOut; +boolean has_fakeconsole; + +char * +exename() +{ + int bsize = PATHLEN; + char *tmp = exenamebuf, *tmp2; + +#ifdef UNICODE + { + TCHAR wbuf[PATHLEN * 4]; + GetModuleFileName((HANDLE) 0, wbuf, PATHLEN * 4); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL); + } +#else + *(tmp + GetModuleFileName((HANDLE) 0, tmp, bsize)) = '\0'; +#endif + tmp2 = strrchr(tmp, PATH_SEPARATOR); + if (tmp2) + *tmp2 = '\0'; + tmp2++; + return tmp2; +} + +boolean +fakeconsole(void) +{ + if (!hStdOut) { + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + + if (!hStdOut && !hStdIn) { + /* Bool rval; */ + AllocConsole(); + AttachConsole(GetCurrentProcessId()); + /* rval = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); */ + freopen("CON", "w", stdout); + freopen("CON", "r", stdin); + } + has_fakeconsole = TRUE; + } + + /* Obtain handles for the standard Console I/O devices */ + hConIn = GetStdHandle(STD_INPUT_HANDLE); + hConOut = GetStdHandle(STD_OUTPUT_HANDLE); +#if 0 + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE)) { + /* Unable to set control handler */ + cmode = 0; /* just to have a statement to break on for debugger */ + } +#endif + return has_fakeconsole; +} +void freefakeconsole() +{ + if (has_fakeconsole) { + FreeConsole(); + } +} +#endif + +#define EXEPATHBUFSZ 256 +char exepathbuf[EXEPATHBUFSZ]; + +char * +exepath(str) +char *str; +{ + char *tmp, *tmp2; + int bsize; + + if (!str) + return (char *) 0; + bsize = EXEPATHBUFSZ; + tmp = exepathbuf; +#ifdef UNICODE + { + TCHAR wbuf[BUFSZ]; + GetModuleFileName((HANDLE) 0, wbuf, BUFSZ); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL); + } +#else + *(tmp + GetModuleFileName((HANDLE) 0, tmp, bsize)) = '\0'; +#endif + tmp2 = strrchr(tmp, PATH_SEPARATOR); + if (tmp2) + *tmp2 = '\0'; + return tmp; +} + +/*ARGSUSED*/ +void +windows_raw_print(str) +const char *str; +{ + if (str) + fprintf(stdout, "%s\n", str); + windows_nhgetch(); + return; +} + +/*ARGSUSED*/ +void +windows_raw_print_bold(str) +const char *str; +{ + windows_raw_print(str); + return; +} + +int +windows_nhgetch() +{ + return getchar(); +} + + +void +windows_nhbell() +{ + return; +} + +/*ARGSUSED*/ +int +windows_nh_poskey(x, y, mod) +int *x, *y, *mod; +{ + return '\033'; +} + +/*ARGSUSED*/ +char +windows_yn_function(query, resp, def) +const char *query; +const char *resp; +char def; +{ + return '\033'; +} + +/*ARGSUSED*/ +static void +windows_getlin(prompt, outbuf) +const char *prompt UNUSED; +char *outbuf; +{ + Strcpy(outbuf, "\033"); +} + +/*pcmain.c*/ diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index 0be1da094..f5121bddc 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -47,6 +47,9 @@ WIN32_FIND_DATA ffd; typedef HWND(WINAPI *GETCONSOLEWINDOW)(); static HWND GetConsoleHandle(void); static HWND GetConsoleHwnd(void); +#if !defined(TTY_GRAPHICS) +extern void NDECL(backsp); +#endif /* The function pointer nt_kbhit contains a kbhit() equivalent * which varies depending on which window port is active. diff --git a/win/win32/vs2017/NetHack.vcxproj b/win/win32/vs2017/NetHack.vcxproj index e51474a65..84daa8434 100644 --- a/win/win32/vs2017/NetHack.vcxproj +++ b/win/win32/vs2017/NetHack.vcxproj @@ -151,15 +151,16 @@ - + GUISTUB;%(PreprocessorDefinitions) + @@ -254,4 +255,4 @@ - \ No newline at end of file + diff --git a/win/win32/vs2017/NetHackW.vcxproj b/win/win32/vs2017/NetHackW.vcxproj index af28761da..c54c8e9f5 100644 --- a/win/win32/vs2017/NetHackW.vcxproj +++ b/win/win32/vs2017/NetHackW.vcxproj @@ -146,14 +146,15 @@ - + TTYSTUB; + @@ -200,4 +201,4 @@ - \ No newline at end of file + diff --git a/win/win32/winhack.c b/win/win32/winhack.c index 9dcbb4e4b..6796a5ce6 100644 --- a/win/win32/winhack.c +++ b/win/win32/winhack.c @@ -15,6 +15,10 @@ #include "mhmain.h" #include "mhmap.h" +#if !defined(SAFEPROCS) +#error You must #define SAFEPROCS to build winhack.c +#endif + /* Borland and MinGW redefine "boolean" in shlwapi.h, so just use the little bit we need */ typedef struct _DLLVERSIONINFO { @@ -66,7 +70,7 @@ NHWinApp _nethack_app; #endif // Foward declarations of functions included in this code module: -extern boolean FDECL(pcmain, (int, char **)); +extern boolean FDECL(main, (int, char **)); static void __cdecl mswin_moveloop(void *); #define MAX_CMDLINE_PARAM 255 @@ -82,24 +86,44 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, TCHAR *p; TCHAR wbuf[BUFSZ]; char buf[BUFSZ]; + DWORD major, minor; - boolean resuming; /* OSVERSIONINFO osvi; */ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); - /* We must initialize state sufficiently to support calls to panic */ + /* + * Get a set of valid safe windowport function + * pointers during early startup initialization. + * + * When get_safe_procs is called with 0 as the param, + * non-functional, but safe function pointers are set + * for all windowport routines. + * + * When get_safe_procs is called with 1 as the param, + * raw_print, raw_print_bold, and wait_synch, and nhgetch + * are set to use C stdio routines via stdio_raw_print, + * stdio_raw_print_bold, stdio_wait_synch, and + * stdio_nhgetch. + */ + windowprocs = *get_safe_procs(0); + + /* + * Now we are going to override a couple + * of the windowprocs functions so that + * error messages are handled in a suitable + * way for the graphical version. + */ windowprocs.win_raw_print = mswin_raw_print; windowprocs.win_raw_print_bold = mswin_raw_print_bold; windowprocs.win_wait_synch = mswin_wait_synch; win10_init(); - sys_early_init(); - /* init applicatio structure */ + /* init application structure */ _nethack_app.hApp = hInstance; _nethack_app.hAccelTable = LoadAccelerators(hInstance, (LPCTSTR) IDC_NETHACKW); @@ -209,10 +233,9 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, } free(savefile); } - resuming = pcmain(argc, argv); - - moveloop(resuming); + /* let main do the argument processing */ + (void) main(argc, argv); return 0; } From b853c6a75b0782ebd66c5a98ebb85182ac47160a Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 18:45:48 -0500 Subject: [PATCH 31/43] Makefile.msc update --- sys/winnt/Makefile.msc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index e8b4e22d3..4cfd29d6b 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -1503,7 +1503,6 @@ $(O)\curswins.c: $(WCURSES)\curswins.c $(WCURSES)\curswins.h $(INCL)\wincurs.h # * $(CFLAGS) replaced with $(cflagsBuild) # * $(CC) replaced with @$(CC) # * targets prefixed with $(O) -# * the single win32api.h reference uncommented # but otherwise untouched. # That means that there is some irrelevant stuff # in here, but maintenance should be easier. @@ -1512,8 +1511,7 @@ $(O)\curswins.c: $(WCURSES)\curswins.c $(WCURSES)\curswins.h $(INCL)\wincurs.h $(O)tos.o: ..\sys\atari\tos.c $(HACK_H) $(INCL)\tcap.h @$(CC) $(cflagsBuild) -Fo$@ ..\sys\atari\tos.c $(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h -$(O)safeprocs.o: ..\sys\share\safeprocs.c $(HACK_H) $(MSWSYS)\win32api.h - @$(CC) $(cflagsBuild) -Fo$@ ..\sys\share\safeprocs.c + @$(CC) $(cflagsBuild) -Fo$@ ..\sys\share\pcmain.c $(O)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) @$(CC) $(cflagsBuild) -Fo$@ ..\sys\share\pcsys.c $(O)pctty.o: ..\sys\share\pctty.c $(HACK_H) From a9342107c8dee746bd2f23e9714ea433323009bc Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 20:09:58 -0500 Subject: [PATCH 32/43] relocate safeproc.c from sys/share to win/share --- {sys => win}/share/safeproc.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {sys => win}/share/safeproc.c (100%) diff --git a/sys/share/safeproc.c b/win/share/safeproc.c similarity index 100% rename from sys/share/safeproc.c rename to win/share/safeproc.c From 4d2abbe23f00b92863a081eea8cd620a9fcddc26 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 20:11:25 -0500 Subject: [PATCH 33/43] Makefile.msc bit --- sys/winnt/Makefile.msc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 4cfd29d6b..c207f254c 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -1461,7 +1461,7 @@ $(O)guistub.o: $(HACK_H) $(MSWSYS)\stubs.c # $(O)winhack.o: $(HACK_H) $(MSWIN)\winhack.c - @$(cc) $(cflagsBuild) -DSAFEPROCS -I$(MSWSYS) -I$(MSWIN) -Fo$@ $(MSWIN)\winhack.c + @$(cc) $(cflagsBuild) -I$(MSWSYS) -I$(MSWIN) -Fo$@ $(MSWIN)\winhack.c #if you aren't linking in the full tty then #include the following stub for proper linkage. From 547f0a32ccf4bd92c83a970db3ba581f42dfcc19 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 20:24:24 -0500 Subject: [PATCH 34/43] Update Visual Studio projects to reflect recent change --- win/win32/vs2017/NetHack.vcxproj | 4 ++-- win/win32/vs2017/NetHackW.vcxproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/win/win32/vs2017/NetHack.vcxproj b/win/win32/vs2017/NetHack.vcxproj index 84daa8434..9f193832a 100644 --- a/win/win32/vs2017/NetHack.vcxproj +++ b/win/win32/vs2017/NetHack.vcxproj @@ -34,7 +34,7 @@ Speed true $(WinWin32Dir);$(IncDir);$(SysWinntDir);$(SysShareDir);$(WinShareDir);%(AdditionalIncludeDirectories) - TILES;WIN32CON;DLB;MSWIN_GRAPHICS;_LIB;%(PreprocessorDefinitions) + TILES;WIN32CON;DLB;MSWIN_GRAPHICS;SAFEPROCS;_LIB;%(PreprocessorDefinitions) kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;%(AdditionalDependencies) @@ -154,7 +154,6 @@ - @@ -162,6 +161,7 @@ + diff --git a/win/win32/vs2017/NetHackW.vcxproj b/win/win32/vs2017/NetHackW.vcxproj index c54c8e9f5..d1ede9f0a 100644 --- a/win/win32/vs2017/NetHackW.vcxproj +++ b/win/win32/vs2017/NetHackW.vcxproj @@ -20,7 +20,7 @@ Disabled true $(WinWin32Dir);$(IncDir);$(SysWinntDir);$(SysShareDir);$(WinShareDir);%(AdditionalIncludeDirectories) - TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;NOTTYGRAPHICS;%(PreprocessorDefinitions) + TILES;_WINDOWS;DLB;MSWIN_GRAPHICS;SAFEPROCS;NOTTYGRAPHICS;%(PreprocessorDefinitions) NDEBUG;%(PreprocessorDefinitions) @@ -149,7 +149,6 @@ - TTYSTUB; @@ -157,6 +156,7 @@ + From 01625455d975f05d41f051d0256cf4ae32f154e2 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 21:17:34 -0500 Subject: [PATCH 35/43] more startup tweaking for Windows --- sys/winnt/windmain.c | 34 +++++++++++++++++++++------------- sys/winnt/winnt.c | 8 ++++---- win/win32/winhack.c | 4 +++- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/sys/winnt/windmain.c b/sys/winnt/windmain.c index 9768a8e6f..72bf24c11 100644 --- a/sys/winnt/windmain.c +++ b/sys/winnt/windmain.c @@ -241,16 +241,20 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ Strcpy(hackdir, dir); } if (argc > 1) { +#if 0 #if !defined(TTY_GRAPHICS) int sfd = 0; boolean tmpconsole = FALSE; hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); #endif +#endif /* 0 */ + /* * Now we know the directory containing 'record' and * may do a prscore(). */ if (!strncmp(argv[1], "-s", 2)) { +#if 0 #if !defined(TTY_GRAPHICS) /* * Check to see if we're redirecting to a file. @@ -264,11 +268,13 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ nethack_exit(EXIT_SUCCESS); } #endif +#endif /* 0 */ #ifdef SYSCF initoptions(); #endif prscore(argc, argv); +#if 0 #if !defined(TTY_GRAPHICS) if (tmpconsole) { getreturn("to exit"); @@ -276,25 +282,26 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ tmpconsole = FALSE; } #endif +#endif /* 0 */ nethack_exit(EXIT_SUCCESS); } - -#if defined(MSWIN_GRAPHICS) - if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */ - mswin_destroy_reg(); - nethack_exit(EXIT_SUCCESS); + if (GUILaunched) { + if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */ + mswin_destroy_reg(); + nethack_exit(EXIT_SUCCESS); + } } -#endif - /* Don't initialize the window system just to print usage */ + /* Don't initialize the full window system just to print usage */ if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) { nhusage(); - +#if 0 #if !defined(TTY_GRAPHICS) if (tmpconsole) { getreturn("to exit"); freefakeconsole(); tmpconsole = FALSE; } +#endif #endif nethack_exit(EXIT_SUCCESS); } @@ -313,18 +320,19 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (argc >= 1 && !strcmpi(default_window_sys, "mswin") - && strstri(argv[0], "nethackw.exe")) + && (strstri(argv[0], "nethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; + if (!iflags.windowtype_locked) { #if defined(TTY_GRAPHICS) - if (!iflags.windowtype_locked) Strcpy(default_window_sys, "tty"); #else #if defined(CURSES_GRAPHICS) - if (!iflags.windowtype_locked) Strcpy(default_window_sys, "curses"); -#endif -#endif +#endif /* CURSES */ +#endif /* TTY */ + } + choose_windows(default_window_sys); if (!dlb_init()) { pline( diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index f5121bddc..c5c214f19 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -495,12 +495,12 @@ void nhassert_failed(const char * exp, const char * file, int line) error(message); } -/* nethack_enter_winnt() is the first thing called from main */ +/* nethack_enter_winnt() is called from main immediately after + initializing the window port */ void nethack_enter_winnt() { -#ifdef TTY_GRAPHICS - nethack_enter_nttty(); -#endif + if (WINDOWPORT("tty")) + nethack_enter_nttty(); } /* CP437 to Unicode mapping according to the Unicode Consortium */ diff --git a/win/win32/winhack.c b/win/win32/winhack.c index 6796a5ce6..c6941d7c0 100644 --- a/win/win32/winhack.c +++ b/win/win32/winhack.c @@ -63,6 +63,8 @@ _nhapply_image_transparent(HDC hDC, int x, int y, int width, int height, // Global Variables: NHWinApp _nethack_app; +extern int GUILaunched; /* We tell shared startup code in windmain.c + that the GUI was launched via this */ #ifdef __BORLANDC__ #define _stricmp(s1, s2) stricmp(s1, s2) @@ -233,7 +235,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, } free(savefile); } - + GUILaunched = 1; /* let main do the argument processing */ (void) main(argc, argv); return 0; From afc34d30163740313120df4a7b3fa25d6ec7c725 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 22:08:55 -0500 Subject: [PATCH 36/43] a late bot() call was observed on GUI Windows so prevent such a crash --- src/botl.c | 3 ++- win/share/safeproc.c | 21 ++++++++++++++++----- win/win32/mswproc.c | 7 +++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/botl.c b/src/botl.c index 2fa84afe0..db96c160b 100644 --- a/src/botl.c +++ b/src/botl.c @@ -234,7 +234,8 @@ do_statusline2() void bot() { - if (youmonst.data && iflags.status_updates) { + /* dosave() flags completion by setting u.uhp to -1 */ + if ((u.uhp != -1) && youmonst.data && iflags.status_updates) { #ifdef STATUS_HILITES bot_via_windowport(); #else diff --git a/win/share/safeproc.c b/win/share/safeproc.c index b3b991a7c..1f268ee16 100644 --- a/win/share/safeproc.c +++ b/win/share/safeproc.c @@ -11,7 +11,7 @@ * assigned to the windowproc function pointers very early * in the startup initialization, perhaps immediately even. * It requires only the following call: - * windowprocs = get_safe_procs(0); + * windowprocs = *get_safe_procs(0); * * The game startup can trigger functions in other modules * that make assumptions on a WindowPort being available @@ -45,12 +45,23 @@ * * The additional platform-independent, but more functional * routines provided in here should be assigned after the - * windowprocs = get_safe_procs() - * call. Here's a list of them: + * windowprocs = *get_safe_procs(n) + * call. * - * + * Usage: + * + * windowprocs = *get_safe_procs(0); + * initializes a set of winprocs function pointers that ensure + * none of the function pointers are left null, but that's all + * it does. + * + * windowprocs = *get_safe_procs(1); + * initializes a set of winprocs functions pointers that ensure + * none of the function pointers are left null, but also + * provides some basic output and input functionality using + * nothing other than C stdio routines (no platform-specific + * or OS-specific code). * - * * *********************************************************** */ diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 3cfcc95bf..85f427725 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -718,6 +718,13 @@ mswin_exit_nhwindows(const char *str) /* Write Window settings to the registry */ mswin_write_reg(); + /* set things back to failsafes */ + windowprocs = *get_safe_procs(0); + + /* and make sure there is still a way to communicate something */ + windowprocs.win_raw_print = mswin_raw_print; + windowprocs.win_raw_print_bold = mswin_raw_print_bold; + windowprocs.win_wait_synch = mswin_wait_synch; } /* Prepare the window to be suspended. */ From 8d516d4358586b3df01da56ae1c9400d52ad1d0e Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 22:34:50 -0500 Subject: [PATCH 37/43] clean up Windows exit routines --- sys/share/pcsys.c | 15 +++------------ sys/winnt/winnt.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c index 0286ad50b..8a99b181e 100644 --- a/sys/share/pcsys.c +++ b/sys/share/pcsys.c @@ -391,19 +391,11 @@ char *name; return; } -#ifdef WIN32 -boolean getreturn_enabled; -int redirect_stdout; -#endif - +#ifndef WIN32 void getreturn(str) const char *str; { -#ifdef WIN32 - if (!getreturn_enabled) - return; -#endif #ifdef TOS msmsg("Hit %s.", str); #else @@ -414,7 +406,6 @@ const char *str; return; } -#ifndef WIN32 void msmsg VA_DECL(const char *, fmt) { @@ -492,7 +483,7 @@ const char *name, *mode; return (FILE *) 0; } -#if defined(MICRO) || defined(WIN32) || defined(OS2) +#if defined(MICRO) || defined(OS2) void nethack_exit(code) int code; @@ -551,4 +542,4 @@ msexit() wait_synch(); return; } -#endif /* MICRO || WIN32 || OS2 */ +#endif /* MICRO || OS2 */ diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index c5c214f19..86432e028 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -43,6 +43,9 @@ boolean win32_cursorblink; /* globals required within here */ HANDLE ffhandle = (HANDLE) 0; WIN32_FIND_DATA ffd; +extern int GUILaunched; +boolean getreturn_enabled; +int redirect_stdout; typedef HWND(WINAPI *GETCONSOLEWINDOW)(); static HWND GetConsoleHandle(void); @@ -495,6 +498,38 @@ void nhassert_failed(const char * exp, const char * file, int line) error(message); } +void +nethack_exit(code) +int code; +{ + /* Only if we started from the GUI, not the command prompt, + * we need to get one last return, so the score board does + * not vanish instantly after being created. + * GUILaunched is defined and set in nttty.c. + */ + synch_cursor(); + if (GUILaunched) + getreturn("to end"); + synch_cursor(); + getreturn_enabled = TRUE; + wait_synch(); + exit(code); +} + +void +getreturn(str) +const char *str; +{ + char buf[BUFSZ]; + + if (!getreturn_enabled) + return; + Sprintf(buf,"Hit %s.", str); + raw_print(buf); + wait_synch(); + return; +} + /* nethack_enter_winnt() is called from main immediately after initializing the window port */ void nethack_enter_winnt() From f706ba094cc805a6be5c3d4377ba4a1c7831f388 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 8 Dec 2018 23:04:53 -0500 Subject: [PATCH 38/43] clean up some tear-down Windows continued to use function pointers belonging to particular window ports that were still assigned even after that port (tty) had gone through its shutdown procedures when requested by the core. Fix that and return to the minimal function pointers that are not part of any of the actual WindowPorts. --- sys/winnt/winnt.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index 86432e028..75dda292d 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -507,12 +507,13 @@ int code; * not vanish instantly after being created. * GUILaunched is defined and set in nttty.c. */ - synch_cursor(); + if (GUILaunched) - getreturn("to end"); - synch_cursor(); - getreturn_enabled = TRUE; - wait_synch(); + getreturn_enabled = TRUE; + else + windowprocs = *get_safe_procs(1); + if (getreturn_enabled) + wait_synch(); exit(code); } From 2b127ee4533e0607a15c7c82b74580fda5865ddc Mon Sep 17 00:00:00 2001 From: copperwater Date: Sun, 9 Dec 2018 09:51:47 -0500 Subject: [PATCH 39/43] Fix resurfacing of "foxen" pluralization bug Inadvertently reintroduced in f9f1236. It was just the conditional that was bad: due to resolving the possible buffer underflow when comparing to "muskox", the pluralizer now only adds -es when the length of the string is greater than 5. So for "box" and "fox" the pluralizer will never add the -es ending, since they are greater than 5. This commit checks for "does not end in muskox" correctly. --- src/objnam.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objnam.c b/src/objnam.c index 7ed8f33fb..05aade695 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -2115,7 +2115,7 @@ const char *const *alt_as_is; /* another set like as_is[] */ /* skip "ox" -> "oxen" entry when pluralizing "ox" unless it is muskox */ if (to_plural && baselen > 2 && !strcmpi(endstring - 2, "ox") - && baselen > 5 && strcmpi(endstring - 6, "muskox")) { + && !(baselen > 5 && !strcmpi(endstring - 6, "muskox"))) { /* "fox" -> "foxes" */ Strcasecpy(endstring, "es"); return TRUE; From cb191e6a0e7d548453747a4ffe7f39db002c382d Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 9 Dec 2018 10:14:19 -0500 Subject: [PATCH 40/43] more windmain --- sys/winnt/windmain.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sys/winnt/windmain.c b/sys/winnt/windmain.c index 72bf24c11..21a2fa1e2 100644 --- a/sys/winnt/windmain.c +++ b/sys/winnt/windmain.c @@ -35,7 +35,7 @@ char FDECL(windows_yn_function, (const char *, const char *, CHAR_P)); void FDECL(windows_getlin, (const char *, char *)); char orgdir[PATHLEN]; -extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ +boolean getreturn_enabled; extern int redirect_stdout; /* from sys/share/pcsys.c */ extern int GUILaunched; HANDLE hStdOut; @@ -58,7 +58,6 @@ mingw_main(argc, argv) int argc; char *argv[]; { - boolean save_getreturn_status = getreturn_enabled; boolean resuming = FALSE; /* assume new game */ register int fd; register char *dir; @@ -199,8 +198,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } } } - save_getreturn_status = getreturn_enabled; - getreturn_enabled = TRUE; + if (GUILaunched || IsDebuggerPresent()) { + getreturn_enabled = TRUE; + } check_recordfile((char *) 0); initoptions(); if (!validate_prefix_locations(failbuf)) { @@ -307,7 +307,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } } } - getreturn_enabled = save_getreturn_status; /* * It seems you really want to play. @@ -393,7 +392,6 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ */ vision_init(); display_gamewindows(); - getreturn_enabled = TRUE; /* * First, try to find and restore a save file for specified character. * We'll return here if new game player_selection() renames the hero. From d93b8b50ae5aca7b5c25b8eef427ee9bf4e88bcb Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 9 Dec 2018 10:31:07 -0500 Subject: [PATCH 41/43] fixes entry for foxen refix commit e0927cd983097392efd06cec32cba43ee828fb82 fixes #167 --- doc/fixes36.2 | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index b4403f0fe..ac5e5af87 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -282,6 +282,7 @@ changing Sting's description to be "(weapon in hand) (light blue aura)" was to "(weapon in hand, flickering/glimmering/gleaming light blue)" fix bit-use collision between WC2_TERM_SIZE and WC2_RESET_STATUS in include/winprocs.h following a recent merge +fix foxen pluralization again after underflow remedy reintroduced the problem tty: turn off an optimization that is the suspected cause of Windows reported partial status lines following level changes tty: ensure that current status fields are always copied to prior status From 46d2724f584a7422388c36bfcd4a378fc63a0705 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 9 Dec 2018 13:09:30 -0500 Subject: [PATCH 42/43] some early startup adjustments --- sys/winnt/windmain.c | 5 +++++ sys/winnt/winnt.c | 20 +++++++++++++++++--- win/share/safeproc.c | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/sys/winnt/windmain.c b/sys/winnt/windmain.c index 21a2fa1e2..6b4b0491c 100644 --- a/sys/winnt/windmain.c +++ b/sys/winnt/windmain.c @@ -33,6 +33,7 @@ int FDECL(windows_nh_poskey, (int *, int *, int *)); void FDECL(windows_raw_print, (const char *)); char FDECL(windows_yn_function, (const char *, const char *, CHAR_P)); void FDECL(windows_getlin, (const char *, char *)); +extern int NDECL(windows_console_custom_nhgetch); char orgdir[PATHLEN]; boolean getreturn_enabled; @@ -72,6 +73,9 @@ char *argv[]; */ if (!WINDOWPORT("safe-startup")) windowprocs = *get_safe_procs(1); + if (!GUILaunched) + windowprocs.win_nhgetch = windows_console_custom_nhgetch; + sys_early_init(); #ifdef _MSC_VER # ifdef DEBUG @@ -311,6 +315,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ /* * It seems you really want to play. */ + /* In 3.6.0, several ports process options before they init * the window port. This allows settings that impact window * ports to be specified or read from the sys or user config files. diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index 75dda292d..c75e53c47 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -53,6 +53,7 @@ static HWND GetConsoleHwnd(void); #if !defined(TTY_GRAPHICS) extern void NDECL(backsp); #endif +int NDECL(windows_console_custom_nhgetch); /* The function pointer nt_kbhit contains a kbhit() equivalent * which varies depending on which window port is active. @@ -508,15 +509,28 @@ int code; * GUILaunched is defined and set in nttty.c. */ - if (GUILaunched) - getreturn_enabled = TRUE; - else + + if (!GUILaunched) { windowprocs = *get_safe_procs(1); + /* use our custom version which works + a little cleaner than the stdio one */ + windowprocs.win_nhgetch = windows_console_custom_nhgetch; + } if (getreturn_enabled) wait_synch(); exit(code); } +#undef kbhit +#include + +int +windows_console_custom_nhgetch(VOID_ARGS) +{ + return _getch(); +} + + void getreturn(str) const char *str; diff --git a/win/share/safeproc.c b/win/share/safeproc.c index 1f268ee16..671ee1822 100644 --- a/win/share/safeproc.c +++ b/win/share/safeproc.c @@ -527,7 +527,7 @@ stdio_wait_synch() fprintf(stdout, "--More--"); (void) fflush(stdout); - while (!index(valid, stdio_nhgetch())) + while (!index(valid, nhgetch())) ; } From 88bbf781d146c022ecad1790b01e85bbb4ba0958 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sun, 9 Dec 2018 11:57:50 -0800 Subject: [PATCH 43/43] Three fixes to NetHackW font handling. Check that player level is valid before checking if it is rogue. Prevent attempting to pick a font that is too small. Don't leak fonts when trying to find a font that fits. --- win/win32/mhfont.h | 3 +++ win/win32/mhmap.c | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/win/win32/mhfont.h b/win/win32/mhfont.h index 20b5d1400..9402d54a9 100644 --- a/win/win32/mhfont.h +++ b/win/win32/mhfont.h @@ -9,6 +9,9 @@ #include "winMS.h" +#define MIN_FONT_WIDTH 9 +#define MIN_FONT_HEIGHT 12 + typedef struct cached_font { int code; HFONT hFont; diff --git a/win/win32/mhmap.c b/win/win32/mhmap.c index d401c4c3c..067d9a6e9 100644 --- a/win/win32/mhmap.c +++ b/win/win32/mhmap.c @@ -160,7 +160,10 @@ mswin_map_stretch(HWND hWnd, LPSIZE map_size, BOOL redraw) // calculate back buffer scale data->monitorScale = win10_monitor_scale(hWnd); - if (data->bAsciiMode || Is_rogue_level(&u.uz)) { + boolean bText = data->bAsciiMode || + (u.uz.dlevel != 0 && Is_rogue_level(&u.uz)); + + if (bText) { data->backScale = data->monitorScale; } else { data->backScale = 1.0; @@ -170,7 +173,7 @@ mswin_map_stretch(HWND hWnd, LPSIZE map_size, BOOL redraw) data->xBackTile = (int) (data->tileWidth * data->backScale); data->yBackTile = (int) (data->tileHeight * data->backScale); - if (data->bAsciiMode || Is_rogue_level(&u.uz)) { + if (bText) { LOGFONT lgfnt; ZeroMemory(&lgfnt, sizeof(lgfnt)); @@ -195,21 +198,27 @@ mswin_map_stretch(HWND hWnd, LPSIZE map_size, BOOL redraw) } TEXTMETRIC textMetrics; - HFONT font; + HFONT font = NULL; while (1) { + + if (font != NULL) + DeleteObject(font); + font = CreateFontIndirect(&lgfnt); SelectObject(data->backBufferDC, font); GetTextMetrics(data->backBufferDC, &textMetrics); - if (textMetrics.tmHeight > data->yBackTile) { + if (textMetrics.tmHeight > data->yBackTile && + lgfnt.lfHeight < -MIN_FONT_HEIGHT) { lgfnt.lfHeight++; continue; } - if (textMetrics.tmAveCharWidth > data->xBackTile) { + if (textMetrics.tmAveCharWidth > data->xBackTile && + lgfnt.lfWeight < -MIN_FONT_WIDTH) { lgfnt.lfWidth++; continue; } @@ -270,7 +279,7 @@ mswin_map_stretch(HWND hWnd, LPSIZE map_size, BOOL redraw) } else { - if (data->bAsciiMode || Is_rogue_level(&u.uz)) { + if (bText) { data->frontScale = 1.0; } else { data->frontScale = data->monitorScale;