/* * load or reload our image data */ void CVmObjFrameDesc::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* read the frame information */ vm_obj_id_t fref = vmb_get_objid(ptr); int frame_idx = osrp2(ptr + VMB_OBJECT_ID); uint ret_ofs = osrp2(ptr + VMB_OBJECT_ID + 2); /* allocate the extension */ alloc_ext(vmg_ fref, frame_idx, ret_ofs); }
/* * Read preprocessor state from a file */ void tok_read_defines(tokcxdef *ctx, osfildef *fp, errcxdef *ec) { int i; tokdfdef **dfp; tokdfdef *df; char buf[4]; /* write each element of the hash chains */ for (i = TOKDFHSHSIZ, dfp = ctx->tokcxdf ; i ; ++dfp, --i) { /* read this hash chain */ for (;;) { /* read the next entry's header, and stop if this is the end */ if (osfrb(fp, buf, 4)) errsig(ec, ERR_RDGAM); if (osrp2(buf) == 0) break; /* set up a new symbol of the appropriate size */ df = (tokdfdef *)mchalo(ec, (sizeof(tokdfdef) + osrp2(buf) + osrp2(buf+2) - 1), "tok_read_defines"); df->explen = osrp2(buf+2); df->nm = df->expan + df->explen; df->len = osrp2(buf); /* read the rest of the symbol */ if (osfrb(fp, df->nm, df->len) || (df->explen != 0 && osfrb(fp, df->expan, df->explen))) errsig(ec, ERR_RDGAM); /* * If a symbol with this name already exists in the table, * discard the new one -- the symbols defined by -D and the * current set of built-in symbols takes precedence over the * set loaded from the file. */ if (tok_find_define(ctx, df->nm, df->len)) { /* simply discard this symbol */ mchfre(df); } else { /* link it into this hash chain */ df->nxt = *dfp; *dfp = df; } } } }
/* * load or reload our image data */ void CVmObjFrameRef::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* read the number of variables */ int nlocals = osrp2(ptr); int nparams = osrp2(ptr+2); ptr += 4; /* allocate the extension */ vm_frameref_ext *ext = alloc_ext(vmg_ nlocals, nparams); /* * Since stack frames are inherently transient, a saved frame ref * object can't point back to a live stack frame, so on restore we have * to assume that our stack frame is inactive. */ ext->fp = 0; /* load the entry pointer */ vmb_get_dh(ptr, &ext->entry); ptr += VMB_DATAHOLDER; /* after other objects are loaded, resolve the entry pointer */ G_obj_table->request_post_load_init(self); /* load the method context variables */ vmb_get_dh(ptr, &ext->self); ptr += VMB_DATAHOLDER; ext->defobj = vmb_get_objid(ptr); ptr += VMB_OBJECT_ID; ext->targobj = vmb_get_objid(ptr); ptr += VMB_OBJECT_ID; ext->targprop = vmb_get_propid(ptr); ptr += VMB_PROP_ID; vmb_get_dh(ptr, &ext->invokee); ptr += VMB_DATAHOLDER; /* read the variable snapshot values */ int i; vm_val_t *v; for (i = nlocals + nparams, v = ext->vars ; i > 0 ; --i, ++v) { vmb_get_dh(ptr, v); ptr += VMB_DATAHOLDER; } }
/* * Given a saved state file, get the name of the image file that was * loaded when the state file was created. */ int CVmSaveFile::restore_get_image(osfildef *fp, char *fname_buf, size_t fname_buf_len) { /* read the signature, size/checksum, and timestamp fields */ char buf[128]; if (osfrb(fp, buf, sizeof(VMSAVEFILE_SIG)-1 + 8 + 24)) return VMERR_READ_FILE; /* check the signature */ if (memcmp(buf, VMSAVEFILE_SIG, sizeof(VMSAVEFILE_SIG)-1) != 0) return VMERR_NOT_SAVED_STATE; /* read the length of the image file name */ if (osfrb(fp, buf, 2)) return VMERR_READ_FILE; /* get the length from the buffer */ size_t len = osrp2(buf); /* if it won't fit in the buffer, return an error */ if (len + 1 > fname_buf_len) return VMERR_READ_FILE; /* read the name into the caller's buffer */ if (osfrb(fp, fname_buf, len)) return VMERR_READ_FILE; /* null-terminate the name */ fname_buf[len] = '\0'; /* success */ return 0; }
/* load image data */ void CVmObjClass::load_image_data(VMG_ vm_obj_id_t self, const char *ptr, size_t siz) { /* make sure the length is valid */ if (siz < 8) err_throw(VMERR_INVAL_METACLASS_DATA); /* allocate or reallocate the extension */ vm_intcls_ext *ext = alloc_ext(vmg0_); /* * read the metaclass index and modifier object ID; note that modifier * objects are always root set objects, so there's no need to worry * about fixups */ ext->meta_idx = osrp2(ptr+2); ext->mod_obj = (vm_obj_id_t)t3rp4u(ptr+4); /* if we have a class state value, read it */ ext->class_state.set_nil(); if (siz >= 2+4+VMB_DATAHOLDER) vmb_get_dh(ptr+8, &ext->class_state); /* register myself */ register_meta(vmg_ self); }
size_t Data::size() const { switch (_type) { case DAT_NUMBER: return 4; // numbers are in 4-byte lsb-first format case DAT_OBJECT: return 2; // object numbers are in 2-byte lsb-first format case DAT_SSTRING: case DAT_DSTRING: case DAT_LIST: return osrp2(_ptr); case DAT_NIL: case DAT_TRUE: return 0; case DAT_PROPNUM: case DAT_SYN: case DAT_FNADDR: case DAT_REDIR: return 2; case DAT_TPL: // template is counted array of 10-byte entries, plus length byte */ return 1 + ((*(uchar *)_ptr) * VOCTPLSIZ); case DAT_TPL2: return 1 + ((*(uchar *)_ptr) * VOCTPL2SIZ); default: return 0; } }
/* * Get only the value portion of a vm_val_t from a portable data holder */ void vmb_get_dh_val(const char *buf, vm_val_t *val) { /* read the format appropriate to the type */ switch((vm_datatype_t)buf[0]) { case VM_OBJ: case VM_OBJX: /* get the object ID from the UINT4 */ val->val.obj = (vm_obj_id_t)t3rp4u(buf+1); break; case VM_PROP: /* get the property ID from the UINT2 */ val->val.prop = (vm_prop_id_t)osrp2(buf+1); break; case VM_INT: val->val.intval = osrp4(buf+1); break; case VM_BIFPTR: case VM_BIFPTRX: /* read the function index and set index as UINT2s */ val->val.bifptr.func_idx = osrp2(buf+1); val->val.bifptr.set_idx = osrp2(buf+3); break; case VM_ENUM: /* get the enumerated constant value from the UINT4 */ val->val.enumval = t3rp4u(buf+1); break; case VM_SSTRING: case VM_DSTRING: case VM_LIST: case VM_CODEOFS: case VM_FUNCPTR: /* get the offset value from the UINT4 */ val->val.ofs = t3rp4u(buf+1); break; default: /* other types have no additional data or cannot be put in a DH */ break; } }
/* * Show an exception table */ void CTcT3Unasm::show_exc_table(class CTcUnasSrc *src, class CTcUnasOut *out, unsigned long base_ofs) { unsigned entries; char ch[10]; /* read the number of entries */ src->next_byte(ch); src->next_byte(ch+1); /* show the entries */ for (entries = osrp2(ch) ; entries != 0 ; --entries) { unsigned long start_ofs; unsigned long end_ofs; unsigned exc_obj_id; unsigned long catch_ofs; /* read the code start offset */ src->next_byte(ch); src->next_byte(ch+1); start_ofs = base_ofs + osrp2(ch); /* read the code end offset */ src->next_byte(ch); src->next_byte(ch+1); end_ofs = base_ofs + osrp2(ch); /* read the object ID */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); exc_obj_id = base_ofs + t3rp4u(ch); /* read the catch offset */ src->next_byte(ch); src->next_byte(ch+1); catch_ofs = base_ofs + osrp2(ch); /* show it */ out->print(" from %lx to %lx object %x catch %lx\n", start_ofs, end_ofs, exc_obj_id, catch_ofs); } }
/* * write out a runtime length-prefixed string */ void outfmt(tiocxdef *ctx, uchar *txt) { uint len; VARUSED(ctx); /* read the length prefix */ len = osrp2(txt) - 2; txt += 2; /* write out the string */ tioputslen(ctx, (char *)txt, len); }
void CVmDbgFrameSymPtr::get_str_val(VMG_ vm_val_t *val) const { /* get a pointer to the symbol field in this record */ const char *symp = (const char *)p_ + G_dbg_lclsym_hdr_size; /* check whether the symbol string is in-line or in the constant pool */ if (is_sym_inline()) { /* it's inline, so we need to create a string object for it */ val->set_obj(CVmObjString::create(vmg_ FALSE, symp + 2, osrp2(symp))); } else { /* it's in the constant pool, so we can just return a VM_SSTR */ val->set_sstring(osrp4(symp)); } }
void emtval(emtcxdef *ctx, tokdef *tok, uchar *base) { ushort oplen; switch(tok->toktyp) { case TOKTSYMBOL: switch(tok->toksym.tokstyp) { case TOKSTARGC: emtop(ctx, OPCARGC); return; case TOKSTSELF: emtop(ctx, OPCPUSHSELF); return; case TOKSTOBJ: case TOKSTFWDOBJ: emtop(ctx, OPCPUSHOBJ); break; case TOKSTLOCAL: if (tok->toksym.toksfr) { emtop(ctx, OPCGETDBLCL); /* debug local - specifies frame */ emtint2(ctx, ctx->emtcxfrob); /* emit frame object */ emtint2(ctx, tok->toksym.toksfr); /* emit frame offset */ } else emtop(ctx, OPCGETLCL); break; case TOKSTBIFN: emtop(ctx, OPCBUILTIN); emtbyte(ctx, 0); break; case TOKSTFUNC: case TOKSTFWDFN: emtop(ctx, OPCPUSHFN); break; case TOKSTPROP: emtop(ctx, OPCGETPSELF); emtbyte(ctx, 0); break; case TOKSTPROPSPEC: emtop(ctx, OPCGETPSELFDATA); break; default: errsig(ctx->emtcxerr, ERR_REQOBJ); /* NOTREACHED */ } emtint2(ctx, tok->toksym.toksval); return; case TOKTNUMBER: emtop(ctx, OPCPUSHNUM); emtint4(ctx, tok->tokval); return; case TOKTNIL: emtop(ctx, OPCPUSHNIL); return; case TOKTTRUE: emtop(ctx, OPCPUSHTRUE); return; case TOKTPOUND: emtop(ctx, OPCPUSHPN); emtint2(ctx, tok->tokofs); return; case TOKTSSTRING: emtop(ctx, OPCPUSHSTR); break; case TOKTDSTRING: emtop(ctx, OPCSAY); break; case TOKTLIST: emtop(ctx, OPCPUSHLST); emtlst(ctx, (uint)tok->tokofs, base); return; } /* if we haven't returned already, we have a large operand */ oplen = osrp2(base + tok->tokofs); emtmem(ctx, base + tok->tokofs, oplen); }
/* emit a list value */ void emtlst(emtcxdef *ctx, uint ofs, uchar *base) { uint initofs; ushort i; emtlidef *lst; emtledef *ele; initofs = ctx->emtcxofs; /* save starting offset */ emtint2(ctx, 2); /* emit placeholder length */ if (ofs == 0) return; /* nothing more to do for empty list */ lst = (emtlidef *)(base + ofs); for (i = lst->emtlicnt, ele = &lst->emtliele[i-1] ; i ; --ele, --i) { uchar dat; switch(ele->emtletyp) { case TOKTLIST: emtbyte(ctx, DAT_LIST); emtlst(ctx, (uint)ele->emtleval, base); continue; case TOKTSSTRING: { uint oplen = osrp2(base + ele->emtleval); emtbyte(ctx, DAT_SSTRING); emtmem(ctx, base + ele->emtleval, oplen); continue; } case TOKTNUMBER: dat = DAT_NUMBER; break; case TOKTNIL: dat = DAT_NIL; break; case TOKTTRUE: dat = DAT_TRUE; break; case TOKTPOUND: dat = DAT_PROPNUM; goto symbol; case TOKTFUNCTION: dat = DAT_FNADDR; goto symbol; case TOKTOBJECT: dat = DAT_OBJECT; goto symbol; case TOKTDOT: dat = DAT_PROPNUM; /* FALLTHROUGH */ symbol: emtbyte(ctx, dat); emtint2(ctx, (ushort)ele->emtleval); continue; default: assert(FALSE); break; } /* if we get here, emit datatype in dat, plus value */ emtbyte(ctx, dat); if (dat != DAT_TRUE && dat != DAT_NIL) emtint4(ctx, ele->emtleval); } oswp2(ctx->emtcxptr + initofs, ctx->emtcxofs - initofs); }
/* send a buffer value (as from a list) to ui callback for display */ static void dbgpbval(dbgcxdef *ctx, dattyp typ, uchar *val, void (*dispfn)(void *, const char *, int), void *dispctx) { char buf[TOKNAMMAX + 1]; char *p = buf; uint len; switch(typ) { case DAT_NUMBER: sprintf(buf, "%ld", (long)osrp4s(val)); len = strlen(buf); break; case DAT_OBJECT: len = dbgnam(ctx, buf, TOKSTOBJ, osrp2(val)); break; case DAT_SSTRING: len = osrp2(val) - 2; p = (char *)val + 2; break; case DAT_NIL: p = "nil"; len = 3; break; case DAT_LIST: (*dispfn)(dispctx, "[", 1); len = osrp2(val) - 2; p = (char *)val + 2; while (len) { dbgpbval(ctx, (dattyp)*p, (uchar *)(p + 1), dispfn, dispctx); lstadv((uchar **)&p, &len); if (len) (*dispfn)(dispctx, " ", 1); } (*dispfn)(dispctx, "]", 1); len = 0; break; case DAT_TRUE: p = "true"; len = 4; break; case DAT_FNADDR: (*dispfn)(dispctx, "&", 1); len = dbgnam(ctx, buf, TOKSTFUNC, osrp2(val)); break; case DAT_PROPNUM: (*dispfn)(dispctx, "&", 1); len = dbgnam(ctx, buf, TOKSTPROP, osrp2(val)); break; default: p = "[unknown type]"; len = 14; break; } if (typ == DAT_SSTRING) (*dispfn)(dispctx, "'", 1); if (len) (*dispfn)(dispctx, p, len); if (typ == DAT_SSTRING) (*dispfn)(dispctx, "'", 1); }
/* process changes to resource file (or just list it) */ static opdef *rscproc(osfildef *fp, osfildef *fpout, opdef *oplist) { char buf[128]; ulong siz; char datebuf[27]; ulong endpos; uchar nambuf[40]; uint rsiz; opdef *op; int copyrsc; ulong startpos; uint endpos_ofs; ulong first_xfcn = 0; ulong extcnt_pos = 0; int found_user_rsc = FALSE; int showed_heading = FALSE; int found_htmlres = FALSE; char *file_type; /* * if we're reading an existing file, check it header; otherwise, * write out a brand new file header */ if (fp != 0) { /* * the input file exists -- check file and version headers, and * get flags and timestamp */ if (osfrb(fp, buf, (int)(sizeof(FIOFILHDR) + sizeof(FIOVSNHDR) + 2))) listexit(fp, "error reading file header"); /* check the file type */ if (memcmp(buf, FIOFILHDR, sizeof(FIOFILHDR)) == 0) file_type = "game"; else if (memcmp(buf, FIOFILHDRRSC, sizeof(FIOFILHDRRSC)) == 0) file_type = "resource"; else listexit(fp, "invalid resource file header"); /* check the version header */ if (memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR, sizeof(FIOVSNHDR)) && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR2, sizeof(FIOVSNHDR2)) && memcmp(buf + sizeof(FIOFILHDR), FIOVSNHDR3, sizeof(FIOVSNHDR3))) listexit(fp, "incompatible resource file version"); /* get the timestamp */ if (osfrb(fp, datebuf, (size_t)26)) listexit(fp, "error reading file"); datebuf[26] = '\0'; } else { struct tm *tblock; time_t timer; /* construct a new file header */ memcpy(buf, FIOFILHDRRSC, sizeof(FIOFILHDRRSC)); memcpy(buf + sizeof(FIOFILHDR), FIOVSNHDR, sizeof(FIOVSNHDR)); /* clear the flags */ oswp2(buf + sizeof(FIOFILHDR) + sizeof(FIOVSNHDR), 0); /* construct the timestamp */ timer = time(0); tblock = localtime(&timer); strcpy(datebuf, asctime(tblock)); } if (fpout) { if (osfwb(fpout, buf, (int)(sizeof(FIOFILHDR) + sizeof(FIOVSNHDR) + 2)) || osfwb(fpout, datebuf, (size_t)26)) listexit(fp, "error writing header"); } /* if listing, show file creation timestamp */ if (fpout == 0) rscptf("\n" "File type: TADS %s file\n" "Date compiled: %s\n", file_type, datebuf); /* * Process the input file, if there is one */ for ( ; fp != 0 ; ) { /* assume this resource will be copied to the output */ copyrsc = TRUE; startpos = osfpos(fp); if (osfrb(fp, buf, 1) || osfrb(fp, buf + 1, (int)(buf[0] + 4))) listexit(fp, "error reading file"); memcpy(nambuf, buf + 1, (size_t)buf[0]); nambuf[buf[0]] = '\0'; endpos_ofs = 1 + buf[0]; endpos = osrp4(buf + endpos_ofs); siz = endpos - startpos; /* see what kind of resource we have, and do the right thing */ if (!strcmp((char *)nambuf, "$EOF")) { /* end of file marker - quit here */ break; } else if (!strcmp((char *)nambuf, "HTMLRES")) { /* if we've already found an HTMLRES list, it's an error */ if (found_htmlres) { rscptf("error: multiple HTMLRES maps found in file\n" " -- redundant entries have been deleted.\n"); copyrsc = FALSE; } else { /* go process it */ oplist = prochtmlres(fp, fpout, oplist, ©rsc, &showed_heading, TRUE); } /* note that we've found a resource */ found_user_rsc = TRUE; found_htmlres = TRUE; } else if (!strcmp((char *)nambuf, "EXTCNT")) { /* place to write start of XFCN's? */ if (siz >= 17 && fpout) extcnt_pos = osfpos(fpout); } else if (!strcmp((char *)nambuf, "XFCN")) { if (osfrb(fp, buf, 3) || osfrb(fp, buf + 3, (int)buf[2])) listexit(fp, "error reading file"); rsiz = osrp2(buf); buf[3 + buf[2]] = '\0'; if (fpout) { /* see if this resource is in the list */ for (op = oplist ; op ; op = op->opnxt) { if (!(op->opflag & OPFDONE) && op->oprestype == RESTYPE_XFCN && !stricmp((char *)buf + 3, op->opres)) { /* note that this resource has now been processed */ op->opflag |= OPFDONE; /* * if it's already here, and we're not deleting * it, warn that the old one will stay around */ if (!(op->opflag & OPFDEL)) { /* warn that we're going to ignore it */ rscptf("warning: XFCN resource \"%s\" already in " "file\n -- the old resource will be kept " "(use -replace to replace it)\n", op->opres); /* don't add it */ op->opflag &= ~OPFADD; } else { /* * we're deleting this resource; if adding * it back in (i.e., replacing it), process * the add operation now */ if (op->opflag & OPFADD) { /* show what we're doing */ show_op("replacing", op->opres, strlen(op->opres), op->oprestype); /* * add the external file, replacing the * one in the input file */ procop(fpout, op, &first_xfcn); } else { /* note that we're deleting it */ show_op("deleting", op->opres, strlen(op->opres), op->oprestype); } /* don't copy the one out of the file */ copyrsc = FALSE; } break; } } } else { /* no output file - just list the resource */ show_list_item(&showed_heading, "XFCN", (ulong)rsiz, buf + 3, (size_t)buf[2]); } /* note that we've found a user resource */ found_user_rsc = TRUE; } if (fpout != 0 && copyrsc) { osfseek(fp, startpos, OSFSK_SET); copyres(fp, fpout, siz, endpos_ofs); } /* skip to the next resource */ osfseek(fp, endpos, OSFSK_SET); } /* add the HTML resources if we haven't already */ if (!found_htmlres) oplist = prochtmlres(fp, fpout, oplist, ©rsc, &showed_heading, FALSE); /* now go through what's left, and add new non-HTML resources */ if (fpout != 0) { for (op = oplist ; op != 0 ; op = op->opnxt) { if (!(op->opflag & OPFDONE) && (op->opflag & OPFADD) && op->oprestype != RESTYPE_HTML) { /* show what we're doing */ show_op("adding", op->opres, strlen(op->opres), op->oprestype); /* add the file */ procop(fpout, op, &first_xfcn); /* mark the operation as completed */ op->opflag |= OPFDONE; } } } /* if just listing, and we didn't find anything, tell the user so */ if (fpout == 0 && !found_user_rsc) rscptf("No user resources found.\n"); /* done reading the file - finish it up */ if (fpout != 0) { /* write EOF resource */ if (osfwb(fpout, "\004$EOF\0\0\0\0", 9)) errexit("error writing resource", 1); /* write first_xfcn value to EXTCNT resource */ if (extcnt_pos) { osfseek(fpout, extcnt_pos + 13, OSFSK_SET); oswp4(buf, first_xfcn); osfwb(fpout, buf, 4); } } /* * return the final oplist (it may have been changed during * processing) */ return oplist; }
/* * load a Global Symbols block */ void CVmImageLoader::load_gsym(VMG_ ulong siz) { char buf[TOK_SYM_MAX_LEN + 128]; /* * if there's no global symbol table, merely load into the runtime * symbol table, since we don't seem to need the information for * debugging */ if (G_prs->get_global_symtab() == 0) { /* load it into the runtime reflection symbol table */ load_runtime_symtab_from_gsym(vmg_ siz); /* we're done */ return; } /* * remember the starting seek position and size, so we can re-read the * block into the runtime symbol table */ long init_seek_pos = fp_->get_seek(); ulong init_siz = siz; /* * note that we have a GSYM block - this implies that the image was * linked for debugging */ has_gsym_ = TRUE; /* read the symbol count */ read_data(buf, 4, &siz); ulong cnt = t3rp4u(buf); /* read the symbols and populate the global symbol table */ for ( ; cnt != 0 ; --cnt) { char *sym_name; size_t sym_len; size_t dat_len; CTcSymbol *sym; tc_symtype_t sym_type; char *dat; /* read the symbol's length, extra data length, and type code */ read_data(buf, 6, &siz); sym_len = osrp2(buf); dat_len = osrp2(buf + 2); sym_type = (tc_symtype_t)osrp2(buf + 4); /* check the lengths to make sure they don't overflow our buffer */ if (sym_len > TOK_SYM_MAX_LEN) { /* * this symbol name is too long - skip the symbol and its * extra data entirely */ skip_data(sym_len + dat_len, &siz); /* go on to the next symbol */ continue; } else if (dat_len + sym_len > sizeof(buf)) { /* * the extra data block is too long - truncate the extra * data so that we don't overflow our buffer, but proceed * anyway with the truncated extra data */ read_data(buf, sizeof(buf), &siz); /* skip the remainder of the extra data */ skip_data(sym_len + dat_len - sizeof(buf), &siz); } else { /* read the symbol's name and its type-specific data */ read_data(buf, sym_len + dat_len, &siz); } /* get the pointer to the extra data (after the name) */ dat = buf + sym_len; /* * allocate a copy of the symbol name in parser memory - this is * necessary because the symbol objects themselves always come * from parser memory, and hence never get individually deleted */ sym_name = (char *)G_prsmem->alloc(sym_len); memcpy(sym_name, buf, sym_len); /* create the new symbol table entry, depending on its type */ switch(sym_type) { case TC_SYM_FUNC: /* create the function symbol */ sym = new CTcSymFunc( sym_name, sym_len, FALSE, (int)osrp2(dat + 4), /* argc */ /* * the optional argument count is present in 3.1+ records, * which are at least 10 bytes; otherwise there aren't any * optional arguments */ (dat_len >= 10 ? osrp2(dat+8) : 0), /* opt_argc */ dat[6] != 0, /* varargs */ dat[7] != 0, /* has_retval */ FALSE, /* is_multimethod */ FALSE, /* is_multimethod_base */ FALSE); /* is_extern */ /* add the reverse mapping entry */ G_debugger->add_rev_sym(sym_name, sym_len, sym_type, t3rp4u(dat)); /* set the function's absolute address */ ((CTcSymFunc *)sym)->set_abs_addr(t3rp4u(dat)); break; case TC_SYM_OBJ: /* create the object symbol */ sym = new CTcSymObj(sym_name, sym_len, FALSE, t3rp4u(dat), FALSE, TC_META_TADSOBJ, 0); /* if there's a modifying object, store it */ if (dat_len >= 8) ((CTcSymObj *)sym)->set_modifying_obj_id(t3rp4u(dat + 4)); else ((CTcSymObj *)sym)->set_modifying_obj_id(VM_INVALID_OBJ); /* add the reverse mapping entry */ G_debugger->add_rev_sym(sym_name, sym_len, sym_type, t3rp4u(dat)); break; case TC_SYM_PROP: /* create the property symbol */ sym = new CTcSymProp(sym_name, sym_len, FALSE, osrp2(dat)); /* set the 'dictionary property' flag if present */ if (dat_len >= 3 && (buf[2] & 1) != 0) ((CTcSymProp *)sym)->set_vocab(TRUE); /* add the reverse mapping entry */ G_debugger->add_rev_sym(sym_name, sym_len, sym_type, osrp2(dat)); break; case TC_SYM_ENUM: /* create the enumerator symbol */ sym = new CTcSymEnum(sym_name, sym_len, FALSE, t3rp4u(dat), (dat_len >= 5 && (buf[4] & 1) != 0)); /* add the reverse mapping entry */ G_debugger->add_rev_sym(sym_name, sym_len, sym_type, t3rp4u(dat)); break; case TC_SYM_BIF: /* create the built-in function symbol */ sym = new CTcSymBif(sym_name, sym_len, FALSE, osrp2(dat + 2), /* function set ID */ osrp2(dat), /* function index */ dat[4] != 0, /* has_retval */ osrp2(dat + 5), /* min_argc */ osrp2(dat + 7), /* max_argc */ dat[9] != 0); /* varargs */ /* add the reverse mapping entry */ G_debugger->add_rev_sym(sym_name, sym_len, sym_type, (osrp2(dat+2) << 16) | osrp2(dat)); break; case TC_SYM_EXTFN: /* not currently supported */ sym = 0; break; case TC_SYM_METACLASS: /* create the metaclass symbol */ sym = new CTcSymMetaclass(sym_name, sym_len, FALSE, osrp2(dat), t3rp4u(dat + 2)); /* * add a reverse mapping symbol for the object instance * (note that we add this as an object, not a metaclass, * since it refers to the IntrinsicClass instance) */ if ((vm_obj_id_t)t3rp4u(dat + 2) != VM_INVALID_OBJ) G_debugger->add_rev_sym(sym_name, sym_len, TC_SYM_OBJ, t3rp4u(dat + 2)); break; default: /* ignore other types of symbols */ sym = 0; break; } /* if the symbol was valid, add it to the global symbol table */ if (sym != 0) G_prs->get_global_symtab()->add_entry(sym); } /* * Seek back to the starting position, and then load the GSYM data * into the runtime reflection symbol table */ fp_->seek(init_seek_pos); load_runtime_symtab_from_gsym(vmg_ init_siz); }
void CVmBifT3::get_stack_trace(VMG_ uint argc) { int single_level = 0; int level; vm_val_t *fp; vm_val_t lst_val; CVmObjList *lst; const uchar *entry_addr; ulong method_ofs; vm_val_t stack_info_cls; int want_named_args = FALSE; int want_locals = FALSE; int want_frefs = FALSE; int flags = 0; const vm_rcdesc *rc; /* check arguments */ check_argc_range(vmg_ argc, 0, 2); /* get the imported stack information class */ stack_info_cls.set_obj(G_predef->stack_info_cls); if (stack_info_cls.val.obj == VM_INVALID_OBJ) { /* * there's no stack information class - we can't return any * meaningful information, so just return nil */ retval_nil(vmg0_); return; } /* * look up T3StackInfo.construct() to determine how many arguments it * wants */ { int min_args, opt_args, varargs; if (vm_objp(vmg_ stack_info_cls.val.obj)->get_prop_interface( vmg_ stack_info_cls.val.obj, G_predef->obj_construct, min_args, opt_args, varargs)) { /* check to see how many extra arguments they want */ want_named_args = (min_args + opt_args >= 7 || varargs); want_locals = (min_args + opt_args >= 8 || varargs); want_frefs = (min_args + opt_args >= 9 || varargs); } } /* check to see if we're fetching a single level or the full trace */ if (argc >= 1) { /* * Get the single level, and adjust to a 0 base. If the level is * nil, we're still getting all levels. */ if (G_stk->get(0)->typ == VM_NIL) { /* it's nil - get all levels */ G_stk->discard(); } else { /* get the level number */ single_level = pop_int_val(vmg0_); /* make sure it's in range */ if (single_level <= 0) err_throw(VMERR_BAD_VAL_BIF); /* we won't need a return list */ lst_val.set_obj_or_nil(VM_INVALID_OBJ); lst = 0; } } /* get the flags argument, if present */ if (argc >= 2) flags = pop_int_val(vmg0_); /* if we're not doing a single level, we need a list for the result */ if (!single_level) { /* * We're returning a full list, so we need to allocate the list for * the return value. First, count stack levels to see how big a * list we'll need. */ fp = G_interpreter->get_frame_ptr(); entry_addr = G_interpreter->get_entry_ptr(); method_ofs = G_interpreter->get_method_ofs(); for (level = 0 ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp), ++level) { /* add an extra level for each system call */ if (method_ofs == 0 && entry_addr != 0) ++level; /* get the return address */ entry_addr = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); } /* create the list */ lst_val.set_obj(CVmObjList::create(vmg_ FALSE, level)); lst = (CVmObjList *)vm_objp(vmg_ lst_val.val.obj); /* * we create other objects while building this list, so the gc * could run - clear the list to ensure it contains valid data */ lst->cons_clear(); /* protect the list from garbage collection while we work */ G_stk->push(&lst_val); /* flag that we're doing the whole stack */ single_level = -1; } else { /* adjust the level to a 0-based index */ single_level -= 1; } /* set up at the current function */ fp = G_interpreter->get_frame_ptr(); entry_addr = G_interpreter->get_entry_ptr(); method_ofs = G_interpreter->get_method_ofs(); rc = 0; /* traverse the frames */ for (level = 0 ; fp != 0 ; ++level) { int fr_argc; int i; vm_obj_id_t def_obj; vm_val_t info_self; vm_val_t info_func; vm_val_t info_obj; vm_val_t info_prop; vm_val_t info_args; vm_val_t info_locals; vm_val_t info_srcloc; vm_val_t info_frameref; CVmObjList *arglst; vm_val_t ele; CVmFuncPtr func_ptr; int gc_cnt = 0; int info_argc = 0; /* if we're looking for a single level, and this isn't it, skip it */ if (single_level >= 0 && level != single_level) goto done_with_level; /* * start with the information values to nil - we'll set the * appropriate ones when we find out what we have */ info_func.set_nil(); info_obj.set_nil(); info_prop.set_nil(); info_self.set_nil(); info_locals.set_nil(); info_frameref.set_nil(); /* get the number of arguments to the function in this frame */ fr_argc = G_interpreter->get_argc_from_frame(vmg_ fp); /* set up a function pointer for the method's entry address */ func_ptr.set(entry_addr); /* get the current frame's defining object */ def_obj = G_interpreter->get_defining_obj_from_frame(vmg_ fp); /* check for special method offsets */ switch (method_ofs) { case VMRUN_RET_OP: /* the real return address is one past the last argument */ method_ofs = G_interpreter->get_param_from_frame(vmg_ fp, argc) ->val.intval; break; case VMRUN_RET_OP_ASILCL: /* the real return address is two past the last argument */ method_ofs = G_interpreter->get_param_from_frame(vmg_ fp, argc+1) ->val.intval; break; } /* determine whether it's an object.prop or a function call */ if (method_ofs == 0) { /* * A zero method offset indicates a recursive VM invocation * from a native function. Presume we have no information on * the caller. */ info_self.set_nil(); fr_argc = 0; /* check for a native caller context */ if (rc != 0) { /* check which kind of native caller we have */ if (rc->bifptr.typ != VM_NIL) { /* we have a built-in function at this level */ info_func = rc->bifptr; } else if (rc->self.typ != VM_NIL) { /* it's an intrinsic class method - get the 'self' */ info_obj = info_self = rc->self; /* get the metaclass */ CVmMetaclass *mc; switch (info_obj.typ) { case VM_OBJ: /* get the metaclass from the object */ mc = vm_objp(vmg_ info_obj.val.obj) ->get_metaclass_reg(); break; case VM_LIST: /* list constant - use the List metaclass */ mc = CVmObjList::metaclass_reg_; break; case VM_SSTRING: /* string constant - use the String metaclass */ mc = CVmObjString::metaclass_reg_; break; default: /* other types don't have metaclasses */ mc = 0; break; } /* get the registration table entry */ vm_meta_entry_t *me = mc == 0 ? 0 : G_meta_table->get_entry_from_reg(mc->get_reg_idx()); /* get the metaclass and property from the entry */ if (me != 0) { /* set 'obj' to the IntrinsicClass object */ info_obj.set_obj(me->class_obj_); /* get the property ID */ info_prop.set_propid(me->xlat_func(rc->method_idx)); } } } } else if (def_obj == VM_INVALID_OBJ) { /* there's no defining object, so this is a function call */ func_ptr.get_fnptr(vmg_ &info_func); } else { /* it's an object.prop invocation */ info_obj.set_obj(def_obj); // $$$ walk up to base modified obj? info_prop.set_propid( G_interpreter->get_target_prop_from_frame(vmg_ fp)); /* get the 'self' in this frame */ info_self.set_obj(G_interpreter->get_self_from_frame(vmg_ fp)); } /* * build the argument list and source location, except for system * routines */ if (method_ofs != 0 || rc != 0) { /* allocate a list object to store the argument list */ int ac = (rc != 0 ? rc->argc : fr_argc); info_args.set_obj(CVmObjList::create(vmg_ FALSE, ac)); arglst = (CVmObjList *)vm_objp(vmg_ info_args.val.obj); /* push the argument list for gc protection */ G_stk->push(&info_args); ++gc_cnt; /* build the argument list */ for (i = 0 ; i < ac ; ++i) { /* add this element to the argument list */ const vm_val_t *v = (rc != 0 ? rc->argp - i : G_interpreter->get_param_from_frame(vmg_ fp, i)); arglst->cons_set_element(i, v); } /* get the source location */ get_source_info(vmg_ entry_addr, method_ofs, &info_srcloc); /* * if they want locals, and this isn't a recursive native * caller, retrieve them */ if (rc == 0 && (((flags & T3_GST_LOCALS) != 0 && want_locals) || ((flags & T3_GST_FREFS) != 0 && want_frefs))) { /* get the locals */ get_stack_locals(vmg_ fp, entry_addr, method_ofs, (flags & T3_GST_LOCALS) != 0 && want_locals ? &info_locals : 0, (flags & T3_GST_FREFS) != 0 && want_frefs ? &info_frameref : 0); /* * that leaves the LookupTable and StackFrameDesc on the * stack, so note that we need to discard the stack level * when we're done with it */ if (info_locals.typ == VM_OBJ) ++gc_cnt; if (info_frameref.typ == VM_OBJ) ++gc_cnt; } } else { /* * it's a system routine - no argument information is * available, so return nil rather than an empty list to to * indicate the absence */ info_args.set_nil(); /* there's obviously no source location for system code */ info_srcloc.set_nil(); } /* * We have all of the information on this level now, so create the * information object for the level. This is an object of the * exported stack-info class, which is a TadsObject type. */ /* start with the original complement of arguments */ info_argc = 7; /* * if we have a modern T3StackInfo object, push the locals, * named argument elements, and frame reference object */ if (want_frefs) { G_stk->push(&info_frameref); ++info_argc; } if (want_named_args) { /* * the constructor has a slot for named arguments - push either * a table or nil, depending... */ vm_val_t *argp; const uchar *t = 0; /* if the flags request locals, retrieve the named arguments */ if ((flags & T3_GST_LOCALS) != 0) t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); /* * if we do in fact have named arguments, build a lookup table * copy and push it; otherwise just push nil */ if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* create a lookup table for the arguments */ G_stk->push()->set_obj(CVmObjLookupTable::create( vmg_ FALSE, n <= 8 ? 8 : n <= 32 ? 32 : 64, n)); CVmObjLookupTable *lt = (CVmObjLookupTable *)vm_objp( vmg_ G_stk->get(0)->val.obj); /* * Populate the lookup table with the named arguments. The * compiler builds the table in the order pushed, which is * right to left. Lookup tables preserve the order in * which elements are added, and reflect this order in key * lists, so to that extent the order of building the * lookup table matters. For readability of the generated * list, in case it's presented to the user, build the * table in left-to-right order, which is the reverse of * the table order in the bytecode table. */ argp += n - 1; for (int i = (n-1)*2 ; i >= 0 ; i -= 2, --argp) { /* get the name pointer and length from the index */ uint ofs = osrp2(t + i), nxtofs = osrp2(t + i + 2); const char *name = (const char *)t + ofs; size_t len = nxtofs - ofs; /* create a string from the name */ vm_val_t str; str.set_obj(CVmObjString::create(vmg_ FALSE, name, len)); /* add it to the table */ lt->add_entry(vmg_ &str, argp); } } else { /* there are no named arguments - push nil */ G_stk->push()->set_nil(); } /* count the argument */ ++info_argc; } if (want_locals) { G_stk->push(&info_locals); ++info_argc; } /* push the rest of the arguments */ G_stk->push(&info_srcloc); G_stk->push(&info_args); G_stk->push(&info_self); G_stk->push(&info_prop); G_stk->push(&info_obj); G_stk->push(&info_func); G_stk->push(&stack_info_cls); ele.set_obj(CVmObjTads::create_from_stack(vmg_ 0, info_argc)); /* discard the gc protection items */ G_stk->discard(gc_cnt); /* * if we're fetching a single level, this is it - return the new * stack info object and we're done */ if (single_level >= 0) { /* return the single level object */ retval_obj(vmg_ ele.val.obj); /* we're done */ return; } /* add the new element to our list */ lst->cons_set_element(level, &ele); done_with_level: /* * If this is a system call level, and we're not in debug mode, * this recursive frame contains the entry address for the caller, * but not the calling byte-code address. Stay on the current * level in this case. */ if (method_ofs == 0 && entry_addr != 0) { /* * This is a recursive caller, and we have a valid entry * address for the prior frame. Stay in the current frame, and * retrieve the actual return address from the calling frame. */ if (rc != 0) { /* get the actual return address from the recursive context */ method_ofs = rc->return_addr - entry_addr; /* * we're now in the bytecode part of the frame, so forget * the recursive context */ rc = 0; } else { /* no recursive context - use a fake return address */ method_ofs = G_interpreter->get_funchdr_size(); } } else { /* move up to the enclosing frame */ entry_addr = G_interpreter->get_enclosing_entry_ptr_from_frame(vmg_ fp); method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp); rc = G_interpreter->get_rcdesc_from_frame(vmg_ fp); fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp); } } /* return the list */ retval_obj(vmg_ lst_val.val.obj); /* discard our gc protection */ G_stk->discard(); }
/* send a value to ui callback for display */ void dbgpval(dbgcxdef *ctx, runsdef *val, void (*dispfn)(void *ctx, const char *str, int strl), void *dispctx, int showtype) { uchar buf[TOKNAMMAX + 1]; uint len; uchar *p = buf; char *typ = 0; switch(val->runstyp) { case DAT_NUMBER: sprintf((char *)buf, "%ld", val->runsv.runsvnum); len = strlen((char *)buf); typ = "number"; break; case DAT_OBJECT: len = dbgnam(ctx, (char *)buf, TOKSTOBJ, val->runsv.runsvobj); typ = "object"; break; case DAT_SSTRING: len = osrp2(val->runsv.runsvstr) - 2; p = val->runsv.runsvstr + 2; typ = "string"; break; case DAT_NIL: p = (uchar *)"nil"; len = 3; break; case DAT_LIST: if (showtype) (*dispfn)(dispctx, "list: ", 6); (*dispfn)(dispctx, "[", 1); len = osrp2(val->runsv.runsvstr) - 2; p = val->runsv.runsvstr + 2; while (len) { dbgpbval(ctx, (dattyp)*p, (uchar *)(p + 1), dispfn, dispctx); lstadv(&p, &len); if (len) (*dispfn)(dispctx, " ", 1); } (*dispfn)(dispctx, "]", 1); len = 0; break; case DAT_TRUE: p = (uchar *)"true"; len = 4; break; case DAT_FNADDR: len = dbgnam(ctx, (char *)buf, TOKSTFUNC, val->runsv.runsvobj); typ = "function pointer"; break; case DAT_PROPNUM: len = dbgnam(ctx, (char *)buf, TOKSTPROP, val->runsv.runsvprp); typ = "property pointer"; break; default: p = (uchar *)"[unknown type]"; len = 14; break; } /* show the type prefix if desired, or add a quote if it's a string */ if (typ && showtype) { /* show the type prefix */ (*dispfn)(dispctx, typ, (int)strlen(typ)); (*dispfn)(dispctx, ": ", 2); } else if (val->runstyp == DAT_SSTRING) { /* it's a string, and we're not showing a type - add a quote */ (*dispfn)(dispctx, "'", 1); } /* * if possible, null-terminate the buffer - do this only if the * length is actually within the buffer, which won't be the case if * the text comes from someplace outside the buffer (which is the * case if it's a string, for example) */ if (len < sizeof(buf)) buf[len] = '\0'; /* display a "&" prefix if it's an address of some kind */ if (val->runstyp == DAT_PROPNUM || val->runstyp == DAT_FNADDR) (*dispfn)(dispctx, "&", 1); /* display the text */ if (len != 0) (*dispfn)(dispctx, (char *)p, len); /* add a closing quote if it's a string and we showed an open quote */ if (val->runstyp == DAT_SSTRING && !(typ && showtype)) (*dispfn)(dispctx, "'", 1); }
/* * disassemble an instruction */ void CTcT3Unasm::disasm_instr(CTcUnasSrc *src, CTcUnasOut *out, char ch_op) { t3_instr_info_t *info; int i; ulong acc; char ch[10]; ulong prv_ofs = src->get_ofs(); /* get the information on this instruction */ info = &instr_info[(int)(uchar)ch_op]; out->print("%8lx %-14.14s ", src->get_ofs() - 1, info->nm); /* check the instruction type */ switch((uchar)ch_op) { case OPC_SWITCH: /* * It's a switch instruction - special handling is required, * since this instruction doesn't fit any of the normal * patterns. First, get the number of elements in the case * table - this is a UINT2 value at the start of the table. */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2(ch); /* display the count */ out->print("0x%x\n", (uint)acc); /* display the table */ for (i = 0 ; i < (int)acc ; ++i) { const char *dt; char valbuf[128]; const char *val = valbuf; /* note the current offset */ prv_ofs = src->get_ofs(); /* read the DATAHOLDER value */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); src->next_byte(ch+4); /* read the jump offset */ src->next_byte(ch+5); src->next_byte(ch+6); /* * stop looping if the offset hasn't changed - this probably * means we're stuck trying to interpret as a "switch" some * data at the end of the stream that happens to look like a * switch but really isn't (such as an exception table, or * debug records) */ if (src->get_ofs() == prv_ofs) break; /* show the value */ switch(ch[0]) { case VM_NIL: dt = "nil"; val = ""; break; case VM_TRUE: dt = "true"; val = ""; break; case VM_OBJ: dt = "object"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_PROP: dt = "prop"; sprintf(valbuf, "0x%04x", osrp2(ch+1)); break; case VM_INT: dt = "int"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_ENUM: dt = "enum"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_SSTRING: dt = "sstring"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_LIST: dt = "list"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; case VM_FUNCPTR: dt = "funcptr"; sprintf(valbuf, "0x%08lx", t3rp4u(ch+1)); break; default: dt = "???"; val = "???"; break; } /* show the slot data */ out->print(" 0x%08lx %-8.8s(%-10.10s) " "-> +0x%04lx (0x%08lx)\n", src->get_ofs() - 7, dt, val, osrp2(ch+5), src->get_ofs() - 2 + osrp2s(ch+5)); } /* read and show the 'default' jump offset */ src->next_byte(ch); src->next_byte(ch+1); out->print(" 0x%08lx default " "-> +0x%04lx (0x%08lx)\n", src->get_ofs() - 2, osrp2(ch), src->get_ofs() - 2 + osrp2s(ch)); /* done */ break; default: /* show all parameters */ for (i = 0 ; i < info->op_cnt ; ++i) { /* add a separator if this isn't the first one */ if (i != 0) out->print(", "); /* display the operand */ switch(info->op_type[i]) { case T3OP_TYPE_8S: /* 8-bit signed integer */ src->next_byte(ch); out->print("0x%x", (int)ch[0]); break; case T3OP_TYPE_8U: /* 8-bit unsigned integer */ src->next_byte(ch); out->print("0x%x", (uint)(uchar)ch[0]); break; case T3OP_TYPE_16S: /* 16-bit signed integer */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2s(ch); out->print("0x%x", (int)acc); break; case T3OP_TYPE_16U: /* 16-bit unsigned integer */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2(ch); out->print("0x%x", (uint)acc); break; case T3OP_TYPE_32S: /* 32-bit signed integer */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("0x%lx", acc); break; case T3OP_TYPE_32U: /* 32-bit unsigned integer */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("0x%lx", acc); break; case T3OP_TYPE_STR: /* string offset */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("string(0x%lx)", acc); break; case T3OP_TYPE_LIST: /* list offset */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("list(0x%lx)", acc); break; case T3OP_TYPE_CODE_ABS: /* 32-bit absolute code address */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("code(0x%08lx)", acc); break; case T3OP_TYPE_CODE_REL: /* 16-bit relative code address */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2s(ch); if ((long)acc < 0) out->print("-0x%04x (0x%08lx)", -(int)acc, src->get_ofs() - 2 + acc); else out->print("+0x%04x (0x%08lx)", acc, src->get_ofs() - 2 + acc); break; case T3OP_TYPE_OBJ: /* object ID */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("object(0x%lx)", acc); break; case T3OP_TYPE_PROP: /* property ID */ src->next_byte(ch); src->next_byte(ch+1); acc = osrp2(ch); out->print("property(0x%x)", (uint)acc); break; case T3OP_TYPE_ENUM: /* enum */ src->next_byte(ch); src->next_byte(ch+1); src->next_byte(ch+2); src->next_byte(ch+3); acc = t3rp4u(ch); out->print("enum(0x%lx)", acc); break; case T3OP_TYPE_CTX_ELE: /* context element identifier */ src->next_byte(ch); switch(ch[0]) { case PUSHCTXELE_TARGPROP: out->print("targetprop"); break; case PUSHCTXELE_TARGOBJ: out->print("targetobj"); break; case PUSHCTXELE_DEFOBJ: out->print("definingobj"); break; default: out->print("0x%x", (int)ch[0]); break; } break; case T3OP_TYPE_INLINE_STR: /* get the string length */ src->next_byte(ch); src->next_byte(ch+1); /* show it */ out->print("string(inline)"); /* skip the string */ for (acc = osrp2(ch) ; acc != 0 ; --acc) src->next_byte(ch); /* done */ break; default: out->print("...unknown type..."); break; } } /* end the line */ out->print("\n"); } }
/* * Read a stream from an object file */ void CTcDataStream::load_object_file(CVmFile *fp, const textchar_t *fname) { ulong stream_len; ulong rem; char buf[1024]; ulong start_ofs; ulong anchor_cnt; /* read the length of the stream */ stream_len = fp->read_uint4(); /* remember my starting offset */ start_ofs = get_ofs(); /* read the stream bytes */ for (rem = stream_len ; rem != 0 ; ) { size_t cur; /* read up to a buffer-full, or however much is left */ cur = sizeof(buf); if (cur > rem) cur = rem; /* read this chunk */ fp->read_bytes(buf, cur); /* add this chunk to the stream */ write(buf, cur); /* deduct the amount we've read from the amount remaining */ rem -= cur; } /* * Read the anchors. For each anchor, we must fix up the anchor by * adding the base address of the stream we just read - the original * anchor offsets in the object file reflect a base stream offset of * zero, but we could be loading the stream after a bunch of other * data have already been loaded into the stream. * * First, read the number of anchors, then loop through the anchors * and read each one. */ for (anchor_cnt = fp->read_uint4() ; anchor_cnt != 0 ; --anchor_cnt) { ulong anchor_ofs; size_t sym_len; CTcStreamAnchor *anchor; /* read this anchor */ fp->read_bytes(buf, 6); /* get the offset, and adjust for the new stream base offset */ anchor_ofs = t3rp4u(buf) + start_ofs; /* get the length of the owning symbol's name, if any */ sym_len = osrp2(buf+4); /* if there's a symbol name, read it */ if (sym_len != 0) { CTcSymbol *owner_sym; /* read the symbol name */ fp->read_bytes(buf, sym_len); /* look it up in the global symbol table */ owner_sym = G_prs->get_global_symtab()->find(buf, sym_len); if (owner_sym == 0) { /* * the owner symbol doesn't exist - this is an internal * inconsistency in the object file, because the anchor * symbol must always be defined in the same file and * hence should have been loaded already; complain and * go on */ G_tcmain->log_error(0, 0, TC_SEV_ERROR, TCERR_OBJFILE_INT_SYM_MISSING, (int)sym_len, buf, fname); /* we can't create the anchor */ anchor = 0; } else { /* create the anchor based on the symbol */ anchor = add_anchor(owner_sym, owner_sym->get_fixup_list_anchor(), anchor_ofs); /* set the anchor in the symbol */ owner_sym->set_anchor(anchor); } } else { /* create the anchor with no external references */ anchor = add_anchor(0, 0, anchor_ofs); } /* load the fixup list */ CTcAbsFixup:: load_fixup_list_from_object_file(fp, fname, anchor->fixup_list_head_); } }
/* * Find a resource in a T3 image file */ static int t3_find_res(osfildef *fp, const char *resname, tads_resinfo *info) { char buf[256]; size_t resname_len; /* note the length of the name we're seeking */ resname_len = strlen(resname); /* * skip the file header - 11 bytes for the signature, 2 bytes for the * format version, 32 reserved bytes, and 24 bytes for the timestamp */ osfseek(fp, 11 + 2 + 32 + 24, OSFSK_CUR); /* scan the data blocks */ for (;;) { unsigned long siz; /* read the block header */ if (osfrb(fp, buf, 10)) return FALSE; /* get the block size */ siz = t3rp4u(buf + 4); /* check the type */ if (memcmp(buf, "MRES", 4) == 0) { unsigned long base_ofs; unsigned int entry_cnt; unsigned int i; /* * remember the current seek position - the data seek location * for each index entry is given as an offset from this * location */ base_ofs = osfpos(fp); /* read the number of entries */ if (osfrb(fp, buf, 2)) return FALSE; /* parse the entry count */ entry_cnt = osrp2(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { unsigned long entry_ofs; unsigned long entry_siz; unsigned int entry_name_len; char *p; size_t rem; /* read this index entry's header */ if (osfrb(fp, buf, 9)) return FALSE; /* parse the header */ entry_ofs = t3rp4u(buf); entry_siz = t3rp4u(buf + 4); entry_name_len = (unsigned char)buf[8]; /* read the entry's name */ if (osfrb(fp, buf, entry_name_len)) return FALSE; /* XOR the bytes of the name with 0xFF */ for (p = buf, rem = entry_name_len ; rem != 0 ; ++p, --rem) *p ^= 0xFF; /* if this is the one we're looking for, return it */ if (entry_name_len == resname_len && memicmp(resname, buf, resname_len) == 0) { /* * fill in the return information - note that the * entry offset given in the header is an offset from * data block's starting location, so fix this up to * an absolute seek location for the return value */ info->seek_pos = base_ofs + entry_ofs; info->siz = entry_siz; /* return success */ return TRUE; } } } else if (memcmp(buf, "EOF ", 4) == 0) { /* * end of file - we've finished without finding the resource, * so return failure */ return FALSE; } else { /* * we don't care about anything else - just skip this block * and keep going; to skip the block, simply seek ahead by the * size of the block's contents as given in the block header */ osfseek(fp, siz, OSFSK_CUR); } } }
/* * Retrieve a list of the named argument names. */ void CVmBifT3::get_named_arg_list(VMG_ uint argc) { /* check arguments */ check_argc(vmg_ argc, 0); /* create the result list; we'll expand as necessary later */ G_stk->push()->set_obj(CVmObjList::create(vmg_ FALSE, 10)); CVmObjList *lst = (CVmObjList *)vm_objp(vmg_ G_stk->get(0)->val.obj); /* clear it out, since we're building it incrementally */ lst->cons_clear(); /* we haven't added any elements yet */ int idx = 0; /* scan the stack and populate the name list from the tables we find */ for (vm_val_t *fp = G_interpreter->get_frame_ptr() ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp)) { /* look for a named argument table in this frame */ vm_val_t *argp; const uchar *t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* * Build the list. The compiler generates the list in * right-to-left order (the order of pushing the arguments). * For readability, reverse this: generate the list left to * right, so that it appears in the original source code order. */ argp += n - 1; for (int i = (n-1)*2 ; i >= 0 ; i -= 2, --argp) { /* get this string's offset and figure its length */ uint ofs = osrp2(t + i); uint len = osrp2(t + i + 2) - ofs; /* create a string from the name */ vm_val_t str; str.set_obj(CVmObjString::create( vmg_ FALSE, (const char *)t + ofs, len)); /* add it to the list */ lst->cons_ensure_space(vmg_ idx, 10); lst->cons_set_element(idx, &str); ++idx; } } } /* set the final list length */ lst->cons_set_len(idx); /* keep only the unique elements */ lst->cons_uniquify(vmg0_); /* return the results */ retval_pop(vmg0_); }
/* * Find a resource in a tads 2 game file */ static int t2_find_res(osfildef *fp, const char *resname, tads_resinfo *info) { char buf[300]; unsigned long startpos; size_t resname_len; int found; /* we haven't found what we're looking for yet */ found = FALSE; /* note the length of the name we're seeking */ resname_len = strlen(resname); /* * note the seek location of the start of the tads 2 game file stream * within the file - if the game file is embedded in a larger file * stream, the seek locations we find within the file are relative to * this starting location */ startpos = osfpos(fp); /* * skip past the tads 2 file header (13 bytes for the signature, 7 * bytes for the version header, 2 bytes for the flags, 26 bytes for * the timestamp) */ osfseek(fp, 13 + 7 + 2 + 26, OSFSK_CUR); /* * scan the sections in the file; stop on $EOF, and skip everything * else but HTMLRES, which is the section type that */ for (;;) { unsigned long endofs; /* read the section type and next-section pointer */ if (osfrb(fp, buf, 1) || osfrb(fp, buf + 1, (int)((unsigned char)buf[0] + 4))) { /* failed to read it - give up */ return FALSE; } /* note the ending position of this section */ endofs = t3rp4u(buf + 1 + (unsigned char)buf[0]); /* check the type */ if (buf[0] == 7 && memcmp(buf+1, "HTMLRES", 7) == 0) { unsigned long entry_cnt; unsigned long i; /* * It's a multimedia resource block. Read the index table * header (which contains the number of entries and a reserved * uint32). */ if (osfrb(fp, buf, 8)) return FALSE; /* get the number of entries from the header */ entry_cnt = t3rp4u(buf); /* read the entries */ for (i = 0 ; i < entry_cnt ; ++i) { unsigned long res_ofs; unsigned long res_siz; unsigned short name_len; /* read the entry header */ if (osfrb(fp, buf, 10)) return FALSE; /* parse the header */ res_ofs = t3rp4u(buf); res_siz = t3rp4u(buf + 4); name_len = osrp2(buf + 8); /* read the entry's name */ if (name_len > sizeof(buf) || osfrb(fp, buf, name_len)) return FALSE; /* * if it matches the name we're looking for, note that we * found it */ if (name_len == resname_len && memicmp(resname, buf, name_len) == 0) { /* * note that we found it, and note its resource size * and offset in the return structure - but keep * scanning the rest of the directory, since we need * to know where the directory ends to know where the * actual resources begin */ found = TRUE; info->seek_pos = res_ofs; info->siz = res_siz; } } /* * if we found our resource, the current seek position is the * base of the offset we found in the directory; so fix up the * offset to give the actual file location */ if (found) { /* fix up the offset with the actual file location */ info->seek_pos += osfpos(fp); /* tell the caller we found it */ return TRUE; } /* we didn't find it - seek to the end of this section */ osfseek(fp, endofs + startpos, OSFSK_SET); } else if (buf[0] == 4 && memcmp(buf+1, "$EOF", 4) == 0) { /* * that's the end of the file - we've finished without finding * the resource, so return failure */ return FALSE; } else { /* * this isn't a section we're interested in - skip to the end * of the section and keep going */ osfseek(fp, endofs + startpos, OSFSK_SET); } } }
/* * Look up a named argument. If 'mandatory' is set, we throw an error if * we can't find a resolution. */ void CVmBifT3::get_named_arg(VMG_ uint argc) { /* check arguments */ check_argc_range(vmg_ argc, 1, 2); /* get the name we're looking for */ const char *name = G_stk->get(0)->get_as_string(vmg0_); if (name == 0) err_throw(VMERR_STRING_VAL_REQD); /* get the length and buffer pointer */ size_t namelen = vmb_get_len(name); name += VMB_LEN; /* * Scan the stack for named parameter tables. A named parameter table * is always in the calling frame at the stack slot just beyond the * last argument. */ for (vm_val_t *fp = G_interpreter->get_frame_ptr() ; fp != 0 ; fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp)) { /* check for a table in this frame */ vm_val_t *argp; const uchar *t = CVmRun::get_named_args_from_frame(vmg_ fp, &argp); if (t != 0) { /* get the number of table entries */ int n = osrp2(t); t += 2; /* scan the table for the name */ for (int i = 0 ; n >= 0 ; --n, i += 2, ++argp) { /* get this element's offset, and figure its length */ uint eofs = osrp2(t + i); uint elen = osrp2(t + i + 2) - eofs; /* check for a match */ if (elen == namelen && memcmp(name, t + eofs, elen) == 0) { /* found it - return the value */ retval(vmg_ argp); /* discard arguments and return */ G_stk->discard(argc); return; } } } } /* * The argument is undefined. If a default value was supplied, simply * return the default value. Otherwise throw an error. */ if (argc >= 2) { /* a default value was supplied - simply return it */ retval(vmg_ G_stk->get(1)); /* discard arguments */ G_stk->discard(argc); } else { /* no default value - throw an error */ err_throw_a(VMERR_MISSING_NAMED_ARG, 1, ERR_TYPE_TEXTCHAR_LEN, name, namelen); } }
int TADS2::cmap_load_internal(const char *filename) { osfildef *fp; static char sig1[] = CMAP_SIG_S100; char buf[256]; uchar lenbuf[2]; size_t len; int sysblk; // if there's no mapping file, use the default mapping if (filename == 0) { // initialize with the default mapping cmap_init_default(); // return success return 0; } // open the file fp = osfoprb(filename, OSFTCMAP); if (fp == 0) return 1; // check the signature if (osfrb(fp, buf, sizeof(sig1)) || memcmp(buf, sig1, sizeof(sig1)) != 0) { osfcls(fp); return 2; } // load the ID G_cmap_id[4] = '\0'; if (osfrb(fp, G_cmap_id, 4)) { osfcls(fp); return 3; } // load the long description if (osfrb(fp, lenbuf, 2) || (len = osrp2(lenbuf)) > sizeof(G_cmap_ldesc) || osfrb(fp, G_cmap_ldesc, len)) { osfcls(fp); return 4; } // load the two tables - input, then output if (osfrb(fp, G_cmap_input, sizeof(G_cmap_input)) || osfrb(fp, G_cmap_output, sizeof(G_cmap_output))) { osfcls(fp); return 5; } // read the next section header if (osfrb(fp, buf, 4)) { osfcls(fp); return 6; } // if it's "SYSI", read the system information string if (!memcmp(buf, "SYSI", 4)) { // read the length prefix, then the string if (osfrb(fp, lenbuf, 2) || (len = osrp2(lenbuf)) > sizeof(buf) || osfrb(fp, buf, len)) { osfcls(fp); return 7; } // we have a system information block sysblk = true; } else { // there's no system information block sysblk = false; } /* * call the OS code, so that it can do any system-dependent * initialization for the new character mapping */ os_advise_load_charmap(G_cmap_id, G_cmap_ldesc, sysblk ? buf : ""); // read the next section header if (sysblk && osfrb(fp, buf, 4)) { osfcls(fp); return 8; } // see if we have an entity list if (!memcmp(buf, "ENTY", 4)) { // read the entities for (;;) { unsigned int cval; char expansion[CMAP_MAX_ENTITY_EXPANSION]; // read the next item's length and character value if (osfrb(fp, buf, 4)) { osfcls(fp); return 9; } // decode the values len = osrp2(buf); cval = osrp2(buf+2); // if we've reached the zero marker, we're done if (len == 0 && cval == 0) break; // read the string if (len > CMAP_MAX_ENTITY_EXPANSION || osfrb(fp, expansion, len)) { osfcls(fp); return 10; } // tell the output code about the expansion tio_set_html_expansion(cval, expansion, len); } } /* * ignore anything else we find - if the file format is updated to * include extra information in the future, and this old code tries * to load an updated file, we'll just ignore the new information, * which should always be placed after the "SYSI" block (if present) * to ensure compatibility with past versions (such as this code) */ // no problems - close the file and return success osfcls(fp); return 0; }
/* * Given a saved game file, try to identify the game file that created the * saved game. */ static int vm_get_game_file_from_savefile(const char *savefile, char *fname, size_t fnamelen) { osfildef *fp; char buf[128]; int ret; size_t len; /* open the saved game file */ fp = osfoprb(savefile, OSFTBIN); /* if that failed, there's no way to read the game file name */ if (fp == 0) return FALSE; /* read the first few bytes */ if (osfrb(fp, buf, 16)) { /* * we couldn't even read that much, so it must not really be a * saved game file */ ret = FALSE; } else { /* check for a saved game signature we recognize */ if (memcmp(buf, "TADS2 save/g\012\015\032\000", 16) == 0) { /* * It's a TADS 2 saved game with embedded .GAM file * information. The filename immediately follows the signature * (the 15 bytes we just matched), with a two-byte length * prefix. Seek to the length prefix and read it. */ osfseek(fp, 16, OSFSK_SET); osfrb(fp, buf, 2); len = osrp2(buf); /* limit the read length to our caller's available buffer */ if (len > fnamelen - 1) len = fnamelen - 1; /* read the filename and null-terminate it */ osfrb(fp, fname, len); fname[len] = '\0'; /* success */ ret = TRUE; } else if (memcmp(buf, "T3-state-v", 10) == 0) { /* * It's a T3 saved state file. The image filename is always * embedded in this type of file, so seek back to the start of * the file and read the filename. * * Note that restore_get_image() returns zero on success, so we * want to return true if and only if that routine returns * zero. */ osfseek(fp, 0, OSFSK_SET); ret = (CVmSaveFile::restore_get_image(fp, fname, fnamelen) == 0); } else { /* * it's not a signature we know, so it must not be a saved * state file (at least not one we can deal with) */ ret = FALSE; } } /* we're done with the file now, so close it */ osfcls(fp); /* return the result */ return ret; }
/* * Process an HTML resource list. If 'old_htmlres' is true, it * indicates that the input file is pointing to an old resource map; * otherwise, we need to construct a brand new one. */ static opdef *prochtmlres(osfildef *fp, osfildef *fpout, opdef *oplist, int *copyrsc, int *showed_heading, int old_htmlres) { opdef *op; opdef *add_list = 0; opdef *prev_op; opdef *next_op; int found; char buf[512]; ulong in_entry_cnt; ulong in_table_siz; ulong out_hdr_siz; ulong out_hdr_pos; ulong i; ulong out_res_cnt; ulong out_total_name_len; ulong rem; struct idx_t **in_list = 0, *out_list = 0, **p, *cur; ulong in_res_base, out_res_base; ulong out_endpos; /* * Scan the oplist for an HTML resource. If there aren't any, we * don't need to modify the HTMLRES list, so tell the caller to copy * this resource unchanged. */ for (op = oplist, found = FALSE ; op != 0 ; op = op->opnxt) { /* if this is an HTML resource, note it and stop looking */ if (op->oprestype == RESTYPE_HTML) { found = TRUE; break; } } /* * If we didn't find any operations on this resource, and we're not * simply listing resources or we don't have an old resource to * list, tell the caller to copy it unchanged. */ if (!found && (fpout != 0 || !old_htmlres)) { *copyrsc = TRUE; return oplist; } /* we'll be handling the resource - tell the caller not to copy it */ *copyrsc = FALSE; /* if there's an old HTMLRES resource, read it */ if (old_htmlres) { /* read the index entry count and size */ if (osfrb(fp, buf, 8)) listexit(fp, "unable to read HTMLRES header"); in_entry_cnt = osrp4(buf); in_table_siz = osrp4(buf + 4); /* allocate space for pointers to all of the entries */ if (in_entry_cnt != 0) { in_list = (struct idx_t **) malloc(in_entry_cnt * sizeof(struct idx_t *)); if (in_list == 0) listexit(fp, "unable to allocate space for HTMLRES entries"); } /* read the index table entries */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { ushort name_siz; ulong res_siz; ulong res_ofs; /* read the entry information */ if (osfrb(fp, buf, 10)) listexit(fp, "unable to read HTMLRES index table entry (prefix)"); /* get the resource size */ res_ofs = osrp4(buf); res_siz = osrp4(buf + 4); /* read the name */ name_siz = osrp2(buf + 8); if (name_siz > sizeof(buf)) listexit(fp, "name too large in HTMLRES index table entry"); if (osfrb(fp, buf, name_siz)) listexit(fp, "unable to read HTMLRES index table entry (name)"); /* build this entry */ *p = alloc_idx_entry(res_ofs, res_siz, buf, name_siz, 0, 0); } /* if we don't have an output file, list the HTMLRES contents */ if (fpout == 0) { /* display all of the entries */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) show_list_item(showed_heading, "HTML", (*p)->siz, (*p)->nam, (*p)->namlen); /* there's no more processing to do */ goto done; } /* * The resources start at the end of the index table - note the * location of the end of the input table, since it's the base * address relative to which the resource offsets are stated. */ in_res_base = osfpos(fp); /* * Go through the resource table in the input file. Find each * one in the op list. If it's not in the op list, we'll copy * it to the output file. */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { int remove_res = FALSE; int add_res = FALSE; /* see if we can find this entry in the op list */ for (prev_op = 0, op = oplist ; op != 0 ; prev_op = op, op = op->opnxt) { /* if this one matches, note it */ if (op->oprestype == RESTYPE_HTML && strlen(op->opres) == (*p)->namlen && !memicmp(op->opres, (*p)->nam, (*p)->namlen)) { /* * if we're adding this resource (not replacing it), * warn that it's already in the file, and ignore * this op; if we're removing it or replacing it, * simply delete this entry from the input list so * it doesn't get copied to the output. */ if (!(op->opflag & OPFDEL)) { /* warn that the old one will stay */ rscptf("warning: HTML resource \"%s\" already " "present\n" " -- old version will be kept (use -replace " "to replace it)\n", op->opres); /* remove it from the processing list */ remove_res = TRUE; } else { /* we are deleting it; see if we're also adding it */ if (op->opflag & OPFADD) { /* * we're replacing this resource - take this * op out of the main list and put it into * the add list */ remove_res = TRUE; add_res = TRUE; /* note the addition */ show_op("replacing", op->opres, strlen(op->opres), op->oprestype); } else { /* note the deletion */ show_op("deleting", op->opres, strlen(op->opres), op->oprestype); /* just remove it */ remove_res = TRUE; } /* get rid of this item from the input list */ free(*p); *p = 0; } /* no need to look further in the operations list */ break; } } /* * If desired, remove this resource from the main list, and * add it into the list of resources to add. */ if (remove_res) { /* unlink it from the main list */ if (prev_op == 0) oplist = op->opnxt; else prev_op->opnxt = op->opnxt; /* if desired, add it to the additions list */ if (add_res) { /* we're adding it - put it in the additions list */ op->opnxt = add_list; add_list = op; } else { /* this item has been processed - delete it */ free(op); } } } } else { /* there are no entries in the input file */ in_entry_cnt = 0; in_table_siz = 0; } /* * Move all of the HTML resources marked as additions in the main * operations list into the additions list. */ for (prev_op = 0, op = oplist ; op != 0 ; op = next_op) { /* note the next op, in case we move this one to the other list */ next_op = op->opnxt; /* * if it's an HTML resource to be added, move it to the * additions list */ if (op->oprestype == RESTYPE_HTML && (op->opflag & OPFADD) != 0) { /* show what we're doing */ show_op("adding", op->opres, strlen(op->opres), op->oprestype); /* unlink it from the main list */ if (prev_op == 0) oplist = op->opnxt; else prev_op->opnxt = op->opnxt; /* add it to the additions list */ op->opnxt = add_list; add_list = op; /* * note that we don't want to advance the 'prev_op' pointer, * since we just removed this item - the previous item is * still the same as it was on the last iteration */ } else { /* * we're leaving this op in the original list - it's now the * previous op in the main list */ prev_op = op; } } /* * Figure out what we'll be putting in the HTMLRES list: we'll add * each surviving entry from the input file, plus all of the items * in the add list, plus all of the HTML items in the main list that * are marked for addition. */ out_res_cnt = 0; out_total_name_len = 0; /* count input file entries that we're going to copy */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { if (*p != 0) add_idx_entry(&out_list, &out_res_cnt, &out_total_name_len, 0, 0, (*p)->nam, (*p)->namlen, 0, *p); } /* * Count items in the additions list. Note that every HTML resource * marked for addition is in the additions list, since we moved all * such resources out of the main list and into the additions list * earlier. */ for (op = add_list ; op != 0 ; op = op->opnxt) add_idx_entry(&out_list, &out_res_cnt, &out_total_name_len, 0, 0, op->opres, (ushort)strlen(op->opres), op, 0); /* write the resource header */ if (osfwb(fpout, "\007HTMLRES\0\0\0\0", 12)) listexit(fp, "unable to write HTMLRES type header"); out_hdr_pos = osfpos(fpout); /* * Reserve space in the output file for the index table. We need * eight bytes for the index table prefix, then ten bytes per entry * plus the name sizes. */ out_hdr_siz = 8 + (10 * out_res_cnt) + out_total_name_len; /* write the index table prefix */ oswp4(buf, out_res_cnt); oswp4(buf + 4, out_hdr_siz); if (osfwb(fpout, buf, 8)) listexit(fp, "unable to write HTMLRES prefix"); /* * Reserve space for the headers. Don't actually write them yet, * since we don't know the actual locations and sizes of the * entries; for now, simply reserve the space, so that we can come * back here later and write the actual headers. Note that we * deduct the eight bytes we've already written from the amount of * filler to put in. */ for (rem = out_hdr_siz - 8 ; rem != 0 ; ) { ulong amt; /* write out a buffer full */ amt = (rem > sizeof(buf) ? sizeof(buf) : rem); if (osfwb(fpout, buf, amt)) listexit(fp, "unable to write HTMLRES header"); /* deduct the amount we wrote from the remainder */ rem -= amt; } /* * note the current position in the output file - this is the base * address of the resources */ out_res_base = osfpos(fpout); /* * Write the resources. */ for (cur = out_list ; cur != 0 ; cur = cur->nxt) { /* * note the current file position as an offset from the resource * base in the output file - this is the offset that we need to * store in the index entry for this object */ cur->ofs = osfpos(fpout) - out_res_base; /* * Copy the resource to the output. If it comes from the input * file, copy from there, otherwise go out and find the external * file and copy its contents. */ if (cur->src_op != 0) { osfildef *fpext; ulong fsiz; /* it comes from an external file - open the file */ fpext = osfoprb(cur->src_op->opfile, OSFTGAME); if (fpext == 0) { rscptf("%s: ", cur->src_op->opfile); errexit("unable to open file", 1); } /* figure the size of the file */ osfseek(fpext, 0L, OSFSK_END); fsiz = osfpos(fpext); osfseek(fpext, 0L, OSFSK_SET); /* copy the contents of the external file to the output */ copybytes(fpext, fpout, fsiz); /* the size is the same as the external file's size */ cur->siz = fsiz; /* done with the file */ osfcls(fpext); } else { /* * it comes from the input resource file - seek to the start * of the resource in the input file, and copy the data to * the output file */ osfseek(fp, in_res_base + cur->src_idx->ofs, OSFSK_SET); copybytes(fp, fpout, cur->src_idx->siz); /* the size is the same as in the input file */ cur->siz = cur->src_idx->siz; } } /* note the current output position - this is the end of the resource */ out_endpos = osfpos(fpout); /* * Now that we've written all of the resources and know their actual * layout in the file, we can go back and write the index table. */ osfseek(fpout, out_hdr_pos + 8, OSFSK_SET); for (cur = out_list ; cur != 0 ; cur = cur->nxt) { /* build this object's index table entry */ oswp4(buf, cur->ofs); oswp4(buf + 4, cur->siz); oswp2(buf + 8, cur->namlen); /* write the entry */ if (osfwb(fpout, buf, 10) || osfwb(fpout, cur->nam, cur->namlen)) listexit(fp, "unable to write HTML index table entry"); } /* * We're done building the resource; now all we need to do is go * back and write the ending position of the resource in the * resource header. */ osfseek(fpout, out_hdr_pos - 4, OSFSK_SET); oswp4(buf, out_endpos); if (osfwb(fpout, buf, 4)) errexit("error writing resource", 1); /* seek back to the end of the resource in the output file */ osfseek(fpout, out_endpos, OSFSK_SET); done: /* if we have an input list, free it */ if (in_list != 0) { /* delete all of the entries in the input table */ for (i = 0, p = in_list ; i < in_entry_cnt ; ++i, ++p) { /* delete this entry if we haven't already done so */ if (*p != 0) free(*p); } /* delete the input pointer list itself */ free(in_list); } /* * delete everything in the additions list, since we're done with * them now */ for (op = add_list ; op != 0 ; op = next_op) { /* note the next entry in the list */ next_op = op->opnxt; /* delete this entry */ free(op); } /* return the op list in its current form */ return oplist; }