diff options
Diffstat (limited to 'engines/kyra/script.cpp')
-rw-r--r-- | engines/kyra/script.cpp | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/engines/kyra/script.cpp b/engines/kyra/script.cpp new file mode 100644 index 0000000000..b43766dba7 --- /dev/null +++ b/engines/kyra/script.cpp @@ -0,0 +1,565 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2004-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/stream.h" +#include "common/util.h" +#include "common/system.h" +#include "kyra/kyra.h" +#include "kyra/resource.h" +#include "kyra/script.h" + +#define FORM_CHUNK 0x4D524F46 +#define TEXT_CHUNK 0x54584554 +#define DATA_CHUNK 0x41544144 +#define ORDR_CHUNK 0x5244524F + +namespace Kyra { +ScriptHelper::ScriptHelper(KyraEngine *vm) : _vm(vm) { +#define COMMAND(x) { &ScriptHelper::x, #x } + // now we create a list of all Command/Opcode procs and so + static CommandEntry commandProcs[] = { + // 0x00 + COMMAND(c1_jmpTo), + COMMAND(c1_setRetValue), + COMMAND(c1_pushRetOrPos), + COMMAND(c1_push), + // 0x04 + COMMAND(c1_push), + COMMAND(c1_pushVar), + COMMAND(c1_pushBPNeg), + COMMAND(c1_pushBPAdd), + // 0x08 + COMMAND(c1_popRetOrPos), + COMMAND(c1_popVar), + COMMAND(c1_popBPNeg), + COMMAND(c1_popBPAdd), + // 0x0C + COMMAND(c1_addSP), + COMMAND(c1_subSP), + COMMAND(c1_execOpcode), + COMMAND(c1_ifNotJmp), + // 0x10 + COMMAND(c1_negate), + COMMAND(c1_eval), + COMMAND(c1_setRetAndJmp) + }; + _commands = commandProcs; +#undef COMMAND +} + +ScriptHelper::~ScriptHelper() { +} + +bool ScriptHelper::loadScript(const char *filename, ScriptData *scriptData, KyraEngine::OpcodeProc *opcodes, int opcodeSize, byte *specialPtr) { + uint32 size = 0; + uint8 *data = _vm->resource()->fileData(filename, &size); + byte *curData = data; + + uint32 formBlockSize = getFORMBlockSize(curData); + if (formBlockSize == (uint32)-1) { + delete [] data; + error("No FORM chunk found in file: '%s'", filename); + return false; + } + + uint32 chunkSize = getIFFBlockSize(data, curData, size, TEXT_CHUNK); + if (chunkSize != (uint32)-1) { + if (specialPtr) { + scriptData->mustBeFreed = 0; + scriptData->text = specialPtr; + specialPtr += chunkSize; + } else { + scriptData->mustBeFreed = 1; + scriptData->text = new byte[chunkSize]; + } + if (!loadIFFBlock(data, curData, size, TEXT_CHUNK, scriptData->text, chunkSize)) { + delete [] data; + unloadScript(scriptData); + error("Couldn't load TEXT chunk from file: '%s'", filename); + return false; + } + } + + chunkSize = getIFFBlockSize(data, curData, size, ORDR_CHUNK); + if (chunkSize == (uint32)-1) { + delete [] data; + unloadScript(scriptData); + error("No ORDR chunk found in file: '%s'", filename); + return false; + } + if (specialPtr) { + scriptData->mustBeFreed = 0; + scriptData->ordr = specialPtr; + specialPtr += chunkSize; + } else { + scriptData->mustBeFreed = 1; + scriptData->ordr = new byte[chunkSize]; + } + if (!loadIFFBlock(data, curData, size, ORDR_CHUNK, scriptData->ordr, chunkSize)) { + delete [] data; + unloadScript(scriptData); + error("Couldn't load ORDR chunk from file: '%s'", filename); + return false; + } + chunkSize = chunkSize / 2; + while (chunkSize--) { + ((uint16*)scriptData->ordr)[chunkSize] = READ_BE_UINT16(&((uint16*)scriptData->ordr)[chunkSize]); + } + + chunkSize = getIFFBlockSize(data, curData, size, DATA_CHUNK); + if (chunkSize == (uint32)-1) { + delete [] data; + unloadScript(scriptData); + error("No DATA chunk found in file: '%s'", filename); + return false; + } + if (specialPtr) { + scriptData->mustBeFreed = 0; + scriptData->data = specialPtr; + specialPtr += chunkSize; + } else { + scriptData->mustBeFreed = 1; + scriptData->data = new byte[chunkSize]; + } + if (!loadIFFBlock(data, curData, size, DATA_CHUNK, scriptData->data, chunkSize)) { + delete [] data; + unloadScript(scriptData); + error("Couldn't load DATA chunk from file: '%s'", filename); + return false; + } + scriptData->dataSize = chunkSize / 2; + scriptData->opcodes = opcodes; + scriptData->opcodeSize = opcodeSize; + + delete [] data; + return true; +} + +void ScriptHelper::unloadScript(ScriptData *data) { + if (data->mustBeFreed) { + delete [] data->text; + delete [] data->ordr; + delete [] data->data; + } + + data->mustBeFreed = 0; + data->text = data->ordr = data->data = 0; +} + +void ScriptHelper::initScript(ScriptState *scriptStat, ScriptData *data) { + scriptStat->dataPtr = data; + scriptStat->ip = 0; + scriptStat->stack[60] = 0; + scriptStat->bp = 62; + scriptStat->sp = 60; +} + +bool ScriptHelper::startScript(ScriptState *script, int function) { + if (!script->dataPtr) { + return false; + } + uint16 functionOffset = ((uint16*)script->dataPtr->ordr)[function]; + if (functionOffset == (uint16)-1) { + return false; + } + script->ip = &script->dataPtr->data[functionOffset*2]; + return true; +} + +bool ScriptHelper::validScript(ScriptState *script) { + if (!script->ip || !script->dataPtr) + return false; + return true; +} + +bool ScriptHelper::runScript(ScriptState *script) { + _parameter = 0; + _continue = true; + + if (!script->ip) { + return false; + } + + int16 code = READ_BE_UINT16(script->ip); script->ip += 2; + int16 opcode = (code >> 8) & 0x1F; + + if (code & 0x8000) { + opcode = 0; + _parameter = code & 0x7FFF; + } else if (code & 0x4000) { + _parameter = (int8)(code); + } else if (code & 0x2000) { + _parameter = READ_BE_UINT16(script->ip); script->ip += 2; + } else { + _parameter = 0; + } + + if (opcode > 18) { + error("Script unknown command: %d", opcode); + } else { + debug(5, "%s(%d)", _commands[opcode].desc, _parameter); + (this->*(_commands[opcode].proc))(script); + } + + return _continue; +} + +uint32 ScriptHelper::getFORMBlockSize(byte *&data) const { + static const uint32 chunkName = FORM_CHUNK; + if (READ_LE_UINT32(data) != chunkName) { + return (uint32)-1; + } + data += 4; + uint32 retValue = READ_BE_UINT32(data); data += 4; + return retValue; +} + +uint32 ScriptHelper::getIFFBlockSize(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName) const { + uint32 size = (uint32)-1; + bool special = false; + + if (data == (start + maxSize)) { + data = start + 0x0C; + } + while (data < (start + maxSize)) { + uint32 chunk = READ_LE_UINT32(data); data += 4; + uint32 size_temp = READ_BE_UINT32(data); data += 4; + if (chunk != chunkName) { + if (special) { + data += (size_temp + 1) & 0xFFFFFFFE; + } else { + data = start + 0x0C; + special = true; + } + } else { + // kill our data + data = start; + size = size_temp; + break; + } + } + return size; +} + +bool ScriptHelper::loadIFFBlock(byte *start, byte *&data, uint32 maxSize, const uint32 chunkName, byte *loadTo, uint32 ptrSize) const { + bool special = false; + + if (data == (start + maxSize)) { + data = start + 0x0C; + } + while (data < (start + maxSize)) { + uint32 chunk = READ_LE_UINT32(data); data += 4; + uint32 chunkSize = READ_BE_UINT32(data); data += 4; + if (chunk != chunkName) { + if (special) { + data += (chunkSize + 1) & 0xFFFFFFFE; + } else { + data = start + 0x0C; + special = true; + } + } else { + uint32 loadSize = 0; + if (chunkSize < ptrSize) + loadSize = chunkSize; + else + loadSize = ptrSize; + memcpy(loadTo, data, loadSize); + chunkSize = (chunkSize + 1) & 0xFFFFFFFE; + if (chunkSize > loadSize) { + data += (chunkSize - loadSize); + } + return true; + } + } + return false; +} + +#pragma mark - +#pragma mark - Command implementations +#pragma mark - + +void ScriptHelper::c1_jmpTo(ScriptState* script) { + script->ip = script->dataPtr->data + (_parameter << 1); +} + +void ScriptHelper::c1_setRetValue(ScriptState* script) { + script->retValue = _parameter; +} + +void ScriptHelper::c1_pushRetOrPos(ScriptState* script) { + switch (_parameter) { + case 0: + script->stack[--script->sp] = script->retValue; + break; + + case 1: + script->stack[--script->sp] = (script->ip - script->dataPtr->data) / 2 + 1; + script->stack[--script->sp] = script->bp; + script->bp = script->sp + 2; + break; + + default: + _continue = false; + script->ip = 0; + break; + } +} + +void ScriptHelper::c1_push(ScriptState* script) { + script->stack[--script->sp] = _parameter; +} + +void ScriptHelper::c1_pushVar(ScriptState* script) { + script->stack[--script->sp] = script->variables[_parameter]; +} + +void ScriptHelper::c1_pushBPNeg(ScriptState* script) { + script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp]; +} + +void ScriptHelper::c1_pushBPAdd(ScriptState* script) { + script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp]; +} + +void ScriptHelper::c1_popRetOrPos(ScriptState* script) { + switch (_parameter) { + case 0: + script->retValue = script->stack[script->sp++]; + break; + + case 1: + if (script->sp >= 60) { + _continue = false; + script->ip = 0; + } else { + script->bp = script->stack[script->sp++]; + script->ip = script->dataPtr->data + (script->stack[script->sp++] << 1); + } + break; + + default: + _continue = false; + script->ip = 0; + break; + } +} + +void ScriptHelper::c1_popVar(ScriptState* script) { + script->variables[_parameter] = script->stack[script->sp++]; +} + +void ScriptHelper::c1_popBPNeg(ScriptState* script) { + script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++]; +} + +void ScriptHelper::c1_popBPAdd(ScriptState* script) { + script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++]; +} + +void ScriptHelper::c1_addSP(ScriptState* script) { + script->sp += _parameter; +} + +void ScriptHelper::c1_subSP(ScriptState* script) { + script->sp -= _parameter; +} + +void ScriptHelper::c1_execOpcode(ScriptState* script) { + assert((uint8)_parameter < script->dataPtr->opcodeSize); + if (script->dataPtr->opcodes[(uint8)_parameter] == &KyraEngine::cmd_dummy) + debug("calling unimplemented opcode(0x%.02X)", (uint8)_parameter); + int val = (_vm->*script->dataPtr->opcodes[(uint8)_parameter])(script); + assert(script); + script->retValue = val; +} + +void ScriptHelper::c1_ifNotJmp(ScriptState* script) { + if (!script->stack[script->sp++]) { + _parameter &= 0x7FFF; + script->ip = script->dataPtr->data + (_parameter << 1); + } +} + +void ScriptHelper::c1_negate(ScriptState* script) { + int16 value = script->stack[script->sp]; + switch (_parameter) { + case 0: + if (!value) { + script->stack[script->sp] = 1; + } else { + script->stack[script->sp] = 0; + } + break; + + case 1: + script->stack[script->sp] = -value; + break; + + case 2: + script->stack[script->sp] = ~value; + break; + + default: + _continue = false; + break; + } +} + +void ScriptHelper::c1_eval(ScriptState* script) { + int16 ret = 0; + bool error = false; + + int16 val1 = script->stack[script->sp++]; + int16 val2 = script->stack[script->sp++]; + + switch (_parameter) { + case 0: + if (!val2 || !val1) { + ret = 0; + } else { + ret = 1; + } + break; + + case 1: + if (val2 || val1) { + ret = 1; + } else { + ret = 0; + } + break; + + case 2: + if (val1 == val2) { + ret = 1; + } else { + ret = 0; + } + break; + + case 3: + if (val1 != val2) { + ret = 1; + } else { + ret = 0; + } + break; + + case 4: + if (val1 > val2) { + ret = 1; + } else { + ret = 0; + } + break; + + case 5: + if (val1 >= val2) { + ret = 1; + } else { + ret = 0; + } + break; + + case 6: + if (val1 < val2) { + ret = 1; + } else { + ret = 0; + } + break; + + case 7: + if (val1 <= val2) { + ret = 1; + } else { + ret = 0; + } + break; + + case 8: + ret = val1 + val2; + break; + + case 9: + ret = val2 - val1; + break; + + case 10: + ret = val1 * val2; + break; + + case 11: + ret = val2 / val1; + break; + + case 12: + ret = val2 >> val1; + break; + + case 13: + ret = val2 << val1; + break; + + case 14: + ret = val1 & val2; + break; + + case 15: + ret = val1 | val2; + break; + + case 16: + ret = val2 % val1; + break; + + case 17: + ret = val1 ^ val2; + break; + + default: + warning("Unknown evaluate func: %d", _parameter); + error = true; + break; + } + + if (error) { + script->ip = 0; + _continue = false; + } else { + script->stack[--script->sp] = ret; + } +} + +void ScriptHelper::c1_setRetAndJmp(ScriptState* script) { + if (script->sp >= 60) { + _continue = false; + script->ip = 0; + } else { + script->retValue = script->stack[script->sp++]; + uint16 temp = script->stack[script->sp++]; + script->stack[60] = 0; + script->ip = &script->dataPtr->data[temp*2]; + } +} +} // end of namespace Kyra |