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 /* NOTES ON CHANGING THE AGX FILE FORMAT
29 
30 	 First of all, don't.
31 
32 	 One of the benefits of adventure creation systems like this is
33    that the same game files can be played on a variety of different
34    platforms without any extra effort on the part of the game
35    author.  If you change the file format, this is no longer true:  games
36    created under the new format won't run on the old interpreters.
37 
38 	 Even if you distribute a new interpreter with your game, there are two
39    problems:
40 
41 	 i) People on other platforms won't be able to play your game unless
42 	and until your modified interpreter is ported to their machine. Since
43 	I-F players as a group tend to use a wider range of different computers
44 	and operating systems than the population at large, this is bad.
45 
46 	 ii) Even for machines that you port your modified interpreter to,
47 	people will now need to maintain two interpreters: the original one
48 	 (for most of the games) and your modified one (for your new game).
49 	This is not only a nuisance but it wastes disk space.
50 
51 
52 	If you *do* decide to change the file format anyhow, please adhere to
53   the following guidelines, to minimize confusion.
54 
55 GUIDLINES FOR NEW FILE FORMAT VERSIONS
56 
57   File format version are labled by a series of four bytes near the
58 beginning of the file. (They are actually the fifth, sixth, seventh,
59 and eight bytes-- the first four bytes are the file format signature
60 that indicate the file is an AGX file and not, say, a PCX file)
61 
62   In order, they are the version owner id and number, and the
63 extension owner id and number.  In "vanilla" AGX, both owner id's are
64 'R', the version number is 2, and the extension number is 1 (the
65 extension associated with AGiliTy 1.0).  (There are AGX files with
66 version numbers 0 and 1 created by earlier releases of agt2agx.  In
67 fact, for downward compatibility, agt2agx will sometimes create a file
68 of version 1 and extnum 7 if later features aren't being used.)
69 
70   I will discuss the difference between "versions" and "extensions"
71 further below, but briefly: extensions are minor changes whereas versions
72 represent vast reoganizations of the file format.  The routines below
73 will still try to read in a file with an unrecognized extension, but
74 they will give up on an unrecognized version.
75 
76   If you create a new extension, then you should first change the
77 extension owner id to something else; the owner id is intended to
78 indicate who is responsible for defining this extension; 'R' indicates
79 Robert Masenten (the author of this file) and so shouldn't be used by anyone
80 else. The id isn't required to be a printable character.
81 
82   You can then define the extension number however you want. The
83 extension number is intended to differentiate between different
84 extensions defined by the same source (e.g. two extensions both
85 defined by me would have the same owner id but different extension
86 numbers). The extensions that I define are numbered sequentially
87 starting at 0, but you don't have to follow this convention if you
88 don't want to.
89 
90   Finally, send me an e-mail note telling me what you've done so I can
91 keep track of the different extensions and prevent conflicts between
92 owner-ids.
93 
94   Creating a new version works the same way: change the version owner-id to
95 something no one is using and set the version number however you want.
96 If you're defining a new version, you can do whatever you want with
97 the two extension bytes.
98 
99 
100 EXTENSIONS AND VERSIONS
101 
102   For purposes of the file format, an 'extension' is a change to the
103 format that follows certain restrictions given below; a 'version' is one that
104 violates one or more of these restrictions.
105 
106   If at all possible you should try to fit your changes to the format
107 within the limitations of an 'extension': it is more likely that other
108 programs will work with your new file format and it is also more
109 likely that your modified interpreter will still be able to understand
110 the original file format.
111 
112   An extension shouldn't change any of the existing data fields; nor
113 should it insert new data fields into the middle of records.  An
114 extension *may* add new fields onto the end of one or more of the
115 records and it can define new blocks.
116 
117   Examples of things that would be extensions (create a new extension
118 id and number, but keep the version the same):
119 
120 --Adding a new field onto the end of the creature record, containing
121 	the code for a sound file that should be played whenever
122 	the creature is in the room.
123 
124 --Adding a new block to the file containing debugging information for
125 	the new AGT compiler you've just written, numbered 35.
126 
127 
128   Things that would *not* be extensions (create a new version id and
129 number; do what you want with the extension id and number)
130 
131   --Going to 32-bit object numbers for everything. (There *are*
132 sneaky ways you could make this an extension; but not if you just do this
133 by converting all the int16 fields in the file to int32)
134 
135   --Changing metacommands to accept an arbitrary string of tokens
136 instead of just  ACTOR,VERB NOUN PREP OBJECT.
137 
138 
139 
140 A FEW NOTES ON BACKWARD COMPATIBILITY
141 
142   (These notes only apply if you are creating an extension; if you are
143 creating a new version, then anything goes)
144 
145   If you add a new field onto an existing record (like the creature
146 soundtrack example above) and read in an old-format file, then the new
147 data fields will be automatically initialized to zero so long as none
148 of them are individually longer than 81 bytes (if any *are* longer than
149 81 bytes then the file routines may break). (There is nothing magic
150 about 81 bytes; it just happens to be the length of the largest data
151 structure that shows up in 'vanilla' AGX files).
152 
153   If you add a new block, then you should check the extension number
154 of the file and if the block doesn't exists be prepared to either
155 initialize the data to something sensible or to exit cleanly with an
156 error message.
157 
158  */
159 
160 /* AGX File format versions and corresponding versions of AGiliTy
161    and Magx: (versions given as 'Version-Extension')
162 
163  AGX  AGiliTy    Magx
164  0-0    0.5
165  0-1    0.7       0.1
166  1-0    0.7.2     0.2
167  1-1    0.8
168  1-2    0.8.1     0.3
169  1-3    0.8.3     0.4
170  1-4    0.8.5
171  1-5    0.8.6     0.5
172  1-6    0.8.7     0.5.2
173  1-7    0.8.8     0.6
174  2-0    0.8.9     0.6.1
175  2-1    1.0       0.6.3
176  2-2    1.1       0.6.4
177  */
178 
179 /* Changes is AGX File format versions:
180   0-0: Original format.
181   0-1: Add
182 	PURE_TIME, PURE_OBJ_DESC, exitmsg_base
183 	noun.related_name
184 	command.noun_adj, command.obj_adj
185   1-0: Change index file format, fixing a bug
186   1-1: Add
187 	  Multi-word verb block(28)
188 	  Preposition block(29)
189 	  (room|noun|creature).oclass
190   1-2: Add (room|noun|creature).unused
191   1-3: Add PURE_GRAMMAR
192   1-4: Add (noun|creature).isglobal and (noun|creature).flagnum
193 	   Add TWO_CYCLE
194   1-5: Add min_ver
195 	   Add PURE_AFTER
196   1-6: Add (noun|creature).seen
197   1-7: Add objflag and objprop blocks (with corrosponding
198 		 support data in the gameinfo block).
199   2-0: No change in file format; version upped to protect against
200 		a bug in early versions of the AGX file code.
201   2-1: Added (noun|creature).proper
202   2-2: Added noun_obj and obj_obj to cmd header
203 	   Added objflag.ystr, objflag.nstr, objprop.str_cnt, objprop.str_list
204 	   Added propstr block.
205 	   Added fallback_ext to file header.
206 */
207 
208 #define AGX_NUMBLOCK 37
209 #define AGT_FILE_SIG 0x51C1C758L
210 
211 /* AGX File format:
212   (This tends to lag a little behind the code below;
213   you should double check against the actual file_info definitions
214   below)
215 All integer values stored little-endian.
216 desc_ptrs: int32 ofs, int32 leng (both in lines)
217 dictionary word: int16
218 slist ptr: int16
219 tline: char[81]
220 filename: char[10]
221 rbool values are packed into bytes, 1 bit per value, from lsb to msb.
222 cfgopt: 0=false, 1=true, 2=leave alone
223 
224 Mandatory blocks are marked with astericks.
225 
226 *File header: 16  bytes
227    uint 32 File ID [AGT_FILE_SIG, 4 bytes]
228    byte Version owner: 'R'
229    byte Version 0
230    byte Extension owner 'R'
231    byte Extension 0
232    char[2]: '\n\r'  -- to catch download errors
233    byte Extension fallback. For non-'R' extensions, this gives the
234 		   'R' extension to fall back to.
235    char[5] Reserved for future use (should be 0 right now)
236 *0-File index:
237    For each block (including itself): [16 bytes]
238 		uint32 starting offset
239 		uint32 block size
240 	uint32 number of records
241 	uint32 size of a record  (recsize*numrec == blocksize)
242 11-Description strings (block of tline)
243 12-Command text (block of int16)
244 *1-Game header
245    uint16 AGT_version_code;  +1 for "big/soggy" games
246    uint32 game_sig  (game signature, used to check save files and debug info)
247    rbool debug_mode, freeze_mode, milltime_mode, bold_mode,
248 		 have_meta, mars_fix, intro_first, TWO_CYCLE;
249    uchar score_mode, statusmode;
250    uint16 max_lives
251    uint32 max_score;
252    uint16 startup_time, delta_time;
253    descr_ptr intro_ptr, title_ptr, ins_ptr;
254    int16 start_room, treas_room, resurrect_room
255    int16 first_room, first_noun, first_creat
256    int16 FLAG_NUM, CNT_NUM, VAR_NUM
257    int16 BASE_VERB
258    cfgopt PURE_ANSWER, PURE_TIME, PURE_ROOMTITLE;
259    cfgopt PURE_AND, PURE_METAVERB;
260    cfgopt PURE_SYN, PURE_NOUN, PURE_ADJ;
261    cfgopt PURE_DUMMY, PURE_SUBNAME, PURE_PROSUB;
262    cfgopt PURE_HOSTILE, PURE_GETHOSTILE;
263    cfgopt PURE_DISAMBIG, PURE_ALL;
264    cfgopt irun_mode, verboseflag;
265    cfgopt PURE_GRAMMAR  (Extension R1-R3)
266    rbool TWO_CYCLE (R1-R4)
267    PURE_AFTER (R1-R5)
268    int16 min_ver
269    uchar font_status;
270    int16 num_rflags, num_nflags, num_cflags;
271    int16 num_rprops, num_nprops, num_cprops;
272 2-Room data (room_rec format, pointers->int ref into static string)
273    include help, desc, special ptrs
274 3-Noun data (noun_rec format)
275    include noun, text, turn, push, pull, play ptrs
276 4-Creature data (creat_rec format)
277    include creature, talk, ask ptrs
278 5-Command headers (cmd_rec format), pointers into command text
279 	 must be in increasing order.
280 6-Standard error message ptrs (array of descptr
281 7-Message ptrs   (array of descptr)
282 8-Question pointers (array of descptr)
283 9-Answer pointers (array of descptr)
284 10-User strings  (array of tline)
285 *13-Static string block (block of chars)
286 14-Subroutine dictionary ids (array of word:int16)
287 *15-Synlist (for verbs) (array of slist:int16)
288 16-Pix names (array of word:int16 -- pointers into dictionary)
289 17-Global nouns (array of word:int16 -- ptrs into dictionary)
290 18-Flag nouns (array of word:int16)
291 *19-Syntbl (block of word:int16)
292 *20-Dictionary text (block of char)
293 *21-Dictionary 'index' (array of uint32)
294 22-OPT block (14 bytes)
295 23-Picture filename ptrs
296 24-Pix filename ptrs
297 25-Font filename ptrs
298 26-Sound filename ptrs
299 27-VOC block, an array of verbinfo_rec
300 28-Multi-word verbs  (Extension R1-R1)
301 29-Prep table (Extension R1-R1)
302 30- ObjFlag Data (Extension R1-R7)
303 31- ObjProp Data (Extension R1-R7)
304 32- ObjFlag Lookup (Extension R1-R7)
305 33- ObjProp Lookup (Extension R1-R7)
306 34- ObjProp string pointers (array of FT_STR) (Extension R2-R2)
307 35- Variable itemization array  (Extension R2-R2)
308 36- Flag itemization array (Extension R2-R2)
309 
310 */
311 
312 /* AGT Version IDs; +1 for LARGE/SOGGY
313 		00000=v1.0
314 	01800=v1.18
315 	01900=v1.19
316 	02000=v1.20           ("Early Classic")
317 	03200=v1.32/COS
318 	03500=v1.35           ("Classic")
319 	05000=v1.5/H
320 	05050=v1.5/F (MDT)
321 		05070=v1.6   (PORK)
322 	08200=v1.82
323 	08300=v1.83
324 		10000=ME/1.0
325 	15000=ME/1.5
326 	15500=ME/1.55
327 	16000=ME/1.6
328 	20000=Magx/0.0
329 	etc.
330 */
331 
332 
333 
334 
335 /* ------------------------------------------------------------- */
336 /*        AGX Block Descriptions                                 */
337 /* ------------------------------------------------------------- */
338 
339 
340 static integer old_base_verb;
341 
342 /* AGX file info blocks */
343 
344 #define g(ft,dt,var) {ft,dt,&var,0}
345 #define r(ft,dt,str,f) {ft,dt,NULL,offsetof(str,f)}
346 #define dptype {FT_DESCPTR,DT_DESCPTR,NULL,0}
347 #define xx DT_DEFAULT
348 #define u16 FT_UINT16
349 #define u32 FT_UINT32
350 #define bb  FT_BOOL
351 #define i16 FT_INT16
352 
353 static file_info fi_gameinfo[] = {
354 	/* 0  */
355 	g(FT_VERSION, xx, aver), /* FT_VERSION converter also sets ver */
356 	g(u32, DT_LONG, game_sig),
357 	/* 6 */
358 	g(bb, xx, debug_mode), g(bb, xx, freeze_mode), g(bb, xx, milltime_mode),
359 	g(bb, xx, bold_mode), g(bb, xx, have_meta), g(bb, xx, mars_fix),
360 	g(bb, xx, intro_first), g(bb, xx, box_title),
361 	/* 7 */
362 	g(FT_BYTE, xx, score_mode), g(FT_BYTE, xx, statusmode),
363 	g(i16, xx, max_lives), g(u32, DT_LONG, max_score),
364 	/* 15 */
365 	g(i16, xx, startup_time), g(i16, xx, delta_time),
366 	/* 19 */
367 	g(FT_DESCPTR, xx, intro_ptr), g(FT_DESCPTR, xx, title_ptr),
368 	g(FT_DESCPTR, xx, ins_ptr),
369 	/* 43 */
370 	g(i16, xx, treas_room),
371 	g(i16, xx, start_room), g(i16, xx, resurrect_room),
372 	g(i16, xx, first_room),  g(i16, xx, first_noun),
373 	g(i16, xx, first_creat), g(i16, xx, FLAG_NUM),
374 	g(i16, xx, CNT_NUM), g(i16, xx, VAR_NUM),
375 	g(i16, xx, old_base_verb),
376 	/* 63 */
377 	g(FT_CFG, xx, PURE_ANSWER), g(FT_CFG, xx, PURE_ROOMTITLE),
378 	g(FT_CFG, xx, PURE_AND), g(FT_CFG, xx, PURE_METAVERB),
379 	g(FT_CFG, xx, PURE_SYN), g(FT_CFG, xx, PURE_NOUN), g(FT_CFG, xx, PURE_ADJ),
380 	g(FT_CFG, xx, PURE_DUMMY), g(FT_CFG, xx, PURE_SUBNAME),
381 	g(FT_CFG, xx, PURE_PROSUB), g(FT_CFG, xx, PURE_HOSTILE),
382 	g(FT_CFG, xx, PURE_GETHOSTILE), g(FT_CFG, xx, PURE_DISAMBIG),
383 	g(FT_CFG, xx, PURE_ALL),
384 	g(FT_CFG, xx, irun_mode), g(FT_CFG, xx, verboseflag),
385 	g(FT_CFG, xx, PURE_TIME),    /* Ext R0-1 */
386 	g(FT_CFG, xx, PURE_OBJ_DESC), /* Ext R0-1 */
387 	/* 81 */
388 	g(i16, xx, exitmsg_base),         /* Ext R0-1 */
389 	/* 83 */
390 	g(FT_CFG, xx, PURE_GRAMMAR),   /* Ext R1-3 */
391 	g(bb, xx, TWO_CYCLE),          /* Ext R1-4 */
392 	g(bb, xx, PURE_AFTER),         /* Ext R1-5 */
393 	g(i16, xx, min_ver),           /* Ext R1-5 */
394 	g(FT_BYTE, xx, font_status),   /* Ext R1-5 */
395 	g(i16, xx, num_rflags), g(i16, xx, num_nflags), g(i16, xx, num_cflags), /* Ext R1-7 */
396 	g(i16, xx, num_rprops), g(i16, xx, num_nprops), g(i16, xx, num_cprops), /* Ext R1-7 */
397 	endrec
398 };
399 
400 static file_info fi_room[] = {
401 	dptype, /* help */
402 	dptype, /* desc */
403 	dptype, /* special */
404 	r(FT_STR, xx, room_rec, name),
405 	r(FT_INT32, xx, room_rec, flag_noun_bits),
406 	r(FT_INT32, xx, room_rec, PIX_bits),
407 	r(FT_SLIST, xx, room_rec, replacing_word),
408 	r(FT_WORD, xx, room_rec, replace_word),
409 	r(FT_WORD, xx, room_rec, autoverb),
410 	r(FT_PATHARRAY, xx, room_rec, path),
411 	r(FT_INT16, xx, room_rec, key),
412 	r(FT_INT16, xx, room_rec, points),
413 	r(FT_INT16, xx, room_rec, light),
414 	r(FT_INT16, xx, room_rec, pict),
415 	r(FT_INT16, xx, room_rec, initdesc),
416 	r(bb, xx, room_rec, seen), r(bb, xx, room_rec, locked_door),
417 	r(bb, xx, room_rec, end), r(bb, xx, room_rec, win), r(bb, xx, room_rec, killplayer),
418 	r(bb, xx, room_rec, unused),    /* Ext R1-2: Can add here since rbool */
419 	r(FT_INT16, xx, room_rec, oclass), /* Ext R1-1 */
420 	endrec
421 };
422 
423 static file_info fi_noun[] = {
424 	dptype, /* Noun */
425 	dptype, /* Text */
426 	dptype, dptype, dptype, dptype, /* Turn, push, pull, play */
427 	r(FT_STR, xx, noun_rec, shortdesc),
428 	r(FT_STR, xx, noun_rec, position),
429 	r(FT_SLIST, xx, noun_rec, syns),
430 	r(FT_WORD, xx, noun_rec, name),  r(FT_WORD, xx, noun_rec, adj),
431 	/* r(FT_WORD,xx,noun_rec,pos_prep),  r(FT_WORD,xx,noun_rec,pos_name),*/
432 	r(FT_INT16, xx, noun_rec, nearby_noun),
433 	r(FT_INT16, xx, noun_rec, num_shots), r(FT_INT16, xx, noun_rec, points),
434 	r(FT_INT16, xx, noun_rec, weight), r(FT_INT16, xx, noun_rec, size),
435 	r(FT_INT16, xx, noun_rec, key),
436 	r(FT_INT16, xx, noun_rec, initdesc), r(FT_INT16, xx, noun_rec, pict),
437 	r(FT_INT16, xx, noun_rec, location),
438 	r(bb, xx, noun_rec, plural),
439 	r(bb, xx, noun_rec, something_pos_near_noun),
440 	r(bb, xx, noun_rec, has_syns),
441 	r(bb, xx, noun_rec, pushable),  r(bb, xx, noun_rec, pullable),
442 	r(bb, xx, noun_rec, turnable),  r(bb, xx, noun_rec, playable),
443 	r(bb, xx, noun_rec, readable),  r(bb, xx, noun_rec, on),
444 	r(bb, xx, noun_rec, closable),  r(bb, xx, noun_rec, open),
445 	r(bb, xx, noun_rec, lockable),  r(bb, xx, noun_rec, locked),
446 	r(bb, xx, noun_rec, edible),  r(bb, xx, noun_rec, wearable),
447 	r(bb, xx, noun_rec, drinkable),  r(bb, xx, noun_rec, poisonous),
448 	r(bb, xx, noun_rec, movable),  r(bb, xx, noun_rec, light),
449 	r(bb, xx, noun_rec, shootable),  r(bb, xx, noun_rec, win),
450 	r(bb, xx, noun_rec, unused),  /* Ext R1-2: Can add here since packed rbool*/
451 	r(bb, xx, noun_rec, isglobal), /* Ext R1-4: ditto (&room for 1 more). */
452 	r(FT_WORD, xx, noun_rec, related_name), /* Ext R0-1 */
453 	r(FT_INT16, xx, noun_rec, oclass), /* Ext R1-1 */
454 	r(FT_INT16, xx, noun_rec, flagnum), /* Ext R1-4 */
455 	r(bb, xx, noun_rec, seen),       /* Ext R1-6 */
456 	r(bb, xx, noun_rec, proper),     /* Ext R2-1 */
457 	endrec
458 };
459 
460 static file_info fi_creat[] = {
461 	dptype,  /* Creature */
462 	dptype, dptype,  /* Talk, ask */
463 	r(FT_STR, xx, creat_rec, shortdesc),
464 	r(FT_SLIST, xx, creat_rec, syns),
465 	r(FT_WORD, xx, creat_rec, name), r(FT_WORD, xx, creat_rec, adj),
466 	r(FT_INT16, xx, creat_rec, location),
467 	r(FT_INT16, xx, creat_rec, weapon), r(FT_INT16, xx, creat_rec, points),
468 	r(FT_INT16, xx, creat_rec, counter), r(FT_INT16, xx, creat_rec, threshold),
469 	r(FT_INT16, xx, creat_rec, timethresh), r(FT_INT16, xx, creat_rec, timecounter),
470 	r(FT_INT16, xx, creat_rec, pict), r(FT_INT16, xx, creat_rec, initdesc),
471 	r(bb, xx, creat_rec, has_syns), r(bb, xx, creat_rec, groupmemb),
472 	r(bb, xx, creat_rec, hostile),
473 	r(bb, xx, creat_rec, unused),    /* Ext R1-2: Can add since packed rbool */
474 	r(bb, xx, creat_rec, isglobal),  /* Ext R1-4: ditto (& space for 3 more) */
475 	r(FT_BYTE, xx, creat_rec, gender),
476 	r(FT_INT16, xx, creat_rec, oclass), /* Ext R1-1 */
477 	r(FT_INT16, xx, creat_rec, flagnum), /* Ext R1-4 */
478 	r(bb, xx, creat_rec, seen),       /* Ext R1-6 */
479 	r(bb, xx, creat_rec, proper),     /* Ext R2-1 */
480 	endrec
481 };
482 
483 static file_info fi_cmdhead[] = {
484 	{FT_CMDPTR, DT_CMDPTR, NULL, 0},
485 	r(FT_INT16, xx, cmd_rec, actor),
486 	r(FT_WORD, xx, cmd_rec, verbcmd), r(FT_WORD, xx, cmd_rec, nouncmd),
487 	r(FT_WORD, xx, cmd_rec, objcmd), r(FT_WORD, xx, cmd_rec, prep),
488 	r(FT_INT16, xx, cmd_rec, cmdsize),
489 	r(FT_WORD, xx, cmd_rec, noun_adj), r(FT_WORD, xx, cmd_rec, obj_adj), /* Ext R0-1*/
490 	r(FT_INT16, xx, cmd_rec, noun_obj), /* Ext R2-2 */
491 	r(FT_INT16, xx, cmd_rec, obj_obj), /* Ext R2-2 */
492 	endrec
493 };
494 
495 static file_info fi_verbentry[] = {
496 	r(FT_WORD, xx, verbentry_rec, verb),
497 	r(FT_WORD, xx, verbentry_rec, prep),
498 	r(FT_INT16, xx, verbentry_rec, objnum),
499 	endrec
500 };
501 
502 
503 static file_info fi_descptr[] = {
504 	r(FT_INT32, xx, descr_ptr, start),
505 	r(FT_INT32, xx, descr_ptr, size),
506 	endrec
507 };
508 
509 static file_info fi_tline[] = {
510 	{FT_TLINE, xx, NULL, 0},
511 	endrec
512 };
513 
514 static file_info fi_attrrec[] = { /* Ext R1-R7 */
515 	r(FT_INT32, xx, attrdef_rec, r),
516 	r(FT_INT32, xx, attrdef_rec, n),
517 	r(FT_INT32, xx, attrdef_rec, c),
518 	r(FT_BYTE, xx, attrdef_rec, rbit),
519 	r(FT_BYTE, xx, attrdef_rec, nbit),
520 	r(FT_BYTE, xx, attrdef_rec, cbit),
521 	r(FT_STR, xx, attrdef_rec, ystr), /* Ext R2-R2 */
522 	r(FT_STR, xx, attrdef_rec, nstr), /* Ext R2-R2 */
523 	endrec
524 };
525 
526 static file_info fi_proprec[] = {   /* Ext R1-R7 */
527 	r(FT_INT32, xx, propdef_rec, r),
528 	r(FT_INT32, xx, propdef_rec, n),
529 	r(FT_INT32, xx, propdef_rec, c),
530 	r(FT_INT32, xx, propdef_rec, str_cnt), /* Ext R2-R2 */
531 	r(FT_INT32, xx, propdef_rec, str_list), /* Ext R2-R2 */
532 	endrec
533 };
534 
535 static file_info fi_varrec[] = {        /* Ext R2-R2 */
536 	r(FT_INT32, xx, vardef_rec, str_cnt),
537 	r(FT_INT32, xx, vardef_rec, str_list),
538 	endrec
539 };
540 
541 static file_info fi_flagrec[] = { /* Ext R2-R2 */
542 	r(FT_STR, xx, flagdef_rec, ystr),
543 	r(FT_STR, xx, flagdef_rec, nstr),
544 	endrec
545 };
546 
547 #undef g
548 #undef r
549 #undef xx
550 #undef u16
551 #undef u32
552 #undef bb
553 #undef i16
554 #undef dptype
555 
set_endrec(file_info * fi,int index)556 static void set_endrec(file_info *fi, int index) {
557 	fi[index].ftype = FT_END;
558 	fi[index].dtype = 0;
559 	fi[index].ptr = NULL;
560 	fi[index].offset = 0;
561 }
562 
563 /* ------------------------------------------------------------- */
564 
565 /* If <to_intern> is true, convert "0" string ptrs to "yes/no" ptrs.
566    If it is false, convert the other way. */
567 /* This is done for the sake of downward compatibility.
568    It *does* mean that the first string in static_str cannot
569    be an attribute's yes/no string. */
570 
571 /* "0" pointers in this case will actually be equal to static_str
572    (since that is the base point for all pointers to static strings.) */
573 
574 const char base_yesstr[] = "yes";
575 const char base_nostr[] = "no";
576 
conv_fstr(const char ** s,rbool yes,rbool to_intern)577 static void conv_fstr(const char **s, rbool yes, rbool to_intern) {
578 	if (to_intern) {  /* Convert to internal form */
579 		assert(*s != NULL);
580 		if (*s == static_str) *s = yes ? base_yesstr : base_nostr;
581 	} else { /* convert to external form */
582 		if (*s == NULL || *s == base_yesstr || *s == base_nostr)
583 			*s = static_str;
584 	}
585 }
586 
fix_objflag_str(rbool to_intern)587 static void fix_objflag_str(rbool to_intern) {
588 	int i;
589 	for (i = 0; i < oflag_cnt; i++) {
590 		conv_fstr(&attrtable[i].ystr, 1, to_intern);
591 		conv_fstr(&attrtable[i].nstr, 0, to_intern);
592 	}
593 	if (flagtable)
594 		for (i = 0; i <= FLAG_NUM; i++) {
595 			conv_fstr(&flagtable[i].ystr, 1, to_intern);
596 			conv_fstr(&flagtable[i].nstr, 0, to_intern);
597 		}
598 }
599 
600 /* ------------------------------------------------------------- */
601 /*        AGX Reading Code                                       */
602 /* ------------------------------------------------------------- */
603 
604 
605 static long descr_ofs;
606 
agx_close_descr(void)607 void agx_close_descr(void) {
608 	if (mem_descr != NULL)
609 		rfree(mem_descr);
610 	else if (descr_ofs != -1)
611 		buffclose(); /* This closes the whole AGX file */
612 }
613 
agx_read_descr(long start,long size)614 descr_line *agx_read_descr(long start, long size) {
615 	long i, line, len;
616 	descr_line *txt;
617 	char *buff;
618 
619 	if (size <= 0) return NULL;
620 
621 	if (mem_descr == NULL && descr_ofs != -1)
622 		buff = (char *)read_recblock(NULL, FT_CHAR, size,
623 		                             descr_ofs + start, size * ft_leng[FT_CHAR]);
624 	else
625 		buff = mem_descr + start;
626 
627 	len = 0;
628 	for (i = 0; i < size; i++) /* Count the number of lines */
629 		if (buff[i] == 0) len++;
630 	txt = (descr_line *)rmalloc(sizeof(descr_line) * (len + 1));
631 	txt[0] = buff;
632 	i = 0;
633 	for (line = 1; line < len;) /* Determine where each of the lines is */
634 		if (buff[i++] == 0)
635 			txt[line++] = buff + i;
636 	txt[len] = NULL; /* Mark the end of the array */
637 	return txt;
638 }
639 
640 
641 /* We need to read in command text and use cmd_rec[] values to
642    rebuild command[].data. We are guaranteed that cmd_rec[] is in
643    increasing order */
644 
read_command(long cmdcnt,long cmdofs,rbool diag)645 static void read_command(long cmdcnt, long cmdofs, rbool diag) {
646 	long i;
647 
648 	for (i = 0; i < last_cmd; i++) {
649 		command[i].data = (integer *)rmalloc(sizeof(integer) * command[i].cmdsize);
650 		read_recblock(command[i].data, FT_INT16, command[i].cmdsize,
651 		              cmdofs + 2 * cmd_ptr[i], 2 * command[i].cmdsize);
652 	}
653 	if (!diag) rfree(cmd_ptr);
654 }
655 
656 
657 /* Correct for differences between old_base_verb and BASE_VERB.
658    This means that the interpreter's set of built-inv verbs has changed
659    since the file was created. */
correct_synlist(void)660 static void correct_synlist(void) {
661 	int i;
662 	if (BASE_VERB == old_base_verb) return; /* Nothing needs to be done */
663 
664 	/* Need to move everything >= old_base_verb to BASE_VERB */
665 	memmove(synlist + BASE_VERB, synlist + old_base_verb,
666 	        sizeof(slist) * (DVERB + MAX_SUB));
667 
668 	if (BASE_VERB < old_base_verb) /* We've _lost_ verbs */
669 		agtwarn("Missing built-in verbs.", 0);
670 
671 	/* Now we need to give the "new" verbs empty synonym lists */
672 	for (i = old_base_verb; i < BASE_VERB; i++)
673 		synlist[i] = synptr;
674 	addsyn(-1);
675 }
676 
677 
678 
set_roomdesc(file_info fi[])679 static void set_roomdesc(file_info fi[]) {
680 	fi[0].ptr = help_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxroom - first_room + 1));
681 	fi[1].ptr = room_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxroom - first_room + 1));
682 	fi[2].ptr = special_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxroom - first_room + 1));
683 }
684 
wset_roomdesc(file_info fi[])685 static void wset_roomdesc(file_info fi[]) {
686 	fi[0].ptr = help_ptr;
687 	fi[1].ptr = room_ptr;
688 	fi[2].ptr = special_ptr;
689 }
690 
set_noundesc(file_info * fi)691 static void set_noundesc(file_info *fi) {
692 	fi[0].ptr = noun_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
693 	fi[1].ptr = text_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
694 	fi[2].ptr = turn_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
695 	fi[3].ptr = push_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
696 	fi[4].ptr = pull_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
697 	fi[5].ptr = play_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
698 }
699 
wset_noundesc(file_info * fi)700 static void wset_noundesc(file_info *fi) {
701 	fi[0].ptr = noun_ptr;
702 	fi[1].ptr = text_ptr;
703 	fi[2].ptr = turn_ptr;
704 	fi[3].ptr = push_ptr;
705 	fi[4].ptr = pull_ptr;
706 	fi[5].ptr = play_ptr;
707 }
708 
set_creatdesc(file_info * fi)709 static void set_creatdesc(file_info *fi) {
710 	fi[0].ptr = creat_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxcreat - first_creat + 1));
711 	fi[1].ptr = talk_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxcreat - first_creat + 1));
712 	fi[2].ptr = ask_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxcreat - first_creat + 1));
713 }
714 
wset_creatdesc(file_info * fi)715 static void wset_creatdesc(file_info *fi) {
716 	fi[0].ptr = creat_ptr;
717 	fi[1].ptr = talk_ptr;
718 	fi[2].ptr = ask_ptr;
719 }
720 
set_cmdptr(file_info * fi)721 static void set_cmdptr(file_info *fi) {
722 	fi[0].ptr = cmd_ptr = (long *)rmalloc(sizeof(long) * last_cmd);
723 }
724 
wset_cmdptr(file_info * fi)725 static void wset_cmdptr(file_info *fi) {
726 	fi[0].ptr = cmd_ptr;
727 }
728 
729 
730 typedef struct {  /* Entries in the index header of the AGX file */
731 	uint32 file_offset;
732 	uint32 blocksize;
733 	uint32 numrec;
734 	uint32 recsize;
735 } index_rec;
736 
737 static file_info fi_index[] = {
738 	{FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, file_offset)},
739 	{FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, blocksize)},
740 	{FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, numrec)},
741 	{FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, recsize)},
742 	endrec
743 };
744 
745 
746 /*
747    uint32 File ID ['....' 4 bytes]
748    byte Version owner: 'R'
749    byte Version 0
750    byte Extension owner 'R'
751    byte Extension 0
752    */
753 
754 struct file_head_rec  {
755 	uint32 fileid;
756 	uint32 res1; /* Reserved for future use */
757 	uchar res2;
758 	uchar eol_chk1;  /* Catch non-binary upload errors */
759 	uchar eol_chk2;
760 	uchar ver_own;
761 	uchar version;
762 	uchar ext_own;
763 	uchar extnum;
764 	uchar fallback_ext;  /* For non-'R' extensions, this is the 'R' extension
765 			  to fall back to. */
766 };
767 
768 static file_info fi_header[] = {
769 	{FT_UINT32, DT_LONG, NULL, offsetof(file_head_rec, fileid)}, /* File ID */
770 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, ver_own)}, /* Owner */
771 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, version)}, /* Version */
772 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, ext_own)}, /*Ext owner*/
773 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, extnum)}, /* Ext vers */
774 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, eol_chk1)},
775 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, eol_chk2)},
776 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, fallback_ext)},
777 	{FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, res2)},
778 	{FT_UINT32, DT_DEFAULT, NULL, offsetof(file_head_rec, res1)},
779 	endrec
780 };
781 
782 static const char *block_name[AGX_NUMBLOCK] = {
783 	"Index", "Game Info", "Room(DA2)", "Noun(DA3)", "Creature(DA4)",
784 	"Command Header(DA5)", "Error Message(STD)", "Message",
785 	"Question", "Answer", "User String", "Description Text(D$$)",
786 	"Command Tokens(DA6)", "Static String", "Subroutine ID",
787 	"Verb Synonym", "RoomPIX", "Global Noun", "Flag Noun", "Word Lists(Syntbl)",
788 	"Dictionary Text", "Dictionary Index", "OPT",
789 	"Picture Filename", "RoomPIX Filename", "Font Filename", "Sound Filename",
790 	"Menu(VOC)", "Multi-word Verb", "Preposition", "ObjFlag", "ObjProp",
791 	"Attrtable", "PropTable", "PropStr", "Itemized Variables",
792 	"Itemized Flags"
793 };
794 
795 
796 /* Return 0 on failure, 1 on success */
read_agx(fc_type fc,rbool diag)797 int read_agx(fc_type fc, rbool diag) {
798 	file_head_rec filehead;
799 	unsigned long fsize;
800 	index_rec *index;
801 	long i;
802 	int index_recsize;
803 	int index_start;
804 
805 	agx_file = 1;
806 	fsize = buffopen(fc, fAGX, 16, NULL, 1);
807 	if (fsize == 0) {
808 		agx_file = 0;
809 		return 0;
810 	}
811 
812 	/* Read header */
813 	read_recarray(&filehead, sizeof(file_head_rec), 1, fi_header,
814 	              "File Header", 0, compute_recsize(fi_header));
815 	if (filehead.fileid != AGT_FILE_SIG) {
816 		buffclose();
817 		return 0;
818 	}
819 	if (DIAG) {
820 		rprintf("AGX file format");
821 		if (isprint(filehead.ver_own) && isprint(filehead.ext_own))
822 			rprintf("   Version:%c%d\tExtension:%c%d\n",
823 			        filehead.ver_own, filehead.version,
824 			        filehead.ext_own, filehead.extnum);
825 		else
826 			rprintf("   Version:%d:%d\tExtension:%d:%d\n",
827 			        filehead.ver_own, filehead.version,
828 			        filehead.ext_own, filehead.extnum);
829 	}
830 	if (filehead.ver_own != 'R' || filehead.version > 2) {
831 		rprintf("Unsupported AGX file version.\n");
832 		rprintf("  Either the file is corrupted or or you need a more recent "
833 		        "version of AGiliTy.\n");
834 		rprintf("\n");
835 		fatal("Can't read AGX file.");
836 	}
837 
838 	index_recsize = compute_recsize(fi_index);
839 	if (filehead.version == 0) {
840 		if (debug_da1)
841 			rprintf("[AGX version 0: obsolete.]\n");
842 		index_recsize += 8; /* Extra junk block in version 0. */
843 		index_start = 8;
844 	} else {
845 		index_start = 16;
846 		if (filehead.eol_chk1 != '\n' || filehead.eol_chk2 != '\r')
847 			fatal("File apparently downloaded as non-binary file.");
848 	}
849 	if (filehead.ext_own != 'R'
850 	        || (filehead.version == 0 && filehead.extnum > 1)
851 	        || (filehead.version == 1 && filehead.extnum > 7)
852 	        || (filehead.version == 2 && filehead.extnum > 2))
853 		agtwarn("Unrecognized extension to AGX file format.", 0);
854 	if (filehead.ext_own != 'R') { /* Assume lowest common denomenator */
855 		if (filehead.version < 2)
856 			fatal("Extensions of AGX beta versions not supported.");
857 		if (filehead.fallback_ext < 1) filehead.fallback_ext = 1;
858 	}
859 
860 	/* Now read master index */
861 	/* This assumes that the file is long enough to absorb any
862 	   'extra' blocks we read in in early versions with fewer blocks. */
863 	/* (Right now, this must be true: the next block alone is big enough) */
864 	index = (index_rec *)read_recarray(NULL, sizeof(index_rec), AGX_NUMBLOCK,
865 	                                   fi_index, "File Index", index_start,
866 	                                   index_recsize * AGX_NUMBLOCK);
867 
868 	/* Zero index entries for any blocks that are beyond the bounds of the
869 	   file's index */
870 	if (AGX_NUMBLOCK > index[0].numrec)
871 		memset(index + index[0].numrec, 0,
872 		       (AGX_NUMBLOCK - index[0].numrec)*sizeof(index_rec));
873 
874 	if (DIAG) {
875 		rprintf("\n");
876 		rprintf("File Index:\n");
877 		rprintf("    Offset   Size   NumRec  RecSz\n");
878 		rprintf("    ------  ------  ------  ------\n");
879 		for (i = 0; i < AGX_NUMBLOCK; i++)
880 			rprintf("%2ld: %6d  %6d  %6d  %6d   %s\n", i,
881 			        index[i].file_offset, index[i].blocksize,
882 			        index[i].numrec, index[i].recsize, block_name[i]);
883 	}
884 	if ((int)index[0].file_offset != index_start)
885 		fatal("File header corrupted.");
886 
887 	for (i = 0; i < AGX_NUMBLOCK; i++) { /* Error checking */
888 #ifdef DEBUG_AGX
889 		rprintf("  Verifying block %d...\n", i);
890 #endif
891 		if (index[i].recsize * index[i].numrec != index[i].blocksize)
892 			fatal("File header corrupted.");
893 		if (index[i].file_offset + index[i].blocksize > fsize)
894 			fatal("File index points past end of file.");
895 	}
896 
897 	/* Check for mandatory fields */
898 	if (!index[0].numrec    /* File index */
899 	        || !index[1].numrec /* Game header */
900 	        || !index[13].numrec /* Static string block */
901 	        || !index[15].numrec /* Synonym list */
902 	        || !index[19].numrec /* Syntbl */
903 	        || !index[20].numrec /* Dictionary text */
904 	        || !index[21].numrec /* Dictionary index */
905 	   )
906 		fatal("AGX file missing mandatory block.");
907 
908 
909 	read_globalrec(fi_gameinfo, "Game Info", index[1].file_offset,
910 	               index[1].blocksize);
911 	if (filehead.version == 0 && filehead.extnum == 0) {
912 		exitmsg_base = 1000;
913 		if (aver >= AGT15)
914 			box_title = 1;
915 	}
916 	if (index[1].blocksize == 83 && filehead.version == 1 && filehead.extnum >= 5) {
917 		/* Detect 0.8-compatibility hack */
918 		filehead.extnum = 2;
919 	}
920 	if (filehead.version == 0 || (filehead.version == 1 && filehead.extnum < 5)) {
921 		if (aver >= AGT182 && aver <= AGT18MAX) {
922 			if (filehead.extnum < 4) TWO_CYCLE = 1;
923 		} else
924 			PURE_AFTER = 1;
925 	}
926 
927 	/* Need to read in ss_array before rooms/nouns/creatures */
928 	ss_size = ss_end = index[13].numrec;
929 	static_str = (char *)read_recblock(NULL, FT_CHAR,
930 	                                   index[13].numrec, index[13].file_offset,
931 	                                   index[13].blocksize);
932 
933 	synptr = syntbl_size = index[19].numrec;
934 	syntbl = (word *)read_recblock(NULL, FT_WORD, index[19].numrec, index[19].file_offset,
935 	                               index[19].blocksize);
936 
937 	maxroom = first_room + index[2].numrec - 1;
938 	set_roomdesc(fi_room);
939 	room = (room_rec *)read_recarray(NULL, sizeof(room_rec), index[2].numrec,
940 	                                 fi_room, "Room", index[2].file_offset, index[2].blocksize);
941 
942 	maxnoun = first_noun + index[3].numrec - 1;
943 	set_noundesc(fi_noun);
944 	noun = (noun_rec *)read_recarray(NULL, sizeof(noun_rec), index[3].numrec,
945 	                                 fi_noun, "Noun", index[3].file_offset, index[3].blocksize);
946 
947 	last_obj = maxcreat = first_creat + index[4].numrec - 1;
948 	set_creatdesc(fi_creat);
949 	creature = (creat_rec *)read_recarray(NULL, sizeof(creat_rec), index[4].numrec,
950 	                                      fi_creat, "Creature", index[4].file_offset,
951 	                                      index[4].blocksize);
952 
953 	last_cmd = index[5].numrec;
954 	set_cmdptr(fi_cmdhead);
955 	command = (cmd_rec *)read_recarray(NULL, sizeof(cmd_rec), index[5].numrec,
956 	                                   fi_cmdhead, "Metacommand", index[5].file_offset,
957 	                                   index[5].blocksize);
958 	if (filehead.ext_own != 'R' && filehead.fallback_ext <= 1) {
959 		for (i = 0; i < last_cmd; i++)
960 			command[i].noun_obj = command[i].obj_obj = 0;
961 	}
962 
963 	NUM_ERR = index[6].numrec;
964 	err_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[6].numrec,
965 	                                     fi_descptr, "Error Message", index[6].file_offset,
966 	                                     index[6].blocksize);
967 
968 	last_message = index[7].numrec;
969 	msg_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[7].numrec,
970 	                                     fi_descptr, "Message", index[7].file_offset,
971 	                                     index[7].blocksize);
972 
973 	MaxQuestion = index[8].numrec;
974 	question = answer = NULL;
975 	quest_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[8].numrec,
976 	                                       fi_descptr, "Question", index[8].file_offset,
977 	                                       index[8].blocksize);
978 	if (index[9].numrec != index[8].numrec)
979 		fatal("File corrputed: questions and answers don't match.");
980 	ans_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[9].numrec,
981 	                                     fi_descptr, "Answer", index[9].file_offset,
982 	                                     index[9].blocksize);
983 
984 	MAX_USTR = index[10].numrec;
985 	userstr = (tline *)read_recarray(NULL, sizeof(tline), index[10].numrec,
986 	                                 fi_tline, "User String", index[10].file_offset,
987 	                                 index[10].blocksize);
988 
989 	MAX_SUB = index[14].numrec;
990 	sub_name = (word *)read_recblock(NULL, FT_WORD, index[14].numrec, index[14].file_offset,
991 	                                 index[14].blocksize);
992 
993 	if (index[16].numrec > MAX_PIX) {
994 		index[16].numrec = MAX_PIX;
995 		index[16].blocksize = index[16].recsize * index[16].numrec;
996 	}
997 	maxpix = index[16].numrec;
998 	for (i = 0; i < MAX_PIX; i++) pix_name[i] = 0; /* In case there are less than
999 					   MAX_PIX names */
1000 	read_recblock(pix_name, FT_WORD, index[16].numrec, index[16].file_offset,
1001 	              index[16].blocksize);
1002 
1003 	numglobal = index[17].numrec;
1004 	globalnoun = (word *)read_recblock(NULL, FT_WORD,
1005 	                                   index[17].numrec, index[17].file_offset,
1006 	                                   index[17].blocksize);
1007 
1008 	if (index[18].numrec > MAX_FLAG_NOUN) {
1009 		index[18].numrec = MAX_FLAG_NOUN;
1010 		index[18].blocksize = index[18].recsize * index[18].numrec;
1011 	}
1012 
1013 	for (i = 0; i < MAX_FLAG_NOUN; i++) flag_noun[i] = 0;
1014 	read_recblock(flag_noun, FT_WORD, index[18].numrec, index[18].file_offset,
1015 	              index[18].blocksize);
1016 
1017 
1018 
1019 	DVERB = index[15].numrec - old_base_verb - MAX_SUB;
1020 	synlist = (slist *)read_recblock(NULL, FT_SLIST, index[15].numrec, index[15].file_offset,
1021 	                                 index[15].blocksize);
1022 	correct_synlist();
1023 
1024 	num_comb = index[28].numrec;
1025 	comblist = (slist *)read_recblock(NULL, FT_SLIST, index[28].numrec, index[28].file_offset,
1026 	                                  index[28].blocksize);
1027 
1028 	num_prep = index[29].numrec;
1029 	userprep = (slist *)read_recblock(NULL, FT_SLIST, index[29].numrec, index[29].file_offset,
1030 	                                  index[29].blocksize);
1031 
1032 	/* dicstr must be read in before dict */
1033 	dictstrsize = dictstrptr = index[20].numrec;
1034 	dictstr = (char *)read_recblock(NULL, FT_CHAR, index[20].numrec, index[20].file_offset,
1035 	                                index[20].blocksize);
1036 
1037 	dp = index[21].numrec;
1038 	dict = (char **)read_recblock(NULL, FT_DICTPTR,
1039 	                              index[21].numrec, index[21].file_offset,
1040 	                              index[21].blocksize);
1041 
1042 	have_opt = (index[22].numrec != 0);
1043 	for (i = 0; i < 14; i++) opt_data[i] = 0;
1044 	if (have_opt) {
1045 		if (index[22].numrec > 14) index[22].numrec = 14;
1046 		read_recblock(opt_data, FT_BYTE, index[22].numrec, index[22].file_offset,
1047 		              index[22].blocksize);
1048 	}
1049 
1050 	maxpict = index[23].numrec;
1051 	pictlist = (filename *)read_recblock(NULL, FT_STR, index[23].numrec, index[23].file_offset,
1052 	                                     index[23].blocksize);
1053 	maxpix = index[24].numrec;
1054 	pixlist = (filename *)read_recblock(NULL, FT_STR, index[24].numrec, index[24].file_offset,
1055 	                                    index[24].blocksize);
1056 	maxfont = index[25].numrec;
1057 	fontlist = (filename *)read_recblock(NULL, FT_STR, index[25].numrec, index[25].file_offset,
1058 	                                     index[25].blocksize);
1059 	maxsong = index[26].numrec;
1060 	songlist = (filename *)read_recblock(NULL, FT_STR, index[26].numrec, index[26].file_offset,
1061 	                                     index[26].blocksize);
1062 
1063 	vm_size = index[27].numrec;
1064 	verbinfo = (verbentry_rec *)read_recarray(NULL, sizeof(verbentry_rec), index[27].numrec,
1065 	           fi_verbentry, "Menu Vocabulary", index[27].file_offset,
1066 	           index[27].blocksize);
1067 
1068 	/* Check that objflag and objprop fields are of correct size */
1069 	if (index[30].numrec != (uint32)objextsize(0))
1070 		fatal("Object flag block not of the correct size.");
1071 
1072 	if (index[31].numrec != (uint32)objextsize(1))
1073 		fatal("Object property block not of the correct size.");
1074 
1075 	objflag = (uchar *)read_recblock(NULL, FT_BYTE, index[30].numrec, index[30].file_offset,
1076 	                                 index[30].blocksize);
1077 	objprop = (long *)read_recblock(NULL, FT_INT32, index[31].numrec, index[31].file_offset,
1078 	                                index[31].blocksize);
1079 
1080 	oflag_cnt = index[32].numrec;
1081 	attrtable = (attrdef_rec *)read_recarray(NULL, sizeof(attrdef_rec), index[32].numrec,
1082 	            fi_attrrec, "Object Flag Table",
1083 	            index[32].file_offset,
1084 	            index[32].blocksize);
1085 	/* Objflags are converted to internal form later, after
1086 	   block 36 has been read in. */
1087 
1088 	oprop_cnt = index[33].numrec;
1089 	proptable = (propdef_rec *)read_recarray(NULL, sizeof(propdef_rec), index[33].numrec,
1090 	            fi_proprec, "Object Property Table",
1091 	            index[33].file_offset,
1092 	            index[33].blocksize);
1093 
1094 	if (filehead.ext_own != 'R' && filehead.fallback_ext <= 1) {
1095 		/* Non-standard extension */
1096 //    int i;
1097 		for (i = 0; i < oflag_cnt; i++) /* These are converted later */
1098 			attrtable[i].ystr = NULL;
1099 		attrtable[i].nstr = NULL;
1100 		for (i = 0; i < oprop_cnt; i++)
1101 			proptable[i].str_cnt = 0;
1102 		propstr_size = 0;
1103 		propstr = NULL;
1104 		vartable = NULL;
1105 		flagtable = NULL;
1106 	} else { /* Normal case */
1107 		propstr_size = index[34].numrec;
1108 		propstr = (const char **)read_recblock(NULL, FT_STR, index[34].numrec,
1109 		                                       index[34].file_offset, index[34].blocksize);
1110 
1111 		if (index[35].numrec && index[35].numrec != (uint32)VAR_NUM + 1)
1112 			fatal("AGX file corrupted: variable itemization table size mismatch.");
1113 		vartable = (vardef_rec *)read_recarray(NULL, sizeof(vardef_rec), index[35].numrec,
1114 		                                       fi_varrec, "Variable Itemization Table",
1115 		                                       index[35].file_offset,
1116 		                                       index[35].blocksize);
1117 
1118 		if (index[36].numrec && index[36].numrec != (uint32)FLAG_NUM + 1)
1119 			fatal("AGX file corrupted: flag itemization table size mismatch.");
1120 		flagtable = (flagdef_rec *)read_recarray(NULL, sizeof(flagdef_rec), index[36].numrec,
1121 		            fi_flagrec, "Flag Itemization Table",
1122 		            index[36].file_offset,
1123 		            index[36].blocksize);
1124 	}
1125 
1126 	fix_objflag_str(1); /* Convert flags and objflags to internal form */
1127 
1128 
1129 	/* Block 12: Command text */
1130 	read_command(index[12].numrec, index[12].file_offset, diag);
1131 
1132 	/* Block 11 is description block; it doesn't get read in by
1133 	   agxread() but during play */
1134 	if ((long)index[11].blocksize <= descr_maxmem) {
1135 		/* ... if we decided to load descriptions into memory */
1136 		mem_descr = (char *)read_recblock(NULL, FT_CHAR, index[11].numrec,
1137 		                                  index[11].file_offset,
1138 		                                  index[11].blocksize);
1139 		buffclose(); /* Don't need to keep it open */
1140 		descr_ofs = -1;
1141 	} else {
1142 		descr_ofs = index[11].file_offset;
1143 		mem_descr = NULL;
1144 	}
1145 	reinit_dict();
1146 	return 1;
1147 }
1148 
1149 
1150 
1151 
1152 /* ------------------------------------------------------------- */
1153 /*        AGX Writing Code                                       */
1154 /* ------------------------------------------------------------- */
1155 
1156 static index_rec *gindex;
1157 
1158 
1159 /* This patches the block descriptions to create AGiliTy-0.8
1160    compatible files. This is just a quick hack to solve a short-term
1161    problem. */
patch_08(void)1162 void patch_08(void) {
1163 	set_endrec(fi_gameinfo, 48); /* Should give size of 83 */
1164 	set_endrec(fi_noun, 45);
1165 	set_endrec(fi_creat, 23);
1166 }
1167 
1168 
1169 /* This writes the file header; it needs to be called near the
1170    end */
write_header(void)1171 void write_header(void) {
1172 	int i;
1173 	rbool simple;
1174 	file_head_rec filehead;
1175 
1176 	filehead.fileid = AGT_FILE_SIG;
1177 	filehead.ver_own = filehead.ext_own = 'R';
1178 	/* The following will be converted to 1-7 if advanced features aren't
1179 	   being used. */
1180 	filehead.version = 2;
1181 	filehead.extnum = 2;
1182 	filehead.fallback_ext = 2; /* 'R' extension to fall back to;
1183 				  only meaningful if ext_own is *not* 'R' */
1184 	filehead.eol_chk1 = '\n';
1185 	filehead.eol_chk2 = '\r';
1186 	filehead.res1 = 0;
1187 	filehead.res2 = 0;
1188 
1189 	/* This automatically patches the block descriptions to create
1190 	   pre-AGiliTy-0.8.8 compatible files.  If it can't (because the
1191 	   file uses 0.8.8+ features) then it leaves the version at 2;
1192 	   otherwise the version is reduced to 1. */
1193 	/* The files thus created are actually hybrid files-- they
1194 	   have some 0.8.8+ features, just not the ones that might
1195 	   break pre-0.8.8 interpreters. */
1196 	simple = 1;
1197 	for (i = 30; i < AGX_NUMBLOCK; i++)
1198 		if (gindex[i].numrec != 0) simple = 0;
1199 	if (simple) {
1200 		gindex[0].numrec = 30; /* 0.8.7 compatibility */
1201 		gindex[0].blocksize = gindex[0].recsize * gindex[0].numrec;
1202 		filehead.version = 1;
1203 		filehead.extnum = 7;
1204 	}
1205 	write_recarray(&filehead, sizeof(file_head_rec), 1, fi_header, 0);
1206 }
1207 
1208 
agx_compute_index(void)1209 static void agx_compute_index(void)
1210 /* This computes the blocksize and offset values for all blocks */
1211 {
1212 	int i;
1213 
1214 	for (i = 0; i < AGX_NUMBLOCK; i++)
1215 		gindex[i].blocksize = gindex[i].recsize * gindex[i].numrec;
1216 	gindex[0].file_offset = 16;
1217 	gindex[11].file_offset = gindex[0].file_offset + gindex[0].blocksize;
1218 	gindex[12].file_offset = gindex[11].file_offset + gindex[11].blocksize;
1219 	gindex[1].file_offset = gindex[12].file_offset + gindex[12].blocksize;
1220 	for (i = 2; i <= AGX_NUMBLOCK - 1; i++)
1221 		if (i == 13)
1222 			gindex[13].file_offset = gindex[10].file_offset + gindex[10].blocksize;
1223 		else if (i != 11 && i != 12)
1224 			gindex[i].file_offset = gindex[i - 1].file_offset + gindex[i - 1].blocksize;
1225 }
1226 
1227 
1228 /* Create the preliminary gindex for the new file and set it up so we can
1229  write descriptions to the new file */
agx_create(fc_type fc)1230 void agx_create(fc_type fc) {
1231 	int i;
1232 
1233 	bw_open(fc, fAGX);
1234 	gindex = (index_rec *)rmalloc(sizeof(index_rec) * AGX_NUMBLOCK);
1235 
1236 	gindex[0].numrec = AGX_NUMBLOCK;
1237 	for (i = 1; i < AGX_NUMBLOCK; i++) /* Initialize the rest to 0 */
1238 		gindex[i].numrec = 0;
1239 
1240 	/* This writes random data to the file; their only purpose
1241 	   is to prevent problems with seeking beyond the end of file */
1242 	write_recarray(NULL, sizeof(file_head_rec), 1, fi_header, 0);
1243 	write_recarray(NULL, sizeof(index_rec), AGX_NUMBLOCK, fi_index, 16);
1244 
1245 	old_base_verb = BASE_VERB; /* This will be constant for any given version
1246 				  of the interpreter, but may change across
1247 				  versions of the interpreter */
1248 	/* Set record sizes */
1249 	gindex[0].recsize = compute_recsize(fi_index);
1250 	gindex[1].recsize = compute_recsize(fi_gameinfo);
1251 	gindex[2].recsize = compute_recsize(fi_room);
1252 	gindex[3].recsize = compute_recsize(fi_noun);
1253 	gindex[4].recsize = compute_recsize(fi_creat);
1254 	gindex[5].recsize = compute_recsize(fi_cmdhead);
1255 	gindex[6].recsize = gindex[7].recsize = gindex[8].recsize =
1256 	        gindex[9].recsize = compute_recsize(fi_descptr);
1257 	gindex[10].recsize = ft_leng[FT_TLINE];
1258 	gindex[11].recsize = ft_leng[FT_CHAR];
1259 	gindex[12].recsize = ft_leng[FT_INT16];
1260 	gindex[13].recsize = gindex[20].recsize = ft_leng[FT_CHAR];
1261 	gindex[14].recsize = gindex[16].recsize = gindex[17].recsize =
1262 	                         gindex[18].recsize = ft_leng[FT_WORD];
1263 	gindex[15].recsize = ft_leng[FT_SLIST];
1264 	gindex[19].recsize = ft_leng[FT_WORD];
1265 	gindex[21].recsize = ft_leng[FT_DICTPTR];
1266 	gindex[22].recsize = ft_leng[FT_BYTE];
1267 	gindex[23].recsize = gindex[24].recsize = gindex[25].recsize =
1268 	                         gindex[26].recsize = ft_leng[FT_STR];
1269 	gindex[27].recsize = compute_recsize(fi_verbentry);
1270 	gindex[28].recsize = ft_leng[FT_SLIST];
1271 	gindex[29].recsize = ft_leng[FT_SLIST];
1272 	gindex[30].recsize = ft_leng[FT_BYTE];
1273 	gindex[31].recsize = ft_leng[FT_INT32];
1274 	gindex[32].recsize = compute_recsize(fi_attrrec);
1275 	gindex[33].recsize = compute_recsize(fi_proprec);
1276 	gindex[34].recsize = ft_leng[FT_STR];
1277 	gindex[35].recsize = compute_recsize(fi_varrec);
1278 	gindex[36].recsize = compute_recsize(fi_flagrec);
1279 
1280 	agx_compute_index(); /* Only the first 10 blocks will be correct */
1281 	/* The important thing is to get the offset of block 11, the desciption
1282 	   text block, so we can write to it. */
1283 	/* Block 11 is the description block; it doesn't get written by agxwrite()
1284 	   but by its own routines. */
1285 }
1286 
1287 
agx_finish_index(void)1288 static void agx_finish_index(void) {
1289 	/* Still have 11, 27-29 */
1290 	/* Block 12 is taken care of elsewhere (in write_command) */
1291 
1292 	gindex[1].numrec = 1;
1293 	gindex[2].numrec = rangefix(maxroom - first_room + 1);
1294 	gindex[3].numrec = rangefix(maxnoun - first_noun + 1);
1295 	gindex[4].numrec = rangefix(maxcreat - first_creat + 1);
1296 	gindex[5].numrec = last_cmd;
1297 	gindex[6].numrec = NUM_ERR;
1298 	gindex[7].numrec = last_message;
1299 	gindex[8].numrec = gindex[9].numrec = MaxQuestion;
1300 	if (userstr != NULL)
1301 		gindex[10].numrec = MAX_USTR;
1302 	else gindex[10].numrec = 0;
1303 	gindex[13].numrec = ss_end;
1304 	gindex[14].numrec = MAX_SUB;
1305 	gindex[15].numrec = TOTAL_VERB;
1306 	gindex[16].numrec = maxpix;
1307 	gindex[17].numrec = numglobal;
1308 	gindex[19].numrec = synptr;
1309 	gindex[20].numrec = dictstrptr;
1310 	gindex[21].numrec = dp;
1311 	gindex[23].numrec = maxpict;
1312 	gindex[24].numrec = maxpix;
1313 	gindex[25].numrec = maxfont;
1314 	gindex[26].numrec = maxsong;
1315 	gindex[27].numrec = vm_size;
1316 	gindex[28].numrec = num_comb;
1317 	gindex[29].numrec = num_prep;
1318 	gindex[30].numrec = objextsize(0);
1319 	gindex[31].numrec = objextsize(1);
1320 	gindex[32].numrec = oflag_cnt;
1321 	gindex[33].numrec = oprop_cnt;
1322 	gindex[34].numrec = propstr_size;
1323 	gindex[35].numrec = (vartable ?  VAR_NUM + 1 : 0);
1324 	gindex[36].numrec = (flagtable ? FLAG_NUM + 1 : 0);
1325 
1326 	/* These may also be zero (?) */
1327 	gindex[22].numrec = have_opt ? 14 : 0;
1328 	gindex[18].numrec = MAX_FLAG_NOUN;
1329 
1330 	agx_compute_index(); /* This time it will be complete except for
1331 			the VOC-TTL-INS blocks at the end */
1332 }
1333 
1334 
1335 
1336 /* The following routine writes a description to disk,
1337    and stores the size and length in dp */
write_descr(descr_ptr * dp_,descr_line * txt)1338 void write_descr(descr_ptr *dp_, descr_line *txt) {
1339 	long i;
1340 	long size;
1341 	char *buff, *buffptr, *src;
1342 
1343 	size = 0;
1344 	if (txt == NULL) {
1345 		dp_->start = 0;
1346 		dp_->size = 0;
1347 		return;
1348 	}
1349 
1350 	for (i = 0; txt[i] != NULL; i++) /* Compute size */
1351 		size += strlen(txt[i]) + 1; /* Remember trailing \0 */
1352 	buff = (char *)rmalloc(sizeof(char) * size);
1353 
1354 	buffptr = buff;
1355 	for (i = 0; txt[i] != NULL; i++) {
1356 		for (src = txt[i]; *src != 0; src++, buffptr++)
1357 			*buffptr = *src;
1358 		*buffptr++ = 0;
1359 	}
1360 	dp_->start = gindex[11].numrec;
1361 	dp_->size = size;
1362 	gindex[11].numrec +=
1363 	    write_recblock(buff, FT_CHAR, size,
1364 	                   gindex[11].file_offset + gindex[11].numrec);
1365 	rfree(buff);
1366 }
1367 
1368 /* Write command text to file and return number of bytes written. */
write_command(long cmdofs)1369 static long write_command(long cmdofs) {
1370 	long i, cnt;
1371 
1372 	cmd_ptr = (long *)rmalloc(sizeof(long) * last_cmd);
1373 	cnt = 0;
1374 	for (i = 0; i < last_cmd; i++) {
1375 		cmd_ptr[i] = cnt;
1376 		write_recblock(command[i].data, FT_INT16, command[i].cmdsize,
1377 		               cmdofs + 2 * cnt);
1378 		cnt += command[i].cmdsize;
1379 	}
1380 	return cnt;
1381 }
1382 
1383 
1384 
1385 
1386 /* Write the bulk of the AGX file. This requires that the descriptions,
1387    etc. have already been written */
agx_write(void)1388 void agx_write(void) {
1389 	gindex[11].blocksize = gindex[11].numrec * gindex[11].recsize;
1390 	gindex[12].file_offset = gindex[11].file_offset + gindex[11].blocksize;
1391 
1392 	gindex[12].numrec = write_command(gindex[12].file_offset);
1393 
1394 	agx_finish_index();
1395 
1396 	/* Need to write these blocks in order */
1397 
1398 	write_globalrec(fi_gameinfo, gindex[1].file_offset);
1399 
1400 	wset_roomdesc(fi_room);
1401 	write_recarray(room, sizeof(room_rec), gindex[2].numrec,
1402 	               fi_room, gindex[2].file_offset);
1403 
1404 	wset_noundesc(fi_noun);
1405 	write_recarray(noun, sizeof(noun_rec), gindex[3].numrec,
1406 	               fi_noun, gindex[3].file_offset);
1407 
1408 	wset_creatdesc(fi_creat);
1409 	write_recarray(creature, sizeof(creat_rec), gindex[4].numrec,
1410 	               fi_creat, gindex[4].file_offset);
1411 
1412 	wset_cmdptr(fi_cmdhead);
1413 	write_recarray(command, sizeof(cmd_rec), gindex[5].numrec,
1414 	               fi_cmdhead, gindex[5].file_offset);
1415 
1416 	write_recarray(err_ptr, sizeof(descr_ptr), gindex[6].numrec,
1417 	               fi_descptr, gindex[6].file_offset);
1418 	write_recarray(msg_ptr, sizeof(descr_ptr), gindex[7].numrec,
1419 	               fi_descptr, gindex[7].file_offset);
1420 	write_recarray(quest_ptr, sizeof(descr_ptr), gindex[8].numrec,
1421 	               fi_descptr, gindex[8].file_offset);
1422 	write_recarray(ans_ptr, sizeof(descr_ptr), gindex[9].numrec,
1423 	               fi_descptr, gindex[9].file_offset);
1424 
1425 	if (userstr != NULL)
1426 		write_recarray(userstr, sizeof(tline), gindex[10].numrec,
1427 		               fi_tline, gindex[10].file_offset);
1428 
1429 	write_recblock(static_str, FT_CHAR,
1430 	               gindex[13].numrec, gindex[13].file_offset);
1431 
1432 	write_recblock(sub_name, FT_WORD, gindex[14].numrec, gindex[14].file_offset);
1433 	write_recblock(synlist, FT_SLIST, gindex[15].numrec, gindex[15].file_offset);
1434 	write_recblock(pix_name, FT_WORD, gindex[16].numrec, gindex[16].file_offset);
1435 	write_recblock(globalnoun, FT_WORD, gindex[17].numrec, gindex[17].file_offset);
1436 	write_recblock(flag_noun, FT_WORD, gindex[18].numrec, gindex[18].file_offset);
1437 	write_recblock(syntbl, FT_WORD, gindex[19].numrec, gindex[19].file_offset);
1438 	write_recblock(dictstr, FT_CHAR, gindex[20].numrec, gindex[20].file_offset);
1439 	write_recblock(dict, FT_DICTPTR, gindex[21].numrec, gindex[21].file_offset);
1440 	if (have_opt)
1441 		write_recblock(opt_data, FT_BYTE, gindex[22].numrec, gindex[22].file_offset);
1442 
1443 	write_recblock(pictlist, FT_STR, gindex[23].numrec, gindex[23].file_offset);
1444 	write_recblock(pixlist, FT_STR, gindex[24].numrec, gindex[24].file_offset);
1445 	write_recblock(fontlist, FT_STR, gindex[25].numrec, gindex[25].file_offset);
1446 	write_recblock(songlist, FT_STR, gindex[26].numrec, gindex[26].file_offset);
1447 
1448 	write_recarray(verbinfo, sizeof(verbentry_rec), gindex[27].numrec,
1449 	               fi_verbentry, gindex[27].file_offset);
1450 	write_recblock(comblist, FT_SLIST, gindex[28].numrec, gindex[28].file_offset);
1451 	write_recblock(userprep, FT_SLIST, gindex[29].numrec, gindex[29].file_offset);
1452 	write_recblock(objflag, FT_BYTE, gindex[30].numrec, gindex[30].file_offset);
1453 	write_recblock(objprop, FT_INT32, gindex[31].numrec, gindex[31].file_offset);
1454 	fix_objflag_str(0); /* Convert to external form */
1455 	write_recarray(attrtable, sizeof(attrdef_rec),
1456 	               gindex[32].numrec, fi_attrrec, gindex[32].file_offset);
1457 	write_recarray(proptable, sizeof(propdef_rec),
1458 	               gindex[33].numrec, fi_proprec, gindex[33].file_offset);
1459 	write_recblock(propstr, FT_STR, gindex[34].numrec, gindex[34].file_offset);
1460 	write_recarray(vartable, sizeof(vardef_rec),
1461 	               gindex[35].numrec, fi_varrec, gindex[35].file_offset);
1462 	write_recarray(flagtable, sizeof(flagdef_rec),
1463 	               gindex[36].numrec, fi_flagrec, gindex[36].file_offset);
1464 	fix_objflag_str(1); /* Restore to internal form */
1465 }
1466 
1467 
1468 /* Write header and master gindex and then close AGX file */
agx_wclose(void)1469 void agx_wclose(void) {
1470 	write_header();
1471 	write_recarray(gindex, sizeof(index_rec), AGX_NUMBLOCK, fi_index, 16);
1472 	bw_close();
1473 	rfree(gindex);
1474 }
1475 
1476 } // End of namespace AGT
1477 } // End of namespace Glk
1478