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