cfad47cfa3/t2compiler/tads2/fiowrt.c

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
#ifdef RCSID
2
static char RCSid[] =
3
"$Header: d:/cvsroot/tads/TADS2/FIOWRT.C,v 1.3 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
  fiowrt.c - write game to binary file
15
Function
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
18
  a game.
19
Notes
20
  None
21
Modified
22
  09/14/92 MJRoberts     - note location of undefined objects in errors
23
  04/11/92 MJRoberts     - creation
24
*/
25
26
#include <stdlib.h>
27
#include <string.h>
28
#include <time.h>
29
#include "os.h"
30
#include "std.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 "sup.h"
40
#include "cmap.h"
41
42
43
/* write a resource header; returns pointer to next-res field in file */
44
static ulong fiowhd(osfildef *fp, errcxdef *ec, char *resname)
45
{
46
    ulong ret;
47
48
    if (osfwb(fp, resname, (int)(resname[0] + 1))) errsig(ec, ERR_WRTGAM);
49
    ret = osfpos(fp);
50
    if (osfwb(fp, "\000\000\000\000", 4)) errsig(ec, ERR_WRTGAM);
51
    return(ret);
52
}
53
54
/* close a resource by writing next-res pointer */
55
static void fiowcls(osfildef *fp, errcxdef *ec, ulong respos)
56
{
57
    ulong endpos;
58
    char  buf[4];
59
    
60
    endpos = osfpos(fp);
61
    osfseek(fp, respos, OSFSK_SET);
62
    oswp4(buf, endpos);
63
    if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM);
64
    osfseek(fp, endpos, OSFSK_SET);
65
}
66
67
/* write a required object (just an object number) */
68
static void fiowrq(errcxdef *ec, osfildef *fp, objnum objn)
69
{
70
    uchar buf[2];
71
    
72
    oswp2(buf, objn);
73
    if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
74
}
75
76
/* context for fiowrtobj callback */
77
struct fiowcxdef
78
{
79
    mcmcxdef *fiowcxmem;
80
    errcxdef *fiowcxerr;
81
    osfildef *fiowcxfp;
82
    uint      fiowcxflg;
83
    int       fiowcxund;
84
    osfildef *fiowcxffp;
85
    uint      fiowcxseed;
86
    uint      fiowcxinc;
87
    int       fiowcxdebug;
88
};
89
typedef struct fiowcxdef fiowcxdef;
90
91
/* write out a symbol table entry */
92
static void fiowrtsym(void *ctx0, toksdef *t)
93
{
94
    fiowcxdef  *ctx = (fiowcxdef *)ctx0;
95
    uchar       buf[TOKNAMMAX + 50];
96
    errcxdef   *ec = ctx->fiowcxerr;
97
    osfildef   *fp = ctx->fiowcxfp;
98
99
    buf[0] = t->tokslen;
100
    buf[1] = t->tokstyp;
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);
104
}
105
106
/* write an object given by a symbol table entry */
107
static void fiowrtobj(void *ctx0, toksdef *t)
108
{
109
    fiowcxdef *ctx = (fiowcxdef *)ctx0;
110
    uchar      buf[TOKNAMMAX + 50];
111
    mcmon      obj;
112
    mcmcxdef  *mctx = ctx->fiowcxmem;
113
    errcxdef  *ec = ctx->fiowcxerr;
114
    osfildef  *fp = ctx->fiowcxfp;
115
    uint       flags = ctx->fiowcxflg;
116
    uchar     *p;
117
    uint       siz;
118
    uint       used;
119
    int        err = 0;
120
    ulong      startpos = osfpos(fp);
121
    
122
    /* set up start of buffer to write */
123
    buf[0] = t->tokstyp;
124
    obj = t->toksval;
125
    oswp2(buf + 1, obj);
126
    
127
    switch(t->tokstyp)
128
    {
129
    case TOKSTOBJ:
130
        /* 
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.  
135
         */
136
        objcomp(mctx, (objnum)obj, ctx->fiowcxdebug);
137
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;
144
                
145
    case TOKSTFUNC:
146
        /* size of function is entire object */
147
        p = mcmlck(mctx, (mcmon)obj);
148
        siz = used = mcmobjsiz(mctx, (mcmon)obj);
149
150
    write_func_or_obj:
151
        /* write type(OBJ) + objnum + size + sizeused */
152
        oswp2(buf+3, siz);
153
        oswp2(buf+5, used);
154
        if (osfwb(fp, buf, 7)) err = ERR_WRTGAM;
155
                
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;
160
        
161
        /* write fast-load record if applicable */
162
        if (ctx->fiowcxffp)
163
        {
164
            oswp4(buf + 7, startpos);
165
            if (osfwb(ctx->fiowcxffp, buf, 11)) err = ERR_WRTGAM;
166
        }
167
                
168
        /*
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).
172
         */
173
        mcmunlck(mctx, (mcmon)obj);
174
        mcmfre(mctx, (mcmon)obj);
175
        break;
176
                
177
    case TOKSTEXTERN:
178
        /* all we must write is the name & number of ext func */
179
        buf[3] = t->tokslen;
180
        memcpy(buf + 4, t->toksnam, (size_t)t->tokslen);
181
        if (osfwb(fp, buf, t->tokslen + 4)) err = ERR_WRTGAM;
182
        
183
        /* write fast-load record if applicable */
184
        if (ctx->fiowcxffp
185
            && osfwb(ctx->fiowcxffp, buf, t->tokslen + 4)) err = ERR_WRTGAM;
186
        break;
187
                
188
    case TOKSTFWDOBJ:
189
    case TOKSTFWDFN:
190
        {
191
            int  e = (t->tokstyp == TOKSTFWDFN ? ERR_UNDEFF : ERR_UNDEFO);
192
193
            if (flags & FIOFBIN)
194
            {
195
                /* write record for the forward reference */
196
                p = mcmlck(mctx, (mcmon)obj);
197
                siz = mcmobjsiz(mctx, (mcmon)obj);
198
                oswp2(buf+3, siz);
199
                if (osfwb(fp, buf, 5)
200
                    || osfwb(fp, p, siz))
201
                    err = ERR_WRTGAM;
202
            }
203
            else
204
            {
205
                /* log the undefined-object error */
206
                sup_log_undefobj(mctx, ec, e,
207
                                 t->toksnam, (int)t->tokslen, obj);
208
209
                /* count the undefined object */
210
                ++(ctx->fiowcxund);
211
212
                /*
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
215
                 */
216
                mcmfre(mctx, (mcmon)obj);
217
            }
218
        }
219
        break;
220
    }
221
                    
222
    /* if an error occurred, signal it */
223
    if (err) errsig(ec, err);
224
}
225
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,
230
                    char *filever)
