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