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