231
{
232
    int         i;
233
    int         j;
234
    int         k;
235
    uchar       buf[TOKNAMMAX + 50];
236
    errcxdef   *ec = vctx->voccxerr;
237
    ulong       fpos;
238
    int         obj;
239
    vocidef  ***vpg;
240
    vocidef   **v;
241
    objnum     *sc;
242
    vocdef     *voc;
243
    vocwdef    *vw;
244
    vocdef    **vhsh;
245
    struct tm  *tblock;
246
    time_t      timer;
247
    fiowcxdef   cbctx;                    /* callback context for toktheach */
248
    uint        xor_seed = 63;
249
    uint        xor_inc = 64;
250
    char       *vsnhdr;
251
    uint        vsnlen;
252
    char        fastnamebuf[OSFNMAX];    /* fast-load record temp file name */
253
    long        flag_seek;
254
255
    /* generate appropriate file version */
256
    switch(filever[0])
257
    {
258
    case 'a':          /* generate .GAM compatible with pre-2.0.15 runtimes */
259
        vsnhdr = FIOVSNHDR2;
260
        vsnlen = sizeof(FIOVSNHDR2);
261
        xor_seed = 17;                                /* use old xor values */
262
        xor_inc = 29;
263
        break;
264
265
    case 'b':                                    /* generate 2.0.15+ format */
266
        vsnhdr = FIOVSNHDR3;
267
        vsnlen = sizeof(FIOVSNHDR3);
268
        break;
269
270
    case 'c':
271
    case '*':                                            /* current version */
272
        vsnhdr = FIOVSNHDR;
273
        vsnlen = sizeof(FIOVSNHDR);
274
        break;
275
276
    default:
277
        errsig(ec, ERR_WRTVSN);
278
    }
279
280
    /* write file header and version header */
281
    if (osfwb(fp, FIOFILHDR, sizeof(FIOFILHDR))
282
          || osfwb(fp, vsnhdr, vsnlen))
283
        errsig(ec, ERR_WRTGAM);
284
285
    /* 
286
     *   write the flags - remember where we wrote it in case we need to
287
     *   change the flags later 
288
     */
289
    flag_seek = osfpos(fp);
290
    oswp2(buf, flags);
291
    if (osfwb(fp, buf, 2))
292
        errsig(ec, ERR_WRTGAM);
293
294
    /* write the timestamp */
295
    timer = time(NULL);
296
    tblock = localtime(&timer);
297
    strcpy(vctx->voccxtim, asctime(tblock));
298
    if (osfwb(fp, vctx->voccxtim, (size_t)26))
299
        errsig(ec, ERR_WRTGAM);
300
301
    /* write xor parameters if generating post 2.0.15 .GAM file */
302
    if (filever[0] != 'a')
303
    {
304
        fpos = fiowhd(fp, ec, "\003XSI");
305
        buf[0] = xor_seed;
306
        buf[1] = xor_inc;
307
        if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
308
        fiowcls(fp, ec, fpos);
309
    }
310
    
311
    /* write count of external functions */
312
    if (extc)
313
    {
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);
317
318
        /* write XFCN-seek-location header if post 2.0.15 file version */
319
        if (filever[0] != 'a')
320
        {
321
            oswp4(buf, 0);      /* placeholder - TADSRSC sets this up later */
322
            if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM);
323
        }
