diff options
Diffstat (limited to 'engines/tony/mpal/loadmpc.cpp')
-rw-r--r-- | engines/tony/mpal/loadmpc.cpp | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/engines/tony/mpal/loadmpc.cpp b/engines/tony/mpal/loadmpc.cpp new file mode 100644 index 0000000000..953820be74 --- /dev/null +++ b/engines/tony/mpal/loadmpc.cpp @@ -0,0 +1,790 @@ +/* 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) { + uint32 num2, num3; + 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; + + 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 + 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 |