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