1 /* SCCS Id: @(#)pcmain.c 3.3 97/01/22 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 /* main.c - MSDOS, OS/2, ST, Amiga, and NT NetHack */
6
7 #include "hack.h"
8 #include "dlb.h"
9
10 #ifndef NO_SIGNAL
11 #include <signal.h>
12 #endif
13
14 #include <ctype.h>
15
16 #if !defined(AMIGA) && !defined(GNUDOS)
17 #include <sys\stat.h>
18 #else
19 # ifdef GNUDOS
20 #include <sys/stat.h>
21 # endif
22 #endif
23
24 #ifdef WIN32
25 #include "win32api.h" /* for GetModuleFileName */
26 #endif
27
28 #ifdef __DJGPP__
29 #include <unistd.h> /* for getcwd() prototype */
30 #endif
31
32 #ifdef OVL0
33 #define SHARED_DCL
34 #else
35 #define SHARED_DCL extern
36 #endif
37
38
39 SHARED_DCL char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */
40
41 #ifdef TOS
42 boolean run_from_desktop = TRUE; /* should we pause before exiting?? */
43 # ifdef __GNUC__
44 long _stksize = 16*1024;
45 # endif
46 #endif
47
48 #ifdef AMIGA
49 extern int bigscreen;
50 void NDECL( preserve_icon );
51 #endif
52
53 STATIC_DCL void FDECL(process_options,(int argc,char **argv));
54 STATIC_DCL void NDECL(nhusage);
55
56 #if defined(MICRO) || defined(WIN32) || defined(OS2)
57 extern void FDECL(nethack_exit,(int));
58 #else
59 #define nethack_exit exit
60 #endif
61
62 #ifdef EXEPATH
63 STATIC_DCL char *FDECL(exepath,(char *));
64 #endif
65
66 #ifdef OVL0
67 int FDECL(main, (int,char **));
68 #endif
69
70 extern void FDECL(pcmain, (int,char **));
71
72
73 #ifdef __BORLANDC__
74 void NDECL( startup );
75 # ifdef OVLB
76 unsigned _stklen = STKSIZ;
77 # else
78 extern unsigned _stklen;
79 # endif
80 #endif
81
82 #ifdef OVL0
83 int
main(argc,argv)84 main(argc,argv)
85 int argc;
86 char *argv[];
87 {
88 pcmain(argc,argv);
89 #ifdef LAN_FEATURES
90 init_lan_features();
91 #endif
92 moveloop();
93 nethack_exit(EXIT_SUCCESS);
94 /*NOTREACHED*/
95 return 0;
96 }
97 #endif /*OVL0*/
98 #ifdef OVL1
99
100 void
pcmain(argc,argv)101 pcmain(argc,argv)
102 int argc;
103 char *argv[];
104 {
105
106 register int fd;
107 register char *dir;
108
109 #ifdef __BORLANDC__
110 startup();
111 #endif
112
113 #ifdef TOS
114 long clock_time;
115 if (*argv[0]) { /* only a CLI can give us argv[0] */
116 hname = argv[0];
117 run_from_desktop = FALSE;
118 } else
119 #endif
120 hname = "NetHack"; /* used for syntax messages */
121
122 choose_windows(DEFAULT_WINDOW_SYS);
123
124 #if !defined(AMIGA) && !defined(GNUDOS)
125 /* Save current directory and make sure it gets restored when
126 * the game is exited.
127 */
128 if (getcwd(orgdir, sizeof orgdir) == (char *)0)
129 error("NetHack: current directory path too long");
130 # ifndef NO_SIGNAL
131 signal(SIGINT, (SIG_RET_TYPE) nethack_exit); /* restore original directory */
132 # endif
133 #endif /* !AMIGA && !GNUDOS */
134
135 dir = nh_getenv("NETHACKDIR");
136 if (dir == (char *)0)
137 dir = nh_getenv("HACKDIR");
138 #ifdef EXEPATH
139 if (dir == (char *)0)
140 dir = exepath(argv[0]);
141 #endif
142 if (dir != (char *)0) {
143 (void) strncpy(hackdir, dir, PATHLEN - 1);
144 hackdir[PATHLEN-1] = '\0';
145 #ifdef NOCWD_ASSUMPTIONS
146 {
147 int prefcnt;
148
149 fqn_prefix[0] = (char *)alloc(strlen(hackdir)+2);
150 Strcpy(fqn_prefix[0], hackdir);
151 append_slash(fqn_prefix[0]);
152 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++)
153 fqn_prefix[prefcnt] = fqn_prefix[0];
154 }
155 #endif
156 #ifdef CHDIR
157 chdirx (dir, 1);
158 #endif
159 }
160 #ifdef AMIGA
161 # ifdef CHDIR
162 /*
163 * If we're dealing with workbench, change the directory. Otherwise
164 * we could get "Insert disk in drive 0" messages. (Must be done
165 * before initoptions())....
166 */
167 if(argc == 0)
168 chdirx(HACKDIR, 1);
169 # endif
170 ami_argset(&argc, argv);
171 ami_wininit_data();
172 #endif
173 initoptions();
174 #ifdef AMIGA
175 ami_mkargline(&argc, &argv);
176 #endif
177
178 #if defined(TOS) && defined(TEXTCOLOR)
179 if (iflags.BIOS && iflags.use_color)
180 set_colors();
181 #endif
182 if (!hackdir[0])
183 #if !defined(LATTICE) && !defined(AMIGA)
184 Strcpy(hackdir, orgdir);
185 #else
186 Strcpy(hackdir, HACKDIR);
187 #endif
188 if(argc > 1) {
189 if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
190 /* avoid matching "-dec" for DECgraphics; since the man page
191 * says -d directory, hope nobody's using -desomething_else
192 */
193 argc--;
194 argv++;
195 dir = argv[0]+2;
196 if(*dir == '=' || *dir == ':') dir++;
197 if(!*dir && argc > 1) {
198 argc--;
199 argv++;
200 dir = argv[0];
201 }
202 if(!*dir)
203 error("Flag -d must be followed by a directory name.");
204 Strcpy(hackdir, dir);
205 }
206 if (argc > 1) {
207
208 /*
209 * Now we know the directory containing 'record' and
210 * may do a prscore().
211 */
212 if (!strncmp(argv[1], "-s", 2)) {
213 #ifdef CHDIR
214 chdirx(hackdir,0);
215 #endif
216 prscore(argc, argv);
217 nethack_exit(EXIT_SUCCESS);
218 }
219 /* Don't initialize the window system just to print usage */
220 if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) {
221 nhusage();
222 nethack_exit(EXIT_SUCCESS);
223 }
224 }
225 }
226
227 /*
228 * It seems you really want to play.
229 */
230 #ifdef TOS
231 if (comp_times((long)time(&clock_time)))
232 error("Your clock is incorrectly set!");
233 #endif
234 u.uhp = 1; /* prevent RIP on early quits */
235 u.ux = 0; /* prevent flush_screen() */
236
237 /* chdir shouldn't be called before this point to keep the
238 * code parallel to other ports which call gethdate just
239 * before here.
240 */
241 #ifdef CHDIR
242 chdirx(hackdir,1);
243 #endif
244
245 #ifdef MSDOS
246 process_options(argc, argv);
247 init_nhwindows(&argc,argv);
248 #else
249 init_nhwindows(&argc,argv);
250 process_options(argc, argv);
251 #endif
252
253 #ifdef MFLOPPY
254 set_lock_and_bones();
255 # ifndef AMIGA
256 copybones(FROMPERM);
257 # endif
258 #endif
259
260 if (!*plname)
261 askname();
262 plnamesuffix(); /* strip suffix from name; calls askname() */
263 /* again if suffix was whole name */
264 /* accepts any suffix */
265 #ifdef WIZARD
266 if (wizard) {
267 # ifdef KR1ED
268 if(!strcmp(plname, WIZARD_NAME))
269 # else
270 if(!strcmp(plname, WIZARD))
271 # endif
272 Strcpy(plname, "wizard");
273 else {
274 wizard = FALSE;
275 discover = TRUE;
276 }
277 }
278 #endif /* WIZARD */
279 #if defined(PC_LOCKING)
280 /* 3.3.0 added this to support detection of multiple games
281 * under the same plname on the same machine in a windowed
282 * or multitasking environment.
283 *
284 * That allows user confirmation prior to overwriting the
285 * level files of a game in progress.
286 *
287 * Also prevents an aborted game's level files from being
288 * overwritten without confirmation when a user starts up
289 * another game with the same player name.
290 */
291 # if defined(WIN32)
292 /* Obtain the name of the logged on user and incorporate
293 * it into the name. */
294 Sprintf(lock, "%s-%s",get_username(0),plname);
295 # else
296 Strcpy(lock,plname);
297 regularize(lock);
298 # endif
299 getlock();
300 #else /* PC_LOCKING */
301 # ifdef AMIGA /* We'll put the bones & levels in the user specified directory -jhsa */
302 Strcat(lock,plname);
303 Strcat(lock,".99");
304 # else
305 # ifndef MFLOPPY
306 /* I'm not sure what, if anything, is left here, but MFLOPPY has
307 * conflicts with set_lock_and_bones() in files.c.
308 */
309 Strcpy(lock,plname);
310 Strcat(lock,".99");
311 regularize(lock); /* is this necessary? */
312 /* not compatible with full path a la AMIGA */
313 # endif
314 # endif
315 #endif /* PC_LOCKING */
316
317 /* Set up level 0 file to keep the game state.
318 */
319 fd = create_levelfile(0);
320 if (fd < 0) {
321 raw_print("Cannot create lock file");
322 } else {
323 hackpid = 1;
324 write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
325 close(fd);
326 }
327 #ifdef MFLOPPY
328 level_info[0].where = ACTIVE;
329 #endif
330
331 /*
332 * Initialisation of the boundaries of the mazes
333 * Both boundaries have to be even.
334 */
335
336 x_maze_max = COLNO-1;
337 if (x_maze_max % 2)
338 x_maze_max--;
339 y_maze_max = ROWNO-1;
340 if (y_maze_max % 2)
341 y_maze_max--;
342
343 /*
344 * Initialize the vision system. This must be before mklev() on a
345 * new game or before a level restore on a saved game.
346 */
347 vision_init();
348
349 dlb_init();
350
351 display_gamewindows();
352
353 if ((fd = restore_saved_game()) >= 0) {
354 #ifdef WIZARD
355 /* Since wizard is actually flags.debug, restoring might
356 * overwrite it.
357 */
358 boolean remember_wiz_mode = wizard;
359 #endif
360 #ifndef NO_SIGNAL
361 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
362 #endif
363 #ifdef NEWS
364 if(iflags.news){
365 display_file(NEWS, FALSE);
366 iflags.news = FALSE;
367 }
368 #endif
369 pline("Restoring save file...");
370 mark_synch(); /* flush output */
371
372 if(!dorecover(fd))
373 goto not_recovered;
374 #ifdef WIZARD
375 if(!wizard && remember_wiz_mode) wizard = TRUE;
376 #endif
377 check_special_room(FALSE);
378 if (discover)
379 You("are in non-scoring discovery mode.");
380
381 if (discover || wizard) {
382 if(yn("Do you want to keep the save file?") == 'n'){
383 (void) delete_savefile();
384 }
385 # ifdef AMIGA
386 else
387 preserve_icon();
388 # endif
389 }
390
391 flags.move = 0;
392 } else {
393 not_recovered:
394 player_selection();
395 newgame();
396 if (discover)
397 You("are in non-scoring discovery mode.");
398
399 flags.move = 0;
400 set_wear();
401 (void) pickup(1);
402 read_engr_at(u.ux,u.uy);
403 }
404
405 #ifndef NO_SIGNAL
406 (void) signal(SIGINT, SIG_IGN);
407 #endif
408 #ifdef OS2
409 gettty(); /* somehow ctrl-P gets turned back on during startup ... */
410 #endif
411
412 return;
413 }
414
415 STATIC_OVL void
process_options(argc,argv)416 process_options(argc, argv)
417 int argc;
418 char *argv[];
419 {
420 int i;
421
422
423 /*
424 * Process options.
425 */
426 while(argc > 1 && argv[1][0] == '-'){
427 argv++;
428 argc--;
429 switch(argv[0][1]){
430 case 'D':
431 #ifdef WIZARD
432 /* If they don't have a valid wizard name, it'll be
433 * changed to discover later. Cannot check for
434 * validity of the name right now--it might have a
435 * character class suffix, for instance.
436 */
437 wizard = TRUE;
438 break;
439 #endif
440 case 'X':
441 discover = TRUE;
442 break;
443 #ifdef NEWS
444 case 'n':
445 iflags.news = FALSE;
446 break;
447 #endif
448 case 'u':
449 if(argv[0][2])
450 (void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
451 else if(argc > 1) {
452 argc--;
453 argv++;
454 (void) strncpy(plname, argv[0], sizeof(plname)-1);
455 } else
456 raw_print("Player name expected after -u");
457 break;
458 #ifndef AMIGA
459 case 'I':
460 case 'i':
461 if (!strncmpi(argv[0]+1, "IBM", 3))
462 switch_graphics(IBM_GRAPHICS);
463 break;
464 /* case 'D': */
465 case 'd':
466 if (!strncmpi(argv[0]+1, "DEC", 3))
467 switch_graphics(DEC_GRAPHICS);
468 break;
469 #endif
470 case 'p': /* profession (role) */
471 if (argv[0][2]) {
472 if ((i = str2role(&argv[0][2])) >= 0)
473 flags.initrole = i;
474 } else if (argc > 1) {
475 argc--;
476 argv++;
477 if ((i = str2role(argv[0])) >= 0)
478 flags.initrole = i;
479 }
480 break;
481 case 'r': /* race */
482 if (argv[0][2]) {
483 if ((i = str2race(&argv[0][2])) >= 0)
484 flags.initrace = i;
485 } else if (argc > 1) {
486 argc--;
487 argv++;
488 if ((i = str2race(argv[0])) >= 0)
489 flags.initrace = i;
490 }
491 break;
492 #ifdef MFLOPPY
493 # ifndef AMIGA
494 /* Player doesn't want to use a RAM disk
495 */
496 case 'R':
497 ramdisk = FALSE;
498 break;
499 # endif
500 #endif
501 #ifdef AMIGA
502 /* interlaced and non-interlaced screens */
503 case 'L':
504 bigscreen = 1;
505 break;
506 case 'l':
507 bigscreen = -1;
508 break;
509 #endif
510 default:
511 if ((i = str2role(&argv[0][1])) >= 0) {
512 flags.initrole = i;
513 break;
514 } else raw_printf("\nUnknown switch: %s", argv[0]);
515 /* FALL THROUGH */
516 case '?':
517 nhusage();
518 nethack_exit(EXIT_SUCCESS);
519 }
520 }
521 }
522
523 STATIC_OVL void
nhusage()524 nhusage()
525 {
526 char buf1[BUFSZ];
527
528 /* -role still works for those cases which aren't already taken, but
529 * is deprecated and will not be listed here.
530 */
531 (void) Sprintf(buf1,
532 "\nUsage: %s [-d dir] -s [-r race] [-p profession] [maxrank] [name]...\n or",
533 hname);
534 if (!iflags.window_inited)
535 raw_printf(buf1);
536 else
537 (void) printf(buf1);
538 (void) Sprintf(buf1,
539 "\n %s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]",
540 hname);
541 #ifdef NEWS
542 Strcat(buf1," [-n]");
543 #endif
544 #ifndef AMIGA
545 Strcat(buf1," [-I] [-i] [-d]");
546 #endif
547 #ifdef MFLOPPY
548 # ifndef AMIGA
549 Strcat(buf1," [-R]");
550 # endif
551 #endif
552 #ifdef AMIGA
553 Strcat(buf1," [-[lL]]");
554 #endif
555 if (!iflags.window_inited)
556 raw_printf("%s\n",buf1);
557 else
558 (void) printf("%s\n",buf1);
559 }
560
561 #ifdef CHDIR
562 void
chdirx(dir,wr)563 chdirx(dir, wr)
564 char *dir;
565 boolean wr;
566 {
567 # ifdef AMIGA
568 static char thisdir[] = "";
569 # else
570 static char thisdir[] = ".";
571 # endif
572 if(dir && chdir(dir) < 0) {
573 error("Cannot chdir to %s.", dir);
574 }
575
576 # ifndef AMIGA
577 /* Change the default drive as well.
578 */
579 chdrive(dir);
580 # endif
581
582 /* warn the player if we can't write the record file */
583 /* perhaps we should also test whether . is writable */
584 /* unfortunately the access system-call is worthless */
585 if (wr) check_recordfile(dir ? dir : thisdir);
586 }
587 #endif /* CHDIR */
588 #endif /*OVL1*/
589 #ifdef OVLB
590
591 #ifdef PORT_HELP
592 # if defined(MSDOS) || defined(WIN32)
593 void
port_help()594 port_help()
595 {
596 /* display port specific help file */
597 display_file( PORT_HELP, 1 );
598 }
599 # endif /* MSDOS || WIN32 */
600 #endif /* PORT_HELP */
601
602 #ifdef EXEPATH
603 # ifdef __DJGPP__
604 #define PATH_SEPARATOR '/'
605 # else
606 #define PATH_SEPARATOR '\\'
607 # endif
608
609 #define EXEPATHBUFSZ 256
610 char exepathbuf[EXEPATHBUFSZ];
611
exepath(str)612 char *exepath(str)
613 char *str;
614 {
615 char *tmp, *tmp2;
616 int bsize;
617
618 if (!str) return (char *)0;
619 bsize = EXEPATHBUFSZ;
620 tmp = exepathbuf;
621 # ifndef WIN32
622 Strcpy (tmp, str);
623 # else
624 *(tmp + GetModuleFileName((HANDLE)0, tmp, bsize)) = '\0';
625 # endif
626 tmp2 = strrchr(tmp, PATH_SEPARATOR);
627 if (tmp2) *tmp2 = '\0';
628 return tmp;
629 }
630 #endif /* EXEPATH */
631 #endif /*OVLB*/
632 /*pcmain.c*/
633