Files
nethack/sys/share/pcmain.c
nethack.rankin 43f72cc357 yet more wizard mode handling (trunk only)
Reorganize the recent wizard mode control:  move set_playmode() from
xxxmain.c to the core, and have it call new authorize_wizard_mode() to do
the port-specific part.  If the set_playmode() call during startup doesn't
result in running in wizard mode (either because not allowed or user
didn't request it), it will be called again during restore if the save
file is from a wizard mode game.

     For ports which check character name for authorization, players will
have to use `nethack -u whatever -D' (or options for name and playmode) to
restore a wizard mode save file if WIZARD has been changed from "wizard".
plname[] from a wizard mode saved game will always have that value, so if
it's not the right one players will need to get authorized by the startup
code before loading the save file.
2007-02-17 05:25:36 +00:00

746 lines
16 KiB
C

/* SCCS Id: @(#)pcmain.c 3.5 2007/02/14 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
/* main.c - MSDOS, OS/2, ST, Amiga, and NT NetHack */
#include "hack.h"
#include "dlb.h"
#ifdef SHORT_FILENAMES
#include "patchlev.h"
#else
#include "patchlevel.h"
#endif
#ifndef NO_SIGNAL
#include <signal.h>
#endif
#include <ctype.h>
#if !defined(AMIGA) && !defined(GNUDOS)
#include <sys\stat.h>
#else
# ifdef GNUDOS
#include <sys/stat.h>
# endif
#endif
#ifdef WIN32
#include "win32api.h" /* for GetModuleFileName */
#endif
#ifdef __DJGPP__
#include <unistd.h> /* for getcwd() prototype */
#endif
char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */
#ifdef TOS
boolean run_from_desktop = TRUE; /* should we pause before exiting?? */
# ifdef __GNUC__
long _stksize = 16*1024;
# endif
#endif
#ifdef AMIGA
extern int bigscreen;
void NDECL( preserve_icon );
#endif
STATIC_DCL void FDECL(process_options,(int argc,char **argv));
STATIC_DCL void NDECL(nhusage);
#if defined(MICRO) || defined(WIN32) || defined(OS2)
extern void FDECL(nethack_exit,(int));
#else
#define nethack_exit exit
#endif
#ifdef WIN32
extern boolean getreturn_enabled; /* from sys/share/pcsys.c */
extern int redirect_stdout; /* from sys/share/pcsys.c */
#endif
#if defined(MSWIN_GRAPHICS)
extern void NDECL(mswin_destroy_reg);
#endif
#ifdef EXEPATH
STATIC_DCL char *FDECL(exepath,(char *));
#endif
int FDECL(main, (int,char **));
extern boolean FDECL(pcmain, (int,char **));
#if defined(__BORLANDC__) && !defined(_WIN32)
void NDECL( startup );
unsigned _stklen = STKSIZ;
#endif
/* If the graphics version is built, we don't need a main; it is skipped
* to help MinGW decide which entry point to choose. If both main and
* WinMain exist, the resulting executable won't work correctly.
*/
#ifndef MSWIN_GRAPHICS
int
main(argc,argv)
int argc;
char *argv[];
{
boolean resuming;
resuming = pcmain(argc,argv);
#ifdef LAN_FEATURES
init_lan_features();
#endif
moveloop(resuming);
nethack_exit(EXIT_SUCCESS);
/*NOTREACHED*/
return 0;
}
#endif /*MSWIN_GRAPHICS*/
boolean
pcmain(argc,argv)
int argc;
char *argv[];
{
register int fd;
register char *dir;
#if defined(WIN32)
char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ];
boolean save_getreturn_status = getreturn_enabled;
#endif
#ifdef NOCWD_ASSUMPTIONS
char failbuf[BUFSZ];
#endif
boolean resuming = FALSE; /* assume new game */
#if defined(__BORLANDC__) && !defined(_WIN32)
startup();
#endif
#ifdef TOS
long clock_time;
if (*argv[0]) { /* only a CLI can give us argv[0] */
hname = argv[0];
run_from_desktop = FALSE;
} else
#endif
hname = "NetHack"; /* used for syntax messages */
choose_windows(DEFAULT_WINDOW_SYS);
#if !defined(AMIGA) && !defined(GNUDOS)
/* Save current directory and make sure it gets restored when
* the game is exited.
*/
if (getcwd(orgdir, sizeof orgdir) == (char *)0)
error("NetHack: current directory path too long");
# ifndef NO_SIGNAL
signal(SIGINT, (SIG_RET_TYPE) nethack_exit); /* restore original directory */
# endif
#endif /* !AMIGA && !GNUDOS */
dir = nh_getenv("NETHACKDIR");
if (dir == (char *)0)
dir = nh_getenv("HACKDIR");
#ifdef EXEPATH
if (dir == (char *)0)
dir = exepath(argv[0]);
#endif
if (dir != (char *)0) {
(void) strncpy(hackdir, dir, PATHLEN - 1);
hackdir[PATHLEN-1] = '\0';
#ifdef NOCWD_ASSUMPTIONS
{
int prefcnt;
fqn_prefix[0] = (char *)alloc(strlen(hackdir)+2);
Strcpy(fqn_prefix[0], hackdir);
append_slash(fqn_prefix[0]);
for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++)
fqn_prefix[prefcnt] = fqn_prefix[0];
}
#endif
#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx (dir, 1);
#endif
}
#ifdef AMIGA
# ifdef CHDIR
/*
* If we're dealing with workbench, change the directory. Otherwise
* we could get "Insert disk in drive 0" messages. (Must be done
* before initoptions())....
*/
if(argc == 0)
chdirx(HACKDIR, 1);
# endif
ami_wininit_data();
#endif
#ifdef WIN32CON
save_getreturn_status = getreturn_enabled;
raw_clear_screen();
getreturn_enabled = TRUE;
#endif
initoptions();
#ifdef NOCWD_ASSUMPTIONS
if (!validate_prefix_locations(failbuf)) {
raw_printf("Some invalid directory locations were specified:\n\t%s\n",
failbuf);
nethack_exit(EXIT_FAILURE);
}
#endif
#if defined(TOS) && defined(TEXTCOLOR)
if (iflags.BIOS && iflags.use_color)
set_colors();
#endif
if (!hackdir[0])
#if !defined(LATTICE) && !defined(AMIGA)
Strcpy(hackdir, orgdir);
#else
Strcpy(hackdir, HACKDIR);
#endif
if(argc > 1) {
if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') {
/* avoid matching "-dec" for DECgraphics; since the man page
* says -d directory, hope nobody's using -desomething_else
*/
argc--;
argv++;
dir = argv[0]+2;
if(*dir == '=' || *dir == ':') dir++;
if(!*dir && argc > 1) {
argc--;
argv++;
dir = argv[0];
}
if(!*dir)
error("Flag -d must be followed by a directory name.");
Strcpy(hackdir, dir);
}
if (argc > 1) {
/*
* Now we know the directory containing 'record' and
* may do a prscore().
*/
if (!strncmp(argv[1], "-s", 2)) {
#if defined(MSWIN_GRAPHICS) || defined(WIN32CON)
int sfd = _fileno(stdout);
redirect_stdout = (sfd >= 0) ? !isatty(sfd) : 0;
# ifdef MSWIN_GRAPHICS
if (!redirect_stdout) {
raw_printf("-s is not supported for the Graphical Interface\n");
nethack_exit(EXIT_SUCCESS);
}
# endif
#endif
#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(hackdir,0);
#endif
prscore(argc, argv);
nethack_exit(EXIT_SUCCESS);
}
#ifdef MSWIN_GRAPHICS
if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */
mswin_destroy_reg();
nethack_exit(EXIT_SUCCESS);
}
#endif
/* Don't initialize the window system just to print usage */
if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) {
nhusage();
nethack_exit(EXIT_SUCCESS);
}
}
}
#ifdef WIN32
getreturn_enabled = save_getreturn_status;
#endif
/*
* It seems you really want to play.
*/
#ifdef TOS
if (comp_times((long)time(&clock_time)))
error("Your clock is incorrectly set!");
#endif
if (!dlb_init()) {
pline("%s\n%s\n%s\n\nNetHack was unable to open the required file \"%s\".%s",
COPYRIGHT_BANNER_A, COPYRIGHT_BANNER_B,
COPYRIGHT_BANNER_C, DLBFILE,
#ifdef WIN32
"\nAre you perhaps trying to run NetHack within a zip utility?");
#else
"");
#endif
error("dlb_init failure.");
}
u.uhp = 1; /* prevent RIP on early quits */
u.ux = 0; /* prevent flush_screen() */
/* chdir shouldn't be called before this point to keep the
* code parallel to other ports.
*/
#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
chdirx(hackdir,1);
#endif
#ifdef MSDOS
/* We do this early for MSDOS because there are several
* display/tile related options that affect init_nhwindows()
*/
process_options(argc, argv);
#endif
#ifdef LOADSYMSETS
# if defined(MSDOS) || defined(WIN32)
/* Player didn't specify any symbol set so use IBM defaults */
if (!symset[PRIMARY].name) {
load_symset("IBMGraphics_2", PRIMARY);
}
# ifdef REINCARNATION
if (!symset[ROGUESET].name) {
load_symset("RogueEpyx", ROGUESET);
}
# endif
# endif
#endif /*LOADSYMSETS*/
#ifdef MSDOS
init_nhwindows(&argc,argv);
#else
init_nhwindows(&argc,argv);
process_options(argc, argv);
#endif
#ifdef WIN32CON
toggle_mouse_support(); /* must come after process_options */
#endif
#ifdef MFLOPPY
set_lock_and_bones();
# ifndef AMIGA
copybones(FROMPERM);
# endif
#endif
if (!*plname)
askname();
plnamesuffix(); /* strip suffix from name; calls askname() */
/* again if suffix was whole name */
/* accepts any suffix */
set_playmode(); /* sets plname to "wizard" for wizard mode */
#if defined(PC_LOCKING)
/* 3.3.0 added this to support detection of multiple games
* under the same plname on the same machine in a windowed
* or multitasking environment.
*
* That allows user confirmation prior to overwriting the
* level files of a game in progress.
*
* Also prevents an aborted game's level files from being
* overwritten without confirmation when a user starts up
* another game with the same player name.
*/
# if defined(WIN32)
/* Obtain the name of the logged on user and incorporate
* it into the name. */
Sprintf(fnamebuf, "%s-%s", get_username(0), plname);
(void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.",
'%', fnamebuf, encodedfnamebuf, BUFSZ);
Sprintf(lock, "%s",encodedfnamebuf);
/* regularize(lock); */ /* we encode now, rather than substitute */
# else
Strcpy(lock,plname);
regularize(lock);
# endif
getlock();
#else /* What follows is !PC_LOCKING */
# ifdef AMIGA /* We'll put the bones & levels in the user specified directory -jhsa */
Strcat(lock,plname);
Strcat(lock,".99");
# else
# ifndef MFLOPPY
/* I'm not sure what, if anything, is left here, but MFLOPPY has
* conflicts with set_lock_and_bones() in files.c.
*/
Strcpy(lock,plname);
Strcat(lock,".99");
regularize(lock); /* is this necessary? */
/* not compatible with full path a la AMIGA */
# endif
# endif
#endif /* PC_LOCKING */
/* Set up level 0 file to keep the game state.
*/
fd = create_levelfile(0, (char *)0);
if (fd < 0) {
raw_print("Cannot create lock file");
} else {
#ifdef WIN32
hackpid = GetCurrentProcessId();
#else
hackpid = 1;
#endif
write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
close(fd);
}
#ifdef MFLOPPY
level_info[0].where = ACTIVE;
#endif
/*
* Initialisation of the boundaries of the mazes
* Both boundaries have to be even.
*/
x_maze_max = COLNO-1;
if (x_maze_max % 2)
x_maze_max--;
y_maze_max = ROWNO-1;
if (y_maze_max % 2)
y_maze_max--;
/*
* Initialize the vision system. This must be before mklev() on a
* new game or before a level restore on a saved game.
*/
vision_init();
display_gamewindows();
#ifdef WIN32
getreturn_enabled = TRUE;
#endif
if ((fd = restore_saved_game()) >= 0) {
#ifndef NO_SIGNAL
(void) signal(SIGINT, (SIG_RET_TYPE) done1);
#endif
#ifdef NEWS
if(iflags.news){
display_file(NEWS, FALSE);
iflags.news = FALSE;
}
#endif
pline("Restoring save file...");
mark_synch(); /* flush output */
if (dorecover(fd)) {
resuming = TRUE; /* not starting new game */
if (discover)
You("are in non-scoring discovery mode.");
if (discover || wizard) {
if(yn("Do you want to keep the save file?") == 'n')
(void) delete_savefile();
else {
nh_compress(fqname(SAVEF, SAVEPREFIX, 0));
}
}
}
}
if (!resuming) {
player_selection();
newgame();
if (discover)
You("are in non-scoring discovery mode.");
}
#ifndef NO_SIGNAL
(void) signal(SIGINT, SIG_IGN);
#endif
#ifdef OS2
gettty(); /* somehow ctrl-P gets turned back on during startup ... */
#endif
return resuming;
}
STATIC_OVL void
process_options(argc, argv)
int argc;
char *argv[];
{
int i;
/*
* Process options.
*/
while(argc > 1 && argv[1][0] == '-'){
argv++;
argc--;
switch(argv[0][1]){
case 'a':
if (argv[0][2]) {
if ((i = str2align(&argv[0][2])) >= 0)
flags.initalign = i;
} else if (argc > 1) {
argc--;
argv++;
if ((i = str2align(argv[0])) >= 0)
flags.initalign = i;
}
break;
case 'D':
wizard = TRUE, discover = FALSE;
break;
case 'X':
discover = TRUE, wizard = FALSE;
break;
#ifdef NEWS
case 'n':
iflags.news = FALSE;
break;
#endif
case 'u':
if(argv[0][2])
(void) strncpy(plname, argv[0]+2, sizeof(plname)-1);
else if(argc > 1) {
argc--;
argv++;
(void) strncpy(plname, argv[0], sizeof(plname)-1);
} else
raw_print("Player name expected after -u");
break;
#ifndef AMIGA
case 'I':
case 'i':
if (!strncmpi(argv[0]+1, "IBM", 3)) {
# ifdef LOADSYMSETS
load_symset("IBMGraphics", PRIMARY);
load_symset("RogueIBM", ROGUESET);
switch_symbols(TRUE);
# endif
}
break;
/* case 'D': */
case 'd':
if (!strncmpi(argv[0]+1, "DEC", 3)) {
# ifdef LOADSYMSETS
load_symset("DECGraphics", PRIMARY);
switch_symbols(TRUE);
# endif
}
break;
#endif
case 'g':
if (argv[0][2]) {
if ((i = str2gend(&argv[0][2])) >= 0)
flags.initgend = i;
} else if (argc > 1) {
argc--;
argv++;
if ((i = str2gend(argv[0])) >= 0)
flags.initgend = i;
}
break;
case 'p': /* profession (role) */
if (argv[0][2]) {
if ((i = str2role(&argv[0][2])) >= 0)
flags.initrole = i;
} else if (argc > 1) {
argc--;
argv++;
if ((i = str2role(argv[0])) >= 0)
flags.initrole = i;
}
break;
case 'r': /* race */
if (argv[0][2]) {
if ((i = str2race(&argv[0][2])) >= 0)
flags.initrace = i;
} else if (argc > 1) {
argc--;
argv++;
if ((i = str2race(argv[0])) >= 0)
flags.initrace = i;
}
break;
#ifdef MFLOPPY
# ifndef AMIGA
/* Player doesn't want to use a RAM disk
*/
case 'R':
ramdisk = FALSE;
break;
# endif
#endif
#ifdef AMIGA
/* interlaced and non-interlaced screens */
case 'L':
bigscreen = 1;
break;
case 'l':
bigscreen = -1;
break;
#endif
case '@':
flags.randomall = 1;
break;
default:
if ((i = str2role(&argv[0][1])) >= 0) {
flags.initrole = i;
break;
} else raw_printf("\nUnknown switch: %s", argv[0]);
/* FALL THROUGH */
case '?':
nhusage();
nethack_exit(EXIT_SUCCESS);
}
}
}
STATIC_OVL void
nhusage()
{
char buf1[BUFSZ], buf2[BUFSZ], *bufptr;
buf1[0] = '\0';
bufptr = buf1;
#define ADD_USAGE(s) if ((strlen(buf1) + strlen(s)) < (BUFSZ - 1)) Strcat(bufptr, s);
/* -role still works for those cases which aren't already taken, but
* is deprecated and will not be listed here.
*/
(void) Sprintf(buf2,
"\nUsage:\n%s [-d dir] -s [-r race] [-p profession] [maxrank] [name]...\n or",
hname);
ADD_USAGE(buf2);
(void) Sprintf(buf2,
"\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]",
hname);
ADD_USAGE(buf2);
#ifdef NEWS
ADD_USAGE(" [-n]");
#endif
#ifndef AMIGA
ADD_USAGE(" [-I] [-i] [-d]");
#endif
#ifdef MFLOPPY
# ifndef AMIGA
ADD_USAGE(" [-R]");
# endif
#endif
#ifdef AMIGA
ADD_USAGE(" [-[lL]]");
#endif
if (!iflags.window_inited)
raw_printf("%s\n",buf1);
else
(void) printf("%s\n",buf1);
#undef ADD_USAGE
}
#ifdef CHDIR
void
chdirx(dir, wr)
char *dir;
boolean wr;
{
# ifdef AMIGA
static char thisdir[] = "";
# else
static char thisdir[] = ".";
# endif
if(dir && chdir(dir) < 0) {
error("Cannot chdir to %s.", dir);
}
# ifndef AMIGA
/* Change the default drive as well.
*/
chdrive(dir);
# endif
/* warn the player if we can't write the record file */
/* perhaps we should also test whether . is writable */
/* unfortunately the access system-call is worthless */
if (wr) check_recordfile(dir ? dir : thisdir);
}
#endif /* CHDIR */
#ifdef PORT_HELP
# if defined(MSDOS) || defined(WIN32)
void
port_help()
{
/* display port specific help file */
display_file( PORT_HELP, 1 );
}
# endif /* MSDOS || WIN32 */
#endif /* PORT_HELP */
/* for KR1ED config, WIZARD is 0 or 1 and WIZARD_NAME is a string;
for usual config, WIZARD is the string; forcing WIZARD_NAME to match it
eliminates conditional testing for which one to use in string ops */
#ifndef KR1ED
# undef WIZARD_NAME
# define WIZARD_NAME WIZARD
#endif
/* validate wizard mode if player has requested access to it */
boolean
authorize_wizard_mode()
{
#ifdef WIZARD
if (!strcmp(plname, WIZARD_NAME)) return TRUE;
#endif
return FALSE;
}
#ifdef EXEPATH
# ifdef __DJGPP__
#define PATH_SEPARATOR '/'
# else
#define PATH_SEPARATOR '\\'
# endif
#define EXEPATHBUFSZ 256
char exepathbuf[EXEPATHBUFSZ];
char *exepath(str)
char *str;
{
char *tmp, *tmp2;
int bsize;
if (!str) return (char *)0;
bsize = EXEPATHBUFSZ;
tmp = exepathbuf;
# ifndef WIN32
Strcpy (tmp, str);
# else
#ifdef UNICODE
{
TCHAR wbuf[BUFSZ];
GetModuleFileName((HANDLE)0, wbuf, BUFSZ);
WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL);
}
#else
*(tmp + GetModuleFileName((HANDLE)0, tmp, bsize)) = '\0';
#endif
# endif
tmp2 = strrchr(tmp, PATH_SEPARATOR);
if (tmp2) *tmp2 = '\0';
return tmp;
}
#endif /* EXEPATH */
/*pcmain.c*/