aboutsummaryrefslogtreecommitdiff
path: root/engines/tony/mpal
diff options
context:
space:
mode:
authorD G Turner2012-10-12 17:03:32 +0100
committerD G Turner2012-10-12 17:03:32 +0100
commit151b7beb47ec4b964862d6779bd48e3a33482bbd (patch)
tree867717c5266d0908d95edd82560599be20a4ede9 /engines/tony/mpal
parent80af0e239473f85c49cc2da3c848dfcde41d4a37 (diff)
parent2b55837650c4229dc3d75b660cecfc7a3292e5e0 (diff)
downloadscummvm-rg350-151b7beb47ec4b964862d6779bd48e3a33482bbd.tar.gz
scummvm-rg350-151b7beb47ec4b964862d6779bd48e3a33482bbd.tar.bz2
scummvm-rg350-151b7beb47ec4b964862d6779bd48e3a33482bbd.zip
Merge branch 'master' into teenagentRefactor
Conflicts: engines/teenagent/callbacks.cpp
Diffstat (limited to 'engines/tony/mpal')
-rw-r--r--engines/tony/mpal/expr.cpp365
-rw-r--r--engines/tony/mpal/expr.h140
-rw-r--r--engines/tony/mpal/loadmpc.cpp788
-rw-r--r--engines/tony/mpal/loadmpc.h59
-rw-r--r--engines/tony/mpal/lzo.cpp511
-rw-r--r--engines/tony/mpal/lzo.h111
-rw-r--r--engines/tony/mpal/memory.cpp127
-rw-r--r--engines/tony/mpal/memory.h78
-rw-r--r--engines/tony/mpal/mpal.cpp2089
-rw-r--r--engines/tony/mpal/mpal.h518
-rw-r--r--engines/tony/mpal/mpaldll.h251
-rw-r--r--engines/tony/mpal/mpalutils.cpp115
-rw-r--r--engines/tony/mpal/mpalutils.h74
13 files changed, 5226 insertions, 0 deletions
diff --git a/engines/tony/mpal/expr.cpp b/engines/tony/mpal/expr.cpp
new file mode 100644
index 0000000000..824cd91651
--- /dev/null
+++ b/engines/tony/mpal/expr.cpp
@@ -0,0 +1,365 @@
+/* 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;
+ }
+
+ ++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
diff --git a/engines/tony/mpal/expr.h b/engines/tony/mpal/expr.h
new file mode 100644
index 0000000000..405624b4fe
--- /dev/null
+++ b/engines/tony/mpal/expr.h
@@ -0,0 +1,140 @@
+/* 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
+ */
+
+#ifndef MPAL_EXPR_H
+#define MPAL_EXPR_H
+
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/**
+ * @defgroup Mathamatical operations
+ */
+//@{
+
+#define OP_MUL ((1 << 4) | 0)
+#define OP_DIV ((1 << 4) | 1)
+#define OP_MODULE ((1 << 4) | 2)
+#define OP_ADD ((2 << 4) | 0)
+#define OP_SUB ((2 << 4) | 1)
+#define OP_SHL ((3 << 4) | 0)
+#define OP_SHR ((3 << 4) | 1)
+#define OP_MINOR ((4 << 4) | 0)
+#define OP_MAJOR ((4 << 4) | 1)
+#define OP_MINEQ ((4 << 4) | 2)
+#define OP_MAJEQ ((4 << 4) | 3)
+#define OP_EQUAL ((5 << 4) | 0)
+#define OP_NOEQUAL ((5 << 4) | 1)
+#define OP_BITAND ((6 << 4) | 0)
+#define OP_BITXOR ((7 << 4) | 0)
+#define OP_BITOR ((8 << 4) | 0)
+#define OP_AND ((9 << 4) | 0)
+#define OP_OR ((10 << 4) | 0)
+
+//@}
+
+/**
+ * @defgroup Structures
+ */
+
+//@{
+/**
+ * Mathamatical framework to manage operations
+ */
+typedef struct {
+ byte _type; // Object Type (see enum ExprListTypes)
+
+ union {
+ int _num; // Identifier (if type == ELT_NUMBER)
+ char *_name; // Variable name (if type == ELT_VAR)
+ MpalHandle _son; // Handle expressions (if type == ELT_PARENTH)
+ byte *_pson; // Handle lockato (if type == ELT_PARENTH2)
+ } _val;
+
+ byte _symbol; // Mathematic symbols (see #define OP_*)
+
+} Expression;
+typedef Expression *LpExpression;
+
+//@}
+
+/**
+ * Object types that can be contained in an EXPRESSION structure
+ */
+enum ExprListTypes {
+ ELT_NUMBER = 1,
+ ELT_VAR = 2,
+ ELT_PARENTH = 3,
+ ELT_PARENTH2 = 4
+};
+
+/****************************************************************************\
+* Function Prototypes
+\****************************************************************************/
+
+/**
+ * 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);
+
+/**
+ * Calculate the value of a mathamatical expression
+ *
+ * @param h Handle to the expression
+ * @returns Numeric value
+ */
+int evaluateExpression(MpalHandle h);
+
+/**
+ * Compare two mathematical expressions together
+ *
+ * @param h1 Expression to be compared
+ * @param h2 Expression to be compared
+ */
+bool compareExpressions(MpalHandle h1, MpalHandle h2);
+
+/**
+ * Frees an expression that was previously parsed
+ *
+ * @param h Handle for the expression
+ */
+void freeExpression(MpalHandle h);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/mpal/loadmpc.cpp b/engines/tony/mpal/loadmpc.cpp
new file mode 100644
index 0000000000..9c45cdf982
--- /dev/null
+++ b/engines/tony/mpal/loadmpc.cpp
@@ -0,0 +1,788 @@
+/* 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 "mpal.h"
+#include "mpaldll.h"
+#include "memory.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Static functions
+\****************************************************************************/
+
+static bool compareCommands(struct Command *cmd1, struct Command *cmd2) {
+ if (cmd1->_type == 2 && cmd2->_type == 2) {
+ if (strcmp(cmd1->_lpszVarName, cmd2->_lpszVarName) == 0 &&
+ compareExpressions(cmd1->_expr, cmd2->_expr))
+ return true;
+ else
+ return false;
+ } else
+ return (memcmp(cmd1, cmd2, sizeof(struct Command)) == 0);
+}
+
+/**
+ * Parses a script from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled script.
+ * @param lpmsScript Pointer to a structure that will be filled with the
+ * data of the script.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ */
+static const byte *ParseScript(const byte *lpBuf, LpMpalScript lpmsScript) {
+ lpmsScript->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ lpmsScript->_nMoments = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ int curCmd = 0;
+
+ for (uint i = 0; i < lpmsScript->_nMoments; i++) {
+ lpmsScript->_moment[i]._dwTime = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_moment[i]._nCmds = *lpBuf;
+ lpBuf++;
+
+ for (int j = 0; j < lpmsScript->_moment[i]._nCmds; j++) {
+ lpmsScript->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+ switch (lpmsScript->_command[curCmd]._type) {
+ case 1:
+ lpmsScript->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmsScript->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmsScript->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case 2: { // Variable assign
+ int len = *lpBuf;
+ lpBuf++;
+ lpmsScript->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmsScript->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+ memcpy(lpmsScript->_command[curCmd]._lpszVarName, lpBuf, len);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmsScript->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+ }
+ default:
+ return NULL;
+ }
+
+ lpmsScript->_moment[i]._cmdNum[j] = curCmd;
+ curCmd++;
+ }
+ }
+ return lpBuf;
+}
+
+/**
+ * Frees a script allocated via a previous call to ParseScript
+ *
+ * @param lpmsScript Pointer to a script structure
+ */
+static void FreeScript(LpMpalScript lpmsScript) {
+ for (int i = 0; i < MAX_COMMANDS_PER_SCRIPT && (lpmsScript->_command[i]._type); ++i, ++lpmsScript) {
+ if (lpmsScript->_command[i]._type == 2) {
+ // Variable Assign
+ globalDestroy(lpmsScript->_command[i]._lpszVarName);
+ freeExpression(lpmsScript->_command[i]._expr);
+ }
+ }
+}
+
+/**
+ * Parses a dialog from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled dialog.
+ * @param lpmdDialog Pointer to a structure that will be filled with the
+ * data of the dialog.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ */
+static const byte *parseDialog(const byte *lpBuf, LpMpalDialog lpmdDialog) {
+ byte *lpLock;
+
+ lpmdDialog->_nObj = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ // Periods
+ uint32 num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (num >= MAX_PERIODS_PER_DIALOG - 1)
+ error("Too much periods in dialog #%d", lpmdDialog->_nObj);
+
+ uint32 i;
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_periodNums[i] = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_periods[i] = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, *lpBuf + 1);
+ lpLock = (byte *)globalLock(lpmdDialog->_periods[i]);
+ Common::copy(lpBuf + 1, lpBuf + 1 + *lpBuf, lpLock);
+ globalUnlock(lpmdDialog->_periods[i]);
+ lpBuf += (*lpBuf) + 1;
+ }
+
+ lpmdDialog->_periodNums[i] = 0;
+ lpmdDialog->_periods[i] = NULL;
+
+ // Groups
+ num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ uint32 curCmd = 0;
+
+ if (num >= MAX_GROUPS_PER_DIALOG)
+ error("Too much groups in dialog #%d", lpmdDialog->_nObj);
+
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_group[i]._num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_group[i]._nCmds = *lpBuf; lpBuf++;
+
+ if (lpmdDialog->_group[i]._nCmds >= MAX_COMMANDS_PER_GROUP)
+ error("Too much commands in group #%d in dialog #%d", lpmdDialog->_group[i]._num, lpmdDialog->_nObj);
+
+ for (uint32 j = 0; j < lpmdDialog->_group[i]._nCmds; j++) {
+ lpmdDialog->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+
+ switch (lpmdDialog->_command[curCmd]._type) {
+ // Call custom function
+ case 1:
+ lpmdDialog->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmdDialog->_command[curCmd]._arg1 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg2 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg3 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmdDialog->_command[curCmd]._arg4 = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ // Variable assign
+ case 2: {
+ uint32 len = *lpBuf;
+ lpBuf++;
+ lpmdDialog->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmdDialog->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+
+ Common::copy(lpBuf, lpBuf + len, lpmdDialog->_command[curCmd]._lpszVarName);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmdDialog->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+ }
+ // Do Choice
+ case 3:
+ lpmdDialog->_command[curCmd]._nChoice = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ uint32 kk;
+ for (kk = 0;kk < curCmd; kk++) {
+ if (compareCommands(&lpmdDialog->_command[kk], &lpmdDialog->_command[curCmd])) {
+ lpmdDialog->_group[i]._cmdNum[j] = kk;
+
+ // Free any data allocated for the duplictaed command
+ if (lpmdDialog->_command[curCmd]._type == 2) {
+ globalDestroy(lpmdDialog->_command[curCmd]._lpszVarName);
+ freeExpression(lpmdDialog->_command[curCmd]._expr);
+
+ lpmdDialog->_command[curCmd]._lpszVarName = NULL;
+ lpmdDialog->_command[curCmd]._expr = 0;
+ lpmdDialog->_command[curCmd]._type = 0;
+ }
+ break;
+ }
+ }
+
+ if (kk == curCmd) {
+ lpmdDialog->_group[i]._cmdNum[j] = curCmd;
+ curCmd++;
+ }
+ }
+ }
+
+ if (curCmd >= MAX_COMMANDS_PER_DIALOG)
+ error("Too much commands in dialog #%d", lpmdDialog->_nObj);
+
+ // Choices
+ num = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (num >= MAX_CHOICES_PER_DIALOG)
+ error("Too much choices in dialog #%d", lpmdDialog->_nObj);
+
+ for (i = 0; i < num; i++) {
+ lpmdDialog->_choice[i]._nChoice = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ uint32 num2 = *lpBuf++;
+
+ if (num2 >= MAX_SELECTS_PER_CHOICE)
+ error("Too much selects in choice #%d in dialog #%d", lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
+
+ for (uint32 j = 0; j < num2; j++) {
+ // When
+ switch (*lpBuf++) {
+ case 0:
+ lpmdDialog->_choice[i]._select[j]._when = NULL;
+ break;
+
+ case 1:
+ lpBuf = parseExpression(lpBuf, &lpmdDialog->_choice[i]._select[j]._when);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ case 2:
+ return NULL;
+ }
+
+ // Attrib
+ lpmdDialog->_choice[i]._select[j]._attr = *lpBuf++;
+
+ // Data
+ lpmdDialog->_choice[i]._select[j]._dwData = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ // PlayGroup
+ uint32 num3 = *lpBuf++;
+
+ if (num3 >= MAX_PLAYGROUPS_PER_SELECT)
+ error("Too much playgroups in select #%d in choice #%d in dialog #%d", j, lpmdDialog->_choice[i]._nChoice, lpmdDialog->_nObj);
+
+ for (uint32 z = 0; z < num3; z++) {
+ lpmdDialog->_choice[i]._select[j]._wPlayGroup[z] = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ }
+
+ lpmdDialog->_choice[i]._select[j]._wPlayGroup[num3] = 0;
+ }
+
+ // Mark the last selection
+ lpmdDialog->_choice[i]._select[num2]._dwData = 0;
+ }
+
+ lpmdDialog->_choice[num]._nChoice = 0;
+
+ return lpBuf;
+}
+
+
+/**
+ * Parses an item from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled dialog.
+ * @param lpmiItem Pointer to a structure that will be filled with the
+ * data of the item.
+ * @returns Pointer to the buffer after the item, or NULL on failure.
+ * @remarks It's necessary that the structure that is passed has been
+ * completely initialized to 0 beforehand.
+ */
+static const byte *parseItem(const byte *lpBuf, LpMpalItem lpmiItem) {
+ lpmiItem->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ byte len = *lpBuf;
+ lpBuf++;
+ memcpy(lpmiItem->_lpszDescribe, lpBuf, MIN((byte)127, len));
+ lpBuf += len;
+
+ if (len >= MAX_DESCRIBE_SIZE)
+ error("Describe too long in item #%d", lpmiItem->_nObj);
+
+ lpmiItem->_nActions=*lpBuf;
+ lpBuf++;
+
+ // Allocation action
+ if (lpmiItem->_nActions > 0)
+ lpmiItem->_action = (ItemAction *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(struct ItemAction) * (int)lpmiItem->_nActions);
+
+ uint32 curCmd = 0;
+
+ for (uint32 i = 0; i < lpmiItem->_nActions; i++) {
+ lpmiItem->_action[i]._num = *lpBuf;
+ lpBuf++;
+
+ lpmiItem->_action[i]._wParm = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ if (lpmiItem->_action[i]._num == 0xFF) {
+ lpmiItem->_action[i]._wTime = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ lpmiItem->_action[i]._perc = *lpBuf;
+ lpBuf++;
+ }
+
+ if (*lpBuf == 0) {
+ lpBuf++;
+ lpmiItem->_action[i]._when = NULL;
+ } else {
+ lpBuf++;
+ lpBuf = parseExpression(lpBuf,&lpmiItem->_action[i]._when);
+ if (lpBuf == NULL)
+ return NULL;
+ }
+
+ lpmiItem->_action[i]._nCmds=*lpBuf;
+ lpBuf++;
+
+ if (lpmiItem->_action[i]._nCmds >= MAX_COMMANDS_PER_ACTION)
+ error("Too much commands in action #%d in item #%d", lpmiItem->_action[i]._num, lpmiItem->_nObj);
+
+ for (uint32 j = 0; j < lpmiItem->_action[i]._nCmds; j++) {
+ lpmiItem->_command[curCmd]._type = *lpBuf;
+ lpBuf++;
+ switch (lpmiItem->_command[curCmd]._type) {
+ case 1: // Call custom function
+ lpmiItem->_command[curCmd]._nCf = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmiItem->_command[curCmd]._arg1 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg2 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg3 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmiItem->_command[curCmd]._arg4 = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ break;
+
+ case 2: // Variable assign
+ len = *lpBuf;
+ lpBuf++;
+ lpmiItem->_command[curCmd]._lpszVarName = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, len + 1);
+ if (lpmiItem->_command[curCmd]._lpszVarName == NULL)
+ return NULL;
+ memcpy(lpmiItem->_command[curCmd]._lpszVarName, lpBuf, len);
+ lpBuf += len;
+
+ lpBuf = parseExpression(lpBuf, &lpmiItem->_command[curCmd]._expr);
+ if (lpBuf == NULL)
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ uint32 kk;
+ for (kk = 0; kk < curCmd; kk++) {
+ if (compareCommands(&lpmiItem->_command[kk], &lpmiItem->_command[curCmd])) {
+ lpmiItem->_action[i]._cmdNum[j] = kk;
+
+ // Free any data allocated for the duplictaed command
+ if (lpmiItem->_command[curCmd]._type == 2) {
+ globalDestroy(lpmiItem->_command[curCmd]._lpszVarName);
+ freeExpression(lpmiItem->_command[curCmd]._expr);
+
+ lpmiItem->_command[curCmd]._lpszVarName = NULL;
+ lpmiItem->_command[curCmd]._expr = 0;
+ lpmiItem->_command[curCmd]._type = 0;
+ }
+ break;
+ }
+ }
+
+ if (kk == curCmd) {
+ lpmiItem->_action[i]._cmdNum[j] = curCmd;
+ curCmd++;
+
+ if (curCmd >= MAX_COMMANDS_PER_ITEM) {
+ error("Too much commands in item #%d", lpmiItem->_nObj);
+ //curCmd=0;
+ }
+ }
+ }
+ }
+
+ lpmiItem->_dwRes = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ return lpBuf;
+}
+
+/**
+ * Frees an item parsed from a prior call to ParseItem
+ *
+ * @param lpmiItem Pointer to an item structure
+ */
+static void freeItem(LpMpalItem lpmiItem) {
+ // Free the actions
+ if (lpmiItem->_action) {
+ for (int i = 0; i < lpmiItem->_nActions; ++i) {
+ if (lpmiItem->_action[i]._when != 0)
+ freeExpression(lpmiItem->_action[i]._when);
+ }
+
+ globalDestroy(lpmiItem->_action);
+ }
+
+ // Free the commands
+ for (int i = 0; i < MAX_COMMANDS_PER_ITEM && (lpmiItem->_command[i]._type); ++i) {
+ if (lpmiItem->_command[i]._type == 2) {
+ // Variable Assign
+ globalDestroy(lpmiItem->_command[i]._lpszVarName);
+ freeExpression(lpmiItem->_command[i]._expr);
+ }
+ }
+}
+
+/**
+ * Parses a location from the MPC file, and inserts its data into a structure
+ *
+ * @param lpBuf Buffer containing the compiled location.
+ * @param lpmiLocation Pointer to a structure that will be filled with the
+ * data of the location.
+ * @returns Pointer to the buffer after the location, or NULL on failure.
+ */
+static const byte *ParseLocation(const byte *lpBuf, LpMpalLocation lpmlLocation) {
+ lpmlLocation->_nObj = (int32)READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+ lpmlLocation->_dwXlen = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmlLocation->_dwYlen = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+ lpmlLocation->_dwPicRes = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ return lpBuf;
+}
+
+
+/****************************************************************************\
+* Exported functions
+\****************************************************************************/
+/**
+ * @defgroup Exported functions
+ */
+//@{
+
+/**
+ * Reads and interprets the MPC file, and create structures for various directives
+ * in the global variables
+ *
+ * @param lpBuf Buffer containing the MPC file data, excluding the header.
+ * @returns True if succeeded OK, false if failure.
+ */
+bool parseMpc(const byte *lpBuf) {
+ byte *lpTemp;
+
+ // 1. Variables
+ if (lpBuf[0] != 'V' || lpBuf[1] != 'A' || lpBuf[2] != 'R' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nVars = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ GLOBALS._hVars = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalVar) * (uint32)GLOBALS._nVars);
+ if (GLOBALS._hVars == NULL)
+ return false;
+
+ GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars);
+
+ for (uint16 i = 0; i < GLOBALS._nVars; i++) {
+ uint16 wLen = *(const byte *)lpBuf;
+ lpBuf++;
+ memcpy(GLOBALS._lpmvVars->_lpszVarName, lpBuf, MIN(wLen, (uint16)32));
+ lpBuf += wLen;
+ GLOBALS._lpmvVars->_dwVal = READ_LE_UINT32(lpBuf);
+ lpBuf += 4;
+
+ lpBuf++; // Skip 'ext'
+ GLOBALS._lpmvVars++;
+ }
+
+ globalUnlock(GLOBALS._hVars);
+
+ // 2. Messages
+ if (lpBuf[0] != 'M' || lpBuf[1] != 'S' || lpBuf[2] != 'G' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nMsgs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+#ifdef NEED_LOCK_MSGS
+ GLOBALS._hMsgs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(MpalMsg) * (uint32)GLOBALS._nMsgs);
+ if (GLOBALS._hMsgs == NULL)
+ return false;
+
+ GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+#else
+ GLOBALS._lpmmMsgs=(LPMPALMSG)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MPALMSG) * (uint32)GLOBALS._nMsgs);
+ if (GLOBALS._lpmmMsgs==NULL)
+ return false;
+#endif
+
+ for (uint16 i = 0; i < GLOBALS._nMsgs; i++) {
+ GLOBALS._lpmmMsgs->_wNum = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ uint16 j;
+ for (j = 0; lpBuf[j] != 0;)
+ j += lpBuf[j] + 1;
+
+ GLOBALS._lpmmMsgs->_hText = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, j + 1);
+ lpTemp = (byte *)globalLock(GLOBALS._lpmmMsgs->_hText);
+
+ for (j = 0; lpBuf[j] != 0;) {
+ memcpy(lpTemp, &lpBuf[j + 1], lpBuf[j]);
+ lpTemp += lpBuf[j];
+ *lpTemp ++= '\0';
+ j += lpBuf[j] + 1;
+ }
+
+ lpBuf += j + 1;
+ *lpTemp = '\0';
+
+ globalUnlock(GLOBALS._lpmmMsgs->_hText);
+ GLOBALS._lpmmMsgs++;
+ }
+
+#ifdef NEED_LOCK_MSGS
+ globalUnlock(GLOBALS._hMsgs);
+#endif
+
+ // 3. Objects
+ if (lpBuf[0] != 'O' || lpBuf[1] != 'B' || lpBuf[2] != 'J' || lpBuf[3] != 'S')
+ return false;
+
+ lpBuf += 4;
+ GLOBALS._nObjs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Check out the dialogs
+ GLOBALS._nDialogs = 0;
+ GLOBALS._hDialogs = GLOBALS._lpmdDialogs = NULL;
+ if (*((const byte *)lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Dialog", 6) == 0) {
+ GLOBALS._nDialogs = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ GLOBALS._hDialogs = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nDialogs * sizeof(MpalDialog));
+ if (GLOBALS._hDialogs == NULL)
+ return false;
+
+ GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+
+ for (uint16 i = 0; i < GLOBALS._nDialogs; i++) {
+ if ((lpBuf = parseDialog(lpBuf + 7, &GLOBALS._lpmdDialogs[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hDialogs);
+ }
+
+ // Check the items
+ GLOBALS._nItems = 0;
+ GLOBALS._hItems = GLOBALS._lpmiItems = NULL;
+ if (*(lpBuf + 2) == 4 && strncmp((const char *)lpBuf + 3, "Item", 4) == 0) {
+ GLOBALS._nItems = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory and read them in
+ GLOBALS._hItems = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nItems * sizeof(MpalItem));
+ if (GLOBALS._hItems == NULL)
+ return false;
+
+ GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+
+ for (uint16 i = 0; i < GLOBALS._nItems; i++) {
+ if ((lpBuf = parseItem(lpBuf + 5, &GLOBALS._lpmiItems[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hItems);
+ }
+
+ // Check the locations
+ GLOBALS._nLocations = 0;
+ GLOBALS._hLocations = GLOBALS._lpmlLocations = NULL;
+ if (*(lpBuf + 2) == 8 && strncmp((const char *)lpBuf + 3, "Location", 8) == 0) {
+ GLOBALS._nLocations = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory and read them in
+ GLOBALS._hLocations = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nLocations * sizeof(MpalLocation));
+ if (GLOBALS._hLocations == NULL)
+ return false;
+
+ GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations);
+
+ for (uint16 i = 0; i < GLOBALS._nLocations; i++) {
+ if ((lpBuf = ParseLocation(lpBuf + 9, &GLOBALS._lpmlLocations[i])) == NULL)
+ return false;
+ }
+
+ globalUnlock(GLOBALS._hLocations);
+ }
+
+ // Check the scripts
+ GLOBALS._nScripts = 0;
+ GLOBALS._hScripts = GLOBALS._lpmsScripts = NULL;
+ if (*(lpBuf + 2) == 6 && strncmp((const char *)lpBuf + 3, "Script", 6) == 0) {
+ GLOBALS._nScripts = READ_LE_UINT16(lpBuf);
+ lpBuf += 2;
+
+ // Allocate memory
+ GLOBALS._hScripts = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, (uint32)GLOBALS._nScripts * sizeof(MpalScript));
+ if (GLOBALS._hScripts == NULL)
+ return false;
+
+ GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+
+ for (uint16 i = 0; i < GLOBALS._nScripts; i++) {
+ if ((lpBuf = ParseScript(lpBuf + 7, &GLOBALS._lpmsScripts[i])) == NULL)
+ return false;
+
+ // Sort the various moments of the script
+ //qsort(
+ //GLOBALS.lpmsScripts[i].Moment,
+ //GLOBALS.lpmsScripts[i].nMoments,
+ //sizeof(GLOBALS.lpmsScripts[i].Moment[0]),
+ //(int (*)(const void *, const void *))CompareMoments
+ //);
+ }
+
+ globalUnlock(GLOBALS._hScripts);
+ }
+
+ if (lpBuf[0] != 'E' || lpBuf[1] != 'N' || lpBuf[2] != 'D' || lpBuf[3] != '0')
+ return false;
+
+ return true;
+}
+
+/**
+ * Free the given dialog
+ */
+static void freeDialog(LpMpalDialog lpmdDialog) {
+ // Free the periods
+ for (int i = 0; i < MAX_PERIODS_PER_DIALOG && (lpmdDialog->_periods[i]); ++i)
+ globalFree(lpmdDialog->_periods[i]);
+
+ for (int i = 0; i < MAX_COMMANDS_PER_DIALOG && (lpmdDialog->_command[i]._type); i++) {
+ if (lpmdDialog->_command[i]._type == 2) {
+ // Variable assign
+ globalDestroy(lpmdDialog->_command[i]._lpszVarName);
+ freeExpression(lpmdDialog->_command[i]._expr);
+ }
+ }
+
+ // Free the choices
+ for (int i = 0; i < MAX_CHOICES_PER_DIALOG; ++i) {
+ for (int j = 0; j < MAX_SELECTS_PER_CHOICE; j++) {
+ if (lpmdDialog->_choice[i]._select[j]._when)
+ freeExpression(lpmdDialog->_choice[i]._select[j]._when);
+ }
+ }
+}
+
+/**
+ * Frees any data allocated from the parsing of the MPC file
+ */
+void freeMpc() {
+ // Free variables
+ globalFree(GLOBALS._hVars);
+
+ // Free messages
+ LpMpalMsg lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+ for (int i = 0; i < GLOBALS._nMsgs; i++, ++lpmmMsgs)
+ globalFree(lpmmMsgs->_hText);
+
+ globalUnlock(GLOBALS._hMsgs);
+ globalFree(GLOBALS._hMsgs);
+
+ // Free objects
+ if (GLOBALS._hDialogs) {
+ LpMpalDialog lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+
+ for (int i = 0; i < GLOBALS._nDialogs; i++, ++lpmdDialogs)
+ freeDialog(lpmdDialogs);
+
+ globalFree(GLOBALS._hDialogs);
+ }
+
+ // Free items
+ if (GLOBALS._hItems) {
+ LpMpalItem lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+
+ for (int i = 0; i < GLOBALS._nItems; ++i, ++lpmiItems)
+ freeItem(lpmiItems);
+
+ globalUnlock(GLOBALS._hItems);
+ globalFree(GLOBALS._hItems);
+ }
+
+ // Free the locations
+ if (GLOBALS._hLocations) {
+ globalFree(GLOBALS._hLocations);
+ }
+
+ // Free the scripts
+ if (GLOBALS._hScripts) {
+ LpMpalScript lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+
+ for (int i = 0; i < GLOBALS._nScripts; ++i, ++lpmsScripts) {
+ FreeScript(lpmsScripts);
+ }
+
+ globalUnlock(GLOBALS._hScripts);
+ }
+}
+
+//@}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/loadmpc.h b/engines/tony/mpal/loadmpc.h
new file mode 100644
index 0000000000..20956288aa
--- /dev/null
+++ b/engines/tony/mpal/loadmpc.h
@@ -0,0 +1,59 @@
+/* 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
+ */
+
+#ifndef __LOADMPC_H
+#define __LOADMPC_H
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Function prototypes
+\****************************************************************************/
+
+/**
+ * Reads and interprets the MPC file, and create structures for various directives
+ * in the global variables
+ *
+ * @param lpBuf Buffer containing the MPC file data, excluding the header.
+ * @returns True if succeeded OK, false if failure.
+ */
+bool parseMpc(const byte *lpBuf);
+
+/**
+ * Frees any data allocated from the parsing of the MPC file
+ */
+void freeMpc();
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/lzo.cpp b/engines/tony/mpal/lzo.cpp
new file mode 100644
index 0000000000..3d0751a5ca
--- /dev/null
+++ b/engines/tony/mpal/lzo.cpp
@@ -0,0 +1,511 @@
+/* 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.
+ *
+ *
+ */
+/* minilzo.c -- mini subset of the LZO real-time data compression library
+
+ This file is part of the LZO real-time data compression library.
+
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The LZO library 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.
+
+ The LZO library 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 the LZO library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+ http://www.oberhumer.com/opensource/lzo/
+ */
+
+#include "lzo.h"
+#include "common/textconsole.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+#define pd(a, b) ((uint32) ((a) - (b)))
+
+#define TEST_IP (ip < ip_end)
+#define TEST_OP 1
+#define NEED_IP(x) ((void) 0)
+#define NEED_OP(x) ((void) 0)
+#define TEST_LB(m_pos) ((void) 0)
+
+#define M2_MAX_OFFSET 0x0800
+#define LZO1X
+
+/**
+ * Decompresses an LZO compressed resource
+ */
+int lzo1x_decompress(const byte *in, uint32 in_len, byte *out, uint32 *out_len) {
+ register byte *op;
+ register const byte *ip;
+ register uint32 t = 0;
+#if defined(COPY_DICT)
+ uint32 m_off;
+ const byte *dict_end;
+#else
+ register const byte *m_pos;
+#endif
+
+ const byte * const ip_end = in + in_len;
+#if defined(HAVE_ANY_OP)
+ byte * const op_end = out + *out_len;
+#endif
+#if defined(LZO1Z)
+ uint32 last_m_off = 0;
+#endif
+
+#if defined(COPY_DICT)
+ if (dict)
+ {
+ if (dict_len > M4_MAX_OFFSET)
+ {
+ dict += dict_len - M4_MAX_OFFSET;
+ dict_len = M4_MAX_OFFSET;
+ }
+ dict_end = dict + dict_len;
+ }
+ else
+ {
+ dict_len = 0;
+ dict_end = NULL;
+ }
+#endif
+
+ *out_len = 0;
+
+ op = out;
+ ip = in;
+
+ if (*ip > 17)
+ {
+ t = *ip++ - 17;
+ if (t < 4)
+ goto match_next;
+ assert(t > 0); NEED_OP(t); NEED_IP(t+1);
+ do *op++ = *ip++; while (--t > 0);
+ goto first_literal_run;
+ }
+
+ while (TEST_IP && TEST_OP)
+ {
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 15 + *ip++;
+ }
+ assert(t > 0); NEED_OP(t+3); NEED_IP(t+4);
+#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4)
+ t += 3;
+ if (t >= 8) do
+ {
+ UA_COPY64(op, ip);
+ op += 8; ip += 8; t -= 8;
+ } while (t >= 8);
+ if (t >= 4)
+ {
+ UA_COPY32(op, ip);
+ op += 4; ip += 4; t -= 4;
+ }
+ if (t > 0)
+ {
+ *op++ = *ip++;
+ if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } }
+ }
+#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+ if (PTR_ALIGNED2_4(op, ip))
+ {
+#endif
+ UA_COPY32(op, ip);
+ op += 4; ip += 4;
+ if (--t > 0)
+ {
+ if (t >= 4)
+ {
+ do {
+ UA_COPY32(op, ip);
+ op += 4; ip += 4; t -= 4;
+ } while (t >= 4);
+ if (t > 0) do *op++ = *ip++; while (--t > 0);
+ }
+ else
+ do *op++ = *ip++; while (--t > 0);
+ }
+#if !defined(LZO_UNALIGNED_OK_4)
+ }
+ else
+#endif
+#endif
+#if !defined(LZO_UNALIGNED_OK_4) && !defined(LZO_UNALIGNED_OK_8)
+ {
+ *op++ = *ip++; *op++ = *ip++; *op++ = *ip++;
+ do *op++ = *ip++; while (--t > 0);
+ }
+#endif
+
+first_literal_run:
+
+ t = *ip++;
+ if (t >= 16)
+ goto match;
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+#else
+ m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2);
+#endif
+ NEED_OP(3);
+ t = 3; COPY_DICT(t, m_off)
+#else
+#if defined(LZO1Z)
+ t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+ m_pos = op - t;
+ last_m_off = t;
+#else
+ m_pos = op - (1 + M2_MAX_OFFSET);
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+#endif
+ TEST_LB(m_pos); NEED_OP(3);
+ *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos;
+#endif
+ goto match_done;
+
+ do {
+match:
+ if (t >= 64)
+ {
+#if defined(COPY_DICT)
+#if defined(LZO1X)
+ m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3);
+ t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+ m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2);
+ t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+ m_off = t & 0x1f;
+ if (m_off >= 0x1c)
+ m_off = last_m_off;
+ else
+ {
+ m_off = 1 + (m_off << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+ }
+ t = (t >> 5) - 1;
+#endif
+#else
+#if defined(LZO1X)
+ m_pos = op - 1;
+ m_pos -= (t >> 2) & 7;
+ m_pos -= *ip++ << 3;
+ t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+ m_pos = op - 1;
+ m_pos -= (t >> 2) & 3;
+ m_pos -= *ip++ << 2;
+ t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+ {
+ uint32 off = t & 0x1f;
+ m_pos = op;
+ if (off >= 0x1c)
+ {
+ assert(last_m_off > 0);
+ m_pos -= last_m_off;
+ }
+ else
+ {
+ off = 1 + (off << 6) + (*ip++ >> 2);
+ m_pos -= off;
+ last_m_off = off;
+ }
+ }
+ t = (t >> 5) - 1;
+#endif
+ TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+ goto copy_match;
+#endif
+ }
+ else if (t >= 32)
+ {
+ t &= 31;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 31 + *ip++;
+ }
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+ last_m_off = m_off;
+#else
+ m_off = 1 + (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#else
+#if defined(LZO1Z)
+ {
+ uint32 off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+ m_pos = op - off;
+ last_m_off = off;
+ }
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+ m_pos = op - 1;
+ m_pos -= UA_GET16(ip) >> 2;
+#else
+ m_pos = op - 1;
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#endif
+ ip += 2;
+ }
+ else if (t >= 16)
+ {
+#if defined(COPY_DICT)
+ m_off = (t & 8) << 11;
+#else
+ m_pos = op;
+ m_pos -= (t & 8) << 11;
+#endif
+ t &= 7;
+ if (t == 0)
+ {
+ NEED_IP(1);
+ while (*ip == 0)
+ {
+ t += 255;
+ ip++;
+ NEED_IP(1);
+ }
+ t += 7 + *ip++;
+ }
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off += (ip[0] << 6) + (ip[1] >> 2);
+#else
+ m_off += (ip[0] >> 2) + (ip[1] << 6);
+#endif
+ ip += 2;
+ if (m_off == 0)
+ goto eof_found;
+ m_off += 0x4000;
+#if defined(LZO1Z)
+ last_m_off = m_off;
+#endif
+#else
+#if defined(LZO1Z)
+ m_pos -= (ip[0] << 6) + (ip[1] >> 2);
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+ m_pos -= UA_GET16(ip) >> 2;
+#else
+ m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+ ip += 2;
+ if (m_pos == op)
+ goto eof_found;
+ m_pos -= 0x4000;
+#if defined(LZO1Z)
+ last_m_off = pd((const byte *)op, m_pos);
+#endif
+#endif
+ }
+ else
+ {
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+ m_off = 1 + (t << 6) + (*ip++ >> 2);
+ last_m_off = m_off;
+#else
+ m_off = 1 + (t >> 2) + (*ip++ << 2);
+#endif
+ NEED_OP(2);
+ t = 2; COPY_DICT(t, m_off)
+#else
+#if defined(LZO1Z)
+ t = 1 + (t << 6) + (*ip++ >> 2);
+ m_pos = op - t;
+ last_m_off = t;
+#else
+ m_pos = op - 1;
+ m_pos -= t >> 2;
+ m_pos -= *ip++ << 2;
+#endif
+ TEST_LB(m_pos); NEED_OP(2);
+ *op++ = *m_pos++; *op++ = *m_pos;
+#endif
+ goto match_done;
+ }
+
+#if defined(COPY_DICT)
+
+ NEED_OP(t+3-1);
+ t += 3-1; COPY_DICT(t, m_off)
+
+#else
+
+ TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+#if defined(LZO_UNALIGNED_OK_8) && defined(LZO_UNALIGNED_OK_4)
+ if (op - m_pos >= 8)
+ {
+ t += (3 - 1);
+ if (t >= 8) do
+ {
+ UA_COPY64(op, m_pos);
+ op += 8; m_pos += 8; t -= 8;
+ } while (t >= 8);
+ if (t >= 4)
+ {
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4;
+ }
+ if (t > 0)
+ {
+ *op++ = m_pos[0];
+ if (t > 1) { *op++ = m_pos[1]; if (t > 2) { *op++ = m_pos[2]; } }
+ }
+ }
+ else
+#elif defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+#if !defined(LZO_UNALIGNED_OK_4)
+ if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op, m_pos))
+ {
+ assert((op - m_pos) >= 4);
+#else
+ if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4)
+ {
+#endif
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4 - (3 - 1);
+ do {
+ UA_COPY32(op, m_pos);
+ op += 4; m_pos += 4; t -= 4;
+ } while (t >= 4);
+ if (t > 0) do *op++ = *m_pos++; while (--t > 0);
+ }
+ else
+#endif
+ {
+copy_match:
+ *op++ = *m_pos++; *op++ = *m_pos++;
+ do *op++ = *m_pos++; while (--t > 0);
+ }
+
+#endif
+
+match_done:
+#if defined(LZO1Z)
+ t = ip[-1] & 3;
+#else
+ t = ip[-2] & 3;
+#endif
+ if (t == 0)
+ break;
+
+match_next:
+ assert(t > 0); assert(t < 4); NEED_OP(t); NEED_IP(t+1);
+#if 0
+ do *op++ = *ip++; while (--t > 0);
+#else
+ *op++ = *ip++;
+ if (t > 1) { *op++ = *ip++; if (t > 2) { *op++ = *ip++; } }
+#endif
+ t = *ip++;
+ } while (TEST_IP && TEST_OP);
+ }
+
+#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP)
+ *out_len = pd(op, out);
+ return LZO_E_EOF_NOT_FOUND;
+#endif
+
+eof_found:
+ assert(t == 1);
+ *out_len = pd(op, out);
+ return (ip == ip_end ? LZO_E_OK :
+ (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+#if defined(HAVE_NEED_IP)
+input_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_INPUT_OVERRUN;
+#endif
+
+#if defined(HAVE_NEED_OP)
+output_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_OUTPUT_OVERRUN;
+#endif
+
+#if defined(LZO_TEST_OVERRUN_LOOKBEHIND)
+lookbehind_overrun:
+ *out_len = pd(op, out);
+ return LZO_E_LOOKBEHIND_OVERRUN;
+#endif
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/lzo.h b/engines/tony/mpal/lzo.h
new file mode 100644
index 0000000000..ebb1c4b516
--- /dev/null
+++ b/engines/tony/mpal/lzo.h
@@ -0,0 +1,111 @@
+/* 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.
+ *
+ *
+ */
+/* minilzo.c -- mini subset of the LZO real-time data compression library
+
+ This file is part of the LZO real-time data compression library.
+
+ Copyright (C) 2011 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2010 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2009 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+ Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+ All Rights Reserved.
+
+ The LZO library 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.
+
+ The LZO library 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 the LZO library; see the file COPYING.
+ If not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ Markus F.X.J. Oberhumer
+ <markus@oberhumer.com>
+ http://www.oberhumer.com/opensource/lzo/
+ */
+
+#ifndef TONY_MPAL_LZO_H
+#define TONY_MPAL_LZO_H
+
+#include "common/scummsys.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/* Error codes for the compression/decompression functions. Negative
+ * values are errors, positive values will be used for special but
+ * normal events.
+ */
+#define LZO_E_OK 0
+#define LZO_E_ERROR (-1)
+#define LZO_E_OUT_OF_MEMORY (-2) /* [lzo_alloc_func_t failure] */
+#define LZO_E_NOT_COMPRESSIBLE (-3) /* [not used right now] */
+#define LZO_E_INPUT_OVERRUN (-4)
+#define LZO_E_OUTPUT_OVERRUN (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN (-6)
+#define LZO_E_EOF_NOT_FOUND (-7)
+#define LZO_E_INPUT_NOT_CONSUMED (-8)
+#define LZO_E_NOT_YET_IMPLEMENTED (-9) /* [not used right now] */
+#define LZO_E_INVALID_ARGUMENT (-10)
+
+#define LZO1X_999_MEM_COMPRESS ((uint32) (14 * 16384L * sizeof(uint16)))
+
+/**
+ * Decompresses an LZO compressed resource
+ */
+int lzo1x_decompress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len);
+
+/**
+ * Comrpess a data block into an LZO stream
+ */
+int lzo1x_1_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem);
+
+/**
+ * better compression ratio at the cost of more memory and time
+ */
+int lzo1x_999_compress(const byte *src, uint32 src_len, byte *dst, uint32 *dst_len, void *wrkmem);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif /* already included */
diff --git a/engines/tony/mpal/memory.cpp b/engines/tony/mpal/memory.cpp
new file mode 100644
index 0000000000..dfbf16e789
--- /dev/null
+++ b/engines/tony/mpal/memory.cpp
@@ -0,0 +1,127 @@
+/* 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.
+ *
+ *
+ */
+
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* MemoryManager methods
+\****************************************************************************/
+
+/**
+ * Allocates a new memory block
+ * @return Returns a MemoryItem instance for the new block
+ */
+MpalHandle MemoryManager::allocate(uint32 size, uint flags) {
+ MemoryItem *newItem = (MemoryItem *)malloc(sizeof(MemoryItem) + size);
+ newItem->_id = BLOCK_ID;
+ newItem->_size = size;
+ newItem->_lockCount = 0;
+
+ // If requested, clear the allocated data block
+ if ((flags & GMEM_ZEROINIT) != 0) {
+ byte *dataP = newItem->_data;
+ Common::fill(dataP, dataP + size, 0);
+ }
+
+ return (MpalHandle)newItem;
+}
+
+/**
+ * Allocates a new memory block and returns its data pointer
+ * @return Data pointer to allocated block
+ */
+void *MemoryManager::alloc(uint32 size, uint flags) {
+ MemoryItem *item = (MemoryItem *)allocate(size, flags);
+ ++item->_lockCount;
+ return &item->_data[0];
+}
+
+#define OFFSETOF(type, field) ((size_t) &(((type *) 0)->field))
+
+/**
+ * Returns a reference to the MemoryItem for a gien byte pointer
+ * @param block Byte pointer
+ */
+MemoryItem *MemoryManager::getItem(MpalHandle handle) {
+ MemoryItem *rec = (MemoryItem *)((byte *)handle - OFFSETOF(MemoryItem, _data));
+ assert(rec->_id == BLOCK_ID);
+ return rec;
+}
+
+/**
+ * Returns a size of a memory block given its pointer
+ */
+uint32 MemoryManager::getSize(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ return item->_size;
+}
+
+/**
+ * Erases a given item
+ */
+void MemoryManager::freeBlock(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ free(item);
+}
+
+/**
+ * Erases a given item
+ */
+void MemoryManager::destroyItem(MpalHandle handle) {
+ MemoryItem *item = getItem(handle);
+ assert(item->_id == BLOCK_ID);
+ free(item);
+}
+
+/**
+ * Locks an item for access
+ */
+byte *MemoryManager::lockItem(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ ++item->_lockCount;
+ return &item->_data[0];
+}
+
+/**
+ * Unlocks a locked item
+ */
+void MemoryManager::unlockItem(MpalHandle handle) {
+ MemoryItem *item = (MemoryItem *)handle;
+ assert(item->_id == BLOCK_ID);
+ assert(item->_lockCount > 0);
+ --item->_lockCount;
+}
+
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/memory.h b/engines/tony/mpal/memory.h
new file mode 100644
index 0000000000..9c21cc20e6
--- /dev/null
+++ b/engines/tony/mpal/memory.h
@@ -0,0 +1,78 @@
+/* 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.
+ *
+ *
+ */
+
+#ifndef TONY_MPAL_MEMORY
+#define TONY_MPAL_MEMORY
+
+#include "common/scummsys.h"
+#include "common/list.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+typedef void *MpalHandle;
+
+struct MemoryItem {
+ uint32 _id;
+ uint32 _size;
+ int _lockCount;
+ byte _data[1];
+
+ // Casting for access to data
+ operator void *() { return &_data[0]; }
+};
+
+class MemoryManager {
+private:
+ static MemoryItem *getItem(MpalHandle handle);
+public:
+ static MpalHandle allocate(uint32 size, uint flags);
+ static void *alloc(uint32 size, uint flags);
+ static void freeBlock(MpalHandle handle);
+ static void destroyItem(MpalHandle handle);
+ static uint32 getSize(MpalHandle handle);
+ static byte *lockItem(MpalHandle handle);
+ static void unlockItem(MpalHandle handle);
+};
+
+// defines
+#define globalAlloc(flags, size) MemoryManager::alloc(size, flags)
+#define globalAllocate(flags, size) MemoryManager::allocate(size, flags)
+#define globalFree(handle) MemoryManager::freeBlock(handle)
+#define globalDestroy(handle) MemoryManager::destroyItem(handle)
+#define globalLock(handle) MemoryManager::lockItem(handle)
+#define globalUnlock(handle) MemoryManager::unlockItem(handle)
+#define globalSize(handle) MemoryManager::getSize(handle)
+
+#define GMEM_FIXED 1
+#define GMEM_MOVEABLE 2
+#define GMEM_ZEROINIT 4
+
+const uint32 BLOCK_ID = 0x12345678;
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp
new file mode 100644
index 0000000000..8d83363c24
--- /dev/null
+++ b/engines/tony/mpal/mpal.cpp
@@ -0,0 +1,2089 @@
+/* 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 "common/scummsys.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "tony/tony.h"
+#include "tony/mpal/lzo.h"
+#include "tony/mpal/mpal.h"
+#include "tony/mpal/mpaldll.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Internal functions
+\****************************************************************************/
+
+/**
+ * Locks the variables for access
+ */
+void lockVar() {
+ GLOBALS._lpmvVars = (LpMpalVar)globalLock(GLOBALS._hVars);
+}
+
+/**
+ * Unlocks variables after use
+ */
+void unlockVar() {
+ globalUnlock(GLOBALS._hVars);
+}
+
+/**
+ * Locks the messages for access
+ */
+static void LockMsg() {
+#ifdef NEED_LOCK_MSGS
+ GLOBALS._lpmmMsgs = (LpMpalMsg)globalLock(GLOBALS._hMsgs);
+#endif
+}
+
+/**
+ * Unlocks the messages after use
+ */
+static void UnlockMsg() {
+#ifdef NEED_LOCK_MSGS
+ globalUnlock(GLOBALS._hMsgs);
+#endif
+}
+
+/**
+ * Locks the dialogs for access
+ */
+static void lockDialogs() {
+ GLOBALS._lpmdDialogs = (LpMpalDialog)globalLock(GLOBALS._hDialogs);
+}
+
+/**
+ * Unlocks the dialogs after use
+ */
+static void unlockDialogs() {
+ globalUnlock(GLOBALS._hDialogs);
+}
+
+/**
+ * Locks the location data structures for access
+ */
+static void lockLocations() {
+ GLOBALS._lpmlLocations = (LpMpalLocation)globalLock(GLOBALS._hLocations);
+}
+
+/**
+ * Unlocks the location structures after use
+ */
+static void unlockLocations() {
+ globalUnlock(GLOBALS._hLocations);
+}
+
+/**
+ * Locks the items structures for use
+ */
+static void lockItems() {
+ GLOBALS._lpmiItems = (LpMpalItem)globalLock(GLOBALS._hItems);
+}
+
+/**
+ * Unlocks the items structures after use
+ */
+static void unlockItems() {
+ globalUnlock(GLOBALS._hItems);
+}
+
+/**
+ * Locks the script data structures for use
+ */
+static void LockScripts() {
+ GLOBALS._lpmsScripts = (LpMpalScript)globalLock(GLOBALS._hScripts);
+}
+
+/**
+ * Unlocks the script data structures after use
+ */
+static void unlockScripts() {
+ globalUnlock(GLOBALS._hScripts);
+}
+
+/**
+ * Returns the current value of a global variable
+ *
+ * @param lpszVarName Name of the variable
+ * @returns Current value
+ * @remarks Before using this method, you must call lockVar() to
+ * lock the global variablves for use. Then afterwards, you will
+ * need to remember to call UnlockVar()
+ */
+int32 varGetValue(const char *lpszVarName) {
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ for (int i = 0; i < GLOBALS._nVars; v++, i++)
+ if (strcmp(lpszVarName, v->_lpszVarName) == 0)
+ return v->_dwVal;
+
+ GLOBALS._mpalError = 1;
+ return 0;
+}
+
+/**
+ * Sets the value of a MPAL global variable
+ * @param lpszVarName Name of the variable
+ * @param val Value to set
+ */
+void varSetValue(const char *lpszVarName, int32 val) {
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ for (uint i = 0; i < GLOBALS._nVars; v++, i++)
+ if (strcmp(lpszVarName, v->_lpszVarName) == 0) {
+ v->_dwVal = val;
+ if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Pattern.", 8) == 0) {
+ i = 0;
+ sscanf(v->_lpszVarName, "Pattern.%u", &i);
+ GLOBALS._lpiifCustom(i, val, -1);
+ } else if (GLOBALS._lpiifCustom != NULL && strncmp(v->_lpszVarName, "Status.", 7) == 0) {
+ i = 0;
+ sscanf(v->_lpszVarName,"Status.%u", &i);
+ GLOBALS._lpiifCustom(i, -1, val);
+ }
+ return;
+ }
+
+ GLOBALS._mpalError = 1;
+ return;
+}
+
+/**
+ * Find the index of a location within the location array. Remember to call LockLoc() beforehand.
+ *
+ * @param nLoc Location number to search for
+ * @returns Index, or -1 if the location is not present
+ * @remarks This function requires the location list to have
+ * first been locked with a call to LockLoc().
+ */
+static int locGetOrderFromNum(uint32 nLoc) {
+ LpMpalLocation loc = GLOBALS._lpmlLocations;
+
+ for (int i = 0; i < GLOBALS._nLocations; i++, loc++)
+ if (loc->_nObj == nLoc)
+ return i;
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a message within the messages array
+ * @param nMsg Message number to search for
+ * @returns Index, or -1 if the message is not present
+ * @remarks This function requires the message list to have
+ * first been locked with a call to LockMsg()
+ */
+static int msgGetOrderFromNum(uint32 nMsg) {
+ LpMpalMsg msg = GLOBALS._lpmmMsgs;
+
+ for (int i = 0; i < GLOBALS._nMsgs; i++, msg++) {
+ if (msg->_wNum == nMsg)
+ return i;
+ }
+
+ return -1;
+}
+
+/**
+ * Find the index of an item within the items array
+ * @param nItem Item number to search for
+ * @returns Index, or -1 if the item is not present
+ * @remarks This function requires the item list to have
+ * first been locked with a call to LockItems()
+ */
+static int itemGetOrderFromNum(uint32 nItem) {
+ LpMpalItem item = GLOBALS._lpmiItems;
+
+ for (int i = 0; i < GLOBALS._nItems; i++, item++) {
+ if (item->_nObj == nItem)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a script within the scripts array
+ * @param nScript Script number to search for
+ * @returns Index, or -1 if the script is not present
+ * @remarks This function requires the script list to have
+ * first been locked with a call to LockScripts()
+ */
+static int scriptGetOrderFromNum(uint32 nScript) {
+ LpMpalScript script = GLOBALS._lpmsScripts;
+
+ for (int i = 0; i < GLOBALS._nScripts; i++, script++) {
+ if (script->_nObj == nScript)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Find the index of a dialog within the dialogs array
+ * @param nDialog Dialog number to search for
+ * @returns Index, or -1 if the dialog is not present
+ * @remarks This function requires the dialog list to have
+ * first been locked with a call to LockDialogs()
+ */
+static int dialogGetOrderFromNum(uint32 nDialog) {
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs;
+
+ for (int i = 0; i < GLOBALS._nDialogs; i++, dialog++) {
+ if (dialog->_nObj == nDialog)
+ return i;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Duplicates a message
+ * @param nMsgOrd Index of the message inside the messages array
+ * @returns Pointer to the duplicated message.
+ * @remarks Remember to free the duplicated message when done with it.
+ */
+static char *DuplicateMessage(uint32 nMsgOrd) {
+ const char *origmsg;
+ char *clonemsg;
+
+ if (nMsgOrd == (uint32)-1)
+ return NULL;
+
+ origmsg = (const char *)globalLock(GLOBALS._lpmmMsgs[nMsgOrd]._hText);
+
+ int j = 0;
+ while (origmsg[j] != '\0' || origmsg[j + 1] != '\0')
+ j++;
+ j += 2;
+
+ clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, j);
+ if (clonemsg == NULL)
+ return NULL;
+
+ memcpy(clonemsg, origmsg, j);
+ globalUnlock(GLOBALS._lpmmMsgs[nMsgOrd]._hText);
+
+ return clonemsg;
+}
+
+
+/**
+ * Duplicate a sentence of a dialog
+ * @param nDlgOrd Index of the dialog in the dialogs array
+ * @param nPeriod Sentence number to be duplicated.
+ * @returns Pointer to the duplicated phrase. Remember to free it
+ * when done with it.
+ */
+static char *duplicateDialogPeriod(uint32 nPeriod) {
+ const char *origmsg;
+ char *clonemsg;
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ for (int j = 0; dialog->_periods[j] != NULL; j++) {
+ if (dialog->_periodNums[j] == nPeriod) {
+ // Found the phrase, it should be duplicated
+ origmsg = (const char *)globalLock(dialog->_periods[j]);
+
+ // Calculate the length and allocate memory
+ int i = 0;
+ while (origmsg[i] != '\0')
+ i++;
+
+ clonemsg = (char *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, i + 1);
+ if (clonemsg == NULL)
+ return NULL;
+
+ memcpy(clonemsg, origmsg, i);
+
+ globalUnlock(dialog->_periods[j]);
+
+ return clonemsg;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Load a resource from the MPR file
+ *
+ * @param dwId ID of the resource to load
+ * @returns Handle to the loaded resource
+ */
+MpalHandle resLoad(uint32 dwId) {
+ MpalHandle h;
+ char head[4];
+ byte *temp, *buf;
+
+ for (int i = 0; i < GLOBALS._nResources; i++)
+ if (GLOBALS._lpResources[i * 2] == dwId) {
+ GLOBALS._hMpr.seek(GLOBALS._lpResources[i * 2 + 1]);
+ uint32 nBytesRead = GLOBALS._hMpr.read(head, 4);
+ if (nBytesRead != 4)
+ return NULL;
+ if (head[0] != 'R' || head[1] != 'E' || head[2] != 'S' || head[3] != 'D')
+ return NULL;
+
+ uint32 nSizeDecomp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return NULL;
+
+ uint32 nSizeComp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return NULL;
+
+ h = globalAllocate(GMEM_MOVEABLE | GMEM_ZEROINIT, nSizeDecomp + (nSizeDecomp / 1024) * 16);
+ buf = (byte *)globalLock(h);
+ temp = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, nSizeComp);
+
+ nBytesRead = GLOBALS._hMpr.read(temp, nSizeComp);
+ if (nBytesRead != nSizeComp)
+ return NULL;
+
+ lzo1x_decompress(temp, nSizeComp, buf, &nBytesRead);
+ if (nBytesRead != nSizeDecomp)
+ return NULL;
+
+ globalDestroy(temp);
+ globalUnlock(h);
+ return h;
+ }
+
+ return NULL;
+}
+
+static uint32 *getSelectList(uint32 i) {
+ uint32 *sl;
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Count how many are active selects
+ int num = 0;
+ for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._curActive)
+ num++;
+ }
+
+ // If there are 0, it's a mistake
+ if (num == 0)
+ return NULL;
+
+ sl = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1));
+ if (sl == NULL)
+ return NULL;
+
+ // Copy all the data inside the active select list
+ int k = 0;
+ for (int j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._curActive)
+ sl[k++] = dialog->_choice[i]._select[j]._dwData;
+ }
+
+ sl[k] = (uint32)NULL;
+ return sl;
+}
+
+static uint32 *GetItemList(uint32 nLoc) {
+ uint32 *il;
+ LpMpalVar v = GLOBALS._lpmvVars;
+
+ uint32 num = 0;
+ for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) {
+ if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc)
+ num++;
+ }
+
+ il = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(uint32) * (num + 1));
+ if (il == NULL)
+ return NULL;
+
+ v = GLOBALS._lpmvVars;
+ uint32 j = 0;
+ for (uint32 i = 0; i < GLOBALS._nVars; i++, v++) {
+ if (strncmp(v->_lpszVarName, "Location", 8) == 0 && v->_dwVal == nLoc) {
+ sscanf(v->_lpszVarName, "Location.%u", &il[j]);
+ j++;
+ }
+ }
+
+ il[j] = (uint32)NULL;
+ return il;
+}
+
+static LpItem getItemData(uint32 nOrdItem) {
+ LpMpalItem curitem = GLOBALS._lpmiItems + nOrdItem;
+ char *dat;
+ char *patlength;
+
+ // Zeroing out the allocated memory is required!!!
+ LpItem ret = (LpItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(Item));
+ if (ret == NULL)
+ return NULL;
+ ret->_speed = 150;
+
+ MpalHandle hDat = resLoad(curitem->_dwRes);
+ dat = (char *)globalLock(hDat);
+
+ if (dat[0] == 'D' && dat[1] == 'A' && dat[2] == 'T') {
+ int i = dat[3]; // For version 1.0!!
+ dat += 4;
+
+ if (i >= 0x10) { // From 1.0, there's a destination point for each object
+ ret->_destX = (int16)READ_LE_UINT16(dat);
+ ret->_destY = (int16)READ_LE_UINT16(dat + 2);
+ dat += 4;
+ }
+
+ if (i >= 0x11) { // From 1.1, there's animation speed
+ ret->_speed = READ_LE_UINT16(dat);
+ dat += 2;
+ } else
+ ret->_speed = 150;
+ }
+
+ ret->_numframe = *dat++;
+ ret->_numpattern = *dat++;
+ ret->_destZ = *dat++;
+
+ // Upload the left & top co-ordinates of each frame
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_frameslocations[i].left = (int16)READ_LE_UINT16(dat);
+ ret->_frameslocations[i].top = (int16)READ_LE_UINT16(dat + 2);
+ dat += 4;
+ }
+
+ // Upload the size of each frame and calculate the right & bottom
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_frameslocations[i].right = (int16)READ_LE_UINT16(dat) + ret->_frameslocations[i].left;
+ ret->_frameslocations[i].bottom = (int16)READ_LE_UINT16(dat + 2) + ret->_frameslocations[i].top;
+ dat += 4;
+ }
+
+ // Upload the bounding boxes of each frame
+ for (int i = 0; i < ret->_numframe; i++) {
+ ret->_bbox[i].left = (int16)READ_LE_UINT16(dat);
+ ret->_bbox[i].top = (int16)READ_LE_UINT16(dat + 2);
+ ret->_bbox[i].right = (int16)READ_LE_UINT16(dat + 4);
+ ret->_bbox[i].bottom = (int16)READ_LE_UINT16(dat + 6);
+ dat += 8;
+ }
+
+ // Load the animation pattern
+ patlength = dat;
+ dat += ret->_numpattern;
+
+ for (int i = 1; i < ret->_numpattern; i++) {
+ for (int j = 0; j < patlength[i]; j++)
+ ret->_pattern[i][j] = dat[j];
+ ret->_pattern[i][(int)patlength[i]] = 255; // Terminate pattern
+ dat += patlength[i];
+ }
+
+ // Upload the individual frames of animations
+ for (int i = 1; i < ret->_numframe; i++) {
+ uint32 dim = (uint32)(ret->_frameslocations[i].right - ret->_frameslocations[i].left) *
+ (uint32)(ret->_frameslocations[i].bottom - ret->_frameslocations[i].top);
+ ret->_frames[i] = (char *)globalAlloc(GMEM_FIXED, dim);
+
+ if (ret->_frames[i] == NULL)
+ return NULL;
+ memcpy(ret->_frames[i], dat, dim);
+ dat += dim;
+ }
+
+ // Check if we've got to the end of the file
+ int i = READ_LE_UINT16(dat);
+ if (i != 0xABCD)
+ return NULL;
+
+ globalUnlock(hDat);
+ globalFree(hDat);
+
+ return ret;
+}
+
+
+/**
+ * Thread that calls a custom function. It is used in scripts, so that each script
+ * function is executed without delaying the others.
+ *
+ * @param param pointer to a pointer to the structure that defines the call.
+ * @remarks The passed structure is freed when the process finishes.
+ */
+void CustomThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ LpCfCall p;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->p = *(const LpCfCall *)param;
+
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->p->_nCf], _ctx->p->_arg1, _ctx->p->_arg2, _ctx->p->_arg3, _ctx->p->_arg4);
+
+ globalFree(_ctx->p);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Main process for running a script.
+ *
+ * @param param Pointer to a pointer to a structure containing the script data.
+ * @remarks The passed structure is freed when the process finishes.
+ */
+void ScriptThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ uint i, j, k;
+ uint32 dwStartTime;
+ uint32 dwCurTime;
+ uint32 dwId;
+ int numHandles;
+ LpCfCall p;
+ CORO_END_CONTEXT(_ctx);
+
+ static uint32 cfHandles[MAX_COMMANDS_PER_MOMENT];
+ LpMpalScript s = *(const LpMpalScript *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ _ctx->dwStartTime = g_vm->getTime();
+ _ctx->numHandles = 0;
+
+// debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Moments: %u\n", s->_nMoments);
+ for (_ctx->i = 0; _ctx->i < s->_nMoments; _ctx->i++) {
+ // Sleep for the required time
+ if (s->_moment[_ctx->i]._dwTime == -1) {
+ CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, _ctx->numHandles, cfHandles, true, CORO_INFINITE);
+ _ctx->dwStartTime = g_vm->getTime();
+ } else {
+ _ctx->dwCurTime = g_vm->getTime();
+ if (_ctx->dwCurTime < _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100)) {
+ // debugC(DEBUG_BASIC, kTonyDebugMPAL, "PlayScript(): Sleeping %lums\n",_ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime*100) - _ctx->dwCurTime);
+ CORO_INVOKE_1(CoroScheduler.sleep, _ctx->dwStartTime + (s->_moment[_ctx->i]._dwTime * 100) - _ctx->dwCurTime);
+ }
+ }
+
+ _ctx->numHandles = 0;
+ for (_ctx->j = 0; _ctx->j < s->_moment[_ctx->i]._nCmds; _ctx->j++) {
+ _ctx->k = s->_moment[_ctx->i]._cmdNum[_ctx->j];
+
+ if (s->_command[_ctx->k]._type == 1) {
+ _ctx->p = (LpCfCall)globalAlloc(GMEM_FIXED, sizeof(CfCall));
+ if (_ctx->p == NULL) {
+ GLOBALS._mpalError = 1;
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ _ctx->p->_nCf = s->_command[_ctx->k]._nCf;
+ _ctx->p->_arg1 = s->_command[_ctx->k]._arg1;
+ _ctx->p->_arg2 = s->_command[_ctx->k]._arg2;
+ _ctx->p->_arg3 = s->_command[_ctx->k]._arg3;
+ _ctx->p->_arg4 = s->_command[_ctx->k]._arg4;
+
+ // !!! New process management
+ if ((cfHandles[_ctx->numHandles++] = CoroScheduler.createProcess(CustomThread, &_ctx->p, sizeof(LpCfCall))) == 0) {
+ GLOBALS._mpalError = 1;
+
+ CORO_KILL_SELF();
+ return;
+ }
+ } else if (s->_command[_ctx->k]._type == 2) {
+ lockVar();
+ varSetValue(
+ s->_command[_ctx->k]._lpszVarName,
+ evaluateExpression(s->_command[_ctx->k]._expr)
+ );
+ unlockVar();
+
+ } else {
+ GLOBALS._mpalError = 1;
+ globalFree(s);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // WORKAROUND: Wait for events to pulse.
+ CORO_SLEEP(1);
+ }
+ }
+
+ globalFree(s);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Thread that performs an action on an item. the thread always executes the action,
+ * so it should create a new item in which the action is the one required.
+ * Furthermore, the expression is not checked, but it is always performed the action.
+ *
+ * @param param Pointer to a pointer to a structure containing the action.
+ */
+void ActionThread(CORO_PARAM, const void *param) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ int j, k;
+ LpMpalItem item;
+
+ ~CoroContextTag() {
+ if (item)
+ globalDestroy(item);
+ }
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // The ActionThread owns the data block pointed to, so we need to make sure it's
+ // freed when the process exits
+ _ctx->item = *(const LpMpalItem *)param;
+
+ GLOBALS._mpalError = 0;
+ for (_ctx->j = 0; _ctx->j < _ctx->item->_action[_ctx->item->_dwRes]._nCmds; _ctx->j++) {
+ _ctx->k = _ctx->item->_action[_ctx->item->_dwRes]._cmdNum[_ctx->j];
+
+ if (_ctx->item->_command[_ctx->k]._type == 1) {
+ // Custom function
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Call=%s params=%d,%d,%d,%d",
+ CoroScheduler.getCurrentPID(), GLOBALS._lplpFunctionStrings[_ctx->item->_command[_ctx->k]._nCf].c_str(),
+ _ctx->item->_command[_ctx->k]._arg1, _ctx->item->_command[_ctx->k]._arg2,
+ _ctx->item->_command[_ctx->k]._arg3, _ctx->item->_command[_ctx->k]._arg4
+ );
+
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->item->_command[_ctx->k]._nCf],
+ _ctx->item->_command[_ctx->k]._arg1,
+ _ctx->item->_command[_ctx->k]._arg2,
+ _ctx->item->_command[_ctx->k]._arg3,
+ _ctx->item->_command[_ctx->k]._arg4
+
+ );
+ } else if (_ctx->item->_command[_ctx->k]._type == 2) {
+ // Variable assign
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d Variable=%s",
+ CoroScheduler.getCurrentPID(), _ctx->item->_command[_ctx->k]._lpszVarName);
+
+ lockVar();
+ varSetValue(_ctx->item->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->item->_command[_ctx->k]._expr));
+ unlockVar();
+
+ } else {
+ GLOBALS._mpalError = 1;
+ break;
+ }
+
+ // WORKAROUND: Wait for events to pulse.
+ CORO_SLEEP(1);
+ }
+
+ globalDestroy(_ctx->item);
+ _ctx->item = NULL;
+
+ debugC(DEBUG_DETAILED, kTonyDebugActions, "Action Process %d ended", CoroScheduler.getCurrentPID());
+
+ CORO_END_CODE;
+}
+
+/**
+ * This thread monitors a created action to detect when it ends.
+ * @remarks Since actions can spawn sub-actions, this needs to be a
+ * separate thread to determine when the outer action is done
+ */
+void ShutUpActionThread(CORO_PARAM, const void *param) {
+ // COROUTINE
+ CORO_BEGIN_CONTEXT;
+ int slotNumber;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ GLOBALS._bExecutingAction = false;
+
+ if (g_vm->_initialLoadSlotNumber != -1) {
+ _ctx->slotNumber = g_vm->_initialLoadSlotNumber;
+ g_vm->_initialLoadSlotNumber = -1;
+
+ CORO_INVOKE_1(g_vm->loadState, _ctx->slotNumber);
+ }
+
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Polls one location (starting point of a process)
+ *
+ * @param param Pointer to an index in the array of polling locations.
+ */
+void LocationPollThread(CORO_PARAM, const void *param) {
+ typedef struct {
+ uint32 _nItem, _nAction;
+
+ uint16 _wTime;
+ byte _perc;
+ MpalHandle _when;
+ byte _nCmds;
+ uint16 _cmdNum[MAX_COMMANDS_PER_ACTION];
+ uint32 _dwLastTime;
+ } MYACTION;
+
+ typedef struct {
+ uint32 _nItem;
+ uint32 _hThread;
+ } MYTHREAD;
+
+ CORO_BEGIN_CONTEXT;
+ uint32 *il;
+ int i, j, k;
+ int numitems;
+ int nRealItems;
+ LpMpalItem curItem, newItem;
+ int nIdleActions;
+ uint32 curTime;
+ uint32 dwSleepTime;
+ uint32 dwId;
+ int ord;
+ bool delayExpired;
+ bool expired;
+
+ MYACTION *myActions;
+ MYTHREAD *myThreads;
+
+ ~CoroContextTag() {
+ // Free data blocks
+ if (myThreads)
+ globalDestroy(myThreads);
+ if (myActions)
+ globalDestroy(myActions);
+ }
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 id = *((const uint32 *)param);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Initialize data pointers
+ _ctx->myActions = NULL;
+ _ctx->myThreads = NULL;
+
+ // To begin with, we need to request the item list from the location
+ _ctx->il = mpalQueryItemList(GLOBALS._nPollingLocations[id]);
+
+ // Count the items
+ for (_ctx->numitems = 0; _ctx->il[_ctx->numitems] != 0; _ctx->numitems++)
+ ;
+
+ // We look for items without idle actions, and eliminate them from the list
+ lockItems();
+ _ctx->nIdleActions = 0;
+ _ctx->nRealItems = 0;
+ for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) {
+ _ctx->ord = itemGetOrderFromNum(_ctx->il[_ctx->i]);
+
+ if (_ctx->ord == -1)
+ continue;
+
+ _ctx->curItem = GLOBALS._lpmiItems + _ctx->ord;
+
+ _ctx->k = 0;
+ for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) {
+ if (_ctx->curItem->_action[_ctx->j]._num == 0xFF)
+ _ctx->k++;
+ }
+
+ _ctx->nIdleActions += _ctx->k;
+
+ if (_ctx->k == 0)
+ // We can remove this item from the list
+ _ctx->il[_ctx->i] = (uint32)NULL;
+ else
+ _ctx->nRealItems++;
+ }
+ unlockItems();
+
+ // If there is nothing left, we can exit
+ if (_ctx->nRealItems == 0) {
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+ _ctx->myThreads = (MYTHREAD *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nRealItems * sizeof(MYTHREAD));
+ if (_ctx->myThreads == NULL) {
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+
+ // We have established that there is at least one item that contains idle actions.
+ // Now we created the mirrored copies of the idle actions.
+ _ctx->myActions = (MYACTION *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, _ctx->nIdleActions * sizeof(MYACTION));
+ if (_ctx->myActions == NULL) {
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->il);
+ CORO_KILL_SELF();
+ return;
+ }
+
+ lockItems();
+ _ctx->k = 0;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->numitems; _ctx->i++) {
+ if (_ctx->il[_ctx->i] == 0)
+ continue;
+
+ _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->il[_ctx->i]);
+
+ for (_ctx->j = 0; _ctx->j < _ctx->curItem->_nActions; _ctx->j++) {
+ if (_ctx->curItem->_action[_ctx->j]._num == 0xFF) {
+ _ctx->myActions[_ctx->k]._nItem = _ctx->il[_ctx->i];
+ _ctx->myActions[_ctx->k]._nAction = _ctx->j;
+
+ _ctx->myActions[_ctx->k]._wTime = _ctx->curItem->_action[_ctx->j]._wTime;
+ _ctx->myActions[_ctx->k]._perc = _ctx->curItem->_action[_ctx->j]._perc;
+ _ctx->myActions[_ctx->k]._when = _ctx->curItem->_action[_ctx->j]._when;
+ _ctx->myActions[_ctx->k]._nCmds = _ctx->curItem->_action[_ctx->j]._nCmds;
+ memcpy(_ctx->myActions[_ctx->k]._cmdNum, _ctx->curItem->_action[_ctx->j]._cmdNum,
+ MAX_COMMANDS_PER_ACTION * sizeof(uint16));
+
+ _ctx->myActions[_ctx->k]._dwLastTime = g_vm->getTime();
+ _ctx->k++;
+ }
+ }
+ }
+
+ unlockItems();
+
+ // We don't need the item list anymore
+ globalDestroy(_ctx->il);
+
+
+ // Here's the main loop
+ while (1) {
+ // Searching for idle actions requiring time to execute
+ _ctx->curTime = g_vm->getTime();
+ _ctx->dwSleepTime = (uint32)-1L;
+
+ for (_ctx->k = 0;_ctx->k<_ctx->nIdleActions;_ctx->k++) {
+ if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) {
+ _ctx->dwSleepTime = 0;
+ break;
+ } else
+ _ctx->dwSleepTime = MIN(_ctx->dwSleepTime, _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime - _ctx->curTime);
+ }
+
+ // We fall alseep, but always checking that the event is set when prompted for closure
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, GLOBALS._hEndPollingLocations[id], _ctx->dwSleepTime, &_ctx->expired);
+
+ //if (_ctx->k == WAIT_OBJECT_0)
+ if (!_ctx->expired)
+ break;
+
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem != 0) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 0, &_ctx->delayExpired);
+
+ // if result == WAIT_OBJECT_0)
+ if (!_ctx->delayExpired)
+ _ctx->myThreads[_ctx->i]._nItem = 0;
+ }
+ }
+
+ _ctx->curTime = g_vm->getTime();
+
+ // Loop through all the necessary idle actions
+ for (_ctx->k = 0; _ctx->k < _ctx->nIdleActions; _ctx->k++) {
+ if (_ctx->curTime >= _ctx->myActions[_ctx->k]._dwLastTime + _ctx->myActions[_ctx->k]._wTime) {
+ _ctx->myActions[_ctx->k]._dwLastTime += _ctx->myActions[_ctx->k]._wTime;
+
+ // It's time to check to see if fortune is on the side of the idle action
+ byte randomVal = (byte)g_vm->_randomSource.getRandomNumber(99);
+ if (randomVal < _ctx->myActions[_ctx->k]._perc) {
+ // Check if there is an action running on the item
+ if ((GLOBALS._bExecutingAction) && (GLOBALS._nExecutingAction == _ctx->myActions[_ctx->k]._nItem))
+ continue;
+
+ // Check to see if there already another idle funning running on the item
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem == _ctx->myActions[_ctx->k]._nItem)
+ break;
+ }
+
+ if (_ctx->i < _ctx->nRealItems)
+ continue;
+
+ // Ok, we are the only ones :)
+ lockItems();
+ _ctx->curItem = GLOBALS._lpmiItems + itemGetOrderFromNum(_ctx->myActions[_ctx->k]._nItem);
+
+ // Check if there is a WhenExecute expression
+ _ctx->j=_ctx->myActions[_ctx->k]._nAction;
+ if (_ctx->curItem->_action[_ctx->j]._when != NULL) {
+ if (!evaluateExpression(_ctx->curItem->_action[_ctx->j]._when)) {
+ unlockItems();
+ continue;
+ }
+ }
+
+ // Ok, we can perform the action. For convenience, we do it in a new process
+ _ctx->newItem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem));
+ if (_ctx->newItem == false) {
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->myActions);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ memcpy(_ctx->newItem,_ctx->curItem, sizeof(MpalItem));
+ unlockItems();
+
+ // We copy the action in #0
+ //_ctx->newItem->Action[0].nCmds = _ctx->curItem->Action[_ctx->j].nCmds;
+ //memcpy(_ctx->newItem->Action[0].CmdNum,_ctx->curItem->Action[_ctx->j].CmdNum,_ctx->newItem->Action[0].nCmds*sizeof(_ctx->newItem->Action[0].CmdNum[0]));
+ _ctx->newItem->_dwRes = _ctx->j;
+
+ // We will create an action, and will provide the necessary details
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem == 0)
+ break;
+ }
+
+ _ctx->myThreads[_ctx->i]._nItem = _ctx->myActions[_ctx->k]._nItem;
+
+ // Create the process
+ if ((_ctx->myThreads[_ctx->i]._hThread = CoroScheduler.createProcess(ActionThread, &_ctx->newItem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE) {
+ //if ((_ctx->myThreads[_ctx->i]._hThread = (void*)_beginthread(ActionThread, 10240, (void *)_ctx->newItem)) == (void*)-1)
+ globalDestroy(_ctx->newItem);
+ globalDestroy(_ctx->myThreads);
+ globalDestroy(_ctx->myActions);
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // Skip all idle actions of the same item
+ }
+ }
+ }
+ }
+
+
+ // Set idle skip on
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[200], 0, 0, 0, 0);
+
+ for (_ctx->i = 0; _ctx->i < _ctx->nRealItems; _ctx->i++) {
+ if (_ctx->myThreads[_ctx->i]._nItem != 0) {
+ CORO_INVOKE_3(CoroScheduler.waitForSingleObject, _ctx->myThreads[_ctx->i]._hThread, 5000, &_ctx->delayExpired);
+
+ //if (result != WAIT_OBJECT_0)
+ //if (_ctx->delayExpired)
+ // TerminateThread(_ctx->MyThreads[_ctx->i].hThread, 0);
+
+ CoroScheduler.killMatchingProcess(_ctx->myThreads[_ctx->i]._hThread);
+ }
+ }
+
+ // Set idle skip off
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[201], 0, 0, 0, 0);
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Wait for the end of the dialog execution thread, and then restore global
+ * variables indicating that the dialogue has finished.
+ *
+ * @param param Pointer to a handle to the dialog
+ * @remarks This additional process is used, instead of clearing variables
+ * within the same dialog thread, because due to the recursive nature of a dialog,
+ * it would be difficult to know within it when the dialog is actually ending.
+ */
+void ShutUpDialogThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 pid = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, pid, CORO_INFINITE);
+
+ GLOBALS._bExecutingDialog = false;
+ GLOBALS._nExecutingDialog = 0;
+ GLOBALS._nExecutingChoice = 0;
+
+ CoroScheduler.setEvent(GLOBALS._hAskChoice);
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+void doChoice(CORO_PARAM, uint32 nChoice);
+
+
+/**
+ * Executes a group of the current dialog. Can 'be the Starting point of a process.
+ * @parm nGroup Number of the group to perform
+ */
+void GroupThread(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ LpMpalDialog dialog;
+ int i, j, k;
+ int type;
+ CORO_END_CONTEXT(_ctx);
+
+ uint32 nGroup = *(const uint32 *)param;
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Lock the _ctx->dialog
+ lockDialogs();
+
+ // Find the pointer to the current _ctx->dialog
+ _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Search inside the group requesting the _ctx->dialog
+ for (_ctx->i = 0; _ctx->dialog->_group[_ctx->i]._num != 0; _ctx->i++) {
+ if (_ctx->dialog->_group[_ctx->i]._num == nGroup) {
+ // Cycle through executing the commands of the group
+ for (_ctx->j = 0; _ctx->j < _ctx->dialog->_group[_ctx->i]._nCmds; _ctx->j++) {
+ _ctx->k = _ctx->dialog->_group[_ctx->i]._cmdNum[_ctx->j];
+
+ _ctx->type = _ctx->dialog->_command[_ctx->k]._type;
+ if (_ctx->type == 1) {
+ // Call custom function
+ CORO_INVOKE_4(GLOBALS._lplpFunctions[_ctx->dialog->_command[_ctx->k]._nCf],
+ _ctx->dialog->_command[_ctx->k]._arg1,
+ _ctx->dialog->_command[_ctx->k]._arg2,
+ _ctx->dialog->_command[_ctx->k]._arg3,
+ _ctx->dialog->_command[_ctx->k]._arg4
+ );
+
+ } else if (_ctx->type == 2) {
+ // Set a variable
+ lockVar();
+ varSetValue(_ctx->dialog->_command[_ctx->k]._lpszVarName, evaluateExpression(_ctx->dialog->_command[_ctx->k]._expr));
+ unlockVar();
+
+ } else if (_ctx->type == 3) {
+ // DoChoice: call the chosen function
+ CORO_INVOKE_1(doChoice, (uint32)_ctx->dialog->_command[_ctx->k]._nChoice);
+
+ } else {
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // WORKAROUND: Wait for events to pulse.
+ CORO_SLEEP(1);
+ }
+
+ // The gruop is finished, so we can return to the calling function.
+ // If the group was the first called, then the process will automatically
+ // end. Otherwise it returns to the caller method
+
+ return;
+ }
+ }
+
+ // If we are here, it means that we have not found the requested group
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Make a choice in the current dialog.
+ *
+ * @param nChoice Number of choice to perform
+ */
+void doChoice(CORO_PARAM, uint32 nChoice) {
+ CORO_BEGIN_CONTEXT;
+ LpMpalDialog dialog;
+ int i, j, k;
+ uint32 nGroup;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ // Lock the dialogs
+ lockDialogs();
+
+ // Get a pointer to the current dialog
+ _ctx->dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+
+ // Search the choice between those required in the dialog
+ for (_ctx->i = 0; _ctx->dialog->_choice[_ctx->i]._nChoice != 0; _ctx->i++) {
+ if (_ctx->dialog->_choice[_ctx->i]._nChoice == nChoice)
+ break;
+ }
+
+ // If nothing has been found, exit with an error
+ if (_ctx->dialog->_choice[_ctx->i]._nChoice == 0) {
+ // If we're here, we did not find the required choice
+ GLOBALS._mpalError = 1;
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // We've found the requested choice. Remember what in global variables
+ GLOBALS._nExecutingChoice = _ctx->i;
+
+ while (1) {
+ GLOBALS._nExecutingChoice = _ctx->i;
+
+ _ctx->k = 0;
+ // Calculate the expression of each selection, to see if they're active or inactive
+ for (_ctx->j = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._dwData != 0; _ctx->j++) {
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when == NULL) {
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1;
+ _ctx->k++;
+ } else if (evaluateExpression(_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._when)) {
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 1;
+ _ctx->k++;
+ } else
+ _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._curActive = 0;
+ }
+
+ // If there are no choices activated, then the dialog is finished.
+ if (_ctx->k == 0) {
+ unlockDialogs();
+ break;
+ }
+
+ // There are choices available to the user, so wait for them to make one
+ CoroScheduler.resetEvent(GLOBALS._hDoneChoice);
+ CoroScheduler.setEvent(GLOBALS._hAskChoice);
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hDoneChoice, CORO_INFINITE);
+
+ // Now that the choice has been made, we can run the groups associated with the choice tbontbtitq
+ _ctx->j = GLOBALS._nSelectedChoice;
+ for (_ctx->k = 0; _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k] != 0; _ctx->k++) {
+ _ctx->nGroup = _ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._wPlayGroup[_ctx->k];
+ CORO_INVOKE_1(GroupThread, &_ctx->nGroup);
+ }
+
+ // Control attribute
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 0)) {
+ // Bit 0 set: the end of the choice
+ unlockDialogs();
+ break;
+ }
+
+ if (_ctx->dialog->_choice[_ctx->i]._select[_ctx->j]._attr & (1 << 1)) {
+ // Bit 1 set: the end of the dialog
+ unlockDialogs();
+
+ CORO_KILL_SELF();
+ return;
+ }
+
+ // End of choic ewithout attributes. We must do it again
+ }
+
+ // If we're here, we found an end choice. Return to the caller group
+ return;
+
+ CORO_END_CODE;
+}
+
+
+/**
+ * Perform an action on a certain item.
+ *
+ * @param nAction Action number
+ * @param ordItem Index of the item in the items list
+ * @param dwParam Any parameter for the action.
+ * @returns Id of the process that was launched to perform the action, or
+ * CORO_INVALID_PID_VALUE if the action was not defined, or the item was inactive.
+ * @remarks You can get the index of an item from its number by using
+ * the itemGetOrderFromNum() function. The items list must first be locked
+ * by calling LockItem().
+ */
+static uint32 doAction(uint32 nAction, uint32 ordItem, uint32 dwParam) {
+ LpMpalItem item = GLOBALS._lpmiItems;
+ LpMpalItem newitem;
+
+ item+=ordItem;
+ Common::String buf = Common::String::format("Status.%u", item->_nObj);
+ if (varGetValue(buf.c_str()) <= 0)
+ return CORO_INVALID_PID_VALUE;
+
+ for (int i = 0; i < item->_nActions; i++) {
+ if (item->_action[i]._num != nAction)
+ continue;
+
+ if (item->_action[i]._wParm != dwParam)
+ continue;
+
+ if (item->_action[i]._when != NULL) {
+ if (!evaluateExpression(item->_action[i]._when))
+ continue;
+ }
+
+ // Now we find the right action to be performed
+ // Duplicate the item and copy the current action in #i into #0
+ newitem = (LpMpalItem)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalItem));
+ if (newitem == NULL)
+ return CORO_INVALID_PID_VALUE;
+
+ // In the new version number of the action in writing dwRes
+ Common::copy((byte *)item, (byte *)item + sizeof(MpalItem), (byte *)newitem);
+
+ //newitem->_action[0]._nCmds=item->_action[i]._nCmds;
+ //memcpy(newitem->_action[0]._cmdNum, item->_action[i]._cmdNum, newitem->Action[0].nCmds * sizeof(newitem->_action[0]._cmdNum[0]));
+
+ newitem->_dwRes = i;
+
+ // And finally we can laucnh the process that will execute the action,
+ // and a second process to free up the memory when the action is finished.
+
+ // !!! New process management
+ uint32 h;
+ if ((h = CoroScheduler.createProcess(ActionThread, &newitem, sizeof(LpMpalItem))) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ if (CoroScheduler.createProcess(ShutUpActionThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ GLOBALS._nExecutingAction = item->_nObj;
+ GLOBALS._bExecutingAction = true;
+
+ return h;
+ }
+
+ return CORO_INVALID_PID_VALUE;
+}
+
+/**
+ * Shows a dialog in a separate process.
+ *
+ * @param nDlgOrd The index of the dialog in the dialog list
+ * @param nGroup Number of the group to perform
+ * @returns The process Id of the process running the dialog
+ * or CORO_INVALID_PID_VALUE on error
+ * @remarks The dialogue runs in a thread created on purpose,
+ * so that must inform through an event and when 'necessary to you make a choice.
+ * The data on the choices may be obtained through various queries.
+ */
+static uint32 doDialog(uint32 nDlgOrd, uint32 nGroup) {
+ // Store the running dialog in a global variable
+ GLOBALS._nExecutingDialog = nDlgOrd;
+
+ // Enables the flag to indicate that there is' a running dialogue
+ GLOBALS._bExecutingDialog = true;
+
+ CoroScheduler.resetEvent(GLOBALS._hAskChoice);
+ CoroScheduler.resetEvent(GLOBALS._hDoneChoice);
+
+ // Create a thread that performs the dialogue group
+
+ // Create the process
+ uint32 h;
+ if ((h = CoroScheduler.createProcess(GroupThread, &nGroup, sizeof(uint32))) == CORO_INVALID_PID_VALUE)
+ return CORO_INVALID_PID_VALUE;
+
+ // Create a thread that waits until the end of the dialog process, and will restore the global variables
+ if (CoroScheduler.createProcess(ShutUpDialogThread, &h, sizeof(uint32)) == CORO_INVALID_PID_VALUE) {
+ // Something went wrong, so kill the previously started dialog process
+ CoroScheduler.killMatchingProcess(h);
+ return CORO_INVALID_PID_VALUE;
+ }
+
+ return h;
+}
+
+
+/**
+ * Takes note of the selection chosen by the user, and warns the process that was running
+ * the box that it can continue.
+ *
+ * @param nChoice Number of choice that was in progress
+ * @param dwData Since combined with select selection
+ * @returns True if everything is OK, false on failure
+ */
+bool doSelection(uint32 i, uint32 dwData) {
+ LpMpalDialog dialog = GLOBALS._lpmdDialogs + GLOBALS._nExecutingDialog;
+ int j;
+
+ for (j = 0; dialog->_choice[i]._select[j]._dwData != 0; j++) {
+ if (dialog->_choice[i]._select[j]._dwData == dwData && dialog->_choice[i]._select[j]._curActive != 0)
+ break;
+ }
+
+ if (dialog->_choice[i]._select[j]._dwData == 0)
+ return false;
+
+ GLOBALS._nSelectedChoice = j;
+ CoroScheduler.setEvent(GLOBALS._hDoneChoice);
+ return true;
+}
+
+
+/**
+ * @defgroup Exported functions
+ */
+//@{
+
+/**
+ * Initializes the MPAL library and opens the .MPC file, which will be used for all queries.
+ *
+ * @param lpszMpcFileName Name of the MPC file
+ * @param lpszMprFileName Name of the MPR file
+ * @param lplpcfArray Array of pointers to custom functions.
+ * @returns True if everything is OK, false on failure
+ */
+bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName,
+ LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings) {
+ byte buf[5];
+ byte *cmpbuf;
+
+ // Save the array of custom functions
+ GLOBALS._lplpFunctions = lplpcfArray;
+ GLOBALS._lplpFunctionStrings = lpcfStrings;
+
+ // OPen the MPC file for reading
+ Common::File hMpc;
+ if (!hMpc.open(lpszMpcFileName))
+ return false;
+
+ // Read and check the header
+ uint32 nBytesRead = hMpc.read(buf, 5);
+ if (nBytesRead != 5)
+ return false;
+
+ if (buf[0] != 'M' || buf[1] != 'P' || buf[2] != 'C' || buf[3] != 0x20)
+ return false;
+
+ bool bCompress = buf[4];
+
+ // Reads the size of the uncompressed file, and allocate memory
+ uint32 dwSizeDecomp = hMpc.readUint32LE();
+ if (hMpc.err())
+ return false;
+
+ byte *lpMpcImage = (byte *)globalAlloc(GMEM_FIXED, dwSizeDecomp + 16);
+ if (lpMpcImage == NULL)
+ return false;
+
+ if (bCompress) {
+ // Get the compressed size and read the data in
+ uint32 dwSizeComp = hMpc.readUint32LE();
+ if (hMpc.err())
+ return false;
+
+ cmpbuf = (byte *)globalAlloc(GMEM_FIXED, dwSizeComp);
+ if (cmpbuf == NULL)
+ return false;
+
+ nBytesRead = hMpc.read(cmpbuf, dwSizeComp);
+ if (nBytesRead != dwSizeComp)
+ return false;
+
+ // Decompress the data
+ lzo1x_decompress(cmpbuf, dwSizeComp, lpMpcImage, &nBytesRead);
+ if (nBytesRead != dwSizeDecomp)
+ return false;
+
+ globalDestroy(cmpbuf);
+ } else {
+ // If the file is not compressed, we directly read in the data
+ nBytesRead = hMpc.read(lpMpcImage, dwSizeDecomp);
+ if (nBytesRead != dwSizeDecomp)
+ return false;
+ }
+
+ // Close the file
+ hMpc.close();
+
+ // Process the data
+ if (parseMpc(lpMpcImage) == false)
+ return false;
+
+ globalDestroy(lpMpcImage);
+
+ // Open the MPR file
+ if (!GLOBALS._hMpr.open(lpszMprFileName))
+ return false;
+
+ // Seek to the end of the file to read overall information
+ GLOBALS._hMpr.seek(-12, SEEK_END);
+
+ uint32 dwSizeComp = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ GLOBALS._nResources = GLOBALS._hMpr.readUint32LE();
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ nBytesRead = GLOBALS._hMpr.read(buf, 4);
+ if (GLOBALS._hMpr.err())
+ return false;
+
+ if (buf[0] !='E' || buf[1] != 'N' || buf[2] != 'D' || buf[3] != '0')
+ return false;
+
+ // Move to the start of the resources header
+ GLOBALS._hMpr.seek(-(12 + (int)dwSizeComp), SEEK_END);
+
+ GLOBALS._lpResources = (uint32 *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, GLOBALS._nResources * 8);
+ if (GLOBALS._lpResources == NULL)
+ return false;
+
+ cmpbuf = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, dwSizeComp);
+ if (cmpbuf == NULL)
+ return false;
+
+ nBytesRead = GLOBALS._hMpr.read(cmpbuf, dwSizeComp);
+ if (nBytesRead != dwSizeComp)
+ return false;
+
+ lzo1x_decompress((const byte *)cmpbuf, dwSizeComp, (byte *)GLOBALS._lpResources, (uint32 *)&nBytesRead);
+ if (nBytesRead != (uint32)GLOBALS._nResources * 8)
+ return false;
+
+ globalDestroy(cmpbuf);
+
+ // Reset back to the start of the file, leaving it open
+ GLOBALS._hMpr.seek(0, SEEK_SET);
+
+ // There is no action or dialog running by default
+ GLOBALS._bExecutingAction = false;
+ GLOBALS._bExecutingDialog = false;
+
+ // There's no polling location
+ Common::fill(GLOBALS._nPollingLocations, GLOBALS._nPollingLocations + MAXPOLLINGLOCATIONS, 0);
+
+ // Create the event that will be used to co-ordinate making choices and choices finishing
+ GLOBALS._hAskChoice = CoroScheduler.createEvent(true, false);
+ GLOBALS._hDoneChoice = CoroScheduler.createEvent(true, false);
+
+ return true;
+}
+
+/**
+ * Frees resources allocated by the MPAL subsystem
+ */
+void mpalFree() {
+ // Free the resource list
+ globalDestroy(GLOBALS._lpResources);
+}
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns numeric results.
+ */
+uint32 mpalQueryDWORD(uint16 wQueryType, ...) {
+ Common::String buf;
+ uint32 dwRet = 0;
+ char *n;
+
+ va_list v;
+ va_start(v, wQueryType);
+
+ GLOBALS._mpalError = OK;
+
+ if (wQueryType == MPQ_VERSION) {
+
+ /*
+ * uint32 mpalQuery(MPQ_VERSION);
+ */
+ dwRet = HEX_VERSION;
+
+ } else if (wQueryType == MPQ_GLOBAL_VAR) {
+ /*
+ * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName);
+ */
+ lockVar();
+ dwRet = (uint32)varGetValue(GETARG(char *));
+ unlockVar();
+
+ } else if (wQueryType == MPQ_MESSAGE) {
+ /*
+ * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg);
+ */
+ error("mpalQuery(MPQ_MESSAGE, uint32 nMsg) used incorrect method variant");
+
+
+ } else if (wQueryType == MPQ_ITEM_PATTERN) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem);
+ */
+ lockVar();
+ buf = Common::String::format("Pattern.%u", GETARG(uint32));
+ dwRet = (uint32)varGetValue(buf.c_str());
+ unlockVar();
+
+ } else if (wQueryType == MPQ_LOCATION_SIZE) {
+ /*
+ * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord);
+ */
+ lockLocations();
+ int x = locGetOrderFromNum(GETARG(uint32));
+ int y = GETARG(uint32);
+ if (x != -1) {
+ if (y == MPQ_X)
+ dwRet = GLOBALS._lpmlLocations[x]._dwXlen;
+ else if (y == MPQ_Y)
+ dwRet = GLOBALS._lpmlLocations[x]._dwYlen;
+ else
+ GLOBALS._mpalError = 1;
+ } else
+ GLOBALS._mpalError = 1;
+
+ unlockLocations();
+
+ } else if (wQueryType == MPQ_LOCATION_IMAGE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc);
+ */
+ error("mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc) used incorrect variant");
+
+ } else if (wQueryType == MPQ_RESOURCE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes);
+ */
+ error("mpalQuery(MPQ_RESOURCE, uint32 dwRes) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_LIST) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc);
+ */
+ error("mpalQuery(MPQ_ITEM_LIST, uint32 nLoc) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_DATA) {
+ /*
+ * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_DATA, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) {
+ /*
+ * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ dwRet = (uint32)false;
+ else
+ dwRet = (uint32)true;
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_ITEM_NAME) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char * lpszName);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ n = GETARG(char *);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ n[0]='\0';
+ else {
+ lockItems();
+ int y = itemGetOrderFromNum(x);
+ memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE);
+ unlockItems();
+ }
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_DIALOG_PERIOD) {
+ /*
+ * char *mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod);
+ */
+ error("mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTLIST) {
+ /*
+ * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice);
+ */
+ error("mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTION) {
+ /*
+ * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData);
+ */
+ lockDialogs();
+ int x = GETARG(uint32);
+ int y = GETARG(uint32);
+ dwRet = (uint32)doSelection(x, y);
+
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DO_ACTION) {
+ /*
+ * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam);
+ */
+ lockItems();
+ lockVar();
+ int x = GETARG(uint32);
+ int z = GETARG(uint32);
+ int y = itemGetOrderFromNum(z);
+ if (y != -1) {
+ dwRet = doAction(x, y, GETARG(uint32));
+ } else {
+ dwRet = CORO_INVALID_PID_VALUE;
+ GLOBALS._mpalError = 1;
+ }
+
+ unlockVar();
+ unlockItems();
+
+ } else if (wQueryType == MPQ_DO_DIALOG) {
+ /*
+ * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup);
+ */
+ if (!GLOBALS._bExecutingDialog) {
+ lockDialogs();
+
+ int x = dialogGetOrderFromNum(GETARG(uint32));
+ int y = GETARG(uint32);
+ dwRet = doDialog(x, y);
+ unlockDialogs();
+ }
+ } else {
+ /*
+ * DEFAULT -> ERROR
+ */
+ GLOBALS._mpalError = 1;
+ }
+
+ va_end(v);
+ return dwRet;
+}
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns a pointer or handle.
+ */
+MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...) {
+ char *n;
+ Common::String buf;
+ va_list v;
+ va_start(v, wQueryType);
+ void *hRet = NULL;
+
+ GLOBALS._mpalError = OK;
+
+ if (wQueryType == MPQ_VERSION) {
+ /*
+ * uint32 mpalQuery(MPQ_VERSION);
+ */
+ error("mpalQuery(MPQ_VERSION) used incorrect variant");
+
+ } else if (wQueryType == MPQ_GLOBAL_VAR) {
+ /*
+ * uint32 mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName);
+ */
+ error("mpalQuery(MPQ_GLOBAL_VAR, char * lpszVarName) used incorrect variant");
+
+ } else if (wQueryType == MPQ_MESSAGE) {
+ /*
+ * char * mpalQuery(MPQ_MESSAGE, uint32 nMsg);
+ */
+ LockMsg();
+ hRet = DuplicateMessage(msgGetOrderFromNum(GETARG(uint32)));
+ UnlockMsg();
+
+ } else if (wQueryType == MPQ_ITEM_PATTERN) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_PATTERN, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_LOCATION_SIZE) {
+ /*
+ * uint32 mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord);
+ */
+ error("mpalQuery(MPQ_LOCATION_SIZE, uint32 nLoc, uint32 dwCoord) used incorrect variant");
+
+ } else if (wQueryType == MPQ_LOCATION_IMAGE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_LOCATION_IMAGE, uint32 nLoc);
+ */
+ lockLocations();
+ int x = locGetOrderFromNum(GETARG(uint32));
+ hRet = resLoad(GLOBALS._lpmlLocations[x]._dwPicRes);
+ unlockLocations();
+
+ } else if (wQueryType == MPQ_RESOURCE) {
+ /*
+ * HGLOBAL mpalQuery(MPQ_RESOURCE, uint32 dwRes);
+ */
+ hRet = resLoad(GETARG(uint32));
+
+ } else if (wQueryType == MPQ_ITEM_LIST) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_LIST, uint32 nLoc);
+ */
+ lockVar();
+ hRet = GetItemList(GETARG(uint32));
+ lockVar();
+
+ } else if (wQueryType == MPQ_ITEM_DATA) {
+ /*
+ * LpItem mpalQuery(MPQ_ITEM_DATA, uint32 nItem);
+ */
+ lockItems();
+ hRet = getItemData(itemGetOrderFromNum(GETARG(uint32)));
+ unlockItems();
+
+ } else if (wQueryType == MPQ_ITEM_IS_ACTIVE) {
+ /*
+ * bool mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem);
+ */
+ error("mpalQuery(MPQ_ITEM_IS_ACTIVE, uint32 nItem) used incorrect variant");
+
+ } else if (wQueryType == MPQ_ITEM_NAME) {
+ /*
+ * uint32 mpalQuery(MPQ_ITEM_NAME, uint32 nItem, char *lpszName);
+ */
+ lockVar();
+ int x = GETARG(uint32);
+ n = GETARG(char *);
+ buf = Common::String::format("Status.%u", x);
+ if (varGetValue(buf.c_str()) <= 0)
+ n[0] = '\0';
+ else {
+ lockItems();
+ int y = itemGetOrderFromNum(x);
+ memcpy(n, (char *)(GLOBALS._lpmiItems + y)->_lpszDescribe, MAX_DESCRIBE_SIZE);
+ unlockItems();
+ }
+
+ unlockVar();
+
+ } else if (wQueryType == MPQ_DIALOG_PERIOD) {
+ /*
+ * char * mpalQuery(MPQ_DIALOG_PERIOD, uint32 nDialog, uint32 nPeriod);
+ */
+ lockDialogs();
+ int y = GETARG(uint32);
+ hRet = duplicateDialogPeriod(y);
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ error("mpalQuery(MPQ_DIALOG_WAITFORCHOICE) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTLIST) {
+ /*
+ * uint32 *mpalQuery(MPQ_DIALOG_SELECTLIST, uint32 nChoice);
+ */
+ lockDialogs();
+ hRet = getSelectList(GETARG(uint32));
+ unlockDialogs();
+
+ } else if (wQueryType == MPQ_DIALOG_SELECTION) {
+ /*
+ * bool mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData);
+ */
+ error("mpalQuery(MPQ_DIALOG_SELECTION, uint32 nChoice, uint32 dwData) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DO_ACTION) {
+ /*
+ * int mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam);
+ */
+ error("mpalQuery(MPQ_DO_ACTION, uint32 nAction, uint32 nItem, uint32 dwParam) used incorrect variant");
+
+ } else if (wQueryType == MPQ_DO_DIALOG) {
+ /*
+ * int mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup);
+ */
+ error("mpalQuery(MPQ_DO_DIALOG, uint32 nDialog, uint32 nGroup) used incorrect variant");
+ } else {
+ /*
+ * DEFAULT -> ERROR
+ */
+ GLOBALS._mpalError = 1;
+ }
+
+ va_end(v);
+ return hRet;
+}
+
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that needs to run within a co-routine context.
+ */
+void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...) {
+ CORO_BEGIN_CONTEXT;
+ uint32 dwRet;
+ CORO_END_CONTEXT(_ctx);
+
+ va_list v;
+ va_start(v, dwRet);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ if (wQueryType == MPQ_DIALOG_WAITFORCHOICE) {
+ /*
+ * void mpalQuery(MPQ_DIALOG_WAITFORCHOICE);
+ */
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._hAskChoice, CORO_INFINITE);
+
+ // WORKAROUND: Introduce a single frame delay so that if there are multiple actions running,
+ // they all have time to be signalled before resetting the event. This fixes a problem where
+ // if you try to use the 'shrimp' on the parrot a second time after trying to first use it
+ // whilst the parrot was talking, the cursor wouldn't be re-enabled afterwards
+ CORO_SLEEP(1);
+
+ CoroScheduler.resetEvent(GLOBALS._hAskChoice);
+
+ if (GLOBALS._bExecutingDialog)
+ *dwRet = (uint32)GLOBALS._nExecutingChoice;
+ else
+ *dwRet = (uint32)((int)-1);
+ } else {
+ error("mpalQueryCORO called with unsupported query type");
+ }
+
+ CORO_END_CODE;
+
+ va_end(v);
+}
+
+/**
+ * Returns the current MPAL error code
+ *
+ * @returns Error code
+ */
+uint32 mpalGetError() {
+ return GLOBALS._mpalError;
+}
+
+/**
+ * Execute a script. The script runs on multitasking by a thread.
+ *
+ * @param nScript Script number to run
+ * @returns TRUE if the script 'was launched, FALSE on failure
+ */
+bool mpalExecuteScript(int nScript) {
+ LockScripts();
+ int n = scriptGetOrderFromNum(nScript);
+ LpMpalScript s = (LpMpalScript)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(MpalScript));
+ if (s == NULL)
+ return false;
+
+ memcpy(s, GLOBALS._lpmsScripts + n, sizeof(MpalScript));
+ unlockScripts();
+
+ // !!! New process management
+ if (CoroScheduler.createProcess(ScriptThread, &s, sizeof(LpMpalScript)) == CORO_INVALID_PID_VALUE)
+ return false;
+
+ return true;
+}
+
+/**
+ * Install a custom routine That will be called by MPAL every time the pattern
+ * of an item has been changed.
+ *
+ * @param lpiifCustom Custom function to install
+ */
+void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCus) {
+ GLOBALS._lpiifCustom = lpiifCus;
+}
+
+/**
+ * Process the idle actions of the items on one location.
+ *
+ * @param nLoc Number of the location whose items must be processed
+ * for idle actions.
+ * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit.
+ * @remarks The maximum number of locations that can be polled
+ * simultaneously is defined defined by MAXPOLLINGFUNCIONS
+ */
+bool mpalStartIdlePoll(int nLoc) {
+ for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) {
+ if (GLOBALS._nPollingLocations[i] == (uint32)nLoc)
+ return false;
+ }
+
+ for (uint32 i = 0; i < MAXPOLLINGLOCATIONS; i++) {
+ if (GLOBALS._nPollingLocations[i] == 0) {
+ GLOBALS._nPollingLocations[i] = nLoc;
+
+ GLOBALS._hEndPollingLocations[i] = CoroScheduler.createEvent(true, false);
+// !!! New process management
+ if ((GLOBALS._pollingThreads[i] = CoroScheduler.createProcess(LocationPollThread, &i, sizeof(uint32))) == CORO_INVALID_PID_VALUE)
+// if ((GLOBALS.hEndPollingLocations[i] = (void*)_beginthread(LocationPollThread, 10240, (void *)i))= = (void*)-1)
+ return false;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Stop processing the idle actions of the items on one location.
+ *
+ * @param nLo Number of the location
+ * @returns TRUE if all OK, FALSE if the specified location was not
+ * in the process of polling
+ */
+void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result) {
+ CORO_BEGIN_CONTEXT;
+ int i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ for (_ctx->i = 0; _ctx->i < MAXPOLLINGLOCATIONS; _ctx->i++) {
+ if (GLOBALS._nPollingLocations[_ctx->i] == (uint32)nLoc) {
+ CoroScheduler.setEvent(GLOBALS._hEndPollingLocations[_ctx->i]);
+
+ CORO_INVOKE_2(CoroScheduler.waitForSingleObject, GLOBALS._pollingThreads[_ctx->i], CORO_INFINITE);
+
+ CoroScheduler.closeEvent(GLOBALS._hEndPollingLocations[_ctx->i]);
+ GLOBALS._nPollingLocations[_ctx->i] = 0;
+
+ if (result)
+ *result = true;
+ return;
+ }
+ }
+
+ if (result)
+ *result = false;
+
+ CORO_END_CODE;
+}
+
+/**
+ * Retrieve the length of a save state
+ *
+ * @returns Length in bytes
+ */
+int mpalGetSaveStateSize() {
+ return GLOBALS._nVars * sizeof(MpalVar) + 4;
+}
+
+/**
+ * Store the save state into a buffer. The buffer must be
+ * length at least the size specified with mpalGetSaveStateSize
+ *
+ * @param buf Buffer where to store the state
+ */
+void mpalSaveState(byte *buf) {
+ lockVar();
+ WRITE_LE_UINT32(buf, GLOBALS._nVars);
+ memcpy(buf + 4, (byte *)GLOBALS._lpmvVars, GLOBALS._nVars * sizeof(MpalVar));
+ unlockVar();
+}
+
+
+/**
+ * Load a save state from a buffer.
+ *
+ * @param buf Buffer where to store the state
+ * @returns Length of the state buffer in bytes
+ */
+int mpalLoadState(byte *buf) {
+ // We must destroy and recreate all the variables
+ globalFree(GLOBALS._hVars);
+
+ GLOBALS._nVars = READ_LE_UINT32(buf);
+
+ GLOBALS._hVars = globalAllocate(GMEM_ZEROINIT | GMEM_MOVEABLE, GLOBALS._nVars * sizeof(MpalVar));
+ lockVar();
+ memcpy((byte *)GLOBALS._lpmvVars, buf + 4, GLOBALS._nVars * sizeof(MpalVar));
+ unlockVar();
+
+ return GLOBALS._nVars * sizeof(MpalVar) + 4;
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/mpal.h b/engines/tony/mpal/mpal.h
new file mode 100644
index 0000000000..5e1b02b3fc
--- /dev/null
+++ b/engines/tony/mpal/mpal.h
@@ -0,0 +1,518 @@
+/* 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
+ */
+
+
+/****************************************************************************\
+* General Introduction
+\****************************************************************************/
+
+/*
+ * MPAL (MultiPurpose Adventure Language) is a high level language
+ * for the definition of adventure. Through the use of MPAL you can describe
+ * storyboard the adventure, and then use it with any user interface.
+ * In fact, unlike many other similar products, MPAL is not programmed through
+ * the whole adventure, but are defined only the locations, objects, as they may
+ * interact with each other, etc.. thus making MPAL useful for any type of adventure.
+ */
+
+/****************************************************************************\
+* Structure
+\****************************************************************************/
+
+/*
+ * MPAL consists of two main files: MPAL.DLL and MPAL.H
+ * The first is the DLL that contains the code to interface with MPAL
+ * adventures, the second is the header that defines the prototypes
+ * functions. MPAL is compiled for Win32, and it can therefore be used with
+ * any compiler that supports Win32 DLL (Watcom C++, Visual C++,
+ * Delphi, etc.), and therefore compatible with both Windows 95 and Windows NT.
+ *
+ * To use the DLL, and 'obviously need to create a library for symbols to export.
+ *
+ */
+
+
+/****************************************************************************\
+* Custom Functions
+\****************************************************************************/
+
+/*
+ * A custom function and a function specified by the program that uses the
+ * library, to perform the particular code. The custom functions are
+ * retrieved from the library as specified in the source MPAL, and in particular
+ * in defining the behavior of an item with some action.
+ *
+ * To use the custom functions, you need to prepare an array of
+ * pointers to functions (such as using the type casting LPCUSTOMFUNCTION,
+ * (defined below), and pass it as second parameter to mpalInit (). Note you
+ * must specify the size of the array, as elements of pointers and which do not
+ * contain the same: the library will call it only those functions specified in
+ * the source MPAL. It can be useful, for debugging reasons, do not bet
+ * the shares of arrays used to debugging function, to avoid unpleasant crash,
+ * if it has been made an error in source and / or some oversight in the code.
+ *
+ */
+
+#ifndef TONY_MPAL_H
+#define TONY_MPAL_H
+
+#include "common/scummsys.h"
+#include "common/coroutines.h"
+#include "common/rect.h"
+#include "common/str.h"
+#include "tony/mpal/memory.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Macro definitions and structures
+\****************************************************************************/
+
+// OK value for the error codes
+#define OK 0
+
+#define MAXFRAMES 400 // frame animation of an object
+#define MAXPATTERN 40 // pattern of animation of an object
+#define MAXPOLLINGLOCATIONS 64
+
+#define GETARG(type) va_arg(v, type)
+
+/**
+ * Macro for use with queries that may refer to X and Y co-ordinates
+ */
+enum QueryCoordinates {
+ MPQ_X,
+ MPQ_Y
+};
+
+/**
+ * Query can be used with mpalQuery methods. In practice corresponds all claims
+ * that can do at the library
+ */
+enum QueryTypes {
+ // General Query
+ MPQ_VERSION = 10,
+
+ MPQ_GLOBAL_VAR = 50,
+ MPQ_RESOURCE,
+ MPQ_MESSAGE,
+
+ // Query on leases
+ MPQ_LOCATION_IMAGE = 100,
+ MPQ_LOCATION_SIZE,
+
+ // Queries about items
+ MPQ_ITEM_LIST = 200,
+ MPQ_ITEM_DATA,
+ MPQ_ITEM_PATTERN,
+ MPQ_ITEM_NAME,
+ MPQ_ITEM_IS_ACTIVE,
+
+ // Query dialog
+ MPQ_DIALOG_PERIOD = 300,
+ MPQ_DIALOG_WAITFORCHOICE,
+ MPQ_DIALOG_SELECTLIST,
+ MPQ_DIALOG_SELECTION,
+
+ // Query execution
+ MPQ_DO_ACTION = 400,
+ MPQ_DO_DIALOG
+};
+
+/**
+ * Framework to manage the animation of an item
+ */
+typedef struct {
+ char *_frames[MAXFRAMES];
+ Common::Rect _frameslocations[MAXFRAMES];
+ Common::Rect _bbox[MAXFRAMES];
+ short _pattern[MAXPATTERN][MAXFRAMES];
+ short _speed;
+ char _numframe;
+ char _numpattern;
+ char _curframe;
+ char _curpattern;
+ short _destX, _destY;
+ signed char _destZ;
+ short _objectID;
+} Item;
+typedef Item *LpItem;
+
+
+/**
+ * Define a custom function, to use the language MPAL to perform various controls as a result of an action
+ */
+typedef void (*LPCUSTOMFUNCTION)(CORO_PARAM, uint32, uint32, uint32, uint32);
+typedef LPCUSTOMFUNCTION *LPLPCUSTOMFUNCTION;
+
+/**
+ *
+ * Define an IRQ of an item that is called when the pattern changes or the status of an item
+ */
+typedef void (*LPITEMIRQFUNCTION)(uint32, int, int);
+typedef LPITEMIRQFUNCTION* LPLPITEMIRQFUNCTION;
+
+/**
+ * @defgroup Macrofunctions query
+ *
+ * The following are defines used for simplifying calling the mpalQuery variants
+ */
+//@{
+
+/**
+ * Gets the current version of MPAL
+ *
+ * @returns Version number (0x1232 = 1.2.3b)
+ */
+#define mpalQueryVersion() \
+ (uint16)mpalQueryDWORD(MPQ_VERSION)
+
+/**
+ * Gets the numerical value of a global variable
+ *
+ * @param lpszVarName Variable name (ASCIIZ)
+ * @returns Global variable value
+ * @remarks This query was implemented for debugging. The program,
+ * if well designed, should not need to access variables from
+ * within the library.
+ */
+#define mpalQueryGlobalVar(lpszVarName) \
+ mpalQueryDWORD(MPQ_GLOBAL_VAR, (const char *)(lpszVarName))
+
+
+/**
+ * Provides access to a resource inside the .MPC file
+ *
+ * @param dwResId Resource Id
+ * @returns Handle to a memory area containing the resource, ready for use.
+ */
+#define mpalQueryResource(dwResId) \
+ mpalQueryHANDLE(MPQ_RESOURCE, (uint32)(dwResId))
+
+
+/**
+ * Returns a message.
+ *
+ * @param nMsg Message number
+ * @returns ASCIIZ message
+ * @remarks The returned pointer must be freed via the memory manager
+* after use. The message will be in ASCIIZ format.
+*/
+#define mpalQueryMessage(nMsg) \
+ (char *)mpalQueryHANDLE(MPQ_MESSAGE, (uint32)(nMsg))
+
+
+/**
+ * Provides a location image
+ * @return Returns a picture handle
+ */
+#define mpalQueryLocationImage(nLoc) \
+ mpalQueryHANDLE(MPQ_LOCATION_IMAGE, (uint32)(nLoc))
+
+
+/**
+ * Request the x or y size of a location in pixels
+ *
+ * @param nLoc Location number
+ * @param dwCoord MPQ_X or MPQ_Y coordinate to retrieve
+ * @returns Size
+ */
+#define mpalQueryLocationSize(nLoc, dwCoord) \
+ mpalQueryDWORD(MPQ_LOCATION_SIZE, (uint32)(nLoc), (uint32)(dwCoord))
+
+
+/**
+ * Provides the list of objects in a location.
+ *
+ * @param nLoc Location number
+ * @returns List of objects (accessible by Item [0], Item [1], etc.)
+ */
+// TODO: Determine if this is endian safe
+#define mpalQueryItemList(nLoc) \
+ (uint32 *)mpalQueryHANDLE(MPQ_ITEM_LIST, (uint32)(nLoc))
+
+
+/**
+ * Provides information on an item
+ *
+ * @param nItem Item number
+ * @returns Structure filled with requested information
+ */
+#define mpalQueryItemData(nItem) \
+ (LpItem)mpalQueryHANDLE(MPQ_ITEM_DATA, (uint32)(nItem))
+
+
+/**
+ * Provides the current pattern of an item
+ *
+ * @param nItem Item number
+ * @returns Number of animation patterns to be executed.
+ * @remarks By default, the pattern of 0 indicates that we should do nothing.
+ */
+#define mpalQueryItemPattern(nItem) \
+ mpalQueryDWORD(MPQ_ITEM_PATTERN, (uint32)(nItem))
+
+
+/**
+ * Returns true if an item is active
+ *
+ * @param nItem Item number
+ * @returns TRUE if the item is active, FALSE otherwise
+ */
+#define mpalQueryItemIsActive(nItem) \
+ (bool)mpalQueryDWORD(MPQ_ITEM_IS_ACTIVE, (uint32)(nItem))
+
+
+/**
+ * Returns the name of an item
+ *
+ * @param nItem Item number
+ * @param lpszName Pointer to a buffer of at least 33 bytes
+ * that will be filled with the name
+ * @remarks If the item is not active (ie. if its status or number
+ * is less than or equal to 0), the string will be empty.
+ */
+#define mpalQueryItemName(nItem, lpszName) \
+ mpalQueryHANDLE(MPQ_ITEM_NAME, (uint32)(nItem), (char *)(lpszName))
+
+
+/**
+ * Returns a sentence of dialog.
+ *
+ * @param nDialog Dialog number
+ * @param nPeriod Number of words
+ * @returns A pointer to the string of words, or NULL on failure.
+ * @remarks The string must be freed after use using the memory manager.
+ * Unlike normal messages, the sentences of dialogue are formed by a single
+ * string terminated with 0.
+ */
+#define mpalQueryDialogPeriod(nPeriod) \
+ (char *)mpalQueryHANDLE(MPQ_DIALOG_PERIOD, (uint32)(nPeriod))
+
+
+/**
+ * Wait until the moment in which the need is signaled to make a choice by the user.
+ * @returns Number of choice to be made, or -1 if the dialogue is finished.
+ */
+#define mpalQueryDialogWaitForChoice(dwRet) \
+ CORO_INVOKE_2(mpalQueryCORO, MPQ_DIALOG_WAITFORCHOICE, dwRet)
+
+/**
+ * Requires a list of various options for some choice within the current dialog.
+ *
+ * @param nChoice Choice number
+ * @returns A pointer to an array containing the data matched to each option.
+ * @remarks The figure 'a uint32 specified in the source to which MPAL
+ * You can assign meaning that the more' suits.
+ * The pointer msut be freed after use using the memory memory.
+ */
+#define mpalQueryDialogSelectList(nChoice) \
+ (uint32 *)mpalQueryHANDLE(MPQ_DIALOG_SELECTLIST, (uint32)(nChoice))
+
+/**
+ * Warns the library that the user has selected, in a certain choice of the current dialog,
+ * corresponding option at a certain given.
+ *
+ * @param nChoice Choice number of the choice that was in progress
+ * @param dwData Option that was selected by the user.
+ * @returns TRUE if all OK, FALSE on failure.
+ * @remarks After execution of this query, MPAL continue
+ * Groups according to the execution of the dialogue. And necessary so the game
+ * remains on hold again for another chosen by mpalQueryDialogWaitForChoice ().
+ */
+#define mpalQueryDialogSelection(nChoice, dwData) \
+ (bool)mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
+
+#define mpalQueryDialogSelectionDWORD(nChoice, dwData) \
+ mpalQueryDWORD(MPQ_DIALOG_SELECTION, (uint32)(nChoice), (uint32)(dwData))
+
+/**
+ * Warns the library an action was performed on a Object.
+ * The library will call custom functions, if necessary.
+ *
+ * @param nAction Action number
+ * @param nItem Item number
+ * @param dwParam Action parameter
+ * @returns Handle to the thread that is performing the action, or CORO_INVALID_PID_VALUE
+ * if the action is not defined for the item, or the item is inactive.
+ * @remarks The parameter is used primarily to implement actions
+ * as "U.S." involving two objects together. The action will be executed only
+ * if the item is active, ie if its status is a positive number greater than 0.
+ */
+#define mpalQueryDoAction(nAction, nItem, dwParam) \
+ mpalQueryDWORD(MPQ_DO_ACTION, (uint32)(nAction), (uint32)(nItem), (uint32)(dwParam))
+
+/**
+ * Warns the library a dialogue was required.
+ *
+ * @param nDialog Dialog number
+ * @param nGroup Group number to use
+ * @returns Handle to the thread that is running the box, or
+ * CORO_INVALID_PID_VALUE if the dialogue does not exist.
+ */
+#define mpalQueryDoDialog(nDialog, nGroup) \
+ mpalQueryDWORD(MPQ_DO_DIALOG, (uint32)(nDialog), (uint32)(nGroup))
+
+/**
+ * @defgroup Functions exported to the main game
+ */
+//@{
+
+/**
+ * Initializes the MPAL library, and opens an .MPC file, which will be 'used for all queries
+ * @param lpszMpcFileName Name of the .MPC file, including extension
+ * @param lpszMprFileName Name of the .MPR file, including extension
+ * @param lplpcfArray Array of pointers to custom functions
+ * @returns TRUE if all OK, FALSE on failure
+ */
+bool mpalInit(const char *lpszFileName, const char *lpszMprFileName,
+ LPLPCUSTOMFUNCTION lplpcfArray, Common::String *lpcfStrings);
+
+/**
+ * Frees resources allocated by the MPAL subsystem
+ */
+void mpalFree();
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns numeric results.
+ */
+uint32 mpalQueryDWORD(uint16 wQueryType, ...);
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that returns a pointer or handle.
+ */
+MpalHandle mpalQueryHANDLE(uint16 wQueryType, ...);
+
+/**
+ * This is a general function to communicate with the library, to request information
+ * about what is in the .MPC file
+ *
+ * @param wQueryType Type of query. The list is in the QueryTypes enum.
+ * @returns 4 bytes depending on the type of query
+ * @remarks This is the specialised version of the original single mpalQuery
+ * method that needs to run within a co-routine context.
+ */
+void mpalQueryCORO(CORO_PARAM, uint16 wQueryType, uint32 *dwRet, ...);
+
+/**
+ * Execute a script. The script runs on multitasking by a thread.
+ *
+ * @param nScript Script number to run
+ * @returns TRUE if the script 'was launched, FALSE on failure
+ */
+bool mpalExecuteScript(int nScript);
+
+/**
+ * Returns the current MPAL error code
+ *
+ * @returns Error code
+ */
+uint32 mpalGetError();
+
+/**
+ * Install a custom routine That will be called by MPAL every time the pattern
+ * of an item has been changed.
+ *
+ * @param lpiifCustom Custom function to install
+ */
+void mpalInstallItemIrq(LPITEMIRQFUNCTION lpiifCustom);
+
+/**
+ * Process the idle actions of the items on one location.
+ *
+ * @param nLoc Number of the location whose items must be processed
+ * for idle actions.
+ * @returns TRUE if all OK, and FALSE if it exceeded the maximum limit.
+ * @remarks The maximum number of locations that can be polled
+ * simultaneously is defined defined by MAXPOLLINGFUNCIONS
+ */
+bool mpalStartIdlePoll(int nLoc);
+
+/**
+ * Stop processing the idle actions of the items on one location.
+ *
+ * @param nLo Number of the location
+ * @returns TRUE if all OK, FALSE if the specified location was not
+ * in the process of polling
+ */
+void mpalEndIdlePoll(CORO_PARAM, int nLoc, bool *result);
+
+
+/**
+ * Load a save state from a buffer.
+ *
+ * @param buf Buffer where to store the state
+ * @returns Length of the state buffer in bytes
+ */
+int mpalLoadState(byte *buf);
+
+/**
+ * Store the save state into a buffer. The buffer must be
+ * length at least the size specified with mpalGetSaveStateSize
+ *
+ * @param buf Buffer where to store the state
+ */
+void mpalSaveState(byte *buf);
+
+/**
+ * Retrieve the length of a save state
+ *
+ * @returns Length in bytes
+ */
+int mpalGetSaveStateSize();
+
+/**
+ * Locks the variables for access
+ */
+void lockVar();
+
+/**
+ * Unlocks variables after use
+ */
+void unlockVar();
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/mpaldll.h b/engines/tony/mpal/mpaldll.h
new file mode 100644
index 0000000000..8897096f51
--- /dev/null
+++ b/engines/tony/mpal/mpaldll.h
@@ -0,0 +1,251 @@
+/* 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
+ */
+
+#ifndef __MPALDLL_H
+#define __MPALDLL_H
+
+#include "common/file.h"
+#include "tony/mpal/memory.h"
+#include "tony/mpal/loadmpc.h"
+#include "tony/mpal/expr.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* Defines
+\****************************************************************************/
+
+#define HEX_VERSION 0x0170
+
+#define MAX_ACTIONS_PER_ITEM 40
+#define MAX_COMMANDS_PER_ITEM 128
+#define MAX_COMMANDS_PER_ACTION 128
+#define MAX_DESCRIBE_SIZE 64
+
+#define MAX_MOMENTS_PER_SCRIPT 256
+#define MAX_COMMANDS_PER_SCRIPT 256
+#define MAX_COMMANDS_PER_MOMENT 32
+
+#define MAX_GROUPS_PER_DIALOG 128
+#define MAX_COMMANDS_PER_DIALOG 480
+#define MAX_COMMANDS_PER_GROUP 64
+#define MAX_CHOICES_PER_DIALOG 64
+#define MAX_SELECTS_PER_CHOICE 64
+#define MAX_PLAYGROUPS_PER_SELECT 9
+#define MAX_PERIODS_PER_DIALOG 400
+
+#define NEED_LOCK_MSGS
+
+/****************************************************************************\
+* Structures
+\****************************************************************************/
+
+#include "common/pack-start.h"
+
+/**
+ * MPAL global variables
+ */
+struct MpalVar {
+ uint32 _dwVal; // Variable value
+ char _lpszVarName[33]; // Variable name
+} PACKED_STRUCT;
+typedef MpalVar *LpMpalVar;
+
+/**
+ * MPAL Messages
+ */
+struct MpalMsg {
+ MpalHandle _hText; // Handle to the message text
+ uint16 _wNum; // Message number
+} PACKED_STRUCT;
+typedef MpalMsg *LpMpalMsg;
+
+/**
+ * MPAL Locations
+ */
+struct MpalLocation {
+ uint32 _nObj; // Location number
+ uint32 _dwXlen, _dwYlen; // Dimensions
+ uint32 _dwPicRes; // Resource that contains the image
+} PACKED_STRUCT;
+typedef MpalLocation *LpMpalLocation;
+
+/**
+ * All the data for a command, ie. tags used by OnAction in the item, the time
+ * in the script, and in the group dialog.
+ */
+struct Command {
+ /*
+ * Types of commands that are recognized
+ *
+ * #1 -> Custom function call (ITEM, SCRIPT, DIALOG)
+ * #2 -> Variable assignment (ITEM, SCRIPT, DIALOG)
+ * #3 -> Making a choice (DIALOG)
+ *
+ */
+ byte _type; // Type of control
+
+ union {
+ int32 _nCf; // Custom function call [#1]
+ char *_lpszVarName; // Variable name [#2]
+ int32 _nChoice; // Number of choice you make [#3]
+ };
+
+ union {
+ int32 _arg1; // Argument for custom function [#1]
+ MpalHandle _expr; // Expression to assign to a variable [#2]
+ };
+
+ int32 _arg2, _arg3, _arg4; // Arguments for custom function [#1]
+} PACKED_STRUCT;
+
+
+/**
+ * MPAL dialog
+ */
+struct MpalDialog {
+ uint32 _nObj; // Dialog number
+
+ struct Command _command[MAX_COMMANDS_PER_DIALOG];
+
+ struct {
+ uint16 _num;
+ byte _nCmds;
+ uint16 _cmdNum[MAX_COMMANDS_PER_GROUP];
+
+ } _group[MAX_GROUPS_PER_DIALOG];
+
+ struct {
+ // The last choice has nChoice == 0
+ uint16 _nChoice;
+
+ // The select number (we're pretty stingy with RAM). The last select has dwData == 0
+ struct {
+ MpalHandle _when;
+ uint32 _dwData;
+ uint16 _wPlayGroup[MAX_PLAYGROUPS_PER_SELECT];
+
+ // Bit 0=endchoice Bit 1=enddialog
+ byte _attr;
+
+ // Modified at run-time: 0 if the select is currently disabled,
+ // and 1 if currently active
+ byte _curActive;
+ } _select[MAX_SELECTS_PER_CHOICE];
+
+ } _choice[MAX_CHOICES_PER_DIALOG];
+
+ uint16 _periodNums[MAX_PERIODS_PER_DIALOG];
+ MpalHandle _periods[MAX_PERIODS_PER_DIALOG];
+
+} PACKED_STRUCT;
+typedef MpalDialog *LpMpalDialog;
+
+/**
+ * MPAL Item
+ */
+struct ItemAction {
+ byte _num; // Action number
+ uint16 _wTime; // If idle, the time which must pass
+ byte _perc; // Percentage of the idle run
+ MpalHandle _when; // Expression to compute. If != 0, then
+ // action can be done
+ uint16 _wParm; // Parameter for action
+
+ byte _nCmds; // Number of commands to be executed
+ uint32 _cmdNum[MAX_COMMANDS_PER_ACTION]; // Commands to execute
+} PACKED_STRUCT;
+
+struct MpalItem {
+ uint32 _nObj; // Item number
+
+ byte _lpszDescribe[MAX_DESCRIBE_SIZE]; // Name
+ byte _nActions; // Number of managed actions
+ uint32 _dwRes; // Resource that contains frames and patterns
+
+ struct Command _command[MAX_COMMANDS_PER_ITEM];
+
+ // Pointer to array of structures containing various managed activities. In practice, of
+ // every action we know what commands to run, including those defined in structures above
+ struct ItemAction *_action;
+
+} PACKED_STRUCT;
+typedef MpalItem *LpMpalItem;
+
+/**
+ * MPAL Script
+ */
+struct MpalScript {
+ uint32 _nObj;
+ uint32 _nMoments;
+
+ struct Command _command[MAX_COMMANDS_PER_SCRIPT];
+
+ struct {
+ int32 _dwTime;
+ byte _nCmds;
+ uint32 _cmdNum[MAX_COMMANDS_PER_MOMENT];
+
+ } _moment[MAX_MOMENTS_PER_SCRIPT];
+
+} PACKED_STRUCT;
+typedef MpalScript *LpMpalScript;
+
+#include "common/pack-end.h"
+
+/****************************************************************************\
+* Function prototypes
+\****************************************************************************/
+
+/**
+ * Returns the current value of a global variable
+ *
+ * @param lpszVarName Name of the variable
+ * @returns Current value
+ * @remarks Before using this method, you must call LockVar() to
+ * lock the global variablves for use. Then afterwards, you will
+ * need to remember to call UnlockVar()
+ */
+extern int32 varGetValue(const char *lpszVarName);
+
+
+/**
+ * Sets the value of a MPAL global variable
+ * @param lpszVarName Name of the variable
+ * @param val Value to set
+ */
+extern void varSetValue(const char *lpszVarName, int32 val);
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif
+
diff --git a/engines/tony/mpal/mpalutils.cpp b/engines/tony/mpal/mpalutils.cpp
new file mode 100644
index 0000000000..0919aed5ac
--- /dev/null
+++ b/engines/tony/mpal/mpalutils.cpp
@@ -0,0 +1,115 @@
+/* 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.
+ *
+ *
+ */
+
+#include "tony/mpal/mpalutils.h"
+#include "tony/tony.h"
+#include "common/memstream.h"
+
+namespace Tony {
+
+namespace MPAL {
+
+/****************************************************************************\
+* RMRes methods
+\****************************************************************************/
+
+/**
+ * Constructor
+ * @param resId MPAL resource to open
+ */
+RMRes::RMRes(uint32 resID) {
+ _h = g_vm->_resUpdate.queryResource(resID);
+ if (_h == NULL)
+ _h = mpalQueryResource(resID);
+ if (_h != NULL)
+ _buf = (byte *)globalLock(_h);
+}
+
+/**
+ * Destructor
+ */
+RMRes::~RMRes() {
+ if (_h != NULL) {
+ globalUnlock(_h);
+ globalFree(_h);
+ }
+}
+
+/**
+ * Returns a pointer to the resource
+ */
+const byte *RMRes::dataPointer() {
+ return _buf;
+}
+
+/**
+ * Returns a pointer to the resource
+ */
+RMRes::operator const byte *() {
+ return dataPointer();
+}
+
+/**
+ * Returns the size of the resource
+ */
+unsigned int RMRes::size() {
+ return globalSize(_h);
+}
+
+Common::SeekableReadStream *RMRes::getReadStream() {
+ return new Common::MemoryReadStream(_buf, size());
+}
+
+bool RMRes::isValid() {
+ return _h != NULL;
+}
+
+/****************************************************************************\
+* RMResRaw methods
+\****************************************************************************/
+
+RMResRaw::RMResRaw(uint32 resID) : RMRes(resID) {
+}
+
+RMResRaw::~RMResRaw() {
+}
+
+const byte *RMResRaw::dataPointer() {
+ return _buf + 8;
+}
+
+RMResRaw::operator const byte *() {
+ return dataPointer();
+}
+
+int RMResRaw::width() {
+ return READ_LE_UINT16(_buf + 4);
+}
+
+int RMResRaw::height() {
+ return READ_LE_UINT16(_buf + 6);
+}
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
diff --git a/engines/tony/mpal/mpalutils.h b/engines/tony/mpal/mpalutils.h
new file mode 100644
index 0000000000..d92bb6f9a2
--- /dev/null
+++ b/engines/tony/mpal/mpalutils.h
@@ -0,0 +1,74 @@
+/* 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.
+ *
+ *
+ */
+
+#ifndef TONY_MPAL_MPALUTILS
+#define TONY_MPAL_MPALUTILS
+
+#include "common/scummsys.h"
+#include "tony/mpal/memory.h"
+
+namespace Common {
+ class SeekableReadStream;
+}
+
+namespace Tony {
+
+namespace MPAL {
+
+class RMRes {
+protected:
+ MpalHandle _h;
+ byte *_buf;
+
+public:
+ RMRes(uint32 resID);
+ virtual ~RMRes();
+
+ // Attributes
+ unsigned int size();
+ const byte *dataPointer();
+ bool isValid();
+
+ // Casting for access to data
+ operator const byte*();
+
+ Common::SeekableReadStream *getReadStream();
+};
+
+class RMResRaw : public RMRes {
+public:
+ RMResRaw(uint32 resID);
+ virtual ~RMResRaw();
+
+ const byte *dataPointer();
+ operator const byte*();
+
+ int width();
+ int height();
+};
+
+} // end of namespace MPAL
+
+} // end of namespace Tony
+
+#endif