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