diff options
Diffstat (limited to 'engines/director/lingo/lingo-code.cpp')
-rw-r--r-- | engines/director/lingo/lingo-code.cpp | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp new file mode 100644 index 0000000000..4c88e5d6e9 --- /dev/null +++ b/engines/director/lingo/lingo-code.cpp @@ -0,0 +1,794 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// Heavily inspired by hoc +// Copyright (C) AT&T 1995 +// All Rights Reserved +// +// Permission to use, copy, modify, and distribute this software and +// its documentation for any purpose and without fee is hereby +// granted, provided that the above copyright notice appear in all +// copies and that both that the copyright notice and this +// permission notice and warranty disclaimer appear in supporting +// documentation, and that the name of AT&T or any of its entities +// not be used in advertising or publicity pertaining to +// distribution of the software without specific, written prior +// permission. +// +// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +// THIS SOFTWARE. + +#include "engines/director/lingo/lingo.h" +#include "common/file.h" +#include "audio/decoders/wave.h" + +#include "director/lingo/lingo-gr.h" + +namespace Director { + +void Lingo::push(Datum d) { + _stack.push_back(d); +} + +void Lingo::pushVoid() { + Datum d; + d.u.i = 0; + d.type = VOID; + push(d); +} + +Datum Lingo::pop(void) { + if (_stack.size() == 0) + error("stack underflow"); + + Datum ret = _stack.back(); + _stack.pop_back(); + + return ret; +} + +void Lingo::c_xpop() { + g_lingo->pop(); +} + +void Lingo::c_printtop(void) { + Datum d = g_lingo->pop(); + + switch (d.type) { + case VOID: + warning("Void"); + break; + case INT: + warning("%d", d.u.i); + break; + case FLOAT: + warning(g_lingo->_floatPrecisionFormat.c_str(), d.u.f); + break; + case VAR: + if (!d.u.sym) { + warning("Inconsistent stack: var, val: %d", d.u.i); + } else { + if (d.u.sym->name) + warning("var: %s", d.u.sym->name); + else + warning("Nameless var. val: %d", d.u.sym->u.i); + } + break; + case STRING: + warning("%s", d.u.s->c_str()); + break; + case POINT: + warning("point(%d, %d)", (int)((*d.u.arr)[0]), (int)((*d.u.arr)[1])); + break; + case SYMBOL: + warning("%s", d.type2str(true)); + break; + default: + warning("--unknown--"); + } +} + +void Lingo::c_constpush() { + Datum d; + inst i = (*g_lingo->_currentScript)[g_lingo->_pc++]; + d.u.i = READ_UINT32(&i); + d.type = INT; + g_lingo->push(d); +} + +void Lingo::c_fconstpush() { + Datum d; + inst i = (*g_lingo->_currentScript)[g_lingo->_pc]; + d.u.f = *((double *)&i); + d.type = FLOAT; + + g_lingo->_pc += g_lingo->calcCodeAlignment(sizeof(double)); + + g_lingo->push(d); +} + +void Lingo::c_stringpush() { + Datum d; + char *s = (char *)&(*g_lingo->_currentScript)[g_lingo->_pc]; + g_lingo->_pc += g_lingo->calcStringAlignment(s); + + d.u.s = new Common::String(s); + d.type = STRING; + g_lingo->push(d); +} + +void Lingo::c_varpush() { + char *name = (char *)&(*g_lingo->_currentScript)[g_lingo->_pc]; + Datum d; + + d.u.sym = g_lingo->lookupVar(name); + if (d.u.sym->type == CASTREF) { + d.type = INT; + int val = d.u.sym->u.i; + + delete d.u.sym; + + d.u.i = val; + } else { + d.type = VAR; + } + + g_lingo->_pc += g_lingo->calcStringAlignment(name); + + g_lingo->push(d); +} + +void Lingo::c_assign() { + Datum d1, d2; + d1 = g_lingo->pop(); + d2 = g_lingo->pop(); + + if (d1.type != VAR) { + warning("assignment to non-variable"); + return; + } + + if (d1.u.sym->type != INT && d1.u.sym->type != VOID && + d1.u.sym->type != FLOAT && d1.u.sym->type != STRING) { + warning("assignment to non-variable '%s'", d1.u.sym->name); + return; + } + + if (d1.u.sym->type == STRING) // Free memory if needed + delete d1.u.sym->u.s; + + if (d1.u.sym->type == POINT || d1.u.sym->type == RECT || d1.u.sym->type == ARRAY) + delete d1.u.sym->u.arr; + + if (d2.type == INT) { + d1.u.sym->u.i = d2.u.i; + } else if (d2.type == FLOAT) { + d1.u.sym->u.f = d2.u.f; + } else if (d2.type == STRING) { + d1.u.sym->u.s = new Common::String(*d2.u.s); + delete d2.u.s; + } else if (d2.type == POINT) { + d1.u.sym->u.arr = new FloatArray(*d2.u.arr); + delete d2.u.arr; + } else if (d2.type == SYMBOL) { + d1.u.sym->u.i = d2.u.i; + } else { + warning("c_assign: unhandled type: %s", d2.type2str()); + } + + d1.u.sym->type = d2.type; + + g_lingo->push(d1); +} + +bool Lingo::verify(Symbol *s) { + if (s->type != INT && s->type != VOID && s->type != FLOAT && s->type != STRING && s->type != POINT) { + warning("attempt to evaluate non-variable '%s'", s->name); + + return false; + } + + if (s->type == VOID) + warning("Variable used before assigning a value '%s'", s->name); + + return true; +} + +void Lingo::c_eval() { + g_lingo->c_varpush(); + + Datum d; + d = g_lingo->pop(); + + if (d.type != VAR) { // It could be cast ref + g_lingo->push(d); + return; + } + + if (!g_lingo->verify(d.u.sym)) + return; + + d.type = d.u.sym->type; + + if (d.u.sym->type == INT) + d.u.i = d.u.sym->u.i; + else if (d.u.sym->type == FLOAT) + d.u.f = d.u.sym->u.f; + else if (d.u.sym->type == STRING) + d.u.s = new Common::String(*d.u.sym->u.s); + else if (d.u.sym->type == POINT) + d.u.arr = d.u.sym->u.arr; + else if (d.u.sym->type == SYMBOL) + d.u.i = d.u.sym->u.i; + else + warning("c_eval: unhandled type: %s", d.type2str()); + + g_lingo->push(d); +} + +void Lingo::c_theentitypush() { + inst e = (*g_lingo->_currentScript)[g_lingo->_pc++]; + inst f = (*g_lingo->_currentScript)[g_lingo->_pc++]; + Datum id = g_lingo->pop(); + + int entity = READ_UINT32(&e); + int field = READ_UINT32(&f); + + Datum d = g_lingo->getTheEntity(entity, id, field); + g_lingo->push(d); +} + +void Lingo::c_theentityassign() { + inst e = (*g_lingo->_currentScript)[g_lingo->_pc++]; + inst f = (*g_lingo->_currentScript)[g_lingo->_pc++]; + Datum id = g_lingo->pop(); + + int entity = READ_UINT32(&e); + int field = READ_UINT32(&f); + + Datum d = g_lingo->pop(); + g_lingo->setTheEntity(entity, id, field, d); + + g_lingo->push(d); // Dummy value +} + +void Lingo::c_swap() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + g_lingo->push(d2); + g_lingo->push(d1); +} + +void Lingo::c_add() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.f += d2.u.f; + } else { + d1.u.i += d2.u.i; + } + g_lingo->push(d1); +} + +void Lingo::c_sub() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.f -= d2.u.f; + } else { + d1.u.i -= d2.u.i; + } + g_lingo->push(d1); +} + +void Lingo::c_mul() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.f *= d2.u.f; + } else { + d1.u.i *= d2.u.i; + } + g_lingo->push(d1); +} + +void Lingo::c_div() { + Datum d2 = g_lingo->pop(); + + if ((d2.type == INT && d2.u.i == 0) || + (d2.type == FLOAT && d2.u.f == 0.0)) + error("division by zero"); + + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.f /= d2.u.f; + } else { + d1.u.i /= d2.u.i; + } + g_lingo->push(d1); +} + +void Lingo::c_negate() { + Datum d = g_lingo->pop(); + + if (d.type == INT) + d.u.i = -d.u.i; + else if (d.type == FLOAT) + d.u.f = -d.u.f; + + g_lingo->push(d); +} + +void Lingo::c_ampersand() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + d1.toString(); + d2.toString(); + + *d1.u.s += *d2.u.s; + + delete d2.u.s; + + g_lingo->push(d1); +} + +void Lingo::c_concat() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + d1.toString(); + d2.toString(); + + *d1.u.s += " "; + *d1.u.s += *d2.u.s; + + delete d2.u.s; + + g_lingo->push(d1); +} + +void Lingo::c_contains() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + d1.toString(); + d2.toString(); + + Common::String *s1 = g_lingo->toLowercaseMac(d1.u.s); + Common::String *s2 = g_lingo->toLowercaseMac(d2.u.s); + + int res = s1->contains(*s2) ? 1 : 0; + + delete d1.u.s; + delete d2.u.s; + delete s1; + delete s2; + + d1.type = INT; + d1.u.i = res; + + g_lingo->push(d1); +} + +void Lingo::c_starts() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + d1.toString(); + d2.toString(); + + Common::String *s1 = g_lingo->toLowercaseMac(d1.u.s); + Common::String *s2 = g_lingo->toLowercaseMac(d2.u.s); + + int res = s1->hasPrefix(*s2) ? 1 : 0; + + delete d1.u.s; + delete d2.u.s; + delete s1; + delete s2; + + d1.type = INT; + d1.u.i = res; + + g_lingo->push(d1); +} + +void Lingo::c_intersects() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + warning("STUB: c_intersects"); + + g_lingo->push(d1); +} + +void Lingo::c_within() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + warning("STUB: c_within"); + + g_lingo->push(d1); +} + +void Lingo::c_and() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + d1.toInt(); + d2.toInt(); + + d1.u.i = (d1.u.i && d2.u.i) ? 1 : 0; + + g_lingo->push(d1); +} + +void Lingo::c_or() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + d1.toInt(); + d2.toInt(); + + d1.u.i = (d1.u.i || d2.u.i) ? 1 : 0; + + g_lingo->push(d1); +} + +void Lingo::c_not() { + Datum d = g_lingo->pop(); + + d.toInt(); + + d.u.i = ~d.u.i ? 1 : 0; + + g_lingo->push(d); +} + +void Lingo::c_eq() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.i = (d1.u.f == d2.u.f) ? 1 : 0; + d1.type = INT; + } else { + d1.u.i = (d1.u.i == d2.u.i) ? 1 : 0; + } + g_lingo->push(d1); +} + +void Lingo::c_neq() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.i = (d1.u.f != d2.u.f) ? 1 : 0; + d1.type = INT; + } else { + d1.u.i = (d1.u.i != d2.u.i) ? 1 : 0; + } + g_lingo->push(d1); +} + +void Lingo::c_gt() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.i = (d1.u.f > d2.u.f) ? 1 : 0; + d1.type = INT; + } else { + d1.u.i = (d1.u.i > d2.u.i) ? 1 : 0; + } + g_lingo->push(d1); +} + +void Lingo::c_lt() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.i = (d1.u.f < d2.u.f) ? 1 : 0; + d1.type = INT; + } else { + d1.u.i = (d1.u.i < d2.u.i) ? 1 : 0; + } + g_lingo->push(d1); +} + +void Lingo::c_ge() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.i = (d1.u.f >= d2.u.f) ? 1 : 0; + d1.type = INT; + } else { + d1.u.i = (d1.u.i >= d2.u.i) ? 1 : 0; + } + g_lingo->push(d1); +} + +void Lingo::c_le() { + Datum d2 = g_lingo->pop(); + Datum d1 = g_lingo->pop(); + + if (g_lingo->alignTypes(d1, d2) == FLOAT) { + d1.u.i = (d1.u.f <= d2.u.f) ? 1 : 0; + d1.type = INT; + } else { + d1.u.i = (d1.u.i <= d2.u.i) ? 1 : 0; + } + g_lingo->push(d1); +} + +void Lingo::c_repeatwhilecode(void) { + Datum d; + int savepc = g_lingo->_pc; + + int body = READ_UINT32(&(*g_lingo->_currentScript)[savepc]); + int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]); + + g_lingo->execute(savepc + 2); /* condition */ + d = g_lingo->pop(); + d.toInt(); + + while (d.u.i) { + g_lingo->execute(body); /* body */ + if (g_lingo->_returning) + break; + + g_lingo->execute(savepc + 2); /* condition */ + d = g_lingo->pop(); + d.toInt(); + } + + if (!g_lingo->_returning) + g_lingo->_pc = end; /* next stmt */ +} + +void Lingo::c_repeatwithcode(void) { + Datum d; + int savepc = g_lingo->_pc; + + int init = READ_UINT32(&(*g_lingo->_currentScript)[savepc]); + int finish = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]); + int body = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]); + int inc = (int32)READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]); + int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 4]); + Common::String countername((char *)&(*g_lingo->_currentScript)[savepc + 5]); + Symbol *counter = g_lingo->lookupVar(countername.c_str()); + + if (counter->type == CASTREF) { + error("Cast ref used as index: %s", countername.c_str()); + } + + g_lingo->execute(init); /* condition */ + d = g_lingo->pop(); + d.toInt(); + counter->u.i = d.u.i; + counter->type = INT; + + while (true) { + g_lingo->execute(body); /* body */ + if (g_lingo->_returning) + break; + + counter->u.i += inc; + g_lingo->execute(finish); /* condition */ + d = g_lingo->pop(); + d.toInt(); + + if (counter->u.i == d.u.i + inc) + break; + } + + if (!g_lingo->_returning) + g_lingo->_pc = end; /* next stmt */ +} + +void Lingo::c_ifcode() { + Datum d; + int savepc = g_lingo->_pc; /* then part */ + + int then = READ_UINT32(&(*g_lingo->_currentScript)[savepc]); + int elsep = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]); + int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]); + int skipEnd = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]); + + debug(8, "executing cond (have to %s end)", skipEnd ? "skip" : "execute"); + g_lingo->execute(savepc + 4); /* condition */ + + d = g_lingo->pop(); + + if (d.toInt()) { + debug(8, "executing then"); + g_lingo->execute(then); + } else if (elsep) { /* else part? */ + debug(8, "executing else"); + g_lingo->execute(elsep); + } + + if (!g_lingo->_returning && !skipEnd) { + g_lingo->_pc = end; /* next stmt */ + debug(8, "executing end"); + } else + debug(8, "Skipped end"); +} + +//************************ +// Built-in functions +//************************ +void Lingo::c_mci() { + Common::String s((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]); + + g_lingo->func_mci(s); + + g_lingo->_pc += g_lingo->calcStringAlignment(s.c_str()); +} + +void Lingo::c_mciwait() { + Common::String s((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]); + + g_lingo->func_mciwait(s); + + g_lingo->_pc += g_lingo->calcStringAlignment(s.c_str()); +} + +void Lingo::c_goto() { + Common::String frame((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]); + g_lingo->_pc += g_lingo->calcStringAlignment(frame.c_str()); + + Common::String movie((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]); + g_lingo->_pc += g_lingo->calcStringAlignment(movie.c_str()); + + g_lingo->func_goto(frame, movie); +} + +void Lingo::c_gotoloop() { + g_lingo->func_gotoloop(); +} + +void Lingo::c_gotonext() { + g_lingo->func_gotonext(); +} + +void Lingo::c_gotoprevious() { + g_lingo->func_gotoprevious(); +} + +void Lingo::c_call() { + Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]); + g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str()); + + int nargs = READ_UINT32(&(*g_lingo->_currentScript)[g_lingo->_pc++]); + + if (!g_lingo->_handlers.contains(name)) { + warning("Call to undefined handler '%s'. Dropping %d stack items", name.c_str(), nargs); + + for (int i = 0; i < nargs; i++) + g_lingo->pop(); + + // Push dummy value + g_lingo->pushVoid(); + + return; + } + + Symbol *sym = g_lingo->_handlers[name]; + + if (sym->nargs < nargs) { + warning("Incorrect number of arguments for function %s. Dropping extra %d", name.c_str(), nargs - sym->nargs); + for (int i = 0; i < nargs - sym->nargs; i++) + g_lingo->pop(); + } + + if (sym->type == BLTIN) { + if (sym->nargs > 0 && nargs < sym->nargs) { + warning("Too few arguments for function %s. Expecting %d but got %d", name.c_str(), sym->nargs, nargs); + for (int i = 0; i < nargs; i++) + g_lingo->pop(); + + g_lingo->pushVoid(); + + return; + } + (*sym->u.func)(); + + return; + } + + for (int i = nargs; i < sym->nargs; i++) { + Datum d; + + d.u.i = 0; + d.type = VOID; + g_lingo->push(d); + } + + CFrame *fp = new CFrame; + + fp->sp = sym; + fp->retpc = g_lingo->_pc; + fp->retscript = g_lingo->_currentScript; + fp->localvars = g_lingo->_localvars; + + // Create new set of local variables + g_lingo->_localvars = new SymbolHash; + + g_lingo->_callstack.push_back(fp); + + g_lingo->_currentScript = sym->u.defn; + g_lingo->execute(0); + + g_lingo->_returning = false; +} + +void Lingo::c_procret() { + if (!g_lingo->_callstack.size()) { + warning("Call stack underflow"); + g_lingo->_returning = true; + return; + } + + CFrame *fp = g_lingo->_callstack.back(); + + g_lingo->_currentScript = fp->retscript; + g_lingo->_pc = fp->retpc; + + g_lingo->cleanLocalVars(); + + // Restore local variables + g_lingo->_localvars = fp->localvars; + + delete fp; + + g_lingo->_returning = true; +} + +void Lingo::c_global() { + Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]); + + Symbol *s = g_lingo->lookupVar(name.c_str(), false); + if (s && !s->global) { + warning("Local variable %s declared as global", name.c_str()); + } + + s = g_lingo->lookupVar(name.c_str(), true, true); + s->global = true; + + g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str()); +} + +} |