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