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