324
325
        /* close the resource */
326
        fiowcls(fp, ec, fpos);
327
    }
328
    
329
    /* 
330
     *   write the character set information, if a character set was
331
     *   specified 
332
     */
333
    if (G_cmap_id[0] != '\0')
334
    {
335
        size_t len;
336
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);
340
341
        /* write out the CHRSET resource header */
342
        fpos = fiowhd(fp, ec, "\006CHRSET");
343
344
        /* write out the ID and LDESC of the internal character set */
345
        len = strlen(G_cmap_ldesc) + 1;
346
        oswp2(buf, len);
347
        if (osfwb(fp, G_cmap_id, 4)
348
            || osfwb(fp, buf, 2)
349
            || osfwb(fp, G_cmap_ldesc, len))
350
            errsig(ec, ERR_WRTGAM);
351
352
        /* close the resource */
353
        fiowcls(fp, ec, fpos);
354
    }
355
356
    /* write OBJ resource header */
357
    fpos = fiowhd(fp, ec, "\003OBJ");
358
359
    /* set up the symbol table callback context for writing the objects */
360
    cbctx.fiowcxmem = mctx;
361
    cbctx.fiowcxerr = ec;
362
    cbctx.fiowcxfp  = fp;
363
    cbctx.fiowcxund = 0;
364
    cbctx.fiowcxseed = xor_seed;
365
    cbctx.fiowcxinc = xor_inc;
366
    cbctx.fiowcxdebug = (flags & FIOFSYM);
367
    if (flags & FIOFFAST)
368
    {
369
        /* try creating the temp file */
370
        cbctx.fiowcxffp = os_create_tempfile(0, fastnamebuf);
371
372
        /* if that failed, we don't have a fast-load table after all */
373
        if (cbctx.fiowcxffp == 0)
374
        {
375
            long curpos;
376
            char flag_buf[2];
377
            
378
            /* clear the fast-load flag */
379
            flags &= ~FIOFFAST;
380
381
            /* 
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 
386
             */
387
388
            /* first, remember where we are right now */
389
            curpos = osfpos(fp);
390
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);
396
397
            /* seek back to where we started */
398
            osfseek(fp, curpos, OSFSK_SET);
399
        }
400
    }
401
    else
402
        cbctx.fiowcxffp = (osfildef *)0;
403
    cbctx.fiowcxflg = flags;
404
405
    /* go through symbol table, and write each object */
406
    toktheach((toktdef *)tab, fiowrtobj, &cbctx);
407
408
    /* also write all objects created with 'new' */
409
    for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
