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