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