Merge from vendor branch GCC:
[dragonfly.git] / games / adventure / io.c
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(&ltext[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(&ltext[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 }