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