diff options
author | D G Turner | 2012-10-12 17:03:32 +0100 |
---|---|---|
committer | D G Turner | 2012-10-12 17:03:32 +0100 |
commit | 151b7beb47ec4b964862d6779bd48e3a33482bbd (patch) | |
tree | 867717c5266d0908d95edd82560599be20a4ede9 /engines/tony/mpal | |
parent | 80af0e239473f85c49cc2da3c848dfcde41d4a37 (diff) | |
parent | 2b55837650c4229dc3d75b660cecfc7a3292e5e0 (diff) | |
download | scummvm-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.cpp | 365 | ||||
-rw-r--r-- | engines/tony/mpal/expr.h | 140 | ||||
-rw-r--r-- | engines/tony/mpal/loadmpc.cpp | 788 | ||||
-rw-r--r-- | engines/tony/mpal/loadmpc.h | 59 | ||||
-rw-r--r-- | engines/tony/mpal/lzo.cpp | 511 | ||||
-rw-r--r-- | engines/tony/mpal/lzo.h | 111 | ||||
-rw-r--r-- | engines/tony/mpal/memory.cpp | 127 | ||||
-rw-r--r-- | engines/tony/mpal/memory.h | 78 | ||||
-rw-r--r-- | engines/tony/mpal/mpal.cpp | 2089 | ||||
-rw-r--r-- | engines/tony/mpal/mpal.h | 518 | ||||
-rw-r--r-- | engines/tony/mpal/mpaldll.h | 251 | ||||
-rw-r--r-- | engines/tony/mpal/mpalutils.cpp | 115 | ||||
-rw-r--r-- | engines/tony/mpal/mpalutils.h | 74 |
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 |