#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/FIOWRT.C,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $"; #endif /* * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. * * Please see the accompanying license file, LICENSE.TXT, for information * on using and copying this software. */ /* Name fiowrt.c - write game to binary file Function Writes a game to binary file. Separated from fio.c so that run-time doesn't have to link in this function, which is unnecessary for playing a game. Notes None Modified 09/14/92 MJRoberts - note location of undefined objects in errors 04/11/92 MJRoberts - creation */ #include #include #include #include "os.h" #include "std.h" #include "mch.h" #include "mcm.h" #include "mcl.h" #include "tok.h" #include "obj.h" #include "voc.h" #include "fio.h" #include "dat.h" #include "sup.h" #include "cmap.h" /* write a resource header; returns pointer to next-res field in file */ static ulong fiowhd(osfildef *fp, errcxdef *ec, char *resname) { ulong ret; if (osfwb(fp, resname, (int)(resname[0] + 1))) errsig(ec, ERR_WRTGAM); ret = osfpos(fp); if (osfwb(fp, "\000\000\000\000", 4)) errsig(ec, ERR_WRTGAM); return(ret); } /* close a resource by writing next-res pointer */ static void fiowcls(osfildef *fp, errcxdef *ec, ulong respos) { ulong endpos; char buf[4]; endpos = osfpos(fp); osfseek(fp, respos, OSFSK_SET); oswp4(buf, endpos); if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM); osfseek(fp, endpos, OSFSK_SET); } /* write a required object (just an object number) */ static void fiowrq(errcxdef *ec, osfildef *fp, objnum objn) { uchar buf[2]; oswp2(buf, objn); if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); } /* context for fiowrtobj callback */ struct fiowcxdef { mcmcxdef *fiowcxmem; errcxdef *fiowcxerr; osfildef *fiowcxfp; uint fiowcxflg; int fiowcxund; osfildef *fiowcxffp; uint fiowcxseed; uint fiowcxinc; int fiowcxdebug; }; typedef struct fiowcxdef fiowcxdef; /* write out a symbol table entry */ static void fiowrtsym(void *ctx0, toksdef *t) { fiowcxdef *ctx = (fiowcxdef *)ctx0; uchar buf[TOKNAMMAX + 50]; errcxdef *ec = ctx->fiowcxerr; osfildef *fp = ctx->fiowcxfp; buf[0] = t->tokslen; buf[1] = t->tokstyp; oswp2(buf + 2, t->toksval); memcpy(buf + 4, t->toksnam, (size_t)buf[0]); if (osfwb(fp, buf, 4 + t->tokslen)) errsig(ec, ERR_WRTGAM); } /* write an object given by a symbol table entry */ static void fiowrtobj(void *ctx0, toksdef *t) { fiowcxdef *ctx = (fiowcxdef *)ctx0; uchar buf[TOKNAMMAX + 50]; mcmon obj; mcmcxdef *mctx = ctx->fiowcxmem; errcxdef *ec = ctx->fiowcxerr; osfildef *fp = ctx->fiowcxfp; uint flags = ctx->fiowcxflg; uchar *p; uint siz; uint used; int err = 0; ulong startpos = osfpos(fp); /* set up start of buffer to write */ buf[0] = t->tokstyp; obj = t->toksval; oswp2(buf + 1, obj); switch(t->tokstyp) { case TOKSTOBJ: /* * Mark object as finished with compilation. Note that we must * do this even though tcdmain() does this as well, because * running preinit() might have updated properties since the * last time we marked objects. */ objcomp(mctx, (objnum)obj, ctx->fiowcxdebug); /* get the object's size information */ p = mcmlck(mctx, (mcmon)obj); siz = mcmobjsiz(mctx, (mcmon)obj); /* size in cache */ used = objfree(p); /* size actually used in object */ if (objflg(p) & OBJFINDEX) used += objnprop(p) * 4; goto write_func_or_obj; case TOKSTFUNC: /* size of function is entire object */ p = mcmlck(mctx, (mcmon)obj); siz = used = mcmobjsiz(mctx, (mcmon)obj); write_func_or_obj: /* write type(OBJ) + objnum + size + sizeused */ oswp2(buf+3, siz); oswp2(buf+5, used); if (osfwb(fp, buf, 7)) err = ERR_WRTGAM; /* write contents of object */ if (flags & FIOFCRYPT) fioxor(p, used, ctx->fiowcxseed, ctx->fiowcxinc); if (osfwb(fp, p, used)) err = ERR_WRTGAM; /* write fast-load record if applicable */ if (ctx->fiowcxffp) { oswp4(buf + 7, startpos); if (osfwb(ctx->fiowcxffp, buf, 11)) err = ERR_WRTGAM; } /* * We're done with the object - delete it so that * it doesn't have to be swapped out (which would * be pointless, since we'll never need it again). */ mcmunlck(mctx, (mcmon)obj); mcmfre(mctx, (mcmon)obj); break; case TOKSTEXTERN: /* all we must write is the name & number of ext func */ buf[3] = t->tokslen; memcpy(buf + 4, t->toksnam, (size_t)t->tokslen); if (osfwb(fp, buf, t->tokslen + 4)) err = ERR_WRTGAM; /* write fast-load record if applicable */ if (ctx->fiowcxffp && osfwb(ctx->fiowcxffp, buf, t->tokslen + 4)) err = ERR_WRTGAM; break; case TOKSTFWDOBJ: case TOKSTFWDFN: { int e = (t->tokstyp == TOKSTFWDFN ? ERR_UNDEFF : ERR_UNDEFO); if (flags & FIOFBIN) { /* write record for the forward reference */ p = mcmlck(mctx, (mcmon)obj); siz = mcmobjsiz(mctx, (mcmon)obj); oswp2(buf+3, siz); if (osfwb(fp, buf, 5) || osfwb(fp, p, siz)) err = ERR_WRTGAM; } else { /* log the undefined-object error */ sup_log_undefobj(mctx, ec, e, t->toksnam, (int)t->tokslen, obj); /* count the undefined object */ ++(ctx->fiowcxund); /* * we don't need this object any longer - delete it so * that we don't have to bother swapping it in or out */ mcmfre(mctx, (mcmon)obj); } } break; } /* if an error occurred, signal it */ if (err) errsig(ec, err); } /* write game to binary file */ static void fiowrt1(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tokctx, tokthdef *tab, uchar *fmts, uint fmtl, osfildef *fp, uint flags, objnum preinit, int extc, uint prpcnt, char *filever) { int i; int j; int k; uchar buf[TOKNAMMAX + 50]; errcxdef *ec = vctx->voccxerr; ulong fpos; int obj; vocidef ***vpg; vocidef **v; objnum *sc; vocdef *voc; vocwdef *vw; vocdef **vhsh; struct tm *tblock; time_t timer; fiowcxdef cbctx; /* callback context for toktheach */ uint xor_seed = 63; uint xor_inc = 64; char *vsnhdr; uint vsnlen; char fastnamebuf[OSFNMAX]; /* fast-load record temp file name */ long flag_seek; /* generate appropriate file version */ switch(filever[0]) { case 'a': /* generate .GAM compatible with pre-2.0.15 runtimes */ vsnhdr = FIOVSNHDR2; vsnlen = sizeof(FIOVSNHDR2); xor_seed = 17; /* use old xor values */ xor_inc = 29; break; case 'b': /* generate 2.0.15+ format */ vsnhdr = FIOVSNHDR3; vsnlen = sizeof(FIOVSNHDR3); break; case 'c': case '*': /* current version */ vsnhdr = FIOVSNHDR; vsnlen = sizeof(FIOVSNHDR); break; default: errsig(ec, ERR_WRTVSN); } /* write file header and version header */ if (osfwb(fp, FIOFILHDR, sizeof(FIOFILHDR)) || osfwb(fp, vsnhdr, vsnlen)) errsig(ec, ERR_WRTGAM); /* * write the flags - remember where we wrote it in case we need to * change the flags later */ flag_seek = osfpos(fp); oswp2(buf, flags); if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); /* write the timestamp */ timer = time(NULL); tblock = localtime(&timer); strcpy(vctx->voccxtim, asctime(tblock)); if (osfwb(fp, vctx->voccxtim, (size_t)26)) errsig(ec, ERR_WRTGAM); /* write xor parameters if generating post 2.0.15 .GAM file */ if (filever[0] != 'a') { fpos = fiowhd(fp, ec, "\003XSI"); buf[0] = xor_seed; buf[1] = xor_inc; if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write count of external functions */ if (extc) { fpos = fiowhd(fp, ec, "\006EXTCNT"); oswp2(buf, extc); /* write the external function count */ if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); /* write XFCN-seek-location header if post 2.0.15 file version */ if (filever[0] != 'a') { oswp4(buf, 0); /* placeholder - TADSRSC sets this up later */ if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM); } /* close the resource */ fiowcls(fp, ec, fpos); } /* * write the character set information, if a character set was * specified */ if (G_cmap_id[0] != '\0') { size_t len; /* this is not allowed with explicit file versions a, b, or c */ if (filever[0] == 'a' || filever[0] == 'b' || filever[0] == 'c') errsig(ec, ERR_VNOCTAB); /* write out the CHRSET resource header */ fpos = fiowhd(fp, ec, "\006CHRSET"); /* write out the ID and LDESC of the internal character set */ len = strlen(G_cmap_ldesc) + 1; oswp2(buf, len); if (osfwb(fp, G_cmap_id, 4) || osfwb(fp, buf, 2) || osfwb(fp, G_cmap_ldesc, len)) errsig(ec, ERR_WRTGAM); /* close the resource */ fiowcls(fp, ec, fpos); } /* write OBJ resource header */ fpos = fiowhd(fp, ec, "\003OBJ"); /* set up the symbol table callback context for writing the objects */ cbctx.fiowcxmem = mctx; cbctx.fiowcxerr = ec; cbctx.fiowcxfp = fp; cbctx.fiowcxund = 0; cbctx.fiowcxseed = xor_seed; cbctx.fiowcxinc = xor_inc; cbctx.fiowcxdebug = (flags & FIOFSYM); if (flags & FIOFFAST) { /* try creating the temp file */ cbctx.fiowcxffp = os_create_tempfile(0, fastnamebuf); /* if that failed, we don't have a fast-load table after all */ if (cbctx.fiowcxffp == 0) { long curpos; char flag_buf[2]; /* clear the fast-load flag */ flags &= ~FIOFFAST; /* * go back to the flags we wrote to the .gam file header, and * re-write the new flags without the fast-load bit - since * we're not going to write a fast-load table, we don't want * readers thinking they're going to find one */ /* first, remember where we are right now */ curpos = osfpos(fp); /* seek back to the flags and re-write the new flags */ osfseek(fp, flag_seek, OSFSK_SET); oswp2(flag_buf, flags); if (osfwb(fp, flag_buf, 2)) errsig(ec, ERR_WRTGAM); /* seek back to where we started */ osfseek(fp, curpos, OSFSK_SET); } } else cbctx.fiowcxffp = (osfildef *)0; cbctx.fiowcxflg = flags; /* go through symbol table, and write each object */ toktheach((toktdef *)tab, fiowrtobj, &cbctx); /* close the resource */ fiowcls(fp, ec, fpos); /* copy fast-load records to output file if applicable */ if (cbctx.fiowcxffp) { osfildef *fp2 = cbctx.fiowcxffp; char copybuf[1024]; ulong len; ulong curlen; /* start with resource header for fast-load records */ fpos = fiowhd(fp, ec, "\003FST"); /* get length of temp file, then rewind it */ len = osfpos(fp2); osfseek(fp2, 0L, OSFSK_SET); /* copy the whole thing to the output file */ while (len) { curlen = (len > sizeof(copybuf) ? sizeof(copybuf) : len); if (osfrb(fp2, copybuf, (size_t)curlen) || osfwb(fp, copybuf, (size_t)curlen)) errsig(ec, ERR_WRTGAM); len -= curlen; } /* close the fast-load resource */ fiowcls(fp, ec, fpos); /* close and delete temp file */ osfcls(fp2); osfdel_temp(fastnamebuf); } /* write vocabulary inheritance records - start with header */ fpos = fiowhd(fp, ec, "\003INH"); /* go through inheritance records and write to file */ for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i) { if (!*vpg) continue; for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j) { if (*v) { buf[0] = (*v)->vociflg; oswp2(buf + 1, obj); oswp2(buf + 3, (*v)->vociloc); oswp2(buf + 5, (*v)->vociilc); oswp2(buf + 7, (*v)->vocinsc); for (k = 0, sc = (*v)->vocisc ; k < (*v)->vocinsc ; ++sc, ++k) { if (k + 9 >= sizeof(buf)) errsig(ec, ERR_FIOMSC); oswp2(buf + 9 + (k << 1), (*sc)); } if (osfwb(fp, buf, 9 + (2 * (*v)->vocinsc))) errsig(ec, ERR_WRTGAM); } } } /* close resource */ fiowcls(fp, ec, fpos); /* write format strings */ if (fmtl) { fpos = fiowhd(fp, ec, "\006FMTSTR"); oswp2(buf, fmtl); if (flags & FIOFCRYPT) fioxor(fmts, fmtl, xor_seed, xor_inc); if (osfwb(fp, buf, 2) || osfwb(fp, fmts, fmtl)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write preinit if preinit object was specified */ if (flags & FIOFPRE) { fpos = fiowhd(fp, ec, "\007PREINIT"); oswp2(buf, preinit); if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write required objects out of voccxdef */ fpos = fiowhd(fp, ec, "\003REQ"); fiowrq(ec, fp, vctx->voccxme); fiowrq(ec, fp, vctx->voccxvtk); fiowrq(ec, fp, vctx->voccxstr); fiowrq(ec, fp, vctx->voccxnum); fiowrq(ec, fp, vctx->voccxprd); fiowrq(ec, fp, vctx->voccxvag); fiowrq(ec, fp, vctx->voccxini); fiowrq(ec, fp, vctx->voccxpre); fiowrq(ec, fp, vctx->voccxper); fiowrq(ec, fp, vctx->voccxprom); fiowrq(ec, fp, vctx->voccxpdis); fiowrq(ec, fp, vctx->voccxper2); fiowrq(ec, fp, vctx->voccxpdef); fiowrq(ec, fp, vctx->voccxpask); fiowrq(ec, fp, vctx->voccxppc); fiowrq(ec, fp, vctx->voccxpask2); fiowrq(ec, fp, vctx->voccxperp); fiowrq(ec, fp, vctx->voccxpostprom); fiowrq(ec, fp, vctx->voccxinitrestore); fiowrq(ec, fp, vctx->voccxpuv); fiowrq(ec, fp, vctx->voccxpnp); fiowrq(ec, fp, vctx->voccxpostact); fiowrq(ec, fp, vctx->voccxendcmd); fiowrq(ec, fp, vctx->voccxprecmd); fiowrq(ec, fp, vctx->voccxpask3); fiowrq(ec, fp, vctx->voccxpre2); fiowrq(ec, fp, vctx->voccxpdef2); fiowcls(fp, ec, fpos); /* write compound words */ if (vctx->voccxcpl) { fpos = fiowhd(fp, ec, "\004CMPD"); oswp2(buf, vctx->voccxcpl); if (flags & FIOFCRYPT) fioxor((uchar *)vctx->voccxcpp, (uint)vctx->voccxcpl, xor_seed, xor_inc); if (osfwb(fp, buf, 2) || osfwb(fp, vctx->voccxcpp, (uint)vctx->voccxcpl)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write special words */ if (vctx->voccxspl) { fpos = fiowhd(fp, ec, "\010SPECWORD"); oswp2(buf, vctx->voccxspl); if (flags & FIOFCRYPT) fioxor((uchar *)vctx->voccxspp, (uint)vctx->voccxspl, xor_seed, xor_inc); if (osfwb(fp, buf, 2) || osfwb(fp, vctx->voccxspp, (uint)vctx->voccxspl)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write vocabulary */ fpos = fiowhd(fp, ec, "\003VOC"); /* go through each hash chain */ for (i = 0, vhsh = vctx->voccxhsh ; i < VOCHASHSIZ ; ++i, ++vhsh) { /* go through each word in this hash chain */ for (voc = *vhsh ; voc ; voc = voc->vocnxt) { /* encrypt the word if desired */ if (flags & FIOFCRYPT) fioxor(voc->voctxt, (uint)(voc->voclen + voc->vocln2), xor_seed, xor_inc); /* go through each object relation attached to the word */ for (vw = vocwget(vctx, voc->vocwlst) ; vw ; vw = vocwget(vctx, vw->vocwnxt)) { oswp2(buf, voc->voclen); oswp2(buf+2, voc->vocln2); oswp2(buf+4, vw->vocwtyp); oswp2(buf+6, vw->vocwobj); oswp2(buf+8, vw->vocwflg); if (osfwb(fp, buf, 10) || osfwb(fp, voc->voctxt, voc->voclen + voc->vocln2)) errsig(ec, ERR_WRTGAM); } } } fiowcls(fp, ec, fpos); /* write the symbol table, if desired */ if (flags & FIOFSYM) { fpos = fiowhd(fp, ec, "\006SYMTAB"); toktheach((toktdef *)tab, fiowrtsym, &cbctx); /* indicate last symbol with a length of zero */ buf[0] = 0; if (osfwb(fp, buf, 4)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); } /* write line source chain, if desired */ if ((flags & (FIOFLIN | FIOFLIN2)) != 0 && vctx->voccxrun->runcxdbg != 0) { lindef *lin; /* write the SRC header */ fpos = fiowhd(fp, ec, "\003SRC"); /* write the line records */ for (lin = vctx->voccxrun->runcxdbg->dbgcxlin ; lin ; lin = lin->linnxt) { /* write this record */ if (linwrt(lin, fp)) errsig(ec, ERR_WRTGAM); } /* done with this section */ fiowcls(fp, ec, fpos); /* * if we have new-style line records, put a SRC2 header (with no * block contents) in the file, so that the debugger realizes * that it has new-style line records (with line numbers rather * than seek offsets) */ if ((flags & FIOFLIN2) != 0) { fpos = fiowhd(fp, ec, "\004SRC2"); fiowcls(fp, ec, fpos); } } /* if writing a precompiled header, write some more information */ if (flags & FIOFBIN) { /* write property count */ fpos = fiowhd(fp, ec, "\006PRPCNT"); oswp2(buf, prpcnt); if (osfwb(fp, buf, 2)) errsig(ec, ERR_WRTGAM); fiowcls(fp, ec, fpos); /* write preprocessor symbol table */ fpos = fiowhd(fp, ec, "\006TADSPP"); tok_write_defines(tokctx, fp, ec); fiowcls(fp, ec, fpos); } /* write end-of-file resource header */ (void)fiowhd(fp, ec, "\004$EOF"); osfcls(fp); /* if there are undefined functions/objects, signal an error */ if (cbctx.fiowcxund) errsig(ec, ERR_UNDEF); } /* write game to binary file */ void fiowrt(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tokctx, tokthdef *tab, uchar *fmts, uint fmtl, char *fname, uint flags, objnum preinit, int extc, uint prpcnt, char *filever) { osfildef *fp; /* open the file */ if (!(fp = osfoprwtb(fname, OSFTGAME))) errsig(vctx->voccxerr, ERR_OPWGAM); ERRBEGIN(vctx->voccxerr) /* write the file */ fiowrt1(mctx, vctx, tokctx, tab, fmts, fmtl, fp, flags, preinit, extc, prpcnt, filever); os_settype(fname, OSFTGAME); ERRCLEAN(vctx->voccxerr) /* clean up by closing the file */ osfcls(fp); ERRENDCLN(vctx->voccxerr) }