410
    {
411
        objnum obj;
412
413
        if (!*vpg) continue;
414
        for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
415
        {
416
            /* if the object was dynamically allocated, write it out */
417
            if (*v && (*v)->vociflg & VOCIFNEW)
418
            {
419
                toksdef t;
420
421
                /* clear the 'new' flag, as this is a static object now */
422
                (*v)->vociflg &= ~VOCIFNEW;
423
424
                /* set up a toksdef, and write it out */
425
                t.tokstyp = TOKSTOBJ;
426
                t.toksval = obj;
427
                fiowrtobj(&cbctx, &t);
428
            }
429
        }
430
    }
431
                    
432
    /* close the resource */
433
    fiowcls(fp, ec, fpos);
434
    
435
    /* copy fast-load records to output file if applicable */
436
    if (cbctx.fiowcxffp)
437
    {
438
        osfildef *fp2 = cbctx.fiowcxffp;
439
        char      copybuf[1024];
440
        ulong     len;
441
        ulong     curlen;
442
        
443
        /* start with resource header for fast-load records */
444
        fpos = fiowhd(fp, ec, "\003FST");
445
446
        /* get length of temp file, then rewind it */
447
        len = osfpos(fp2);
448
        osfseek(fp2, 0L, OSFSK_SET);
449
450
        /* copy the whole thing to the output file */
451
        while (len)
452
        {
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);
457
            len -= curlen;
458
        }
459
460
        /* close the fast-load resource */
461
        fiowcls(fp, ec, fpos);
462
        
463
        /* close and delete temp file */
464
        osfcls(fp2);
465
        osfdel_temp(fastnamebuf);
466
    }
467
    
468
    /* write vocabulary inheritance records - start with header */
469
    fpos = fiowhd(fp, ec, "\003INH");
470
    
471
    /* go through inheritance records and write to file */
472
    for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
473
    {
474
        if (!*vpg) continue;
475
        for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
476
        {
477
            if (*v)
478
            {
479
                buf[0] = (*v)->vociflg;
480
                oswp2(buf + 1, obj);
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)
485
                {
486
                    if (k + 9 >= sizeof(buf)) errsig(ec, ERR_FIOMSC);
487
                    oswp2(buf + 9 + (k << 1), (*sc));
488
                }
489
                if (osfwb(fp, buf, 9 + (2 * (*v)->vocinsc)))
490
                    errsig(ec, ERR_WRTGAM);
491
            }
492
        }
493
    }
494
    
495
    /* close resource */
496
    fiowcls(fp, ec, fpos);
497
    
498
    /* write format strings */
499
    if (fmtl)
500
    {
501
        fpos = fiowhd(fp, ec, "\006FMTSTR");
502
        oswp2(buf, fmtl);
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);
507
    }
508
    
509
    /* write preinit if preinit object was specified */
510
    if (flags & FIOFPRE)
511
    {
512
        fpos = fiowhd(fp, ec, "\007PREINIT");
513
        oswp2(buf, preinit);
514
        if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
515
        fiowcls(fp, ec, fpos);
516
    }
517
    
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);
548
    
549
    /* write compound words */
550
    if (vctx->voccxcpl)
551
    {
552
        fpos = fiowhd(fp, ec, "\004CMPD");
553
        oswp2(buf, vctx->voccxcpl);
554
        if (flags & FIOFCRYPT)
555
            fioxor((uchar *)vctx->voccxcpp, (uint)vctx->voccxcpl,
556
                   xor_seed, xor_inc);
557
        if (osfwb(fp, buf, 2)
558
            || osfwb(fp, vctx->voccxcpp, (uint)vctx->voccxcpl))
559
            errsig(ec, ERR_WRTGAM);
560
        fiowcls(fp, ec, fpos);
561
    }
562
    
563
    /* write special words */
564
    if (vctx->voccxspl)
565
    {
566
        fpos = fiowhd(fp, ec, "\010SPECWORD");
567
        oswp2(buf, vctx->voccxspl);
568
        if (flags & FIOFCRYPT)
569
            fioxor((uchar *)vctx->voccxspp, (uint)vctx->voccxspl,
570
                   xor_seed, xor_inc);
571
        if (osfwb(fp, buf, 2)
572
            || osfwb(fp, vctx->voccxspp, (uint)vctx->voccxspl))
573
            errsig(ec, ERR_WRTGAM);
574
        fiowcls(fp, ec, fpos);
575
    }
