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