1 /* NetHack 3.6 pcmain.c $NHDT-Date: 1543465755 2018/11/29 04:29:15 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.101 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Derek S. Ray, 2015. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* main.c - MSDOS, OS/2, ST, Amiga NetHack */
17 #if !defined(AMIGA) && !defined(GNUDOS)
26 #include <unistd.h> /* for getcwd() prototype */
29 char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */
32 boolean run_from_desktop = TRUE; /* should we pause before exiting?? */
34 long _stksize = 16 * 1024;
40 void NDECL(preserve_icon);
43 STATIC_DCL void FDECL(process_options, (int argc, char **argv));
44 STATIC_DCL void NDECL(nhusage);
46 #if defined(MICRO) || defined(OS2)
47 extern void FDECL(nethack_exit, (int));
49 #define nethack_exit exit
53 STATIC_DCL char *FDECL(exepath, (char *));
56 int FDECL(main, (int, char **));
58 extern boolean FDECL(pcmain, (int, char **));
60 #if defined(__BORLANDC__)
62 unsigned _stklen = STKSIZ;
65 /* If the graphics version is built, we don't need a main; it is skipped
66 * to help MinGW decide which entry point to choose. If both main and
67 * WinMain exist, the resulting executable won't work correctly.
73 mingw_main(argc, argv)
81 resuming = pcmain(argc, argv);
83 nethack_exit(EXIT_SUCCESS);
99 #ifdef NOCWD_ASSUMPTIONS
102 boolean resuming = FALSE; /* assume new game */
106 /* set these appropriately for VS debugging */
107 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
108 _CrtSetReportMode(_CRT_ERROR,
109 _CRTDBG_MODE_DEBUG); /* | _CRTDBG_MODE_FILE);*/
110 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
111 /*| _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW);*/
112 /* use STDERR by default
113 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
114 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
116 _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
117 | _CRTDBG_ALLOC_MEM_DF
118 | _CRTDBG_CHECK_ALWAYS_DF
119 | _CRTDBG_CHECK_CRT_DF
120 | _CRTDBG_DELAY_FREE_MEM_DF
121 | _CRTDBG_LEAK_CHECK_DF);
122 _CrtSetBreakAlloc(1423);
127 #if defined(__BORLANDC__)
133 if (*argv[0]) { /* only a CLI can give us argv[0] */
135 run_from_desktop = FALSE;
138 hname = "NetHack"; /* used for syntax messages */
140 choose_windows(DEFAULT_WINDOW_SYS);
142 #if !defined(AMIGA) && !defined(GNUDOS)
143 /* Save current directory and make sure it gets restored when
144 * the game is exited.
146 if (getcwd(orgdir, sizeof orgdir) == (char *) 0)
147 error("NetHack: current directory path too long");
150 (SIG_RET_TYPE) nethack_exit); /* restore original directory */
152 #endif /* !AMIGA && !GNUDOS */
154 dir = nh_getenv("NETHACKDIR");
155 if (dir == (char *) 0)
156 dir = nh_getenv("HACKDIR");
158 if (dir == (char *) 0)
159 dir = exepath(argv[0]);
162 if (IsDebuggerPresent()) {
163 static char exepath[_MAX_PATH];
164 /* check if we're running under the debugger so we can get to the right folder anyway */
165 if (dir != (char *)0) {
166 char *top = (char *)0;
168 if (strlen(dir) < (_MAX_PATH - 1))
169 strcpy(exepath, dir);
170 top = strstr(exepath, "\\build\\.\\Debug");
171 if (!top) top = strstr(exepath, "\\build\\.\\Release");
174 if (strlen(exepath) < (_MAX_PATH - (strlen("\\binary\\") + 1))) {
175 Strcat(exepath, "\\binary\\");
176 if (strlen(exepath) < (PATHLEN - 1)) {
184 if (dir != (char *)0) {
186 boolean have_syscf = FALSE;
188 (void) strncpy(hackdir, dir, PATHLEN - 1);
189 hackdir[PATHLEN - 1] = '\0';
190 #ifdef NOCWD_ASSUMPTIONS
194 fqn_prefix[0] = (char *) alloc(strlen(hackdir) + 2);
195 Strcpy(fqn_prefix[0], hackdir);
196 append_slash(fqn_prefix[0]);
197 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++)
198 fqn_prefix[prefcnt] = fqn_prefix[0];
201 /* sysconf should be searched for in this location */
202 envp = nh_getenv("COMMONPROGRAMFILES");
204 if ((sptr = index(envp, ';')) != 0)
206 if (strlen(envp) > 0) {
207 fqn_prefix[SYSCONFPREFIX] =
208 (char *) alloc(strlen(envp) + 10);
209 Strcpy(fqn_prefix[SYSCONFPREFIX], envp);
210 append_slash(fqn_prefix[SYSCONFPREFIX]);
211 Strcat(fqn_prefix[SYSCONFPREFIX], "NetHack\\");
215 /* okay so we have the overriding and definitive locaton
216 for sysconf, but only in the event that there is not a
217 sysconf file there (for whatever reason), check a secondary
218 location rather than abort. */
220 /* Is there a SYSCF_FILE there? */
221 fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY);
229 /* No SYSCF_FILE where there should be one, and
230 without an installer, a user may not be able
231 to place one there. So, let's try somewhere else... */
232 fqn_prefix[SYSCONFPREFIX] = fqn_prefix[0];
234 /* Is there a SYSCF_FILE there? */
235 fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY);
243 /* user's home directory should default to this - unless
245 envp = nh_getenv("USERPROFILE");
247 if ((sptr = index(envp, ';')) != 0)
249 if (strlen(envp) > 0) {
250 fqn_prefix[CONFIGPREFIX] =
251 (char *) alloc(strlen(envp) + 2);
252 Strcpy(fqn_prefix[CONFIGPREFIX], envp);
253 append_slash(fqn_prefix[CONFIGPREFIX]);
259 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
266 * If we're dealing with workbench, change the directory. Otherwise
267 * we could get "Insert disk in drive 0" messages. (Must be done
268 * before initoptions())....
277 #ifdef NOCWD_ASSUMPTIONS
278 if (!validate_prefix_locations(failbuf)) {
279 raw_printf("Some invalid directory locations were specified:\n\t%s\n",
281 nethack_exit(EXIT_FAILURE);
285 #if defined(TOS) && defined(TEXTCOLOR)
286 if (iflags.BIOS && iflags.use_color)
290 #if !defined(LATTICE) && !defined(AMIGA)
291 Strcpy(hackdir, orgdir);
293 Strcpy(hackdir, HACKDIR);
296 if (argcheck(argc, argv, ARG_VERSION) == 2)
297 nethack_exit(EXIT_SUCCESS);
299 if (argcheck(argc, argv, ARG_DEBUG) == 1) {
304 if (argc > 1 && !strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
305 /* avoid matching "-dec" for DECgraphics; since the man page
306 * says -d directory, hope nobody's using -desomething_else
311 if (*dir == '=' || *dir == ':')
313 if (!*dir && argc > 1) {
319 error("Flag -d must be followed by a directory name.");
320 Strcpy(hackdir, dir);
324 * Now we know the directory containing 'record' and
325 * may do a prscore().
327 if (!strncmp(argv[1], "-s", 2)) {
328 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
335 nethack_exit(EXIT_SUCCESS);
338 /* Don't initialize the window system just to print usage */
339 if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) {
341 nethack_exit(EXIT_SUCCESS);
346 * It seems you really want to play.
349 if (comp_times((long) time(&clock_time)))
350 error("Your clock is incorrectly set!");
354 "%s\n%s\n%s\n%s\n\nNetHack was unable to open the required file "
356 copyright_banner_line(1), copyright_banner_line(2),
357 copyright_banner_line(3), copyright_banner_line(4), DLBFILE,
359 error("dlb_init failure.");
362 u.uhp = 1; /* prevent RIP on early quits */
363 u.ux = 0; /* prevent flush_screen() */
365 /* chdir shouldn't be called before this point to keep the
366 * code parallel to other ports.
368 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
373 /* In 3.6.0, several ports process options before they init
374 * the window port. This allows settings that impact window
375 * ports to be specified or read from the sys or user config files.
377 process_options(argc, argv);
382 init_nhwindows(&argc, argv);
384 init_nhwindows(&argc, argv);
385 process_options(argc, argv);
389 set_lock_and_bones();
395 /* strip role,race,&c suffix; calls askname() if plname[] is empty
396 or holds a generic user name like "player" or "games" */
398 set_playmode(); /* sets plname to "wizard" for wizard mode */
400 /* unlike Unix where the game might be invoked with a script
401 which forces a particular character name for each player
402 using a shared account, we always allow player to rename
403 the character during role/race/&c selection */
404 iflags.renameallowed = TRUE;
406 /* until the getlock code is resolved, override askname()'s
407 setting of renameallowed; when False, player_selection()
408 won't resent renaming as an option */
409 iflags.renameallowed = FALSE;
412 #if defined(PC_LOCKING)
413 /* 3.3.0 added this to support detection of multiple games
414 * under the same plname on the same machine in a windowed
415 * or multitasking environment.
417 * That allows user confirmation prior to overwriting the
418 * level files of a game in progress.
420 * Also prevents an aborted game's level files from being
421 * overwritten without confirmation when a user starts up
422 * another game with the same player name.
424 Strcpy(lock, plname);
427 #else /* What follows is !PC_LOCKING */
428 #ifdef AMIGA /* We'll put the bones & levels in the user specified directory \
430 Strcat(lock, plname);
434 /* I'm not sure what, if anything, is left here, but MFLOPPY has
435 * conflicts with set_lock_and_bones() in files.c.
437 Strcpy(lock, plname);
439 regularize(lock); /* is this necessary? */
440 /* not compatible with full path a la AMIGA */
443 #endif /* PC_LOCKING */
445 /* Set up level 0 file to keep the game state.
447 fd = create_levelfile(0, (char *) 0);
449 raw_print("Cannot create lock file");
452 write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
456 level_info[0].where = ACTIVE;
460 * Initialize the vision system. This must be before mklev() on a
461 * new game or before a level restore on a saved game.
465 display_gamewindows();
467 * First, try to find and restore a save file for specified character.
468 * We'll return here if new game player_selection() renames the hero.
471 if ((fd = restore_saved_game()) >= 0) {
473 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
477 display_file(NEWS, FALSE);
482 pline("Restoring save file...");
484 pline("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\95\9c\8c³
\92\86\81D
\81D
\81D");
485 mark_synch(); /* flush output */
488 resuming = TRUE; /* not starting new game */
491 You("are in non-scoring discovery mode.");
493 pline("
\94
\8c©
\83\82\81[
\83h
\82Å
\82Í
\83X
\83R
\83A
\82Í
\82Ì
\82ç
\82È
\82¢
\82æ
\81D");
494 if (discover || wizard) {
496 if (yn("Do you want to keep the save file?") == 'n')
498 if (yn("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\8ec
\82µ
\82Ä
\82¨
\82«
\82Ü
\82·
\82©
\81H") == 'n')
499 (void) delete_savefile();
501 nh_compress(fqname(SAVEF, SAVEPREFIX, 0));
508 /* new game: start by choosing role, race, etc;
509 player might change the hero's name while doing that,
510 in which case we try to restore under the new name
511 and skip selection this time if that didn't succeed */
512 if (!iflags.renameinprogress) {
514 if (iflags.renameinprogress) {
515 /* player has renamed the hero while selecting role;
516 discard current lock file and create another for
517 the new character name */
518 #if 0 /* this needs to be reconciled with the getlock mess above... */
519 delete_levelfile(0); /* remove empty lock file */
522 goto attempt_restore;
528 You("are in non-scoring discovery mode.");
530 pline("
\94
\8c©
\83\82\81[
\83h
\82Å
\82Í
\83X
\83R
\83A
\82Í
\82Ì
\82ç
\82È
\82¢
\82æ
\81D");
534 (void) signal(SIGINT, SIG_IGN);
537 gettty(); /* somehow ctrl-P gets turned back on during startup ... */
543 process_options(argc, argv)
552 while (argc > 1 && argv[1][0] == '-') {
555 switch (argv[0][1]) {
558 if ((i = str2align(&argv[0][2])) >= 0)
560 } else if (argc > 1) {
563 if ((i = str2align(argv[0])) >= 0)
568 wizard = TRUE, discover = FALSE;
571 discover = TRUE, wizard = FALSE;
580 (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
584 (void) strncpy(plname, argv[0], sizeof(plname) - 1);
586 raw_print("Player name expected after -u");
591 if (!strncmpi(argv[0] + 1, "IBM", 3)) {
592 load_symset("IBMGraphics", PRIMARY);
593 load_symset("RogueIBM", ROGUESET);
594 switch_symbols(TRUE);
599 if (!strncmpi(argv[0] + 1, "DEC", 3)) {
600 load_symset("DECGraphics", PRIMARY);
601 switch_symbols(TRUE);
607 if ((i = str2gend(&argv[0][2])) >= 0)
609 } else if (argc > 1) {
612 if ((i = str2gend(argv[0])) >= 0)
616 case 'p': /* profession (role) */
618 if ((i = str2role(&argv[0][2])) >= 0)
620 } else if (argc > 1) {
623 if ((i = str2role(argv[0])) >= 0)
629 if ((i = str2race(&argv[0][2])) >= 0)
631 } else if (argc > 1) {
634 if ((i = str2race(argv[0])) >= 0)
640 /* Player doesn't want to use a RAM disk
648 /* interlaced and non-interlaced screens */
660 if ((i = str2role(&argv[0][1])) >= 0) {
664 raw_printf("\nUnknown switch: %s", argv[0]);
668 nethack_exit(EXIT_SUCCESS);
676 char buf1[BUFSZ], buf2[BUFSZ], *bufptr;
681 #define ADD_USAGE(s) \
682 if ((strlen(buf1) + strlen(s)) < (BUFSZ - 1)) \
685 /* -role still works for those cases which aren't already taken, but
686 * is deprecated and will not be listed here.
688 (void) Sprintf(buf2, "\nUsage:\n%s [-d dir] -s [-r race] [-p profession] "
689 "[maxrank] [name]...\n or",
694 buf2, "\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]",
701 ADD_USAGE(" [-I] [-i] [-d]");
709 ADD_USAGE(" [-[lL]]");
711 if (!iflags.window_inited)
712 raw_printf("%s\n", buf1);
714 (void) printf("%s\n", buf1);
725 static char thisdir[] = "";
727 static char thisdir[] = ".";
729 if (dir && chdir(dir) < 0) {
730 error("Cannot chdir to %s.", dir);
734 /* Change the default drive as well.
739 /* warn the player if we can't write the record file */
740 /* perhaps we should also test whether . is writable */
741 /* unfortunately the access system-call is worthless */
743 check_recordfile(dir ? dir : thisdir);
752 /* display port specific help file */
753 display_file(PORT_HELP, 1);
756 #endif /* PORT_HELP */
758 /* validate wizard mode if player has requested access to it */
760 authorize_wizard_mode()
762 if (!strcmp(plname, WIZARD_NAME))
769 #define PATH_SEPARATOR '/'
771 #define PATH_SEPARATOR '\\'
774 #define EXEPATHBUFSZ 256
775 char exepathbuf[EXEPATHBUFSZ];
786 bsize = EXEPATHBUFSZ;
789 tmp2 = strrchr(tmp, PATH_SEPARATOR);