This is the second of a series of changes related to save/restore.
No EDITLEVEL bump has been included, because although the code
is changed extensively by this, the content of the savefiles have
not been changed.
Push the use of the structlevel bwrite() and mread() function use
out of the core and into sfstruct.c. This is groundwork for upcoming
changes.
In the core, replace the bwrite() and mread() calls with the
use of type-specific savefile output (Sfo) and savefile
input (Sfi) macros. The macros are defined in a new header file
savefile.h, which also contains the prototypes for the sfo_* and
sfi_* functions that the macros ultimately expand to. The functions
themselves are in src/sfbase.c.
On C99, each Sfo or Sfi macro expansion refers directly to the
corresponding type-specific sfo_* or sfi_* function.
If C23 or later is is use, the majority (all but 3 types) of the
macros refer to a single _Generic output routine sfo(nhfp, dt, tag),
and a single _Generic input routine sfi(nhfp, dt, tag), which handles
the dispatch of the type-specific underlying functions. This was
somewhat experimental, but turned out to be practical because the
compiler would gripe if the type for a variable was not included in
the _Generic when passed as an argument, so it could be fixed.
This alters the savefile verication process by having a common set
return values for the related functions such as uptodate(),
check_version(), etc. The new return values return more information
about savefile incompatibilities, beyond failure/sucess. The
additional information will be useful for an upcoming addition.
The expanded return values are:
SF_UPTODATE (0) everything matched and looks good
SF_OUTDATED (1) savefile is outdated
SF_CRITICAL_BYTE_COUNT_MISMATCH (2) critical size count mismatch
SF_DM_IL32LLP64_ON_ILP32LL64 (3) Windows x64 savefile on x86
SF_DM_I32LP64_ON_ILP32LL64 (4) Unix 64 savefile on x86
SF_DM_ILP32LL64_ON_I32LP64 (5) x86 savefile on Unix 64
SF_DM_ILP32LL64_ON_IL32LLP64 (6) x86 savefile on Windows x64
SF_DM_I32LP64_ON_IL32LLP64 (7) Unix 64 savefile on Windows x64
SF_DM_IL32LLP64_ON_I32LP64 (8) Windows x64 savefile on Unix 64
SF_DM_MISMATCH (9) some other mismatch
The callers in the core have been adjusted to deal with the expanded
return values.
Other miscellaneous inclusions:
- go.oracle_loc -> svo.oracle_loc.
- add a bit (1UL << 30) to called SFCTOOL_BIT as groundwork
for changes to follow.
785 lines
21 KiB
C
785 lines
21 KiB
C
/* NetHack 3.7 pcmain.c $NHDT-Date: 1693359605 2023/08/30 01:40:05 $ $NHDT-Branch: keni-crashweb2 $:$NHDT-Revision: 1.133 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Derek S. Ray, 2015. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/* main.c - MSDOS, OS/2, ST, Amiga NetHack */
|
|
|
|
#include "hack.h"
|
|
#include "dlb.h"
|
|
#include "sfproto.h"
|
|
|
|
#ifndef NO_SIGNAL
|
|
#include <signal.h>
|
|
#endif
|
|
|
|
#if !defined(AMIGA) && !defined(__DJGPP__)
|
|
#include <sys\stat.h>
|
|
#else
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef __DJGPP__
|
|
#include <unistd.h> /* for getcwd() prototype */
|
|
#endif
|
|
|
|
char *exepath(char *);
|
|
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 preserve_icon(void);
|
|
#endif
|
|
|
|
static void process_options(int argc, char **argv);
|
|
static void nhusage(void);
|
|
|
|
#ifdef PORT_HELP
|
|
#if defined(MSDOS)
|
|
void port_help(void);
|
|
#endif /* MSDOS */
|
|
#endif /* PORT_HELP */
|
|
|
|
int main(int, char **);
|
|
|
|
extern boolean pcmain(int, char **);
|
|
|
|
#if defined(__BORLANDC__)
|
|
void startup(void);
|
|
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.
|
|
*/
|
|
int
|
|
#ifndef __MINGW32__
|
|
main(int argc, char *argv[])
|
|
#else
|
|
mingw_main(int argc, char *argv[])
|
|
#endif
|
|
{
|
|
boolean resuming;
|
|
|
|
early_init(argc, argv);
|
|
resuming = pcmain(argc, argv);
|
|
moveloop(resuming);
|
|
nethack_exit(EXIT_SUCCESS);
|
|
/*NOTREACHED*/
|
|
return 0;
|
|
}
|
|
|
|
boolean
|
|
pcmain(int argc, char *argv[])
|
|
{
|
|
NHFILE *nhfp;
|
|
char *dir;
|
|
#if defined(MSDOS)
|
|
char *envp = NULL;
|
|
char *sptr = NULL;
|
|
#endif
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
char failbuf[BUFSZ];
|
|
#endif
|
|
boolean resuming = FALSE; /* assume new game */
|
|
|
|
#ifdef _MSC_VER
|
|
# ifdef DEBUG
|
|
/* set these appropriately for VS debugging */
|
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
|
|
_CrtSetReportMode(_CRT_ERROR,
|
|
_CRTDBG_MODE_DEBUG); /* | _CRTDBG_MODE_FILE);*/
|
|
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
|
|
/*| _CRTDBG_MODE_FILE | _CRTDBG_MODE_WNDW);*/
|
|
/* use STDERR by default
|
|
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
|
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/
|
|
/* Heap Debugging
|
|
_CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
|
|
| _CRTDBG_ALLOC_MEM_DF
|
|
| _CRTDBG_CHECK_ALWAYS_DF
|
|
| _CRTDBG_CHECK_CRT_DF
|
|
| _CRTDBG_DELAY_FREE_MEM_DF
|
|
| _CRTDBG_LEAK_CHECK_DF);
|
|
_CrtSetBreakAlloc(1423);
|
|
*/
|
|
# endif
|
|
#endif
|
|
|
|
#if defined(__BORLANDC__)
|
|
startup();
|
|
#endif
|
|
|
|
#ifdef TOS
|
|
long clock_time;
|
|
if (*argv[0]) { /* only a CLI can give us argv[0] */
|
|
gh.hname = argv[0];
|
|
run_from_desktop = FALSE;
|
|
} else
|
|
#endif
|
|
gh.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
|
|
#ifdef _MSC_VER
|
|
if (IsDebuggerPresent()) {
|
|
static char exepath[_MAX_PATH];
|
|
/* check if we're running under the debugger so we can get to the right folder anyway */
|
|
if (dir != (char *)0) {
|
|
char *top = (char *)0;
|
|
|
|
if (strlen(dir) < (_MAX_PATH - 1))
|
|
strcpy(exepath, dir);
|
|
top = strstr(exepath, "\\build\\.\\Debug");
|
|
if (!top) top = strstr(exepath, "\\build\\.\\Release");
|
|
if (top) {
|
|
*top = '\0';
|
|
if (strlen(exepath) < (_MAX_PATH - (strlen("\\binary\\") + 1))) {
|
|
Strcat(exepath, "\\binary\\");
|
|
if (strlen(exepath) < (PATHLEN - 1)) {
|
|
dir = exepath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (dir != (char *)0) {
|
|
int fd;
|
|
boolean have_syscf = FALSE;
|
|
|
|
(void) strncpy(gh.hackdir, dir, PATHLEN - 1);
|
|
gh.hackdir[PATHLEN - 1] = '\0';
|
|
#ifdef NOCWD_ASSUMPTIONS
|
|
{
|
|
int prefcnt;
|
|
|
|
gf.fqn_prefix[0] = (char *) alloc(strlen(gh.hackdir) + 2);
|
|
Strcpy(gf.fqn_prefix[0], gh.hackdir);
|
|
append_slash(gf.fqn_prefix[0]);
|
|
for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++)
|
|
gf.fqn_prefix[prefcnt] = gf.fqn_prefix[0];
|
|
|
|
#if defined(MSDOS)
|
|
/* sysconf should be searched for in this location */
|
|
envp = nh_getenv("COMMONPROGRAMFILES");
|
|
if (envp) {
|
|
if ((sptr = strchr(envp, ';')) != 0)
|
|
*sptr = '\0';
|
|
if (strlen(envp) > 0) {
|
|
gf.fqn_prefix[SYSCONFPREFIX] =
|
|
(char *) alloc(strlen(envp) + 10);
|
|
Strcpy(gf.fqn_prefix[SYSCONFPREFIX], envp);
|
|
append_slash(gf.fqn_prefix[SYSCONFPREFIX]);
|
|
Strcat(gf.fqn_prefix[SYSCONFPREFIX], "NetHack\\");
|
|
}
|
|
}
|
|
|
|
/* okay so we have the overriding and definitive location
|
|
for sysconf, but only in the event that there is not a
|
|
sysconf file there (for whatever reason), check a secondary
|
|
location rather than abort. */
|
|
|
|
/* Is there a SYSCF_FILE there? */
|
|
fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY);
|
|
if (fd >= 0) {
|
|
/* readable */
|
|
close(fd);
|
|
have_syscf = TRUE;
|
|
}
|
|
|
|
if (!have_syscf) {
|
|
/* No SYSCF_FILE where there should be one, and
|
|
without an installer, a user may not be able
|
|
to place one there. So, let's try somewhere else... */
|
|
gf.fqn_prefix[SYSCONFPREFIX] = gf.fqn_prefix[0];
|
|
|
|
/* Is there a SYSCF_FILE there? */
|
|
fd = open(fqname(SYSCF_FILE, SYSCONFPREFIX, 0), O_RDONLY);
|
|
if (fd >= 0) {
|
|
/* readable */
|
|
close(fd);
|
|
have_syscf = TRUE;
|
|
}
|
|
}
|
|
|
|
/* user's home directory should default to this - unless
|
|
* overridden */
|
|
envp = nh_getenv("USERPROFILE");
|
|
if (envp) {
|
|
if ((sptr = strchr(envp, ';')) != 0)
|
|
*sptr = '\0';
|
|
if (strlen(envp) > 0) {
|
|
gf.fqn_prefix[CONFIGPREFIX] =
|
|
(char *) alloc(strlen(envp) + 2);
|
|
Strcpy(gf.fqn_prefix[CONFIGPREFIX], envp);
|
|
append_slash(gf.fqn_prefix[CONFIGPREFIX]);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#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(WININIT);
|
|
#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)
|
|
if (iflags.BIOS && iflags.use_color)
|
|
set_colors();
|
|
#endif
|
|
if (!gh.hackdir[0])
|
|
#if !defined(LATTICE) && !defined(AMIGA)
|
|
Strcpy(gh.hackdir, orgdir);
|
|
#else
|
|
Strcpy(gh.hackdir, HACKDIR);
|
|
#endif
|
|
if (argc > 1) {
|
|
if (argcheck(argc, argv, ARG_VERSION) == 2)
|
|
nethack_exit(EXIT_SUCCESS);
|
|
|
|
if (argcheck(argc, argv, ARG_DEBUG) == 1) {
|
|
argc--;
|
|
argv++;
|
|
}
|
|
|
|
if (argc > 1 && !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(gh.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(CHDIR) && !defined(NOCWD_ASSUMPTIONS)
|
|
chdirx(gh.hackdir, 0);
|
|
#endif
|
|
#ifdef SYSCF
|
|
initoptions();
|
|
#endif
|
|
prscore(argc, argv);
|
|
nethack_exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
/* Don't initialize the window system just to print usage */
|
|
if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) {
|
|
nhusage();
|
|
nethack_exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* 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%s\n\nNetHack was unable to open the required file "
|
|
"\"%s\".%s",
|
|
copyright_banner_line(1), copyright_banner_line(2),
|
|
copyright_banner_line(3), copyright_banner_line(4), DLBFILE,
|
|
"");
|
|
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(gh.hackdir, 1);
|
|
#endif
|
|
|
|
#if defined(MSDOS)
|
|
/* In 3.6.0, several ports process options before they init
|
|
* the window port. This allows settings that impact window
|
|
* ports to be specified or read from the sys or user config files.
|
|
*/
|
|
process_options(argc, argv);
|
|
|
|
#endif /* MSDOS */
|
|
|
|
#if defined(MSDOS)
|
|
init_nhwindows(&argc, argv);
|
|
#else
|
|
init_nhwindows(&argc, argv);
|
|
process_options(argc, argv);
|
|
#endif
|
|
|
|
/* strip role,race,&c suffix; calls askname() if plname[] is empty
|
|
or holds a generic user name like "player" or "games" */
|
|
plnamesuffix();
|
|
set_playmode(); /* sets plname to "wizard" for wizard mode */
|
|
#if 0
|
|
/* unlike Unix where the game might be invoked with a script
|
|
which forces a particular character name for each player
|
|
using a shared account, we always allow player to rename
|
|
the character during role/race/&c selection */
|
|
iflags.renameallowed = TRUE;
|
|
#else
|
|
/* until the getlock code is resolved, override askname()'s
|
|
setting of renameallowed; when False, player_selection()
|
|
won't resent renaming as an option */
|
|
iflags.renameallowed = FALSE;
|
|
#endif
|
|
|
|
#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.
|
|
*/
|
|
Strcpy(gl.lock, svp.plname);
|
|
regularize(gl.lock);
|
|
getlock();
|
|
#else /* What follows is !PC_LOCKING */
|
|
#ifdef AMIGA /* We'll put the bones & levels in the user specified directory \
|
|
-jhsa */
|
|
Strcat(gl.lock, svp.plname);
|
|
Strcat(gl.lock, ".99");
|
|
#else
|
|
/* I'm not sure what, if anything, is left here, but old MFLOPPY had
|
|
* conflicts with set_lock_and_bones() in files.c.
|
|
*/
|
|
Strcpy(gl.lock, svp.plname);
|
|
Strcat(gl.lock, ".99");
|
|
regularize(gl.lock); /* is this necessary? */
|
|
/* not compatible with full path a la AMIGA */
|
|
#endif
|
|
#endif /* PC_LOCKING */
|
|
|
|
/* Set up level 0 file to keep the game state.
|
|
*/
|
|
nhfp = create_levelfile(0, (char *) 0);
|
|
if (!nhfp) {
|
|
raw_print("Cannot create lock file");
|
|
} else {
|
|
svh.hackpid = 1;
|
|
Sfo_int(nhfp, &svh.hackpid, "svh.hackpid");
|
|
close_nhfile(nhfp);
|
|
}
|
|
|
|
/*
|
|
* Initialize the vision system. This must be before mklev() on a
|
|
* new game or before a level restore on a saved game.
|
|
*/
|
|
vision_init();
|
|
|
|
init_sound_disp_gamewindows();
|
|
/*
|
|
* First, try to find and restore a save file for specified character.
|
|
* We'll return here if new game player_selection() renames the hero.
|
|
*/
|
|
attempt_restore:
|
|
if ((nhfp = 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(nhfp)) {
|
|
resuming = TRUE; /* not starting new game */
|
|
if (discover)
|
|
You("are in non-scoring discovery mode.");
|
|
if (discover || wizard) {
|
|
if (y_n("Do you want to keep the save file?") == 'n')
|
|
(void) delete_savefile();
|
|
else {
|
|
nh_compress(fqname(gs.SAVEF, SAVEPREFIX, 0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!resuming) {
|
|
/* new game: start by choosing role, race, etc;
|
|
player might change the hero's name while doing that,
|
|
in which case we try to restore under the new name
|
|
and skip selection this time if that didn't succeed */
|
|
if (!iflags.renameinprogress) {
|
|
player_selection();
|
|
if (iflags.renameinprogress) {
|
|
/* player has renamed the hero while selecting role;
|
|
discard current lock file and create another for
|
|
the new character name */
|
|
#if 0 /* this needs to be reconciled with the getlock mess above... */
|
|
delete_levelfile(0); /* remove empty lock file */
|
|
getlock();
|
|
#endif
|
|
goto attempt_restore;
|
|
}
|
|
}
|
|
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 void
|
|
process_options(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(svp.plname, argv[0] + 2, sizeof(svp.plname) - 1);
|
|
else if (argc > 1) {
|
|
argc--;
|
|
argv++;
|
|
(void) strncpy(svp.plname, argv[0], sizeof(svp.plname) - 1);
|
|
} else
|
|
raw_print("Player name expected after -u");
|
|
break;
|
|
#ifndef AMIGA
|
|
case 'I':
|
|
case 'i':
|
|
if (!strncmpi(argv[0] + 1, "IBM", 3)) {
|
|
load_symset("IBMGraphics", PRIMARYSET);
|
|
load_symset("RogueIBM", ROGUESET);
|
|
switch_symbols(TRUE);
|
|
}
|
|
break;
|
|
/* case 'D': */
|
|
case 'd':
|
|
if (!strncmpi(argv[0] + 1, "DEC", 3)) {
|
|
load_symset("DECGraphics", PRIMARYSET);
|
|
switch_symbols(TRUE);
|
|
}
|
|
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 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 void
|
|
nhusage(void)
|
|
{
|
|
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",
|
|
gh.hname);
|
|
ADD_USAGE(buf2);
|
|
|
|
(void) Sprintf(
|
|
buf2, "\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]",
|
|
gh.hname);
|
|
ADD_USAGE(buf2);
|
|
#ifdef NEWS
|
|
ADD_USAGE(" [-n]");
|
|
#endif
|
|
#ifndef AMIGA
|
|
ADD_USAGE(" [-I] [-i] [-d]");
|
|
#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(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)
|
|
void
|
|
port_help(void)
|
|
{
|
|
/* display port specific help file */
|
|
display_file(PORT_HELP, 1);
|
|
}
|
|
#endif /* MSDOS */
|
|
#endif /* PORT_HELP */
|
|
|
|
/* validate wizard mode if player has requested access to it */
|
|
boolean
|
|
authorize_wizard_mode(void)
|
|
{
|
|
if (!strcmp(svp.plname, WIZARD_NAME))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* similar to above, validate explore mode access */
|
|
boolean
|
|
authorize_explore_mode(void)
|
|
{
|
|
return TRUE; /* no restrictions on explore mode */
|
|
}
|
|
|
|
#ifdef EXEPATH
|
|
#ifdef __DJGPP__
|
|
#define PATH_SEPARATOR '/'
|
|
#else
|
|
#define PATH_SEPARATOR '\\'
|
|
#endif
|
|
|
|
#define EXEPATHBUFSZ 256
|
|
char exepathbuf[EXEPATHBUFSZ];
|
|
|
|
char *
|
|
exepath(char *str)
|
|
{
|
|
char *tmp, *tmp2;
|
|
|
|
if (!str)
|
|
return (char *) 0;
|
|
tmp = exepathbuf;
|
|
Strcpy(tmp, str);
|
|
tmp2 = strrchr(tmp, PATH_SEPARATOR);
|
|
if (tmp2)
|
|
*tmp2 = '\0';
|
|
return tmp;
|
|
}
|
|
#endif /* EXEPATH */
|
|
|
|
#ifdef CROSS_TO_AMIGA
|
|
void msmsg
|
|
VA_DECL(const char *, fmt)
|
|
{
|
|
VA_START(fmt);
|
|
VA_INIT(fmt, const char *);
|
|
Vprintf(fmt, VA_ARGS);
|
|
flushout();
|
|
VA_END();
|
|
return;
|
|
}
|
|
|
|
unsigned long
|
|
sys_random_seed(void)
|
|
{
|
|
unsigned long seed = 0L;
|
|
unsigned long pid = (unsigned long) getpid();
|
|
boolean no_seed = TRUE;
|
|
|
|
#ifdef AMIGA_STRONG_RANDOM_SEED_HERE
|
|
/* hypothetical - strong seed code is required */
|
|
/* then has_strong_seed could be set */
|
|
#endif
|
|
if (no_seed) {
|
|
seed = (unsigned long) getnow(); /* time((TIME_type) 0) */
|
|
/* Quick dirty band-aid to prevent PRNG prediction */
|
|
if (pid) {
|
|
if (!(pid & 3L))
|
|
pid -= 1L;
|
|
seed *= pid;
|
|
}
|
|
}
|
|
return seed;
|
|
}
|
|
#endif
|
|
|
|
/*pcmain.c*/
|