1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/TADS2/FIO.C,v 1.4 1999/07/11 00:46:29 MJRoberts Exp $";
4 #endif
5
6 /*
7 * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved.
8 *
9 * Please see the accompanying license file, LICENSE.TXT, for information
10 * on using and copying this software.
11 */
12 /*
13 Name
14 fio.c - file i/o functions
15 Function
16 file i/o: read game, write game, save game, restore game
17 Notes
18 none
19 Modified
20 04/11/99 CNebel - Include appctx.h, not trd.h.
21 10/23/97 CNebel - removed now-obsolete Think C hack.
22 04/02/92 MJRoberts - creation
23 */
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "os.h"
29 #include "std.h"
30 #include "appctx.h"
31 #include "mch.h"
32 #include "mcm.h"
33 #include "mcl.h"
34 #include "tok.h"
35 #include "obj.h"
36 #include "voc.h"
37 #include "fio.h"
38 #include "dat.h"
39 #include "prs.h"
40 #include "linf.h"
41 #include "cmap.h"
42
43
44 /* compare a resource string */
45 /* int fioisrsc(uchar *filbuf, char *refnam); */
46 #define fioisrsc(filbuf, refnam) \
47 (((filbuf)[0] == strlen(refnam)) && \
48 !memcmp(filbuf+1, refnam, (size_t)((filbuf)[0])))
49
50 /* callback to load an object on demand */
fioldobj(void * ctx0,mclhd handle,uchar * ptr,ushort siz)51 void OS_LOADDS fioldobj(void *ctx0, mclhd handle, uchar *ptr, ushort siz)
52 {
53 fiolcxdef *ctx = (fiolcxdef *)ctx0;
54 ulong seekpos = (ulong)handle;
55 osfildef *fp = ctx->fiolcxfp;
56 char buf[7];
57 errcxdef *ec = ctx->fiolcxerr;
58 uint rdsiz;
59
60 /* figure out what type of object is to be loaded */
61 osfseek(fp, seekpos + ctx->fiolcxst, OSFSK_SET);
62 if (osfrb(fp, buf, 7)) errsig(ec, ERR_LDGAM);
63 switch(buf[0])
64 {
65 case TOKSTFUNC:
66 rdsiz = osrp2(buf + 3);
67 break;
68
69 case TOKSTOBJ:
70 rdsiz = osrp2(buf + 5);
71 break;
72
73 case TOKSTFWDOBJ:
74 case TOKSTFWDFN:
75 default:
76 errsig(ec, ERR_UNKOTYP);
77 }
78
79 if (siz < rdsiz) errsig(ec, ERR_LDBIG);
80 if (osfrb(fp, ptr, rdsiz)) errsig(ec, ERR_LDGAM);
81 if (ctx->fiolcxflg & FIOFCRYPT)
82 fioxor(ptr, rdsiz, ctx->fiolcxseed, ctx->fiolcxinc);
83 }
84
85 /* shut down load-on-demand subsystem (close load file) */
fiorcls(fiolcxdef * ctx)86 void fiorcls(fiolcxdef *ctx)
87 {
88 if (ctx != 0 && ctx->fiolcxfp != 0)
89 {
90 /* close the file */
91 osfcls(ctx->fiolcxfp);
92
93 /* forget the file object */
94 ctx->fiolcxfp = 0;
95 }
96 }
97
98 /*
99 * Read an HTMLRES resource map
100 */
fiordhtml(errcxdef * ec,osfildef * fp,appctxdef * appctx,int resfileno,const char * resfilename)101 static void fiordhtml(errcxdef *ec, osfildef *fp, appctxdef *appctx,
102 int resfileno, const char *resfilename)
103 {
104 uchar buf[256];
105
106 /*
107 * resource map - if the host system is interested, tell it about it
108 */
109 if (appctx != 0)
110 {
111 ulong entry_cnt;
112 ulong i;
113
114 /* read the index table header */
115 if (osfrb(fp, buf, 8))
116 errsig1(ec, ERR_RDRSC, ERRTSTR,
117 errstr(ec, resfilename, strlen(resfilename)));
118
119 /* get the number of entries in the table */
120 entry_cnt = osrp4(buf);
121
122 /* read the index entries */
123 for (i = 0 ; i < entry_cnt ; ++i)
124 {
125 ulong res_ofs;
126 ulong res_siz;
127 ushort res_namsiz;
128
129 /* read this entry */
130 if (osfrb(fp, buf, 10))
131 errsig1(ec, ERR_RDRSC, ERRTSTR,
132 errstr(ec, resfilename, strlen(resfilename)));
133
134 /* get the entry header */
135 res_ofs = osrp4(buf);
136 res_siz = osrp4(buf + 4);
137 res_namsiz = osrp2(buf + 8);
138
139 /* read this entry's name */
140 if (osfrb(fp, buf, res_namsiz))
141 errsig1(ec, ERR_RDRSC, ERRTSTR,
142 errstr(ec, resfilename, strlen(resfilename)));
143
144 /* tell the host system about this entry */
145 if (appctx->add_resource)
146 (*appctx->add_resource)(appctx->add_resource_ctx,
147 res_ofs, res_siz,
148 (char *)buf,
149 (size_t)res_namsiz,
150 resfileno);
151 }
152
153 /* tell the host system where the resources start */
154 if (appctx->set_resmap_seek != 0)
155 {
156 long pos = osfpos(fp);
157 (*appctx->set_resmap_seek)(appctx->set_resmap_seek_ctx,
158 pos, resfileno);
159 }
160 }
161 }
162
163 /*
164 * Read an external resource file. This is a limited version of the
165 * general file reader that can only read resource files, not full game
166 * files.
167 */
fiordrscext(errcxdef * ec,osfildef * fp,appctxdef * appctx,int resfileno,char * resfilename)168 static void fiordrscext(errcxdef *ec, osfildef *fp, appctxdef *appctx,
169 int resfileno, char *resfilename)
170 {
171 uchar buf[TOKNAMMAX + 50];
172 unsigned long endpos;
173 unsigned long startofs;
174
175 /* note the starting offset */
176 startofs = osfpos(fp);
177
178 /* check file and version headers, and get flags and timestamp */
179 if (osfrb(fp, buf, (int)(sizeof(FIOFILHDR) + sizeof(FIOVSNHDR) + 2)))
180 errsig1(ec, ERR_RDRSC, ERRTSTR,
181 errstr(ec, resfilename, strlen(resfilename)));
182 if (memcmp(buf, FIOFILHDRRSC, (size_t)sizeof(FIOFILHDRRSC)))
183 errsig1(ec, ERR_BADHDRRSC, ERRTSTR,
184 errstr(ec, resfilename, strlen(resfilename)));
185 if (memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR,
186 (size_t)sizeof(FIOVSNHDR))
187 && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR2,
188 (size_t)sizeof(FIOVSNHDR2))
189 && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR3,
190 (size_t)sizeof(FIOVSNHDR3)))
191 errsig(ec, ERR_BADVSN);
192 if (osfrb(fp, buf, (size_t)26))
193 errsig1(ec, ERR_RDRSC, ERRTSTR,
194 errstr(ec, resfilename, strlen(resfilename)));
195
196 /* now read resources from the file */
197 for (;;)
198 {
199 /* read resource type and next-resource pointer */
200 if (osfrb(fp, buf, 1)
201 || osfrb(fp, buf + 1, (int)(buf[0] + 4)))
202 errsig1(ec, ERR_RDRSC, ERRTSTR,
203 errstr(ec, resfilename, strlen(resfilename)));
204 endpos = osrp4(buf + 1 + buf[0]);
205
206 /* check the resource type */
207 if (fioisrsc(buf, "HTMLRES"))
208 {
209 /* read the HTML resource map */
210 fiordhtml(ec, fp, appctx, resfileno, resfilename);
211
212 /*
213 * skip the resources - they're entirely for the host
214 * application's use
215 */
216 osfseek(fp, endpos + startofs, OSFSK_SET);
217 }
218 else if (fioisrsc(buf, "$EOF"))
219 {
220 /* we're done reading the file */
221 break;
222 }
223 else
224 errsig(ec, ERR_UNKRSC);
225 }
226 }
227
228 /*
229 * read a game from a binary file
230 *
231 * flags:
232 * &1 ==> run preinit
233 * &2 ==> preload objects
234 */
fiord1(mcmcxdef * mctx,voccxdef * vctx,tokcxdef * tctx,osfildef * fp,const char * fname,fiolcxdef * setupctx,ulong startofs,objnum * preinit,uint * flagp,tokpdef * path,uchar ** fmtsp,uint * fmtlp,uint * pcntptr,int flags,appctxdef * appctx,char * argv0)235 static void fiord1(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tctx,
236 osfildef *fp, const char *fname,
237 fiolcxdef *setupctx, ulong startofs,
238 objnum *preinit, uint *flagp, tokpdef *path,
239 uchar **fmtsp, uint *fmtlp, uint *pcntptr, int flags,
240 appctxdef *appctx, char *argv0)
241 {
242 int i;
243 int siz;
244 uchar buf[TOKNAMMAX + 50];
245 errcxdef *ec = vctx->voccxerr;
246 ulong endpos;
247 int obj;
248 ulong curpos;
249 runxdef *ex;
250 ulong eof_reset = 0; /* reset here at EOF if non-zero */
251 int xfcns_done = FALSE; /* already loaded XFCN's */
252 ulong xfcn_pos = 0; /* location of XFCN's if preloadable */
253 uint xor_seed = 17; /* seed value for fioxor */
254 uint xor_inc = 29; /* increment value for fioxor */
255
256 /* set up loader callback context */
257 setupctx->fiolcxfp = fp;
258 setupctx->fiolcxerr = ec;
259 setupctx->fiolcxst = startofs;
260 setupctx->fiolcxseed = xor_seed;
261 setupctx->fiolcxinc = xor_inc;
262
263 /* check file and version headers, and get flags and timestamp */
264 if (osfrb(fp, buf, (int)(sizeof(FIOFILHDR) + sizeof(FIOVSNHDR) + 2)))
265 errsig(ec, ERR_RDGAM);
266 if (memcmp(buf, FIOFILHDR, (size_t)sizeof(FIOFILHDR)))
267 errsig(ec, ERR_BADHDR);
268 if (memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR,
269 (size_t)sizeof(FIOVSNHDR))
270 && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR2,
271 (size_t)sizeof(FIOVSNHDR2))
272 && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR3,
273 (size_t)sizeof(FIOVSNHDR3)))
274 errsig(ec, ERR_BADVSN);
275 if (osfrb(fp, vctx->voccxtim, (size_t)26)) errsig(ec, ERR_RDGAM);
276
277 /*
278 * if the game wasn't compiled with 2.2 or later, make a note,
279 * because we need to ignore certain property flags (due to a bug in
280 * the old compiler)
281 */
282 if (memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR2,
283 (size_t)sizeof(FIOVSNHDR2)) == 0
284 || memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR3,
285 (size_t)sizeof(FIOVSNHDR3)) == 0)
286 mctx->mcmcxflg |= MCMCXF_NO_PRP_DEL;
287
288 setupctx->fiolcxflg =
289 *flagp = osrp2(buf + sizeof(FIOFILHDR) + sizeof(FIOVSNHDR));
290
291 /* now read resources from the file */
292 for (;;)
293 {
294 /* read resource type and next-resource pointer */
295 if (osfrb(fp, buf, 1)
296 || osfrb(fp, buf + 1, (int)(buf[0] + 4)))
297 errsig(ec, ERR_RDGAM);
298 endpos = osrp4(buf + 1 + buf[0]);
299
300 if (fioisrsc(buf, "OBJ"))
301 {
302 /* skip regular objects if fast-load records are included */
303 if (*flagp & FIOFFAST)
304 {
305 osfseek(fp, endpos + startofs, OSFSK_SET);
306 continue;
307 }
308
309 curpos = osfpos(fp) - startofs;
310 while (curpos != endpos)
311 {
312 /* read type and object number */
313 if (osfrb(fp, buf, 3)) errsig(ec, ERR_RDGAM);
314 obj = osrp2(buf+1);
315
316 switch(buf[0])
317 {
318 case TOKSTFUNC:
319 case TOKSTOBJ:
320 if (osfrb(fp, buf + 3, 4)) errsig(ec, ERR_RDGAM);
321 mcmrsrv(mctx, (ushort)osrp2(buf + 3), (mcmon)obj,
322 (mclhd)curpos);
323 curpos += osrp2(buf + 5) + 7;
324
325 /* load object if preloading */
326 if (flags & 2)
327 {
328 (void)mcmlck(mctx, (mcmon)obj);
329 mcmunlck(mctx, (mcmon)obj);
330 }
331
332 /* seek past this object */
333 osfseek(fp, curpos + startofs, OSFSK_SET);
334 break;
335
336 case TOKSTFWDOBJ:
337 case TOKSTFWDFN:
338 {
339 ushort siz;
340 uchar *p;
341
342 if (osfrb(fp, buf+3, 2)) errsig(ec, ERR_RDGAM);
343 siz = osrp2(buf+3);
344 p = mcmalonum(mctx, siz, (mcmon)obj);
345 if (osfrb(fp, p, siz)) errsig(ec, ERR_RDGAM);
346 mcmunlck(mctx, (mcmon)obj);
347 curpos += 5 + siz;
348 break;
349 }
350
351 case TOKSTEXTERN:
352 if (!vctx->voccxrun->runcxext)
353 errsig(ec, ERR_UNXEXT);
354 ex = &vctx->voccxrun->runcxext[obj];
355
356 if (osfrb(fp, buf + 3, 1)
357 || osfrb(fp, ex->runxnam, (int)buf[3]))
358 errsig(ec, ERR_RDGAM);
359 ex->runxnam[buf[3]] = '\0';
360 curpos += buf[3] + 4;
361 break;
362
363 default:
364 errsig(ec, ERR_UNKOTYP);
365 }
366 }
367 }
368 else if (fioisrsc(buf, "FST"))
369 {
370 uchar *p;
371 uchar *bufp;
372 ulong siz;
373
374 if (!(*flagp & FIOFFAST))
375 {
376 osfseek(fp, endpos + startofs, OSFSK_SET);
377 continue;
378 }
379
380 curpos = osfpos(fp) - startofs;
381 siz = endpos - curpos;
382 if (siz && siz < OSMALMAX
383 && (bufp = p = (uchar *)osmalloc((size_t)siz)) != 0)
384 {
385 uchar *p1;
386 ulong siz2;
387 uint sizcur;
388
389 for (p1 = p, siz2 = siz ; siz2 ; siz2 -= sizcur, p1 += sizcur)
390 {
391 sizcur = (siz2 > (uint)0xffff ? (uint)0xffff : siz2);
392 if (osfrb(fp, p1, sizcur)) errsig(ec, ERR_RDGAM);
393 }
394
395 while (siz)
396 {
397 obj = osrp2(p + 1);
398 switch(*p)
399 {
400 case TOKSTFUNC:
401 case TOKSTOBJ:
402 mcmrsrv(mctx, (ushort)osrp2(p + 3), (mcmon)obj,
403 (mclhd)osrp4(p + 7));
404 p += 11;
405 siz -= 11;
406
407 /* preload object if desired */
408 if (flags & 2)
409 {
410 (void)mcmlck(mctx, (mcmon)obj);
411 mcmunlck(mctx, (mcmon)obj);
412 }
413 break;
414
415 case TOKSTEXTERN:
416 if (!vctx->voccxrun->runcxext)
417 errsig(ec, ERR_UNXEXT);
418 ex = &vctx->voccxrun->runcxext[obj];
419
420 memcpy(ex->runxnam, p + 4, (size_t)p[3]);
421 ex->runxnam[p[3]] = '\0';
422 siz -= p[3] + 4;
423 p += p[3] + 4;
424 break;
425
426 default:
427 errsig(ec, ERR_UNKOTYP);
428 }
429 }
430
431 /* done with temporary block; free it */
432 osfree(bufp);
433 osfseek(fp, endpos + startofs, OSFSK_SET);
434 }
435 else
436 {
437 while (curpos != endpos)
438 {
439 if (osfrb(fp, buf, 3)) errsig(ec, ERR_RDGAM);
440 obj = osrp2(buf + 1);
441 switch(buf[0])
442 {
443 case TOKSTFUNC:
444 case TOKSTOBJ:
445 if (osfrb(fp, buf + 3, 8)) errsig(ec, ERR_RDGAM);
446 mcmrsrv(mctx, (ushort)osrp2(buf + 3), (mcmon)obj,
447 (mclhd)osrp4(buf + 7));
448 curpos += 11;
449
450 /* preload object if desired */
451 if (flags & 2)
452 {
453 (void)mcmlck(mctx, (mcmon)obj);
454 mcmunlck(mctx, (mcmon)obj);
455 osfseek(fp, curpos + startofs, OSFSK_SET);
456 }
457 break;
458
459 case TOKSTEXTERN:
460 if (!vctx->voccxrun->runcxext)
461 errsig(ec, ERR_UNXEXT);
462 ex = &vctx->voccxrun->runcxext[obj];
463
464 if (osfrb(fp, buf + 3, 1)
465 || osfrb(fp, ex->runxnam, (int)buf[3]))
466 errsig(ec, ERR_RDGAM);
467 ex->runxnam[buf[3]] = '\0';
468 curpos += buf[3] + 4;
469 break;
470
471 default:
472 errsig(ec, ERR_UNKOTYP);
473 }
474 }
475 }
476
477 /* if we can preload xfcn's, do so now */
478 if (xfcn_pos)
479 {
480 eof_reset = endpos; /* remember to return here when done */
481 osfseek(fp, xfcn_pos, OSFSK_SET); /* go to xfcn's */
482 }
483 }
484 else if (fioisrsc(buf, "XFCN"))
485 {
486 if (!vctx->voccxrun->runcxext) errsig(ec, ERR_UNXEXT);
487
488 /* read length and name of resource */
489 if (osfrb(fp, buf, 3) || osfrb(fp, buf + 3, (int)buf[2]))
490 errsig(ec, ERR_RDGAM);
491 siz = osrp2(buf);
492
493 #if 0
494 /*
495 * external functions are now obsolete - do not load
496 */
497
498 /* look for an external function with the same name */
499 for (i = vctx->voccxrun->runcxexc, ex = vctx->voccxrun->runcxext
500 ; i ; ++ex, --i)
501 {
502 j = strlen(ex->runxnam);
503 if (j == buf[2] && !memcmp(buf + 3, ex->runxnam, (size_t)j))
504 break;
505 }
506
507 /* if we found an external function of this name, load it */
508 if (i && !xfcns_done)
509 {
510 /* load the function */
511 ex->runxptr = os_exfld(fp, (unsigned)siz);
512 }
513 else
514 {
515 /* this XFCN isn't used; don't bother loading it */
516 osfseek(fp, endpos + startofs, OSFSK_SET);
517 }
518 #else
519 /* external functions are obsolete; simply skip the data */
520 osfseek(fp, endpos + startofs, OSFSK_SET);
521 #endif
522 }
523 else if (fioisrsc(buf, "HTMLRES"))
524 {
525 /* read the resources */
526 fiordhtml(ec, fp, appctx, 0, fname);
527
528 /*
529 * skip the resources - they're entirely for the host
530 * application's use
531 */
532 osfseek(fp, endpos + startofs, OSFSK_SET);
533 }
534 else if (fioisrsc(buf, "INH"))
535 {
536 uchar *p;
537 uchar *bufp;
538 ulong siz;
539
540 /* do it in a single file read, if we can, for speed */
541 curpos = osfpos(fp) - startofs;
542 siz = endpos - curpos;
543 if (siz && siz < OSMALMAX
544 && (bufp = p = (uchar *)osmalloc((size_t)siz)) != 0)
545 {
546 uchar *p1;
547 ulong siz2;
548 uint sizcur;
549
550 for (p1 = p, siz2 = siz ; siz2 ; siz2 -= sizcur, p1 += sizcur)
551 {
552 sizcur = (siz2 > (uint)0xffff ? (uint)0xffff : siz2);
553 if (osfrb(fp, p1, sizcur)) errsig(ec, ERR_RDGAM);
554 }
555
556 while (siz)
557 {
558 i = osrp2(p + 7);
559 obj = osrp2(p + 1);
560
561 vociadd(vctx, (objnum)obj, (objnum)osrp2(p+3), i,
562 (objnum *)(p + 9), p[0] | VOCIFXLAT);
563 vocinh(vctx, obj)->vociilc = osrp2(p + 5);
564
565 p += 9 + (2 * i);
566 siz -= 9 + (2 * i);
567 }
568
569 /* done with temporary block; free it */
570 osfree(bufp);
571 }
572 else
573 {
574 while (curpos != endpos)
575 {
576 if (osfrb(fp, buf, 9)) errsig(ec, ERR_RDGAM);
577 i = osrp2(buf + 7); /* get number of superclasses */
578 obj = osrp2(buf + 1); /* get object number */
579 if (i && osfrb(fp, buf + 9, 2 * i)) errsig(ec, ERR_RDGAM);
580
581 vociadd(vctx, (objnum)obj, (objnum)osrp2(buf+3),
582 i, (objnum *)(buf + 9), buf[0] | VOCIFXLAT);
583 vocinh(vctx, obj)->vociilc = osrp2(buf + 5);
584
585 curpos += 9 + (2 * i);
586 }
587 }
588 }
589 else if (fioisrsc(buf, "REQ"))
590 {
591 curpos = osfpos(fp) - startofs;
592 siz = endpos - curpos;
593
594 if (osfrb(fp, buf, (uint)siz)) errsig(ec, ERR_RDGAM);
595 vctx->voccxme = vctx->voccxme_init = osrp2(buf);
596 vctx->voccxvtk = osrp2(buf+2);
597 vctx->voccxstr = osrp2(buf+4);
598 vctx->voccxnum = osrp2(buf+6);
599 vctx->voccxprd = osrp2(buf+8);
600 vctx->voccxvag = osrp2(buf+10);
601 vctx->voccxini = osrp2(buf+12);
602 vctx->voccxpre = osrp2(buf+14);
603 vctx->voccxper = osrp2(buf+16);
604
605 /* if we have a cmdPrompt function, read it */
606 if (siz >= 20)
607 vctx->voccxprom = osrp2(buf + 18);
608 else
609 vctx->voccxprom = MCMONINV;
610
611 /* if we have the NLS functions, read them */
612 if (siz >= 26)
613 {
614 vctx->voccxpdis = osrp2(buf + 20);
615 vctx->voccxper2 = osrp2(buf + 22);
616 vctx->voccxpdef = osrp2(buf + 24);
617 }
618 else
619 {
620 /* the new NLS functions aren't defined in this file */
621 vctx->voccxpdis = MCMONINV;
622 vctx->voccxper2 = MCMONINV;
623 vctx->voccxpdef = MCMONINV;
624 }
625
626 /* test for parseAskobj separately, as it was added later */
627 if (siz >= 28)
628 vctx->voccxpask = osrp2(buf + 26);
629 else
630 vctx->voccxpask = MCMONINV;
631
632 /* test for preparseCmd separately - it's another late comer */
633 if (siz >= 30)
634 vctx->voccxppc = osrp2(buf + 28);
635 else
636 vctx->voccxppc = MCMONINV;
637
638 /* check for parseAskobjActor separately - another late comer */
639 if (siz >= 32)
640 vctx->voccxpask2 = osrp2(buf + 30);
641 else
642 vctx->voccxpask2 = MCMONINV;
643
644 /* if we have parseErrorParam, read it as well */
645 if (siz >= 34)
646 {
647 vctx->voccxperp = osrp2(buf + 32);
648 }
649 else
650 {
651 /* parseErrorParam isn't defined in this file */
652 vctx->voccxperp = MCMONINV;
653 }
654
655 /*
656 * if we have commandAfterRead and initRestore, read them as
657 * well
658 */
659 if (siz >= 38)
660 {
661 vctx->voccxpostprom = osrp2(buf + 34);
662 vctx->voccxinitrestore = osrp2(buf + 36);
663 }
664 else
665 {
666 /* these new functions aren't defined in this game */
667 vctx->voccxpostprom = MCMONINV;
668 vctx->voccxinitrestore = MCMONINV;
669 }
670
671 /* check for and read parseUnknownVerb, parseNounPhrase */
672 if (siz >= 42)
673 {
674 vctx->voccxpuv = osrp2(buf + 38);
675 vctx->voccxpnp = osrp2(buf + 40);
676 }
677 else
678 {
679 vctx->voccxpuv = MCMONINV;
680 vctx->voccxpnp = MCMONINV;
681 }
682
683 /* check for postAction, endCommand */
684 if (siz >= 48)
685 {
686 vctx->voccxpostact = osrp2(buf + 42);
687 vctx->voccxendcmd = osrp2(buf + 44);
688 vctx->voccxprecmd = osrp2(buf + 46);
689 }
690 else
691 {
692 vctx->voccxpostact = MCMONINV;
693 vctx->voccxendcmd = MCMONINV;
694 vctx->voccxprecmd = MCMONINV;
695 }
696
697 /* check for parseAskobjIndirect */
698 if (siz >= 50)
699 vctx->voccxpask3 = osrp2(buf + 48);
700 else
701 vctx->voccxpask3 = MCMONINV;
702
703 /* check for preparseExt and parseDefaultExt */
704 if (siz >= 54)
705 {
706 vctx->voccxpre2 = osrp2(buf + 50);
707 vctx->voccxpdef2 = osrp2(buf + 52);
708 }
709 else
710 {
711 vctx->voccxpre2 = MCMONINV;
712 vctx->voccxpdef2 = MCMONINV;
713 }
714 }
715 else if (fioisrsc(buf, "VOC"))
716 {
717 uchar *p;
718 uchar *bufp;
719 ulong siz;
720 int len1;
721 int len2;
722
723 /* do it in a single file read, if we can, for speed */
724 curpos = osfpos(fp) - startofs;
725 siz = endpos - curpos;
726 if (siz && siz < OSMALMAX
727 && (bufp = p = (uchar *)osmalloc((size_t)siz)) != 0)
728 {
729 uchar *p1;
730 ulong siz2;
731 uint sizcur;
732
733 for (p1 = p, siz2 = siz ; siz2 ; siz2 -= sizcur, p1 += sizcur)
734 {
735 sizcur = (siz2 > (uint)0xffff ? (uint)0xffff : siz2);
736 if (osfrb(fp, p1, sizcur)) errsig(ec, ERR_RDGAM);
737 }
738
739 while (siz)
740 {
741 len1 = osrp2(p);
742 len2 = osrp2(p + 2);
743 if (*flagp & FIOFCRYPT)
744 fioxor(p + 10, (uint)(len1 + len2),
745 xor_seed, xor_inc);
746 vocadd2(vctx, (prpnum)osrp2(p+4), (objnum)osrp2(p+6),
747 osrp2(p+8), p + 10, len1,
748 (len2 ? p + 10 + len1 : (uchar*)0), len2);
749
750 p += 10 + len1 + len2;
751 siz -= 10 + len1 + len2;
752 }
753
754 /* done with the temporary block; free it up */
755 osfree(bufp);
756 }
757 else
758 {
759 /* can't do it in one file read; do it the slow way */
760 while (curpos != endpos)
761 {
762 if (osfrb(fp, buf, 10)
763 || osfrb(fp, buf + 10,
764 (len1 = osrp2(buf)) + (len2 = osrp2(buf + 2))))
765 errsig(ec, ERR_RDGAM);
766
767 if (*flagp & FIOFCRYPT)
768 fioxor(buf + 10, (uint)(len1 + len2),
769 xor_seed, xor_inc);
770 vocadd2(vctx, (prpnum)osrp2(buf+4), (objnum)osrp2(buf+6),
771 osrp2(buf+8), buf + 10, len1,
772 (len2 ? buf + 10 + len1 : (uchar*)0), len2);
773 curpos += 10 + len1 + len2;
774 }
775 }
776 }
777 else if (fioisrsc(buf, "FMTSTR"))
778 {
779 uchar *fmts;
780 uint fmtl;
781
782 if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM);
783 fmtl = osrp2(buf);
784 fmts = mchalo(vctx->voccxerr, (ushort)fmtl, "fiord1");
785 if (osfrb(fp, fmts, fmtl)) errsig(ec, ERR_RDGAM);
786 if (*flagp & FIOFCRYPT) fioxor(fmts, fmtl, xor_seed, xor_inc);
787 tiosetfmt(vctx->voccxtio, vctx->voccxrun, fmts, fmtl);
788
789 if (fmtsp) *fmtsp = fmts;
790 if (fmtlp) *fmtlp = fmtl;
791 }
792 else if (fioisrsc(buf, "CMPD"))
793 {
794 if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM);
795 vctx->voccxcpl = osrp2(buf);
796 vctx->voccxcpp = (char *)mchalo(vctx->voccxerr,
797 (ushort)vctx->voccxcpl, "fiord1");
798 if (osfrb(fp, vctx->voccxcpp, (uint)vctx->voccxcpl))
799 errsig(ec, ERR_RDGAM);
800 if (*flagp & FIOFCRYPT)
801 fioxor((uchar *)vctx->voccxcpp, (uint)vctx->voccxcpl,
802 xor_seed, xor_inc);
803 }
804 else if (fioisrsc(buf, "SPECWORD"))
805 {
806 if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM);
807 vctx->voccxspl = osrp2(buf);
808 vctx->voccxspp = (char *)mchalo(vctx->voccxerr,
809 (ushort)vctx->voccxspl, "fiord1");
810 if (osfrb(fp, vctx->voccxspp, (uint)vctx->voccxspl))
811 errsig(ec, ERR_RDGAM);
812 if (*flagp & FIOFCRYPT)
813 fioxor((uchar *)vctx->voccxspp, (uint)vctx->voccxspl,
814 xor_seed, xor_inc);
815 }
816 else if (fioisrsc(buf, "SYMTAB"))
817 {
818 tokthdef *symtab;
819
820 /* if there's no debugger context, don't bother with this */
821 if (!vctx->voccxrun->runcxdbg)
822 {
823 osfseek(fp, endpos + startofs, OSFSK_SET);
824 continue;
825 }
826
827 if (!(symtab = vctx->voccxrun->runcxdbg->dbgcxtab))
828 {
829 symtab = (tokthdef *)mchalo(ec, (ushort)sizeof(tokthdef),
830 "fiord:symtab");
831 tokthini(ec, mctx, (toktdef *)symtab);
832 vctx->voccxrun->runcxdbg->dbgcxtab = symtab;
833 }
834
835 /* read symbols until we find a zero-length symbol */
836 for (;;)
837 {
838 int hash;
839
840 if (osfrb(fp, buf, 4)) errsig(ec, ERR_RDGAM);
841 if (buf[0] == 0) break;
842 if (osfrb(fp, buf + 4, (int)buf[0])) errsig(ec, ERR_RDGAM);
843 buf[4 + buf[0]] = '\0';
844 hash = tokhsh((char *)buf + 4);
845
846 (*symtab->tokthsc.toktfadd)((toktdef *)symtab,
847 (char *)buf + 4,
848 (int)buf[0], (int)buf[1],
849 osrp2(buf + 2), hash);
850 }
851 }
852 else if (fioisrsc(buf, "SRC"))
853 {
854 /* skip source file id's if there's no debugger context */
855 if (vctx->voccxrun->runcxdbg == 0)
856 {
857 osfseek(fp, endpos + startofs, OSFSK_SET);
858 continue;
859 }
860
861 while ((osfpos(fp) - startofs) != endpos)
862 {
863 /* the only thing we know how to read is linfdef's */
864 if (linfload(fp, vctx->voccxrun->runcxdbg, ec, path))
865 errsig(ec, ERR_RDGAM);
866 }
867 }
868 else if (fioisrsc(buf, "SRC2"))
869 {
870 /*
871 * this is simply a marker indicating that we have new-style
872 * (line-number-based) source debugging information in the
873 * file -- set the new-style debug info flag
874 */
875 if (vctx->voccxrun->runcxdbg != 0)
876 vctx->voccxrun->runcxdbg->dbgcxflg |= DBGCXFLIN2;
877
878 /* the contents are empty - skip the block */
879 osfseek(fp, endpos + startofs, OSFSK_SET);
880 }
881 else if (fioisrsc(buf, "PREINIT"))
882 {
883 if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM);
884 *preinit = osrp2(buf);
885 }
886 else if (fioisrsc(buf, "ERRMSG"))
887 {
888 errini(ec, fp);
889 osfseek(fp, endpos + startofs, OSFSK_SET);
890 }
891 else if (fioisrsc(buf, "EXTCNT"))
892 {
893 uchar *p;
894 ushort len;
895 ulong siz;
896
897 curpos = osfpos(fp) - startofs;
898 siz = endpos - curpos;
899 if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM);
900 i = osrp2(buf);
901
902 len = i * sizeof(runxdef);
903 p = mchalo(ec, len, "fiord:runxdef");
904 memset(p, 0, (size_t)len);
905
906 vctx->voccxrun->runcxext = (runxdef *)p;
907 vctx->voccxrun->runcxexc = i;
908
909 /* see if start-of-XFCN information is present */
910 if (siz >= 6)
911 {
912 /* get location of first XFCN, and seek there */
913 if (osfrb(fp, buf, 4)) errsig(ec, ERR_RDGAM);
914 xfcn_pos = osrp4(buf);
915 }
916
917 /* seek past this resource */
918 osfseek(fp, endpos + startofs, OSFSK_SET);
919 }
920 else if (fioisrsc(buf, "PRPCNT"))
921 {
922 if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM);
923 if (pcntptr) *pcntptr = osrp2(buf);
924 }
925 else if (fioisrsc(buf, "TADSPP") && tctx != 0)
926 {
927 tok_read_defines(tctx, fp, ec);
928 }
929 else if (fioisrsc(buf, "XSI"))
930 {
931 if (osfrb(fp, buf, 2)) errsig(ec, ERR_RDGAM);
932 setupctx->fiolcxseed = xor_seed = buf[0];
933 setupctx->fiolcxinc = xor_inc = buf[1];
934 osfseek(fp, endpos + startofs, OSFSK_SET);
935 }
936 else if (fioisrsc(buf, "CHRSET"))
937 {
938 size_t len;
939
940 /* read the character set ID and LDESC */
941 if (osfrb(fp, buf, 6)
942 || (len = osrp2(buf+4)) > CMAP_LDESC_MAX_LEN
943 || osfrb(fp, buf+6, len))
944 errsig(ec, ERR_RDGAM);
945
946 /* establish this character set mapping */
947 buf[4] = '\0';
948 cmap_set_game_charset(ec, (char *)buf, (char *)buf + 6, argv0);
949 }
950 else if (fioisrsc(buf, "$EOF"))
951 {
952 if (eof_reset)
953 {
954 osfseek(fp, eof_reset, OSFSK_SET); /* back after EXTCNT */
955 eof_reset = 0; /* really done at next EOF */
956 xfcns_done = TRUE; /* don't do XFCN's again */
957 }
958 else
959 break;
960 }
961 else
962 errsig(ec, ERR_UNKRSC);
963 }
964 }
965
966 /* read binary file */
fiord(mcmcxdef * mctx,voccxdef * vctx,tokcxdef * tctx,char * fname,char * exename,fiolcxdef * setupctx,objnum * preinit,uint * flagp,tokpdef * path,uchar ** fmtsp,uint * fmtlp,uint * pcntptr,int flags,struct appctxdef * appctx,char * argv0)967 void fiord(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tctx, char *fname,
968 char *exename, fiolcxdef *setupctx, objnum *preinit, uint *flagp,
969 tokpdef *path, uchar **fmtsp, uint *fmtlp, uint *pcntptr,
970 int flags, struct appctxdef *appctx, char *argv0)
971 {
972 osfildef *fp;
973 ulong startofs;
974 char *display_fname;
975
976 /* presume there will be no need to run preinit */
977 *preinit = MCMONINV;
978
979 /*
980 * get the display filename - use the real filename if one is
981 * provided, otherwise use the name of the executable file itself
982 */
983 display_fname = (fname != 0 ? fname : exename);
984
985 /* open the file and read and check file header */
986 fp = (fname != 0 ? osfoprb(fname, OSFTGAME)
987 : os_exeseek(exename, "TGAM"));
988 if (fp == 0)
989 errsig(vctx->voccxerr, ERR_OPRGAM);
990
991 /*
992 * we've identified the .GAM file source - tell the host system
993 * about it, if it's interested
994 */
995 if (appctx != 0 && appctx->set_game_name != 0)
996 (*appctx->set_game_name)(appctx->set_game_name_ctx, display_fname);
997
998 /* remember starting location in file */
999 startofs = osfpos(fp);
1000
1001 ERRBEGIN(vctx->voccxerr)
1002
1003 /*
1004 * Read the game file. Note that the .GAM file always has resource
1005 * file number zero.
1006 */
1007 fiord1(mctx, vctx, tctx, fp, display_fname,
1008 setupctx, startofs, preinit, flagp, path,
1009 fmtsp, fmtlp, pcntptr, flags, appctx, argv0);
1010
1011 /*
1012 * If the host system accepts additional resource files, look for
1013 * additional resource files. These are files in the same directory
1014 * as the .GAM file, with the .GAM suffix replaced by suffixes from
1015 *. RS0 to .RS9.
1016 */
1017 if (appctx != 0 && appctx->add_resfile != 0)
1018 {
1019 char suffix_lc[4];
1020 char suffix_uc[4];
1021 int i;
1022 char *base_name;
1023
1024 /* use the game or executable filename, as appropriate */
1025 base_name = display_fname;
1026
1027 /* build the initial suffixes - try both upper- and lower-case */
1028 suffix_uc[0] = 'R';
1029 suffix_uc[1] = 'S';
1030 suffix_uc[3] = '\0';
1031 suffix_lc[0] = 'r';
1032 suffix_lc[1] = 's';
1033 suffix_lc[3] = '\0';
1034
1035 /* loop through each possible suffix (.RS0 through .RS9) */
1036 for (i = 0 ; i < 9 ; ++i)
1037 {
1038 char resname[OSFNMAX];
1039 osfildef *fpres;
1040 int resfileno;
1041
1042 /*
1043 * Build the next resource filename. If there's an explicit
1044 * resource path, use it, otherwise use the same directory
1045 * that contains the .GAM file.
1046 */
1047 if (appctx->ext_res_path != 0)
1048 {
1049 /*
1050 * There's an explicit resource path - append the root
1051 * (filename-only, minus path) portion of the .GAM file
1052 * name to the resource path.
1053 */
1054 os_build_full_path(resname, sizeof(resname),
1055 appctx->ext_res_path,
1056 os_get_root_name(base_name));
1057 }
1058 else
1059 {
1060 /*
1061 * there's no resource path - use the entire .GAM
1062 * filename, including directory, so that we look in the
1063 * same directory that contains the .GAM file
1064 */
1065 if (base_name != 0)
1066 strcpy(resname, base_name);
1067 else
1068 resname[0] = '\0';
1069 }
1070
1071 /* add the current extension (replacing any current extension) */
1072 os_remext(resname);
1073 suffix_lc[2] = suffix_uc[2] = '0' + i;
1074 os_addext(resname, suffix_lc);
1075
1076 /* try opening the file */
1077 fpres = osfoprb(resname, OSFTGAME);
1078
1079 /* if that didn't work, try the upper-case name */
1080 if (fpres == 0)
1081 {
1082 /* replace the suffix with the upper-case version */
1083 os_remext(resname);
1084 os_addext(resname, suffix_uc);
1085
1086 /* try again with the new name */
1087 fpres = osfoprb(resname, OSFTGAME);
1088 }
1089
1090 /* if we opened it successfully, read it */
1091 if (fpres != 0)
1092 {
1093 /* tell the host system about it */
1094 resfileno = (*appctx->add_resfile)
1095 (appctx->add_resfile_ctx, resname);
1096
1097 /* read the file */
1098 fiordrscext(vctx->voccxerr, fpres, appctx,
1099 resfileno, resname);
1100
1101 /* we're done with the file, so close it */
1102 osfcls(fpres);
1103 }
1104 }
1105 }
1106
1107 ERRCLEAN(vctx->voccxerr)
1108 /* if an error occurs during read, clean up by closing the file */
1109 osfcls(fp);
1110 ERRENDCLN(vctx->voccxerr);
1111 }
1112
1113 /* save game header */
1114 #define FIOSAVHDR "TADS2 save\012\015\032"
1115
1116 /* save game header prefix - .GAM file information */
1117 #define FIOSAVHDR_PREFIX "TADS2 save/g\012\015\032"
1118
1119 /*
1120 * Saved game format version string - note that the length of the
1121 * version string must be fixed, so when this is updated, it must be
1122 * updated to another string of the same length. This should be updated
1123 * whenever a change is made to the format that can't be otherwise
1124 * detected from the data stream in the saved game file.
1125 */
1126 #define FIOSAVVSN "v2.2.1"
1127
1128 /* old saved game format version strings */
1129 #define FIOSAVVSN1 "v2.2.0"
1130
1131 /* read fuse/daemon/alarm record */
fiorfda(osfildef * fp,vocddef * p,uint cnt)1132 static int fiorfda(osfildef *fp, vocddef *p, uint cnt)
1133 {
1134 vocddef *q;
1135 uint i;
1136 uchar buf[14];
1137
1138 /* start by clearing out entire record */
1139 for (i = 0, q = p ; i < cnt ; ++q, ++i)
1140 q->vocdfn = MCMONINV;
1141
1142 /* now restore all the records from the file */
1143 for (;;)
1144 {
1145 /* read a record, and quit if it's the last one */
1146 if (osfrb(fp, buf, 13)) return(TRUE);
1147 if ((i = osrp2(buf)) == 0xffff) return(FALSE);
1148
1149 /* restore this record */
1150 q = p + i;
1151 q->vocdfn = osrp2(buf+2);
1152 q->vocdarg.runstyp = buf[4];
1153 switch(buf[4])
1154 {
1155 case DAT_NUMBER:
1156 q->vocdarg.runsv.runsvnum = osrp4(buf+5);
1157 break;
1158 case DAT_OBJECT:
1159 case DAT_FNADDR:
1160 q->vocdarg.runsv.runsvobj = osrp2(buf+5);
1161 break;
1162 case DAT_PROPNUM:
1163 q->vocdarg.runsv.runsvprp = osrp2(buf+5);
1164 break;
1165 }
1166 q->vocdprp = osrp2(buf+9);
1167 q->vocdtim = osrp2(buf+11);
1168 }
1169 }
1170
1171 /*
1172 * Look in a saved game file to determine if it has information on which
1173 * GAM file created it. If the GAM file information is available, this
1174 * routine returns true and stores the game file name in the given
1175 * buffer; if the information isn't available, we'll return false.
1176 */
fiorso_getgame(char * saved_file,char * fnamebuf,size_t buflen)1177 int fiorso_getgame(char *saved_file, char *fnamebuf, size_t buflen)
1178 {
1179 osfildef *fp;
1180 uint namelen;
1181 char buf[sizeof(FIOSAVHDR_PREFIX) + 2];
1182
1183 /* open the input file */
1184 if (!(fp = osfoprb(saved_file, OSFTSAVE)))
1185 return FALSE;
1186
1187 /* read the prefix header and check */
1188 if (osfrb(fp, buf, (int)(sizeof(FIOSAVHDR_PREFIX) + 2))
1189 || memcmp(buf, FIOSAVHDR_PREFIX, sizeof(FIOSAVHDR_PREFIX)) != 0)
1190 {
1191 /*
1192 * there's no game file information - close the file and
1193 * indicate that we have no information
1194 */
1195 osfcls(fp);
1196 return FALSE;
1197 }
1198
1199 /* get the length of the filename */
1200 namelen = osrp2(buf + sizeof(FIOSAVHDR_PREFIX));
1201 if (namelen > buflen - 1)
1202 namelen = buflen - 1;
1203
1204 /* read the filename */
1205 osfrb(fp, fnamebuf, namelen);
1206
1207 /* null-terminate the string */
1208 fnamebuf[namelen] = '\0';
1209
1210 /* done with the file */
1211 osfcls(fp);
1212
1213 /* indicate that we found the information */
1214 return TRUE;
1215 }
1216
1217 /* restore game: returns TRUE on failure */
fiorso(voccxdef * vctx,char * fname)1218 int fiorso(voccxdef *vctx, char *fname)
1219 {
1220 osfildef *fp;
1221 objnum obj;
1222 uchar *p;
1223 uchar *mut;
1224 uint mutsiz;
1225 uint oldmutsiz;
1226 int propcnt;
1227 mcmcxdef *mctx = vctx->voccxmem;
1228 uchar buf[sizeof(FIOSAVHDR) + sizeof(FIOSAVVSN)];
1229 ushort newsiz;
1230 int err = FALSE;
1231 char timestamp[26];
1232 int version = 0; /* version ID - 0 = current version */
1233 int result;
1234
1235 /* presume success */
1236 result = FIORSO_SUCCESS;
1237
1238 /* open the input file */
1239 if (!(fp = osfoprb(fname, OSFTSAVE)))
1240 return FIORSO_FILE_NOT_FOUND;
1241
1242 /* check for a prefix header - if it's there, skip it */
1243 if (!osfrb(fp, buf, (int)(sizeof(FIOSAVHDR_PREFIX) + 2))
1244 && memcmp(buf, FIOSAVHDR_PREFIX, sizeof(FIOSAVHDR_PREFIX)) == 0)
1245 {
1246 ulong skip_len;
1247
1248 /*
1249 * The prefix header is present - skip it. The 2-byte value
1250 * following the header is the length of the prefix data block
1251 * (not including the header), so simply skip the additional
1252 * number of bytes specified.
1253 */
1254 skip_len = (ulong)osrp2(buf + sizeof(FIOSAVHDR_PREFIX));
1255 osfseek(fp, skip_len, OSFSK_CUR);
1256 }
1257 else
1258 {
1259 /*
1260 * there's no prefix header - seek back to the start of the file
1261 * and read the standard header information
1262 */
1263 osfseek(fp, 0, OSFSK_SET);
1264 }
1265
1266
1267 /* read headers and check */
1268 if (osfrb(fp, buf, (int)(sizeof(FIOSAVHDR) + sizeof(FIOSAVVSN)))
1269 || memcmp(buf, FIOSAVHDR, (size_t)sizeof(FIOSAVHDR)))
1270 {
1271 /* it's not a saved game file */
1272 result = FIORSO_NOT_SAVE_FILE;
1273 goto ret_error;
1274 }
1275
1276 /* check the version string */
1277 if (memcmp(buf + sizeof(FIOSAVHDR), FIOSAVVSN,
1278 (size_t)sizeof(FIOSAVVSN)) == 0)
1279 {
1280 /* it's the current version */
1281 version = 0;
1282 }
1283 else if (memcmp(buf + sizeof(FIOSAVHDR), FIOSAVVSN1,
1284 (size_t)sizeof(FIOSAVVSN1)) == 0)
1285 {
1286 /* it's old version #1 */
1287 version = 1;
1288 }
1289 else
1290 {
1291 /*
1292 * this isn't a recognized version - the file must have been
1293 * saved by a newer version of the system, so we can't assume we
1294 * will be able to parse the format
1295 */
1296 result = FIORSO_BAD_FMT_VSN;
1297 goto ret_error;
1298 }
1299
1300 /*
1301 * Read timestamp and check - the game must have been saved by the
1302 * same .GAM file that we are now running, because the .SAV file is
1303 * written entirely in terms of the contents of the .GAM file; any
1304 * change in the .GAM file invalidates the .SAV file.
1305 */
1306 if (osfrb(fp, timestamp, 26)
1307 || memcmp(timestamp, vctx->voccxtim, (size_t)26))
1308 {
1309 result = FIORSO_BAD_GAME_VSN;
1310 goto ret_error;
1311 }
1312
1313 /* first revert every object to original (post-compilation) state */
1314 vocrevert(vctx);
1315
1316 /*
1317 * the most common error from here on is simply a file read error,
1318 * so presume that this is what will happen; if we are successful or
1319 * encounter a different error, we'll change the status at that
1320 * point
1321 */
1322 result = FIORSO_READ_ERROR;
1323
1324 /* go through file and load changed objects */
1325 for (;;)
1326 {
1327 /* get the header */
1328 if (osfrb(fp, buf, 7))
1329 goto ret_error;
1330
1331 /* get the object number from the header, and stop if we're done */
1332 obj = osrp2(buf+1);
1333 if (obj == MCMONINV)
1334 break;
1335
1336 /* if the object was dynamically allocated, recreate it */
1337 if (buf[0] == 1)
1338 {
1339 int sccnt;
1340 objnum sc;
1341
1342 /* create the object */
1343 mutsiz = osrp2(buf + 3);
1344 p = mcmalonum(mctx, (ushort)mutsiz, (mcmon)obj);
1345
1346 /* read the object's contents */
1347 if (osfrb(fp, p, mutsiz))
1348 goto ret_error;
1349
1350 /* get the superclass data (at most one superclass) */
1351 sccnt = objnsc(p);
1352 if (sccnt) sc = osrp2(objsc(p));
1353
1354 /* create inheritance records for the object */
1355 vociadd(vctx, obj, MCMONINV, sccnt, &sc, VOCIFNEW | VOCIFVOC);
1356
1357 #if 0
1358 {
1359 int wrdcnt;
1360
1361 /* read the object's vocabulary and add it back */
1362 if (osfrb(fp, buf, 2))
1363 goto ret_error;
1364 wrdcnt = osrp2(buf);
1365 while (wrdcnt--)
1366 {
1367 int len1;
1368 int len2;
1369 char wrd[80];
1370
1371 /* read the header */
1372 if (osfrb(fp, buf, 6))
1373 goto ret_error;
1374 len1 = osrp2(buf+2);
1375 len2 = osrp2(buf+4);
1376
1377 /* read the word text */
1378 if (osfrb(fp, wrd, len1 + len2))
1379 goto ret_error;
1380
1381 /* add the word */
1382 vocadd2(vctx, buf[0], obj, buf[1], wrd, len1,
1383 wrd + len1, len2);
1384 }
1385 }
1386 #endif
1387
1388 }
1389 else
1390 {
1391 /* get the remaining data from the header */
1392 propcnt = osrp2(buf + 3);
1393 mutsiz = osrp2(buf + 5);
1394
1395 /* expand object if it's not big enough for mutsiz */
1396 p = mcmlck(mctx, (mcmon)obj);
1397 oldmutsiz = mcmobjsiz(mctx, (mcmon)obj) - objrst(p);
1398 if (oldmutsiz < mutsiz)
1399 {
1400 newsiz = mutsiz - oldmutsiz;
1401 p = (uchar *)objexp(mctx, obj, &newsiz);
1402 }
1403
1404 /* reset statistics, and read mutable part from file */
1405 mut = p + objrst(p);
1406 objsnp(p, propcnt);
1407 objsfree(p, mutsiz + objrst(p));
1408 if (osfrb(fp, mut, mutsiz))
1409 err = TRUE;
1410
1411 /* reset ignore flags as needed */
1412 objsetign(mctx, obj);
1413 }
1414
1415 /* touch and unlock the object */
1416 mcmtch(mctx, (mcmon)obj);
1417 mcmunlck(mctx, (mcmon)obj);
1418 if (err)
1419 goto ret_error;
1420 }
1421
1422 /* read fuses/daemons/alarms */
1423 if (fiorfda(fp, vctx->voccxdmn, vctx->voccxdmc)
1424 || fiorfda(fp, vctx->voccxfus, vctx->voccxfuc)
1425 || fiorfda(fp, vctx->voccxalm, vctx->voccxalc))
1426 goto ret_error;
1427
1428 /* read the dynamically added and deleted vocabulary */
1429 for (;;)
1430 {
1431 int len1;
1432 int len2;
1433 char wrd[80];
1434 int flags;
1435 int typ;
1436
1437 /* read the header */
1438 if (osfrb(fp, buf, 8))
1439 goto ret_error;
1440
1441 typ = buf[0];
1442 flags = buf[1];
1443 len1 = osrp2(buf+2);
1444 len2 = osrp2(buf+4);
1445 obj = osrp2(buf+6);
1446
1447 /* check to see if this is the end marker */
1448 if (obj == MCMONINV) break;
1449
1450 /* read the word text */
1451 if (osfrb(fp, wrd+2, len1))
1452 goto ret_error;
1453 if (len2)
1454 {
1455 wrd[len1 + 2] = ' ';
1456 if (osfrb(fp, &wrd[len1 + 3], len2))
1457 goto ret_error;
1458 oswp2(wrd, len1 + len2 + 3);
1459 }
1460 else
1461 oswp2(wrd, len1 + 2);
1462
1463 /* add or delete the word as appropriate */
1464 if (flags & VOCFDEL)
1465 vocdel1(vctx, obj, (char *)wrd, (prpnum)typ, FALSE, FALSE, FALSE);
1466 else
1467 vocadd2(vctx, buf[0], obj, buf[1], (uchar *)wrd+2, len1,
1468 (uchar *)wrd+len1, len2);
1469 }
1470
1471 /*
1472 * the following was added in save format version "v2.2.1", so skip
1473 * it if the save version is older than that
1474 */
1475 if (version != 1)
1476 {
1477 /* read the current "Me" object */
1478 if (osfrb(fp, buf, 2))
1479 goto ret_error;
1480 vctx->voccxme = osrp2(buf);
1481 }
1482
1483 /* done - close file and return success indication */
1484 osfcls(fp);
1485 return FIORSO_SUCCESS;
1486
1487 /* come here on failure - close file and return error indication */
1488 ret_error:
1489 osfcls(fp);
1490 return result;
1491 }
1492
1493 /* write fuse/daemon/alarm block */
fiowfda(osfildef * fp,vocddef * p,uint cnt)1494 static int fiowfda(osfildef *fp, vocddef *p, uint cnt)
1495 {
1496 uchar buf[14];
1497 uint i;
1498
1499 for (i = 0 ; i < cnt ; ++i, ++p)
1500 {
1501 if (p->vocdfn == MCMONINV) continue; /* not set - ignore */
1502
1503 oswp2(buf, i); /* element in array to be set */
1504 oswp2(buf+2, p->vocdfn); /* object number for function/target */
1505 buf[4] = p->vocdarg.runstyp; /* type of argument */
1506 switch(buf[4])
1507 {
1508 case DAT_NUMBER:
1509 oswp4(buf+5, p->vocdarg.runsv.runsvnum);
1510 break;
1511 case DAT_OBJECT:
1512 case DAT_FNADDR:
1513 oswp2(buf+5, p->vocdarg.runsv.runsvobj);
1514 break;
1515 case DAT_PROPNUM:
1516 oswp2(buf+5, p->vocdarg.runsv.runsvprp);
1517 break;
1518 }
1519 oswp2(buf+9, p->vocdprp);
1520 oswp2(buf+11, p->vocdtim);
1521
1522 /* write this record to file */
1523 if (osfwb(fp, buf, 13)) return(TRUE);
1524 }
1525
1526 /* write end record - -1 for array element number */
1527 oswp2(buf, 0xffff);
1528 return(osfwb(fp, buf, 13));
1529 }
1530
1531 /* context for vocabulary saver callback function */
1532 struct fiosav_cb_ctx
1533 {
1534 int err;
1535 osfildef *fp;
1536 };
1537
1538 #ifdef NEVER
1539 /*
1540 * callback for vocabulary saver - called by voc_iterate for each word
1541 * defined for a particular object, allowing us to write all the words
1542 * attached to a dynamically allocated object to the save file
1543 */
fiosav_cb(struct fiosav_cb_ctx * ctx,vocdef * voc,vocwdef * vocw)1544 static void fiosav_cb(struct fiosav_cb_ctx *ctx,
1545 vocdef *voc, vocwdef *vocw)
1546 {
1547 char buf[10];
1548
1549 /* write the part of speech, flags, and word lengths */
1550 buf[0] = vocw->vocwtyp;
1551 buf[1] = vocw->vocwflg;
1552 oswp2(buf+2, voc->voclen);
1553 oswp2(buf+4, voc->vocln2);
1554 if (osfwb(ctx->fp, buf, 6)) ctx->err = TRUE;
1555
1556 /* write the words */
1557 if (osfwb(ctx->fp, voc->voctxt, voc->voclen + voc->vocln2))
1558 ctx->err = TRUE;
1559 }
1560 #endif
1561
1562 /*
1563 * Callback for vocabulary saver - called by voc_iterate for every
1564 * word. We'll write the word if it was dynamically added or deleted,
1565 * so that we can restore that status when the game is restored.
1566 */
fiosav_voc_cb(void * ctx0,vocdef * voc,vocwdef * vocw)1567 static void fiosav_voc_cb(void *ctx0, vocdef *voc, vocwdef *vocw)
1568 {
1569 struct fiosav_cb_ctx *ctx = (struct fiosav_cb_ctx *)ctx0;
1570 char buf[10];
1571
1572 /* if the word was dynamically allocated or deleted, save it */
1573 if ((vocw->vocwflg & VOCFNEW) || (vocw->vocwflg & VOCFDEL))
1574 {
1575 /* write the header information */
1576 buf[0] = vocw->vocwtyp;
1577 buf[1] = vocw->vocwflg;
1578 oswp2(buf+2, voc->voclen);
1579 oswp2(buf+4, voc->vocln2);
1580 oswp2(buf+6, vocw->vocwobj);
1581 if (osfwb(ctx->fp, buf, 8)) ctx->err = TRUE;
1582
1583 /* write the words */
1584 if (osfwb(ctx->fp, voc->voctxt, voc->voclen + voc->vocln2))
1585 ctx->err = TRUE;
1586 }
1587 }
1588
1589
1590 /* save game; returns TRUE on failure */
fiosav(voccxdef * vctx,char * fname,char * game_fname)1591 int fiosav(voccxdef *vctx, char *fname, char *game_fname)
1592 {
1593 osfildef *fp;
1594 vocidef ***vpg;
1595 vocidef **v;
1596 int i;
1597 int j;
1598 objnum obj;
1599 uchar *p;
1600 uchar *mut;
1601 uint mutsiz;
1602 int propcnt;
1603 mcmcxdef *mctx = vctx->voccxmem;
1604 uchar buf[7];
1605 int err = FALSE;
1606 struct fiosav_cb_ctx fnctx;
1607
1608 /* open the output file */
1609 if ((fp = osfopwb(fname, OSFTSAVE)) == 0)
1610 return TRUE;
1611
1612 /*
1613 * If we have game file information, save the game file information
1614 * with the saved game file. This lets the player start the
1615 * run-time and restore the game by specifying only the saved game
1616 * file.
1617 */
1618 if (game_fname != 0)
1619 {
1620 size_t len;
1621
1622 /* write the prefix header */
1623 len = strlen(game_fname);
1624 oswp2(buf, len);
1625 if (osfwb(fp, FIOSAVHDR_PREFIX, (int)sizeof(FIOSAVHDR_PREFIX))
1626 || osfwb(fp, buf, 2)
1627 || osfwb(fp, game_fname, (int)len))
1628 goto ret_error;
1629 }
1630
1631 /* write save game header and timestamp */
1632 if (osfwb(fp, FIOSAVHDR, (int)sizeof(FIOSAVHDR))
1633 || osfwb(fp, FIOSAVVSN, (int)sizeof(FIOSAVVSN))
1634 || osfwb(fp, vctx->voccxtim, 26))
1635 goto ret_error;
1636
1637 /* go through each object, and write if it's been changed */
1638 for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
1639 {
1640 if (!*vpg) continue;
1641 for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
1642 {
1643 if (*v != 0)
1644 {
1645 /* write object if it's dirty */
1646 if (mcmobjdirty(mctx, (mcmon)obj))
1647 {
1648 p = mcmlck(mctx, (mcmon)obj);
1649 mut = p + objrst(p);
1650 propcnt = objnprop(p);
1651 mutsiz = objfree(p) - objrst(p);
1652 if ((objflg(p) & OBJFINDEX) != 0)
1653 mutsiz += propcnt * 4;
1654
1655 /*
1656 * If the object was dynamically allocated, write
1657 * the whole object. Otherwise, write just the
1658 * mutable part.
1659 */
1660 if ((*v)->vociflg & VOCIFNEW)
1661 {
1662 /* indicate that the object is dynamic */
1663 buf[0] = 1;
1664 oswp2(buf + 1, obj);
1665
1666 /* write the entire object */
1667 mutsiz = objfree(p);
1668 oswp2(buf + 3, mutsiz);
1669 if (osfwb(fp, buf, 7)
1670 || osfwb(fp, p, mutsiz))
1671 err = TRUE;
1672
1673 #ifdef NEVER
1674 {
1675 int wrdcnt;
1676
1677 /* count the words, and write the count */
1678 voc_count(vctx, obj, 0, &wrdcnt, (int *)0);
1679 oswp2(buf, wrdcnt);
1680 if (osfwb(fp, buf, 2))
1681 err = TRUE;
1682
1683 /* write the words */
1684 fnctx.err = 0;
1685 fnctx.fp = fp;
1686 voc_iterate(vctx, obj, fiosav_cb, &fnctx);
1687 if (fnctx.err != 0)
1688 err = TRUE;
1689 }
1690 #endif
1691 }
1692 else if (mutsiz)
1693 {
1694 /* write number of properties, size of mut, and mut */
1695 buf[0] = 0; /* indicate that the object is static */
1696 oswp2(buf + 1, obj);
1697 oswp2(buf + 3, propcnt);
1698 oswp2(buf + 5, mutsiz);
1699 if (osfwb(fp, buf, 7)
1700 || osfwb(fp, mut, mutsiz))
1701 err = TRUE;
1702 }
1703
1704 mcmunlck(mctx, (mcmon)obj);
1705 if (err != 0)
1706 goto ret_error;
1707 }
1708 }
1709 }
1710 }
1711
1712 /* write end-of-objects indication */
1713 buf[0] = 0;
1714 oswp2(buf + 1, MCMONINV);
1715 oswp4(buf + 3, 0);
1716 if (osfwb(fp, buf, 7))
1717 goto ret_error;
1718
1719 /* write fuses/daemons/alarms */
1720 if (fiowfda(fp, vctx->voccxdmn, vctx->voccxdmc)
1721 || fiowfda(fp, vctx->voccxfus, vctx->voccxfuc)
1722 || fiowfda(fp, vctx->voccxalm, vctx->voccxalc))
1723 goto ret_error;
1724
1725 /* write run-time vocabulary additions and deletions */
1726 fnctx.fp = fp;
1727 fnctx.err = 0;
1728 voc_iterate(vctx, MCMONINV, fiosav_voc_cb, &fnctx);
1729 if (fnctx.err)
1730 goto ret_error;
1731
1732 /* write end marker for vocabulary additions and deletions */
1733 oswp2(buf+6, MCMONINV);
1734 if (osfwb(fp, buf, 8))
1735 goto ret_error;
1736
1737 /* write the current "Me" object */
1738 oswp2(buf, vctx->voccxme);
1739 if (osfwb(fp, buf, 2))
1740 goto ret_error;
1741
1742 /* done - close file and return success indication */
1743 osfcls(fp);
1744 os_settype(fname, OSFTSAVE);
1745 return FALSE;
1746
1747 /* come here on failure - close file and return error indication */
1748 ret_error:
1749 osfcls(fp);
1750 return TRUE;
1751 }
1752