OSDN Git Service

adjust dungeon levels
[nethackexpress/trunk.git] / util / recover.c
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
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
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
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
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
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
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
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
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*/