aboutsummaryrefslogtreecommitdiff
path: root/engines/gob/expression.cpp
diff options
context:
space:
mode:
authorSven Hesse2009-06-22 16:30:35 +0000
committerSven Hesse2009-06-22 16:30:35 +0000
commitc3aafec1fb516d76fa03c0ffaf2248b90dada7b4 (patch)
tree94d5dee6ac5f985fab46ba9dbd96aee4eb540d4e /engines/gob/expression.cpp
parent487a9bd14b8b8978bfb3f05cf90e712b9b727d59 (diff)
downloadscummvm-rg350-c3aafec1fb516d76fa03c0ffaf2248b90dada7b4.tar.gz
scummvm-rg350-c3aafec1fb516d76fa03c0ffaf2248b90dada7b4.tar.bz2
scummvm-rg350-c3aafec1fb516d76fa03c0ffaf2248b90dada7b4.zip
Renaming parse.h and parse.cpp to expression.h and expression.cpp
svn-id: r41773
Diffstat (limited to 'engines/gob/expression.cpp')
-rw-r--r--engines/gob/expression.cpp1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/engines/gob/expression.cpp b/engines/gob/expression.cpp
new file mode 100644
index 0000000000..eb465534c7
--- /dev/null
+++ b/engines/gob/expression.cpp
@@ -0,0 +1,1145 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/endian.h"
+
+#include "gob/gob.h"
+#include "gob/expression.h"
+#include "gob/global.h"
+#include "gob/game.h"
+#include "gob/script.h"
+#include "gob/inter.h"
+
+namespace Gob {
+
+Expression::Stack::Stack(size_t size) {
+ opers = new byte[size];
+ values = new int32[size];
+ memset(opers , 0, size * sizeof(byte ));
+ memset(values, 0, size * sizeof(int32));
+}
+
+Expression::Stack::~Stack() {
+ delete[] opers;
+ delete[] values;
+}
+
+Expression::StackFrame::StackFrame(const Stack &stack) {
+ opers = stack.opers - 1;
+ values = stack.values - 1;
+ pos = -1;
+}
+
+void Expression::StackFrame::push(int count) {
+ opers += count;
+ values += count;
+ pos += count;
+}
+
+void Expression::StackFrame::pop(int count) {
+ opers -= count;
+ values -= count;
+ pos -= count;
+}
+
+Expression::Expression(GobEngine *vm) : _vm(vm) {
+ _resultStr[0] = 0;
+ _resultInt = 0;
+}
+
+int32 Expression::encodePtr(byte *ptr, int type) {
+ int32 offset = 0;
+
+ switch (type) {
+ case kExecPtr:
+ offset = _vm->_game->_script->getOffset(ptr);
+ break;
+ case kInterVar:
+ offset = ptr - ((byte *) _vm->_inter->_variables->getAddressOff8(0));
+ break;
+ case kResStr:
+ offset = ptr - ((byte *) _resultStr);
+ break;
+ default:
+ error("Expression::encodePtr(): Unknown pointer type");
+ }
+ assert((offset & 0xF0000000) == 0);
+ return (type << 28) | offset;
+}
+
+byte *Expression::decodePtr(int32 n) {
+ byte *ptr;
+
+ switch (n >> 28) {
+ case kExecPtr:
+ ptr = _vm->_game->_script->getData();
+ break;
+ case kInterVar:
+ ptr = (byte *) _vm->_inter->_variables->getAddressOff8(0);
+ break;
+ case kResStr:
+ ptr = (byte *) _resultStr;
+ break;
+ default:
+ error("Expression::decodePtr(): Unknown pointer type");
+ }
+ return ptr + (n & 0x0FFFFFFF);
+}
+
+void Expression::skipExpr(char stopToken) {
+ int16 dimCount;
+ byte operation;
+ int16 num;
+ int16 dim;
+
+ num = 0;
+ while (true) {
+ operation = _vm->_game->_script->readByte();
+
+ if ((operation >= 14) && (operation <= OP_FUNC)) {
+ switch (operation) {
+ case 14:
+ _vm->_game->_script->skip(4);
+ if (_vm->_game->_script->peekByte() == 97)
+ _vm->_game->_script->skip(1);
+ break;
+
+ case OP_LOAD_VAR_INT16:
+ case OP_LOAD_VAR_INT8:
+ case OP_LOAD_IMM_INT16:
+ case OP_LOAD_VAR_INT32:
+ case OP_LOAD_VAR_INT32_AS_INT16:
+ _vm->_game->_script->skip(2);
+ break;
+
+ case OP_LOAD_IMM_INT32:
+ _vm->_game->_script->skip(4);
+ break;
+
+ case OP_LOAD_IMM_INT8:
+ _vm->_game->_script->skip(1);
+ break;
+
+ case OP_LOAD_IMM_STR:
+ _vm->_game->_script->skip(strlen(_vm->_game->_script->peekString()) + 1);
+ break;
+
+ case OP_LOAD_VAR_STR:
+ _vm->_game->_script->skip(2);
+ if (_vm->_game->_script->peekByte() == 13) {
+ _vm->_game->_script->skip(1);
+ skipExpr(OP_END_MARKER);
+ }
+ break;
+
+ case 15:
+ _vm->_game->_script->skip(2);
+
+ case OP_ARRAY_INT8:
+ case OP_ARRAY_INT32:
+ case OP_ARRAY_INT16:
+ case OP_ARRAY_STR:
+ dimCount = _vm->_game->_script->peekByte(2);
+ // skip header and dimensions
+ _vm->_game->_script->skip(3 + dimCount);
+ // skip indices
+ for (dim = 0; dim < dimCount; dim++)
+ skipExpr(OP_END_MARKER);
+
+ if ((operation == OP_ARRAY_STR) && (_vm->_game->_script->peekByte() == 13)) {
+ _vm->_game->_script->skip(1);
+ skipExpr(OP_END_MARKER);
+ }
+ break;
+
+ case OP_FUNC:
+ _vm->_game->_script->skip(1);
+ skipExpr(OP_END_EXPR);
+ }
+ continue;
+ } // if ((operation >= OP_ARRAY_INT8) && (operation <= OP_FUNC))
+
+ if (operation == OP_BEGIN_EXPR) {
+ num++;
+ continue;
+ }
+
+ if ((operation == OP_NOT) || ((operation >= OP_NEG) && (operation <= 8)))
+ continue;
+
+ if ((operation >= OP_OR) && (operation <= OP_NEQ))
+ continue;
+
+ if (operation == OP_END_EXPR)
+ num--;
+
+ if (operation != stopToken)
+ continue;
+
+ if ((stopToken != OP_END_EXPR) || (num < 0))
+ return;
+ }
+}
+
+void Expression::printExpr(char stopToken) {
+ // Expression printing disabled by default
+ return;
+
+ int32 savedPos = _vm->_game->_script->pos();
+ printExpr_internal(stopToken);
+
+ // restore IP to start of expression
+ _vm->_game->_script->seek(savedPos);
+}
+
+void Expression::printExpr_internal(char stopToken) {
+ int16 dimCount;
+ byte operation;
+ int16 num;
+ int16 dim;
+ byte *arrDesc;
+ byte func;
+
+ num = 0;
+ while (true) {
+ operation = _vm->_game->_script->readByte();
+
+ if ((operation >= OP_ARRAY_INT8) && (operation <= OP_FUNC)) {
+ // operands
+
+ switch (operation) {
+ case OP_LOAD_VAR_INT16: // int16 variable load
+ debugN(5, "var16_%d", _vm->_game->_script->readUint16());
+ break;
+
+ case OP_LOAD_VAR_INT8: // int8 variable load:
+ debugN(5, "var8_%d", _vm->_game->_script->readUint16());
+ break;
+
+ case OP_LOAD_IMM_INT32: // int32/uint32 immediate
+ debugN(5, "%d", _vm->_game->_script->readInt32());
+ break;
+
+ case OP_LOAD_IMM_INT16: // int16 immediate
+ debugN(5, "%d", _vm->_game->_script->readInt16());
+ break;
+
+ case OP_LOAD_IMM_INT8: // int8 immediate
+ debugN(5, "%d", _vm->_game->_script->readInt8());
+ break;
+
+ case OP_LOAD_IMM_STR: // string immediate
+ debugN(5, "\42%s\42", _vm->_game->_script->readString());
+ break;
+
+ case OP_LOAD_VAR_INT32:
+ case OP_LOAD_VAR_INT32_AS_INT16:
+ debugN(5, "var_%d", _vm->_game->_script->readUint16());
+ break;
+
+ case OP_LOAD_VAR_STR: // string variable load
+ debugN(5, "(&var_%d)", _vm->_game->_script->readUint16());
+ if (_vm->_game->_script->peekByte() == 13) {
+ _vm->_game->_script->skip(1);
+ debugN(5, "{");
+ printExpr_internal(OP_END_MARKER); // this also prints the closing }
+ }
+ break;
+
+ case OP_ARRAY_INT8: // int8 array access
+ case OP_ARRAY_INT32: // int32 array access
+ case OP_ARRAY_INT16: // int16 array access
+ case OP_ARRAY_STR: // string array access
+ debugN(5, "\n");
+ if (operation == OP_ARRAY_STR)
+ debugN(5, "(&");
+
+ debugN(5, "var_%d[", _vm->_game->_script->readInt16());
+ dimCount = _vm->_game->_script->readByte();
+ arrDesc = _vm->_game->_script->getData() + _vm->_game->_script->pos();
+ _vm->_game->_script->skip(dimCount);
+ for (dim = 0; dim < dimCount; dim++) {
+ printExpr_internal(OP_END_MARKER);
+ debugN(5, " of %d", (int16) arrDesc[dim]);
+ if (dim != dimCount - 1)
+ debugN(5, ",");
+ }
+ debugN(5, "]");
+ if (operation == OP_ARRAY_STR)
+ debugN(5, ")");
+
+ if ((operation == OP_ARRAY_STR) && (_vm->_game->_script->peekByte() == 13)) {
+ _vm->_game->_script->skip(1);
+ debugN(5, "{");
+ printExpr_internal(OP_END_MARKER); // this also prints the closing }
+ }
+ break;
+
+ case OP_FUNC: // function
+ func = _vm->_game->_script->readByte();
+ if (func == FUNC_SQR)
+ debugN(5, "sqr(");
+ else if (func == FUNC_RAND)
+ debugN(5, "rand(");
+ else if (func == FUNC_ABS)
+ debugN(5, "abs(");
+ else if ((func == FUNC_SQRT1) || (func == FUNC_SQRT2) || (func == FUNC_SQRT3))
+ debugN(5, "sqrt(");
+ else
+ debugN(5, "id(");
+ printExpr_internal(OP_END_EXPR);
+ break;
+ }
+ continue;
+ } // if ((operation >= OP_ARRAY_INT8) && (operation <= OP_FUNC))
+
+ // operators
+ switch (operation) {
+ case OP_BEGIN_EXPR:
+ debugN(5, "(");
+ break;
+
+ case OP_NOT:
+ debugN(5, "!");
+ break;
+
+ case OP_END_EXPR:
+ debugN(5, ")");
+ break;
+
+ case OP_NEG:
+ debugN(5, "-");
+ break;
+
+ case OP_ADD:
+ debugN(5, "+");
+ break;
+
+ case OP_SUB:
+ debugN(5, "-");
+ break;
+
+ case OP_BITOR:
+ debugN(5, "|");
+ break;
+
+ case OP_MUL:
+ debugN(5, "*");
+ break;
+
+ case OP_DIV:
+ debugN(5, "/");
+ break;
+
+ case OP_MOD:
+ debugN(5, "%%");
+ break;
+
+ case OP_BITAND:
+ debugN(5, "&");
+ break;
+
+ case OP_OR:
+ debugN(5, "||");
+ break;
+
+ case 31:
+ debugN(5, "&&");
+ break;
+
+ case OP_LESS:
+ debugN(5, "<");
+ break;
+
+ case OP_LEQ:
+ debugN(5, "<=");
+ break;
+
+ case OP_GREATER:
+ debugN(5, ">");
+ break;
+
+ case OP_GEQ:
+ debugN(5, ">=");
+ break;
+
+ case OP_EQ:
+ debugN(5, "==");
+ break;
+
+ case OP_NEQ:
+ debugN(5, "!=");
+ break;
+
+ case 99:
+ debugN(5, "\n");
+ break;
+
+ case OP_END_MARKER:
+ debugN(5, "}");
+ if (stopToken != OP_END_MARKER) {
+ debugN(5, "Closing paren without opening?");
+ }
+ break;
+
+ default:
+ debugN(5, "<%d>", (int16) operation);
+ error("Expression::printExpr(): invalid operator in expression");
+ break;
+ }
+
+ if (operation == OP_BEGIN_EXPR) {
+ num++;
+ continue;
+ }
+
+ if ((operation == OP_NOT) || ((operation >= OP_NEG) && (operation <= 8)))
+ continue;
+
+ if ((operation >= OP_OR) && (operation <= OP_NEQ))
+ continue;
+
+ if (operation == OP_END_EXPR)
+ num--;
+
+ if (operation == stopToken) {
+ if ((stopToken != OP_END_EXPR) || (num < 0)) {
+ return;
+ }
+ }
+ }
+}
+
+
+void Expression::printVarIndex() {
+ byte *arrDesc;
+ int16 dim;
+ int16 dimCount;
+ int16 operation;
+ int16 temp;
+
+ int32 pos = _vm->_game->_script->pos();
+
+ operation = _vm->_game->_script->readByte();
+ switch (operation) {
+ case OP_LOAD_VAR_INT32:
+ case OP_LOAD_VAR_STR:
+ temp = _vm->_game->_script->readUint16() * 4;
+ debugN(5, "&var_%d", temp);
+ if ((operation == OP_LOAD_VAR_STR) && (_vm->_game->_script->peekByte() == 13)) {
+ _vm->_game->_script->skip(1);
+ debugN(5, "+");
+ printExpr(OP_END_MARKER);
+ }
+ break;
+
+ case OP_ARRAY_INT32:
+ case OP_ARRAY_STR:
+ debugN(5, "&var_%d[", _vm->_game->_script->readUint16());
+ dimCount = _vm->_game->_script->readByte();
+ arrDesc = _vm->_game->_script->getData() + _vm->_game->_script->pos();
+ _vm->_game->_script->skip(dimCount);
+ for (dim = 0; dim < dimCount; dim++) {
+ printExpr(OP_END_MARKER);
+ debugN(5, " of %d", (int16) arrDesc[dim]);
+ if (dim != dimCount - 1)
+ debugN(5, ",");
+ }
+ debugN(5, "]");
+
+ if ((operation == OP_ARRAY_STR) && (_vm->_game->_script->peekByte() == 13)) {
+ _vm->_game->_script->skip(1);
+ debugN(5, "+");
+ printExpr(OP_END_MARKER);
+ }
+ break;
+
+ default:
+ debugN(5, "var_0");
+ break;
+ }
+ debugN(5, "\n");
+
+ _vm->_game->_script->seek(pos);
+ return;
+}
+
+int Expression::cmpHelper(const StackFrame &stackFrame) {
+ byte type = stackFrame.opers[-3];
+ int cmpTemp = 0;
+
+ if (type == OP_LOAD_IMM_INT16) {
+ cmpTemp = (int)stackFrame.values[-3] - (int)stackFrame.values[-1];
+ } else if (type == OP_LOAD_IMM_STR) {
+ if ((char *)decodePtr(stackFrame.values[-3]) != _resultStr) {
+ strcpy(_resultStr, (char *)decodePtr(stackFrame.values[-3]));
+ stackFrame.values[-3] = encodePtr((byte *) _resultStr, kResStr);
+ }
+ cmpTemp = strcmp(_resultStr, (char *)decodePtr(stackFrame.values[-1]));
+ }
+
+ return cmpTemp;
+}
+
+bool Expression::getVarBase(uint32 &varBase, bool mindStop,
+ uint16 *size, uint16 *type) {
+
+ varBase = 0;
+
+ byte operation = _vm->_game->_script->peekByte();
+ while ((operation == 14) || (operation == 15)) {
+ _vm->_game->_script->skip(1);
+
+ if (operation == 14) {
+ // Add a direct offset
+
+ varBase += _vm->_game->_script->readInt16() * 4;
+
+ if (size)
+ *size = _vm->_game->_script->peekUint16();
+ if (type)
+ *type = 14;
+
+ _vm->_game->_script->skip(2);
+
+ debugC(2, kDebugParser, "varBase: %d, by %d", varBase, operation);
+
+ if (_vm->_game->_script->peekByte() != 97) {
+ if (mindStop)
+ return true;
+ } else
+ _vm->_game->_script->skip(1);
+
+ } else if (operation == 15) {
+ // Add an offset from an array
+
+ varBase += _vm->_game->_script->readInt16() * 4;
+
+ uint16 offset1 = _vm->_game->_script->readUint16();
+
+ if (size)
+ *size = offset1;
+ if (type)
+ *type = 15;
+
+ uint8 dimCount = _vm->_game->_script->readByte();
+ byte *dimArray = _vm->_game->_script->getData() + _vm->_game->_script->pos();
+
+ _vm->_game->_script->skip(dimCount);
+
+ uint16 offset2 = 0;
+ for (int i = 0; i < dimCount; i++) {
+ int16 dim = CLIP<int>(parseValExpr(OP_END_MARKER), 0, dimArray[i] - 1);
+
+ offset2 = offset2 * dimArray[i] + dim;
+ }
+
+ varBase += offset2 * offset1 * 4;
+
+ debugC(2, kDebugParser, "varBase: %d, by %d", varBase, operation);
+
+ if (_vm->_game->_script->peekByte() != 97) {
+ if (mindStop)
+ return true;
+ } else
+ _vm->_game->_script->skip(1);
+ }
+
+ operation = _vm->_game->_script->peekByte();
+ }
+
+ return false;
+}
+
+int16 Expression::parseVarIndex(uint16 *size, uint16 *type) {
+ int16 temp2;
+ byte *arrDesc;
+ int16 dim;
+ int16 dimCount;
+ int16 operation;
+ int16 temp;
+ int16 offset;
+ int16 val;
+ uint32 varBase;
+
+ if (getVarBase(varBase, true, size, type))
+ return varBase;
+
+ operation = _vm->_game->_script->readByte();
+
+ if (size)
+ *size = 0;
+ if (type)
+ *type = operation;
+
+ debugC(5, kDebugParser, "var parse = %d", operation);
+ switch (operation) {
+ case OP_ARRAY_INT8:
+ case OP_ARRAY_INT32:
+ case OP_ARRAY_INT16:
+ case OP_ARRAY_STR:
+ temp = _vm->_game->_script->readInt16();
+ dimCount = _vm->_game->_script->readByte();
+ arrDesc = _vm->_game->_script->getData() + _vm->_game->_script->pos();
+ _vm->_game->_script->skip(dimCount);
+ offset = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(OP_END_MARKER);
+ offset = arrDesc[dim] * offset + temp2;
+ }
+ if (operation == OP_ARRAY_INT8)
+ return varBase + temp + offset;
+ if (operation == OP_ARRAY_INT32)
+ return varBase + (temp + offset) * 4;
+ if (operation == OP_ARRAY_INT16)
+ return varBase + (temp + offset) * 2;
+ temp *= 4;
+ offset *= 4;
+ if (_vm->_game->_script->peekByte() == 13) {
+ _vm->_game->_script->skip(1);
+ temp += parseValExpr(OP_END_MARKER);
+ }
+ return varBase + offset * _vm->_global->_inter_animDataSize + temp;
+
+ case OP_LOAD_VAR_INT16:
+ return varBase + _vm->_game->_script->readInt16() * 2;
+
+ case OP_LOAD_VAR_INT8:
+ return varBase + _vm->_game->_script->readInt16();
+
+ case OP_LOAD_VAR_INT32:
+ case OP_LOAD_VAR_INT32_AS_INT16:
+ case OP_LOAD_VAR_STR:
+ temp = _vm->_game->_script->readInt16() * 4;
+ debugC(5, kDebugParser, "oper = %d", _vm->_game->_script->peekInt16());
+ if ((operation == OP_LOAD_VAR_STR) && (_vm->_game->_script->peekByte() == 13)) {
+ _vm->_game->_script->skip(1);
+ val = parseValExpr(OP_END_MARKER);
+ temp += val;
+ debugC(5, kDebugParser, "parse subscript = %d", val);
+ }
+ return varBase + temp;
+
+ default:
+ return 0;
+ }
+}
+
+int16 Expression::parseValExpr(byte stopToken) {
+ parseExpr(stopToken, 0);
+
+ return _resultInt;
+}
+
+// Load a value according to the operation
+void Expression::loadValue(byte operation, uint32 varBase, const StackFrame &stackFrame) {
+ int16 dimCount;
+ int16 temp;
+ int16 temp2;
+ int16 offset;
+ int16 dim;
+ byte *arrDescPtr;
+ int32 prevPrevVal;
+ int32 prevVal;
+ int32 curVal;
+
+ switch (operation) {
+ case OP_ARRAY_INT8:
+ case OP_ARRAY_INT32:
+ case OP_ARRAY_INT16:
+ case OP_ARRAY_STR:
+ *stackFrame.opers = (operation == OP_ARRAY_STR) ? OP_LOAD_IMM_STR : OP_LOAD_IMM_INT16;
+ temp = _vm->_game->_script->readInt16();
+ dimCount = _vm->_game->_script->readByte();
+ arrDescPtr = _vm->_game->_script->getData() + _vm->_game->_script->pos();
+ _vm->_game->_script->skip(dimCount);
+ offset = 0;
+ for (dim = 0; dim < dimCount; dim++) {
+ temp2 = parseValExpr(OP_END_MARKER);
+ offset = offset * arrDescPtr[dim] + temp2;
+ }
+ if (operation == OP_ARRAY_INT8)
+ *stackFrame.values = (int8) READ_VARO_UINT8(varBase + temp + offset);
+ else if (operation == OP_ARRAY_INT32)
+ *stackFrame.values = READ_VARO_UINT32(varBase + temp * 4 + offset * 4);
+ else if (operation == OP_ARRAY_INT16)
+ *stackFrame.values = (int16) READ_VARO_UINT16(varBase + temp * 2 + offset * 2);
+ else if (operation == OP_ARRAY_STR) {
+ *stackFrame.values = encodePtr(_vm->_inter->_variables->getAddressOff8(
+ varBase + temp * 4 + offset * _vm->_global->_inter_animDataSize * 4),
+ kInterVar);
+ if (_vm->_game->_script->peekByte() == 13) {
+ _vm->_game->_script->skip(1);
+ temp2 = parseValExpr(OP_END_MARKER);
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = READ_VARO_UINT8(varBase + temp * 4 +
+ offset * 4 * _vm->_global->_inter_animDataSize + temp2);
+ }
+ }
+ break;
+
+ case OP_LOAD_VAR_INT16:
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = (int16) READ_VARO_UINT16(varBase + _vm->_game->_script->readInt16() * 2);
+ break;
+
+ case OP_LOAD_VAR_INT8:
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = (int8) READ_VARO_UINT8(varBase + _vm->_game->_script->readInt16());
+ break;
+
+ case OP_LOAD_IMM_INT32:
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = _vm->_game->_script->readInt32();
+ break;
+
+ case OP_LOAD_IMM_INT16:
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = _vm->_game->_script->readInt16();
+ break;
+
+ case OP_LOAD_IMM_INT8:
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = _vm->_game->_script->readInt8();
+ break;
+
+ case OP_LOAD_IMM_STR:
+ *stackFrame.opers = OP_LOAD_IMM_STR;
+ *stackFrame.values = encodePtr((byte *) _vm->_game->_script->readString(), kExecPtr);
+ break;
+
+ case OP_LOAD_VAR_INT32:
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = READ_VARO_UINT32(varBase + _vm->_game->_script->readInt16() * 4);
+ break;
+
+ case OP_LOAD_VAR_INT32_AS_INT16:
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = (int16) READ_VARO_UINT16(varBase + _vm->_game->_script->readInt16() * 4);
+ break;
+
+ case OP_LOAD_VAR_STR:
+ *stackFrame.opers = OP_LOAD_IMM_STR;
+ temp = _vm->_game->_script->readInt16() * 4;
+ *stackFrame.values = encodePtr(_vm->_inter->_variables->getAddressOff8(varBase + temp), kInterVar);
+ if (_vm->_game->_script->peekByte() == 13) {
+ _vm->_game->_script->skip(1);
+ temp += parseValExpr(OP_END_MARKER);
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = READ_VARO_UINT8(varBase + temp);
+ }
+ break;
+
+ case OP_FUNC:
+ operation = _vm->_game->_script->readByte();
+ parseExpr(OP_END_EXPR, 0);
+
+ switch (operation) {
+ case FUNC_SQRT1:
+ case FUNC_SQRT2:
+ case FUNC_SQRT3:
+ curVal = 1;
+ prevVal = 1;
+
+ do {
+ prevPrevVal = prevVal;
+ prevVal = curVal;
+ curVal = (curVal + _resultInt / curVal) / 2;
+ } while ((curVal != prevVal) && (curVal != prevPrevVal));
+ _resultInt = curVal;
+ break;
+
+ case FUNC_SQR:
+ _resultInt =
+ _resultInt * _resultInt;
+ break;
+
+ case FUNC_ABS:
+ if (_resultInt < 0)
+ _resultInt = -_resultInt;
+ break;
+
+ case FUNC_RAND:
+ _resultInt =
+ _vm->_util->getRandom(_resultInt);
+ break;
+ }
+
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ *stackFrame.values = _resultInt;
+ break;
+ }
+}
+
+void Expression::simpleArithmetic1(StackFrame &stackFrame) {
+ switch (stackFrame.opers[-1]) {
+ case OP_ADD:
+ if (stackFrame.opers[-2] == OP_LOAD_IMM_STR) {
+ if ((char *) decodePtr(stackFrame.values[-2]) != _resultStr) {
+ strcpy(_resultStr, (char *) decodePtr(stackFrame.values[-2]));
+ stackFrame.values[-2] = encodePtr((byte *) _resultStr, kResStr);
+ }
+ strcat(_resultStr, (char *) decodePtr(stackFrame.values[0]));
+ stackFrame.pop(2);
+ }
+ break;
+
+ case OP_MUL:
+ stackFrame.values[-2] *= stackFrame.values[0];
+ stackFrame.pop(2);
+ break;
+
+ case OP_DIV:
+ stackFrame.values[-2] /= stackFrame.values[0];
+ stackFrame.pop(2);
+ break;
+
+ case OP_MOD:
+ stackFrame.values[-2] %= stackFrame.values[0];
+ stackFrame.pop(2);
+ break;
+
+ case OP_BITAND:
+ stackFrame.values[-2] &= stackFrame.values[0];
+ stackFrame.pop(2);
+ break;
+ }
+}
+
+void Expression::simpleArithmetic2(StackFrame &stackFrame) {
+ if (stackFrame.pos > 1) {
+ if (stackFrame.opers[-2] == OP_NEG) {
+ stackFrame.opers[-2] = OP_LOAD_IMM_INT16;
+ stackFrame.values[-2] = -stackFrame.values[-1];
+ stackFrame.pop();
+ } else if (stackFrame.opers[-2] == OP_NOT) {
+ stackFrame.opers[-2] = (stackFrame.opers[-1] == GOB_FALSE) ? GOB_TRUE : GOB_FALSE;
+ stackFrame.pop();
+ }
+ }
+
+ if (stackFrame.pos > 2) {
+ switch (stackFrame.opers[-2]) {
+ case OP_MUL:
+ stackFrame.values[-3] *= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_DIV:
+ stackFrame.values[-3] /= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_MOD:
+ stackFrame.values[-3] %= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_BITAND:
+ stackFrame.values[-3] &= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+ }
+ }
+
+}
+
+// Complex arithmetics with brackets
+bool Expression::complexArithmetic(Stack &stack, StackFrame &stackFrame, int16 brackStart) {
+ switch (stackFrame.opers[-2]) {
+ case OP_ADD:
+ if (stack.opers[brackStart] == OP_LOAD_IMM_INT16) {
+ stack.values[brackStart] += stackFrame.values[-1];
+ } else if (stack.opers[brackStart] == OP_LOAD_IMM_STR) {
+ if ((char *) decodePtr(stack.values[brackStart]) != _resultStr) {
+ strcpy(_resultStr, (char *) decodePtr(stack.values[brackStart]));
+ stack.values[brackStart] =
+ encodePtr((byte *) _resultStr, kResStr);
+ }
+ strcat(_resultStr, (char *) decodePtr(stackFrame.values[-1]));
+ }
+ stackFrame.pop(2);
+ break;
+
+ case OP_SUB:
+ stack.values[brackStart] -= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_BITOR:
+ stack.values[brackStart] |= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_MUL:
+ stackFrame.values[-3] *= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_DIV:
+ stackFrame.values[-3] /= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_MOD:
+ stackFrame.values[-3] %= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_BITAND:
+ stackFrame.values[-3] &= stackFrame.values[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_OR:
+ // (x OR false) == x
+ // (x OR true) == true
+ if (stackFrame.opers[-3] == GOB_FALSE)
+ stackFrame.opers[-3] = stackFrame.opers[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_AND:
+ // (x AND false) == false
+ // (x AND true) == x
+ if (stackFrame.opers[-3] == GOB_TRUE)
+ stackFrame.opers[-3] = stackFrame.opers[-1];
+ stackFrame.pop(2);
+ break;
+
+ case OP_LESS:
+ stackFrame.opers[-3] = (cmpHelper(stackFrame) < 0) ? GOB_TRUE : GOB_FALSE;
+ stackFrame.pop(2);
+ break;
+
+ case OP_LEQ:
+ stackFrame.opers[-3] = (cmpHelper(stackFrame) <= 0) ? GOB_TRUE : GOB_FALSE;
+ stackFrame.pop(2);
+ break;
+
+ case OP_GREATER:
+ stackFrame.opers[-3] = (cmpHelper(stackFrame) > 0) ? GOB_TRUE : GOB_FALSE;
+ stackFrame.pop(2);
+ break;
+
+ case OP_GEQ:
+ stackFrame.opers[-3] = (cmpHelper(stackFrame) >= 0) ? GOB_TRUE : GOB_FALSE;
+ stackFrame.pop(2);
+ break;
+
+ case OP_EQ:
+ stackFrame.opers[-3] = (cmpHelper(stackFrame) == 0) ? GOB_TRUE : GOB_FALSE;
+ stackFrame.pop(2);
+ break;
+
+ case OP_NEQ:
+ stackFrame.opers[-3] = (cmpHelper(stackFrame) != 0) ? GOB_TRUE : GOB_FALSE;
+ stackFrame.pop(2);
+ break;
+
+ default:
+ return true;
+ }
+
+ return false;
+}
+
+// Assign the result to the appropriate _result variable
+void Expression::getResult(byte operation, int32 value, byte *type) {
+ if (type != 0)
+ *type = operation;
+
+ switch (operation) {
+ case OP_NOT:
+ if (type != 0)
+ *type ^= 1;
+ break;
+
+ case OP_LOAD_IMM_INT16:
+ _resultInt = value;
+ break;
+
+ case OP_LOAD_IMM_STR:
+ if ((char *) decodePtr(value) != _resultStr)
+ strcpy(_resultStr, (char *) decodePtr(value));
+ break;
+
+ case OP_LOAD_VAR_INT32:
+ case OP_LOAD_VAR_INT32_AS_INT16:
+ break;
+
+ default:
+ _resultInt = 0;
+ if (type != 0)
+ *type = OP_LOAD_IMM_INT16;
+ break;
+ }
+}
+
+int16 Expression::parseExpr(byte stopToken, byte *type) {
+ Stack stack;
+ StackFrame stackFrame(stack);
+ byte operation;
+ bool escape;
+ int16 brackStart;
+ uint32 varBase;
+
+ while (true) {
+ getVarBase(varBase);
+
+ stackFrame.push();
+
+ operation = _vm->_game->_script->readByte();
+ if ((operation >= OP_ARRAY_INT8) && (operation <= OP_FUNC)) {
+
+ loadValue(operation, varBase, stackFrame);
+
+ if ((stackFrame.pos > 0) && ((stackFrame.opers[-1] == OP_NEG) || (stackFrame.opers[-1] == OP_NOT))) {
+ stackFrame.pop();
+
+ if (*stackFrame.opers == OP_NEG) {
+ *stackFrame.opers = OP_LOAD_IMM_INT16;
+ stackFrame.values[0] = -stackFrame.values[1];
+ } else
+ *stackFrame.opers = (stackFrame.opers[1] == GOB_FALSE) ? GOB_TRUE : GOB_FALSE;
+ }
+
+ if (stackFrame.pos <= 0)
+ continue;
+
+ simpleArithmetic1(stackFrame);
+
+ continue;
+ } // (op >= OP_ARRAY_INT8) && (op <= OP_FUNC)
+
+ if ((operation == stopToken) || (operation == OP_OR) ||
+ (operation == OP_AND) || (operation == OP_END_EXPR)) {
+ while (stackFrame.pos >= 2) {
+ escape = false;
+ if ((stackFrame.opers[-2] == OP_BEGIN_EXPR) &&
+ ((operation == OP_END_EXPR) || (operation == stopToken))) {
+ stackFrame.opers[-2] = stackFrame.opers[-1];
+ if ((stackFrame.opers[-2] == OP_LOAD_IMM_INT16) || (stackFrame.opers[-2] == OP_LOAD_IMM_STR))
+ stackFrame.values[-2] = stackFrame.values[-1];
+
+ stackFrame.pop();
+
+ simpleArithmetic2(stackFrame);
+
+ if (operation != stopToken)
+ break;
+ } // if ((stackFrame.opers[-2] == OP_BEGIN_EXPR) && ...)
+
+ for (brackStart = (stackFrame.pos - 2); (brackStart > 0) &&
+ (stack.opers[brackStart] < OP_OR) && (stack.opers[brackStart] != OP_BEGIN_EXPR);
+ brackStart--)
+ ;
+
+ if ((stack.opers[brackStart] >= OP_OR) || (stack.opers[brackStart] == OP_BEGIN_EXPR))
+ brackStart++;
+
+ if (complexArithmetic(stack, stackFrame, brackStart))
+ break;
+
+ } // while (stackFrame.pos >= 2)
+
+ if ((operation == OP_OR) || (operation == OP_AND)) {
+ if (stackFrame.opers[-1] == OP_LOAD_IMM_INT16) {
+ if (stackFrame.values[-1] != 0)
+ stackFrame.opers[-1] = GOB_TRUE;
+ else
+ stackFrame.opers[-1] = GOB_FALSE;
+ }
+
+ if (((operation == OP_OR) && (stackFrame.opers[-1] == GOB_TRUE)) ||
+ ((operation == OP_AND) && (stackFrame.opers[-1] == GOB_FALSE))) {
+ if ((stackFrame.pos > 1) && (stackFrame.opers[-2] == OP_BEGIN_EXPR)) {
+ skipExpr(OP_END_EXPR);
+ stackFrame.opers[-2] = stackFrame.opers[-1];
+ stackFrame.pop(2);
+ } else {
+ skipExpr(stopToken);
+ }
+ operation = _vm->_game->_script->peekByte(-1);
+ if ((stackFrame.pos > 0) && (stackFrame.opers[-1] == OP_NOT)) {
+ if (stackFrame.opers[0] == GOB_FALSE)
+ stackFrame.opers[-1] = GOB_TRUE;
+ else
+ stackFrame.opers[-1] = GOB_FALSE;
+
+ stackFrame.pop();
+ }
+ } else
+ stackFrame.opers[0] = operation;
+ } else
+ stackFrame.pop();
+
+ if (operation != stopToken)
+ continue;
+
+ getResult(stack.opers[0], stack.values[0], type);
+
+ return 0;
+ } // (operation == stopToken) || (operation == OP_OR) || (operation == OP_AND) || (operation == OP_END_EXPR)
+
+ if ((operation < OP_NEG) || (operation > OP_NOT)) {
+ if ((operation < OP_LESS) || (operation > OP_NEQ))
+ continue;
+
+ if (stackFrame.pos > 2) {
+ if (stackFrame.opers[-2] == OP_ADD) {
+ if (stackFrame.opers[-3] == OP_LOAD_IMM_INT16) {
+ stackFrame.values[-3] += stackFrame.values[-1];
+ } else if (stackFrame.opers[-3] == OP_LOAD_IMM_STR) {
+ if ((char *) decodePtr(stackFrame.values[-3]) != _resultStr) {
+ strcpy(_resultStr, (char *) decodePtr(stackFrame.values[-3]));
+ stackFrame.values[-3] = encodePtr((byte *) _resultStr, kResStr);
+ }
+ strcat(_resultStr, (char *) decodePtr(stackFrame.values[-1]));
+ }
+ stackFrame.pop(2);
+
+ } else if (stackFrame.opers[-2] == OP_SUB) {
+ stackFrame.values[-3] -= stackFrame.values[-1];
+ stackFrame.pop(2);
+ } else if (stackFrame.opers[-2] == OP_BITOR) {
+ stackFrame.values[-3] |= stackFrame.values[-1];
+ stackFrame.pop(2);
+ }
+ }
+ }
+ *stackFrame.opers = operation;
+ }
+}
+
+int32 Expression::getResultInt() {
+ return _resultInt;
+}
+
+char *Expression::getResultStr() {
+ return _resultStr;
+}
+
+} // End of namespace Gob