diff --git a/doc/fixes36.4 b/doc/fixes36.4 index 693eebc5c..9a4d40dba 100644 --- a/doc/fixes36.4 +++ b/doc/fixes36.4 @@ -24,6 +24,7 @@ Fixes to Post-3.6.3 Problems that Were Exposed Via git Repository Platform- and/or Interface-Specific Fixes or Features ----------------------------------------------------- fix compilation on platforms that split the ncurses and tinfo libraries +allow run-from-removable-device on Windows General New Features diff --git a/include/extern.h b/include/extern.h index ed0a65cf5..8c5a2cbd2 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1887,6 +1887,7 @@ E boolean NDECL(authorize_wizard_mode); #endif /* MICRO || WIN32 */ #if defined(WIN32) E int NDECL(getlock); +E const char *NDECL(get_portable_device); #endif /* ### pcsys.c ### */ diff --git a/include/sys.h b/include/sys.h index 855de36b8..16382ef99 100644 --- a/include/sys.h +++ b/include/sys.h @@ -17,6 +17,9 @@ struct sysopt { char *debugfiles; /* files to show debugplines in. '*' is all. */ #ifdef DUMPLOG char *dumplogfile; /* where the dump file is saved */ +#endif +#ifdef WIN32 + char *portable_device_top; /* nethack configuration for a portable drive */ #endif int env_dbgfl; /* 1: debugfiles comes from getenv("DEBUGFILES") * so sysconf's DEBUGFILES shouldn't override it; diff --git a/src/files.c b/src/files.c index d0e55445a..80fbcb34f 100644 --- a/src/files.c +++ b/src/files.c @@ -2591,6 +2591,12 @@ char *origbuf; if (sysopt.dumplogfile) free((genericptr_t) sysopt.dumplogfile); sysopt.dumplogfile = dupstr(bufp); +#endif +#ifdef WIN32 + } else if (src == SET_IN_SYS && match_varname(buf, "portable_device_top", 8)) { + if (sysopt.portable_device_top) + free((genericptr_t) sysopt.portable_device_top); + sysopt.portable_device_top = dupstr(bufp); #endif } else if (src == SET_IN_SYS && match_varname(buf, "GENERICUSERS", 12)) { if (sysopt.genericusers) @@ -4320,6 +4326,16 @@ reveal_paths(VOID_ARGS) #endif /* ?DUMPLOG */ raw_printf("No end-of-game disclosure file (%s).", nodumpreason); +#ifdef WIN32 + if (sysopt.portable_device_top) { + const char *pd = get_portable_device(); + + raw_printf("Writable folder for portable device config (sysconf %s):", + "portable_device_top"); + raw_printf(" \"%s\"", pd); + } +#endif + /* personal configuration file */ buf[0] = '\0'; diff --git a/src/sys.c b/src/sys.c index 93c14ef69..6f71b0bd2 100644 --- a/src/sys.c +++ b/src/sys.c @@ -34,6 +34,9 @@ sys_early_init() #endif #ifdef DUMPLOG sysopt.dumplogfile = (char *) 0; +#endif +#ifdef WIN32 + sysopt.portable_device_top = (char *) 0; #endif sysopt.env_dbgfl = 0; /* haven't checked getenv("DEBUGFILES") yet */ sysopt.shellers = (char *) 0; @@ -105,6 +108,12 @@ sysopt_release() #ifdef DUMPLOG if (sysopt.dumplogfile) free((genericptr_t)sysopt.dumplogfile), sysopt.dumplogfile=(char *)0; +#endif +#ifdef WIN32 + if (sysopt.portable_device_top) { + free((genericptr_t) sysopt.portable_device_top); + sysopt.portable_device_top = (char *) 0; + } #endif if (sysopt.genericusers) free((genericptr_t) sysopt.genericusers), diff --git a/src/trap.c b/src/trap.c index 0639421ba..568bcc811 100644 --- a/src/trap.c +++ b/src/trap.c @@ -486,8 +486,8 @@ unsigned ftflags; ; /* KMH -- You can't escape the Sokoban level traps */ else if (Levitation || u.ustuck || (!Can_fall_thru(&u.uz) && !levl[u.ux][u.uy].candig) - || (Flying && !(ftflags & TOOKPLUNGE)) - || is_clinger(g.youmonst.data) + || ((Flying || is_clinger(g.youmonst.data)) + && !(ftflags & TOOKPLUNGE)) || (Inhell && !u.uevent.invoked && newlevel == bottom)) { dont_fall = "don't fall in."; } else if (g.youmonst.data->msize >= MZ_HUGE) { @@ -505,9 +505,13 @@ unsigned ftflags; } return; } - if (Flying && (ftflags & TOOKPLUNGE) && td && t) - You("swoop down %s!", (t->ttyp == TRAPDOOR) - ? "through the trap door" : "into the gaping hole"); + if ((Flying || is_clinger(g.youmonst.data)) + && (ftflags & TOOKPLUNGE) && td && t) + You("%s down %s!", + Flying ? "swoop" : "deliberately drop", + (t->ttyp == TRAPDOOR) + ? "through the trap door" + : "into the gaping hole"); if (*u.ushops) shopdig(1); diff --git a/sys/winnt/windmain.c b/sys/winnt/windmain.c index e0360065a..3e9d6b721 100644 --- a/sys/winnt/windmain.c +++ b/sys/winnt/windmain.c @@ -61,6 +61,9 @@ int NDECL(other_self_recover_prompt); char orgdir[PATHLEN]; boolean getreturn_enabled; +int windows_startup_state = 0; /* we flag whether to continue with this */ + /* 0 = keep starting up, everything is good */ + extern int redirect_stdout; /* from sys/share/pcsys.c */ extern int GUILaunched; HANDLE hStdOut; @@ -161,6 +164,134 @@ folder_file_exists(const char * folder, const char * file_name) return file_exists(path); } +boolean +test_portable_config( + const char *executable_path, + char *portable_device_top_path, + size_t portable_device_top_path_size) +{ + boolean retval = FALSE, + save_initoptions_noterminate = iflags.initoptions_noterminate; + char tmppath[MAX_PATH], *toppath, *sysconftop; +#ifdef UNICODE + TCHAR wdrive[_MAX_DRIVE]; + TCHAR wthisdir[_MAX_DIR]; + TCHAR wfname[_MAX_FNAME]; + TCHAR wext[_MAX_EXT]; +#endif + char drive[_MAX_DRIVE]; + char thisdir[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ext[_MAX_EXT]; + errno_t err; + + if (portable_device_top_path && folder_file_exists(executable_path, "sysconf")) { + /* + There is a sysconf file (not just sysconf.template) present in + the exe path, which is not the way NetHack is initially distributed, + so assume it means that the admin/installer wants to override + something, perhaps set up for a fully-portable configuration that + leaves no traces behind elsewhere on this computer's hard drive - + delve into that... + */ + + *portable_device_top_path = '\0'; + (void) strncpy(tmppath, executable_path, sizeof tmppath - (1 + sizeof "sysconf")); + tmppath[sizeof tmppath - (1 + sizeof "sysconf")] = '\0'; + (void) strcat(tmppath, "sysconf"); + /* split the path up */ +#ifdef UNICODE + { + int sz, wchars_num = MultiByteToWideChar( CP_ACP, 0, tmppath, -1, NULL, 0); + wchar_t *wstr; + + if (wchars_num) { + wstr = (wchar_t *) alloc(wchars_num * sizeof(wchar_t)); + MultiByteToWideChar( CP_ACP, 0, tmppath, -1, wstr, wchars_num); + err = _wsplitpath_s(wstr, wdrive, _MAX_DRIVE, wthisdir, _MAX_DIR, + wfname, _MAX_FNAME, wext, _MAX_EXT); + free(wstr); + } + sz = WideCharToMultiByte(CP_ACP, 0, wdrive, -1, drive, + 0, NULL, NULL); + if (sz <= sizeof drive) + WideCharToMultiByte(CP_ACP, 0, wdrive, -1, drive, + sz, NULL, NULL); + + } +#else + err = _splitpath_s(tmppath, drive, _MAX_DRIVE, thisdir, _MAX_DIR, + fname, _MAX_FNAME, ext, _MAX_EXT); +#endif + if (err != 0) + goto done_test; + + toppath = (char *) alloc(portable_device_top_path_size); + *toppath = '\0'; + /* -2 because we need to append the path separator */ + (void) strncpy(toppath, drive, portable_device_top_path_size - 2); + toppath[portable_device_top_path_size - 2] = '\0'; + (void) strcat(toppath, "\\"); + + iflags.initoptions_noterminate = 1; + /* assure_syscf_file(); */ + config_error_init(TRUE, tmppath, FALSE); + /* ... and _must_ parse correctly. */ + if (read_config_file(tmppath, SET_IN_SYS) + && sysopt.portable_device_top + && (strlen(sysopt.portable_device_top) + strlen(toppath) + < portable_device_top_path_size - 3)) { + sysconftop = sysopt.portable_device_top; + if (sysconftop[1] == ':') + sysconftop += 2; /* skip the device if specified */ + if (*sysconftop == '\\') + sysconftop += 1; /* skip the root folder if specified */ + (void) strcat(toppath, sysconftop); + append_slash(toppath); + retval = TRUE; + } else { + if (config_error_done()) + retval = FALSE; + } + iflags.initoptions_noterminate = save_initoptions_noterminate; + sysopt_release(); + if (retval) + Strcpy(portable_device_top_path, toppath); + free(toppath); + } + done_test: + + return retval; +} + +static char portable_device_top_path[MAX_PATH]; + +const char *get_portable_device() +{ + return (const char *) portable_device_top_path; +} + +boolean illegal_dir(const char *d1, const char *d2) +{ + int i; + char tmpbuf[MAX_PATH]; + + if (!strcmpi(d1, d2)) { + (void) strncpy(tmpbuf, &portable_device_top_path[3], + sizeof tmpbuf - 1); + tmpbuf[sizeof tmpbuf - 1] = '\0'; + i = (int) strlen(tmpbuf) - 1; + if (tmpbuf[i] == '\\') + tmpbuf[i] = '\0'; + raw_printf("Illegal \"portable_device_top = %s\" in your sysconf file", + tmpbuf); + raw_printf("because the exe is running from that folder."); + raw_printf("Point 'portable_device_top' to a different folder."); + return TRUE; + } + return FALSE; +} + void set_default_prefix_locations(const char *programPath) { @@ -177,29 +308,43 @@ set_default_prefix_locations(const char *programPath) strcpy(executable_path, get_executable_path()); append_slash(executable_path); - build_known_folder_path(&FOLDERID_Profile, profile_path, - sizeof(profile_path), FALSE); + if (test_portable_config(executable_path, portable_device_top_path, + sizeof portable_device_top_path)) { + if (illegal_dir(portable_device_top_path, executable_path)) + windows_startup_state = 2; + g.fqn_prefix[SYSCONFPREFIX] = executable_path; + g.fqn_prefix[CONFIGPREFIX] = portable_device_top_path; + g.fqn_prefix[HACKPREFIX] = portable_device_top_path; + g.fqn_prefix[SAVEPREFIX] = portable_device_top_path; + g.fqn_prefix[LEVELPREFIX] = portable_device_top_path; + g.fqn_prefix[BONESPREFIX] = portable_device_top_path; + g.fqn_prefix[SCOREPREFIX] = portable_device_top_path; + g.fqn_prefix[LOCKPREFIX] = portable_device_top_path; + g.fqn_prefix[TROUBLEPREFIX] = portable_device_top_path; + g.fqn_prefix[DATAPREFIX] = executable_path; + } else { + build_known_folder_path(&FOLDERID_Profile, profile_path, + sizeof(profile_path), FALSE); - build_known_folder_path(&FOLDERID_Profile, versioned_profile_path, - sizeof(profile_path), TRUE); + build_known_folder_path(&FOLDERID_Profile, versioned_profile_path, + sizeof(profile_path), TRUE); - build_known_folder_path(&FOLDERID_LocalAppData, - versioned_user_data_path, sizeof(versioned_user_data_path), TRUE); - - build_known_folder_path(&FOLDERID_ProgramData, - versioned_global_data_path, sizeof(versioned_global_data_path), TRUE); - - 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; + build_known_folder_path(&FOLDERID_LocalAppData, + versioned_user_data_path, sizeof(versioned_user_data_path), TRUE); + build_known_folder_path(&FOLDERID_ProgramData, + versioned_global_data_path, sizeof(versioned_global_data_path), TRUE); + 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; + } } /* copy file if destination does not exist */ @@ -393,6 +538,12 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ copy_config_content(); process_options(argc, argv); + /* did something earlier flag a need to exit without starting a game? */ + if (windows_startup_state > 0) { + raw_printf("Exiting."); + nethack_exit(EXIT_FAILURE); + } + /* Finished processing options, lock all directory paths */ for(int i = 0; i < PREFIX_COUNT; i++) fqn_prefix_locked[i] = TRUE; @@ -538,9 +689,9 @@ attempt_restore: You("are in non-scoring discovery mode."); } - // iflags.debug_fuzzer = TRUE; + // iflags.debug_fuzzer = TRUE; - moveloop(resuming); + moveloop(resuming); nethack_exit(EXIT_SUCCESS); /*NOTREACHED*/ return 0; @@ -566,15 +717,15 @@ char *argv[]; iflags.initoptions_noterminate = FALSE; reveal_paths(); nethack_exit(EXIT_SUCCESS); - } + } if (argcheck(argc, argv, ARG_DEBUG) == 1) { argc--; argv++; - } - if (argcheck(argc, argv, ARG_WINDOWS) == 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