1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * The game adventure was originally written in Fortran by Will Crowther 6 * and Don Woods. It was later translated to C and enhanced by Jim 7 * Gillogly. This code is derived from software contributed to Berkeley 8 * by Jim Gillogly at The Rand Corporation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)io.c 8.1 (Berkeley) 5/31/93 35 * $FreeBSD: src/games/adventure/io.c,v 1.8.2.1 2001/03/05 11:43:11 kris Exp $ 36 * $DragonFly: src/games/adventure/io.c,v 1.3 2007/04/18 18:32:12 swildner Exp $ 37 */ 38 39 /* Re-coding of advent in C: file i/o and user i/o */ 40 41 #include "hdr.h" 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <err.h> 46 47 static int next(void); 48 static void rdesc(int); 49 static void rdflt(void); 50 static void rhints(void); 51 static void rliq(void); 52 static void rlocs(void); 53 static int rnum(void); 54 static void rtrav(void); 55 static void rvoc(void); 56 #ifdef DEBUG 57 static void twrite(int); 58 #endif 59 60 /* Get command from user. No prompt, usually. */ 61 void 62 getin(char **wrd1, char **wrd2) 63 { 64 char *s; 65 static char wd1buf[MAXSTR], wd2buf[MAXSTR]; 66 int first, numch; 67 68 *wrd1 = wd1buf; /* return ptr to internal string */ 69 *wrd2 = wd2buf; 70 wd2buf[0] = 0; /* in case it isn't set here */ 71 for (s = wd1buf, first = 1, numch = 0;;) { 72 if ((*s = getchar()) >= 'A' && *s <= 'Z') 73 *s = *s - ('A' - 'a'); /* convert to upper case */ 74 switch (*s) { /* start reading from user */ 75 case '\n': 76 *s = 0; 77 return; 78 case ' ': 79 if (s == wd1buf || s == wd2buf) /* initial blank */ 80 continue; 81 *s = 0; 82 if (first) { /* finished 1st wd; start 2nd */ 83 first = numch = 0; 84 s = wd2buf; 85 break; 86 } else { /* finished 2nd word */ 87 FLUSHLINE; 88 *s = 0; 89 return; 90 } 91 case EOF: 92 printf("user closed input stream, quitting...\n"); 93 exit(0); 94 default: 95 if (++numch >= MAXSTR) { /* string too long */ 96 printf("Give me a break!!\n"); 97 wd1buf[0] = wd2buf[0] = 0; 98 FLUSHLINE; 99 return; 100 } 101 s++; 102 } 103 } 104 } 105 106 /* confirm with rspeak */ 107 int 108 yes(int x, int y, int z) 109 { 110 int result; 111 int ch; 112 113 result = FALSE; 114 for (;;) { 115 rspeak(x); /* tell him what we want */ 116 if ((ch = getchar()) == 'y') 117 result = TRUE; 118 else if (ch == 'n') 119 result = FALSE; 120 else if (ch == EOF) { 121 printf("user closed input stream, quitting...\n"); 122 exit(0); 123 } 124 FLUSHLINE; 125 if (ch == 'y' || ch == 'n') 126 break; 127 printf("Please answer the question.\n"); 128 } 129 if (result == TRUE) 130 rspeak(y); 131 if (result == FALSE) 132 rspeak(z); 133 return (result); 134 } 135 136 /* confirm with mspeak */ 137 int 138 yesm(int x, int y, int z) 139 { 140 int result; 141 int ch; 142 143 result = FALSE; 144 for (;;) { 145 mspeak(x); /* tell him what we want */ 146 if ((ch = getchar()) == 'y') 147 result = TRUE; 148 else if (ch == 'n') 149 result = FALSE; 150 else if (ch == EOF) { 151 printf("user closed input stream, quitting...\n"); 152 exit(0); 153 } 154 FLUSHLINE; 155 if (ch == 'y' || ch == 'n') 156 break; 157 printf("Please answer the question.\n"); 158 } 159 if (result == TRUE) 160 mspeak(y); 161 if (result == FALSE) 162 mspeak(z); 163 return (result); 164 } 165 166 /* FILE *inbuf, *outbuf; */ 167 168 char *inptr; /* Pointer into virtual disk */ 169 170 int outsw = 0; /* putting stuff to data file? */ 171 172 const char iotape[] = "Ax3F'\003tt$8h\315qer*h\017nGKrX\207:!l"; 173 const char *tape = iotape; /* pointer to encryption tape */ 174 175 /* next virtual char, bump adr */ 176 static int 177 next(void) 178 { 179 int ch; 180 181 ch = (*inptr ^ random()) & 0xFF; /* Decrypt input data */ 182 if (outsw) { /* putting data in tmp file */ 183 if (*tape == 0) 184 tape = iotape; /* rewind encryption tape */ 185 *inptr = ch ^ *tape++; /* re-encrypt and replace value */ 186 } 187 inptr++; 188 return (ch); 189 } 190 191 char breakch; /* tell which char ended rnum */ 192 193 /* "read" data from virtual file */ 194 void 195 rdata(void) 196 { 197 int sect; 198 char ch; 199 200 inptr = data_file; /* Pointer to virtual data file */ 201 srandom(SEED); /* which is lightly encrypted. */ 202 203 clsses = 1; 204 for (;;) { /* read data sections */ 205 sect = next() - '0'; /* 1st digit of section number */ 206 #ifdef VERBOSE 207 printf("Section %c", sect + '0'); 208 #endif 209 if ((ch = next()) != LF) { /* is there a second digit? */ 210 FLUSHLF; 211 #ifdef VERBOSE 212 putchar(ch); 213 #endif 214 sect = 10 * sect + ch - '0'; 215 } 216 #ifdef VERBOSE 217 putchar('\n'); 218 #endif 219 switch (sect) { 220 case 0: /* finished reading database */ 221 return; 222 case 1: /* long form descriptions */ 223 rdesc(1); 224 break; 225 case 2: /* short form descriptions */ 226 rdesc(2); 227 break; 228 case 3: /* travel table */ 229 rtrav(); 230 break; 231 case 4: /* vocabulary */ 232 rvoc(); 233 break; 234 case 5: /* object descriptions */ 235 rdesc(5); 236 break; 237 case 6: /* arbitrary messages */ 238 rdesc(6); 239 break; 240 case 7: /* object locations */ 241 rlocs(); 242 break; 243 case 8: /* action defaults */ 244 rdflt(); 245 break; 246 case 9: /* liquid assets */ 247 rliq(); 248 break; 249 case 10: /* class messages */ 250 rdesc(10); 251 break; 252 case 11: /* hints */ 253 rhints(); 254 break; 255 case 12: /* magic messages */ 256 rdesc(12); 257 break; 258 default: 259 printf("Invalid data section number: %d\n", sect); 260 for (;;) 261 putchar(next()); 262 } 263 if (breakch != LF) /* routines return after "-1" */ 264 FLUSHLF; 265 } 266 } 267 268 char nbf[12]; 269 270 /* read initial location num */ 271 static int 272 rnum(void) 273 { 274 char *s; 275 276 tape = iotape; /* restart encryption tape */ 277 for (s = nbf, *s = 0;; s++) 278 if ((*s = next()) == TAB || *s == '\n' || *s == LF) 279 break; 280 breakch = *s; /* save char for rtrav() */ 281 *s = 0; /* got the number as ascii */ 282 if (nbf[0] == '-') /* end of data */ 283 return (-1); 284 return (atoi(nbf)); /* convert it to integer */ 285 } 286 287 char *seekhere; 288 289 /* read description-format msgs */ 290 static void 291 rdesc(int sect) 292 { 293 int locc; 294 char *seekstart, *maystart; 295 296 seekhere = inptr; /* Where are we in virtual file? */ 297 outsw = 1; /* these msgs go into tmp file */ 298 for (oldloc = -1, seekstart = seekhere;;) { 299 maystart = inptr; /* maybe starting new entry */ 300 if ((locc = rnum()) != oldloc && oldloc >= 0 /* finished msg */ 301 /* unless sect 5 */ 302 && !(sect == 5 && (locc == 0 || locc >= 100))) { 303 switch (sect) { /* now put it into right table */ 304 case 1: /* long descriptions */ 305 ltext[oldloc].seekadr = seekhere; 306 ltext[oldloc].txtlen = maystart - seekstart; 307 break; 308 case 2: /* short descriptions */ 309 stext[oldloc].seekadr = seekhere; 310 stext[oldloc].txtlen = maystart - seekstart; 311 break; 312 case 5: /* object descriptions */ 313 ptext[oldloc].seekadr = seekhere; 314 ptext[oldloc].txtlen = maystart - seekstart; 315 break; 316 case 6: /* random messages */ 317 if (oldloc > RTXSIZ) 318 errx(1, "Too many random msgs"); 319 rtext[oldloc].seekadr = seekhere; 320 rtext[oldloc].txtlen = maystart - seekstart; 321 break; 322 case 10: /* class messages */ 323 ctext[clsses].seekadr = seekhere; 324 ctext[clsses].txtlen = maystart - seekstart; 325 cval[clsses++] = oldloc; 326 break; 327 case 12: /* magic messages */ 328 if (oldloc > MAGSIZ) 329 errx(1, "Too many magic msgs"); 330 mtext[oldloc].seekadr = seekhere; 331 mtext[oldloc].txtlen = maystart - seekstart; 332 break; 333 default: 334 errx(1, "rdesc called with bad section"); 335 } 336 seekhere += maystart - seekstart; 337 } 338 if (locc < 0) { 339 outsw = 0; /* turn off output */ 340 seekhere += 3; /* -1<delimiter> */ 341 return; 342 } 343 if (sect != 5 || (locc > 0 && locc < 100)) { 344 if (oldloc != locc) /* starting a new message */ 345 seekstart = maystart; 346 oldloc = locc; 347 } 348 FLUSHLF; /* scan the line */ 349 } 350 } 351 352 /* read travel table */ 353 static void 354 rtrav(void) 355 { 356 int locc; 357 struct travlist *t; 358 char *s; 359 char buf[12]; 360 int len, m, n, entries; 361 362 entries = 0; 363 t = NULL; 364 for (oldloc = -1;;) { /* get another line */ 365 /* end of entry */ 366 if ((locc = rnum()) != oldloc && oldloc >= 0) { 367 t->next = 0; /* terminate the old entry */ 368 #if DEBUG 369 printf("%d:%d entries\n", oldloc, entries); 370 twrite(oldloc); 371 #endif 372 } 373 if (locc == -1) 374 return; 375 if (locc != oldloc) { /* getting a new entry */ 376 t = travel[locc] = malloc(sizeof(*t)); 377 if (t == NULL) 378 errx(1, "Out of memory!"); 379 /* printf("New travel list for %d\n", locc); */ 380 entries = 0; 381 oldloc = locc; 382 } 383 for (s = buf;; s++) /* get the newloc number /ASCII */ 384 if ((*s = next()) == TAB || *s == LF) 385 break; 386 *s = 0; 387 len = strlen(buf); /* quad long number handling */ 388 /* printf("Newloc: %s (%d chars)\n", buf, len); */ 389 if (len < 4) { /* no "m" conditions */ 390 m = 0; 391 n = atoi(buf); /* newloc mod 1000 = newloc */ 392 } else { /* a long integer */ 393 n = atoi(buf + len - 3); 394 buf[len - 3] = 0; /* terminate newloc/1000 */ 395 m = atoi(buf); 396 } 397 while (breakch != LF) { /* only do one line at a time */ 398 if (entries++) { 399 t = t->next = malloc(sizeof(*t)); 400 if (t == NULL) 401 errx(1, "Out of memory!"); 402 } 403 t->tverb = rnum(); /* get verb from the file */ 404 t->tloc = n; /* table entry mod 1000 */ 405 t->conditions = m; /* table entry / 1000 */ 406 /* printf("entry %d for %d\n", entries, locc); */ 407 } 408 } 409 } 410 411 #ifdef DEBUG 412 413 /* travel options from this loc */ 414 static void 415 twrite(int loq) 416 { 417 struct travlist *t; 418 419 printf("If"); 420 speak(<ext[loq]); 421 printf("then\n"); 422 for (t = travel[loq]; t != 0; t = t->next) { 423 printf("verb %d takes you to ", t->tverb); 424 if (t->tloc <= 300) 425 speak(<ext[t->tloc]); 426 else if (t->tloc <= 500) 427 printf("special code %d\n", t->tloc - 300); 428 else 429 rspeak(t->tloc - 500); 430 printf("under conditions %d\n", t->conditions); 431 } 432 } 433 #endif /* DEBUG */ 434 435 /* read the vocabulary */ 436 static void 437 rvoc(void) 438 { 439 char *s; 440 int rv_index; 441 char buf[6]; 442 443 for (;;) { 444 rv_index = rnum(); 445 if (rv_index < 0) 446 break; 447 for (s = buf, *s = 0;; s++) /* get the word */ 448 if ((*s = next()) == TAB || *s == '\n' || *s == LF 449 || *s == ' ') 450 break; 451 /* terminate word with newline, LF, tab, blank */ 452 if (*s != '\n' && *s != LF) 453 FLUSHLF; /* can be comments */ 454 *s = 0; 455 /* printf("\"%s\"=%d\n", buf, rv_index); */ 456 vocab(buf, -2, rv_index); 457 } 458 } 459 460 /* initial object locations */ 461 static void 462 rlocs(void) 463 { 464 for (;;) { 465 if ((obj = rnum()) < 0) 466 break; 467 plac[obj] = rnum(); /* initial loc for this obj */ 468 if (breakch == TAB) /* there's another entry */ 469 fixd[obj] = rnum(); 470 else 471 fixd[obj] = 0; 472 } 473 } 474 475 /* default verb messages */ 476 static void 477 rdflt(void) 478 { 479 for (;;) { 480 if ((verb = rnum()) < 0) 481 break; 482 actspk[verb] = rnum(); 483 } 484 } 485 486 /* liquid assets &c: cond bits */ 487 static void 488 rliq(void) 489 { 490 int bitnum; 491 492 for (;;) { /* read new bit list */ 493 if ((bitnum = rnum()) < 0) 494 break; 495 for (;;) { /* read locs for bits */ 496 cond[rnum()] |= setbit[bitnum]; 497 if (breakch == LF) 498 break; 499 } 500 } 501 } 502 503 static void 504 rhints(void) 505 { 506 int hintnum, i; 507 508 hntmax = 0; 509 for (;;) { 510 if ((hintnum = rnum()) < 0) 511 break; 512 for (i = 1; i < 5; i++) 513 hints[hintnum][i] = rnum(); 514 if (hintnum > hntmax) 515 hntmax = hintnum; 516 } 517 } 518 519 void 520 rspeak(int msg) 521 { 522 if (msg != 0) 523 speak(&rtext[msg]); 524 } 525 526 void 527 mspeak(int msg) 528 { 529 if (msg != 0) 530 speak(&mtext[msg]); 531 } 532 533 /* read, decrypt, and print a message (not ptext) */ 534 /* msg is a pointer to seek address and length of mess */ 535 void 536 speak(const struct text *msg) 537 { 538 char *s, nonfirst; 539 540 s = msg->seekadr; 541 nonfirst = 0; 542 while (s - msg->seekadr < msg->txtlen) { /* read a line at a time */ 543 tape = iotape; /* restart decryption tape */ 544 while ((*s++ ^ *tape++) != TAB); /* read past loc num */ 545 /* assume tape is longer than location number */ 546 /* plus the lookahead put together */ 547 if ((*s ^ *tape) == '>' && 548 (*(s + 1) ^ *(tape + 1)) == '$' && 549 (*(s + 2) ^ *(tape + 2)) == '<') 550 break; 551 if (blklin && !nonfirst++) 552 putchar('\n'); 553 do { 554 if (*tape == 0) 555 tape = iotape; /* rewind decryp tape */ 556 putchar(*s ^ *tape); 557 } while ((*s++ ^ *tape++) != LF); /* better end with LF */ 558 } 559 } 560 561 /* read, decrypt and print a ptext message */ 562 /* msg is the number of all the p msgs for this place */ 563 /* assumes object 1 doesn't have prop 1, obj 2 no prop 2, etc. */ 564 void 565 pspeak(int m, int skip) 566 { 567 char *s, nonfirst; 568 char *numst, ps_save; 569 struct text *msg; 570 char *tbuf; 571 572 msg = &ptext[m]; 573 if ((tbuf = malloc(msg->txtlen + 1)) == 0) 574 errx(1, "Out of memory!"); 575 memcpy(tbuf, msg->seekadr, (unsigned)msg->txtlen + 1); /* Room to null */ 576 s = tbuf; 577 578 nonfirst = 0; 579 while (s - tbuf < msg->txtlen) { /* read line at a time */ 580 tape = iotape; /* restart decryption tape */ 581 for (numst = s; (*s ^= *tape++) != TAB; s++) /* get number */ 582 ; /* nothing */ 583 584 ps_save = *s; /* Temporarily trash the string (cringe) */ 585 *s++ = 0; /* decrypting number within the string */ 586 587 if (atoi(numst) != 100 * skip && skip >= 0) { 588 while ((*s++ ^ *tape++) != LF) /* flush the line */ 589 if (*tape == 0) 590 tape = iotape; 591 continue; 592 } 593 if ((*s ^ *tape) == '>' && (*(s + 1) ^ *(tape + 1)) == '$' && 594 (*(s + 2) ^ *(tape + 2)) == '<') 595 break; 596 if (blklin && !nonfirst++) 597 putchar('\n'); 598 do { 599 if (*tape == 0) 600 tape = iotape; 601 putchar(*s ^ *tape); 602 } while ((*s++ ^ *tape++) != LF); /* better end with LF */ 603 if (skip < 0) 604 break; 605 } 606 free(tbuf); 607 } 608