1 /* NetHack 3.6 pcmain.c $NHDT-Date: 1449116336 2015/12/03 04:18:56 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.66 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* main.c - MSDOS, OS/2, ST, Amiga, and Windows NetHack */
16 #if !defined(AMIGA) && !defined(GNUDOS)
25 #include "win32api.h" /* for GetModuleFileName */
29 #include <unistd.h> /* for getcwd() prototype */
32 char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */
35 boolean run_from_desktop = TRUE; /* should we pause before exiting?? */
37 long _stksize = 16 * 1024;
43 void NDECL(preserve_icon);
46 STATIC_DCL void FDECL(process_options, (int argc, char **argv));
47 STATIC_DCL void NDECL(nhusage);
49 #if defined(MICRO) || defined(WIN32) || defined(OS2)
50 extern void FDECL(nethack_exit, (int));
52 #define nethack_exit exit
56 extern boolean getreturn_enabled; /* from sys/share/pcsys.c */
57 extern int redirect_stdout; /* from sys/share/pcsys.c */
59 char default_window_sys[] = "mswin";
62 #if defined(MSWIN_GRAPHICS)
63 extern void NDECL(mswin_destroy_reg);
67 STATIC_DCL char *FDECL(exepath, (char *));
70 int FDECL(main, (int, char **));
72 extern boolean FDECL(pcmain, (int, char **));
74 #if defined(__BORLANDC__) && !defined(_WIN32)
76 unsigned _stklen = STKSIZ;
79 /* If the graphics version is built, we don't need a main; it is skipped
80 * to help MinGW decide which entry point to choose. If both main and
81 * WinMain exist, the resulting executable won't work correctly.
93 Strcpy(default_window_sys, "tty");
96 resuming = pcmain(argc, argv);
101 nethack_exit(EXIT_SUCCESS);
117 char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ];
118 boolean save_getreturn_status = getreturn_enabled;
120 #ifdef NOCWD_ASSUMPTIONS
123 boolean resuming = FALSE; /* assume new game */
127 /* set these appropriately for VS debugging */
128 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
129 _CrtSetReportMode(_CRT_ERROR,
130 _CRTDBG_MODE_DEBUG); /* | _CRTDBG_MODE_FILE);*/
131 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
132 /*| _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW);*/
133 /* use STDERR by default
134 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
135 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
139 #if defined(__BORLANDC__) && !defined(_WIN32)
145 if (*argv[0]) { /* only a CLI can give us argv[0] */
147 run_from_desktop = FALSE;
150 hname = "NetHack"; /* used for syntax messages */
153 choose_windows(DEFAULT_WINDOW_SYS);
155 choose_windows(default_window_sys);
158 #if !defined(AMIGA) && !defined(GNUDOS)
159 /* Save current directory and make sure it gets restored when
160 * the game is exited.
162 if (getcwd(orgdir, sizeof orgdir) == (char *) 0)
163 error("NetHack: current directory path too long");
166 (SIG_RET_TYPE) nethack_exit); /* restore original directory */
168 #endif /* !AMIGA && !GNUDOS */
170 dir = nh_getenv("NETHACKDIR");
171 if (dir == (char *) 0)
172 dir = nh_getenv("HACKDIR");
174 if (dir == (char *) 0)
175 dir = exepath(argv[0]);
177 if (dir != (char *) 0) {
178 (void) strncpy(hackdir, dir, PATHLEN - 1);
179 hackdir[PATHLEN - 1] = '\0';
180 #ifdef NOCWD_ASSUMPTIONS
184 fqn_prefix[0] = (char *) alloc(strlen(hackdir) + 2);
185 Strcpy(fqn_prefix[0], hackdir);
186 append_slash(fqn_prefix[0]);
187 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++)
188 fqn_prefix[prefcnt] = fqn_prefix[0];
190 #if defined(WIN32) || defined(MSDOS)
191 /* sysconf should be searched for in this location */
192 envp = nh_getenv("COMMONPROGRAMFILES");
194 if ((sptr = index(envp, ';')) != 0)
196 if (strlen(envp) > 0) {
197 fqn_prefix[SYSCONFPREFIX] =
198 (char *) alloc(strlen(envp) + 10);
199 Strcpy(fqn_prefix[SYSCONFPREFIX], envp);
200 append_slash(fqn_prefix[SYSCONFPREFIX]);
201 Strcat(fqn_prefix[SYSCONFPREFIX], "NetHack\\");
205 /* user's home directory should default to this - unless
207 envp = nh_getenv("USERPROFILE");
209 if ((sptr = index(envp, ';')) != 0)
211 if (strlen(envp) > 0) {
212 fqn_prefix[CONFIGPREFIX] =
213 (char *) alloc(strlen(envp) + 2);
214 Strcpy(fqn_prefix[CONFIGPREFIX], envp);
215 append_slash(fqn_prefix[CONFIGPREFIX]);
221 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
228 * If we're dealing with workbench, change the directory. Otherwise
229 * we could get "Insert disk in drive 0" messages. (Must be done
230 * before initoptions())....
238 save_getreturn_status = getreturn_enabled;
240 getreturn_enabled = TRUE;
244 #ifdef NOCWD_ASSUMPTIONS
245 if (!validate_prefix_locations(failbuf)) {
246 raw_printf("Some invalid directory locations were specified:\n\t%s\n",
248 nethack_exit(EXIT_FAILURE);
252 #if defined(TOS) && defined(TEXTCOLOR)
253 if (iflags.BIOS && iflags.use_color)
257 #if !defined(LATTICE) && !defined(AMIGA)
258 Strcpy(hackdir, orgdir);
260 Strcpy(hackdir, HACKDIR);
263 if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
264 /* avoid matching "-dec" for DECgraphics; since the man page
265 * says -d directory, hope nobody's using -desomething_else
270 if (*dir == '=' || *dir == ':')
272 if (!*dir && argc > 1) {
278 error("Flag -d must be followed by a directory name.");
279 Strcpy(hackdir, dir);
283 * Now we know the directory containing 'record' and
284 * may do a prscore().
286 if (!strncmp(argv[1], "-s", 2)) {
288 int sfd = (int) _fileno(stdout);
289 redirect_stdout = (sfd >= 0) ? !isatty(sfd) : 0;
291 if (!redirect_stdout) {
293 "-s is not supported for the Graphical Interface\n");
294 nethack_exit(EXIT_SUCCESS);
298 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
305 nethack_exit(EXIT_SUCCESS);
308 #ifdef MSWIN_GRAPHICS
309 if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */
311 nethack_exit(EXIT_SUCCESS);
314 /* Don't initialize the window system just to print usage */
315 if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) {
317 nethack_exit(EXIT_SUCCESS);
323 getreturn_enabled = save_getreturn_status;
326 * It seems you really want to play.
329 if (comp_times((long) time(&clock_time)))
330 error("Your clock is incorrectly set!");
334 "%s\n%s\n%s\n%s\n\nNetHack was unable to open the required file "
336 copyright_banner_line(1), copyright_banner_line(2),
337 copyright_banner_line(3), copyright_banner_line(4), DLBFILE,
339 "\nAre you perhaps trying to run NetHack within a zip utility?");
343 error("dlb_init failure.");
346 u.uhp = 1; /* prevent RIP on early quits */
347 u.ux = 0; /* prevent flush_screen() */
349 /* chdir shouldn't be called before this point to keep the
350 * code parallel to other ports.
352 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
356 #if defined(MSDOS) || defined(WIN32)
357 /* In 3.6.0, several ports process options before they init
358 * the window port. This allows settings that impact window
359 * ports to be specified or read from the sys or user config files.
361 process_options(argc, argv);
365 if (!strncmpi(windowprocs.name, "mswin", 5))
369 if (!strncmpi(windowprocs.name, "tty", 3)) {
370 iflags.use_background_glyph = FALSE;
373 iflags.use_background_glyph = TRUE;
378 #if defined(MSDOS) || defined(WIN32)
379 /* Player didn't specify any symbol set so use IBM defaults */
380 if (!symset[PRIMARY].name) {
381 load_symset("IBMGraphics_2", PRIMARY);
383 if (!symset[ROGUESET].name) {
384 load_symset("RogueEpyx", ROGUESET);
388 #if defined(MSDOS) || defined(WIN32)
389 init_nhwindows(&argc, argv);
391 init_nhwindows(&argc, argv);
392 process_options(argc, argv);
396 toggle_mouse_support(); /* must come after process_options */
400 set_lock_and_bones();
406 /* strip role,race,&c suffix; calls askname() if plname[] is empty
407 or holds a generic user name like "player" or "games" */
409 set_playmode(); /* sets plname to "wizard" for wizard mode */
411 /* unlike Unix where the game might be invoked with a script
412 which forces a particular character name for each player
413 using a shared account, we always allow player to rename
414 the character during role/race/&c selection */
415 iflags.renameallowed = TRUE;
417 /* until the getlock code is resolved, override askname()'s
418 setting of renameallowed; when False, player_selection()
419 won't resent renaming as an option */
420 iflags.renameallowed = FALSE;
423 #if defined(PC_LOCKING)
424 /* 3.3.0 added this to support detection of multiple games
425 * under the same plname on the same machine in a windowed
426 * or multitasking environment.
428 * That allows user confirmation prior to overwriting the
429 * level files of a game in progress.
431 * Also prevents an aborted game's level files from being
432 * overwritten without confirmation when a user starts up
433 * another game with the same player name.
436 /* Obtain the name of the logged on user and incorporate
437 * it into the name. */
438 Sprintf(fnamebuf, "%s-%s", get_username(0), plname);
440 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", '%',
441 fnamebuf, encodedfnamebuf, BUFSZ);
442 Sprintf(lock, "%s", encodedfnamebuf);
443 /* regularize(lock); */ /* we encode now, rather than substitute */
445 Strcpy(lock, plname);
449 #else /* What follows is !PC_LOCKING */
450 #ifdef AMIGA /* We'll put the bones & levels in the user specified directory \
452 Strcat(lock, plname);
456 /* I'm not sure what, if anything, is left here, but MFLOPPY has
457 * conflicts with set_lock_and_bones() in files.c.
459 Strcpy(lock, plname);
461 regularize(lock); /* is this necessary? */
462 /* not compatible with full path a la AMIGA */
465 #endif /* PC_LOCKING */
467 /* Set up level 0 file to keep the game state.
469 fd = create_levelfile(0, (char *) 0);
471 raw_print("Cannot create lock file");
474 hackpid = GetCurrentProcessId();
478 write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
482 level_info[0].where = ACTIVE;
486 * Initialize the vision system. This must be before mklev() on a
487 * new game or before a level restore on a saved game.
491 display_gamewindows();
493 getreturn_enabled = TRUE;
497 * First, try to find and restore a save file for specified character.
498 * We'll return here if new game player_selection() renames the hero.
501 if ((fd = restore_saved_game()) >= 0) {
503 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
507 display_file(NEWS, FALSE);
512 pline("Restoring save file...");
514 pline("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\95\9c\8c³
\92\86\81D
\81D
\81D");
515 mark_synch(); /* flush output */
518 resuming = TRUE; /* not starting new game */
521 You("are in non-scoring discovery mode.");
523 pline("
\94
\8c©
\83\82\81[
\83h
\82Å
\82Í
\83X
\83R
\83A
\82Í
\82Ì
\82ç
\82È
\82¢
\82æ
\81D");
524 if (discover || wizard) {
526 if (yn("Do you want to keep the save file?") == 'n')
528 if (yn("
\83Z
\81[
\83u
\83t
\83@
\83C
\83\8b\82ð
\8ec
\82µ
\82Ä
\82¨
\82«
\82Ü
\82·
\82©
\81H") == 'n')
529 (void) delete_savefile();
531 nh_compress(fqname(SAVEF, SAVEPREFIX, 0));
538 /* new game: start by choosing role, race, etc;
539 player might change the hero's name while doing that,
540 in which case we try to restore under the new name
541 and skip selection this time if that didn't succeed */
542 if (!iflags.renameinprogress) {
544 if (iflags.renameinprogress) {
545 /* player has renamed the hero while selecting role;
546 discard current lock file and create another for
547 the new character name */
548 #if 0 /* this needs to be reconciled with the getlock mess above... */
549 delete_levelfile(0); /* remove empty lock file */
552 goto attempt_restore;
558 You("are in non-scoring discovery mode.");
560 pline("
\94
\8c©
\83\82\81[
\83h
\82Å
\82Í
\83X
\83R
\83A
\82Í
\82Ì
\82ç
\82È
\82¢
\82æ
\81D");
564 (void) signal(SIGINT, SIG_IGN);
567 gettty(); /* somehow ctrl-P gets turned back on during startup ... */
573 process_options(argc, argv)
582 while (argc > 1 && argv[1][0] == '-') {
585 switch (argv[0][1]) {
588 if ((i = str2align(&argv[0][2])) >= 0)
590 } else if (argc > 1) {
593 if ((i = str2align(argv[0])) >= 0)
598 wizard = TRUE, discover = FALSE;
601 discover = TRUE, wizard = FALSE;
610 (void) strncpy(plname, argv[0] + 2, sizeof(plname) - 1);
614 (void) strncpy(plname, argv[0], sizeof(plname) - 1);
616 raw_print("Player name expected after -u");
621 if (!strncmpi(argv[0] + 1, "IBM", 3)) {
622 load_symset("IBMGraphics", PRIMARY);
623 load_symset("RogueIBM", ROGUESET);
624 switch_symbols(TRUE);
629 if (!strncmpi(argv[0] + 1, "DEC", 3)) {
630 load_symset("DECGraphics", PRIMARY);
631 switch_symbols(TRUE);
637 if ((i = str2gend(&argv[0][2])) >= 0)
639 } else if (argc > 1) {
642 if ((i = str2gend(argv[0])) >= 0)
646 case 'p': /* profession (role) */
648 if ((i = str2role(&argv[0][2])) >= 0)
650 } else if (argc > 1) {
653 if ((i = str2role(argv[0])) >= 0)
659 if ((i = str2race(&argv[0][2])) >= 0)
661 } else if (argc > 1) {
664 if ((i = str2race(argv[0])) >= 0)
670 /* Player doesn't want to use a RAM disk
678 /* interlaced and non-interlaced screens */
687 case 'w': /* windowtype */
688 if (strncmpi(&argv[0][2], "tty", 3)) {
696 choose_windows(&argv[0][2]);
703 if ((i = str2role(&argv[0][1])) >= 0) {
707 raw_printf("\nUnknown switch: %s", argv[0]);
711 nethack_exit(EXIT_SUCCESS);
719 char buf1[BUFSZ], buf2[BUFSZ], *bufptr;
724 #define ADD_USAGE(s) \
725 if ((strlen(buf1) + strlen(s)) < (BUFSZ - 1)) \
728 /* -role still works for those cases which aren't already taken, but
729 * is deprecated and will not be listed here.
731 (void) Sprintf(buf2, "\nUsage:\n%s [-d dir] -s [-r race] [-p profession] "
732 "[maxrank] [name]...\n or",
737 buf2, "\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]",
744 ADD_USAGE(" [-I] [-i] [-d]");
752 ADD_USAGE(" [-[lL]]");
754 if (!iflags.window_inited)
755 raw_printf("%s\n", buf1);
757 (void) printf("%s\n", buf1);
768 static char thisdir[] = "";
770 static char thisdir[] = ".";
772 if (dir && chdir(dir) < 0) {
773 error("Cannot chdir to %s.", dir);
777 /* Change the default drive as well.
782 /* warn the player if we can't write the record file */
783 /* perhaps we should also test whether . is writable */
784 /* unfortunately the access system-call is worthless */
786 check_recordfile(dir ? dir : thisdir);
791 #if defined(MSDOS) || defined(WIN32)
795 /* display port specific help file */
796 display_file(PORT_HELP, 1);
798 #endif /* MSDOS || WIN32 */
799 #endif /* PORT_HELP */
801 /* validate wizard mode if player has requested access to it */
803 authorize_wizard_mode()
805 if (!strcmp(plname, WIZARD_NAME))
812 #define PATH_SEPARATOR '/'
814 #define PATH_SEPARATOR '\\'
818 static char exenamebuf[PATHLEN];
824 char *tmp = exenamebuf, *tmp2;
828 TCHAR wbuf[PATHLEN * 4];
829 GetModuleFileName((HANDLE) 0, wbuf, PATHLEN * 4);
830 WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL);
833 *(tmp + GetModuleFileName((HANDLE) 0, tmp, bsize)) = '\0';
835 tmp2 = strrchr(tmp, PATH_SEPARATOR);
843 #define EXEPATHBUFSZ 256
844 char exepathbuf[EXEPATHBUFSZ];
855 bsize = EXEPATHBUFSZ;
863 GetModuleFileName((HANDLE) 0, wbuf, BUFSZ);
864 WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL);
867 *(tmp + GetModuleFileName((HANDLE) 0, tmp, bsize)) = '\0';
870 tmp2 = strrchr(tmp, PATH_SEPARATOR);