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