Files
scummvm/engines/made/script.cpp
T
Benjamin Haisch b6c7385eb4 - Renamed XmidiResource to GenericResource
- Added MIDI resource type
- Added ScriptFunctionsLgop2 and ScriptFunctionsMhne (for Leather Goddesses of Phobos 2 and The Manhole: New and Enhanced, resp.)
- Many changes for LGOP2 and The Manhole: N&E

Note about the new ScriptFunctions classes:
I copied the ScriptFunctionsRtz class and so duplicated a lot of code. Most of the opcode functions are the same in all games but there might be differences. Once all common opcode functions have been figured out, they'll be moved to a common place (like the ScriptFunctions class).

svn-id: r31871
2008-05-05 10:45:11 +00:00

641 lines
15 KiB
C++

/* 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/util.h"
#include "made/made.h"
#include "made/script.h"
#include "made/database.h"
#include "made/scriptfuncs.h"
namespace Made {
/* ScriptStack */
ScriptStack::ScriptStack() {
for (int16 i = 0; i < kScriptStackSize; i++)
_stack[i] = 0;
_stackPos = kScriptStackSize;
}
ScriptStack::~ScriptStack() {
}
int16 ScriptStack::top() {
return _stack[_stackPos];
}
int16 ScriptStack::pop() {
if (_stackPos == kScriptStackSize)
error("ScriptStack::pop() Stack underflow");
return _stack[_stackPos++];
}
void ScriptStack::push(int16 value) {
if (_stackPos == 0)
error("ScriptStack::push() Stack overflow");
_stack[--_stackPos] = value;
}
void ScriptStack::setTop(int16 value) {
_stack[_stackPos] = value;
}
int16 ScriptStack::peek(int16 index) {
return _stack[index];
}
void ScriptStack::poke(int16 index, int16 value) {
_stack[index] = value;
}
void ScriptStack::alloc(int16 count) {
_stackPos -= count;
}
void ScriptStack::free(int16 count) {
_stackPos += count;
}
void ScriptStack::setStackPos(int16 stackPtr) {
_stackPos = stackPtr;
}
int16 *ScriptStack::getStackPtr() {
return &_stack[_stackPos];
}
/* ScriptInterpreter */
ScriptInterpreter::ScriptInterpreter(MadeEngine *vm) : _vm(vm) {
#define COMMAND(x) { &ScriptInterpreter::x, #x }
static CommandEntry commandProcs[] = {
/* 01 */
COMMAND(cmd_branchTrue),
COMMAND(cmd_branchFalse),
COMMAND(cmd_branch),
COMMAND(cmd_true),
/* 05 */
COMMAND(cmd_false),
COMMAND(cmd_push),
COMMAND(cmd_not),
COMMAND(cmd_add),
/* 09 */
COMMAND(cmd_sub),
COMMAND(cmd_mul),
COMMAND(cmd_div),
COMMAND(cmd_mod),
/* 13 */
COMMAND(cmd_band),
COMMAND(cmd_bor),
COMMAND(cmd_bnot),
COMMAND(cmd_lt),
/* 17 */
COMMAND(cmd_eq),
COMMAND(cmd_gt),
COMMAND(cmd_loadConstant),
COMMAND(cmd_loadVariable),
/* 21 */
COMMAND(cmd_getObjectProperty),
COMMAND(cmd_setObjectProperty),
COMMAND(cmd_set),
COMMAND(cmd_print),
/* 25 */
COMMAND(cmd_terpri),
COMMAND(cmd_printNumber),
COMMAND(cmd_vref),
COMMAND(cmd_vset),
/* 29 */
COMMAND(cmd_vsize),
COMMAND(cmd_exit),
COMMAND(cmd_return),
COMMAND(cmd_call),
/* 33 */
COMMAND(cmd_svar),
COMMAND(cmd_sset),
COMMAND(cmd_split),
COMMAND(cmd_snlit),
/* 37 */
COMMAND(cmd_yorn),
COMMAND(cmd_save),
COMMAND(cmd_restore),
COMMAND(cmd_arg),
/* 41 */
COMMAND(cmd_aset),
COMMAND(cmd_tmp),
COMMAND(cmd_tset),
COMMAND(cmd_tspace),
/* 45 */
COMMAND(cmd_class),
COMMAND(cmd_objectp),
COMMAND(cmd_vectorp),
COMMAND(cmd_restart),
/* 49 */
COMMAND(cmd_rand),
COMMAND(cmd_randomize),
COMMAND(cmd_send),
COMMAND(cmd_extend),
/* 53 */
COMMAND(cmd_catch),
COMMAND(cmd_cdone),
COMMAND(cmd_throw),
COMMAND(cmd_functionp),
/* 57 */
COMMAND(cmd_le),
COMMAND(cmd_ge),
COMMAND(cmd_varx),
COMMAND(cmd_setx)
};
_commands = commandProcs;
_commandsMax = ARRAYSIZE(commandProcs) + 1;
if (_vm->getGameID() == GID_RTZ)
_functions = new ScriptFunctionsRtz(_vm);
else if (_vm->getGameID() == GID_LGOP2)
_functions = new ScriptFunctionsLgop2(_vm);
else if (_vm->getGameID() == GID_MANHOLE)
_functions = new ScriptFunctionsMhne(_vm);
else
error("Unsupported GameID");
_functions->setupExternalsTable();
#undef COMMAND
}
ScriptInterpreter::~ScriptInterpreter() {
delete _functions;
}
void ScriptInterpreter::runScript(int16 scriptObjectIndex) {
_terminated = false;
_runningScriptObjectIndex = scriptObjectIndex;
_localStackPos = _stack.getStackPos();
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
_codeIp = _codeBase;
while (!_terminated) {
byte opcode = readByte();
if (opcode >= 1 && opcode <= _commandsMax) {
debug(4, "opcode = %s\n", _commands[opcode - 1].desc);
(this->*_commands[opcode - 1].proc)();
} else {
warning("ScriptInterpreter::runScript(%d) Unknown opcode %02X\n", _runningScriptObjectIndex, opcode);
}
}
}
byte ScriptInterpreter::readByte() {
return *_codeIp++;
}
int16 ScriptInterpreter::readInt16() {
int16 temp = (int16)READ_LE_UINT16(_codeIp);
_codeIp += 2;
debug(4, "readInt16() value = %04X\n", temp);
return temp;
}
void ScriptInterpreter::cmd_branchTrue() {
int16 ofs = readInt16();
if (_stack.top() != 0)
_codeIp = _codeBase + ofs;
}
void ScriptInterpreter::cmd_branchFalse() {
int16 ofs = readInt16();
if (_stack.top() == 0)
_codeIp = _codeBase + ofs;
}
void ScriptInterpreter::cmd_branch() {
int16 ofs = readInt16();
_codeIp = _codeBase + ofs;
}
void ScriptInterpreter::cmd_true() {
_stack.setTop(-1);
}
void ScriptInterpreter::cmd_false() {
_stack.setTop(0);
}
void ScriptInterpreter::cmd_push() {
_stack.push();
}
void ScriptInterpreter::cmd_not() {
if (_stack.top() == 0)
_stack.setTop(-1);
else
_stack.setTop(0);
}
void ScriptInterpreter::cmd_add() {
int16 value = _stack.pop();
_stack.setTop(_stack.top() + value);
}
void ScriptInterpreter::cmd_sub() {
int16 value = _stack.pop();
_stack.setTop(_stack.top() - value);
}
void ScriptInterpreter::cmd_mul() {
int16 value = _stack.pop();
_stack.setTop(_stack.top() * value);
}
void ScriptInterpreter::cmd_div() {
int16 value = _stack.pop();
if (value == 0)
_stack.setTop(0);
else
_stack.setTop(_stack.top() / value);
}
void ScriptInterpreter::cmd_mod() {
int16 value = _stack.pop();
if (value == 0)
_stack.setTop(0);
else
_stack.setTop(_stack.top() % value);
}
void ScriptInterpreter::cmd_band() {
int16 value = _stack.pop();
_stack.setTop(_stack.top() & value);
}
void ScriptInterpreter::cmd_bor() {
int16 value = _stack.pop();
_stack.setTop(_stack.top() | value);
}
void ScriptInterpreter::cmd_bnot() {
_stack.setTop(~_stack.top());
}
void ScriptInterpreter::cmd_lt() {
int16 value = _stack.pop();
if (_stack.top() < value)
_stack.setTop(-1);
else
_stack.setTop(0);
}
void ScriptInterpreter::cmd_eq() {
int16 value = _stack.pop();
if (_stack.top() == value)
_stack.setTop(-1);
else
_stack.setTop(0);
}
void ScriptInterpreter::cmd_gt() {
int16 value = _stack.pop();
if (_stack.top() > value)
_stack.setTop(-1);
else
_stack.setTop(0);
}
void ScriptInterpreter::cmd_loadConstant() {
int16 value = readInt16();
debug(4, "value = %04X (%d)\n", value, value);
_stack.setTop(value);
}
void ScriptInterpreter::cmd_loadVariable() {
int16 variable = readInt16();
int16 value = _vm->_dat->getVar(variable);
debug(4, "variable = %d; value = %d (%04X)\n", variable, value, value); fflush(stdout);
_stack.setTop(value);
}
void ScriptInterpreter::cmd_getObjectProperty() {
int16 propertyId = _stack.pop();
int16 objectIndex = _stack.top();
int16 value = _vm->_dat->getObjectProperty(objectIndex, propertyId);
debug(4, "value = %04X(%d)\n", value, value);
_stack.setTop(value);
}
void ScriptInterpreter::cmd_setObjectProperty() {
int16 value = _stack.pop();
int16 propertyId = _stack.pop();
int16 objectIndex = _stack.top();
value = _vm->_dat->setObjectProperty(objectIndex, propertyId, value);
_stack.setTop(value);
}
void ScriptInterpreter::cmd_set() {
int16 variable = readInt16();
debug(4, "var(%d) = %04d (%d)\n", variable, _stack.top(), _stack.top());
_vm->_dat->setVar(variable, _stack.top());
}
void ScriptInterpreter::cmd_print() {
// TODO: This opcode was used for printing debug messages
Object *obj = _vm->_dat->getObject(_stack.top());
const char *text = obj->getString();
debug(4, "%s", text); fflush(stdout);
_stack.setTop(0);
}
void ScriptInterpreter::cmd_terpri() {
// TODO: This opcode was used for printing debug messages
debug(4, "\n");
_stack.setTop(0);
}
void ScriptInterpreter::cmd_printNumber() {
// TODO: This opcode was used for printing debug messages
debug(4, "%d", _stack.top());
}
void ScriptInterpreter::cmd_vref() {
int16 value = 0;
int16 index = _stack.pop();
int16 objectIndex = _stack.top();
debug(4, "index = %d; objectIndex = %d\n", index, objectIndex); fflush(stdout);
if (objectIndex > 0) {
Object *obj = _vm->_dat->getObject(objectIndex);
value = obj->getVectorItem(index);
}
_stack.setTop(value);
debug(4, "--> value = %d\n", value); fflush(stdout);
}
void ScriptInterpreter::cmd_vset() {
int16 value = _stack.pop();
int16 index = _stack.pop();
int16 objectIndex = _stack.top();
debug(4, "index = %d; objectIndex = %d; value = %d\n", index, objectIndex, value); fflush(stdout);
if (objectIndex > 0) {
Object *obj = _vm->_dat->getObject(objectIndex);
obj->setVectorItem(index, value);
}
_stack.setTop(value);
}
void ScriptInterpreter::cmd_vsize() {
int16 objectIndex = _stack.top();
int16 size = 0;
if (objectIndex > 0) {
Object *obj = _vm->_dat->getObject(objectIndex);
size = obj->getVectorSize();
}
_stack.setTop(size);
}
void ScriptInterpreter::cmd_exit() {
warning("Unimplemented command: cmd_exit");
}
void ScriptInterpreter::cmd_return() {
// TODO: Check if returning from main function
int16 funcResult = _stack.top();
_stack.setStackPos(_localStackPos);
_localStackPos = kScriptStackLimit - _stack.pop();
_runningScriptObjectIndex = _stack.pop();
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
_codeIp = _codeBase + _stack.pop();
byte argc = _stack.pop();
_stack.free(argc);
_stack.setTop(funcResult);
debug(4, "LEAVE: stackPtr = %d; _localStackPos = %d\n\n\n", _stack.getStackPos(), _localStackPos);
}
void ScriptInterpreter::cmd_call() {
debug(4, "\n\n\nENTER: stackPtr = %d; _localStackPos = %d\n", _stack.getStackPos(), _localStackPos);
byte argc = readByte();
_stack.push(argc);
_stack.push(_codeIp - _codeBase);
_stack.push(_runningScriptObjectIndex);
_stack.push(kScriptStackLimit - _localStackPos);
_localStackPos = _stack.getStackPos();
_runningScriptObjectIndex = _stack.peek(_localStackPos + argc + 4);
debug(4, "argc = %d; _runningScriptObjectIndex = %04X\n", argc, _runningScriptObjectIndex); fflush(stdout);
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
_codeIp = _codeBase;
}
void ScriptInterpreter::cmd_svar() {
warning("Unimplemented command: cmd_svar");
}
void ScriptInterpreter::cmd_sset() {
warning("Unimplemented command: cmd_sset");
}
void ScriptInterpreter::cmd_split() {
warning("Unimplemented command: cmd_split");
}
void ScriptInterpreter::cmd_snlit() {
warning("Unimplemented command: cmd_snlit");
}
void ScriptInterpreter::cmd_yorn() {
warning("Unimplemented command: cmd_yorn");
}
void ScriptInterpreter::cmd_save() {
warning("Unimplemented command: cmd_save");
_stack.setTop(0);
}
void ScriptInterpreter::cmd_restore() {
warning("Unimplemented command: cmd_restore");
}
void ScriptInterpreter::cmd_arg() {
int16 argIndex = readByte();
debug(4, "argIndex = %d; value = %04X (%d)\n", argIndex, _stack.peek(_localStackPos + 4 + argIndex), _stack.peek(_localStackPos + 4 + argIndex));
_stack.setTop(_stack.peek(_localStackPos + 4 + argIndex));
}
void ScriptInterpreter::cmd_aset() {
int16 argIndex = readByte();
debug(4, "argIndex = %d; value = %d\n", argIndex, _stack.peek(_localStackPos + 4 + argIndex));
_stack.poke(_localStackPos + 4 + argIndex, _stack.top());
}
void ScriptInterpreter::cmd_tmp() {
int16 tempIndex = readByte();
debug(4, "tempIndex = %d; value = %d\n", tempIndex, _stack.peek(_localStackPos - tempIndex - 1));
_stack.setTop(_stack.peek(_localStackPos - tempIndex - 1));
}
void ScriptInterpreter::cmd_tset() {
int16 tempIndex = readByte();
debug(4, "tempIndex = %d; value = %d\n", tempIndex, _stack.top());
_stack.poke(_localStackPos - tempIndex - 1, _stack.top());
}
void ScriptInterpreter::cmd_tspace() {
int16 tempCount = readByte();
debug(4, "tempCount = %d\n", tempCount);
_stack.alloc(tempCount);
}
void ScriptInterpreter::cmd_class() {
warning("Unimplemented command: cmd_class");
}
void ScriptInterpreter::cmd_objectp() {
warning("Unimplemented command: cmd_objectp");
}
void ScriptInterpreter::cmd_vectorp() {
warning("Unimplemented command: cmd_vectorp");
}
void ScriptInterpreter::cmd_restart() {
warning("Unimplemented command: cmd_restart");
}
void ScriptInterpreter::cmd_rand() {
_stack.setTop(_vm->_rnd->getRandomNumber(_stack.top() - 1));
}
void ScriptInterpreter::cmd_randomize() {
_vm->_rnd->setSeed(g_system->getMillis());
_stack.setTop(0);
}
void ScriptInterpreter::cmd_send() {
debug(4, "\n\n\nENTER: stackPtr = %d; _localStackPos = %d\n", _stack.getStackPos(), _localStackPos);
byte argc = readByte();
debug(4, "argc = %d\n", argc);
_stack.push(argc);
_stack.push(_codeIp - _codeBase);
_stack.push(_runningScriptObjectIndex);
_stack.push(kScriptStackLimit - _localStackPos);
_localStackPos = _stack.getStackPos();
int16 propertyId = _stack.peek(_localStackPos + argc + 2);
int16 objectIndex = _stack.peek(_localStackPos + argc + 4);
debug(4, "objectIndex = %d (%04X); propertyId = %d(%04X)\n", objectIndex, objectIndex, propertyId, propertyId); fflush(stdout);
if (objectIndex != 0) {
objectIndex = _vm->_dat->getObject(objectIndex)->getClass();
} else {
objectIndex = _stack.peek(_localStackPos + argc + 3);
}
debug(4, "--> objectIndex = %d(%04X)\n", objectIndex, objectIndex); fflush(stdout);
if (objectIndex != 0) {
_runningScriptObjectIndex = _vm->_dat->getObjectProperty(objectIndex, propertyId);
if (_runningScriptObjectIndex != 0) {
_codeBase = _vm->_dat->getObject(_runningScriptObjectIndex)->getData();
_codeIp = _codeBase;
} else {
_stack.push(0);
cmd_return();
}
} else {
_stack.push(0);
cmd_return();
}
}
void ScriptInterpreter::cmd_extend() {
byte func = readByte();
byte argc = readByte();
int16 *argv = _stack.getStackPtr();
//debug(4, "func = %d (%s); argc = %d\n", func, extendFuncNames[func], argc);
debug(4, "func = %d; argc = %d\n", func, argc);
for (int i = 0; i < argc; i++)
debug(4, "argv[%02d] = %04X (%d)\n", i, argv[i], argv[i]);
int16 result = _functions->callFunction(func, argc, argv);
debug(4, "result = %04X (%d)\n", result, result);
_stack.free(argc);
_stack.setTop(result);
}
void ScriptInterpreter::cmd_catch() {
warning("Unimplemented command: cmd_catch");
}
void ScriptInterpreter::cmd_cdone() {
warning("Unimplemented command: cmd_cdone");
}
void ScriptInterpreter::cmd_throw() {
warning("Unimplemented command: cmd_throw");
}
void ScriptInterpreter::cmd_functionp() {
warning("Unimplemented command: cmd_functionp");
}
void ScriptInterpreter::cmd_le() {
int16 value = _stack.pop();
if (_stack.top() <= value)
_stack.setTop(-1);
else
_stack.setTop(0);
}
void ScriptInterpreter::cmd_ge() {
int16 value = _stack.pop();
if (_stack.top() >= value)
_stack.setTop(-1);
else
_stack.setTop(0);
}
void ScriptInterpreter::cmd_varx() {
warning("Unimplemented command: cmd_varx");
}
void ScriptInterpreter::cmd_setx() {
warning("Unimplemented command: cmd_setx");
}
} // End of namespace Made