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