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