/* SCCS Id: @(#)recover.c 3.3 99/10/23 */ /* Copyright (c) Janet Walz, 1992. */ /* NetHack may be freely redistributed. See license for details. */ /* * Utility for reconstructing NetHack save file from a set of individual * level files. Requires that the `checkpoint' option be enabled at the * time NetHack creates those level files. */ #include "config.h" #if !defined(O_WRONLY) && !defined(LSC) && !defined(AZTEC_C) #include #endif #ifdef WIN32 #include "win32api.h" #endif #ifdef VMS extern int FDECL(vms_creat, (const char *,unsigned)); extern int FDECL(vms_open, (const char *,int,unsigned)); #endif /* VMS */ int FDECL(restore_savefile, (char *)); void FDECL(set_levelfile_name, (int)); int FDECL(open_levelfile, (int)); int NDECL(create_savefile); void FDECL(copy_bytes, (int,int)); #define Fprintf (void)fprintf #define Close (void)close #ifdef UNIX #define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */ #else # ifdef VMS #define SAVESIZE (PL_NSIZ + 22) /* [.save]player.e;1 */ # else # ifdef WIN32 #define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */ # else #define SAVESIZE FILENAME /* from macconf.h or pcconf.h */ # endif # endif #endif #if defined(EXEPATH) char *FDECL(exepath, (char *)); #endif #ifdef __BORLANDC__ extern unsigned _stklen = STKSIZ; #endif char savename[SAVESIZE]; /* holds relative path of save file from playground */ int main(argc, argv) int argc; char *argv[]; { int argno; const char *dir = (char *)0; #ifdef AMIGA char *startdir = (char *)0; #endif if (!dir) dir = getenv("NETHACKDIR"); if (!dir) dir = getenv("HACKDIR"); #if defined(EXEPATH) if (!dir) dir = exepath(argv[0]); #endif if (argc == 1 || (argc == 2 && !strcmp(argv[1], "-"))) { Fprintf(stderr, "Usage: %s [ -d directory ] base1 [ base2 ... ]\n", argv[0]); #if defined(WIN32) || defined(MSDOS) if (dir) { Fprintf(stderr, "\t(Unless you override it with -d, recover will look \n"); Fprintf(stderr, "\t in the %s directory on your system)\n", dir); } #endif exit(EXIT_FAILURE); } argno = 1; if (!strncmp(argv[argno], "-d", 2)) { dir = argv[argno]+2; if (*dir == '=' || *dir == ':') dir++; if (!*dir && argc > argno) { argno++; dir = argv[argno]; } if (!*dir) { Fprintf(stderr, "%s: flag -d must be followed by a directory name.\n", argv[0]); exit(EXIT_FAILURE); } argno++; } #if defined(SECURE) && !defined(VMS) if (dir # ifdef HACKDIR && strcmp(dir, HACKDIR) # endif ) { (void) setgid(getgid()); (void) setuid(getuid()); } #endif /* SECURE && !VMS */ #ifdef HACKDIR if (!dir) dir = HACKDIR; #endif #ifdef AMIGA startdir = getcwd(0,255); #endif if (dir && chdir((char *) dir) < 0) { Fprintf(stderr, "%s: cannot chdir to %s.\n", argv[0], dir); exit(EXIT_FAILURE); } while (argc > argno) { if (restore_savefile(argv[argno]) == 0) Fprintf(stderr, "recovered \"%s\" to %s\n", argv[argno], savename); argno++; } #ifdef AMIGA if (startdir) (void)chdir(startdir); #endif exit(EXIT_SUCCESS); /*NOTREACHED*/ return 0; } static char lock[256]; void set_levelfile_name(lev) int lev; { char *tf; tf = rindex(lock, '.'); if (!tf) tf = lock + strlen(lock); (void) sprintf(tf, ".%d", lev); #ifdef VMS (void) strcat(tf, ";1"); #endif } int open_levelfile(lev) int lev; { int fd; set_levelfile_name(lev); #if defined(MICRO) || defined(WIN32) || defined(MSDOS) fd = open(lock, O_RDONLY | O_BINARY); #else fd = open(lock, O_RDONLY, 0); #endif return fd; } int create_savefile() { int fd; #if defined(MICRO) || defined(WIN32) || defined(MSDOS) fd = open(savename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK); #else fd = creat(savename, FCMASK); #endif return fd; } void 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) { Fprintf(stderr, "file copy failed!\n"); exit(EXIT_FAILURE); } } while (nfrom == BUFSIZ); } int restore_savefile(basename) char *basename; { int gfd, lfd, sfd; int lev, savelev, hpid; xchar levc; struct version_info version_data; /* 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 */ (void) strcpy(lock, basename); gfd = open_levelfile(0); if (gfd < 0) { Fprintf(stderr, "Cannot open level 0 for %s.\n", basename); return(-1); } if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) { Fprintf(stderr, "%s\n%s%s%s\n", "Checkpoint data incompletely written or subsequently clobbered;", "recovery for \"", basename, "\" impossible."); Close(gfd); return(-1); } if (read(gfd, (genericptr_t) &savelev, sizeof(savelev)) != sizeof(savelev)) { Fprintf(stderr, "Checkpointing was not in effect for %s -- recovery impossible.\n", basename); Close(gfd); return(-1); } if ((read(gfd, (genericptr_t) savename, sizeof savename) != sizeof savename) || (read(gfd, (genericptr_t) &version_data, sizeof version_data) != sizeof version_data)) { Fprintf(stderr, "Error reading %s -- can't recover.\n", lock); Close(gfd); return(-1); } /* save file should contain: * version info * current level (including pets) * (non-level-based) game state * other levels */ sfd = create_savefile(); if (sfd < 0) { Fprintf(stderr, "Cannot create savefile %s.\n", savename); Close(gfd); return(-1); } lfd = open_levelfile(savelev); if (lfd < 0) { Fprintf(stderr, "Cannot open level of save for %s.\n", basename); Close(gfd); Close(sfd); return(-1); } if (write(sfd, (genericptr_t) &version_data, sizeof version_data) != sizeof version_data) { Fprintf(stderr, "Error writing %s; recovery failed.\n", savename); Close(gfd); Close(sfd); return(-1); } copy_bytes(lfd, sfd); Close(lfd); (void) unlink(lock); copy_bytes(gfd, sfd); Close(gfd); set_levelfile_name(0); (void) unlink(lock); 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)); copy_bytes(lfd, sfd); Close(lfd); (void) unlink(lock); } } } Close(sfd); #if 0 /* OBSOLETE, HackWB is no longer in use */ #ifdef AMIGA /* we need to create an icon for the saved game * or HackWB won't notice the file. */ { char iconfile[FILENAME]; int in, out; (void) sprintf(iconfile, "%s.info", savename); in = open("NetHack:default.icon", O_RDONLY); out = open(iconfile, O_WRONLY | O_TRUNC | O_CREAT); if(in > -1 && out > -1){ copy_bytes(in,out); } if(in > -1)close(in); if(out > -1)close(out); } #endif #endif return(0); } #ifdef EXEPATH # ifdef __DJGPP__ #define PATH_SEPARATOR '/' # else #define PATH_SEPARATOR '\\' # 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; # ifndef WIN32 strcpy (tmp, str); # else *(tmp + GetModuleFileName((HANDLE)0, tmp, bsize)) = '\0'; # endif tmp2 = strrchr(tmp, PATH_SEPARATOR); if (tmp2) *tmp2 = '\0'; return tmp; } #endif /* EXEPATH */ #ifdef AMIGA #include "date.h" const char amiga_version_string[] = AMIGA_VERSION_STRING; #endif /*recover.c*/