aboutsummaryrefslogtreecommitdiff
path: root/engines/toon/script.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/toon/script.cpp')
-rw-r--r--engines/toon/script.cpp504
1 files changed, 504 insertions, 0 deletions
diff --git a/engines/toon/script.cpp b/engines/toon/script.cpp
new file mode 100644
index 0000000000..06d482f4e2
--- /dev/null
+++ b/engines/toon/script.cpp
@@ -0,0 +1,504 @@
+/* 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.
+*
+* $URL$
+* $Id$
+*
+*/
+
+
+#include "common/endian.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/system.h"
+
+#include "toon/toon.h"
+#include "toon/script.h"
+
+namespace Toon {
+EMCInterpreter::EMCInterpreter(ToonEngine *vm) : _vm(vm), _scriptData(0), _filename(0) {
+
+#define OPCODE(x) { &EMCInterpreter::x, #x }
+ static const OpcodeEntry opcodes[] = {
+ // 0x00
+ OPCODE(op_jmp),
+ OPCODE(op_setRetValue),
+ OPCODE(op_pushRetOrPos),
+ OPCODE(op_push),
+ // 0x04
+ OPCODE(op_push),
+ OPCODE(op_pushReg),
+ OPCODE(op_pushBPNeg),
+ OPCODE(op_pushBPAdd),
+ // 0x08
+ OPCODE(op_popRetOrPos),
+ OPCODE(op_popReg),
+ OPCODE(op_popBPNeg),
+ OPCODE(op_popBPAdd),
+ // 0x0C
+ OPCODE(op_addSP),
+ OPCODE(op_subSP),
+ OPCODE(op_sysCall),
+ OPCODE(op_ifNotJmp),
+ // 0x10
+ OPCODE(op_negate),
+ OPCODE(op_eval),
+ OPCODE(op_setRetAndJmp)
+ };
+ _opcodes = opcodes;
+#undef OPCODE
+}
+
+bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
+ switch (chunk._type) {
+ case MKID_BE('TEXT'):
+ _scriptData->text = new byte[chunk._size];
+ assert(_scriptData->text);
+ if (chunk._stream->read(_scriptData->text, chunk._size) != chunk._size)
+ error("Couldn't read TEXT chunk from file '%s'", _filename);
+ break;
+
+ case MKID_BE('ORDR'):
+ _scriptData->ordr = new uint16[chunk._size >> 1];
+ assert(_scriptData->ordr);
+ if (chunk._stream->read(_scriptData->ordr, chunk._size) != chunk._size)
+ error("Couldn't read ORDR chunk from file '%s'", _filename);
+
+ for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
+ _scriptData->ordr[i] = READ_BE_UINT16(&_scriptData->ordr[i]);
+ break;
+
+ case MKID_BE('DATA'):
+ _scriptData->data = new uint16[chunk._size >> 1];
+ assert(_scriptData->data);
+ if (chunk._stream->read(_scriptData->data, chunk._size) != chunk._size)
+ error("Couldn't read DATA chunk from file '%s'", _filename);
+
+ for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
+ _scriptData->data[i] = READ_BE_UINT16(&_scriptData->data[i]);
+ break;
+
+ default:
+ warning("Unexpected chunk '%s' of size %d found in file '%s'", Common::tag2string(chunk._type).c_str(), chunk._size, _filename);
+ }
+
+ return false;
+}
+
+bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const Opcode *> *opcodes) {
+ Common::SeekableReadStream *stream = _vm->resources()->openFile(filename);
+ if (!stream) {
+ error("Couldn't open script file '%s'", filename);
+ return false; // for compilers that don't support NORETURN
+ }
+
+ memset(scriptData, 0, sizeof(EMCData));
+
+ _scriptData = scriptData;
+ _filename = filename;
+
+ IFFParser iff(*stream);
+ Common::Functor1Mem< Common::IFFChunk &, bool, EMCInterpreter > c(this, &EMCInterpreter::callback);
+ iff.parse(c);
+
+ if (!_scriptData->ordr)
+ error("No ORDR chunk found in file: '%s'", filename);
+
+ if (!_scriptData->data)
+ error("No DATA chunk found in file: '%s'", filename);
+
+ if (stream->err())
+ error("Read error while parsing file '%s'", filename);
+
+ delete stream;
+
+ _scriptData->sysFuncs = opcodes;
+
+ strncpy(_scriptData->filename, filename, 13);
+ _scriptData->filename[12] = 0;
+
+ _scriptData = 0;
+ _filename = 0;
+
+ return true;
+}
+
+void EMCInterpreter::unload(EMCData *data) {
+ if (!data)
+ return;
+
+ delete[] data->text;
+ delete[] data->ordr;
+ delete[] data->data;
+
+ data->text = 0;
+ data->ordr = data->data = 0;
+}
+
+void EMCInterpreter::init(EMCState *scriptStat, const EMCData *data) {
+ scriptStat->dataPtr = data;
+ scriptStat->ip = 0;
+ scriptStat->stack[EMCState::kStackLastEntry] = 0;
+ scriptStat->bp = EMCState::kStackSize + 1;
+ scriptStat->sp = EMCState::kStackLastEntry;
+ scriptStat->running = false;
+}
+
+bool EMCInterpreter::start(EMCState *script, int function) {
+ if (!script->dataPtr)
+ return false;
+
+ uint16 functionOffset = script->dataPtr->ordr[function];
+ if (functionOffset == 0xFFFF)
+ return false;
+
+ script->ip = &script->dataPtr->data[functionOffset+1];
+
+ return true;
+}
+
+bool EMCInterpreter::isValid(EMCState *script) {
+ if (!script->ip || !script->dataPtr || _vm->shouldQuitGame())
+ return false;
+ return true;
+}
+
+bool EMCInterpreter::run(EMCState *script) {
+
+ if (script->running)
+ return false;
+
+ _parameter = 0;
+
+ if (!script->ip)
+ return false;
+
+ script->running = true;
+
+
+ // Should be no Problem at all to cast to uint32 here, since that's the biggest ptrdiff the original
+ // would allow, of course that's not realistic to happen to be somewhere near the limit of uint32 anyway.
+ const uint32 instOffset = (uint32)((const byte *)script->ip - (const byte *)script->dataPtr->data);
+ int16 code = *script->ip++;
+ 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 = *script->ip++;
+ } else {
+ _parameter = 0;
+ }
+
+ if (opcode > 18) {
+ error("Unknown script opcode: %d in file '%s' at offset 0x%.08X", opcode, script->dataPtr->filename, instOffset);
+ } else {
+ static bool EMCDebug = false;
+ if (EMCDebug)
+ debugC(5, 0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset * 2, _opcodes[opcode].desc, _parameter, (uint)_parameter);
+ //debug(0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter);
+
+ (this->*(_opcodes[opcode].proc))(script);
+ }
+
+ script->running = false;
+ return (script->ip != 0);
+}
+
+#pragma mark -
+#pragma mark - Command implementations
+#pragma mark -
+
+void EMCInterpreter::op_jmp(EMCState *script) {
+ script->ip = script->dataPtr->data + _parameter;
+}
+
+void EMCInterpreter::op_setRetValue(EMCState *script) {
+ script->retValue = _parameter;
+}
+
+void EMCInterpreter::op_pushRetOrPos(EMCState *script) {
+ switch (_parameter) {
+ case 0:
+ script->stack[--script->sp] = script->retValue;
+ break;
+
+ case 1:
+ script->stack[--script->sp] = script->ip - script->dataPtr->data + 1;
+ script->stack[--script->sp] = script->bp;
+ script->bp = script->sp + 2;
+ break;
+
+ default:
+ script->ip = 0;
+ }
+}
+
+void EMCInterpreter::op_push(EMCState *script) {
+ script->stack[--script->sp] = _parameter;
+}
+
+void EMCInterpreter::op_pushReg(EMCState *script) {
+ script->stack[--script->sp] = script->regs[_parameter];
+}
+
+void EMCInterpreter::op_pushBPNeg(EMCState *script) {
+ script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp];
+}
+
+void EMCInterpreter::op_pushBPAdd(EMCState *script) {
+ script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp];
+}
+
+void EMCInterpreter::op_popRetOrPos(EMCState *script) {
+ switch (_parameter) {
+ case 0:
+ script->retValue = script->stack[script->sp++];
+ break;
+
+ case 1:
+ if (script->sp >= EMCState::kStackLastEntry) {
+ script->ip = 0;
+ } else {
+ script->bp = script->stack[script->sp++];
+ script->ip = script->dataPtr->data + script->stack[script->sp++];
+ }
+ break;
+
+ default:
+ script->ip = 0;
+ }
+}
+
+void EMCInterpreter::op_popReg(EMCState *script) {
+ script->regs[_parameter] = script->stack[script->sp++];
+}
+
+void EMCInterpreter::op_popBPNeg(EMCState *script) {
+ script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++];
+}
+
+void EMCInterpreter::op_popBPAdd(EMCState *script) {
+ script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++];
+}
+
+void EMCInterpreter::op_addSP(EMCState *script) {
+ script->sp += _parameter;
+}
+
+void EMCInterpreter::op_subSP(EMCState *script) {
+ script->sp -= _parameter;
+}
+
+void EMCInterpreter::op_sysCall(EMCState *script) {
+ const uint8 id = _parameter;
+
+ assert(script->dataPtr->sysFuncs);
+ assert(id < script->dataPtr->sysFuncs->size());
+
+ if ((*script->dataPtr->sysFuncs)[id] && ((*script->dataPtr->sysFuncs)[id])->isValid()) {
+ script->retValue = (*(*script->dataPtr->sysFuncs)[id])(script);
+ } else {
+ script->retValue = 0;
+ warning("Unimplemented system call 0x%.02X/%d used in file '%s'", id, id, script->dataPtr->filename);
+ }
+}
+
+void EMCInterpreter::op_ifNotJmp(EMCState *script) {
+ if (!script->stack[script->sp++]) {
+ _parameter &= 0x7FFF;
+ script->ip = script->dataPtr->data + _parameter;
+ }
+}
+
+void EMCInterpreter::op_negate(EMCState *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:
+ warning("Unknown negation func: %d", _parameter);
+ script->ip = 0;
+ }
+}
+
+void EMCInterpreter::op_eval(EMCState *script) {
+ int16 ret = 0;
+ bool error = false;
+
+ int16 val1 = script->stack[script->sp++];
+ int16 val2 = script->stack[script->sp++];
+
+ switch (_parameter) {
+ case 0:
+ ret = (val2 && val1) ? 1 : 0;
+ break;
+
+ case 1:
+ ret = (val2 || val1) ? 1 : 0;
+ break;
+
+ case 2:
+ ret = (val1 == val2) ? 1 : 0;
+ break;
+
+ case 3:
+ ret = (val1 != val2) ? 1 : 0;
+ break;
+
+ case 4:
+ ret = (val1 > val2) ? 1 : 0;
+ break;
+
+ case 5:
+ ret = (val1 >= val2) ? 1 : 0;
+ break;
+
+ case 6:
+ ret = (val1 < val2) ? 1 : 0;
+ break;
+
+ case 7:
+ ret = (val1 <= val2) ? 1 : 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;
+ }
+
+ if (error)
+ script->ip = 0;
+ else
+ script->stack[--script->sp] = ret;
+}
+
+void EMCInterpreter::op_setRetAndJmp(EMCState *script) {
+ if (script->sp >= EMCState::kStackLastEntry) {
+ script->ip = 0;
+ } else {
+ script->retValue = script->stack[script->sp++];
+ uint16 temp = script->stack[script->sp++];
+ script->stack[EMCState::kStackLastEntry] = 0;
+ script->ip = &script->dataPtr->data[temp];
+ }
+}
+
+void EMCInterpreter::saveState(EMCState *script, Common::WriteStream *stream) {
+ stream->writeSint16LE(script->bp);
+ stream->writeSint16LE(script->sp);
+ if (!script->ip) {
+ stream->writeSint16LE(-1);
+ } else {
+ stream->writeSint16LE(script->ip - script->dataPtr->data);
+ }
+
+ for (int32 i = 0; i < EMCState::kStackSize; i++) {
+ stream->writeSint16LE(script->stack[i]);
+ }
+
+ for (int32 i = 0; i < 30; i++) {
+ stream->writeSint16LE(script->regs[i]);
+ }
+
+ stream->writeSint16LE(script->retValue);
+ stream->writeByte(script->running);
+}
+void EMCInterpreter::loadState(EMCState *script, Common::ReadStream *stream) {
+ script->bp = stream->readSint16LE();
+ script->sp = stream->readSint16LE();
+
+ int16 scriptIp = stream->readSint16LE();
+ if (scriptIp == -1) {
+ script->ip = 0;
+ } else {
+ script->ip = scriptIp + script->dataPtr->data;
+ }
+
+ for (int32 i = 0; i < EMCState::kStackSize; i++) {
+ script->stack[i] = stream->readSint16LE();
+ }
+
+ for (int32 i = 0; i < 30; i++) {
+ script->regs[i] = stream->readSint16LE();
+ }
+
+ script->retValue = stream->readSint16LE();
+ script->running = stream->readByte();
+}
+
+} // End of namespace Toon
+