1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/agt/agility.h"
24
25 namespace Glk {
26 namespace AGT {
27
28 static void read_da6(fc_type fc);
29 static void check_cmd_version(void);
30 static void build_cmd_table(void);
31 static void fixcmd(integer *, int);
32
33 /* This parses the block of integers of a command to standardize
34 the command numbers */
35 static short *cmd_table; /* Holds the command translation table used
36 by fixcmd */
37 int topcmd; /* The highest legal opcode in the current AGT version. */
38
39
40
41 static genfile fd_desc; /* File pointer for description file. */
42 static long desc_size; /* Size of description file. */
43
44 static int top_quest; /* Highest question actually referenced */
45 /* This is computed by fixcmd */
46
47 static int SL_NAME, SL_TEXT, SL_ROOM, SL_WORD;
48 static integer MAX_CMD_SIZE;
49
50 static rbool encrypt_desc = 1; /* Are descriptions encrypted? */
51
52
53 /* This translates v1.8 status mode codes into ME statue mode codes */
54 const uchar agt18_statmode[] = {0, 4, 3, 1};
55
56
57 /*-------------------------------------------------------------------------*/
58 /* Utilities to convert strings, do ASCII translation, etc. and to add */
59 /* words to the dictionary (the actual dictionary routines are in */
60 /* agtdata.c; these routines here are just wrappers that prepare words */
61 /* to be added). */
62 /*-------------------------------------------------------------------------*/
63
64
65
fatals(const char * msg,const char * fname)66 static void fatals(const char *msg, const char *fname) {
67 Common::String str = Common::String::format(msg, fname);
68 error("%s", str.c_str());
69 }
70
71
add_dic1(uchar * buff,int n)72 static word add_dic1(uchar *buff, int n) {
73 char nbuff[100];
74
75 int i;
76 if (n > 100) n = 100;
77 for (i = 0; i < buff[0] && i < n; i++)
78 nbuff[i] = buff[i + 1];
79 nbuff[i] = 0;
80 return add_dict(nbuff);
81 }
82
83
add_slist(uchar * buff)84 static slist add_slist(uchar *buff) {
85 int j, k;
86 slist start_ptr;
87 char nbuff[100];
88
89 k = 0;
90 start_ptr = synptr;
91 if (buff[0] > 80) fatal("Invalid game file format");
92 for (j = 1; j <= buff[0]; j++)
93 if (rspace(buff[j]) && k > 0) {
94 nbuff[k] = 0;
95 addsyn(add_dict(nbuff));
96 k = 0;
97 } else nbuff[k++] = buff[j];
98 if (k > 0) {
99 nbuff[k] = 0;
100 addsyn(add_dict(nbuff));
101 }
102 addsyn(-1); /* End of list marker */
103 return start_ptr;
104 }
105
106
107
108
109 /*-------------------------------------------------------------------------*/
110 /* Description file manipulation routines: routines to open and close the */
111 /* description file and read in individual descriptions. */
112 /*-------------------------------------------------------------------------*/
113
114 /* The memory-based stuff is not done yet */
115
116
convert_agt_descr(uchar * s)117 void convert_agt_descr(uchar *s)
118 /* Convert encrypted pascal string into plaintext C string */
119 {
120 int j, n;
121
122 n = s[0];
123 if (n > 80) {
124 s[0] = 0;
125 return;
126 }
127
128 if (encrypt_desc)
129 for (j = 0; j < n; j++)
130 if (s[j + 1] != ' ')
131 s[j] = fixchar[(s[j + 1] - (j + 1) + 0x100) & 0xFF];
132 else s[j] = ' ';
133 else
134 for (j = 0; j < n; j++)
135 s[j] = fixchar[s[j + 1]];
136
137 s[n] = 0;
138 }
139
140
open_descr(fc_type fc)141 void open_descr(fc_type fc) {
142 const char *errstr;
143 long i;
144 int alpha, cnt;
145 tline buff;
146
147 fd_desc = readopen(fc, fDSS, &errstr);
148 if (errstr != NULL) fatal(errstr);
149 desc_size = binsize(fd_desc);
150 if (DIAG) {
151 char *s;
152 s = formal_name(fc, fDSS);
153 rprintf("Opened file %s (size:%ld)\n", s, desc_size);
154 rfree(s);
155 }
156
157 /* <Sigh> Now need to figure out if the input is encoded. Do this by
158 reading a few random lines and seeing if they "look" encoded */
159 alpha = cnt = 0;
160 if (aver > AGT135 || aver == AGTCOS)
161 encrypt_desc = 1;
162 else {
163 binread(fd_desc, buff, 81, 1, &errstr); /* Throw away first line */
164 while (cnt < 300) {
165 if (!binread(fd_desc, buff, 81, 1, &errstr)) { /* EOF */
166 writeln("");
167 agtwarn("EOF while analyzing descriptions", 0);
168 rprintf("......assuming type ");
169 break;
170 }
171 if (buff[0] > 0 && buff[1] != ' ' && buff[1] > 0) /* To avoid "banner"
172 lines */
173 {
174 for (i = 1; i <= buff[0]; i++) {
175 if (buff[i] >= 'A' && buff[i] <= 'z') alpha++;
176 if (buff[i] != ' ') cnt++;
177 }
178 }
179 }
180 if (3 * cnt < 4 * alpha) {
181 encrypt_desc = 0;
182 if (aver == AGT135) aver = AGT12;
183 } else encrypt_desc = 1;
184 }
185 if (DIAG) {
186 if (encrypt_desc) rprintf(" [encrypted]\n");
187 else rprintf(" [plaintext: %d/%d]\n", alpha, cnt);
188 }
189
190 mem_descr = NULL;
191 if (desc_size <= descr_maxmem) {
192 /* This is where we need to read the data in and convert it:
193 encrypted Pascal strings --> plaintext C strings */
194 binseek(fd_desc, 0);
195 mem_descr = (char *)rmalloc(desc_size);
196 /* Read in the whole file */
197 binread(fd_desc, mem_descr, desc_size, 1, &errstr);
198 if (errstr != NULL) fatal(errstr);
199 for (i = 0; i < desc_size; i += sizeof(tline))
200 convert_agt_descr((uchar *)(mem_descr + i));
201 /* Decode and convert to C string */
202 }
203 }
204
205
close_descr(void)206 void close_descr(void) {
207 if (mem_descr != NULL)
208 rfree(mem_descr);
209 else {
210 readclose(fd_desc);
211 fd_desc = NULL;
212 }
213 }
214
215
agt_read_descr(long start,long len)216 descr_line *agt_read_descr(long start, long len) {
217 tline *d;
218 descr_line *lines;
219 long i;
220 const char *errstr;
221
222 if (len == -1 || start == -1) return NULL;
223 lines = (descr_line *)rmalloc(sizeof(descr_line) * (len + 1));
224
225 if (mem_descr != NULL) {
226 d = ((tline *)mem_descr) + start;
227 for (i = 0; i < len; i++)
228 lines[i] = (char *)(d + i);
229 } else {
230 d = (tline *)rmalloc(sizeof(tline) * len);
231 binseek(fd_desc, start * sizeof(tline));
232 binread(fd_desc, d, sizeof(tline), len, &errstr);
233 if (errstr != NULL) fatal(errstr);
234 for (i = 0; i < len; i++) {
235 lines[i] = (char *)(d + i);
236 convert_agt_descr((uchar *)(d + i));
237 }
238 }
239 lines[len] = NULL; /* Mark end of array */
240 return lines;
241 }
242
243
244
245 /*-------------------------------------------------------------------------*/
246 /* Read DA2: The Room File. */
247 /*-------------------------------------------------------------------------*/
248
249
250 #define seti(a) (room[i].a=buff[bp] | (buff[bp+1]<<8),bp+=2)
251 #define set32(a) (room[i].a=buff[bp] | (buff[bp+1]<<8) | (buff[bp+2]<<16)|\
252 (buff[bp+3]<<24), bp+=4)
253 #define setb(a) (room[i].a=buff[bp],bp++)
254
255 #define setstr(leng) (bp+=(leng),new_str((char*)buff+bp-(leng),(leng),1))
256 #define setd(leng) (bp+=(leng),add_dic1(buff+bp-(leng),(leng)))
257 #define setsl() (bp+=sizeof(tline),add_slist(buff+bp-sizeof(tline)))
258 #define nonecheck(leng) (memcmp(buff+bp,nonestr,5)==0)
259
260
read_da2(fc_type fc)261 static void read_da2(fc_type fc) {
262 int i, j, numroom;
263 uchar *buff; /* [FRS_ROOM];*/
264 long bp;
265
266 numroom = maxroom - first_room + 1;
267 if (numroom < 0) return;
268 room_name = (long *)rmalloc(numroom * sizeof(long));
269
270 buffopen(fc, fDA2, FRS_ROOM, "room", numroom);
271
272 bp = 0;
273 for (i = 0; i < numroom; i++) {
274 buff = buffread(i);
275 bp = 0;
276 if (nonecheck(SL_ROOM))
277 room[i].unused = 1;
278 else room[i].unused = 0;
279 room_name[i] = setstr(SL_ROOM);
280 room[i].replace_word = setd(SL_WORD);
281 room[i].replacing_word = setsl();
282 for (j = 0; j < 12; j++) seti(path[j]);
283
284 if (aver >= AGT15) set32(flag_noun_bits); /* Menu flags */
285 else room[i].flag_noun_bits = 0;
286
287 if (aver >= AGTME10) set32(PIX_bits); /* PIX bits */
288 else room[i].PIX_bits = 0;
289
290 seti(path[12]); /* Special */
291
292 /* There's a small possibility that the 1.5/Hotel flag_noun_bits
293 goes here, rather than above; 1.5/F is known to go above */
294
295 setb(seen);
296 seti(key);
297 setb(locked_door);
298 if (room_inside != NULL)
299 room_inside[i] = fixsign16(buff[bp], buff[bp + 1]);
300 bp += 2; /* Skip # of nouns in this room */
301
302 seti(points);
303 seti(light);
304 setb(end);
305 setb(win);
306 if (aver != AGT10) setb(killplayer); /* I'm guessing here */
307 else room[i].killplayer = room[i].end;
308
309 if (aver >= AGTME10) {
310 seti(initdesc);
311 seti(pict);
312 } else {
313 room[i].initdesc = 0;
314 room[i].pict = 0;
315 }
316 if (aver >= AGTME15) room[i].autoverb = setd(SL_WORD);
317 else room[i].autoverb = 0;
318 room[i].oclass = 0;
319 room[i].seen = 0;
320 }
321 if (DIAG)
322 rprintf(" Internal:%ld\n", bp);
323 buffclose();
324 }
325
326
327
328 /*-------------------------------------------------------------------------*/
329 /* Read DA3: The Noun File. */
330 /*-------------------------------------------------------------------------*/
331
332 #undef seti
333 #undef setb
334 #define seti(a) (noun[i].a=buff[bp] | (buff[bp+1]<<8),bp+=2)
335 #define setb(a) (noun[i].a=buff[bp],bp++)
336
read_da3(fc_type fc)337 static void read_da3(fc_type fc) {
338 int i, numnoun;
339 long recsize;
340 uchar *buff; /* [FRS_NOUN];*/
341 long bp;
342
343 numnoun = maxnoun - first_noun + 1;
344 if (numnoun < 0) return;
345 noun_sdesc = (long *)rmalloc(numnoun * sizeof(long));
346 noun_pos = (long *)rmalloc(numnoun * sizeof(long));
347
348 recsize = buffopen(fc, fDA3, FRS_NOUN, "noun", numnoun);
349 if (aver == AGT15 && recsize > 263) aver = AGT15F;
350
351 bp = 0;
352 for (i = 0; i < numnoun; i++) {
353 buff = buffread(i);
354 bp = 0;
355 if (nonecheck(SL_NAME)) {
356 bp += SL_NAME;
357 noun[i].name = 0;
358 noun[i].unused = 1;
359 } else {
360 noun[i].name = setd(SL_NAME);
361 noun[i].unused = 0;
362 }
363 noun_sdesc[i] = setstr(SL_TEXT);
364 noun[i].adj = setd(SL_NAME);
365
366 if (aver >= AGT15F) seti(initdesc);
367 else noun[i].initdesc = 0;
368
369 setb(plural);
370 /* The following is a guess for ME games */
371 noun_pos[i] = setstr((ver == 3) ? SL_ROOM : SL_NAME);
372 setb(something_pos_near_noun); /* These may not be valid */
373 seti(nearby_noun); /* in masters ed. */
374
375 setb(has_syns);
376 noun[i].syns = setsl(); /*,SL_TEXT);*/
377 if (aver >= AGT15)
378 noun[i].related_name = setd(SL_NAME);
379 else
380 noun[i].related_name = 0;
381 seti(location);
382 seti(weight);
383 seti(size);
384 seti(key);
385 /* All of following flags known to be valid except
386 pullable, on, and win */
387 setb(pushable);
388 setb(pullable);
389 setb(turnable);
390 setb(playable);
391 setb(readable);
392 setb(on);
393 setb(closable);
394 setb(open);
395 setb(lockable);
396 setb(locked);
397 setb(edible);
398 setb(wearable);
399 setb(drinkable);
400 setb(poisonous);
401 setb(movable);
402 setb(light);
403 setb(shootable);
404 seti(num_shots);
405 seti(points);
406 if (noun_inside != NULL)
407 noun_inside[i] = fixsign16(buff[bp], buff[bp + 1]);
408 bp += 2; /* Skip # of nouns contained in this one */
409 setb(win);
410 if (ver == 3) seti(pict);
411 else noun[i].pict = 0;
412 noun[i].oclass = 0; /* AGT games don't support classes */
413 noun[i].isglobal = 0;
414 noun[i].flagnum = 0;
415 noun[i].seen = 0;
416 noun[i].proper = 0;
417 }
418 if (DIAG)
419 rprintf(" Internal:%ld\n", bp);
420 buffclose();
421 }
422
423
424 #undef seti
425 #undef setb
426 #define seti(a) (creature[i].a=buff[bp] | (buff[bp+1]<<8),bp+=2)
427 #define setb(a) (creature[i].a=buff[bp],bp++)
428
429
430
431 /*-------------------------------------------------------------------------*/
432 /* Read DA4: The Creature File. */
433 /*-------------------------------------------------------------------------*/
434
read_da4(fc_type fc)435 static void read_da4(fc_type fc) {
436 int i, numcreat;
437 uchar *buff; /* [FRS_CREAT];*/
438 long bp;
439
440 numcreat = maxcreat - first_creat + 1;
441 if (numcreat <= 0) return;
442 creat_sdesc = (long *)rmalloc(numcreat * sizeof(long));
443
444 buffopen(fc, fDA4, FRS_CREAT, "creature", numcreat);
445
446 bp = 0;
447 for (i = 0; i < numcreat; i++) {
448 buff = buffread(i);
449 bp = 0;
450 if (nonecheck(SL_NAME)) {
451 bp += SL_NAME;
452 creature[i].name = 0;
453 creature[i].unused = 1;
454 } else {
455 creature[i].name = setd(SL_NAME);
456 creature[i].unused = 0;
457 }
458 creat_sdesc[i] = setstr(SL_TEXT);
459 creature[i].adj = setd(SL_NAME);
460 if (ver == 3) seti(initdesc);
461 else creature[i].initdesc = 0;
462 setb(has_syns);
463 creature[i].syns = setsl();
464 setb(groupmemb);
465 seti(location);
466 seti(weapon);
467 setb(hostile);
468 seti(points);
469 if (creat_inside != NULL)
470 creat_inside[i] = fixsign16(buff[bp], buff[bp + 1]);
471 bp += 2; /* Skip # of nouns the creature is carrying */
472 seti(counter);
473 seti(threshold);
474 seti(timethresh);
475 seti(timecounter);
476 setb(gender);
477 if (ver == 3) seti(pict);
478 else creature[i].pict = 0;
479 creature[i].oclass = 0; /* AGT games don't support classes */
480 creature[i].isglobal = 0;
481 creature[i].flagnum = 0;
482 creature[i].seen = 0;
483 creature[i].proper = 0;
484 }
485 if (DIAG)
486 rprintf(" Internal:%ld\n", bp);
487 buffclose();
488 }
489
490 #undef seti
491 #undef setb
492
493
494
495
496 /*-------------------------------------------------------------------------*/
497 /* Read Commands (DA5 and DA6) and convert them to a uniform internal */
498 /* format. */
499 /*-------------------------------------------------------------------------*/
500
501
translate_vnum(int vnum)502 static int translate_vnum(int vnum)
503 /* actor is a numerical index occuring at the beginning of each command.
504 In general, it contains the verb number of the verb associated with
505 this command; because of AGiliTy's dictionary organization, we don't
506 really need this (the verb itself will be converted to a number anyhow),
507 but the field contains other useful information as well:
508 i)If this command header is really the object of a redirection command,
509 then the actor will have 1000 or 2000 added to it, depending on
510 AGT version.
511 ii)If this command is directed at an actor, then the creature number
512 will be in this field instead of the verb number.
513 Commands directed to ANYBODY have one plus the maximum verb number
514 in this field (a hassle since the maximum verb number depends on
515 AGT version: Classic:106, Master's:123); EVERYONE is the next
516 code after ANYBODY.
517 What this routine does is rationalize the differences between AGT versions.
518 --Verb values (ie. not referring to creatures) are converted to 1.
519 --Redirections are marked by multiplying by negative one and setting
520 cmdsize to 0.
521 --ANYBODY is set to 2
522 --EVERYBODY is set to 3
523 */
524 {
525 rbool redir; /* Is this command redirected? */
526 integer anycode;
527 int redir_val;
528
529 /* Earlier games use 1000 as redirect value, later games use 2000: */
530 /* We strip it off, but remember whether it was there or not so we
531 can restore this information later. */
532 redir_val = (aver <= AGT18MAX ? 1000 : 2000);
533 if (vnum >= redir_val) {
534 vnum = vnum % redir_val;
535 redir = 1;
536 } else redir = 0;
537
538 anycode = (aver <= AGT18MAX) ? 106 : 123;
539
540 /* Now to correct ANYBODY to something consistent and set verb
541 references to 1 since we don't need them and they just confuse things */
542 if (vnum < anycode) vnum = 1; /* "player" */
543 else if (vnum == anycode) vnum = 2; /* ANYBODY */
544 else if (vnum == anycode + 1) vnum = 3; /* EVERYBODY */
545
546 /* Finally restore redirection info. We now use the sign of vnum
547 to indicate this.*/
548 if (redir) vnum = -vnum;
549
550 return vnum;
551 }
552
553
554 #define CREC_SIZE (FRS_CMD)
555
556 static long badtokcnt;
557
read_da5(fc_type fc)558 static void read_da5(fc_type fc) {
559 long i, j;
560 uchar *buff; /* [CREC_SIZE];*/
561 long bp;
562
563 if (!have_meta) return;
564 if (last_cmd <= 0)
565 fatal("Bogus last_cmd");
566
567 buffopen(fc, fDA5, CREC_SIZE, "command", last_cmd);
568
569 if (aver >= AGT15F) cmd_ptr = (long *)rmalloc(sizeof(long) * last_cmd);
570 else cmd_ptr = NULL;
571
572 bp = 0;
573 for (i = 0; i < last_cmd; i++) {
574 buff = buffread(i);
575 command[i].actor = translate_vnum(buff[0] + (buff[1] << 8));
576 bp = 2;
577 command[i].verbcmd = setd(SL_WORD);
578 command[i].nouncmd = setd(SL_WORD);
579 if (aver >= AGTME10)
580 command[i].prep = setd(SL_WORD);
581 else command[i].prep = 0;
582 command[i].objcmd = setd(SL_WORD);
583 command[i].noun_adj = command[i].obj_adj = 0;
584 command[i].noun_obj = command[i].obj_obj = 0;
585 if (aver < AGT15F) {
586 command[i].data = (integer *)rmalloc(MAX_CMD_SIZE * sizeof(integer));
587 for (j = 0; j < MAX_CMD_SIZE; j++)
588 command[i].data[j] = fixsign16(buff[bp + 2 * j], buff[bp + 2 * j + 1]);
589 bp += 2 * MAX_CMD_SIZE;
590 command[i].cmdsize = MAX_CMD_SIZE;
591 } else {
592 cmd_ptr[i] = (long)buff[bp] + (((long)buff[bp + 1]) << 8);
593 bp += 2;
594 }
595 }
596 if (DIAG)
597 rprintf(" Internal:%ld\n", bp);
598 buffclose();
599
600 /* Now to read in DA6 for versions that have it */
601 if (aver >= AGT15F) read_da6(fc);
602 check_cmd_version(); /* This uses the opcodes to check gamefile
603 version information and change it if neccesary. */
604 build_cmd_table(); /* Create the command translation table for
605 this version of AGT. */
606
607 badtokcnt = 0;
608 if (!RAW_CMD_OUT)
609 for (i = 0; i < last_cmd; i++)
610 fixcmd(command[i].data, command[i].cmdsize);
611 rfree(cmd_table);
612 if (badtokcnt > MAX_BADTOK)
613 agtnwarn("Total number of bad opcodes:", badtokcnt, 1);
614 }
615
616
617
read_da6(fc_type fc)618 static void read_da6(fc_type fc)
619 /* This will only be called for versions with a DA6 file--
620 i.e. Master's Edition and proto-ME games. */
621 {
622 genfile fda6;
623 char *fullname;
624 const char *errstr;
625 long fsize; /* Size of the file */
626 long frame; /* The first element of the file that is in the buffer. */
627 uchar *cbuf; /* Buffer */
628 long cfile_size, cbuf_size; /* Number of tokens in file and in buffer */
629 long i, j;
630 long cmdstart, cmdend; /* Marks the start and end of the current command */
631 long ip; /* Points to instruction in cmd.data[] that we are writing to */
632 long bp; /* Pointer into buffer */
633 long endp; /* Used to indicate end of current read loop
634 (with an infinite buffer, this would always be an adjusted
635 cmdend) */
636 long adj_cbuf_size; /* Stores number of bytes actually read in to cbuf */
637
638 fda6 = openbin(fc, fDA6, "Could not open code file '%s'.", 1);
639 fsize = binsize(fda6);
640 fullname = formal_name(fc, fDA6);
641 if (DIAG) rprintf("Reading code file %s (size:%ld)\n", fullname, fsize);
642
643 if (aver == AGT15F && fsize == 20000) aver = AGT16;
644 if (aver >= AGTME10) cfile_size = 20000;
645 else if (aver == AGT16) cfile_size = 10000;
646 else cfile_size = 5000;
647
648 if (fsize != 2 * cfile_size)
649 fatals("Code file %s is the wrong size.", fullname);
650
651 cbuf_size = (cfile_size < CBUF_SIZE) ? cfile_size : CBUF_SIZE;
652 cbuf = (uchar *)rmalloc(2 * cbuf_size);
653 frame = cfile_size + 1; /* Guarentee frame will be wrong */
654
655 for (i = 0; i < last_cmd; i++)
656 if (cmd_ptr[i] >= 2) {
657 for (j = i + 1; j < last_cmd && cmd_ptr[j] <= cmd_ptr[i]; j++);
658 if (j < last_cmd) cmdend = cmd_ptr[j];
659 else cmdend = cfile_size;
660 if (cmdend > cfile_size) fatals("Code file overrun(%s)", fullname);
661 --cmdend;
662 cmdstart = cmd_ptr[i] - 1;
663 command[i].cmdsize = cmdend - cmdstart;
664 command[i].data = (integer *)rmalloc(command[i].cmdsize * sizeof(integer));
665
666 ip = 0;
667 bp = cmdstart - frame;
668 adj_cbuf_size = cbuf_size;
669
670 while (ip < command[i].cmdsize) {
671 if (bp < 0 || bp >= adj_cbuf_size) { /* Read in new block */
672 frame = frame + bp;
673 binseek(fda6, frame * 2);
674 if (frame + cbuf_size <= cfile_size)
675 adj_cbuf_size = cbuf_size;
676 else
677 adj_cbuf_size = cfile_size - frame;
678 if (adj_cbuf_size <= 0) fatal("Unexpected end of file.");
679 if (!binread(fda6, cbuf, 2, adj_cbuf_size, &errstr))
680 fatal(errstr);
681 bp = 0;
682 }
683 endp = cmdend - frame;
684 if (endp > cbuf_size) endp = cbuf_size;
685 for (; bp < endp; ip++, bp++)
686 command[i].data[ip] = fixsign16(cbuf[bp * 2L], cbuf[bp * 2L + 1]);
687 }
688 } else {
689 command[i].data = NULL;
690 command[i].cmdsize = 0;
691 }
692 rfree(cbuf);
693 readclose(fda6);
694 rfree(fullname);
695 }
696
697
698
699
700
check_endcmd(void)701 static int check_endcmd(void)
702 /* What is the most common last byte for metacommands? Except
703 under very abnormal situations, this should be the EndCmd opcode */
704 {
705 int count[MAX_TOKEN_ID + 1];
706 int i, tok, maxcnt, maxtok;
707 /* int nextcnt; */
708
709 for (i = 0; i <= MAX_TOKEN_ID; i++) count[i] = 0;
710 for (i = 0; i < last_cmd; i++)
711 if (command[i].cmdsize > 0) {
712 tok = command[i].data[command[i].cmdsize - 1];
713 if (tok >= 0 && tok <= MAX_TOKEN_ID) count[tok]++;
714 }
715 maxcnt = maxtok = 0; /* nextcnt=0;*/
716 for (i = 0; i <= MAX_TOKEN_ID; i++)
717 if (count[i] >= maxcnt) {
718 /* nextcnt=maxcnt; */
719 maxcnt = count[i];
720 maxtok = i;
721 }
722 return maxtok;
723 }
724
compute_endcode(int ver_)725 static int compute_endcode(int ver_)
726 /* Computes the correct endcode for a given gamefile version */
727 {
728 int i;
729
730 for (i = 0; FIX_LIST[ver_][i].tnew != -1; i++);
731 return (FIX_LIST[ver_][i].told - 3); /* -3 to get to EndCmd */
732 }
733
734
check_cmd_version(void)735 static void check_cmd_version(void)
736 /* Run through the commands looking at the last legal byte. This is
737 normally the EndCmd token code, which can give us info on which
738 token encoding scheme is being used. */
739 {
740 int endcode;
741
742 endcode = check_endcmd();
743 if (DIAG) rprintf(" (EndCmd=%d)\n", endcode);
744 if (endcode < 150) return; /* No metacommands, or something else is wrong. */
745 if (endcode == compute_endcode(aver)) return; /* We're okay */
746
747 /* Check for the special cases we know about */
748 if (aver == AGT135) {
749 if (endcode == compute_endcode(AGT182)) {
750 aver = AGT182;
751 return;
752 } else if (endcode == compute_endcode(AGT118)) {
753 aver = AGT118;
754 return;
755 }
756 }
757 if (aver == AGTME10)
758 if (endcode == compute_endcode(AGTME10A)) {
759 aver = AGTME10A;
760 return;
761 }
762 if (aver == AGTMAST)
763 if (endcode == compute_endcode(AGTME155)) {
764 aver = AGTME155;
765 return;
766 }
767
768 /* If we still haven't fixed the problem, print out a warning and
769 pray. */
770 agtnwarn("Game has invalid EndCmd: ", endcode, 1);
771 }
772
773
774
build_cmd_table(void)775 static void build_cmd_table(void) {
776 int told, tnew, fp;
777 const cmd_fix_rec *fixtbl;
778
779 topcmd = compute_endcode(aver) + 3;
780 cmd_table = (short *)rmalloc(sizeof(short) * topcmd);
781
782 fixtbl = FIX_LIST[aver];
783 fp = 0; /* Pointer into fix table */
784 tnew = 0; /* This shouldn't be neccessary */
785 for (told = 0; told < topcmd;) {
786 if (told == fixtbl[fp].told) tnew = fixtbl[fp++].tnew;
787 cmd_table[told++] = tnew++;
788 }
789 }
790
791
792
badtokerr(const char * s,int tok)793 static void badtokerr(const char *s, int tok) {
794 if (++badtokcnt <= MAX_BADTOK) agtnwarn(s, tok, 1);
795 }
796
fixcmd(integer * clist,int cnt)797 static void fixcmd(integer *clist, int cnt)
798 /* Okay, we need to go through the elements of clist (which is an array,
799 actually), figure out which ones are commands (as opposed to arguments)
800 and tweak them to hide version differences. */
801 {
802 long ip;
803
804 /* Now need to go through and adjust opcodes. */
805 for (ip = 0; ip < cnt; ip++)
806 if (clist[ip] >= topcmd || clist[ip] < 0)
807 badtokerr("Invalid token found: ", clist[ip]);
808 else {
809
810 clist[ip] = cmd_table[clist[ip]]; /* Translate */
811
812 /* Now increment ip by the length of the instruction */
813 /* Remember that we are already incrementing by one automatically */
814
815 if (clist[ip] >= END_ACT) break; /* CMD end marker */
816 if (clist[ip] <= MAX_COND)
817 ip += cond_def[clist[ip]].argnum;
818 else if (clist[ip] < WIN_ACT) {
819 if (clist[ip] == 1087 && ip + 1 < cnt) /* AskQuestion: Adjust top_quest */
820 if (top_quest < clist[ip + 1]) top_quest = clist[ip + 1];
821 ip += act_def[clist[ip] - START_ACT].argnum;
822 }
823 /* else do nothing */
824 }
825 }
826
827
828
829
830
831 /*-------------------------------------------------------------------------*/
832 /* DA1 Reading Utilites: routines to read the various lines of the DA1 file */
833 /*-------------------------------------------------------------------------*/
834
chop_newline(char * s)835 static void chop_newline(char *s)
836 /* Remove trailing \r,\n, etc. characters */
837 {
838 char *t;
839
840 for (t = s; *t != 0; t++); /* Find the end of the string */
841 for (; t >= s && (*t == 0 || *t == '\r' || *t == '\n'); t--);
842 *(t + 1) = 0;
843 }
844
fix_answer(char * s)845 static void fix_answer(char *s)
846 /* Put answer s into standard format: all lower case and with trailing/
847 following whitespace removed */
848 {
849 char *t, *p;
850
851 for (t = s; *t != 0; t++)
852 *t = tolower(*t);
853 /* Eliminate trailing space and newlines */
854 for (; t >= s && (*t == 0 || rspace(*t)); t--);
855 *(t + 1) = 0;
856 /* Eliminate leading space and newlines */
857 for (t = s; rspace(*t); t++);
858 if (t != s) {
859 for (p = s; *t != 0;)
860 *(p++) = *(t++);
861 *p = 0;
862 }
863 }
864
865
866 static char linebuffer[81];
867 static int bhold;
868 static int linenum;
869 static rbool unexpected_eof;
870
read_line(genfile fd,const char * typestr)871 static void read_line(genfile fd, const char *typestr)
872 /* Read a line into buffer, unless bhold=1 in which case we want
873 to use the last line read */
874 {
875 if (bhold == 0) {
876 readln(fd, linebuffer, 80);
877 if (linebuffer[0] == 0 && texteof(fd)) {
878 unexpected_eof = 1;
879 strcpy(linebuffer, ">End Of File<");
880 } else chop_newline(linebuffer);
881 linenum++;
882 }
883 if (debug_da1 && typestr != NULL) {
884 rprintf("%s %4d:%s", typestr, linenum, linebuffer);
885 if (bhold) rprintf(" *");
886 writeln("");
887 }
888 bhold = 0;
889 }
890
891
report(const char * s,genfile fd)892 static void report(const char *s, genfile fd) {
893 if (DIAG) rprintf("REPORT:%s at %d\n", s, linenum);
894 }
895
isbool(genfile fd)896 static int isbool(genfile fd) {
897 read_line(fd, NULL);
898 bhold = 1;
899 return (strncasecmp(linebuffer, "TRUE", 4) == 0 ||
900 strncasecmp(linebuffer, "FALSE", 5) == 0);
901 }
902
isnum(genfile fd)903 static int isnum(genfile fd) {
904 char *errstr;
905
906 read_line(fd, NULL);
907 bhold = 1;
908 (void)strtol(linebuffer, &errstr, 10);
909 while (*errstr == '\n' || *errstr == '\r') errstr++;
910 if (debug_da1)
911 rprintf("NUMCHK: %s==>%c\n", linebuffer, *errstr);
912 return (*errstr == '\0');
913 }
914
readrbool(genfile fd)915 static rbool readrbool(genfile fd) {
916 read_line(fd, "BOOL");
917 return (strncasecmp(linebuffer, "TRUE", 4) == 0);
918 }
919
920
readnum(genfile fd)921 static long readnum(genfile fd) {
922 read_line(fd, "NUM ");
923 return strtol(linebuffer, NULL, 10);
924 }
925
926
readptr(genfile fd,descr_ptr * desc)927 static void readptr(genfile fd, descr_ptr *desc) {
928 read_line(fd, "PTR ");
929 desc->start = strtol(linebuffer, NULL, 10);
930 read_line(fd, "LEN");
931 desc->size = strtol(linebuffer, NULL, 10);
932 }
933
934
readjunk(genfile fd)935 static void readjunk(genfile fd) {
936 read_line(fd, "JUNK");
937 }
938
readtext(genfile fd,tline s)939 static void readtext(genfile fd, tline s) {
940 read_line(fd, "TEXT");
941 strncpy((char *)s, linebuffer, 80);
942 s[80] = 0;
943 }
944
readfname(genfile fd)945 static long readfname(genfile fd) {
946 read_line(fd, "FILE");
947 return new_str(linebuffer, 0, 0);
948 /* Copy filename string to static string space and return index */
949 }
950
readdict(genfile fd)951 static word readdict(genfile fd) {
952 read_line(fd, "DICT");
953 return add_dict(linebuffer);
954 }
955
956
readslist(genfile fd)957 static slist readslist(genfile fd) { /* Read in synonym list line */
958 slist start_ptr;
959 char nbuff[50];
960 int j, k;
961
962 start_ptr = synptr;
963 read_line(fd, "SYN ");
964 /* Need to see if it is none or * terminated. */
965 for (j = 0; linebuffer[j] != 0 && linebuffer[j] != '*'; j++);
966 linebuffer[j] = 0;
967 k = 0;
968 for (j = 0; linebuffer[j] != 0; j++)
969 if (rspace(linebuffer[j]) && k > 0) {
970 nbuff[k] = 0;
971 addsyn(add_dict(nbuff));
972 k = 0;
973 } else if (!rspace(linebuffer[j]))
974 nbuff[k++] = linebuffer[j];
975 if (k > 0) {
976 nbuff[k] = 0;
977 addsyn(add_dict(nbuff));
978 }
979 addsyn(-1);
980 return start_ptr;
981 }
982
983
984
985 /*-------------------------------------------------------------------------*/
986 /* Version analysis: Utilities to analyse the file format version and */
987 /* deduce sizes. */
988 /*-------------------------------------------------------------------------*/
989
soggy_test(fc_type fc)990 static int soggy_test(fc_type fc) {
991 genfile fda3;
992 long fsize;
993
994 if (DIAG) {
995 char *s;
996 s = formal_name(fc, fDA3);
997 rprintf("Testing %s for abnormal noun organization....", s);
998 rfree(s);
999 }
1000 fda3 = openbin(fc, fDA3, "Could not find room file '%s'.", 1);
1001 fsize = binsize(fda3);
1002 readclose(fda3);
1003
1004 if (fsize % (maxnoun - 300 + 1) != 0) {
1005 if (DIAG) rprintf("FOUND!\n");
1006 return 1;
1007 }
1008 if (fsize / (maxnoun - 300 + 1) > 300) {
1009 if (DIAG) rprintf("FOUND!\n");
1010 return 1;
1011 }
1012 if (DIAG) rprintf("nope.\n");
1013 return 0;
1014 }
1015
1016
deduce_sizes(fc_type fc,rbool diag)1017 static void deduce_sizes(fc_type fc, rbool diag)
1018 /* If diag is true, we will also allocate space for
1019 noun inside information; this is used by agtout */
1020 {
1021 if (ver == 0) {
1022 ver = 1;
1023 if (maxroom >= 200) ver = 2;
1024 else if (maxnoun != 0)
1025 if (maxnoun < 300)
1026 if (maxcreat != 0)
1027 if (maxcreat >= 500) ver = 4; /* SOGGY */
1028 else ver = 1; /* Small */
1029 else if (aver == AGTCOS) ver = 4; /* SOGGY */
1030 else ver = 1; /* Small */
1031 else if (aver != AGTCOS) ver = 2; /* Large */
1032 else if (soggy_test(fc)) ver = 4;
1033 else ver = 2;
1034 else if (maxcreat != 0)
1035 if (maxcreat >= 500)
1036 if (aver != AGTCOS) ver = 2; /* Large */
1037 else if (soggy_test(fc)) ver = 4; /* Either large or SOGGY */
1038 else ver = 2;
1039 else ver = 1; /* Small */
1040 else
1041 agtwarn("No nouns or creatures: unable to determine version."
1042 "\nAssuming AGT Small", 0);
1043 }
1044
1045 if (aver < AGTME15)
1046 MaxQuestion = 25;
1047 else
1048 MaxQuestion = 100; /* This is a guess. */
1049 if (aver == AGTCOS)
1050 MaxQuestion = 10;
1051 if (aver == AGT15 || aver == AGT15F)
1052 MaxQuestion = 57;
1053 first_room = 2;
1054 if (ver == 1) {
1055 first_noun = 200;
1056 first_creat = 300;
1057 last_obj = 399;
1058 last_message = 250;
1059 } else { /* ver 2 or 3 or 4 */
1060 if (ver != 4)
1061 first_noun = 300;
1062 else first_noun = 200;
1063 first_creat = 500;
1064 last_obj = 699;
1065 if (aver <= AGT12) last_message = 500;
1066 else if (aver < AGTME155) last_message = 600;
1067 else last_message = 800;
1068 }
1069 if (aver == AGTCOS) {
1070 if (ver == 4) last_obj = 610;
1071 else last_obj = 599;
1072 if (ver == 4) last_message = 810; /* Soggy case */
1073 else last_message = 700;
1074 }
1075
1076 if (aver >= AGT18 && aver <= AGT18MAX) {
1077 bold_mode = 1;
1078 build_fixchar();
1079 fixchar[(int)'\\'] = FORMAT_CODE;
1080 }
1081
1082 if (aver < AGTME10) {
1083 SL_TEXT = 81;
1084 SL_NAME = SL_WORD = 23;
1085 SL_ROOM = 31;
1086 } else {
1087 SL_TEXT = 81;
1088 SL_NAME = SL_WORD = 16;
1089 SL_ROOM = 31;
1090 }
1091 if (aver == AGT15 || aver == AGT15F) SL_NAME = SL_WORD = 16;
1092
1093 if (aver >= AGTME10) {
1094 MAX_USTR = 25;
1095 MAX_SUB = 15;
1096 } else MAX_SUB = MAX_USTR = 0;
1097
1098 if (aver >= AGT15)
1099 NUM_ERR = 185; /* Number of standard error messages */
1100 else
1101 NUM_ERR = 0;
1102
1103 DVERB = 50;
1104 FLAG_NUM = 255;
1105 CNT_NUM = VAR_NUM = 50;
1106 exitmsg_base = 1000;
1107
1108 num_rflags = num_nflags = num_cflags = 0;
1109 num_rprops = num_nprops = num_cprops = 0;
1110 objflag = NULL;
1111 objprop = NULL;
1112 attrtable = NULL;
1113 proptable = NULL;
1114 oflag_cnt = 0;
1115 oprop_cnt = 0;
1116 propstr = NULL;
1117 propstr_size = 0;
1118 vartable = NULL;
1119 flagtable = NULL;
1120
1121
1122
1123 /* Now to allocate space for all of the 'immortal' data structures */
1124 /* We do this all at once to avoid fragmentation; all of the following
1125 will be around for the life of the program (unless we restart) and so
1126 should be allocated first */
1127
1128 synlist = (slist *)rmalloc(sizeof(slist) * TOTAL_VERB);
1129 comblist = NULL; /* The original AGT didn't support multi-word verbs */
1130 num_comb = 0;
1131 userprep = NULL; /* ... nor did it allow user-defined prepostions */
1132 num_prep = 0;
1133
1134 if (numglobal > 0)
1135 globalnoun = (word *)rmalloc(numglobal * sizeof(word));
1136
1137 if (aver < AGTME15 && aver != AGT10) {
1138 question = (tline *)rmalloc(MaxQuestion * sizeof(tline));
1139 answer = (tline *)rmalloc(MaxQuestion * sizeof(tline));
1140 } else if (aver >= AGTME15) {
1141 quest_ptr = (descr_ptr *)rmalloc(MaxQuestion * sizeof(descr_ptr));
1142 ans_ptr = (descr_ptr *)rmalloc(MaxQuestion * sizeof(descr_ptr));
1143 }
1144 msg_ptr = (descr_ptr *)rmalloc((last_message) * sizeof(descr_ptr));
1145
1146 if (maxroom >= first_room) {
1147 room = (room_rec *)rmalloc((maxroom - first_room + 1) * sizeof(room_rec));
1148 room_ptr = (descr_ptr *)rmalloc((maxroom - first_room + 1) * sizeof(descr_ptr));
1149 help_ptr = (descr_ptr *)rmalloc((maxroom - first_room + 1) * sizeof(descr_ptr));
1150 special_ptr = (descr_ptr *)rmalloc((maxroom - first_room + 1) * sizeof(descr_ptr));
1151 if (diag) room_inside = (integer *)rmalloc((maxroom - first_room + 1) * sizeof(integer));
1152 }
1153
1154 if (maxnoun >= first_noun) {
1155 noun = (noun_rec *)rmalloc((maxnoun - first_noun + 1) * sizeof(noun_rec));
1156 noun_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
1157 push_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
1158 pull_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
1159 text_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
1160 turn_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
1161 play_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
1162 if (diag) noun_inside = (integer *)rmalloc((maxnoun - first_noun + 1) * sizeof(integer));
1163 }
1164
1165 if (maxcreat >= first_creat) {
1166 creature = (creat_rec *)rmalloc((maxcreat - first_creat + 1) * sizeof(creat_rec));
1167 creat_ptr = (descr_ptr *)rmalloc((maxcreat - first_creat + 1) * sizeof(descr_ptr));
1168 ask_ptr = (descr_ptr *)rmalloc((maxcreat - first_creat + 1) * sizeof(descr_ptr));
1169 talk_ptr = (descr_ptr *)rmalloc((maxcreat - first_creat + 1) * sizeof(descr_ptr));
1170 if (diag) creat_inside = (integer *)rmalloc((maxcreat - first_creat + 1) * sizeof(integer));
1171 }
1172
1173 if (aver >= AGTME10) {
1174 userstr = (tline *)rmalloc(MAX_USTR * sizeof(tline));
1175 sub_name = (word *)rmalloc(MAX_SUB * sizeof(word));
1176 }
1177 command = (cmd_rec *)rmalloc(sizeof(cmd_rec) * last_cmd);
1178
1179 if (aver >= AGT15)
1180 err_ptr = (descr_ptr *)rmalloc(NUM_ERR * sizeof(descr_ptr));
1181
1182
1183 reinit_dict(); /* The dictionary grows dynamically so we want to
1184 allocate it AFTER we have allocated all the permenent
1185 things */
1186 }
1187
1188
1189
1190
1191 /*-------------------------------------------------------------------------*/
1192 /* Read DA1: The Info file; this is a text file containing a miscellany of */
1193 /* game information that wouldn't fit elsewhere */
1194 /*-------------------------------------------------------------------------*/
1195
try_read_da1(fc_type fc,genfile fda1,rbool diag)1196 static int try_read_da1(fc_type fc, genfile fda1, rbool diag)
1197 /* Returns new aver value to try, or 0 on success. */
1198 /* diag determines if noun inside info will be read */
1199 /* VER values: 1=Small
1200 2=Large
1201 3=Master's Edition
1202 4="Soggy Large", with a larger last_room
1203 */
1204 /* AVER values: see agility.h for the current values */
1205 /* NOTE: This routine is allowed to set *ver*, but is not allowed to
1206 change *aver*; should it be neccessary to change *aver*, then the routine
1207 should return the new *aver* value.
1208 (The only exception to this is in the very beginning-- and that may get
1209 changed)
1210 [This is done to allow the user to force a version number]
1211 */
1212 {
1213 int i;
1214
1215 MAX_CMD_SIZE = 30;
1216 maxpict = maxpix = maxfont = maxsong = 0;
1217 linenum = 0;
1218 bhold = 0;
1219 game_sig = 0;
1220 unexpected_eof = 0;
1221
1222 if (aver == 0 && isbool(fda1)) aver = AGTMAST;
1223 /* From this point on can assume ME detected */
1224
1225 freeze_mode = 0; /* The default values */
1226 if (aver >= AGTME10) { /* 2 rbool */
1227 debug_mode = readrbool(fda1); /* DEBUG */
1228 if (aver >= AGTME15) {
1229 if (!isbool(fda1)) aver = AGTME10;
1230 else freeze_mode = readrbool(fda1);
1231 } /* FREEZE */
1232 ver = 3;
1233 }
1234
1235 start_room = readnum(fda1);
1236 treas_room = readnum(fda1);
1237 if (aver != AGT10) resurrect_room = readnum(fda1);
1238 else resurrect_room = start_room;
1239 if (aver >= AGTME10) { /* 4 int */
1240 score_mode = readnum(fda1); /* Score option */
1241 statusmode = readnum(fda1); /* Status option */
1242 startup_time = readnum(fda1); /* Starting time */
1243 delta_time = readnum(fda1); /* Delta_time */
1244 } else {
1245 score_mode = statusmode = 0;
1246 startup_time = delta_time = 0;
1247 }
1248 max_lives = readnum(fda1);
1249 if (aver != AGT10) max_score = readnum(fda1);
1250 else max_score = 0;
1251 maxroom = readnum(fda1);
1252 maxnoun = readnum(fda1);
1253 maxcreat = readnum(fda1);
1254 if (aver >= AGTME10) numglobal = readnum(fda1); /* # of global nouns? */
1255 else numglobal = 0;
1256 last_cmd = readnum(fda1);
1257 readjunk(fda1); /* Number of items being carried */
1258 readjunk(fda1); /* Number of items being worn */
1259 if (isbool(fda1)) return AGT10; /* AGT v1.0 */
1260 /* From this point on, can assume AGT v1.0 is detected. */
1261 readptr(fda1, &intro_ptr);
1262
1263 deduce_sizes(fc, diag);
1264
1265 if (aver >= AGTME10) {
1266
1267 (void)readdict(fda1); /* ?!?! Not sure what this is */
1268
1269 report("Reading global and flag nouns", fda1);
1270
1271 for (i = 0; i < MAX_FLAG_NOUN; i++)
1272 flag_noun[i] = readdict(fda1); /* Read in flag nouns; may be NONE */
1273 for (; i < 32; i++)
1274 readjunk(fda1);
1275
1276 for (i = 0; i < numglobal; i++)
1277 globalnoun[i] = readdict(fda1); /* Global nouns */
1278 } else
1279 for (i = 0; i < MAX_FLAG_NOUN; i++)
1280 flag_noun[i] = 0;
1281
1282 report("Reading questions and junk", fda1);
1283
1284 if (aver < AGTME15 && aver != AGT10) {
1285 for (i = 0; i < MaxQuestion; i++) {
1286 readtext(fda1, question[i]); /* Question[i]== question #(i+1) */
1287 chop_newline(question[i]);
1288 readtext(fda1, answer[i]);
1289 fix_answer(answer[i]);
1290 }
1291 } else if (aver >= AGTME15) {
1292 /* There are 400 lines of description pointers, meaning
1293 200 descriptions. I'm guessing they're all questions and
1294 answers, which means that there are 100 questions here. */
1295 for (i = 0; i < MaxQuestion; i++) {
1296 readptr(fda1, &quest_ptr[i]);
1297 readptr(fda1, &ans_ptr[i]);
1298 }
1299 }
1300
1301 if (!isbool(fda1)) { /* Something is wrong... */
1302 if (aver == AGTMAST)
1303 return AGTME10;
1304 else if (aver != AGTCOS && aver != AGT15 && aver != AGT15F) return AGTCOS;
1305 else return AGT15;
1306 }
1307 report("Reading have_meta", fda1);
1308 have_meta = readrbool(fda1);
1309
1310 if (have_meta) {
1311 for (i = 0; i <= last_obj; i++) { /* i.e. iterate over all objects */
1312 readjunk(fda1);
1313 readjunk(fda1);
1314 }
1315 }
1316
1317 /* The Master's Edition apparently _always_ sets have_meta,
1318 even if there are no metacommands. The only way to determine
1319 if there are really metacommands is to check last_cmd */
1320 if (aver >= AGTME10 && last_cmd == 0) have_meta = 0;
1321
1322 report("Reading synonyms", fda1);
1323
1324 for (i = 0; i < TOTAL_VERB; i++)
1325 synlist[i] = synptr; /* Is this correct? */
1326 addsyn(-1); /* Put an end-of-list marker in place */
1327
1328 for (i = 0; i < 56; i++)
1329 synlist[i] = readslist(fda1); /* May read <none> */
1330
1331 if (aver >= AGTME10) { /* Unknown verbs */
1332 synlist[56] = readslist(fda1); /* VIEW */
1333 synlist[57] = synlist[14]; /* AFTER */
1334 synlist[14] = readslist(fda1); /* THROW */
1335 }
1336
1337 if (aver == AGT183) {
1338 /* Eliminate LIST_EXITS and add INSTRUCTIONS */
1339 synlist[58] = synlist[52];
1340 /* Move 'REMOVE'-- the last thing before INS in 1.83 verblist --
1341 up to INSTRUCTIONS where it belongs */
1342 for (i = 52; i > 42; i--) /* i:=Remove to Brief (above List Exits) */
1343 synlist[i] = synlist[i - 1];
1344 synlist[41] = synptr; /* LIST_EXITS, which doesn't exist in 1.83 and so
1345 can't have synonyms */
1346 addsyn(-1);
1347 }
1348
1349 report("Starting dummy verbs", fda1);
1350 for (i = 0; i < 25; i++) {
1351 if (i != 0 || aver < AGTME10)
1352 synlist[i + BASE_VERB] = readslist(fda1);
1353 synlist[i + BASE_VERB + 25] = readslist(fda1);
1354 }
1355 if (aver >= AGTME10) {
1356 synlist[BASE_VERB] = readslist(fda1);
1357 for (i = 0; i < 15; i++) /* Subroutines */
1358 synlist[i + BASE_VERB + 50] = readslist(fda1);
1359 }
1360 report("Reading DESC ptrs", fda1);
1361 if (aver >= AGT15)
1362 for (i = 0; i < NUM_ERR; i++)
1363 readptr(fda1, &err_ptr[i]); /* Read in "standard" error messages. */
1364 else /* Otherwise need to initialize them to nothing */
1365 for (i = 0; i < NUM_ERR; i++) {
1366 err_ptr[i].start = 0;
1367 err_ptr[i].size = -1;
1368 }
1369
1370 report("Reading messages", fda1);
1371 if (DIAG) rprintf(" MSGS:1..%ld [%ld]\n", last_message, last_message);
1372 for (i = 0; i < last_message; i++)
1373 readptr(fda1, &msg_ptr[i]);
1374
1375 report("Reading room descs", fda1);
1376 for (i = 0; i <= maxroom - first_room; i++) {
1377 readptr(fda1, &room_ptr[i]);
1378 readptr(fda1, &help_ptr[i]);
1379 readptr(fda1, &special_ptr[i]);
1380 }
1381
1382 report("Reading noun descs", fda1);
1383 for (i = 0; i <= maxnoun - first_noun; i++) {
1384 readptr(fda1, &noun_ptr[i]);
1385 readptr(fda1, &push_ptr[i]);
1386 readptr(fda1, &pull_ptr[i]);
1387 readptr(fda1, &text_ptr[i]);
1388 readptr(fda1, &turn_ptr[i]);
1389 readptr(fda1, &play_ptr[i]);
1390 }
1391
1392 report("Reading creatures", fda1);
1393 if (maxcreat >= first_creat) {
1394 for (i = 0; i <= maxcreat - first_creat; i++) {
1395 readptr(fda1, &creat_ptr[i]);
1396 if (aver != 0 && aver <= AGTCLASS) {
1397 ask_ptr[i].start = talk_ptr[i].start = 0;
1398 ask_ptr[i].size = talk_ptr[i].size = -1;
1399 } else {
1400 readptr(fda1, &talk_ptr[i]);
1401 readptr(fda1, &ask_ptr[i]);
1402 if (aver == 0 && (talk_ptr[i].size == 0 || ask_ptr[i].size == 0 ||
1403 unexpected_eof)) return AGT135;
1404 }
1405 }
1406 }
1407 if (aver == AGT135 && unexpected_eof) return AGT12;
1408
1409 if (aver >= AGTME10) {
1410 if (aver >= AGTME155 && !isnum(fda1)) return AGTME15;
1411
1412 maxpict = rangefix(readnum(fda1)); /* Number of pictures */
1413 maxpix = rangefix(readnum(fda1)); /* Numper of PIXs */
1414 maxsong = rangefix(readnum(fda1)); /* Number of sounds */
1415 maxfont = rangefix(readnum(fda1)); /* Number of fonts. */
1416
1417 if (maxpix > MAX_PIX) {
1418 rprintf("Invalid MAXPIX value?!?\n");
1419 maxpix = MAX_PIX;
1420 }
1421
1422 t_pictlist = (long *)rmalloc(sizeof(long) * maxpict);
1423 t_pixlist = (long *)rmalloc(sizeof(long) * maxpix);
1424 t_songlist = (long *)rmalloc(sizeof(long) * maxsong);
1425 t_fontlist = (long *)rmalloc(sizeof(long) * maxfont);
1426
1427 for (i = 0; i < maxpict; i++)
1428 t_pictlist[i] = readfname(fda1); /* picture file names */
1429 for (i = 0; i < maxpix; i++)
1430 pix_name[i] = readdict(fda1); /* PIX names */
1431 for (i = 0; i < maxpix; i++)
1432 t_pixlist[i] = readfname(fda1); /* PIX filenames */
1433 for (i = 0; i < maxsong; i++)
1434 t_songlist[i] = readfname(fda1); /* Sound filenames */
1435 for (i = 0; i < maxfont; i++)
1436 t_fontlist[i] = readfname(fda1); /* Font filenames */
1437
1438 for (i = 0; i < MAX_USTR; i++)
1439 readtext(fda1, userstr[i]); /* This is just a guess-- should be
1440 tested. */
1441 } else {
1442 for (i = 0; i < maxpix; i++) pix_name[i] = 0;
1443 maxpict = maxpix = maxsong = maxfont = 0;
1444 }
1445 if ((aver == AGT135 || aver == 0) && isnum(fda1)) return AGT183;
1446 if (aver == AGT183) {
1447 long tval;
1448 tval = readnum(fda1); /* Needs to be translated */
1449 if (tval >= 1 && tval <= 4)
1450 statusmode = agt18_statmode[tval - 1];
1451 else statusmode = 0;
1452 tval = readnum(fda1); /* Hours */
1453 startup_time = readnum(fda1); /* Minutes */
1454 tval += startup_time / 60;
1455 startup_time = (startup_time % 60) + 100 * tval;
1456 if (readrbool(fda1) && startup_time < 1200)
1457 startup_time += 1200;
1458 milltime_mode = readrbool(fda1); /* Military time */
1459 delta_time = readnum(fda1);
1460 }
1461 if (DIAG) rprintf("Read in %d lines\n", linenum);
1462 return 0;
1463 }
1464
1465
1466
set_da1_null(void)1467 static void set_da1_null(void)
1468 /* Set pointers that are malloc'd by try_read_da1 to NULL, to clear
1469 the way for free_da1_stuff to recover them */
1470 {
1471 static_str = NULL;
1472 ss_end = ss_size = 0;
1473 command = NULL;
1474 cmd_ptr = NULL;
1475 synlist = NULL;
1476 userstr = NULL;
1477 sub_name = NULL;
1478 globalnoun = NULL;
1479 err_ptr = NULL;
1480 quest_ptr = ans_ptr = NULL;
1481 question = answer = NULL;
1482 msg_ptr = room_ptr = help_ptr = special_ptr = NULL;
1483 noun_ptr = push_ptr = pull_ptr = text_ptr = turn_ptr = play_ptr = NULL;
1484 room_inside = noun_inside = creat_inside = NULL;
1485 creat_ptr = ask_ptr = talk_ptr = NULL;
1486 pictlist = pixlist = fontlist = songlist = NULL;
1487 room = NULL;
1488 noun = NULL;
1489 creature = NULL;
1490 command = NULL;
1491 t_pictlist = t_pixlist = t_fontlist = t_songlist = NULL;
1492 }
1493
1494
1495
free_da1_stuff(void)1496 static void free_da1_stuff(void)
1497 /* Free all data structures malloc'd by try_read_da1 */
1498 /* (This is neccessary since try_read_da1 may have to restart) */
1499 /* Note that if a pointer is NULL, rfree does nothing */
1500 /* Recall that rfree() is a macro that sets its argument to NULL */
1501 /* after freeing it */
1502 {
1503 rfree(static_str);
1504 ss_end = ss_size = 0;
1505 rfree(userstr);
1506 rfree(sub_name);
1507 rfree(globalnoun);
1508 rfree(err_ptr);
1509 rfree(synlist);
1510 rfree(quest_ptr);
1511 rfree(ans_ptr);
1512 rfree(question);
1513 rfree(answer);
1514 rfree(msg_ptr);
1515 rfree(room_ptr);
1516 rfree(help_ptr);
1517 rfree(special_ptr);
1518 rfree(noun_ptr);
1519 rfree(push_ptr);
1520 rfree(pull_ptr);
1521 rfree(text_ptr);
1522 rfree(room_inside);
1523 rfree(noun_inside);
1524 rfree(creat_inside);
1525 rfree(turn_ptr);
1526 rfree(play_ptr);
1527 rfree(creat_ptr);
1528 rfree(ask_ptr);
1529 rfree(talk_ptr);
1530 rfree(t_pictlist);
1531 rfree(t_pixlist);
1532 rfree(t_songlist);
1533 rfree(t_fontlist);
1534 rfree(room);
1535 rfree(noun);
1536 rfree(creature);
1537 rfree(command);
1538 free_dict();
1539 }
1540
read_da1(fc_type fc,rbool diag)1541 static rbool read_da1(fc_type fc, rbool diag)
1542 /* diag is set by agtout to save extra diagnostic information */
1543 /* It has nothing to do with DIAG */
1544 {
1545 genfile fda1;
1546 int i;
1547
1548 ver = 0;
1549 aver = 0;
1550 top_quest = 0; /* Highest question actually referenced; set by fixcmd */
1551 fda1 = openfile(fc, fDA1, NULL, 0);
1552 if (!filevalid(fda1, fDA1)) return 0;
1553
1554 if (DIAG) {
1555 char *s;
1556 s = formal_name(fc, fDA1);
1557 rprintf("Reading info file %s\n", s);
1558 rfree(s);
1559 }
1560 set_da1_null();
1561 while ((i = try_read_da1(fc, fda1, diag)) != 0) {
1562 if (aver == i) {
1563 rprintf("[Recoginiton loop: AVER=%d]\n", aver);
1564 fatal("AGT version not recognized\n");
1565 }
1566 aver = i;
1567 /* fseek(fda1,0,SEEK_SET); Go back to beginning... */
1568 textrewind(fda1);
1569 if (DIAG)
1570 rprintf("...Found incompatibility; restarting, w/ AVER=%d\n", aver);
1571 free_da1_stuff();
1572 /* set_da1_null();*/
1573 ver = 0;
1574 }
1575 if (aver == 0) aver = AGTSTD; /* i.e. if we didn't notice any differences from
1576 standard format, it must be a standard file. */
1577 readclose(fda1);
1578 return 1; /* Success */
1579 }
1580
1581
1582
1583 /*-------------------------------------------------------------------------*/
1584 /* Miscellaneous routines to tie up loose ends and clean up afterwards. */
1585 /*-------------------------------------------------------------------------*/
1586
finish_read(rbool cleanup)1587 static void finish_read(rbool cleanup)
1588 /* cleanup=0 means it will leave cmd_ptr, 1=it cleans up cmd_ptr */
1589 /* The only reason to set cleanup==0 is if we are writing a diagnostic
1590 program of some sort */
1591 {
1592 int i;
1593
1594 if (aver >= AGT18 && aver <= AGT18MAX) {
1595 intro_first = 1;
1596 max_lives = 1;
1597 TWO_CYCLE = 1;
1598 PURE_AFTER = 0;
1599 } else {
1600 intro_first = 0;
1601 TWO_CYCLE = 0;
1602 PURE_AFTER = 1;
1603 }
1604
1605 min_ver = 0; /* All original AGT games will run with any version of
1606 AGiliTy. */
1607
1608 if (aver >= AGTME10)
1609 PURE_ROOMTITLE = 0;
1610
1611 if (aver >= AGT15)
1612 box_title = 1;
1613 else box_title = 0;
1614
1615 /* Compute max_score if it isn't already computed */
1616 if (max_score == 0) {
1617 for (i = 0; i < maxroom - first_room + 1; i++)
1618 if (!room[i].unused) max_score += room[i].points;
1619 for (i = 0; i < maxnoun - first_noun + 1; i++)
1620 if (!noun[i].unused) max_score += noun[i].points;
1621 for (i = 0; i < maxcreat - first_creat + 1; i++)
1622 if (!creature[i].unused) max_score += creature[i].points;
1623 }
1624
1625 if (cleanup) rfree(cmd_ptr);
1626 if (ss_end > 0)
1627 static_str = (char *)rrealloc(static_str, sizeof(char) * ss_end);
1628
1629 /* Now convert string handles into honest pointers */
1630 for (i = 0; i <= maxroom - first_room; i++)
1631 room[i].name = static_str + room_name[i];
1632 for (i = 0; i <= maxnoun - first_noun; i++) {
1633 noun[i].shortdesc = static_str + noun_sdesc[i];
1634 noun[i].position = static_str + noun_pos[i];
1635 }
1636 for (i = 0; i <= maxcreat - first_creat; i++)
1637 creature[i].shortdesc = static_str + creat_sdesc[i];
1638
1639 if (aver >= AGTME10) {
1640 pictlist = (filename *)rmalloc(sizeof(filename) * maxpict);
1641 pixlist = (filename *)rmalloc(sizeof(filename) * maxpix);
1642 songlist = (filename *)rmalloc(sizeof(filename) * maxsong);
1643 fontlist = (filename *)rmalloc(sizeof(filename) * maxfont);
1644
1645 for (i = 0; i < maxpict; i++)
1646 pictlist[i] = static_str + t_pictlist[i];
1647 for (i = 0; i < maxpix; i++)
1648 pixlist[i] = static_str + t_pixlist[i];
1649 for (i = 0; i < maxsong; i++)
1650 songlist[i] = static_str + t_songlist[i];
1651 for (i = 0; i < maxfont; i++)
1652 fontlist[i] = static_str + t_fontlist[i];
1653 }
1654
1655 /* Free the various temporary arrays */
1656 rfree(room_name);
1657 rfree(noun_sdesc);
1658 rfree(noun_pos);
1659 rfree(creat_sdesc);
1660 rfree(t_pictlist);
1661 rfree(t_pixlist);
1662 rfree(t_songlist);
1663 rfree(t_fontlist);
1664
1665 /* Reallocate questions and asnwers to only use the space that they need */
1666 if (!RAW_CMD_OUT && top_quest < MaxQuestion) {
1667 MaxQuestion = top_quest; /* top_quest is computed by fixcmd */
1668 if (top_quest == 0) {
1669 rfree(question);
1670 rfree(answer);
1671 rfree(quest_ptr);
1672 rfree(ans_ptr);
1673 } else {
1674 if (question != NULL)
1675 question = (tline *)rrealloc(question, top_quest * sizeof(tline));
1676 if (answer != NULL)
1677 answer = (tline *)rrealloc(answer, top_quest * sizeof(tline));
1678 if (quest_ptr != NULL)
1679 quest_ptr = (descr_ptr *)rrealloc(quest_ptr, top_quest * sizeof(descr_ptr));
1680 if (ans_ptr != NULL)
1681 ans_ptr = (descr_ptr *)rrealloc(ans_ptr, top_quest * sizeof(descr_ptr));
1682 }
1683 }
1684 }
1685
free_all_agtread()1686 void free_all_agtread() {
1687 int i;
1688
1689 if (!agx_file)
1690 for (i = 0; i < last_cmd; i++)
1691 rfree(command[i].data);
1692 free_da1_stuff();
1693 /* userstr, globalnoun, quest_ptr, ans_ptr, question, answer, msg_ptr,
1694 room_ptr, help_ptr, special_ptr, noun_ptr, push_ptr, pull_ptr,
1695 text_ptr, turn_ptr, play_ptr, creat_ptr, ask_ptr, talk_ptr,
1696 room_inside, noun_inside, creat_inside
1697 pictlist, pixlist, songlist, fontlist,
1698 room, noun, creature, command,
1699 dictionary data structures */
1700 }
1701
readagt(fc_type fc,rbool diag)1702 rbool readagt(fc_type fc, rbool diag)
1703 /* If diag==1, then extra diagnostic information is preserved */
1704 {
1705 agx_file = 0;
1706 mem_descr = NULL;
1707 build_fixchar();
1708 init_dict();
1709 if (!read_da1(fc, diag)) return 0; /* Couldn't open DA1 file */
1710 read_da2(fc);
1711 read_da3(fc);
1712 read_da4(fc);
1713 read_da5(fc);
1714 read_voc(fc);
1715 read_opt(fc);
1716 finish_read(!diag);
1717 return 1;
1718 }
1719
1720 } // End of namespace AGT
1721 } // End of namespace Glk
1722