diff --git a/doc/fixes36.3 b/doc/fixes36.3 index 707de2ca2..1f7985f01 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -248,6 +248,7 @@ in symset:curses, symbol S_tree was accidentally set to horizontal line where plus-or-minus sign was meant; also, change S_bars to not-equals sign percentage highlighting for Xp broke up/down/changed highlighting for it; it was flagged as having gone up every time the percentage changed +deaf change to zap_over_floor needed to be restricted to player actions only curses: sometimes the message window would show a blank line after a prompt curses: the change to show map in columns 1..79 instead of 2..80 made the highlight for '@' show up in the wrong place if clipped map had been diff --git a/include/decl.h b/include/decl.h index b6ef548eb..66033f783 100644 --- a/include/decl.h +++ b/include/decl.h @@ -409,6 +409,9 @@ E const char *const monexplain[], invisexplain[], *const oclass_names[]; #define PREFIXES_IN_USE #endif +#ifdef WIN32 +E boolean fqn_prefix_locked[PREFIX_COUNT]; +#endif #ifdef PREFIXES_IN_USE E const char *fqn_prefix_names[PREFIX_COUNT]; #endif diff --git a/include/flag.h b/include/flag.h index 135cf0f54..89535b53e 100644 --- a/include/flag.h +++ b/include/flag.h @@ -254,6 +254,8 @@ struct instance_flags { boolean mon_polycontrol; /* debug: control monster polymorphs */ boolean in_dumplog; /* doing the dumplog right now? */ boolean in_parse; /* is a command being parsed? */ + /* suppress terminate during options parsing, for --showpaths */ + boolean initoptions_noterminate; /* stuff that is related to options and/or user or platform preferences */ diff --git a/src/decl.c b/src/decl.c index 54468ad85..42c8b62b5 100644 --- a/src/decl.c +++ b/src/decl.c @@ -110,7 +110,13 @@ const char *materialnm[] = { "mysterious", "liquid", "wax", "organic", /* Global windowing data, defined here for multi-window-system support */ NEARDATA winid WIN_MESSAGE, WIN_STATUS, WIN_MAP, WIN_INVEN; - +#ifdef WIN32 +boolean fqn_prefix_locked[PREFIX_COUNT] = { FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, + FALSE }; +#endif + #ifdef PREFIXES_IN_USE const char *fqn_prefix_names[PREFIX_COUNT] = { "hackdir", "leveldir", "savedir", "bonesdir", "datadir", diff --git a/src/files.c b/src/files.c index a9566c687..fd275c904 100644 --- a/src/files.c +++ b/src/files.c @@ -106,6 +106,10 @@ extern void FDECL(amii_set_text_font, (char *, int)); #ifndef WIN_CE #define DeleteFile unlink #endif +#ifdef WIN32 +/*from windmain.c */ +extern char *FDECL(translate_path_variables, (const char *, char *)); +#endif #endif #ifdef MAC @@ -299,6 +303,13 @@ const char *basenam; int whichprefix UNUSED_if_not_PREFIXES_IN_USE; int buffnum UNUSED_if_not_PREFIXES_IN_USE; { +#ifdef PREFIXES_IN_USE + char *bufptr; +#endif +#ifdef WIN32 + char tmpbuf[BUFSZ]; +#endif + #ifndef PREFIXES_IN_USE return basenam; #else @@ -310,15 +321,19 @@ int buffnum UNUSED_if_not_PREFIXES_IN_USE; impossible("Invalid fqn_filename_buffer specified: %d", buffnum); buffnum = 0; } - if (strlen(g.fqn_prefix[whichprefix]) + strlen(basenam) - >= FQN_MAX_FILENAME) { - impossible("fqname too long: %s + %s", g.fqn_prefix[whichprefix], - basenam); + bufptr = g.fqn_prefix[whichprefix]; +#ifdef WIN32 + if (strchr(g.fqn_prefix[whichprefix], '%') + || strchr(g.fqn_prefix[whichprefix], '~')) + bufptr = translate_path_variables(g.fqn_prefix[whichprefix], tmpbuf); +#endif + if (strlen(bufptr) + strlen(basenam) >= FQN_MAX_FILENAME) { + impossible("fqname too long: %s + %s", bufptr, basenam); return basenam; /* XXX */ } - Strcpy(fqn_filename_buffer[buffnum], g.fqn_prefix[whichprefix]); + Strcpy(fqn_filename_buffer[buffnum], bufptr); return strcat(fqn_filename_buffer[buffnum], basenam); -#endif +#endif /* !PREFIXES_IN_USE */ } int @@ -2390,6 +2405,10 @@ int prefixid; if (!bufp) return; +#ifdef WIN32 + if (fqn_prefix_locked[prefixid]) + return; +#endif /* Backward compatibility, ignore trailing ;n */ if ((ptr = index(bufp, ';')) != 0) *ptr = '\0'; @@ -4129,6 +4148,10 @@ assure_syscf_file() { int fd; +#ifdef WIN32 + /* We are checking that the sysconf exists ... lock the path */ + fqn_prefix_locked[SYSCONFPREFIX] = TRUE; +#endif /* * All we really care about is the end result - can we read the file? * So just check that directly. diff --git a/src/muse.c b/src/muse.c index 5173952a3..770e6b60d 100644 --- a/src/muse.c +++ b/src/muse.c @@ -2478,8 +2478,8 @@ boolean by_you; /* true: if mon kills itself, hero gets credit/blame */ vis = FALSE; /* skip makeknown() below */ res = FALSE; /* failed to cure sliming */ } else { - m_useup(mon, obj); /* before explode() */ dmg = (2 * (rn1(3, 3) + 2 * bcsign(obj)) + 1) / 3; + m_useup(mon, obj); /* before explode() */ /* -11 => monster's fireball */ explode(mon->mx, mon->my, -11, dmg, SCROLL_CLASS, /* by_you: override -11 for mon but not others */ diff --git a/src/options.c b/src/options.c index e4cc893d0..fef599d4a 100644 --- a/src/options.c +++ b/src/options.c @@ -661,7 +661,7 @@ initoptions() /* ... and _must_ parse correctly. */ if (!read_config_file(SYSCF_FILE, SET_IN_SYS)) { - if (config_error_done()) + if (config_error_done() && !iflags.initoptions_noterminate) nh_terminate(EXIT_FAILURE); } config_error_done(); diff --git a/src/zap.c b/src/zap.c index be7c85464..1151297b5 100644 --- a/src/zap.c +++ b/src/zap.c @@ -4435,7 +4435,9 @@ short exploding_wand_typ; } else if (is_pool(x, y)) { const char *msgtxt = (!Deaf) ? "You hear hissing gas." /* Deaf-aware */ - : "That seemed remarkably uneventful."; + : (type >= 0) + ? "That seemed remarkably uneventful." + : (const char *) 0; if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP */ if (see_it) @@ -4449,7 +4451,8 @@ short exploding_wand_typ; if (see_it) msgtxt = "The water evaporates."; } - Norep("%s", msgtxt); + if (msgtxt) + Norep("%s", msgtxt); if (lev->typ == ROOM) newsym(x, y); } else if (IS_FOUNTAIN(lev->typ)) { diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index a39bd8a0b..15acd2ef7 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -118,7 +118,9 @@ char *argv[]; #ifdef CHDIR chdirx((char *) 0, 0); #endif + iflags.initoptions_noterminate = TRUE; initoptions(); + iflags.initoptions_noterminate = FALSE; reveal_paths(); exit(EXIT_SUCCESS); } diff --git a/sys/winnt/.nethackrc.template b/sys/winnt/.nethackrc.template index 50605b27d..b2bacbd7f 100644 --- a/sys/winnt/.nethackrc.template +++ b/sys/winnt/.nethackrc.template @@ -143,28 +143,24 @@ OPTIONS=vary_msgcount:4 # IMPORTANT: If you change any of these locations, the directories they # point at must exist. NetHack will not create them for you. # -# HACKDIR is the default location for everything. -# Note: On Windows HACKDIR defaults to the location -# of the NetHack.exe or NetHackw.exe file so -# setting HACKDIR below to override that is -# not usually necessary or recommended. -#HACKDIR=c:\games\nethack +# The location that documentation and helps files are placed +#HACKDIR=c:\User\USERNAME\NetHack\3.6 # -# The location that level files in progress are stored (default=HACKDIR, writeable) -#LEVELDIR=c:\nethack\levels +# The location that level files in progress are stored (writeable) +#LEVELDIR=c:\User\USERNAME\AppData\Local\NetHack\3.6 # -# The location where saved games are kept (default=HACKDIR, writeable) -#SAVEDIR=c:\nethack\save +# The location where saved games are kept (writeable) +#SAVEDIR=c:\User\USERNAME\AppData\Local\NetHack\3.6 # -# The location that bones files are kept (default=HACKDIR, writeable) -#BONESDIR=c:\nethack\save +# The location that bones files are kept (writeable) +#BONESDIR=c:\ProgramData\NetHack\3.6 # -# The location that file synchronization locks are stored (default=HACKDIR, writeable) -#LOCKDIR=c:\nethack\levels +# The location that score files are kept (writeable) +#SCOREDIR=c:\ProgramData\NetHack\3.6 +# +# The location that file synchronization locks are stored (writeable) +#LOCKDIR=c:\ProgramData\NetHack\3.6 # -# The location that a record of game aborts and self-diagnosed game problems -# is kept (default=HACKDIR, writeable) -#TROUBLEDIR=c:\nethack\trouble # Finnish keyboards might need these modifications uncommented. # For \, @, $, [, | diff --git a/sys/winnt/sysconf.template b/sys/winnt/sysconf.template index 93aaa1ddc..058b29122 100644 --- a/sys/winnt/sysconf.template +++ b/sys/winnt/sysconf.template @@ -77,30 +77,27 @@ WIZARDS=* # IMPORTANT: If you change any of these locations, the directories they # point at must exist. NetHack will not create them for you. # -# HACKDIR is the default location for everything. -# Note: On Windows HACKDIR defaults to the location -# of the NetHack.exe or NetHackw.exe file so -# setting HACKDIR below to override that is -# not usually necessary or recommended. -#HACKDIR=c:\games\nethack -# # The location that users can adjust their config file startup options -#CONFIGDIR=c:\games\nethack -# -# The location that level files in progress are stored (default=HACKDIR, writeable) -#LEVELDIR=c:\nethack\levels -# -# The location where saved games are kept (default=HACKDIR, writeable) -#SAVEDIR=c:\nethack\save -# -# The location that bones files are kept (default=HACKDIR, writeable) -#BONESDIR=c:\nethack\save -# -# The location that file synchronization locks are stored (default=HACKDIR, writeable) -#LOCKDIR=c:\nethack\levels +#CONFIGDIR=c:\User\USERNAME\NetHack # # The location that a record of game aborts and self-diagnosed game problems # is kept (default=HACKDIR, writeable) -#TROUBLEDIR=c:\nethack\trouble - - +#TROUBLEDIR=c:\User\USERNAME\NetHack\3.6 +# +# The location that documentation and helps files are placed +#HACKDIR=c:\User\USERNAME\NetHack\3.6 +# +# The location that level files in progress are stored (writeable) +#LEVELDIR=c:\User\USERNAME\AppData\Local\NetHack\3.6 +# +# The location where saved games are kept (writeable) +#SAVEDIR=c:\User\USERNAME\AppData\Local\NetHack\3.6 +# +# The location that bones files are kept (writeable) +#BONESDIR=c:\ProgramData\NetHack\3.6 +# +# The location that score files are kept (writeable) +#SCOREDIR=c:\ProgramData\NetHack\3.6 +# +# The location that file synchronization locks are stored (writeable) +#LOCKDIR=c:\ProgramData\NetHack\3.6 diff --git a/sys/winnt/windmain.c b/sys/winnt/windmain.c index 8f9c76474..c65a8037d 100644 --- a/sys/winnt/windmain.c +++ b/sys/winnt/windmain.c @@ -24,6 +24,7 @@ static void FDECL(process_options, (int argc, char **argv)); static void NDECL(nhusage); static char *NDECL(get_executable_path); +char *FDECL(translate_path_variables, (char *, char *)); char *NDECL(exename); boolean NDECL(fakeconsole); void NDECL(freefakeconsole); @@ -61,8 +62,7 @@ char default_window_sys[] = "mswin"; static struct stat hbuf; #endif #include -#if defined(WIN32) || defined(MSDOS) -#endif + extern char orgdir[]; @@ -99,13 +99,17 @@ void build_known_folder_path( const KNOWNFOLDERID * folder_id, char * path, - size_t path_size) + size_t path_size, + boolean versioned) { get_known_folder_path(folder_id, path, path_size); strcat(path, "\\NetHack\\"); create_directory(path); - Sprintf(eos(path), "%d.%d\\", VERSION_MAJOR, VERSION_MINOR); - create_directory(path); + if (versioned) { + Sprintf(eos(path), "%d.%d\\", + VERSION_MAJOR, VERSION_MINOR); + create_directory(path); + } } void @@ -148,97 +152,44 @@ folder_file_exists(const char * folder, const char * file_name) return file_exists(path); } -/* - * Rules for setting prefix locations - * - * COMMON_NETHACK_PATH = %COMMONPROGRAMFILES%\NetHack\3.6\ - * PROFILE_PATH = %SystemDrive%\Users\%USERNAME%\ - * - * NETHACK_PROFILE_PATH = PROFILE_PATH\NetHack\3.6\ - * NETHACK_PER_USER_DATA_PATH = PROFILE_PATH\AppData\Local\NetHack\3.6\ - * NETHACK_GLOBAL_DATA_PATH = %SystemDrive%\ProgramData\NetHack\3.6\ - * EXECUTABLE_PATH = path to where .exe lives - * - * HACKPREFIX: - * - use environment variable NETHACKDIR if variable is defined - * - otherwise use environment variable HACKDIR if variable is defined - * - otherwise if store install use NETHACK_PROFILE_PATH - * - otherwise if manual install use EXECUTABLE_PATH - * - * LEVELPREFIX, SAVEPREFIX: - * - if store install use NETHACK_PER_USER_DATA_PATH - * - if manual install use HACKPREFIX - * - * BONESPREFIX, SCOREPREFIX, LOCKPREFIX: - * - if store install use NETHACK_GLOBAL_DATA_PATH - * - if manual install use HACKPREFIX - * - * DATAPREFIX - * - if store install use EXECUTABLE_PATH - * - if manual install use HACKPREFIX - * - * SYSCONFPREFIX - * - use COMMON_NETHACK_PATH if sysconf present - * - otherwise use HACKPREFIX - * - * CONFIGPREFIX - * - if manual install use PROFILE_PATH - * - if store install use NETHACK_PROFILE_PATH - */ - void set_default_prefix_locations(const char *programPath) { char *envp = NULL; char *sptr = NULL; - static char hack_path[MAX_PATH]; static char executable_path[MAX_PATH]; - static char nethack_profile_path[MAX_PATH]; - static char nethack_per_user_data_path[MAX_PATH]; - static char nethack_global_data_path[MAX_PATH]; - static char sysconf_path[MAX_PATH]; + static char profile_path[MAX_PATH]; + static char versioned_profile_path[MAX_PATH]; + static char versioned_user_data_path[MAX_PATH]; + static char versioned_global_data_path[MAX_PATH]; static char versioninfo[20]; strcpy(executable_path, get_executable_path()); append_slash(executable_path); - build_environment_path("NETHACKDIR", NULL, hack_path, sizeof(hack_path)); + build_known_folder_path(&FOLDERID_Profile, profile_path, + sizeof(profile_path), FALSE); - if (hack_path[0] == '\0') - build_environment_path("HACKDIR", NULL, hack_path, sizeof(hack_path)); - - build_known_folder_path(&FOLDERID_Profile, nethack_profile_path, - sizeof(nethack_profile_path)); + build_known_folder_path(&FOLDERID_Profile, versioned_profile_path, + sizeof(profile_path), TRUE); build_known_folder_path(&FOLDERID_LocalAppData, - nethack_per_user_data_path, sizeof(nethack_per_user_data_path)); + versioned_user_data_path, sizeof(versioned_user_data_path), TRUE); build_known_folder_path(&FOLDERID_ProgramData, - nethack_global_data_path, sizeof(nethack_global_data_path)); + versioned_global_data_path, sizeof(versioned_global_data_path), TRUE); - if (hack_path[0] == '\0') - strcpy(hack_path, nethack_profile_path); - - g.fqn_prefix[LEVELPREFIX] = nethack_per_user_data_path; - g.fqn_prefix[SAVEPREFIX] = nethack_per_user_data_path; - g.fqn_prefix[BONESPREFIX] = nethack_global_data_path; + g.fqn_prefix[SYSCONFPREFIX] = versioned_global_data_path; + g.fqn_prefix[CONFIGPREFIX] = profile_path; + g.fqn_prefix[HACKPREFIX] = versioned_profile_path; + g.fqn_prefix[SAVEPREFIX] = versioned_user_data_path; + g.fqn_prefix[LEVELPREFIX] = versioned_user_data_path; + g.fqn_prefix[BONESPREFIX] = versioned_global_data_path; + g.fqn_prefix[SCOREPREFIX] = versioned_global_data_path; + g.fqn_prefix[LOCKPREFIX] = versioned_global_data_path; + g.fqn_prefix[TROUBLEPREFIX] = versioned_profile_path; g.fqn_prefix[DATAPREFIX] = executable_path; - g.fqn_prefix[SCOREPREFIX] = nethack_global_data_path; - g.fqn_prefix[LOCKPREFIX] = nethack_global_data_path; - g.fqn_prefix[CONFIGPREFIX] = nethack_profile_path; - - g.fqn_prefix[HACKPREFIX] = hack_path; - g.fqn_prefix[TROUBLEPREFIX] = hack_path; - - Sprintf(versioninfo, "NetHack\\%d.%d", VERSION_MAJOR, VERSION_MINOR); - build_environment_path("COMMONPROGRAMFILES", versioninfo, sysconf_path, - sizeof(sysconf_path)); - - if(!folder_file_exists(sysconf_path, SYSCF_FILE)) - strcpy(sysconf_path, hack_path); - - g.fqn_prefix[SYSCONFPREFIX] = sysconf_path; } @@ -304,43 +255,50 @@ update_file( } -void copy_config_content() +void copy_sysconf_content() { - /* Keep templates up to date */ - /* TODO: Update the package to store config file as .nethackrc */ - update_file(g.fqn_prefix[CONFIGPREFIX], CONFIG_TEMPLATE, - g.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE, FALSE); + /* Using the SYSCONFPREFIX path, lock it so that it does not change */ + fqn_prefix_locked[SYSCONFPREFIX] = TRUE; + update_file(g.fqn_prefix[SYSCONFPREFIX], SYSCF_TEMPLATE, g.fqn_prefix[DATAPREFIX], SYSCF_TEMPLATE, FALSE); + update_file(g.fqn_prefix[SYSCONFPREFIX], SYMBOLS_TEMPLATE, + g.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, FALSE); + + /* If the required early game file does not exist, copy it */ + copy_file(g.fqn_prefix[SYSCONFPREFIX], SYSCF_FILE, + g.fqn_prefix[DATAPREFIX], SYSCF_TEMPLATE); + + update_file(g.fqn_prefix[SYSCONFPREFIX], SYMBOLS, + g.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, TRUE); +} + +void copy_config_content() +{ + /* Using the CONFIGPREFIX path, lock it so that it does not change */ + fqn_prefix_locked[CONFIGPREFIX] = TRUE; + + /* Keep templates up to date */ + update_file(g.fqn_prefix[CONFIGPREFIX], CONFIG_TEMPLATE, + g.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE, FALSE); + /* If the required early game file does not exist, copy it */ /* NOTE: We never replace .nethackrc or sysconf */ copy_file(g.fqn_prefix[CONFIGPREFIX], CONFIG_FILE, g.fqn_prefix[DATAPREFIX], CONFIG_TEMPLATE); - copy_file(g.fqn_prefix[SYSCONFPREFIX], SYSCF_FILE, - g.fqn_prefix[DATAPREFIX], SYSCF_TEMPLATE); - - /* Update symbols and save a copy if we are replacing */ - /* TODO: Can't HACKDIR be changed during option parsing - causing us to perhaps be checking options against the wrong - symbols file? */ - update_file(g.fqn_prefix[HACKPREFIX], SYMBOLS, - g.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, TRUE); } void copy_hack_content() { + nhassert(fqn_prefix_locked[HACKPREFIX]); + /* Keep Guidebook and opthelp up to date */ update_file(g.fqn_prefix[HACKPREFIX], GUIDEBOOK_FILE, g.fqn_prefix[DATAPREFIX], GUIDEBOOK_FILE, FALSE); update_file(g.fqn_prefix[HACKPREFIX], OPTIONFILE, g.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); - - /* Keep templates up to date */ - update_file(g.fqn_prefix[HACKPREFIX], SYMBOLS_TEMPLATE, - g.fqn_prefix[DATAPREFIX], SYMBOLS_TEMPLATE, FALSE); - } /* @@ -408,25 +366,34 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ set_default_prefix_locations(argv[0]); #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) - chdir(fqn_prefix[HACKPREFIX]); + chdir(g.fqn_prefix[HACKPREFIX]); #endif - copy_config_content(); - if (GUILaunched || IsDebuggerPresent()) getreturn_enabled = TRUE; check_recordfile((char *) 0); iflags.windowtype_deferred = TRUE; - initoptions(); + copy_sysconf_content(); + initoptions(); + + /* Now that sysconf has had a chance to set the TROUBLEPREFIX, don't + allow it to be changed from here on out. */ + fqn_prefix_locked[TROUBLEPREFIX] = TRUE; + + copy_config_content(); + process_options(argc, argv); + + /* Finished processing options, lock all directory paths */ + for(int i = 0; i < PREFIX_COUNT; i++) + fqn_prefix_locked[i] = TRUE; + if (!validate_prefix_locations(failbuf)) { raw_printf("Some invalid directory locations were specified:\n\t%s\n", failbuf); nethack_exit(EXIT_FAILURE); } - process_options(argc, argv); - copy_hack_content(); /* @@ -580,7 +547,9 @@ char *argv[]; nethack_exit(EXIT_SUCCESS); if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { + iflags.initoptions_noterminate = TRUE; initoptions(); + iflags.initoptions_noterminate = FALSE; reveal_paths(); nethack_exit(EXIT_SUCCESS); } @@ -885,6 +854,61 @@ get_executable_path() return path_buffer; } +char * +translate_path_variables(str, buf) +const char *str; +char *buf; +{ + const char *src; + char evar[BUFSZ], *dest, *envp, *eptr = (char *) 0; + boolean in_evar; + size_t ccount, ecount, destcount, slen = str ? strlen(str) : 0; + + if (!slen || !buf) { + if (buf) + *buf = '\0'; + return buf; + } + + dest = buf; + src = str; + in_evar = FALSE; + destcount = ecount = 0; + for (ccount = 0; ccount < slen && destcount < (BUFSZ - 1) && + ecount < (BUFSZ - 1); ++ccount, ++src) { + if (*src == '%') { + if (in_evar) { + *eptr = '\0'; + envp = nh_getenv(evar); + if (envp) { + size_t elen = strlen(envp); + + if ((elen + destcount) < (size_t) (BUFSZ - 1)) { + Strcpy(dest, envp); + dest += elen; + destcount += elen; + } + } + } else { + eptr = evar; + ecount = 0; + } + in_evar = !in_evar; + continue; + } + if (in_evar) { + *eptr++ = *src; + ecount++; + } else { + *dest++ = *src; + destcount++; + } + } + *dest = '\0'; + return buf; +} + + /*ARGSUSED*/ void windows_raw_print(str)