Commit | Line | Data |
---|---|---|
da1ddb48 KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * The game adventure was original written Fortran by Will Crowther | |
6 | * and Don Woods. It was later translated to C and enhanced by | |
7 | * Jim Gillogly. | |
8 | * | |
9 | * %sccs.include.redist.c% | |
10 | */ | |
11 | ||
12 | #ifndef lint | |
13 | static char sccsid[] = "@(#)io.c 5.1 (Berkeley) %G%"; | |
14 | #endif /* not lint */ | |
f2b5c7ab | 15 | |
da1ddb48 | 16 | /* Re-coding of advent in C: file i/o and user i/o */ |
f2b5c7ab RH |
17 | |
18 | #include "hdr.h" | |
19 | #include <stdio.h> | |
20 | ||
21 | ||
22 | getin(wrd1,wrd2) /* get command from user */ | |
23 | char **wrd1,**wrd2; /* no prompt, usually */ | |
24 | { register char *s; | |
25 | static char wd1buf[MAXSTR],wd2buf[MAXSTR]; | |
26 | int first, numch; | |
27 | ||
28 | *wrd1=wd1buf; /* return ptr to internal string*/ | |
29 | *wrd2=wd2buf; | |
30 | wd2buf[0]=0; /* in case it isn't set here */ | |
31 | for (s=wd1buf, first=1, numch=0;;) | |
32 | { if ((*s=getchar())>='A' && *s <='Z') *s = *s - ('A' -'a'); | |
33 | /* convert to upper case */ | |
34 | switch(*s) /* start reading from user */ | |
35 | { case '\n': | |
36 | *s=0; | |
37 | return; | |
38 | case ' ': | |
39 | if (s==wd1buf||s==wd2buf) /* initial blank */ | |
40 | continue; | |
41 | *s=0; | |
42 | if (first) /* finished 1st wd; start 2nd */ | |
43 | { first=numch=0; | |
44 | s=wd2buf; | |
45 | break; | |
46 | } | |
47 | else /* finished 2nd word */ | |
48 | { FLUSHLINE; | |
49 | *s=0; | |
50 | return; | |
51 | } | |
52 | default: | |
53 | if (++numch>=MAXSTR) /* string too long */ | |
54 | { printf("Give me a break!!\n"); | |
55 | wd1buf[0]=wd2buf[0]=0; | |
56 | FLUSHLINE; | |
57 | return; | |
58 | } | |
59 | s++; | |
60 | } | |
61 | } | |
62 | } | |
63 | ||
64 | ||
65 | confirm(mesg) /* confirm irreversible action */ | |
66 | char *mesg; | |
67 | { register int result; | |
68 | printf("%s",mesg); /* tell him what he did */ | |
69 | if (getchar()=='y') /* was his first letter a 'y'? */ | |
70 | result=1; | |
71 | else result=0; | |
72 | FLUSHLINE; | |
73 | return(result); | |
74 | } | |
75 | ||
76 | yes(x,y,z) /* confirm with rspeak */ | |
77 | int x,y,z; | |
78 | { register int result; | |
79 | register char ch; | |
80 | for (;;) | |
81 | { rspeak(x); /* tell him what we want*/ | |
82 | if ((ch=getchar())=='y') | |
83 | result=TRUE; | |
84 | else if (ch=='n') result=FALSE; | |
85 | FLUSHLINE; | |
86 | if (ch=='y'|| ch=='n') break; | |
87 | printf("Please answer the question.\n"); | |
88 | } | |
89 | if (result==TRUE) rspeak(y); | |
90 | if (result==FALSE) rspeak(z); | |
91 | return(result); | |
92 | } | |
93 | ||
94 | yesm(x,y,z) /* confirm with mspeak */ | |
95 | int x,y,z; | |
96 | { register int result; | |
97 | register char ch; | |
98 | for (;;) | |
99 | { mspeak(x); /* tell him what we want*/ | |
100 | if ((ch=getchar())=='y') | |
101 | result=TRUE; | |
102 | else if (ch=='n') result=FALSE; | |
103 | FLUSHLINE; | |
104 | if (ch=='y'|| ch=='n') break; | |
105 | printf("Please answer the question.\n"); | |
106 | } | |
107 | if (result==TRUE) mspeak(y); | |
108 | if (result==FALSE) mspeak(z); | |
109 | return(result); | |
110 | } | |
111 | ||
112 | FILE *inbuf,*outbuf; | |
113 | ||
114 | int adrptr; /* current seek adr ptr */ | |
115 | int outsw = 0; /* putting stuff to data file? */ | |
116 | ||
117 | char iotape[] = "Ax3F'tt$8hqer*hnGKrX:!l"; | |
118 | char *tape = iotape; /* pointer to encryption tape */ | |
119 | ||
120 | next() /* next char frm file, bump adr */ | |
121 | { register char ch,t; | |
122 | adrptr++; /* seek address in file */ | |
123 | ch=getc(inbuf); | |
124 | if (outsw) /* putting data in tmp file */ | |
125 | { if (*tape==0) tape=iotape; /* rewind encryption tape */ | |
126 | putc(ch ^ *tape++,outbuf); /* encrypt & output char */ | |
127 | } | |
128 | return(ch); | |
129 | } | |
130 | ||
131 | ||
132 | char breakch; /* tell which char ended rnum */ | |
133 | ||
134 | ||
135 | rdata() /* read all data from orig file */ | |
136 | { register int sect; | |
137 | register char ch; | |
138 | if ((inbuf=fopen(DATFILE,"r"))==NULL) /* all the data lives in here */ | |
139 | { printf("Cannot open data file %s\n",DATFILE); | |
140 | exit(0); | |
141 | } | |
142 | if ((outbuf=fopen(TMPFILE,"w"))==NULL) /* the text lines will go here */ | |
143 | { printf("Cannot create output file %s\n",TMPFILE); | |
144 | exit(0); | |
145 | } | |
146 | setup=clsses=1; | |
147 | for (;;) /* read data sections */ | |
148 | { sect=next()-'0'; /* 1st digit of section number */ | |
149 | printf("Section %c",sect+'0'); | |
150 | if ((ch=next())!=LF) /* is there a second digit? */ | |
151 | { FLUSHLF; | |
152 | putchar(ch); | |
153 | sect=10*sect+ch-'0'; | |
154 | } | |
155 | putchar('\n'); | |
156 | switch(sect) | |
157 | { case 0: /* finished reading database */ | |
158 | fclose(inbuf); | |
159 | fclose(outbuf); | |
160 | return; | |
161 | case 1: /* long form descriptions */ | |
162 | rdesc(1); | |
163 | break; | |
164 | case 2: /* short form descriptions */ | |
165 | rdesc(2); | |
166 | break; | |
167 | case 3: /* travel table */ | |
168 | rtrav(); break; | |
169 | case 4: /* vocabulary */ | |
170 | rvoc(); | |
171 | break; | |
172 | case 5: /* object descriptions */ | |
173 | rdesc(5); | |
174 | break; | |
175 | case 6: /* arbitrary messages */ | |
176 | rdesc(6); | |
177 | break; | |
178 | case 7: /* object locations */ | |
179 | rlocs(); break; | |
180 | case 8: /* action defaults */ | |
181 | rdflt(); break; | |
182 | case 9: /* liquid assets */ | |
183 | rliq(); break; | |
184 | case 10: /* class messages */ | |
185 | rdesc(10); | |
186 | break; | |
187 | case 11: /* hints */ | |
188 | rhints(); break; | |
189 | case 12: /* magic messages */ | |
190 | rdesc(12); | |
191 | break; | |
192 | default: | |
193 | printf("Invalid data section number: %d\n",sect); | |
194 | for (;;) putchar(next()); | |
195 | } | |
196 | if (breakch!=LF) /* routines return after "-1" */ | |
197 | FLUSHLF; | |
198 | } | |
199 | } | |
200 | ||
201 | char nbf[12]; | |
202 | ||
203 | ||
204 | rnum() /* read initial location num */ | |
205 | { register char *s; | |
206 | tape = iotape; /* restart encryption tape */ | |
207 | for (s=nbf,*s=0;; s++) | |
208 | if ((*s=next())==TAB || *s=='\n' || *s==LF) | |
209 | break; | |
210 | breakch= *s; /* save char for rtrav() */ | |
211 | *s=0; /* got the number as ascii */ | |
212 | if (nbf[0]=='-') return(-1); /* end of data */ | |
213 | return(atoi(nbf)); /* convert it to integer */ | |
214 | } | |
215 | ||
216 | int seekhere = 1; /* initial seek for output file */ | |
217 | ||
218 | rdesc(sect) /* read description-format msgs */ | |
219 | int sect; | |
220 | { register char *s,*t; | |
221 | register int locc; | |
222 | int seekstart, maystart, adrstart; | |
223 | char *entry; | |
224 | outsw=1; /* these msgs go into tmp file */ | |
225 | if (sect==1) putc('X',outbuf); /* so seekadr > 0 */ | |
226 | adrptr=0; | |
227 | for (oldloc= -1, seekstart=seekhere;;) | |
228 | { maystart=adrptr; /* maybe starting new entry */ | |
229 | if ((locc=rnum())!=oldloc && oldloc>=0 /* finished msg */ | |
230 | && ! (sect==5 && (locc==0 || locc>=100)))/* unless sect 5*/ | |
231 | { switch(sect) /* now put it into right table */ | |
232 | { case 1: /* long descriptions */ | |
233 | ltext[oldloc].seekadr=seekhere; | |
234 | ltext[oldloc].txtlen=maystart-seekstart; | |
235 | break; | |
236 | case 2: /* short descriptions */ | |
237 | stext[oldloc].seekadr=seekhere; | |
238 | stext[oldloc].txtlen=maystart-seekstart; | |
239 | break; | |
240 | case 5: /* object descriptions */ | |
241 | ptext[oldloc].seekadr=seekhere; | |
242 | ptext[oldloc].txtlen=maystart-seekstart; | |
243 | break; | |
244 | case 6: /* random messages */ | |
245 | if (oldloc>RTXSIZ) | |
246 | { printf("Too many random msgs\n"); | |
247 | exit(0); | |
248 | } | |
249 | rtext[oldloc].seekadr=seekhere; | |
250 | rtext[oldloc].txtlen=maystart-seekstart; | |
251 | break; | |
252 | case 10: /* class messages */ | |
253 | ctext[clsses].seekadr=seekhere; | |
254 | ctext[clsses].txtlen=maystart-seekstart; | |
255 | cval[clsses++]=oldloc; | |
256 | break; | |
257 | case 12: /* magic messages */ | |
258 | if (oldloc>MAGSIZ) | |
259 | { printf("Too many magic msgs\n"); | |
260 | exit(0); | |
261 | } | |
262 | mtext[oldloc].seekadr=seekhere; | |
263 | mtext[oldloc].txtlen=maystart-seekstart; | |
264 | break; | |
265 | default: | |
266 | printf("rdesc called with bad section\n"); | |
267 | exit(0); | |
268 | } | |
269 | seekhere += maystart-seekstart; | |
270 | } | |
271 | if (locc<0) | |
272 | { outsw=0; /* turn off output */ | |
273 | seekhere += 3; /* -1<delimiter> */ | |
274 | return; | |
275 | } | |
276 | if (sect!=5 || (locc>0 && locc<100)) | |
277 | { if (oldloc!=locc)/* starting a new message */ | |
278 | seekstart=maystart; | |
279 | oldloc=locc; | |
280 | } | |
281 | FLUSHLF; /* scan the line */ | |
282 | } | |
283 | } | |
284 | ||
285 | ||
286 | rtrav() /* read travel table */ | |
287 | { register int locc; | |
288 | register struct travlist *t; | |
289 | register char *s; | |
290 | char buf[12]; | |
291 | int len,m,n,entries; | |
292 | for (oldloc= -1;;) /* get another line */ | |
293 | { if ((locc=rnum())!=oldloc && oldloc>=0) /* end of entry */ | |
294 | { | |
295 | t->next = 0; /* terminate the old entry */ | |
296 | /* printf("%d:%d entries\n",oldloc,entries); */ | |
297 | /* twrite(oldloc); */ | |
298 | } | |
299 | if (locc== -1) return; | |
300 | if (locc!=oldloc) /* getting a new entry */ | |
301 | { t=travel[locc]=(struct travlist *) malloc(sizeof (struct travlist)); | |
302 | /* printf("New travel list for %d\n",locc); */ | |
303 | entries=0; | |
304 | oldloc=locc; | |
305 | } | |
306 | for (s=buf;; *s++) /* get the newloc number /ASCII */ | |
307 | if ((*s=next())==TAB || *s==LF) break; | |
308 | *s=0; | |
309 | len=length(buf)-1; /* quad long number handling */ | |
310 | /* printf("Newloc: %s (%d chars)\n",buf,len); */ | |
311 | if (len<4) /* no "m" conditions */ | |
312 | { m=0; | |
313 | n=atoi(buf); /* newloc mod 1000 = newloc */ | |
314 | } | |
315 | else /* a long integer */ | |
316 | { n=atoi(buf+len-3); | |
317 | buf[len-3]=0; /* terminate newloc/1000 */ | |
318 | m=atoi(buf); | |
319 | } | |
320 | while (breakch!=LF) /* only do one line at a time */ | |
321 | { if (entries++) t=t->next=(struct travlist *) malloc(sizeof (struct travlist)); | |
322 | t->tverb=rnum();/* get verb from the file */ | |
323 | t->tloc=n; /* table entry mod 1000 */ | |
324 | t->conditions=m;/* table entry / 1000 */ | |
325 | /* printf("entry %d for %d\n",entries,locc); */ | |
326 | } | |
327 | } | |
328 | } | |
329 | ||
330 | ||
331 | twrite(loq) /* travel options from this loc */ | |
332 | int loq; | |
333 | { register struct travlist *t; | |
334 | printf("If"); | |
335 | speak(<ext[loq]); | |
336 | printf("then\n"); | |
337 | for (t=travel[loq]; t!=0; t=t->next) | |
338 | { printf("verb %d takes you to ",t->tverb); | |
339 | if (t->tloc<=300) | |
340 | speak(<ext[t->tloc]); | |
341 | else if (t->tloc<=500) | |
342 | printf("special code %d\n",t->tloc-300); | |
343 | else | |
344 | rspeak(t->tloc-500); | |
345 | printf("under conditions %d\n",t->conditions); | |
346 | } | |
347 | } | |
348 | ||
349 | ||
350 | ||
351 | rvoc() | |
352 | { register char *s; /* read the vocabulary */ | |
353 | register int index; | |
354 | char buf[6]; | |
355 | for (;;) | |
356 | { index=rnum(); | |
357 | if (index<0) break; | |
358 | for (s=buf,*s=0;; s++) /* get the word */ | |
359 | if ((*s=next())==TAB || *s=='\n' || *s==LF | |
360 | || *s==' ') break; | |
361 | /* terminate word with newline, LF, tab, blank */ | |
362 | if (*s!='\n' && *s!=LF) FLUSHLF; /* can be comments */ | |
363 | *s=0; | |
364 | /* printf("\"%s\"=%d\n",buf,index);*/ | |
365 | vocab(buf,-2,index); | |
366 | } | |
367 | /* prht(); */ | |
368 | } | |
369 | ||
370 | ||
371 | rlocs() /* initial object locations */ | |
372 | { for (;;) | |
373 | { if ((obj=rnum())<0) break; | |
374 | plac[obj]=rnum(); /* initial loc for this obj */ | |
375 | if (breakch==TAB) /* there's another entry */ | |
376 | fixd[obj]=rnum(); | |
377 | else fixd[obj]=0; | |
378 | } | |
379 | } | |
380 | ||
381 | rdflt() /* default verb messages */ | |
382 | { for (;;) | |
383 | { if ((verb=rnum())<0) break; | |
384 | actspk[verb]=rnum(); | |
385 | } | |
386 | } | |
387 | ||
388 | rliq() /* liquid assets &c: cond bits */ | |
389 | { register int bitnum; | |
390 | for (;;) /* read new bit list */ | |
391 | { if ((bitnum=rnum())<0) break; | |
392 | for (;;) /* read locs for bits */ | |
393 | { cond[rnum()] |= setbit[bitnum]; | |
394 | if (breakch==LF) break; | |
395 | } | |
396 | } | |
397 | } | |
398 | ||
399 | rhints() | |
400 | { register int hintnum,i; | |
401 | hntmax=0; | |
402 | for (;;) | |
403 | { if ((hintnum=rnum())<0) break; | |
404 | for (i=1; i<5; i++) | |
405 | hints[hintnum][i]=rnum(); | |
406 | if (hintnum>hntmax) hntmax=hintnum; | |
407 | } | |
408 | } | |
409 | ||
410 | ||
411 | rspeak(msg) | |
412 | int msg; | |
413 | { if (msg!=0) speak(&rtext[msg]); | |
414 | } | |
415 | ||
416 | ||
417 | mspeak(msg) | |
418 | int msg; | |
419 | { if (msg!=0) speak(&mtext[msg]); | |
420 | } | |
421 | ||
422 | ||
423 | doseek(offset) /* do 2 seeks to get to right place in the file */ | |
424 | unsigned offset; | |
425 | { | |
426 | extern unsigned filesize; | |
427 | lseek(datfd,(long)offset+(long)filesize, 0); | |
428 | #ifdef notdef | |
429 | blockadr=chadr=0; | |
430 | if (offset<0) /* right place is offset+filesize*/ | |
431 | { blockadr += 64; /* take off 32768 bytes */ | |
432 | chadr += offset+32768; /* & make them into 64 blocks */ | |
433 | } | |
434 | else chadr += offset; | |
435 | if (filesize<0) /* data starts after file */ | |
436 | { blockadr += 64; /* which may also be large */ | |
437 | chadr += filesize+32768; | |
438 | } | |
439 | else chadr += filesize; | |
440 | if (chadr<0) /* and the leftovers may be lge */ | |
441 | { blockadr += 64; | |
442 | chadr += 32768; | |
443 | } | |
444 | seek(datfd,blockadr,3); /* get within 32767 */ | |
445 | seek(datfd,chadr,1); /* then the rest of the way */ | |
446 | #endif | |
447 | } | |
448 | ||
449 | ||
450 | speak(msg) /* read, decrypt, and print a message (not ptext) */ | |
451 | struct text *msg;/* msg is a pointer to seek address and length of mess */ | |
452 | { register char *s,nonfirst; | |
453 | register char *tbuf; | |
454 | doseek(msg->seekadr); | |
7d2e4f2d | 455 | if ((tbuf=(char *) malloc(msg->txtlen+1)) == 0) bug(109); |
f2b5c7ab RH |
456 | read(datfd,tbuf,msg->txtlen); |
457 | s=tbuf; | |
458 | nonfirst=0; | |
459 | while (s-tbuf<msg->txtlen) /* read a line at a time */ | |
460 | { tape=iotape; /* restart decryption tape */ | |
461 | while ((*s++^*tape++)!=TAB); /* read past loc num */ | |
462 | /* assume tape is longer than location number */ | |
463 | /* plus the lookahead put together */ | |
464 | if ((*s^*tape)=='>' && | |
465 | (*(s+1)^*(tape+1))=='$' && | |
466 | (*(s+2)^*(tape+2))=='<') break; | |
467 | if (blklin&&!nonfirst++) putchar('\n'); | |
468 | do | |
469 | { if (*tape==0) tape=iotape;/* rewind decryp tape */ | |
470 | putchar(*s^*tape); | |
471 | } while ((*s++^*tape++)!=LF); /* better end with LF */ | |
472 | } | |
473 | free(tbuf); | |
474 | } | |
475 | ||
476 | ||
477 | pspeak(msg,skip) /* read, decrypt an print a ptext message */ | |
478 | int msg; /* msg is the number of all the p msgs for this place */ | |
479 | int skip; /* assumes object 1 doesn't have prop 1, obj 2 no prop 2 &c*/ | |
480 | { register char *s,nonfirst; | |
481 | register char *tbuf; | |
482 | char *numst; | |
483 | int lstr; | |
484 | doseek(ptext[msg].seekadr); | |
7d2e4f2d | 485 | if ((tbuf=(char *) malloc((lstr=ptext[msg].txtlen)+1)) == 0) bug(108); |
f2b5c7ab RH |
486 | read(datfd,tbuf,lstr); |
487 | s=tbuf; | |
488 | nonfirst=0; | |
489 | while (s-tbuf<lstr) /* read a line at a time */ | |
490 | { tape=iotape; /* restart decryption tape */ | |
491 | for (numst=s; (*s^= *tape++)!=TAB; s++); /* get number */ | |
492 | *s++=0; /* decrypting number within the string */ | |
493 | if (atoi(numst)!=100*skip && skip>=0) | |
494 | { while ((*s++^*tape++)!=LF) /* flush the line */ | |
495 | if (*tape==0) tape=iotape; | |
496 | continue; | |
497 | } | |
498 | if ((*s^*tape)=='>' && (*(s+1)^*(tape+1))=='$' && | |
499 | (*(s+2)^*(tape+2))=='<') break; | |
500 | if (blklin && ! nonfirst++) putchar('\n'); | |
501 | do | |
502 | { if (*tape==0) tape=iotape; | |
503 | putchar(*s^*tape); | |
504 | } while ((*s++^*tape++)!=LF); /* better end with LF */ | |
505 | if (skip<0) break; | |
506 | } | |
507 | free(tbuf); | |
508 | } | |
509 |