msdos.c (14805B)
1 /* An assortment of MSDOS functions. 2 */ 3 4 #include <stdio.h> 5 #include "hack.h" 6 7 #ifdef MSDOS 8 # include <dos.h> 9 10 void 11 flushout() 12 { 13 (void) fflush(stdout); 14 } 15 16 getuid() { 17 return 1; 18 } 19 20 char * 21 getlogin() { 22 return ((char *) NULL); 23 } 24 25 tgetch() { 26 char ch, popch(); 27 static char DOSgetch(), BIOSgetch(); 28 29 if (!(ch = popch())) { 30 # ifdef DGK 31 /* BIOSgetch can use the numeric key pad on IBM compatibles. */ 32 if (flags.IBMBIOS) 33 ch = BIOSgetch(); 34 else 35 # endif 36 ch = DOSgetch(); 37 } 38 return ((ch == '\r') ? '\n' : ch); 39 } 40 41 # define DIRECT_INPUT 0x7 42 static char 43 DOSgetch() { 44 union REGS regs; 45 46 regs.h.ah = DIRECT_INPUT; 47 intdos(®s, ®s); 48 if (!regs.h.al) { /* an extended code -- not yet supported */ 49 regs.h.ah = DIRECT_INPUT; 50 intdos(®s, ®s); /* eat the next character */ 51 regs.h.al = 0; /* and return a 0 */ 52 } 53 return (regs.h.al); 54 } 55 56 57 # ifdef DGK 58 # include <ctype.h> 59 # include <fcntl.h> 60 61 # define Sprintf (void) sprintf 62 # define PATHSEP ';' 63 64 65 static char * 66 getcomspec(warn) { 67 return getenv("COMSPEC"); 68 } 69 70 # ifdef SHELL 71 # include <process.h> 72 dosh() { 73 extern char orgdir[]; 74 char *comspec; 75 76 if (comspec = getcomspec()) { 77 settty("To return to HACK, type \"exit\" at the DOS prompt.\n"); 78 chdirx(orgdir, 0); 79 if (spawnl(P_WAIT, comspec, comspec, NULL) < 0) { 80 printf("\nCan't spawn %s !\n", comspec); 81 flags.toplin = 0; 82 more(); 83 } 84 chdirx(hackdir, 0); 85 start_screen(); 86 docrt(); 87 } else 88 pline("No COMSPEC !? Can't exec COMMAND.COM"); 89 return(0); 90 } 91 # endif /* SHELL */ 92 93 /* Normal characters are output when the shift key is not pushed. 94 * Shift characters are output when either shift key is pushed. 95 */ 96 # define KEYPADHI 83 97 # define KEYPADLOW 71 98 # define iskeypad(x) (KEYPADLOW <= (x) && (x) <= KEYPADHI) 99 static struct { 100 char normal, shift; 101 } keypad[KEYPADHI - KEYPADLOW + 1] = { 102 {'y', 'Y'}, /* 7 */ 103 {'k', 'K'}, /* 8 */ 104 {'u', 'U'}, /* 9 */ 105 {'m', CTRL('P')}, /* - */ 106 {'h', 'H'}, /* 4 */ 107 {'g', 'g'}, /* 5 */ 108 {'l', 'L'}, /* 6 */ 109 {'p', 'P'}, /* + */ 110 {'b', 'B'}, /* 1 */ 111 {'j', 'J'}, /* 2 */ 112 {'n', 'N'}, /* 3 */ 113 {'i', 'I'}, /* Ins */ 114 {'.', ':'} /* Del */ 115 }; 116 117 /* BIOSgetch gets keys directly with a BIOS call. 118 */ 119 # define SHIFT (0x1 | 0x2) 120 # define KEYBRD_BIOS 0x16 121 122 static char 123 BIOSgetch() { 124 unsigned char scan, shift, ch; 125 union REGS regs; 126 127 /* Get scan code. 128 */ 129 regs.h.ah = 0; 130 int86(KEYBRD_BIOS, ®s, ®s); 131 ch = regs.h.al; 132 scan = regs.h.ah; 133 134 /* Get shift status. 135 */ 136 regs.h.ah = 2; 137 int86(KEYBRD_BIOS, ®s, ®s); 138 shift = regs.h.al; 139 140 /* If scan code is for the keypad, translate it. 141 */ 142 if (iskeypad(scan)) { 143 if (shift & SHIFT) 144 ch = keypad[scan - KEYPADLOW].shift; 145 else 146 ch = keypad[scan - KEYPADLOW].normal; 147 } 148 return ch; 149 } 150 151 dotogglepickup() { 152 flags.pickup = !flags.pickup; 153 pline("Pickup: %s.", flags.pickup ? "ON" : "OFF"); 154 return (0); 155 } 156 157 158 /* Convert from a symbol to a string for printing object classes 159 */ 160 /* Names from objects.h 161 * char obj_symbols[] = { 162 * ILLOBJ_SYM, AMULET_SYM, FOOD_SYM, WEAPON_SYM, TOOL_SYM, 163 * BALL_SYM, CHAIN_SYM, ROCK_SYM, ARMOR_SYM, POTION_SYM, SCROLL_SYM, 164 * WAND_SYM, RING_SYM, GEM_SYM, 0 }; 165 */ 166 extern char obj_symbols[]; 167 static char *names[] = {"Illegal objects", "Amulets", "Comestibles", "Weapons", 168 "Tools", "Iron balls", "Chains", "Rocks", "Armor", 169 "Potions", "Scrolls", "Wands", "Rings", "Gems"}; 170 char * 171 let_to_name(let) 172 char let; 173 { 174 char *pos = index(obj_symbols, let); 175 extern char *US, *UE; 176 177 /* buffer is not checked for overflow. 178 */ 179 static char buf[100]; 180 181 if (pos == NULL) 182 pos = obj_symbols; 183 if (!US || !UE) 184 return names[pos - obj_symbols]; 185 else { 186 Sprintf(buf, "%s%s%s", US, names[pos - obj_symbols], UE); 187 return buf; 188 } 189 } 190 191 /* construct the string file.level */ 192 void 193 name_file(file, level) 194 char *file; 195 int level; 196 { 197 char *tf; 198 199 if (tf = rindex(file, '.')) 200 Sprintf(tf+1, "%d", level); 201 } 202 203 204 # define FINDFIRST 0x4E00 205 # define FINDNEXT 0x4F00 206 # define GETDTA 0x2F00 207 # define SETFILETIME 0x5701 208 # define GETSWITCHAR 0x3700 209 # define FREESPACE 0x36 210 211 static char 212 switchar() 213 { 214 union REGS regs; 215 216 regs.x.ax = GETSWITCHAR; 217 intdos(®s, ®s); 218 return regs.h.dl; 219 } 220 221 long 222 freediskspace(path) 223 char *path; 224 { 225 union REGS regs; 226 227 regs.h.ah = FREESPACE; 228 if (path[0] && path[1] == ':') 229 regs.h.dl = (toupper(path[0]) - 'A') + 1; 230 else 231 regs.h.dl = 0; 232 intdos(®s, ®s); 233 if (regs.x.ax == 0xFFFF) 234 return -1L; /* bad drive number */ 235 else 236 return ((long) regs.x.bx * regs.x.cx * regs.x.ax); 237 } 238 239 /* Functions to get filenames using wildcards 240 */ 241 static 242 findfirst(path) 243 char *path; 244 { 245 union REGS regs; 246 struct SREGS sregs; 247 248 regs.x.ax = FINDFIRST; 249 regs.x.cx = 0; /* normal files */ 250 regs.x.dx = FP_OFF(path); 251 sregs.ds = FP_SEG(path); 252 intdosx(®s, ®s, &sregs); 253 return !regs.x.cflag; 254 } 255 256 static 257 findnext() { 258 union REGS regs; 259 260 regs.x.ax = FINDNEXT; 261 intdos(®s, ®s); 262 return !regs.x.cflag; 263 } 264 265 /* Get disk transfer area */ 266 static char * 267 getdta() { 268 union REGS regs; 269 struct SREGS sregs; 270 char *ret; 271 272 regs.x.ax = GETDTA; 273 intdosx(®s, ®s, &sregs); 274 FP_OFF(ret) = regs.x.bx; 275 FP_SEG(ret) = sregs.es; 276 return ret; 277 } 278 279 long 280 filesize(file) 281 char *file; 282 { 283 char *dta; 284 285 if (findfirst(file)) { 286 dta = getdta(); 287 return (* (long *) (dta + 26)); 288 } else 289 return -1L; 290 } 291 292 void 293 eraseall(path, files) 294 char *path, *files; 295 { 296 char *getdta(), *dta, buf[PATHLEN]; 297 298 dta = getdta(); 299 Sprintf(buf, "%s%s", path, files); 300 if (findfirst(buf)) 301 do { 302 Sprintf(buf, "%s%s", path, dta + 30); 303 (void) unlink(buf); 304 } while (findnext()); 305 } 306 307 /* Rewritten for version 3.3 to be faster 308 */ 309 void 310 copybones(mode) { 311 char from[PATHLEN], to[PATHLEN], last[13], copy[8]; 312 char *frompath, *topath, *dta, *comspec; 313 int status; 314 long fs; 315 extern saveprompt; 316 317 if (!ramdisk) 318 return; 319 320 /* Find the name of the last file to be transferred 321 */ 322 frompath = (mode != TOPERM) ? permbones : levels; 323 dta = getdta(); 324 last[0] = '\0'; 325 Sprintf(from, "%s%s", frompath, allbones); 326 if (findfirst(from)) 327 do { 328 strcpy(last, dta + 30); 329 } while (findnext()); 330 331 topath = (mode == TOPERM) ? permbones : levels; 332 if (last[0]) { 333 Sprintf(copy, "%cC copy", switchar()); 334 335 /* Remove any bones files in `to' directory. 336 */ 337 eraseall(topath, allbones); 338 339 /* Copy `from' to `to' */ 340 Sprintf(to, "%s%s", topath, allbones); 341 comspec = getcomspec(); 342 status =spawnl(P_WAIT, comspec, comspec, copy, from, 343 to, "> nul", NULL); 344 } else 345 return; 346 347 /* See if the last file got there. If so, remove the ramdisk bones 348 * files. 349 */ 350 Sprintf(to, "%s%s", topath, last); 351 if (findfirst(to)) { 352 if (mode == TOPERM) 353 eraseall(frompath, allbones); 354 return; 355 } 356 357 /* Last file didn't get there. 358 */ 359 Sprintf(to, "%s%s", topath, allbones); 360 msmsg("Cannot copy `%s' to `%s' -- %s\n", from, to, 361 (status < 0) ? "can't spawn COMSPEC !" : 362 (freediskspace(topath) < filesize(from)) ? 363 "insufficient disk space." : "bad path(s)?"); 364 if (mode == TOPERM) { 365 msmsg("Bones will be left in `%s'\n", 366 *levels ? levels : hackdir); 367 return; 368 } else { 369 /* Remove all bones files on the RAMdisk */ 370 eraseall(levels, allbones); 371 playwoRAMdisk(); 372 } 373 } 374 375 playwoRAMdisk() { 376 msmsg("Do you wish to play without a RAMdisk (y/n) ? "); 377 378 /* Set ramdisk false *before* exit'ing (because msexit calls 379 * copybones) 380 */ 381 ramdisk = FALSE; 382 if (getchar() != 'y') { 383 settty("Be seeing you ...\n"); 384 exit(0); 385 } 386 set_lock_and_bones(); 387 return; 388 } 389 390 saveDiskPrompt(start) { 391 extern saveprompt; 392 char buf[BUFSIZ], *bp; 393 int fd; 394 395 if (saveprompt) { 396 /* Don't prompt if you can find the save file */ 397 if ((fd = open(SAVEF, 0)) >= 0) { 398 (void) close(fd); 399 return 1; 400 } 401 remember_topl(); 402 home(); 403 cl_end(); 404 msmsg("If save file is on a SAVE disk, put that disk in now.\n"); 405 cl_end(); 406 msmsg("File name (default `%s'%s) ? ", SAVEF, 407 start ? "" : ", <Esc> cancels save"); 408 getlin(buf); 409 home(); 410 cl_end(); 411 curs(1, 2); 412 cl_end(); 413 if (!start && *buf == '\033') 414 return 0; 415 416 /* Strip any whitespace. Also, if nothing was entered except 417 * whitespace, do not change the value of SAVEF. 418 */ 419 for (bp = buf; *bp; bp++) 420 if (!isspace(*bp)) { 421 strncpy(SAVEF, bp, PATHLEN); 422 break; 423 } 424 } 425 return 1; 426 } 427 428 /* Return 1 if the record file was found */ 429 static 430 record_exists() { 431 int fd; 432 433 if ((fd = open(RECORD, 0)) >= 0) { 434 close(fd); 435 return TRUE; 436 } 437 return FALSE; 438 } 439 440 /* Return 1 if the comspec was found */ 441 static 442 comspec_exists() { 443 int fd; 444 char *comspec; 445 446 if (comspec = getcomspec()) 447 if ((fd = open(comspec, 0)) >= 0) { 448 close(fd); 449 return TRUE; 450 } 451 return FALSE; 452 } 453 454 /* Prompt for game disk, then check for record file. 455 */ 456 void 457 gameDiskPrompt() { 458 extern saveprompt; 459 460 if (saveprompt) { 461 if (record_exists() && comspec_exists()) 462 return; 463 (void) putchar('\n'); 464 getreturn("when the GAME disk has been put in"); 465 } 466 if (comspec_exists() && record_exists()) 467 return; 468 469 if (!comspec_exists()) 470 msmsg("\n\nWARNING: can't find comspec `%s'!\n", getcomspec()); 471 if (!record_exists()) 472 msmsg("\n\nWARNING: can't find record file `%s'!\n", RECORD); 473 msmsg("If the GAME disk is not in, put it in now.\n"); 474 getreturn("to continue"); 475 } 476 477 /* Read configuration */ 478 void 479 read_config_file() { 480 char tmp_ramdisk[PATHLEN], tmp_levels[PATHLEN]; 481 char buf[BUFSZ], *bufp; 482 FILE *fp, *fopenp(), *fopen(); 483 extern char plname[]; 484 extern int saveprompt; 485 486 tmp_ramdisk[0] = 0; 487 tmp_levels[0] = 0; 488 if ((fp = fopen(configfile, "r")) == NULL 489 && (fp = fopenp(configfile, "r", NULL)) == NULL) { 490 msmsg("Warning: no configuration file!\n"); 491 getreturn("to continue"); 492 return; 493 } 494 while (fgets(buf, BUFSZ, fp)) { 495 if (*buf == '#') 496 continue; 497 498 /* remove trailing whitespace 499 */ 500 bufp = index(buf, '\n'); 501 while (bufp > buf && isspace(*bufp)) 502 bufp--; 503 if (bufp == buf) 504 continue; /* skip all-blank lines */ 505 else 506 *(bufp + 1) = 0; /* 0 terminate line */ 507 508 /* find the '=' */ 509 if (!(bufp = strchr(buf, '='))) { 510 msmsg("Bad option line: '%s'\n", buf); 511 getreturn("to continue"); 512 continue; 513 } 514 515 /* skip whitespace between '=' and value */ 516 while (isspace(*++bufp)) 517 ; 518 519 /* Go through possible variables */ 520 if (!strncmp(buf, "HACKDIR", 4)) { 521 strncpy(hackdir, bufp, PATHLEN); 522 523 } else if (!strncmp(buf, "RAMDISK", 3)) { 524 strncpy(tmp_ramdisk, bufp, PATHLEN); 525 526 } else if (!strncmp(buf, "LEVELS", 4)) { 527 strncpy(tmp_levels, bufp, PATHLEN); 528 529 } else if (!strncmp(buf, "OPTIONS", 4)) { 530 parseoptions(bufp, TRUE); 531 if (plname[0]) /* If a name was given */ 532 plnamesuffix(); /* set the character class */ 533 534 } else if (!strncmp(buf, "SAVE", 4)) { 535 char *ptr; 536 if (ptr = index(bufp, ';')) { 537 *ptr = '\0'; 538 if (*(ptr+1) == 'n' || *(ptr+1) == 'N') 539 saveprompt = FALSE; 540 } 541 (void) strncpy(SAVEF, bufp, PATHLEN); 542 append_slash(SAVEF); 543 544 } else if (!strncmp(buf, "GRAPHICS", 4)) { 545 struct symbols s; 546 547 if (sscanf(bufp, "%u%u%u%u%u%u%u%u%u", &s.vwall, 548 &s.hwall, &s.tlcorn, &s.trcorn, &s.blcorn, 549 &s.brcorn, &s.door, &s.room, &s.corr) == 9) 550 symbol = s; 551 else { 552 msmsg("GRAPHICS did not contain 9 values\n"); 553 getreturn("to continue"); 554 } 555 } else { 556 msmsg("Bad option line: '%s'\n", buf); 557 getreturn("to continue"); 558 } 559 } 560 fclose(fp); 561 562 strcpy(permbones, tmp_levels); 563 if (tmp_ramdisk[0]) { 564 strcpy(levels, tmp_ramdisk); 565 if (strcmpi(permbones, levels)) /* if not identical */ 566 ramdisk = TRUE; 567 } else 568 strcpy(levels, tmp_levels); 569 strcpy(bones, levels); 570 } 571 572 FILE * 573 fopenp(name, mode, pathname) 574 char *name, *mode, *pathname; 575 { 576 char buffer[BUFSIZ], *buf, *bufp, *pathp, *getenv(), lastch; 577 FILE *fp; 578 579 /* If pathname is given, use it instead of buf so the calling 580 * process knows the path we found name under 581 */ 582 if (pathname) 583 buf = pathname; 584 else 585 buf = buffer; 586 587 /* Try the default directory first. If the file can't be opened, 588 * start looking along the path. 589 */ 590 pathp = getenv("PATH"); 591 while (pathp && *pathp) { 592 bufp = buf; 593 while (*pathp && *pathp != PATHSEP) 594 lastch = *bufp++ = *pathp++; 595 if (lastch != '\\' && lastch != '/') 596 *bufp++ = '\\'; 597 strcpy(bufp, name); 598 if (fp = fopen(buf, mode)) 599 return fp; 600 if (*pathp) 601 pathp++; 602 } 603 return NULL; 604 } 605 606 /* Set names for bones[] and lock[] 607 */ 608 void 609 set_lock_and_bones() { 610 if (!ramdisk) { 611 strcpy(levels, permbones); 612 strcpy(bones, permbones); 613 } 614 append_slash(permbones); 615 append_slash(levels); 616 append_slash(bones); 617 strcat(bones, allbones); 618 strcpy(lock, levels); 619 strcat(lock, alllevels); 620 } 621 622 /* Add a backslash to any name not ending in /, \ or : There must 623 * be room for the \ 624 */ 625 void 626 append_slash(name) 627 char *name; 628 { 629 char *ptr; 630 631 if (!*name) 632 return; 633 ptr = name + (strlen(name) - 1); 634 if (*ptr != '\\' && *ptr != '/' && *ptr != ':') { 635 *++ptr = '\\'; 636 *++ptr = '\0'; 637 } 638 } 639 640 641 void 642 getreturn(str) 643 char *str; 644 { 645 int ch; 646 647 msmsg("Hit <RETURN> %s.", str); 648 while ((ch = getchar()) != '\n') 649 ; 650 } 651 652 void 653 msmsg(fmt, a1, a2, a3) 654 char *fmt; 655 long a1, a2, a3; 656 { 657 printf(fmt, a1, a2, a3); 658 flushout(); 659 } 660 661 /* Chdrive() changes the default drive. 662 */ 663 #define SELECTDISK 0x0E 664 void 665 chdrive(str) 666 char *str; 667 { 668 char *ptr; 669 union REGS inregs; 670 char drive; 671 672 if ((ptr = index(str, ':')) != NULL) { 673 drive = toupper(*(ptr - 1)); 674 inregs.h.ah = SELECTDISK; 675 inregs.h.dl = drive - 'A'; 676 intdos(&inregs, &inregs); 677 } 678 } 679 680 /* Use the IOCTL DOS function call to change stdin and stdout to raw 681 * mode. For stdin, this prevents MSDOS from trapping ^P, thus 682 * freeing us of ^P toggling 'echo to printer'. 683 * Thanks to Mark Zbikowski (markz@microsoft.UUCP). 684 */ 685 686 # define DEVICE 0x80 687 # define RAW 0x20 688 # define IOCTL 0x44 689 # define STDIN fileno(stdin) 690 # define STDOUT fileno(stdout) 691 # define GETBITS 0 692 # define SETBITS 1 693 694 static unsigned old_stdin, old_stdout, ioctl(); 695 696 disable_ctrlP() { 697 if (!flags.rawio) 698 return; 699 old_stdin = ioctl(STDIN, GETBITS, 0); 700 old_stdout = ioctl(STDOUT, GETBITS, 0); 701 if (old_stdin & DEVICE) 702 ioctl(STDIN, SETBITS, old_stdin | RAW); 703 if (old_stdout & DEVICE) 704 ioctl(STDOUT, SETBITS, old_stdout | RAW); 705 } 706 707 enable_ctrlP() { 708 if (!flags.rawio) 709 return; 710 if (old_stdin) 711 (void) ioctl(STDIN, SETBITS, old_stdin); 712 if (old_stdout) 713 (void) ioctl(STDOUT, SETBITS, old_stdout); 714 } 715 716 static unsigned 717 ioctl(handle, mode, setvalue) 718 unsigned setvalue; 719 { 720 union REGS regs; 721 722 regs.h.ah = IOCTL; 723 regs.h.al = mode; 724 regs.x.bx = handle; 725 regs.h.dl = setvalue; 726 regs.h.dh = 0; /* Zero out dh */ 727 intdos(®s, ®s); 728 return (regs.x.dx); 729 } 730 731 732 # endif /* DGK */ 733 734 /* Chdir back to original directory 735 */ 736 # undef exit 737 void 738 msexit(code) 739 { 740 # ifdef CHDIR 741 extern char orgdir[]; 742 # endif 743 744 # ifdef DGK 745 flushout(); 746 enable_ctrlP(); /* in case this wasn't done */ 747 if (ramdisk) 748 copybones(TOPERM); 749 # endif 750 # ifdef CHDIR 751 chdir(orgdir); /* chdir, not chdirx */ 752 # ifdef DGK 753 chdrive(orgdir); 754 # endif 755 # endif 756 exit(code); 757 } 758 #endif /* MSDOS */