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