Exemple #1
0
/*
 *   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);
}
Exemple #2
0
/*
 *   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;
            }
        }
    }
}
Exemple #3
0
/*
 *   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;
    }
}
Exemple #4
0
/*
 *   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;
}
Exemple #5
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);
}
Exemple #6
0
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;
	}
}
Exemple #7
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;
    }
}
Exemple #8
0
/*
 *   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);
    }
}
Exemple #9
0
/*
 *   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);
}
Exemple #10
0
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));
    }
}
Exemple #11
0
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);
}
Exemple #12
0
/* 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);
}
Exemple #13
0
/* 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, &copyrsc,
                                     &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, &copyrsc,
                             &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;
}
Exemple #15
0
/*
 *   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);
}
Exemple #16
0
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();
}
Exemple #17
0
/* 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);
}
Exemple #18
0
/*
 *   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_);
    }
}
Exemple #20
0
/*
 *   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);
        }
    }
}
Exemple #21
0
/*
 *   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_);
}
Exemple #22
0
/*
 *   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);
        }
    }
}
Exemple #23
0
/*
 *   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);
    }
}
Exemple #24
0
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;
}
Exemple #25
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;
}