scummvm/kyra/script.cpp

507 lines
14 KiB
C++
Raw Normal View History

/* ScummVM - Kyrandia Interpreter
* Copyright (C) 2003-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "stdafx.h"
#include "kyra.h"
#include "script.h"
#include "resource.h"
#include "common/stream.h"
#define COMMAND(x) { &VMContext::x, #x }
#define OPCODE(x) { &VMContext::x, #x }
namespace Kyra {
VMContext::VMContext(KyraEngine* engine) {
_engine = engine;
// now we create a list of all Command/Opcode procs and so
static CommandEntry commandProcs[] = {
// 0
COMMAND(c1_goToLine),
COMMAND(c1_setReturn),
COMMAND(c1_pushRetRec),
COMMAND(c1_push),
COMMAND(c1_push),
COMMAND(c1_pushVar),
COMMAND(c1_pushFrameNeg),
COMMAND(c1_pushFramePos),
COMMAND(c1_popRetRec),
COMMAND(c1_popVar),
// 10
COMMAND(c1_popFrameNeg),
COMMAND(c1_popFramePos),
COMMAND(c1_addToSP),
COMMAND(c1_subFromSP),
COMMAND(c1_execOpcode),
COMMAND(c1_ifNotGoTo),
COMMAND(c1_negate),
COMMAND(c1_evaluate),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 20
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 30
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 40
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 50
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 60
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 70
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 80
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 90
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 100
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 110
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 120
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 130
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 140
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 150
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 160
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 170
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 180
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 190
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 200
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 210
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 220
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 230
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 240
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
// 250
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
COMMAND(c1_unknownCommand),
{ 0, 0 }
};
_scriptFile = NULL;
_scriptFileSize = 0;
}
void VMContext::loadScript(const char* file) {
printf("a\n");
if (_scriptFile) {
delete [] _scriptFile;
_scriptFileSize = 0;
}
debug("--------------");
// loads the new file
_scriptFile = _engine->resManager()->fileData(file, &_scriptFileSize);
printf("c\n");
if (!_scriptFileSize || !_scriptFile) {
error("couldn't load script file '%s'", file);
}
Common::MemoryReadStream script(_scriptFile, _scriptFileSize);
memset(_chunks, 0, sizeof(ScriptChunk) * kCountChunkTypes);
uint8 chunkName[sizeof("EMC2ORDR") + 1];
// so lets look for our chunks :)
while(true) {
if (script.eof()) {
break;
}
// lets read only the first 4 chars
script.read(chunkName, sizeof(uint8) * 4);
chunkName[4] = '\0';
debug("chunk name(4 chars): '%s'", chunkName);
// check name of chunk
if (!scumm_stricmp((char*)chunkName, "FORM")) {
// FreeKyra swaps the size I only read it in BigEndian :)
_chunks[kForm]._size = script.readUint32BE();
debug("_chunks[kForm]._size = %d", _chunks[kForm]._size);
} else if (!scumm_stricmp((char*)chunkName, "TEXT")) {
uint32 text_size = script.readUint32BE();
text_size += text_size % 2 != 0 ? 1 : 0;
_chunks[kText]._data = _scriptFile + script.pos();
_chunks[kText]._size = READ_BE_UINT16(_chunks[kText]._data) >> 1;
_chunks[kText]._additional = _chunks[kText]._data + (_chunks[kText]._size << 1);
debug("_chunks[kText]._size = %d, real chunk size = %d", _chunks[kText]._size, text_size);
script.seek(script.pos() + text_size);
} else if (!scumm_stricmp((char*)chunkName, "DATA")) {
_chunks[kData]._size = script.readUint32BE();
_chunks[kData]._data = _scriptFile + script.pos();
debug("_chunks[kData]._size = %d", _chunks[kData]._size);
// mostly it will be the end of the file because all files should end with a 'DATA' chunk
script.seek(script.pos() + _chunks[kData]._size);
} else {
// read next 4 chars
script.read(&chunkName[4], sizeof(uint8) * 4);
chunkName[8] = '\0';
debug("chunk name(8 chars): '%s'", chunkName);
if (!scumm_stricmp((char*)chunkName, "EMC2ORDR")) {
_chunks[kEmc2Ordr]._size = script.readUint32BE() >> 1;
_chunks[kEmc2Ordr]._data = _scriptFile + script.pos();
debug("_chunks[kEmc2Ordr]._size = %d, real chunk size = %d", _chunks[kEmc2Ordr]._size, _chunks[kEmc2Ordr]._size * 2);
script.seek(script.pos() + _chunks[kEmc2Ordr]._size * 2);
} else {
// any unkown chunk or problems with seeking through the file
error("unknown chunk");
}
}
}
// so file loaded
debug("--------------");
}
int32 VMContext::param(int32 index) {
if (_stackPos - index + 1 >= 16 || _stackPos - index + 1 < 0)
return -0xFFFF;
return _stack[_stackPos - index + 1];
}
const char* VMContext::stringAtIndex(int32 index) {
if (index < 0 || (uint32)index >= _chunks[kText]._size)
return 0;
return (char*)(_chunks[kText]._additional + _chunks[kText]._data[index]);
}
bool VMContext::startScript(int32 func) {
if ((uint32)func >= _chunks[kEmc2Ordr]._size || func < 0) {
debug("script doesn't support function %d", func);
return false;
}
_instructionPos = (READ_BE_UINT16(&_chunks[kEmc2Ordr]._data[func]) << 1) + 2;
_stackPos = 0;
_tempPos = 0;
_delay = 0;
_scriptState = kScriptRunning;
return true;
}
uint32 VMContext::contScript(void) {
uint8* script_start = _chunks[kData]._data;
assert(script_start);
uint32 scriptStateAtStart = _scriptState;
// runs the script
while(true) {
if ((uint32)_instructionPos > _chunks[kData]._size) {
debug("_instructionPos( = %d) > _chunks[kData]._size( = %d)", _instructionPos, _chunks[kData]._size);
_error = true;
break;
}
_currentCommand = *(script_start + _instructionPos++);
// gets out
if (_currentCommand & 0x80) {
_argument = ((_currentCommand & 0x0F) << 8) | *(script_start + _instructionPos++);
_currentCommand &= 0xF0;
} else if (_currentCommand & 0x40) {
_argument = *(script_start + _instructionPos++);
} else if (_currentCommand & 0x20) {
_instructionPos++;
uint16 tmp = *(uint16*)(script_start + _instructionPos);
tmp &= 0xFF7F;
_argument = READ_BE_UINT16(&tmp);
_instructionPos += 2;
} else {
debug("unknown way of getting the command");
}
_currentCommand &= 0x1f;
CommandProc currentProc = _commands[_currentCommand].proc;
(this->*currentProc)();
if (_error) {
_scriptState = kScriptError;
break;
}
if (scriptStateAtStart != _scriptState) {
break;
}
}
return _scriptState;
}
} // end of namespace Kyra