3
"$Header: d:/cvsroot/tads/TADS2/FIOWRT.C,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $";
7
* Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved.
9
* Please see the accompanying license file, LICENSE.TXT, for information
10
* on using and copying this software.
14
fiowrt.c - write game to binary file
16
Writes a game to binary file. Separated from fio.c so that run-time
17
doesn't have to link in this function, which is unnecessary for playing
22
09/14/92 MJRoberts - note location of undefined objects in errors
23
04/11/92 MJRoberts - creation
43
/* write a resource header; returns pointer to next-res field in file */
44
static ulong fiowhd(osfildef *fp, errcxdef *ec, char *resname)
48
if (osfwb(fp, resname, (int)(resname[0] + 1))) errsig(ec, ERR_WRTGAM);
50
if (osfwb(fp, "\000\000\000\000", 4)) errsig(ec, ERR_WRTGAM);
54
/* close a resource by writing next-res pointer */
55
static void fiowcls(osfildef *fp, errcxdef *ec, ulong respos)
61
osfseek(fp, respos, OSFSK_SET);
63
if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM);
64
osfseek(fp, endpos, OSFSK_SET);
67
/* write a required object (just an object number) */
68
static void fiowrq(errcxdef *ec, osfildef *fp, objnum objn)
73
if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
76
/* context for fiowrtobj callback */
89
typedef struct fiowcxdef fiowcxdef;
91
/* write out a symbol table entry */
92
static void fiowrtsym(void *ctx0, toksdef *t)
94
fiowcxdef *ctx = (fiowcxdef *)ctx0;
95
uchar buf[TOKNAMMAX + 50];
96
errcxdef *ec = ctx->fiowcxerr;
97
osfildef *fp = ctx->fiowcxfp;
101
oswp2(buf + 2, t->toksval);
102
memcpy(buf + 4, t->toksnam, (size_t)buf[0]);
103
if (osfwb(fp, buf, 4 + t->tokslen)) errsig(ec, ERR_WRTGAM);
106
/* write an object given by a symbol table entry */
107
static void fiowrtobj(void *ctx0, toksdef *t)
109
fiowcxdef *ctx = (fiowcxdef *)ctx0;
110
uchar buf[TOKNAMMAX + 50];
112
mcmcxdef *mctx = ctx->fiowcxmem;
113
errcxdef *ec = ctx->fiowcxerr;
114
osfildef *fp = ctx->fiowcxfp;
115
uint flags = ctx->fiowcxflg;
120
ulong startpos = osfpos(fp);
122
/* set up start of buffer to write */
131
* Mark object as finished with compilation. Note that we must
132
* do this even though tcdmain() does this as well, because
133
* running preinit() might have updated properties since the
134
* last time we marked objects.
136
objcomp(mctx, (objnum)obj, ctx->fiowcxdebug);
138
/* get the object's size information */
139
p = mcmlck(mctx, (mcmon)obj);
140
siz = mcmobjsiz(mctx, (mcmon)obj); /* size in cache */
141
used = objfree(p); /* size actually used in object */
142
if (objflg(p) & OBJFINDEX) used += objnprop(p) * 4;
143
goto write_func_or_obj;
146
/* size of function is entire object */
147
p = mcmlck(mctx, (mcmon)obj);
148
siz = used = mcmobjsiz(mctx, (mcmon)obj);
151
/* write type(OBJ) + objnum + size + sizeused */
154
if (osfwb(fp, buf, 7)) err = ERR_WRTGAM;
156
/* write contents of object */
157
if (flags & FIOFCRYPT)
158
fioxor(p, used, ctx->fiowcxseed, ctx->fiowcxinc);
159
if (osfwb(fp, p, used)) err = ERR_WRTGAM;
161
/* write fast-load record if applicable */
164
oswp4(buf + 7, startpos);
165
if (osfwb(ctx->fiowcxffp, buf, 11)) err = ERR_WRTGAM;
169
* We're done with the object - delete it so that
170
* it doesn't have to be swapped out (which would
171
* be pointless, since we'll never need it again).
173
mcmunlck(mctx, (mcmon)obj);
174
mcmfre(mctx, (mcmon)obj);
178
/* all we must write is the name & number of ext func */
180
memcpy(buf + 4, t->toksnam, (size_t)t->tokslen);
181
if (osfwb(fp, buf, t->tokslen + 4)) err = ERR_WRTGAM;
183
/* write fast-load record if applicable */
185
&& osfwb(ctx->fiowcxffp, buf, t->tokslen + 4)) err = ERR_WRTGAM;
191
int e = (t->tokstyp == TOKSTFWDFN ? ERR_UNDEFF : ERR_UNDEFO);
195
/* write record for the forward reference */
196
p = mcmlck(mctx, (mcmon)obj);
197
siz = mcmobjsiz(mctx, (mcmon)obj);
199
if (osfwb(fp, buf, 5)
200
|| osfwb(fp, p, siz))
205
/* log the undefined-object error */
206
sup_log_undefobj(mctx, ec, e,
207
t->toksnam, (int)t->tokslen, obj);
209
/* count the undefined object */
213
* we don't need this object any longer - delete it so
214
* that we don't have to bother swapping it in or out
216
mcmfre(mctx, (mcmon)obj);
222
/* if an error occurred, signal it */
223
if (err) errsig(ec, err);
226
/* write game to binary file */
227
static void fiowrt1(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tokctx,
228
tokthdef *tab, uchar *fmts, uint fmtl, osfildef *fp,
229
uint flags, objnum preinit, int extc, uint prpcnt,
235
uchar buf[TOKNAMMAX + 50];
236
errcxdef *ec = vctx->voccxerr;
247
fiowcxdef cbctx; /* callback context for toktheach */
252
char fastnamebuf[OSFNMAX]; /* fast-load record temp file name */
255
/* generate appropriate file version */
258
case 'a': /* generate .GAM compatible with pre-2.0.15 runtimes */
260
vsnlen = sizeof(FIOVSNHDR2);
261
xor_seed = 17; /* use old xor values */
265
case 'b': /* generate 2.0.15+ format */
267
vsnlen = sizeof(FIOVSNHDR3);
271
case '*': /* current version */
273
vsnlen = sizeof(FIOVSNHDR);
277
errsig(ec, ERR_WRTVSN);
280
/* write file header and version header */
281
if (osfwb(fp, FIOFILHDR, sizeof(FIOFILHDR))
282
|| osfwb(fp, vsnhdr, vsnlen))
283
errsig(ec, ERR_WRTGAM);
286
* write the flags - remember where we wrote it in case we need to
287
* change the flags later
289
flag_seek = osfpos(fp);
291
if (osfwb(fp, buf, 2))
292
errsig(ec, ERR_WRTGAM);
294
/* write the timestamp */
296
tblock = localtime(&timer);
297
strcpy(vctx->voccxtim, asctime(tblock));
298
if (osfwb(fp, vctx->voccxtim, (size_t)26))
299
errsig(ec, ERR_WRTGAM);
301
/* write xor parameters if generating post 2.0.15 .GAM file */
302
if (filever[0] != 'a')
304
fpos = fiowhd(fp, ec, "\003XSI");
307
if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
308
fiowcls(fp, ec, fpos);
311
/* write count of external functions */
314
fpos = fiowhd(fp, ec, "\006EXTCNT");
315
oswp2(buf, extc); /* write the external function count */
316
if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
318
/* write XFCN-seek-location header if post 2.0.15 file version */
319
if (filever[0] != 'a')
321
oswp4(buf, 0); /* placeholder - TADSRSC sets this up later */
322
if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM);
325
/* close the resource */
326
fiowcls(fp, ec, fpos);
330
* write the character set information, if a character set was
333
if (G_cmap_id[0] != '\0')
337
/* this is not allowed with explicit file versions a, b, or c */
338
if (filever[0] == 'a' || filever[0] == 'b' || filever[0] == 'c')
339
errsig(ec, ERR_VNOCTAB);
341
/* write out the CHRSET resource header */
342
fpos = fiowhd(fp, ec, "\006CHRSET");
344
/* write out the ID and LDESC of the internal character set */
345
len = strlen(G_cmap_ldesc) + 1;
347
if (osfwb(fp, G_cmap_id, 4)
349
|| osfwb(fp, G_cmap_ldesc, len))
350
errsig(ec, ERR_WRTGAM);
352
/* close the resource */
353
fiowcls(fp, ec, fpos);
356
/* write OBJ resource header */
357
fpos = fiowhd(fp, ec, "\003OBJ");
359
/* set up the symbol table callback context for writing the objects */
360
cbctx.fiowcxmem = mctx;
361
cbctx.fiowcxerr = ec;
364
cbctx.fiowcxseed = xor_seed;
365
cbctx.fiowcxinc = xor_inc;
366
cbctx.fiowcxdebug = (flags & FIOFSYM);
367
if (flags & FIOFFAST)
369
/* try creating the temp file */
370
cbctx.fiowcxffp = os_create_tempfile(0, fastnamebuf);
372
/* if that failed, we don't have a fast-load table after all */
373
if (cbctx.fiowcxffp == 0)
378
/* clear the fast-load flag */
382
* go back to the flags we wrote to the .gam file header, and
383
* re-write the new flags without the fast-load bit - since
384
* we're not going to write a fast-load table, we don't want
385
* readers thinking they're going to find one
388
/* first, remember where we are right now */
391
/* seek back to the flags and re-write the new flags */
392
osfseek(fp, flag_seek, OSFSK_SET);
393
oswp2(flag_buf, flags);
394
if (osfwb(fp, flag_buf, 2))
395
errsig(ec, ERR_WRTGAM);
397
/* seek back to where we started */
398
osfseek(fp, curpos, OSFSK_SET);
402
cbctx.fiowcxffp = (osfildef *)0;
403
cbctx.fiowcxflg = flags;
405
/* go through symbol table, and write each object */
406
toktheach((toktdef *)tab, fiowrtobj, &cbctx);
408
/* also write all objects created with 'new' */
409
for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
414
for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
416
/* if the object was dynamically allocated, write it out */
417
if (*v && (*v)->vociflg & VOCIFNEW)
421
/* clear the 'new' flag, as this is a static object now */
422
(*v)->vociflg &= ~VOCIFNEW;
424
/* set up a toksdef, and write it out */
425
t.tokstyp = TOKSTOBJ;
427
fiowrtobj(&cbctx, &t);
432
/* close the resource */
433
fiowcls(fp, ec, fpos);
435
/* copy fast-load records to output file if applicable */
438
osfildef *fp2 = cbctx.fiowcxffp;
443
/* start with resource header for fast-load records */
444
fpos = fiowhd(fp, ec, "\003FST");
446
/* get length of temp file, then rewind it */
448
osfseek(fp2, 0L, OSFSK_SET);
450
/* copy the whole thing to the output file */
453
curlen = (len > sizeof(copybuf) ? sizeof(copybuf) : len);
454
if (osfrb(fp2, copybuf, (size_t)curlen)
455
|| osfwb(fp, copybuf, (size_t)curlen))
456
errsig(ec, ERR_WRTGAM);
460
/* close the fast-load resource */
461
fiowcls(fp, ec, fpos);
463
/* close and delete temp file */
465
osfdel_temp(fastnamebuf);
468
/* write vocabulary inheritance records - start with header */
469
fpos = fiowhd(fp, ec, "\003INH");
471
/* go through inheritance records and write to file */
472
for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
475
for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
479
buf[0] = (*v)->vociflg;
481
oswp2(buf + 3, (*v)->vociloc);
482
oswp2(buf + 5, (*v)->vociilc);
483
oswp2(buf + 7, (*v)->vocinsc);
484
for (k = 0, sc = (*v)->vocisc ; k < (*v)->vocinsc ; ++sc, ++k)
486
if (k + 9 >= sizeof(buf)) errsig(ec, ERR_FIOMSC);
487
oswp2(buf + 9 + (k << 1), (*sc));
489
if (osfwb(fp, buf, 9 + (2 * (*v)->vocinsc)))
490
errsig(ec, ERR_WRTGAM);
496
fiowcls(fp, ec, fpos);
498
/* write format strings */
501
fpos = fiowhd(fp, ec, "\006FMTSTR");
503
if (flags & FIOFCRYPT) fioxor(fmts, fmtl, xor_seed, xor_inc);
504
if (osfwb(fp, buf, 2) || osfwb(fp, fmts, fmtl))
505
errsig(ec, ERR_WRTGAM);
506
fiowcls(fp, ec, fpos);
509
/* write preinit if preinit object was specified */
512
fpos = fiowhd(fp, ec, "\007PREINIT");
514
if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
515
fiowcls(fp, ec, fpos);
518
/* write required objects out of voccxdef */
519
fpos = fiowhd(fp, ec, "\003REQ");
520
fiowrq(ec, fp, vctx->voccxme);
521
fiowrq(ec, fp, vctx->voccxvtk);
522
fiowrq(ec, fp, vctx->voccxstr);
523
fiowrq(ec, fp, vctx->voccxnum);
524
fiowrq(ec, fp, vctx->voccxprd);
525
fiowrq(ec, fp, vctx->voccxvag);
526
fiowrq(ec, fp, vctx->voccxini);
527
fiowrq(ec, fp, vctx->voccxpre);
528
fiowrq(ec, fp, vctx->voccxper);
529
fiowrq(ec, fp, vctx->voccxprom);
530
fiowrq(ec, fp, vctx->voccxpdis);
531
fiowrq(ec, fp, vctx->voccxper2);
532
fiowrq(ec, fp, vctx->voccxpdef);
533
fiowrq(ec, fp, vctx->voccxpask);
534
fiowrq(ec, fp, vctx->voccxppc);
535
fiowrq(ec, fp, vctx->voccxpask2);
536
fiowrq(ec, fp, vctx->voccxperp);
537
fiowrq(ec, fp, vctx->voccxpostprom);
538
fiowrq(ec, fp, vctx->voccxinitrestore);
539
fiowrq(ec, fp, vctx->voccxpuv);
540
fiowrq(ec, fp, vctx->voccxpnp);
541
fiowrq(ec, fp, vctx->voccxpostact);
542
fiowrq(ec, fp, vctx->voccxendcmd);
543
fiowrq(ec, fp, vctx->voccxprecmd);
544
fiowrq(ec, fp, vctx->voccxpask3);
545
fiowrq(ec, fp, vctx->voccxpre2);
546
fiowrq(ec, fp, vctx->voccxpdef2);
547
fiowcls(fp, ec, fpos);
549
/* write compound words */
552
fpos = fiowhd(fp, ec, "\004CMPD");
553
oswp2(buf, vctx->voccxcpl);
554
if (flags & FIOFCRYPT)
555
fioxor((uchar *)vctx->voccxcpp, (uint)vctx->voccxcpl,
557
if (osfwb(fp, buf, 2)
558
|| osfwb(fp, vctx->voccxcpp, (uint)vctx->voccxcpl))
559
errsig(ec, ERR_WRTGAM);
560
fiowcls(fp, ec, fpos);
563
/* write special words */
566
fpos = fiowhd(fp, ec, "\010SPECWORD");
567
oswp2(buf, vctx->voccxspl);
568
if (flags & FIOFCRYPT)
569
fioxor((uchar *)vctx->voccxspp, (uint)vctx->voccxspl,
571
if (osfwb(fp, buf, 2)
572
|| osfwb(fp, vctx->voccxspp, (uint)vctx->voccxspl))
573
errsig(ec, ERR_WRTGAM);
574
fiowcls(fp, ec, fpos);
577
/* write vocabulary */
578
fpos = fiowhd(fp, ec, "\003VOC");
580
/* go through each hash chain */
581
for (i = 0, vhsh = vctx->voccxhsh ; i < VOCHASHSIZ ; ++i, ++vhsh)
583
/* go through each word in this hash chain */
584
for (voc = *vhsh ; voc ; voc = voc->vocnxt)
586
/* encrypt the word if desired */
587
if (flags & FIOFCRYPT)
588
fioxor(voc->voctxt, (uint)(voc->voclen + voc->vocln2),
591
/* go through each object relation attached to the word */
592
for (vw = vocwget(vctx, voc->vocwlst) ; vw ;
593
vw = vocwget(vctx, vw->vocwnxt))
595
/* clear the 'new' flag, as this is a static object now */
596
vw->vocwflg &= ~VOCFNEW;
598
/* write out this object relation */
599
oswp2(buf, voc->voclen);
600
oswp2(buf+2, voc->vocln2);
601
oswp2(buf+4, vw->vocwtyp);
602
oswp2(buf+6, vw->vocwobj);
603
oswp2(buf+8, vw->vocwflg);
604
if (osfwb(fp, buf, 10)
605
|| osfwb(fp, voc->voctxt, voc->voclen + voc->vocln2))
606
errsig(ec, ERR_WRTGAM);
610
fiowcls(fp, ec, fpos);
612
/* write the symbol table, if desired */
615
fpos = fiowhd(fp, ec, "\006SYMTAB");
616
toktheach((toktdef *)tab, fiowrtsym, &cbctx);
618
/* indicate last symbol with a length of zero */
620
if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM);
621
fiowcls(fp, ec, fpos);
624
/* write line source chain, if desired */
625
if ((flags & (FIOFLIN | FIOFLIN2)) != 0 && vctx->voccxrun->runcxdbg != 0)
629
/* write the SRC header */
630
fpos = fiowhd(fp, ec, "\003SRC");
632
/* write the line records */
633
for (lin = vctx->voccxrun->runcxdbg->dbgcxlin ; lin ;
636
/* write this record */
638
errsig(ec, ERR_WRTGAM);
641
/* done with this section */
642
fiowcls(fp, ec, fpos);
645
* if we have new-style line records, put a SRC2 header (with no
646
* block contents) in the file, so that the debugger realizes
647
* that it has new-style line records (with line numbers rather
650
if ((flags & FIOFLIN2) != 0)
652
fpos = fiowhd(fp, ec, "\004SRC2");
653
fiowcls(fp, ec, fpos);
657
/* if writing a precompiled header, write some more information */
660
/* write property count */
661
fpos = fiowhd(fp, ec, "\006PRPCNT");
663
if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
664
fiowcls(fp, ec, fpos);
666
/* write preprocessor symbol table */
667
fpos = fiowhd(fp, ec, "\006TADSPP");
668
tok_write_defines(tokctx, fp, ec);
669
fiowcls(fp, ec, fpos);
672
/* write end-of-file resource header */
673
(void)fiowhd(fp, ec, "\004$EOF");
676
/* if there are undefined functions/objects, signal an error */
677
if (cbctx.fiowcxund) errsig(ec, ERR_UNDEF);
680
/* write game to binary file */
681
void fiowrt(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tokctx, tokthdef *tab,
682
uchar *fmts, uint fmtl, char *fname, uint flags, objnum preinit,
683
int extc, uint prpcnt, char *filever)
688
if (!(fp = osfoprwtb(fname, OSFTGAME)))
689
errsig(vctx->voccxerr, ERR_OPWGAM);
691
ERRBEGIN(vctx->voccxerr)
694
fiowrt1(mctx, vctx, tokctx, tab, fmts, fmtl, fp, flags, preinit,
695
extc, prpcnt, filever);
696
os_settype(fname, OSFTGAME);
698
ERRCLEAN(vctx->voccxerr)
699
/* clean up by closing the file */
701
ERRENDCLN(vctx->voccxerr)