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