/* 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. * */ /* * This code is based on original Tony Tough source code * * Copyright (c) 1997-2003 Nayma Software */ #include "tony/mpal/mpal.h" #include "tony/mpal/memory.h" #include "tony/mpal/mpaldll.h" #include "tony/tony.h" namespace Tony { namespace MPAL { /** * Duplicate a mathematical expression. * * @param h Handle to the original expression * @retruns Pointer to the cloned expression */ static byte *duplicateExpression(MpalHandle h) { byte *orig, *clone; orig = (byte *)globalLock(h); int num = *(byte *)orig; LpExpression one = (LpExpression)(orig+1); clone = (byte *)globalAlloc(GMEM_FIXED, sizeof(Expression) * num + 1); LpExpression two = (LpExpression)(clone + 1); memcpy(clone, orig, sizeof(Expression) * num + 1); for (int i = 0; i < num; i++) { if (one->_type == ELT_PARENTH) { two->_type = ELT_PARENTH2; two->_val._pson = duplicateExpression(two->_val._son); } ++one; ++two; } globalUnlock(h); return clone; } static int Compute(int a, int b, byte symbol) { switch (symbol) { case OP_MUL: return a * b; case OP_DIV: return a / b; case OP_MODULE: return a % b; case OP_ADD: return a + b; case OP_SUB: return a - b; case OP_SHL: return a << b; case OP_SHR: return a >> b; case OP_MINOR: return a < b; case OP_MAJOR: return a > b; case OP_MINEQ: return a <= b; case OP_MAJEQ: return a >= b; case OP_EQUAL: return a == b; case OP_NOEQUAL: return a != b; case OP_BITAND: return a & b; case OP_BITXOR: return a ^ b; case OP_BITOR: return a | b; case OP_AND: return a && b; case OP_OR: return a || b; default: GLOBALS._mpalError = 1; break; } return 0; } static void solve(LpExpression one, int num) { LpExpression two, three; while (num > 1) { two = one + 1; if ((two->_symbol == 0) || (one->_symbol & 0xF0) <= (two->_symbol & 0xF0)) { two->_val._num = Compute(one->_val._num, two->_val._num, one->_symbol); memmove(one, two, (num - 1) * sizeof(Expression)); --num; } else { int j = 1; three = two + 1; while ((three->_symbol != 0) && (two->_symbol & 0xF0) > (three->_symbol & 0xF0)) { ++two; ++three; ++j; } three->_val._num = Compute(two->_val._num, three->_val._num, two->_symbol); memmove(two, three, (num - j - 1) * sizeof(Expression)); --num; } } } /** * Calculates the result of a mathematical expression, replacing the current * value of any variable. * * @param expr Pointer to an expression duplicated by DuplicateExpression * @returns Value */ static int evaluateAndFreeExpression(byte *expr) { int num = *expr; LpExpression one = (LpExpression)(expr + 1); // 1) Substitutions of variables LpExpression cur = one; for (int i = 0; i < num; i++, cur++) { if (cur->_type == ELT_VAR) { cur->_type = ELT_NUMBER; cur->_val._num = varGetValue(cur->_val._name); } } // 2) Replacement of brackets (using recursive calls) cur = one; for (int i = 0; i < num; i++, cur++) { if (cur->_type == ELT_PARENTH2) { cur->_type = ELT_NUMBER; cur->_val._num = evaluateAndFreeExpression(cur->_val._pson); } } // 3) algebraic resolution solve(one, num); int val = one->_val._num; globalDestroy(expr); return val; } /** * Parses a mathematical expression from the MPC file * * @param buf Buffer containing the expression to evaluate * @param h Pointer to a handle that, at the end of execution, * will point to the area of memory containing the parsed expression * @returns Pointer to the buffer immediately after the expression, or NULL if error. */ const byte *parseExpression(const byte *lpBuf, MpalHandle *h) { byte *start; uint32 num = *lpBuf; lpBuf++; if (num == 0) return NULL; *h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, num * sizeof(Expression) + 1); if (*h == NULL) return NULL; start = (byte *)globalLock(*h); *start = (byte)num; LpExpression cur = (LpExpression)(start + 1); for (uint32 i = 0;i < num; i++) { cur->_type = *(lpBuf); // *(lpBuf + 1) contains the unary operator, unused => skipped lpBuf += 2; switch (cur->_type) { case ELT_NUMBER: cur->_val._num = (int32)READ_LE_UINT32(lpBuf); lpBuf += 4; break; case ELT_VAR: cur->_val._name = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, (*lpBuf) + 1); if (cur->_val._name == NULL) return NULL; memcpy(cur->_val._name, lpBuf + 1, *lpBuf); lpBuf += *lpBuf + 1; break; case ELT_PARENTH: lpBuf = parseExpression(lpBuf, &cur->_val._son); if (lpBuf == NULL) return NULL; break; default: return NULL; } cur->_symbol = *lpBuf; lpBuf++; cur++; } if (*lpBuf != 0) return NULL; lpBuf++; return lpBuf; } /** * Calculate the value of a mathamatical expression * * @param h Handle to the expression * @returns Numeric value */ int evaluateExpression(MpalHandle h) { lockVar(); int ret = evaluateAndFreeExpression(duplicateExpression(h)); unlockVar(); return ret; } /** * Compare two mathematical expressions together * * @param h1 Expression to be compared * @param h2 Expression to be compared */ bool compareExpressions(MpalHandle h1, MpalHandle h2) { byte *e1, *e2; e1 = (byte *)globalLock(h1); e2 = (byte *)globalLock(h2); int num1 = *(byte *)e1; int num2 = *(byte *)e2; if (num1 != num2) { globalUnlock(h1); globalUnlock(h2); return false; } LpExpression one = (LpExpression)(e1 + 1); LpExpression two = (LpExpression)(e2 + 1); for (int i = 0; i < num1; i++) { if (one->_type != two->_type || (i != num1 - 1 && one->_symbol != two->_symbol)) { globalUnlock(h1); globalUnlock(h2); return false; } switch (one->_type) { case ELT_NUMBER: if (one->_val._num != two->_val._num) { globalUnlock(h1); globalUnlock(h2); return false; } break; case ELT_VAR: if (strcmp(one->_val._name, two->_val._name) != 0) { globalUnlock(h1); globalUnlock(h2); return false; } break; case ELT_PARENTH: if (!compareExpressions(one->_val._son, two->_val._son)) { globalUnlock(h1); globalUnlock(h2); return false; } break; default: break; } ++one; ++two; } globalUnlock(h1); globalUnlock(h2); return true; } /** * Frees an expression that was previously parsed * * @param h Handle for the expression */ void freeExpression(MpalHandle h) { byte *data = (byte *)globalLock(h); int num = *data; LpExpression cur = (LpExpression)(data + 1); for (int i = 0; i < num; ++i, ++cur) { switch (cur->_type) { case ELT_VAR: globalDestroy(cur->_val._name); break; case ELT_PARENTH: freeExpression(cur->_val._son); break; default: break; } } globalUnlock(h); globalFree(h); } } // end of namespace MPAL } // end of namespace Tony