OSDN Git Service

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