576
    
577
    /* write vocabulary */
578
    fpos = fiowhd(fp, ec, "\003VOC");
579
580
    /* go through each hash chain */
581
    for (i = 0, vhsh = vctx->voccxhsh ; i < VOCHASHSIZ ; ++i, ++vhsh)
582
    {
583
        /* go through each word in this hash chain */
584
        for (voc = *vhsh ; voc ; voc = voc->vocnxt)
585
        {
586
            /* encrypt the word if desired */
587
            if (flags & FIOFCRYPT)
588
                fioxor(voc->voctxt, (uint)(voc->voclen + voc->vocln2),
589
                       xor_seed, xor_inc);
590
591
            /* go through each object relation attached to the word */
592
            for (vw = vocwget(vctx, voc->vocwlst) ; vw ;
593
                 vw = vocwget(vctx, vw->vocwnxt))
594
            {
595
                /* clear the 'new' flag, as this is a static object now */
596
                vw->vocwflg &= ~VOCFNEW;
597
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);
607
            }
608
        }
609
    }
610
    fiowcls(fp, ec, fpos);
611
    
612
    /* write the symbol table, if desired */
613
    if (flags & FIOFSYM)
614
    {
615
        fpos = fiowhd(fp, ec, "\006SYMTAB");
616
        toktheach((toktdef *)tab, fiowrtsym, &cbctx);
617
618
        /* indicate last symbol with a length of zero */
619
        buf[0] = 0;
620
        if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM);
621
        fiowcls(fp, ec, fpos);
622
    }
623
    
624
    /* write line source chain, if desired */
625
    if ((flags & (FIOFLIN | FIOFLIN2)) != 0 && vctx->voccxrun->runcxdbg != 0)
626
    {
627
        lindef *lin;
628
629
        /* write the SRC header */
630
        fpos = fiowhd(fp, ec, "\003SRC");
631
632
        /* write the line records */
633
        for (lin = vctx->voccxrun->runcxdbg->dbgcxlin ; lin ;
634
             lin = lin->linnxt)
635
        {
636
            /* write this record */
637
            if (linwrt(lin, fp))
638
                errsig(ec, ERR_WRTGAM);
639
        }
640
641
        /* done with this section */
642
        fiowcls(fp, ec, fpos);
643
644
        /* 
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
648
         *   than seek offsets) 
649
         */
650
        if ((flags & FIOFLIN2) != 0)
651
        {
652
            fpos = fiowhd(fp, ec, "\004SRC2");
653
            fiowcls(fp, ec, fpos);
654
        }
655
    }
656
657
    /* if writing a precompiled header, write some more information */
658
    if (flags & FIOFBIN)
659
    {
660
        /* write property count */
661
        fpos = fiowhd(fp, ec, "\006PRPCNT");
662
        oswp2(buf, prpcnt);
663
        if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM);
664
        fiowcls(fp, ec, fpos);
665
666
        /* write preprocessor symbol table */
667
        fpos = fiowhd(fp, ec, "\006TADSPP");
668
        tok_write_defines(tokctx, fp, ec);
669
        fiowcls(fp, ec, fpos);
670
    }
671
672
    /* write end-of-file resource header */
673
    (void)fiowhd(fp, ec, "\004$EOF");
674
    osfcls(fp);
675
    
676
    /* if there are undefined functions/objects, signal an error */
677
    if (cbctx.fiowcxund) errsig(ec, ERR_UNDEF);
678
}
679
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)
684
{
685
    osfildef *fp;
686
    
687
    /* open the file */
688
    if (!(fp = osfoprwtb(fname, OSFTGAME)))
689
        errsig(vctx->voccxerr, ERR_OPWGAM);
690
    
691
    ERRBEGIN(vctx->voccxerr)
692
    
693
    /* write the file */
694
    fiowrt1(mctx, vctx, tokctx, tab, fmts, fmtl, fp, flags, preinit,
695
            extc, prpcnt, filever);
696
    os_settype(fname, OSFTGAME);
697
   
698
    ERRCLEAN(vctx->voccxerr)
699
        /* clean up by closing the file */
700
        osfcls(fp);
701
    ERRENDCLN(vctx->voccxerr)
702
}