1/* SCCS Id: @(#)pcmain.c 3.4 2002/08/22 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5/* main.c - MSDOS, OS/2, ST, Amiga, and NT NetHack */ 6 7#if 0 8 9#include "hack.h" 10#include "dlb.h" 11 12#ifndef NO_SIGNAL 13#include <signal.h> 14#endif 15 16#include <ctype.h> 17 18#if 0 19#if !defined(AMIGA) && !defined(GNUDOS) 20#include <sys\stat.h> 21#else 22# ifdef GNUDOS 23#include <sys/stat.h> 24# endif 25#endif 26#endif 27 28#ifdef WIN32 29#include "win32api.h" /* for GetModuleFileName */ 30#endif 31 32#ifdef __DJGPP__ 33#include <unistd.h> /* for getcwd() prototype */ 34#endif 35 36#ifdef OVL0 37#define SHARED_DCL 38#else 39#define SHARED_DCL extern 40#endif 41 42 43#define PATHLEN 512 44SHARED_DCL char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */ 45 46#ifdef TOS 47boolean run_from_desktop = TRUE; /* should we pause before exiting?? */ 48# ifdef __GNUC__ 49long _stksize = 16*1024; 50# endif 51#endif 52 53#ifdef AMIGA 54extern int bigscreen; 55void NDECL( preserve_icon ); 56#endif 57 58STATIC_DCL void FDECL(process_options,(int argc,char **argv)); 59STATIC_DCL void NDECL(nhusage); 60 61#if defined(MICRO) || defined(WIN32) || defined(OS2) 62extern void FDECL(nethack_exit,(int)); 63#else 64#define nethack_exit exit 65#endif 66 67#ifdef WIN32 68extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ 69#endif 70 71#if defined(MSWIN_GRAPHICS) 72extern void NDECL(mswin_destroy_reg); 73#endif 74 75#ifdef EXEPATH 76STATIC_DCL char *FDECL(exepath,(char *)); 77#endif 78 79#ifdef OVL0 80int FDECL(main, (int,char **)); 81#endif 82 83extern void FDECL(pcmain, (int,char **)); 84 85 86#if defined(__BORLANDC__) && !defined(_WIN32) 87void NDECL( startup ); 88# ifdef OVLB 89unsigned _stklen = STKSIZ; 90# else 91extern unsigned _stklen; 92# endif 93#endif 94 95#ifdef OVL0 96/* If the graphics version is built, we don't need a main; it is skipped 97 * to help MinGW decide which entry point to choose. If both main and 98 * WinMain exist, the resulting executable won't work correctly. 99 */ 100#ifndef MSWIN_GRAPHICS 101int 102main(argc,argv) 103int argc; 104char *argv[]; 105{ 106 pcmain(argc,argv); 107#ifdef LAN_FEATURES 108 init_lan_features(); 109#endif 110 moveloop(); 111 nethack_exit(EXIT_SUCCESS); 112 /*NOTREACHED*/ 113 return 0; 114} 115#endif /*MSWIN_GRAPHICS*/ 116#endif /*OVL0*/ 117#ifdef OVL1 118 119extern char hackdir[]; 120 121void 122pcmain(argc,argv) 123int argc; 124char *argv[]; 125{ 126 127 register int fd; 128 register char *dir; 129#if defined(WIN32) 130 char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; 131#endif 132#ifdef NOCWD_ASSUMPTIONS 133 char failbuf[BUFSZ]; 134#endif 135 136#if defined(__BORLANDC__) && !defined(_WIN32) 137 startup(); 138#endif 139 140#ifdef TOS 141 long clock_time; 142 if (*argv[0]) { /* only a CLI can give us argv[0] */ 143 hname = argv[0]; 144 run_from_desktop = FALSE; 145 } else 146#endif 147 hname = "NetHack"; /* used for syntax messages */ 148 149 choose_windows(DEFAULT_WINDOW_SYS); 150 151#if !defined(AMIGA) && !defined(GNUDOS) 152 /* Save current directory and make sure it gets restored when 153 * the game is exited. 154 */ 155 if (getcwd(orgdir, sizeof orgdir) == (char *)0) 156 error("NetHack: current directory path too long"); 157# ifndef NO_SIGNAL 158 signal(SIGINT, (SIG_RET_TYPE) nethack_exit); /* restore original directory */ 159# endif 160#endif /* !AMIGA && !GNUDOS */ 161 162 dir = nh_getenv("NETHACKDIR"); 163 if (dir == (char *)0) 164 dir = nh_getenv("HACKDIR"); 165#ifdef EXEPATH 166 if (dir == (char *)0) 167 dir = exepath(argv[0]); 168#endif 169 if (dir != (char *)0) { 170 (void) strncpy(hackdir, dir, PATHLEN - 1); 171 hackdir[PATHLEN-1] = '\0'; 172#ifdef NOCWD_ASSUMPTIONS 173 { 174 int prefcnt; 175 176 fqn_prefix[0] = (char *)alloc(strlen(hackdir)+2); 177 Strcpy(fqn_prefix[0], hackdir); 178 append_slash(fqn_prefix[0]); 179 for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) 180 fqn_prefix[prefcnt] = fqn_prefix[0]; 181 } 182#endif 183#ifdef CHDIR 184 chdirx (dir, 1); 185#endif 186 } 187#ifdef AMIGA 188# ifdef CHDIR 189 /* 190 * If we're dealing with workbench, change the directory. Otherwise 191 * we could get "Insert disk in drive 0" messages. (Must be done 192 * before initoptions()).... 193 */ 194 if(argc == 0) 195 chdirx(HACKDIR, 1); 196# endif 197 ami_wininit_data(); 198#endif 199 initoptions(); 200 201#ifdef NOCWD_ASSUMPTIONS 202 if (!validate_prefix_locations(failbuf)) { 203 raw_printf("Some invalid directory locations were specified:\n\t%s\n", 204 failbuf); 205 nethack_exit(EXIT_FAILURE); 206 } 207#endif 208 209#if defined(TOS) && defined(TEXTCOLOR) 210 if (iflags.BIOS && iflags.use_color) 211 set_colors(); 212#endif 213 if (!hackdir[0]) 214#if !defined(LATTICE) && !defined(AMIGA) 215 Strcpy(hackdir, orgdir); 216#else 217 Strcpy(hackdir, HACKDIR); 218#endif 219 if(argc > 1) { 220 if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { 221 /* avoid matching "-dec" for DECgraphics; since the man page 222 * says -d directory, hope nobody's using -desomething_else 223 */ 224 argc--; 225 argv++; 226 dir = argv[0]+2; 227 if(*dir == '=' || *dir == ':') dir++; 228 if(!*dir && argc > 1) { 229 argc--; 230 argv++; 231 dir = argv[0]; 232 } 233 if(!*dir) 234 error("Flag -d must be followed by a directory name."); 235 Strcpy(hackdir, dir); 236 } 237 if (argc > 1) { 238 239 /* 240 * Now we know the directory containing 'record' and 241 * may do a prscore(). 242 */ 243 if (!strncmp(argv[1], "-s", 2)) { 244#if !defined(MSWIN_GRAPHICS) 245# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) 246 chdirx(hackdir,0); 247# endif 248 prscore(argc, argv); 249#else 250 raw_printf("-s is not supported for the Graphical Interface\n"); 251#endif /*MSWIN_GRAPHICS*/ 252 nethack_exit(EXIT_SUCCESS); 253 } 254 255#ifdef MSWIN_GRAPHICS 256 if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */ 257 mswin_destroy_reg(); 258 nethack_exit(EXIT_SUCCESS); 259 } 260#endif 261 /* Don't initialize the window system just to print usage */ 262 if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) { 263 nhusage(); 264 nethack_exit(EXIT_SUCCESS); 265 } 266 } 267 } 268 269 /* 270 * It seems you really want to play. 271 */ 272#ifdef TOS 273 if (comp_times((long)time(&clock_time))) 274 error("Your clock is incorrectly set!"); 275#endif 276 u.uhp = 1; /* prevent RIP on early quits */ 277 u.ux = 0; /* prevent flush_screen() */ 278 279 /* chdir shouldn't be called before this point to keep the 280 * code parallel to other ports. 281 */ 282#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) 283 chdirx(hackdir,1); 284#endif 285 286#ifdef MSDOS 287 process_options(argc, argv); 288 init_nhwindows(&argc,argv); 289#else 290 init_nhwindows(&argc,argv); 291 process_options(argc, argv); 292#endif 293 294#ifdef WIN32CON 295 toggle_mouse_support(); /* must come after process_options */ 296#endif 297 298#ifdef MFLOPPY 299 set_lock_and_bones(); 300# ifndef AMIGA 301 copybones(FROMPERM); 302# endif 303#endif 304 305 if (!*plname) 306 askname(); 307 plnamesuffix(); /* strip suffix from name; calls askname() */ 308 /* again if suffix was whole name */ 309 /* accepts any suffix */ 310#ifdef WIZARD 311 if (wizard) { 312# ifdef KR1ED 313 if(!strcmp(plname, WIZARD_NAME)) 314# else 315 if(!strcmp(plname, WIZARD)) 316# endif 317 Strcpy(plname, "wizard"); 318 else { 319 wizard = FALSE; 320 discover = TRUE; 321 } 322 } 323#endif /* WIZARD */ 324#if defined(PC_LOCKING) 325 /* 3.3.0 added this to support detection of multiple games 326 * under the same plname on the same machine in a windowed 327 * or multitasking environment. 328 * 329 * That allows user confirmation prior to overwriting the 330 * level files of a game in progress. 331 * 332 * Also prevents an aborted game's level files from being 333 * overwritten without confirmation when a user starts up 334 * another game with the same player name. 335 */ 336# if defined(WIN32) 337 /* Obtain the name of the logged on user and incorporate 338 * it into the name. */ 339 Sprintf(fnamebuf, "%s-%s", get_username(0), plname); 340 (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", 341 '%', fnamebuf, encodedfnamebuf, BUFSZ); 342 Sprintf(lock, "%s",encodedfnamebuf); 343 /* regularize(lock); */ /* we encode now, rather than substitute */ 344# else 345 Strcpy(lock,plname); 346 regularize(lock); 347# endif 348 getlock(); 349#else /* What follows is !PC_LOCKING */ 350# ifdef AMIGA /* We'll put the bones & levels in the user specified directory -jhsa */ 351 Strcat(lock,plname); 352 Strcat(lock,".99"); 353# else 354# ifndef MFLOPPY 355 /* I'm not sure what, if anything, is left here, but MFLOPPY has 356 * conflicts with set_lock_and_bones() in files.c. 357 */ 358 Strcpy(lock,plname); 359 Strcat(lock,".99"); 360 regularize(lock); /* is this necessary? */ 361 /* not compatible with full path a la AMIGA */ 362# endif 363# endif 364#endif /* PC_LOCKING */ 365 366 /* Set up level 0 file to keep the game state. 367 */ 368 fd = create_levelfile(0, (char *)0); 369 if (fd < 0) { 370 raw_print("Cannot create lock file"); 371 } else { 372#ifdef WIN32 373 hackpid = GetCurrentProcessId(); 374#else 375 hackpid = 1; 376#endif 377 write(fd, (genericptr_t) &hackpid, sizeof(hackpid)); 378 close(fd); 379 } 380#ifdef MFLOPPY 381 level_info[0].where = ACTIVE; 382#endif 383 384 /* 385 * Initialisation of the boundaries of the mazes 386 * Both boundaries have to be even. 387 */ 388 389 x_maze_max = COLNO-1; 390 if (x_maze_max % 2) 391 x_maze_max--; 392 y_maze_max = ROWNO-1; 393 if (y_maze_max % 2) 394 y_maze_max--; 395 396 /* 397 * Initialize the vision system. This must be before mklev() on a 398 * new game or before a level restore on a saved game. 399 */ 400 vision_init(); 401 402 dlb_init(); 403 404 display_gamewindows(); 405#ifdef WIN32 406 getreturn_enabled = TRUE; 407#endif 408 409 if ((fd = restore_saved_game()) >= 0) { 410#ifdef WIZARD 411 /* Since wizard is actually flags.debug, restoring might 412 * overwrite it. 413 */ 414 boolean remember_wiz_mode = wizard; 415#endif 416#ifndef NO_SIGNAL 417 (void) signal(SIGINT, (SIG_RET_TYPE) done1); 418#endif 419#ifdef NEWS 420 if(iflags.news){ 421 display_file(NEWS, FALSE); 422 iflags.news = FALSE; 423 } 424#endif 425 pline("Restoring save file..."); 426 mark_synch(); /* flush output */ 427 428 if(!dorecover(fd)) 429 goto not_recovered; 430#ifdef WIZARD 431 if(!wizard && remember_wiz_mode) wizard = TRUE; 432#endif 433 check_special_room(FALSE); 434 if (discover) 435 You("are in non-scoring discovery mode."); 436 437 if (discover || wizard) { 438 if(yn("Do you want to keep the save file?") == 'n'){ 439 (void) delete_savefile(); 440 } 441 } 442 443 flags.move = 0; 444 } else { 445not_recovered: 446 player_selection(); 447 newgame(); 448 if (discover) 449 You("are in non-scoring discovery mode."); 450 451 flags.move = 0; 452 set_wear(); 453 (void) pickup(1); 454 read_engr_at(u.ux,u.uy); 455 } 456 457#ifndef NO_SIGNAL 458 (void) signal(SIGINT, SIG_IGN); 459#endif 460#ifdef OS2 461 gettty(); /* somehow ctrl-P gets turned back on during startup ... */ 462#endif 463 return; 464} 465 466STATIC_OVL void 467process_options(argc, argv) 468int argc; 469char *argv[]; 470{ 471 int i; 472 473 474 /* 475 * Process options. 476 */ 477 while(argc > 1 && argv[1][0] == '-'){ 478 argv++; 479 argc--; 480 switch(argv[0][1]){ 481 case 'a': 482 if (argv[0][2]) { 483 if ((i = str2align(&argv[0][2])) >= 0) 484 flags.initalign = i; 485 } else if (argc > 1) { 486 argc--; 487 argv++; 488 if ((i = str2align(argv[0])) >= 0) 489 flags.initalign = i; 490 } 491 break; 492 case 'D': 493#ifdef WIZARD 494 /* If they don't have a valid wizard name, it'll be 495 * changed to discover later. Cannot check for 496 * validity of the name right now--it might have a 497 * character class suffix, for instance. 498 */ 499 wizard = TRUE; 500 break; 501#endif 502 case 'X': 503 discover = TRUE; 504 break; 505#ifdef NEWS 506 case 'n': 507 iflags.news = FALSE; 508 break; 509#endif 510 case 'u': 511 if(argv[0][2]) 512 (void) strncpy(plname, argv[0]+2, sizeof(plname)-1); 513 else if(argc > 1) { 514 argc--; 515 argv++; 516 (void) strncpy(plname, argv[0], sizeof(plname)-1); 517 } else 518 raw_print("Player name expected after -u"); 519 break; 520#ifndef AMIGA 521 case 'I': 522 case 'i': 523 if (!strncmpi(argv[0]+1, "IBM", 3)) 524 switch_graphics(IBM_GRAPHICS); 525 break; 526 /* case 'D': */ 527 case 'd': 528 if (!strncmpi(argv[0]+1, "DEC", 3)) 529 switch_graphics(DEC_GRAPHICS); 530 break; 531#endif 532 case 'g': 533 if (argv[0][2]) { 534 if ((i = str2gend(&argv[0][2])) >= 0) 535 flags.initgend = i; 536 } else if (argc > 1) { 537 argc--; 538 argv++; 539 if ((i = str2gend(argv[0])) >= 0) 540 flags.initgend = i; 541 } 542 break; 543 case 'p': /* profession (role) */ 544 if (argv[0][2]) { 545 if ((i = str2role(&argv[0][2])) >= 0) 546 flags.initrole = i; 547 } else if (argc > 1) { 548 argc--; 549 argv++; 550 if ((i = str2role(argv[0])) >= 0) 551 flags.initrole = i; 552 } 553 break; 554 case 'r': /* race */ 555 if (argv[0][2]) { 556 if ((i = str2race(&argv[0][2])) >= 0) 557 flags.initrace = i; 558 } else if (argc > 1) { 559 argc--; 560 argv++; 561 if ((i = str2race(argv[0])) >= 0) 562 flags.initrace = i; 563 } 564 break; 565#ifdef MFLOPPY 566# ifndef AMIGA 567 /* Player doesn't want to use a RAM disk 568 */ 569 case 'R': 570 ramdisk = FALSE; 571 break; 572# endif 573#endif 574#ifdef AMIGA 575 /* interlaced and non-interlaced screens */ 576 case 'L': 577 bigscreen = 1; 578 break; 579 case 'l': 580 bigscreen = -1; 581 break; 582#endif 583 case '@': 584 flags.randomall = 1; 585 break; 586 default: 587 if ((i = str2role(&argv[0][1])) >= 0) { 588 flags.initrole = i; 589 break; 590 } else raw_printf("\nUnknown switch: %s", argv[0]); 591 /* FALL THROUGH */ 592 case '?': 593 nhusage(); 594 nethack_exit(EXIT_SUCCESS); 595 } 596 } 597} 598 599STATIC_OVL void 600nhusage() 601{ 602 char buf1[BUFSZ], buf2[BUFSZ], *bufptr; 603 604 buf1[0] = '\0'; 605 bufptr = buf1; 606 607#define ADD_USAGE(s) if ((strlen(buf1) + strlen(s)) < (BUFSZ - 1)) Strcat(bufptr, s); 608 609 /* -role still works for those cases which aren't already taken, but 610 * is deprecated and will not be listed here. 611 */ 612 (void) Sprintf(buf2, 613"\nUsage:\n%s [-d dir] -s [-r race] [-p profession] [maxrank] [name]...\n or", 614 hname); 615 ADD_USAGE(buf2); 616 617 (void) Sprintf(buf2, 618 "\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]", 619 hname); 620 ADD_USAGE(buf2); 621#ifdef NEWS 622 ADD_USAGE(" [-n]"); 623#endif 624#ifndef AMIGA 625 ADD_USAGE(" [-I] [-i] [-d]"); 626#endif 627#ifdef MFLOPPY 628# ifndef AMIGA 629 ADD_USAGE(" [-R]"); 630# endif 631#endif 632#ifdef AMIGA 633 ADD_USAGE(" [-[lL]]"); 634#endif 635 if (!iflags.window_inited) 636 raw_printf("%s\n",buf1); 637 else 638 (void) printf("%s\n",buf1); 639#undef ADD_USAGE 640} 641 642#ifdef CHDIR 643void 644chdirx(dir, wr) 645char *dir; 646boolean wr; 647{ 648# ifdef AMIGA 649 static char thisdir[] = ""; 650# else 651 static char thisdir[] = "."; 652# endif 653 if(dir && chdir(dir) < 0) { 654 error("Cannot chdir to %s.", dir); 655 } 656 657# ifndef AMIGA 658 /* Change the default drive as well. 659 */ 660 chdrive(dir); 661# endif 662 663 /* warn the player if we can't write the record file */ 664 /* perhaps we should also test whether . is writable */ 665 /* unfortunately the access system-call is worthless */ 666 if (wr) check_recordfile(dir ? dir : thisdir); 667} 668#endif /* CHDIR */ 669#endif /*OVL1*/ 670#ifdef OVLB 671 672#ifdef PORT_HELP 673# if defined(MSDOS) || defined(WIN32) 674void 675port_help() 676{ 677 /* display port specific help file */ 678 display_file( PORT_HELP, 1 ); 679} 680# endif /* MSDOS || WIN32 */ 681#endif /* PORT_HELP */ 682 683#ifdef EXEPATH 684# ifdef __DJGPP__ 685#define PATH_SEPARATOR '/' 686# else 687#define PATH_SEPARATOR '\\' 688# endif 689 690#define EXEPATHBUFSZ 256 691char exepathbuf[EXEPATHBUFSZ]; 692 693char *exepath(str) 694char *str; 695{ 696 char *tmp, *tmp2; 697 int bsize; 698 699 if (!str) return (char *)0; 700 bsize = EXEPATHBUFSZ; 701 tmp = exepathbuf; 702# ifndef WIN32 703 Strcpy (tmp, str); 704# else 705 #ifdef UNICODE 706 { 707 TCHAR wbuf[BUFSZ]; 708 GetModuleFileName((HANDLE)0, wbuf, BUFSZ); 709 WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL); 710 } 711 #else 712 *(tmp + GetModuleFileName((HANDLE)0, tmp, bsize)) = '\0'; 713 #endif 714# endif 715 tmp2 = strrchr(tmp, PATH_SEPARATOR); 716 if (tmp2) *tmp2 = '\0'; 717 return tmp; 718} 719#endif /* EXEPATH */ 720#endif /*OVLB*/ 721/*pcmain.c*/ 722 723#endif