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