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