2023-07-14 20:48:13 +02:00

373 lines
7.8 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* 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 {
static const size_t EXPR_LENGTH_SIZE =
#ifndef NO_CXX11_ALIGNAS
alignof(max_align_t)
#else
sizeof(byte)
#endif
;
/**
* Duplicate a mathematical expression.
*
* @param h Handle to the original expression
* @retruns Pointer to the cloned expression
*/
static void *duplicateExpression(MpalHandle h) {
byte *orig, *clone;
orig = (byte *)globalLock(h);
int num = *orig;
LpExpression one = (LpExpression)(orig + EXPR_LENGTH_SIZE);
clone = (byte *)globalAlloc(GMEM_FIXED, sizeof(Expression) * num + EXPR_LENGTH_SIZE);
LpExpression two = (LpExpression)(clone + EXPR_LENGTH_SIZE);
memcpy(clone, orig, sizeof(Expression) * num + EXPR_LENGTH_SIZE);
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 int32 Compute(int32 a, int32 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 int32 evaluateAndFreeExpression(void *expr) {
int num = *(byte *)expr;
LpExpression one = (LpExpression)((byte *)expr + EXPR_LENGTH_SIZE);
// 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);
int32 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, const Common::UnalignedPtr<MpalHandle> &h) {
byte *start;
byte num = *lpBuf;
lpBuf++;
if (num == 0)
return NULL;
h.store(globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, num * sizeof(Expression) + EXPR_LENGTH_SIZE));
if (h.load() == NULL)
return NULL;
start = (byte *)globalLock(h.load());
*start = num;
LpExpression cur = (LpExpression)(start + EXPR_LENGTH_SIZE);
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:
// As name is a byte, there is no alignment rule
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 mathematical expression
*
* @param h Handle to the expression
* @returns Numeric value
*/
int32 evaluateExpression(MpalHandle h) {
lockVar();
int32 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 = *e1;
int num2 = *e2;
if (num1 != num2) {
globalUnlock(h1);
globalUnlock(h2);
return false;
}
LpExpression one = (LpExpression)(e1 + EXPR_LENGTH_SIZE);
LpExpression two = (LpExpression)(e2 + EXPR_LENGTH_SIZE);
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 + EXPR_LENGTH_SIZE);
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