1 /* NetHack 3.6 pcmain.c $NHDT-Date: 1457207045 2016/03/05 19:44:05 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.69 $ */
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 */
58 extern int GUILaunched
;
61 char default_window_sys
[] = "mswin";
62 boolean
NDECL(fakeconsole
);
63 void NDECL(freefakeconsole
);
66 #if defined(MSWIN_GRAPHICS)
67 extern void NDECL(mswin_destroy_reg
);
71 STATIC_DCL
char *FDECL(exepath
, (char *));
74 int FDECL(main
, (int, char **));
76 extern boolean
FDECL(pcmain
, (int, char **));
78 #if defined(__BORLANDC__) && !defined(_WIN32)
80 unsigned _stklen
= STKSIZ
;
83 /* If the graphics version is built, we don't need a main; it is skipped
84 * to help MinGW decide which entry point to choose. If both main and
85 * WinMain exist, the resulting executable won't work correctly.
97 Strcpy(default_window_sys
, "tty");
100 resuming
= pcmain(argc
, argv
);
105 nethack_exit(EXIT_SUCCESS
);
118 #if defined(WIN32) || defined(MSDOS)
123 char fnamebuf
[BUFSZ
], encodedfnamebuf
[BUFSZ
];
124 boolean save_getreturn_status
= getreturn_enabled
;
126 #ifdef NOCWD_ASSUMPTIONS
129 boolean resuming
= FALSE
; /* assume new game */
133 /* set these appropriately for VS debugging */
134 _CrtSetReportMode(_CRT_WARN
, _CRTDBG_MODE_DEBUG
);
135 _CrtSetReportMode(_CRT_ERROR
,
136 _CRTDBG_MODE_DEBUG
); /* | _CRTDBG_MODE_FILE);*/
137 _CrtSetReportMode(_CRT_ASSERT
, _CRTDBG_MODE_DEBUG
);
138 /*| _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW);*/
139 /* use STDERR by default
140 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
141 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
145 #if defined(__BORLANDC__) && !defined(_WIN32)
151 if (*argv
[0]) { /* only a CLI can give us argv[0] */
153 run_from_desktop
= FALSE
;
156 hname
= "NetHack"; /* used for syntax messages */
159 choose_windows(DEFAULT_WINDOW_SYS
);
161 choose_windows(default_window_sys
);
164 #if !defined(AMIGA) && !defined(GNUDOS)
165 /* Save current directory and make sure it gets restored when
166 * the game is exited.
168 if (getcwd(orgdir
, sizeof orgdir
) == (char *) 0)
169 error("NetHack: current directory path too long");
172 (SIG_RET_TYPE
) nethack_exit
); /* restore original directory */
174 #endif /* !AMIGA && !GNUDOS */
176 dir
= nh_getenv("NETHACKDIR");
177 if (dir
== (char *) 0)
178 dir
= nh_getenv("HACKDIR");
180 if (dir
== (char *) 0)
181 dir
= exepath(argv
[0]);
184 if (IsDebuggerPresent()) {
185 static char exepath
[_MAX_PATH
];
186 /* check if we're running under the debugger so we can get to the right folder anyway */
187 if (dir
!= (char *)0) {
188 char *top
= (char *)0;
190 if (strlen(dir
) < (_MAX_PATH
- 1))
191 strcpy(exepath
, dir
);
192 top
= strstr(exepath
, "\\build\\.\\Debug");
193 if (!top
) top
= strstr(exepath
, "\\build\\.\\Release");
196 if (strlen(exepath
) < (_MAX_PATH
- (strlen("\\binary\\") + 1))) {
197 Strcat(exepath
, "\\binary\\");
198 if (strlen(exepath
) < (PATHLEN
- 1)) {
206 if (dir
!= (char *)0) {
208 boolean have_syscf
= FALSE
;
210 (void) strncpy(hackdir
, dir
, PATHLEN
- 1);
211 hackdir
[PATHLEN
- 1] = '\0';
212 #ifdef NOCWD_ASSUMPTIONS
216 fqn_prefix
[0] = (char *) alloc(strlen(hackdir
) + 2);
217 Strcpy(fqn_prefix
[0], hackdir
);
218 append_slash(fqn_prefix
[0]);
219 for (prefcnt
= 1; prefcnt
< PREFIX_COUNT
; prefcnt
++)
220 fqn_prefix
[prefcnt
] = fqn_prefix
[0];
222 #if defined(WIN32) || defined(MSDOS)
223 /* sysconf should be searched for in this location */
224 envp
= nh_getenv("COMMONPROGRAMFILES");
226 if ((sptr
= index(envp
, ';')) != 0)
228 if (strlen(envp
) > 0) {
229 fqn_prefix
[SYSCONFPREFIX
] =
230 (char *) alloc(strlen(envp
) + 10);
231 Strcpy(fqn_prefix
[SYSCONFPREFIX
], envp
);
232 append_slash(fqn_prefix
[SYSCONFPREFIX
]);
233 Strcat(fqn_prefix
[SYSCONFPREFIX
], "NetHack\\");
237 /* okay so we have the overriding and definitive locaton
238 for sysconf, but only in the event that there is not a
239 sysconf file there (for whatever reason), check a secondary
240 location rather than abort. */
242 /* Is there a SYSCF_FILE there? */
243 fd
= open(fqname(SYSCF_FILE
, SYSCONFPREFIX
, 0), O_RDONLY
);
251 /* No SYSCF_FILE where there should be one, and
252 without an installer, a user may not be able
253 to place one there. So, let's try somewhere else... */
254 fqn_prefix
[SYSCONFPREFIX
] = fqn_prefix
[0];
256 /* Is there a SYSCF_FILE there? */
257 fd
= open(fqname(SYSCF_FILE
, SYSCONFPREFIX
, 0), O_RDONLY
);
265 /* user's home directory should default to this - unless
267 envp
= nh_getenv("USERPROFILE");
269 if ((sptr
= index(envp
, ';')) != 0)
271 if (strlen(envp
) > 0) {
272 fqn_prefix
[CONFIGPREFIX
] =
273 (char *) alloc(strlen(envp
) + 2);
274 Strcpy(fqn_prefix
[CONFIGPREFIX
], envp
);
275 append_slash(fqn_prefix
[CONFIGPREFIX
]);
281 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
288 * If we're dealing with workbench, change the directory. Otherwise
289 * we could get "Insert disk in drive 0" messages. (Must be done
290 * before initoptions())....
298 save_getreturn_status
= getreturn_enabled
;
300 getreturn_enabled
= TRUE
;
304 #ifdef NOCWD_ASSUMPTIONS
305 if (!validate_prefix_locations(failbuf
)) {
306 raw_printf("Some invalid directory locations were specified:\n\t%s\n",
308 nethack_exit(EXIT_FAILURE
);
312 #if defined(TOS) && defined(TEXTCOLOR)
313 if (iflags
.BIOS
&& iflags
.use_color
)
317 #if !defined(LATTICE) && !defined(AMIGA)
318 Strcpy(hackdir
, orgdir
);
320 Strcpy(hackdir
, HACKDIR
);
323 if (!strncmp(argv
[1], "-d", 2) && argv
[1][2] != 'e') {
324 /* avoid matching "-dec" for DECgraphics; since the man page
325 * says -d directory, hope nobody's using -desomething_else
330 if (*dir
== '=' || *dir
== ':')
332 if (!*dir
&& argc
> 1) {
338 error("Flag -d must be followed by a directory name.");
339 Strcpy(hackdir
, dir
);
344 boolean tmpconsole
= FALSE
;
345 hStdOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
348 * Now we know the directory containing 'record' and
349 * may do a prscore().
351 if (!strncmp(argv
[1], "-s", 2)) {
356 tmpconsole
= fakeconsole();
360 * Check to see if we're redirecting to a file.
362 sfd
= (int) _fileno(stdout
);
363 redirect_stdout
= (sfd
>= 0) ? !isatty(sfd
) : 0;
365 if (!redirect_stdout
&& !hStdOut
) {
367 "-s is not supported for the Graphical Interface\n");
368 nethack_exit(EXIT_SUCCESS
);
372 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
381 getreturn("to exit");
386 nethack_exit(EXIT_SUCCESS
);
389 #ifdef MSWIN_GRAPHICS
390 if (!strncmpi(argv
[1], "-clearreg", 6)) { /* clear registry */
392 nethack_exit(EXIT_SUCCESS
);
395 /* Don't initialize the window system just to print usage */
396 if (!strncmp(argv
[1], "-?", 2) || !strncmp(argv
[1], "/?", 2)) {
400 tmpconsole
= fakeconsole();
407 getreturn("to exit");
412 nethack_exit(EXIT_SUCCESS
);
418 getreturn_enabled
= save_getreturn_status
;
421 * It seems you really want to play.
424 if (comp_times((long) time(&clock_time
)))
425 error("Your clock is incorrectly set!");
429 "%s\n%s\n%s\n%s\n\nNetHack was unable to open the required file "
431 copyright_banner_line(1), copyright_banner_line(2),
432 copyright_banner_line(3), copyright_banner_line(4), DLBFILE
,
434 "\nAre you perhaps trying to run NetHack within a zip utility?");
438 error("dlb_init failure.");
441 u
.uhp
= 1; /* prevent RIP on early quits */
442 u
.ux
= 0; /* prevent flush_screen() */
444 /* chdir shouldn't be called before this point to keep the
445 * code parallel to other ports.
447 #if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
451 #if defined(MSDOS) || defined(WIN32)
452 /* In 3.6.0, several ports process options before they init
453 * the window port. This allows settings that impact window
454 * ports to be specified or read from the sys or user config files.
456 process_options(argc
, argv
);
460 if (!strncmpi(windowprocs.name, "mswin", 5))
464 if (!strncmpi(windowprocs
.name
, "tty", 3)) {
465 iflags
.use_background_glyph
= FALSE
;
468 iflags
.use_background_glyph
= TRUE
;
473 #if defined(MSDOS) || defined(WIN32)
474 /* Player didn't specify any symbol set so use IBM defaults */
475 if (!symset
[PRIMARY
].name
) {
476 load_symset("IBMGraphics_2", PRIMARY
);
478 if (!symset
[ROGUESET
].name
) {
479 load_symset("RogueEpyx", ROGUESET
);
483 #if defined(MSDOS) || defined(WIN32)
484 init_nhwindows(&argc
, argv
);
486 init_nhwindows(&argc
, argv
);
487 process_options(argc
, argv
);
491 toggle_mouse_support(); /* must come after process_options */
495 set_lock_and_bones();
501 /* strip role,race,&c suffix; calls askname() if plname[] is empty
502 or holds a generic user name like "player" or "games" */
504 set_playmode(); /* sets plname to "wizard" for wizard mode */
506 /* unlike Unix where the game might be invoked with a script
507 which forces a particular character name for each player
508 using a shared account, we always allow player to rename
509 the character during role/race/&c selection */
510 iflags
.renameallowed
= TRUE
;
512 /* until the getlock code is resolved, override askname()'s
513 setting of renameallowed; when False, player_selection()
514 won't resent renaming as an option */
515 iflags
.renameallowed
= FALSE
;
518 #if defined(PC_LOCKING)
519 /* 3.3.0 added this to support detection of multiple games
520 * under the same plname on the same machine in a windowed
521 * or multitasking environment.
523 * That allows user confirmation prior to overwriting the
524 * level files of a game in progress.
526 * Also prevents an aborted game's level files from being
527 * overwritten without confirmation when a user starts up
528 * another game with the same player name.
531 /* Obtain the name of the logged on user and incorporate
532 * it into the name. */
533 Sprintf(fnamebuf
, "%s-%s", get_username(0), plname
);
535 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", '%',
536 fnamebuf
, encodedfnamebuf
, BUFSZ
);
537 Sprintf(lock
, "%s", encodedfnamebuf
);
538 /* regularize(lock); */ /* we encode now, rather than substitute */
540 Strcpy(lock
, plname
);
544 #else /* What follows is !PC_LOCKING */
545 #ifdef AMIGA /* We'll put the bones & levels in the user specified directory \
547 Strcat(lock
, plname
);
551 /* I'm not sure what, if anything, is left here, but MFLOPPY has
552 * conflicts with set_lock_and_bones() in files.c.
554 Strcpy(lock
, plname
);
556 regularize(lock
); /* is this necessary? */
557 /* not compatible with full path a la AMIGA */
560 #endif /* PC_LOCKING */
562 /* Set up level 0 file to keep the game state.
564 fd
= create_levelfile(0, (char *) 0);
566 raw_print("Cannot create lock file");
569 hackpid
= GetCurrentProcessId();
573 write(fd
, (genericptr_t
) &hackpid
, sizeof(hackpid
));
577 level_info
[0].where
= ACTIVE
;
581 * Initialize the vision system. This must be before mklev() on a
582 * new game or before a level restore on a saved game.
586 display_gamewindows();
588 getreturn_enabled
= TRUE
;
592 * First, try to find and restore a save file for specified character.
593 * We'll return here if new game player_selection() renames the hero.
596 if ((fd
= restore_saved_game()) >= 0) {
598 (void) signal(SIGINT
, (SIG_RET_TYPE
) done1
);
602 display_file(NEWS
, FALSE
);
606 pline("Restoring save file...");
607 mark_synch(); /* flush output */
610 resuming
= TRUE
; /* not starting new game */
612 You("are in non-scoring discovery mode.");
613 if (discover
|| wizard
) {
614 if (yn("Do you want to keep the save file?") == 'n')
615 (void) delete_savefile();
617 nh_compress(fqname(SAVEF
, SAVEPREFIX
, 0));
624 /* new game: start by choosing role, race, etc;
625 player might change the hero's name while doing that,
626 in which case we try to restore under the new name
627 and skip selection this time if that didn't succeed */
628 if (!iflags
.renameinprogress
) {
630 if (iflags
.renameinprogress
) {
631 /* player has renamed the hero while selecting role;
632 discard current lock file and create another for
633 the new character name */
634 #if 0 /* this needs to be reconciled with the getlock mess above... */
635 delete_levelfile(0); /* remove empty lock file */
638 goto attempt_restore
;
643 You("are in non-scoring discovery mode.");
647 (void) signal(SIGINT
, SIG_IGN
);
650 gettty(); /* somehow ctrl-P gets turned back on during startup ... */
656 process_options(argc
, argv
)
665 while (argc
> 1 && argv
[1][0] == '-') {
668 switch (argv
[0][1]) {
671 if ((i
= str2align(&argv
[0][2])) >= 0)
673 } else if (argc
> 1) {
676 if ((i
= str2align(argv
[0])) >= 0)
681 wizard
= TRUE
, discover
= FALSE
;
684 discover
= TRUE
, wizard
= FALSE
;
693 (void) strncpy(plname
, argv
[0] + 2, sizeof(plname
) - 1);
697 (void) strncpy(plname
, argv
[0], sizeof(plname
) - 1);
699 raw_print("Player name expected after -u");
704 if (!strncmpi(argv
[0] + 1, "IBM", 3)) {
705 load_symset("IBMGraphics", PRIMARY
);
706 load_symset("RogueIBM", ROGUESET
);
707 switch_symbols(TRUE
);
712 if (!strncmpi(argv
[0] + 1, "DEC", 3)) {
713 load_symset("DECGraphics", PRIMARY
);
714 switch_symbols(TRUE
);
720 if ((i
= str2gend(&argv
[0][2])) >= 0)
722 } else if (argc
> 1) {
725 if ((i
= str2gend(argv
[0])) >= 0)
729 case 'p': /* profession (role) */
731 if ((i
= str2role(&argv
[0][2])) >= 0)
733 } else if (argc
> 1) {
736 if ((i
= str2role(argv
[0])) >= 0)
742 if ((i
= str2race(&argv
[0][2])) >= 0)
744 } else if (argc
> 1) {
747 if ((i
= str2race(argv
[0])) >= 0)
753 /* Player doesn't want to use a RAM disk
761 /* interlaced and non-interlaced screens */
770 case 'w': /* windowtype */
771 if (strncmpi(&argv
[0][2], "tty", 3)) {
779 choose_windows(&argv
[0][2]);
786 if ((i
= str2role(&argv
[0][1])) >= 0) {
790 raw_printf("\nUnknown switch: %s", argv
[0]);
794 nethack_exit(EXIT_SUCCESS
);
802 char buf1
[BUFSZ
], buf2
[BUFSZ
], *bufptr
;
807 #define ADD_USAGE(s) \
808 if ((strlen(buf1) + strlen(s)) < (BUFSZ - 1)) \
811 /* -role still works for those cases which aren't already taken, but
812 * is deprecated and will not be listed here.
814 (void) Sprintf(buf2
, "\nUsage:\n%s [-d dir] -s [-r race] [-p profession] "
815 "[maxrank] [name]...\n or",
820 buf2
, "\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]",
827 ADD_USAGE(" [-I] [-i] [-d]");
835 ADD_USAGE(" [-[lL]]");
837 if (!iflags
.window_inited
)
838 raw_printf("%s\n", buf1
);
840 (void) printf("%s\n", buf1
);
851 static char thisdir
[] = "";
853 static char thisdir
[] = ".";
855 if (dir
&& chdir(dir
) < 0) {
856 error("Cannot chdir to %s.", dir
);
860 /* Change the default drive as well.
865 /* warn the player if we can't write the record file */
866 /* perhaps we should also test whether . is writable */
867 /* unfortunately the access system-call is worthless */
869 check_recordfile(dir
? dir
: thisdir
);
874 #if defined(MSDOS) || defined(WIN32)
878 /* display port specific help file */
879 display_file(PORT_HELP
, 1);
881 #endif /* MSDOS || WIN32 */
882 #endif /* PORT_HELP */
884 /* validate wizard mode if player has requested access to it */
886 authorize_wizard_mode()
888 if (!strcmp(plname
, WIZARD_NAME
))
895 #define PATH_SEPARATOR '/'
897 #define PATH_SEPARATOR '\\'
901 static char exenamebuf
[PATHLEN
];
902 extern HANDLE hConIn
;
903 extern HANDLE hConOut
;
904 boolean has_fakeconsole
;
910 char *tmp
= exenamebuf
, *tmp2
;
914 TCHAR wbuf
[PATHLEN
* 4];
915 GetModuleFileName((HANDLE
) 0, wbuf
, PATHLEN
* 4);
916 WideCharToMultiByte(CP_ACP
, 0, wbuf
, -1, tmp
, bsize
, NULL
, NULL
);
919 *(tmp
+ GetModuleFileName((HANDLE
) 0, tmp
, bsize
)) = '\0';
921 tmp2
= strrchr(tmp
, PATH_SEPARATOR
);
932 HANDLE hStdOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
933 HANDLE hStdIn
= GetStdHandle(STD_INPUT_HANDLE
);
935 if (!hStdOut
&& !hStdIn
) {
938 AttachConsole(GetCurrentProcessId());
939 /* rval = SetStdHandle(STD_OUTPUT_HANDLE, hWrite); */
940 freopen("CON", "w", stdout
);
941 freopen("CON", "r", stdin
);
943 has_fakeconsole
= TRUE
;
946 /* Obtain handles for the standard Console I/O devices */
947 hConIn
= GetStdHandle(STD_INPUT_HANDLE
);
948 hConOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
950 if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE
) CtrlHandler
, TRUE
)) {
951 /* Unable to set control handler */
952 cmode
= 0; /* just to have a statement to break on for debugger */
955 return has_fakeconsole
;
957 void freefakeconsole()
959 if (has_fakeconsole
) {
965 #define EXEPATHBUFSZ 256
966 char exepathbuf
[EXEPATHBUFSZ
];
977 bsize
= EXEPATHBUFSZ
;
985 GetModuleFileName((HANDLE
) 0, wbuf
, BUFSZ
);
986 WideCharToMultiByte(CP_ACP
, 0, wbuf
, -1, tmp
, bsize
, NULL
, NULL
);
989 *(tmp
+ GetModuleFileName((HANDLE
) 0, tmp
, bsize
)) = '\0';
992 tmp2
= strrchr(tmp
, PATH_SEPARATOR
);