OSDN Git Service

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