E int FDECL(validate_prefix_locations, (char *));
E char** NDECL(get_saved_games);
E void FDECL(free_saved_games, (char**));
+#ifdef SELF_RECOVER
+E boolean NDECL(recover_savefile);
+#endif
/* ### fountain.c ### */
E void FDECL(nt_regularize, (char *));
E int NDECL((*nt_kbhit));
E void FDECL(Delay, (int));
+# if !defined(WIN_CE)
+E boolean FDECL(is_NetHack_process, (int));
+# endif /* !WIN_CE */
# endif /* WIN32 */
-
#endif /* MICRO || WIN32 */
/* ### mthrowu.c ### */
#define PC_LOCKING /* Prevent overwrites of aborted or in-progress games */
/* without first receiving confirmation. */
+#define SELF_RECOVER /* Allow the game itself to recover from an aborted game */
/*
* -----------------------------------------------------------------
#ifdef NOCWD_ASSUMPTIONS
STATIC_DCL void FDECL(adjust_prefix, (char *, int));
#endif
+#ifdef SELF_RECOVER
+STATIC_DCL boolean FDECL(copy_bytes, (int, int));
+#endif
/*
/* ---------- END PANIC/IMPOSSIBLE LOG ----------- */
+#ifdef SELF_RECOVER
+
+/* ---------- BEGIN INTERNAL RECOVER ----------- */
+boolean
+recover_savefile()
+{
+ int gfd, lfd, sfd;
+ int lev, savelev, hpid;
+ xchar levc;
+ struct version_info version_data;
+ int processed[256];
+ char savename[SAVESIZE];
+
+ for (lev = 0; lev < 256; lev++)
+ processed[lev] = 0;
+
+ /* level 0 file contains:
+ * pid of creating process (ignored here)
+ * level number for current level of save file
+ * name of save file nethack would have created
+ * and game state
+ */
+ gfd = open_levelfile(0);
+ if (gfd < 0) {
+ raw_printf("Cannot open level 0 for %s.\n", lock);
+ return FALSE;
+ }
+ if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
+ raw_printf(
+"\nCheckpoint data incompletely written or subsequently clobbered. Recovery impossible.");
+ (void)close(gfd);
+ return FALSE;
+ }
+#if defined(WIN32) && !defined(WIN_CE)
+ if (is_NetHack_process(hpid)) {
+ raw_printf(
+ "\nThe level files belong to an active NetHack process and cannot be recovered.");
+ (void)close(gfd);
+ return FALSE;
+ }
+#endif
+ if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
+ != sizeof(savelev)) {
+ raw_printf("\nCheckpointing was not in effect for %s -- recovery impossible.\n",
+ lock);
+ (void)close(gfd);
+ return FALSE;
+ }
+ if ((read(gfd, (genericptr_t) savename, sizeof savename)
+ != sizeof savename) ||
+ (read(gfd, (genericptr_t) &version_data, sizeof version_data)
+ != sizeof version_data)) {
+ raw_printf("\nError reading %s -- can't recover.\n", lock);
+ (void)close(gfd);
+ return FALSE;
+ }
+
+ /* save file should contain:
+ * version info
+ * current level (including pets)
+ * (non-level-based) game state
+ * other levels
+ */
+ set_savefile_name();
+ sfd = create_savefile();
+ if (sfd < 0) {
+ raw_printf("\nCannot recover savefile %s.\n", SAVEF);
+ (void)close(gfd);
+ return FALSE;
+ }
+
+ lfd = open_levelfile(savelev);
+ if (lfd < 0) {
+ raw_printf("\nCannot open level of save for %s.\n", lock);
+ (void)close(gfd);
+ (void)close(sfd);
+ delete_savefile();
+ return FALSE;
+ }
+
+ if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
+ != sizeof version_data) {
+ raw_printf("\nError writing %s; recovery failed.", SAVEF);
+ (void)close(gfd);
+ (void)close(sfd);
+ delete_savefile();
+ return FALSE;
+ }
+
+ if (!copy_bytes(lfd, sfd)) {
+ (void) close(lfd);
+ (void) close(sfd);
+ delete_savefile();
+ return FALSE;
+ }
+ (void)close(lfd);
+ processed[savelev] = 1;
+
+ if (!copy_bytes(gfd, sfd)) {
+ (void) close(lfd);
+ (void) close(sfd);
+ delete_savefile();
+ return FALSE;
+ }
+ (void)close(gfd);
+ processed[0] = 1;
+
+ for (lev = 1; lev < 256; lev++) {
+ /* level numbers are kept in xchars in save.c, so the
+ * maximum level number (for the endlevel) must be < 256
+ */
+ if (lev != savelev) {
+ lfd = open_levelfile(lev);
+ if (lfd >= 0) {
+ /* any or all of these may not exist */
+ levc = (xchar) lev;
+ write(sfd, (genericptr_t) &levc, sizeof(levc));
+ if (!copy_bytes(lfd, sfd)) {
+ (void) close(lfd);
+ (void) close(sfd);
+ delete_savefile();
+ return FALSE;
+ }
+ (void)close(lfd);
+ processed[lev] = 1;
+ }
+ }
+ }
+ (void)close(sfd);
+
+ /*
+ * We have a successful savefile!
+ * Only now do we erase the level files.
+ */
+ for (lev = 0; lev < 256; lev++) {
+ if (processed[lev]) {
+ const char *fq_lock;
+ set_levelfile_name(lock, lev);
+ fq_lock = fqname(lock, LEVELPREFIX, 3);
+ (void) unlink(fq_lock);
+ }
+ }
+ return TRUE;
+}
+
+boolean
+copy_bytes(ifd, ofd)
+int ifd, ofd;
+{
+ char buf[BUFSIZ];
+ int nfrom, nto;
+
+ do {
+ nfrom = read(ifd, buf, BUFSIZ);
+ nto = write(ofd, buf, nfrom);
+ if (nto != nfrom) return FALSE;
+ } while (nfrom == BUFSIZ);
+ return TRUE;
+}
+
+/* ---------- END INTERNAL RECOVER ----------- */
+#endif /*SELF_RECOVER*/
+
/*files.c*/
/* we ignore QUIT and INT at this point */
if (!lock_file(HLOCK, LOCKPREFIX, 10)) {
wait_synch();
-#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(orgdir, 0);
-#endif
+# endif
error("Quitting.");
}
fq_lock = fqname(lock, LEVELPREFIX, 1);
if((fd = open(fq_lock,0)) == -1) {
if(errno == ENOENT) goto gotlock; /* no such file */
-#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(orgdir, 0);
-#endif
-#if defined(WIN32)
+# endif
+# if defined(WIN32)
error("Bad directory or name: %s\n%s\n",
fq_lock, strerror(errno));
-#else
+# else
perror(fq_lock);
-#endif
+# endif
unlock_file(HLOCK);
error("Cannot open %s", fq_lock);
}
(void) close(fd);
if(iflags.window_inited) {
+# ifdef SELF_RECOVER
+ c = yn("There are files from a game in progress under your name. Recover?");
+# else
pline("There is already a game in progress under your name.");
pline("You may be able to use \"recover %s\" to get it back.\n",tbuf);
c = yn("Do you want to destroy the old game?");
+# endif
} else {
# if defined(MSDOS) && defined(NO_TERMS)
grmode = iflags.grmode;
# endif
c = 'n';
ct = 0;
+# ifdef SELF_RECOVER
+ msmsg(
+ "There are files from a game in progress under your name. Recover? [yn]");
+# else
msmsg("\nThere is already a game in progress under your name.\n");
msmsg("If this is unexpected, you may be able to use \n");
msmsg("\"recover %s\" to get it back.",tbuf);
msmsg("\nDo you want to destroy the old game? [yn] ");
+# endif
while ((ci=nhgetch()) != '\n') {
if (ct > 0) {
# if defined(WIN32CON)
}
}
if(c == 'y' || c == 'Y')
+# ifndef SELF_RECOVER
if(eraseoldlocks()) {
-# if defined(WIN32CON)
+# if defined(WIN32CON)
clear_screen(); /* display gets fouled up otherwise */
-# endif
+# endif
goto gotlock;
} else {
unlock_file(HLOCK);
-#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(orgdir, 0);
-#endif
+# endif
error("Couldn't destroy old game.");
}
+# else /*SELF_RECOVER*/
+ if(recover_savefile()) {
+# if defined(WIN32CON)
+ clear_screen(); /* display gets fouled up otherwise */
+# endif
+ goto gotlock;
+ } else {
+ unlock_file(HLOCK);
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+ chdirx(orgdir, 0);
+# endif
+ error("Couldn't recover old game.");
+ }
+# endif /*SELF_RECOVER*/
else {
unlock_file(HLOCK);
-#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(orgdir, 0);
-#endif
+# endif
error("%s", "");
}
if (fd == -1) ern = errno;
unlock_file(HLOCK);
if(fd == -1) {
-#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(orgdir, 0);
-#endif
-#if defined(WIN32)
+# endif
+# if defined(WIN32)
error("cannot creat file (%s.)\n%s\n%s\"%s\" exists?\n",
fq_lock, strerror(ern), " Are you sure that the directory",
fqn_prefix[LEVELPREFIX]);
-#else
+# else
error("cannot creat file (%s.)", fq_lock);
-#endif
+# endif
} else {
if(write(fd, (char *) &hackpid, sizeof(hackpid))
!= sizeof(hackpid)){
-#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(orgdir, 0);
-#endif
+# endif
error("cannot write lock (%s)", fq_lock);
}
if(close(fd) == -1) {
-#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
+# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(orgdir, 0);
-#endif
+# endif
error("cannot close lock (%s)", fq_lock);
}
}
if (grmode) gr_init();
# endif
}
-# endif /* PC_LOCKING */
+#endif /* PC_LOCKING */
# ifndef WIN32
void
abort();
}
+#if !defined(WIN_CE)
+#include <tlhelp32.h>
+boolean
+is_NetHack_process(pid)
+int pid;
+{
+ HANDLE hProcessSnap = NULL;
+ PROCESSENTRY32 pe32 = {0};
+ boolean bRet = FALSE;
+
+ hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (hProcessSnap == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ /* Set size of the processentry32 structure before using it. */
+ pe32.dwSize = sizeof(PROCESSENTRY32);
+ if (Process32First(hProcessSnap, &pe32)) {
+ do {
+ if (pe32.th32ProcessID == (unsigned)pid && pe32.szExeFile &&
+ ((strlen(pe32.szExeFile) >= 12 &&
+ !strcmpi(&pe32.szExeFile[strlen(pe32.szExeFile) - 12], "nethackw.exe")) ||
+ (strlen(pe32.szExeFile) >= 11 &&
+ !strcmpi(&pe32.szExeFile[strlen(pe32.szExeFile) - 11], "nethack.exe"))))
+ bRet = TRUE;
+ }
+ while (Process32Next(hProcessSnap, &pe32));
+ }
+ else
+ bRet = FALSE;
+ CloseHandle(hProcessSnap);
+ return bRet;
+}
+#endif /* WIN_CE*/
#endif /* WIN32 */
/*winnt.c*/