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