1 /*	SCCS Id: @(#)recover.c	3.4	1999/10/23	*/
2 /*	Copyright (c) Janet Walz, 1992.				  */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*
6  *  Utility for reconstructing NetHack save file from a set of individual
7  *  level files.  Requires that the `checkpoint' option be enabled at the
8  *  time NetHack creates those level files.
9  */
10 #include "config.h"
11 #if !defined(O_WRONLY) && !defined(LSC) && !defined(AZTEC_C)
12 #include <fcntl.h>
13 #endif
14 #ifdef WIN32
15 #include <errno.h>
16 #include "win32api.h"
17 #endif
18 
19 #ifdef VMS
20 extern int FDECL(vms_creat, (const char *,unsigned));
21 extern int FDECL(vms_open, (const char *,int,unsigned));
22 #endif	/* VMS */
23 
24 int FDECL(restore_savefile, (char *));
25 void FDECL(set_levelfile_name, (int));
26 int FDECL(open_levelfile, (int));
27 int NDECL(create_savefile);
28 void FDECL(copy_bytes, (int,int));
29 
30 #ifndef WIN_CE
31 #define Fprintf	(void)fprintf
32 #else
33 #define Fprintf	(void)nhce_message
34 static void nhce_message(FILE*, const char*, ...);
35 #endif
36 
37 #define Close	(void)close
38 
39 #ifdef UNIX
40 #define SAVESIZE	(PL_NSIZ + 13)	/* save/99999player.e */
41 #else
42 # ifdef VMS
43 #define SAVESIZE	(PL_NSIZ + 22)	/* [.save]<uid>player.e;1 */
44 # else
45 #  ifdef WIN32
46 #define SAVESIZE	(PL_NSIZ + 40)  /* username-player.NetHack-saved-game */
47 #  else
48 #define SAVESIZE	FILENAME	/* from macconf.h or pcconf.h */
49 #  endif
50 # endif
51 #endif
52 
53 #if defined(EXEPATH)
54 char *FDECL(exepath, (char *));
55 #endif
56 
57 #if defined(__BORLANDC__) && !defined(_WIN32)
58 extern unsigned _stklen = STKSIZ;
59 #endif
60 char savename[SAVESIZE]; /* holds relative path of save file from playground */
61 
62 
63 int
main(argc,argv)64 main(argc, argv)
65 int argc;
66 char *argv[];
67 {
68 	int argno;
69 	const char *dir = (char *)0;
70 #ifdef AMIGA
71 	char *startdir = (char *)0;
72 #endif
73 
74 
75 	if (!dir) dir = getenv("NETHACKDIR");
76 	if (!dir) dir = getenv("HACKDIR");
77 #if defined(EXEPATH)
78 	if (!dir) dir = exepath(argv[0]);
79 #endif
80 	if (argc == 1 || (argc == 2 && !strcmp(argv[1], "-"))) {
81 	    Fprintf(stderr,
82 		"Usage: %s [ -d directory ] base1 [ base2 ... ]\n", argv[0]);
83 #if defined(WIN32) || defined(MSDOS)
84 	    if (dir) {
85 	    	Fprintf(stderr, "\t(Unless you override it with -d, recover will look \n");
86 	    	Fprintf(stderr, "\t in the %s directory on your system)\n", dir);
87 	    }
88 #endif
89 	    exit(EXIT_FAILURE);
90 	}
91 
92 	argno = 1;
93 	if (!strncmp(argv[argno], "-d", 2)) {
94 		dir = argv[argno]+2;
95 		if (*dir == '=' || *dir == ':') dir++;
96 		if (!*dir && argc > argno) {
97 			argno++;
98 			dir = argv[argno];
99 		}
100 		if (!*dir) {
101 		    Fprintf(stderr,
102 			"%s: flag -d must be followed by a directory name.\n",
103 			argv[0]);
104 		    exit(EXIT_FAILURE);
105 		}
106 		argno++;
107 	}
108 #if defined(SECURE) && !defined(VMS)
109 	if (dir
110 # ifdef HACKDIR
111 		&& strcmp(dir, HACKDIR)
112 # endif
113 		) {
114 		(void) setgid(getgid());
115 		(void) setuid(getuid());
116 	}
117 #endif	/* SECURE && !VMS */
118 
119 #ifdef HACKDIR
120 	if (!dir) dir = HACKDIR;
121 #endif
122 
123 #ifdef AMIGA
124 	startdir = getcwd(0,255);
125 #endif
126 	if (dir && chdir((char *) dir) < 0) {
127 		Fprintf(stderr, "%s: cannot chdir to %s.\n", argv[0], dir);
128 		exit(EXIT_FAILURE);
129 	}
130 
131 	while (argc > argno) {
132 		if (restore_savefile(argv[argno]) == 0)
133 		    Fprintf(stderr, "recovered \"%s\" to %s\n",
134 			    argv[argno], savename);
135 		argno++;
136 	}
137 #ifdef AMIGA
138 	if (startdir) (void)chdir(startdir);
139 #endif
140 	exit(EXIT_SUCCESS);
141 	/*NOTREACHED*/
142 	return 0;
143 }
144 
145 static char lock[256];
146 
147 void
set_levelfile_name(lev)148 set_levelfile_name(lev)
149 int lev;
150 {
151 	char *tf;
152 
153 	tf = rindex(lock, '.');
154 	if (!tf) tf = lock + strlen(lock);
155 	(void) sprintf(tf, ".%d", lev);
156 #ifdef VMS
157 	(void) strcat(tf, ";1");
158 #endif
159 }
160 
161 int
open_levelfile(lev)162 open_levelfile(lev)
163 int lev;
164 {
165 	int fd;
166 
167 	set_levelfile_name(lev);
168 #if defined(MICRO) || defined(WIN32) || defined(MSDOS)
169 	fd = open(lock, O_RDONLY | O_BINARY);
170 #else
171 	fd = open(lock, O_RDONLY, 0);
172 #endif
173 	return fd;
174 }
175 
176 int
create_savefile()177 create_savefile()
178 {
179 	int fd;
180 
181 #if defined(MICRO) || defined(WIN32) || defined(MSDOS)
182 	fd = open(savename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK);
183 #else
184 	fd = creat(savename, FCMASK);
185 #endif
186 	return fd;
187 }
188 
189 void
copy_bytes(ifd,ofd)190 copy_bytes(ifd, ofd)
191 int ifd, ofd;
192 {
193 	char buf[BUFSIZ];
194 	int nfrom, nto;
195 
196 	do {
197 		nfrom = read(ifd, buf, BUFSIZ);
198 		nto = write(ofd, buf, nfrom);
199 		if (nto != nfrom) {
200 			Fprintf(stderr, "file copy failed!\n");
201 			exit(EXIT_FAILURE);
202 		}
203 	} while (nfrom == BUFSIZ);
204 }
205 
206 int
restore_savefile(basename)207 restore_savefile(basename)
208 char *basename;
209 {
210 	int gfd, lfd, sfd;
211 	int lev, savelev, hpid;
212 	xchar levc;
213 	struct version_info version_data;
214 
215 	/* level 0 file contains:
216 	 *	pid of creating process (ignored here)
217 	 *	level number for current level of save file
218 	 *	name of save file nethack would have created
219 	 *	and game state
220 	 */
221 	(void) strcpy(lock, basename);
222 	gfd = open_levelfile(0);
223 	if (gfd < 0) {
224 #if defined(WIN32) && !defined(WIN_CE)
225  	    if(errno == EACCES) {
226 	  	Fprintf(stderr,
227 			"\nThere are files from a game in progress under your name.");
228 		Fprintf(stderr,"\nThe files are locked or inaccessible.");
229 		Fprintf(stderr,"\nPerhaps the other game is still running?\n");
230 	    } else
231 	  	Fprintf(stderr,
232 			"\nTrouble accessing level 0 (errno = %d).\n", errno);
233 #endif
234 	    Fprintf(stderr, "Cannot open level 0 for %s.\n", basename);
235 	    return(-1);
236 	}
237 	if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) {
238 	    Fprintf(stderr, "%s\n%s%s%s\n",
239 	     "Checkpoint data incompletely written or subsequently clobbered;",
240 		    "recovery for \"", basename, "\" impossible.");
241 	    Close(gfd);
242 	    return(-1);
243 	}
244 	if (read(gfd, (genericptr_t) &savelev, sizeof(savelev))
245 							!= sizeof(savelev)) {
246 	    Fprintf(stderr,
247 	    "Checkpointing was not in effect for %s -- recovery impossible.\n",
248 		    basename);
249 	    Close(gfd);
250 	    return(-1);
251 	}
252 	if ((read(gfd, (genericptr_t) savename, sizeof savename)
253 		!= sizeof savename) ||
254 	    (read(gfd, (genericptr_t) &version_data, sizeof version_data)
255 		!= sizeof version_data)) {
256 	    Fprintf(stderr, "Error reading %s -- can't recover.\n", lock);
257 	    Close(gfd);
258 	    return(-1);
259 	}
260 
261 	/* save file should contain:
262 	 *	version info
263 	 *	current level (including pets)
264 	 *	(non-level-based) game state
265 	 *	other levels
266 	 */
267 	sfd = create_savefile();
268 	if (sfd < 0) {
269 	    Fprintf(stderr, "Cannot create savefile %s.\n", savename);
270 	    Close(gfd);
271 	    return(-1);
272 	}
273 
274 	lfd = open_levelfile(savelev);
275 	if (lfd < 0) {
276 	    Fprintf(stderr, "Cannot open level of save for %s.\n", basename);
277 	    Close(gfd);
278 	    Close(sfd);
279 	    return(-1);
280 	}
281 
282 	if (write(sfd, (genericptr_t) &version_data, sizeof version_data)
283 		!= sizeof version_data) {
284 	    Fprintf(stderr, "Error writing %s; recovery failed.\n", savename);
285 	    Close(gfd);
286 	    Close(sfd);
287 	    return(-1);
288 	}
289 
290 	copy_bytes(lfd, sfd);
291 	Close(lfd);
292 	(void) unlink(lock);
293 
294 	copy_bytes(gfd, sfd);
295 	Close(gfd);
296 	set_levelfile_name(0);
297 	(void) unlink(lock);
298 
299 	for (lev = 1; lev < 256; lev++) {
300 		/* level numbers are kept in xchars in save.c, so the
301 		 * maximum level number (for the endlevel) must be < 256
302 		 */
303 		if (lev != savelev) {
304 			lfd = open_levelfile(lev);
305 			if (lfd >= 0) {
306 				/* any or all of these may not exist */
307 				levc = (xchar) lev;
308 				write(sfd, (genericptr_t) &levc, sizeof(levc));
309 				copy_bytes(lfd, sfd);
310 				Close(lfd);
311 				(void) unlink(lock);
312 			}
313 		}
314 	}
315 
316 	Close(sfd);
317 
318 #if 0 /* OBSOLETE, HackWB is no longer in use */
319 #ifdef AMIGA
320 			/* we need to create an icon for the saved game
321 			 * or HackWB won't notice the file.
322 			 */
323 	{
324 	char iconfile[FILENAME];
325 	int in, out;
326 
327 	(void) sprintf(iconfile, "%s.info", savename);
328 	in = open("NetHack:default.icon", O_RDONLY);
329 	out = open(iconfile, O_WRONLY | O_TRUNC | O_CREAT);
330 	if(in > -1 && out > -1){
331 		copy_bytes(in,out);
332 	}
333 	if(in > -1)close(in);
334 	if(out > -1)close(out);
335 	}
336 #endif
337 #endif
338 	return(0);
339 }
340 
341 #ifdef EXEPATH
342 # ifdef __DJGPP__
343 #define PATH_SEPARATOR '/'
344 # else
345 #define PATH_SEPARATOR '\\'
346 # endif
347 
348 #define EXEPATHBUFSZ 256
349 char exepathbuf[EXEPATHBUFSZ];
350 
exepath(str)351 char *exepath(str)
352 char *str;
353 {
354 	char *tmp, *tmp2;
355 	int bsize;
356 
357 	if (!str) return (char *)0;
358 	bsize = EXEPATHBUFSZ;
359 	tmp = exepathbuf;
360 #if !defined(WIN32)
361 	strcpy (tmp, str);
362 #else
363 # if defined(WIN_CE)
364 	{
365 	  TCHAR wbuf[EXEPATHBUFSZ];
366 	  GetModuleFileName((HANDLE)0, wbuf, EXEPATHBUFSZ);
367 	  NH_W2A(wbuf, tmp, bsize);
368 	}
369 # else
370 	*(tmp + GetModuleFileName((HANDLE)0, tmp, bsize)) = '\0';
371 # endif
372 #endif
373 	tmp2 = strrchr(tmp, PATH_SEPARATOR);
374 	if (tmp2) *tmp2 = '\0';
375 	return tmp;
376 }
377 #endif /* EXEPATH */
378 
379 #ifdef AMIGA
380 #include "date.h"
381 const char amiga_version_string[] = AMIGA_VERSION_STRING;
382 #endif
383 
384 #ifdef WIN_CE
nhce_message(FILE * f,const char * str,...)385 void nhce_message(FILE* f, const char* str, ...)
386 {
387     va_list ap;
388 	TCHAR wbuf[NHSTR_BUFSIZE];
389 	char buf[NHSTR_BUFSIZE];
390 
391     va_start(ap, str);
392 	vsprintf(buf, str, ap);
393     va_end(ap);
394 
395 	MessageBox(NULL, NH_A2W(buf, wbuf, NHSTR_BUFSIZE), TEXT("Recover"), MB_OK);
396 }
397 #endif
398 
399 /*recover.c*/
400