mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 05:34:00 +00:00
1285 lines
27 KiB
C++
1285 lines
27 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.
|
|
*
|
|
*
|
|
*/
|
|
|
|
// TODO: Clean up game variable handling and move it to ToltecsEngine
|
|
|
|
#include "common/events.h"
|
|
#include "common/keyboard.h"
|
|
#include "common/file.h"
|
|
#include "common/savefile.h"
|
|
#include "common/config-manager.h"
|
|
|
|
#include "base/plugins.h"
|
|
#include "base/version.h"
|
|
|
|
#include "sound/mixer.h"
|
|
|
|
#include "toltecs/toltecs.h"
|
|
#include "toltecs/animation.h"
|
|
#include "toltecs/palette.h"
|
|
#include "toltecs/resource.h"
|
|
#include "toltecs/script.h"
|
|
#include "toltecs/screen.h"
|
|
#include "toltecs/segmap.h"
|
|
|
|
namespace Toltecs {
|
|
|
|
ScriptInterpreter::ScriptInterpreter(ToltecsEngine *vm) : _vm(vm) {
|
|
|
|
_stack = new byte[kScriptStackSize];
|
|
|
|
memset(_slots, 0, sizeof(_slots));
|
|
|
|
_savedSp = 0;
|
|
|
|
}
|
|
|
|
ScriptInterpreter::~ScriptInterpreter() {
|
|
delete[] _stack;
|
|
}
|
|
|
|
void ScriptInterpreter::loadScript(uint resIndex, uint slotIndex) {
|
|
|
|
if (_slots[slotIndex].data) {
|
|
delete[] _slots[slotIndex].data;
|
|
}
|
|
|
|
_slots[slotIndex].resIndex = resIndex;
|
|
byte *scriptData = _vm->_res->load(resIndex);
|
|
_slots[slotIndex].size = _vm->_res->getCurItemSize();
|
|
_slots[slotIndex].data = new byte[_slots[slotIndex].size];
|
|
memcpy(_slots[slotIndex].data, scriptData, _slots[slotIndex].size);
|
|
|
|
}
|
|
|
|
void ScriptInterpreter::runScript(uint slotIndex) {
|
|
|
|
_switchLocalDataNear = true;
|
|
_switchLocalDataFar = false;
|
|
_switchLocalDataToStack = false;
|
|
_cmpBitTest = false;
|
|
|
|
_regs.reg0 = 0;
|
|
_regs.reg1 = 0;
|
|
_regs.reg2 = 0;
|
|
_regs.reg3 = 0;
|
|
_regs.reg4 = slotIndex;
|
|
_regs.reg5 = 0;
|
|
_regs.reg6 = 0;
|
|
_regs.sp = 4096;
|
|
_regs.reg8 = 0;
|
|
|
|
_code = getSlotData(_regs.reg4);
|
|
|
|
while (1) {
|
|
|
|
if (_vm->_movieSceneFlag)
|
|
_vm->_mouseButton = 0;
|
|
|
|
if (_switchLocalDataNear) {
|
|
_switchLocalDataNear = false;
|
|
_localData = getSlotData(_regs.reg4);
|
|
}
|
|
|
|
if (_switchLocalDataFar) {
|
|
_switchLocalDataFar = false;
|
|
_localData = getSlotData(_regs.reg5);
|
|
_switchLocalDataNear = true;
|
|
}
|
|
|
|
if (_switchLocalDataToStack) {
|
|
_switchLocalDataToStack = false;
|
|
_localData = _stack + 2;
|
|
_switchLocalDataNear = true;
|
|
}
|
|
|
|
byte opcode = readByte();
|
|
execOpcode(opcode);
|
|
|
|
// Call updateScreen roughly every 10ms else the mouse cursor will be jerky
|
|
if (_vm->_system->getMillis() % 10 == 0)
|
|
_vm->_system->updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
byte ScriptInterpreter::readByte() {
|
|
return *_code++;
|
|
}
|
|
|
|
int16 ScriptInterpreter::readInt16() {
|
|
int16 value = READ_LE_UINT16(_code);
|
|
_code += 2;
|
|
return value;
|
|
}
|
|
|
|
void look(byte *code) {
|
|
char ln[256];
|
|
snprintf(ln, 256, "\t\t\t%02X %02X %02X %02X %02X %02X %02X %02X",
|
|
code[0], code[1], code[2], code[3], code[4], code[5], code[6], code[7]);
|
|
debug(1, "%s", ln);
|
|
}
|
|
|
|
void ScriptInterpreter::execOpcode(byte opcode) {
|
|
|
|
#if 0
|
|
char ln[256];
|
|
snprintf(ln, 256, "\t\t\t%02X %02X %02X %02X %02X %02X %02X %02X",
|
|
_code[0], _code[1], _code[2], _code[3], _code[4], _code[5], _code[6], _code[7]);
|
|
debug(1, "%s", ln);
|
|
#endif
|
|
|
|
int16 ofs;
|
|
|
|
debug(1, "opcode = %d", opcode);
|
|
|
|
switch (opcode) {
|
|
case 0:
|
|
{
|
|
// ok
|
|
_subCode = _code;
|
|
byte length = readByte();
|
|
debug(1, "length = %d", length);
|
|
uint16 kernelOpcode = readInt16();
|
|
debug(1, "callKernel %d", kernelOpcode);
|
|
execKernelOpcode(kernelOpcode);
|
|
_code += length - 2;
|
|
break;
|
|
}
|
|
case 1:
|
|
// ok
|
|
_regs.reg0 = readInt16();
|
|
debug(1, "mov reg0, #%d", _regs.reg0);
|
|
break;
|
|
case 2:
|
|
// ok
|
|
_regs.reg1 = readInt16();
|
|
debug(1, "mov reg1, #%d", _regs.reg1);
|
|
break;
|
|
case 3:
|
|
// ok
|
|
_regs.reg3 = readInt16();
|
|
debug(1, "mov reg3, #%d", _regs.reg3);
|
|
break;
|
|
case 4:
|
|
// ok
|
|
_regs.reg5 = _regs.reg0;
|
|
debug(1, "mov reg5, reg0");
|
|
break;
|
|
case 5:
|
|
// ok
|
|
_regs.reg3 = _regs.reg0;
|
|
debug(1, "mov reg3, reg0");
|
|
break;
|
|
case 6:
|
|
// ok
|
|
_regs.reg1 = _regs.reg0;
|
|
debug(1, "mov reg1, reg0");
|
|
break;
|
|
case 7:
|
|
_regs.reg1 = localRead16(_regs.reg3);
|
|
debug(1, "mov reg1, *%d", _regs.reg3);
|
|
break;
|
|
case 8:
|
|
localWrite16(_regs.reg3, _regs.reg0);
|
|
break;
|
|
case 9:
|
|
localWrite16(readInt16(), _regs.reg0);
|
|
break;
|
|
case 10:
|
|
localWrite8(readInt16(), _regs.reg0);
|
|
break;
|
|
case 11:
|
|
localWrite16(readInt16(), _regs.reg5);
|
|
break;
|
|
case 12:
|
|
localWrite16(readInt16(), _regs.reg4);
|
|
break;
|
|
case 13:
|
|
localWrite16(readInt16(), _regs.reg3);
|
|
break;
|
|
case 14:
|
|
_regs.reg3 = localRead16(readInt16());
|
|
break;
|
|
case 15:
|
|
_regs.reg2 = localRead16(_regs.reg1);
|
|
break;
|
|
case 16:
|
|
_regs.reg2 = localRead16(_regs.reg1 + readInt16());
|
|
break;
|
|
case 17:
|
|
_regs.reg2 = _regs.reg0;
|
|
break;
|
|
case 18:
|
|
_regs.reg0 += readInt16();
|
|
break;
|
|
case 19:
|
|
localWrite16(_regs.reg3, localRead16(_regs.reg3) + _regs.reg0);
|
|
break;
|
|
case 20:
|
|
_regs.reg0 += _regs.reg2;
|
|
break;
|
|
case 21:
|
|
_regs.reg3 += _regs.sp;
|
|
break;
|
|
case 22:
|
|
_regs.reg1 += _regs.sp;
|
|
break;
|
|
case 23:
|
|
localWrite16(_regs.reg3, localRead16(_regs.reg3) - _regs.reg0);
|
|
break;
|
|
case 24:
|
|
_regs.reg0 /= readInt16();
|
|
break;
|
|
case 25:
|
|
localWrite16(_regs.reg3, localRead16(_regs.reg3) / _regs.reg0);
|
|
break;
|
|
case 26:
|
|
// NOP
|
|
break;
|
|
case 27:
|
|
_regs.reg0 *= readInt16();
|
|
break;
|
|
case 28:
|
|
localWrite16(_regs.reg3, localRead16(_regs.reg3) * _regs.reg0);
|
|
break;
|
|
case 29:
|
|
_regs.reg0 *= _regs.reg2;
|
|
break;
|
|
case 30:
|
|
localWrite16(_regs.reg3, localRead16(_regs.reg3) + 1);
|
|
break;
|
|
case 31:
|
|
localWrite16(_regs.reg3, localRead16(_regs.reg3) - 1);
|
|
break;
|
|
case 32:
|
|
_switchLocalDataFar = true;
|
|
break;
|
|
case 33:
|
|
_switchLocalDataToStack = true;
|
|
break;
|
|
case 34:
|
|
pushInt16(_regs.reg0);
|
|
debug(1, "pushw reg0");
|
|
break;
|
|
case 35:
|
|
pushInt16(_regs.reg1);
|
|
debug(1, "pushw reg1");
|
|
break;
|
|
case 36:
|
|
_regs.reg1 = popInt16();
|
|
debug(1, "popw reg1");
|
|
break;
|
|
case 37:
|
|
_regs.reg0 = popInt16();
|
|
debug(1, "popw reg0");
|
|
break;
|
|
case 38:
|
|
_regs.reg2 = -_regs.reg2;
|
|
break;
|
|
case 39:
|
|
_regs.reg8 = readInt16();
|
|
_cmpBitTest = false;
|
|
break;
|
|
case 40:
|
|
_regs.reg8 = _regs.reg0;
|
|
_cmpBitTest = false;
|
|
break;
|
|
case 41:
|
|
_regs.reg8 = readInt16();
|
|
_cmpBitTest = true;
|
|
break;
|
|
case 42:
|
|
_regs.reg8 = _regs.reg0;
|
|
_cmpBitTest = true;
|
|
break;
|
|
case 43:
|
|
debug(1, "retn (slot: %d; ofs: %04X)\n", _regs.reg4, _regs.reg0);
|
|
_code = getSlotData(_regs.reg4) + _regs.reg0;
|
|
break;
|
|
case 44:
|
|
debug(1, "retf (slot: %d; ofs: %04X)\n", _regs.reg5, _regs.reg0);
|
|
_code = getSlotData(_regs.reg5) + _regs.reg0;
|
|
_regs.reg4 = _regs.reg5;
|
|
_switchLocalDataNear = true;
|
|
break;
|
|
case 45:
|
|
debug(1, "callnear %04X (slot: %d; ofs: %04X)\n", _regs.reg0, _regs.reg4, _regs.reg0);
|
|
pushInt16(_code - getSlotData(_regs.reg4));
|
|
pushInt16(_regs.reg4);
|
|
_code = getSlotData(_regs.reg4) + _regs.reg0;
|
|
break;
|
|
case 46:
|
|
debug(1, "callfar %04X (slot: %d; ofs: %04X)\n", _regs.reg0, _regs.reg5, _regs.reg0);
|
|
pushInt16(_code - getSlotData(_regs.reg4));
|
|
pushInt16(_regs.reg4);
|
|
_code = getSlotData(_regs.reg5) + _regs.reg0;
|
|
_regs.reg4 = _regs.reg5;
|
|
_switchLocalDataNear = true;
|
|
break;
|
|
case 47:
|
|
_regs.reg4 = popInt16();
|
|
ofs = popInt16();
|
|
_code = getSlotData(_regs.reg4) + ofs;
|
|
debug(1, "ret (slot: %d; ofs: %04X)\n", _regs.reg4, ofs);
|
|
//_code = getSlotData(_regs.reg4) + popInt16();
|
|
_switchLocalDataNear = true;
|
|
break;
|
|
case 48:
|
|
_regs.reg4 = popInt16();
|
|
ofs = popInt16();
|
|
_code = getSlotData(_regs.reg4) + ofs;
|
|
debug(1, "retsp (slot: %d; ofs: %04X)\n", _regs.reg4, ofs);
|
|
//_code = getSlotData(_regs.reg4) + popInt16();
|
|
_regs.sp += _regs.reg0;
|
|
_switchLocalDataNear = true;
|
|
break;
|
|
case 49:
|
|
ofs = readByte();
|
|
_code += ofs;
|
|
break;
|
|
case 50:
|
|
if (_cmpBitTest) {
|
|
_regs.reg1 &= _regs.reg8;
|
|
if (_regs.reg1 == 0)
|
|
_code += 4;
|
|
} else {
|
|
if (_regs.reg1 == _regs.reg8)
|
|
_code += 4;
|
|
}
|
|
_code++;
|
|
break;
|
|
case 51:
|
|
if (_cmpBitTest) {
|
|
_regs.reg1 &= _regs.reg8;
|
|
if (_regs.reg1 != 0)
|
|
_code += 4;
|
|
} else {
|
|
if (_regs.reg1 != _regs.reg8)
|
|
_code += 4;
|
|
}
|
|
_code++;
|
|
break;
|
|
case 52:
|
|
if ((uint16)_regs.reg1 >= (uint16)_regs.reg8)
|
|
_code += 4;
|
|
_code++;
|
|
break;
|
|
case 53:
|
|
if ((uint16)_regs.reg1 <= (uint16)_regs.reg8)
|
|
_code += 4;
|
|
_code++;
|
|
break;
|
|
case 54:
|
|
if ((uint16)_regs.reg1 < (uint16)_regs.reg8)
|
|
_code += 4;
|
|
_code++;
|
|
break;
|
|
case 55:
|
|
if ((uint16)_regs.reg1 > (uint16)_regs.reg8)
|
|
_code += 4;
|
|
_code++;
|
|
break;
|
|
default:
|
|
{
|
|
/*
|
|
FILE *ex = fopen("error.0", "wb");
|
|
fwrite(_code - 8, 4096, 1, ex);
|
|
fclose(ex);
|
|
*/
|
|
error("Invalid opcode %d", opcode);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void ScriptInterpreter::execKernelOpcode(uint16 kernelOpcode) {
|
|
|
|
switch (kernelOpcode) {
|
|
|
|
case 0:
|
|
case 1:
|
|
// ok, NOPs
|
|
break;
|
|
|
|
case 2:// ok
|
|
{
|
|
debug(0, "o2_getGameVar(%d, %d)", arg16(3), arg16(5));
|
|
int16 value = getGameVar(arg16(3));
|
|
localWrite16(arg16(5), value);
|
|
break;
|
|
}
|
|
|
|
case 3:// ok
|
|
{
|
|
debug(0, "o2_setGameVar(%d, %d)", arg16(3), arg16(5));
|
|
VarType varType = getGameVarType(arg16(3));
|
|
int16 value;
|
|
if (varType == vtByte)
|
|
value = arg8(5);
|
|
else if (varType == vtWord)
|
|
value = arg16(5);
|
|
setGameVar(arg16(3), value);
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
|
|
debug(0, "o2_updateScreen()");
|
|
|
|
// TODO? updateSamples();
|
|
|
|
_vm->_screen->updateShakeScreen();
|
|
|
|
if (_vm->_quitGame)
|
|
return;
|
|
|
|
if (!_vm->_movieSceneFlag)
|
|
_vm->updateInput();
|
|
else
|
|
_vm->_mouseButton = 0;
|
|
|
|
// TODO? Check keyb
|
|
|
|
_vm->_counter01--;
|
|
if (_vm->_counter01 <= 0) {
|
|
_vm->_counter01 = MIN(_vm->_counter02, 30);
|
|
_vm->_counter02 = 0;
|
|
_vm->updateScreen();
|
|
_vm->_flag01 = 1;
|
|
_vm->_system->delayMillis(5);
|
|
_vm->_counter02 = 1; // ?
|
|
} else {
|
|
_vm->_screen->clearSprites();
|
|
_vm->_flag01 = 0;
|
|
//_vm->_system->updateScreen();
|
|
}
|
|
|
|
// TODO
|
|
break;
|
|
|
|
}
|
|
|
|
case 5:// ok
|
|
{
|
|
debug(0, "o2_getRandomNumber(%d)", arg16(3));
|
|
localWrite16(arg16(5), _vm->_rnd->getRandomNumber(arg16(3) - 1));
|
|
break;
|
|
}
|
|
|
|
case 6:// ok
|
|
{
|
|
debug(0, "o2_printText()");
|
|
_vm->_screen->printText((byte*)localPtr(arg16(3)));
|
|
break;
|
|
}
|
|
|
|
case 7:// ok
|
|
{
|
|
debug(0, "o2_updateVerbLine(slot: %d; offset: %04X)", arg16(5), arg16(3));
|
|
_vm->_screen->updateVerbLine(arg16(5), arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 8:// ok
|
|
{
|
|
debug(0, "o2_setFontColor(%d)", arg8(3));
|
|
_vm->_screen->_fontColor1 = 0;
|
|
_vm->_screen->_fontColor2 = arg8(3);
|
|
break;
|
|
}
|
|
|
|
case 9:// ok
|
|
{
|
|
debug(0, "o2_getTalkTextDuration()");
|
|
int16 duration = _vm->_screen->getTalkTextDuration();
|
|
localWrite16(arg16(3), duration);
|
|
break;
|
|
}
|
|
|
|
case 10:// ok
|
|
{
|
|
debug(0, "o2_talk(slot: %d; offset: %d)", arg16(5), arg16(3));
|
|
_vm->talk(arg16(5), arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 11:// ok
|
|
{
|
|
debug(0, "o2_findFragment(%d, %d)", arg16(3), arg16(5));
|
|
localWrite16(arg16(5), _vm->_palette->findFragment(arg16(3)));
|
|
break;
|
|
}
|
|
|
|
case 12:// ok
|
|
{
|
|
debug(0, "o2_clearPaletteFragments()");
|
|
_vm->_palette->clearFragments();
|
|
break;
|
|
}
|
|
|
|
case 13:// ok
|
|
{
|
|
debug(0, "o2_addFragment(%d, %d)", arg16(3), arg16(5));
|
|
_vm->_palette->addFragment(arg16(3), arg16(5));
|
|
break;
|
|
}
|
|
|
|
case 14:// ok
|
|
{
|
|
debug(0, "o2_setDeltaPalette(animPalette, %d, %d, %d, %d)", arg8(6), arg8(5), arg8(4), arg8(3));
|
|
_vm->_palette->setDeltaPalette(_vm->_palette->getAnimPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
|
|
break;
|
|
}
|
|
|
|
case 16:// ok
|
|
{
|
|
debug(0, "o2_buildColorTransTable(%d, %d, %d)", arg8(4), (char)arg8(3), arg8(5));
|
|
_vm->_palette->buildColorTransTable(arg8(4), (char)arg8(3), arg8(5));
|
|
break;
|
|
}
|
|
|
|
case 17:// ok
|
|
{
|
|
debug(0, "o2_setDeltaPalette(mainPalette, %d, %d, %d, %d)", arg8(6), arg8(5), arg8(4), arg8(3));
|
|
_vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
|
|
break;
|
|
}
|
|
|
|
case 18:// ok
|
|
{
|
|
debug(0, "o2_loadScript(resIndex: %d; slotIndex: %d)", arg16(4), arg8(3));
|
|
int16 codeOfs = _code - getSlotData(_regs.reg4);
|
|
loadScript(arg16(4), arg8(3));
|
|
_code = getSlotData(_regs.reg4) + codeOfs;
|
|
_switchLocalDataNear = true;
|
|
break;
|
|
}
|
|
|
|
case 19:// ok
|
|
{
|
|
debug(0, "o2_registerFont(%d, %d)", arg8(3), arg16(4));
|
|
_vm->_screen->registerFont(arg8(3), arg16(4));
|
|
break;
|
|
}
|
|
|
|
case 20:// ok
|
|
{
|
|
debug(0, "o2_loadAddPalette(startIndex: %d; resIndex: %d)", arg8(3), arg16(4));
|
|
_vm->_palette->loadAddPalette(arg16(4), arg8(3));
|
|
break;
|
|
}
|
|
|
|
case 21:// TODO
|
|
{
|
|
debug(0, "o2_loadScene(resIndex: %d; flag: %d)", arg16(4), arg8(3));
|
|
if (arg8(3) == 0) {
|
|
_vm->loadScene(arg16(4));
|
|
} else {
|
|
_vm->_screen->loadMouseCursor(arg16(4));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 22:// ok
|
|
{
|
|
debug(0, "o2_setGuiHeight(%d)", arg8(3));
|
|
_vm->setGuiHeight(arg8(3));
|
|
break;
|
|
}
|
|
|
|
case 23:// ok
|
|
{
|
|
debug(0, "o2_findMouseInRectIndex1(offset: %d; slot: %d; elemSize: %d; var: %d; index: %d)", arg16(3), arg16(5), arg16(7), arg16(9), arg16(11));
|
|
int16 index = -1;
|
|
if (_vm->_mouseY < _vm->_cameraHeight) {
|
|
index = _vm->findRectAtPoint(getSlotData(arg16(5)) + arg16(3),
|
|
_vm->_mouseX + _vm->_cameraX,
|
|
_vm->_mouseY + _vm->_cameraY,
|
|
arg16(11) + 1, arg16(7));
|
|
}
|
|
localWrite16(arg16(9), index);
|
|
break;
|
|
}
|
|
|
|
case 24:// ok
|
|
{
|
|
debug(0, "o2_findMouseInRectIndex2(offset: %d, slot: %d, elemSize: %d, var: %d)", arg16(3), arg16(5), arg16(7), arg16(9));
|
|
int16 index = -1;
|
|
|
|
debug(0, "_vm->_mouseDisabled = %d", _vm->_mouseDisabled);
|
|
|
|
/* FIXME: This opcode is called after the Revistronic logo at the beginning,
|
|
but at the slot/offset there's bytecode and not a rect array as expected.
|
|
To avoid crashes we skip searching the rectangle index for now when scene 215 is active.
|
|
I don't know yet whether this is a bug in the original engine as well or just here.
|
|
Needs some more checking.
|
|
*/
|
|
if (_vm->_sceneResIndex != 215) {
|
|
if (_vm->_mouseY < _vm->_cameraHeight) {
|
|
index = _vm->findRectAtPoint(getSlotData(arg16(5)) + arg16(3),
|
|
_vm->_mouseX + _vm->_cameraX,
|
|
_vm->_mouseY + _vm->_cameraY,
|
|
0, arg16(7));
|
|
}
|
|
}
|
|
|
|
localWrite16(arg16(9), index);
|
|
|
|
break;
|
|
}
|
|
|
|
case 25:// ok
|
|
{
|
|
debug(0, "o2_drawGuiImage(x: %d; y: %d; resIndex: %d)", arg16(5), arg16(3), arg16(7));
|
|
_vm->_screen->drawGuiImage(arg16(5), arg16(3), arg16(7));
|
|
break;
|
|
}
|
|
|
|
case 26:// ok
|
|
{
|
|
debug(0, "o2_addAnimatedSpriteNoLoop(2; x: %d; y: %d; fragmentId: %d; offset: %d)", arg16(5), arg16(3), arg16(7), arg16(9));
|
|
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte*)localPtr(0), (int16*)localPtr(arg16(9)), false, 2);
|
|
break;
|
|
}
|
|
|
|
case 27:// ok
|
|
{
|
|
debug(0, "o2_addAnimatedSprite(2; x: %d; y: %d; fragmentId: %d; offset: %d)", arg16(5), arg16(3), arg16(7), arg16(9));
|
|
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte*)localPtr(0), (int16*)localPtr(arg16(9)), true, 2);
|
|
break;
|
|
}
|
|
|
|
case 28:// ok
|
|
{
|
|
debug(1, "o2_addStaticSprite()");
|
|
_vm->_screen->addStaticSprite(_subCode + 3);
|
|
break;
|
|
}
|
|
|
|
case 29:// ok
|
|
{
|
|
debug(0, "o2_addAnimatedSprite(1; x: %d; y: %d; value: %d; offset: %d)", arg16(5), arg16(3), arg16(7), arg16(9));
|
|
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte*)localPtr(0), (int16*)localPtr(arg16(9)), true, 1);
|
|
break;
|
|
}
|
|
|
|
case 30:// ok
|
|
{
|
|
debug(0, "o2_findPath(sourceX: %d; sourceY: %d; destX: %d; destY: %d; slotIndex: %d; offset: %d)", arg16(5), arg16(3), arg16(9), arg16(7), arg16(13), arg16(11));
|
|
_vm->_segmap->findPath((int16*)(getSlotData(arg16(13)) + arg16(11)), arg16(9), arg16(7), arg16(5), arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 31:// ok
|
|
{
|
|
debug(0, "o2_walk()");
|
|
_vm->walk(getSlotData(arg16(5)) + arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 32:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraUp()");
|
|
_vm->scrollCameraUp(4);
|
|
break;
|
|
}
|
|
|
|
case 33:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraDown()");
|
|
_vm->scrollCameraDown(4);
|
|
break;
|
|
}
|
|
|
|
case 34:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraLeft()");
|
|
_vm->scrollCameraLeft(4);
|
|
break;
|
|
}
|
|
|
|
case 35:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraRight()");
|
|
_vm->scrollCameraRight(4);
|
|
break;
|
|
}
|
|
|
|
case 36:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraUpEx(%d)", arg16(3));
|
|
_vm->scrollCameraUp(arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 37:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraDownEx(%d)", arg16(3));
|
|
_vm->scrollCameraDown(arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 38:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraLeftEx(%d)", arg16(3));
|
|
_vm->scrollCameraLeft(arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 39:// ok
|
|
{
|
|
debug(0, "o2_scrollCameraRightEx(%d)", arg16(3));
|
|
_vm->scrollCameraRight(arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 40:// ok
|
|
{
|
|
debug(0, "o2_setCamera(%d, %d)", arg16(5), arg16(3));
|
|
_vm->setCamera(arg16(5), arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 42:// ok
|
|
{
|
|
debug(0, "o2_getRgbModifiertAtPoint(x: %d; y: %d; id: %d; varSlot: %d; varOffset: %d)", arg16(5), arg16(3), arg16(7), arg16(11), arg16(9));
|
|
byte *rgb = getSlotData(arg16(11)) + arg16(9);
|
|
_vm->_segmap->getRgbModifiertAtPoint(arg16(5), arg16(3), arg16(7), rgb[0], rgb[1], rgb[2]);
|
|
break;
|
|
}
|
|
|
|
case 43:// ok
|
|
{
|
|
debug(0, "o2_startAnim(%d)", arg16(3));
|
|
_vm->_anim->start(arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 44:// ok
|
|
{
|
|
debug(0, "o2_animNextFrame()");
|
|
_vm->_anim->nextFrame();
|
|
break;
|
|
}
|
|
|
|
case 45:// ok
|
|
{
|
|
// NOP
|
|
break;
|
|
}
|
|
|
|
case 46:// ok
|
|
{
|
|
debug(0, "o2_getAnimFrameNumber(%d)", arg16(3));
|
|
localWrite16(arg16(3), _vm->_anim->getFrameNumber());
|
|
break;
|
|
}
|
|
|
|
case 47:
|
|
{
|
|
// almost ok
|
|
debug(0, "o2_getAnimStatus()");
|
|
int16 status = _vm->_anim->getStatus();
|
|
if (status == 0 || status == 1) {
|
|
// TODO mov screenFlag01, 0
|
|
}
|
|
localWrite16(arg16(3), status);
|
|
break;
|
|
}
|
|
|
|
case 48:// ok
|
|
{
|
|
_vm->_screen->startShakeScreen(arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 49:// ok
|
|
{
|
|
_vm->_screen->stopShakeScreen();
|
|
break;
|
|
}
|
|
|
|
case 50:// TODO
|
|
{
|
|
debug(0, "o2_startSequence");
|
|
break;
|
|
}
|
|
|
|
case 51:// TODO
|
|
{
|
|
debug(0, "o2_endSequence");
|
|
break;
|
|
}
|
|
|
|
case 52:// TODO
|
|
{
|
|
debug(0, "o2_sequenceVolumeStuff");
|
|
break;
|
|
}
|
|
|
|
case 53:// TODO
|
|
{
|
|
debug(0, "o2_playSound1(%d, %d, %d, %d)", arg16(9), arg16(7), arg16(5), arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 54:// TODO
|
|
{
|
|
debug(0, "o2_playSound2(%d, %d, %d)", arg16(7), arg16(5), arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 55:// TODO
|
|
debug(0, "o2_clearScreen()");
|
|
break;
|
|
|
|
case 56:// ok
|
|
{
|
|
// NOP
|
|
break;
|
|
}
|
|
|
|
case 57:// TODO
|
|
{
|
|
debug(0, "o2_handleInput");
|
|
int16 varOfs = arg16(3);
|
|
|
|
localWrite16(varOfs, 0);
|
|
|
|
//_vm->_input->update();
|
|
break;
|
|
}
|
|
|
|
case 58:// TODO
|
|
{
|
|
debug(0, "o2_runOptionsScreen(%d, %d)", arg16(5), arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 59:// TODO
|
|
{
|
|
debug(0, "o2_precacheResources(%04X)", arg16(3));
|
|
break;
|
|
}
|
|
|
|
case 60:// TODO
|
|
{
|
|
debug(0, "o2_precacheSounds1(%04X)", arg16(3));
|
|
// CHECKME
|
|
_vm->_screen->clearSprites();
|
|
break;
|
|
}
|
|
|
|
case 61:// TODO
|
|
{
|
|
debug(0, "o2_deleteAllPbfFilesByExternalArray()");
|
|
break;
|
|
}
|
|
|
|
case 63:// ok
|
|
{
|
|
_regs.sp = _savedSp;
|
|
break;
|
|
}
|
|
|
|
case 64:// ok
|
|
{
|
|
_savedSp = _regs.sp;
|
|
break;
|
|
}
|
|
|
|
case 65:// TODO
|
|
{
|
|
debug(0, "o2_playMovie(%d, %d)", arg16(3), arg16(5));
|
|
break;
|
|
}
|
|
|
|
case 66:
|
|
// NOP
|
|
break;
|
|
|
|
default:
|
|
error("Invalid kernel opcode %d", kernelOpcode);
|
|
}
|
|
|
|
}
|
|
|
|
VarType ScriptInterpreter::getGameVarType(uint variable) {
|
|
switch (variable) {
|
|
case 0: return vtByte;
|
|
case 1: return vtWord;
|
|
case 2: return vtWord;
|
|
case 3: return vtByte;
|
|
case 4: return vtWord;
|
|
case 5: return vtWord;
|
|
case 6: return vtWord;
|
|
case 7: return vtWord;
|
|
case 8: return vtWord;
|
|
case 9: return vtWord;
|
|
case 10: return vtWord;
|
|
case 11: return vtWord;
|
|
case 12: return vtByte;
|
|
case 13: return vtWord;
|
|
case 14: return vtWord;
|
|
case 15: return vtWord;
|
|
case 16: return vtWord;
|
|
case 17: return vtWord;
|
|
case 18: return vtWord;
|
|
case 19: return vtWord;
|
|
case 20: return vtWord;
|
|
case 21: return vtWord;
|
|
default:
|
|
error("Invalid game variable");
|
|
}
|
|
}
|
|
|
|
const char *getVarName(uint variable) {
|
|
switch (variable) {
|
|
case 0: return "mouseDisabled";
|
|
case 1: return "mouseY";
|
|
case 2: return "mouseX";
|
|
case 3: return "mouseButton";
|
|
case 4: return "verbLineY";
|
|
case 5: return "verbLineX";
|
|
case 6: return "verbLineWidth";
|
|
case 7: return "verbLineCount";
|
|
case 8: return "verbLineNum";
|
|
case 9: return "talkTextItemNum";
|
|
case 10: return "talkTextY";
|
|
case 11: return "talkTextX";
|
|
case 12: return "talkTextFontColor";
|
|
case 13: return "cameraY";
|
|
case 14: return "cameraX";
|
|
case 15: return "walkSpeedY";
|
|
case 16: return "walkSpeedX";
|
|
case 17: return "flag01";
|
|
case 18: return "sceneResIndex";
|
|
case 19: return "guiHeight";
|
|
case 20: return "sceneHeight";
|
|
case 21: return "sceneWidth";
|
|
}
|
|
return "(invalid)";
|
|
}
|
|
|
|
int16 ScriptInterpreter::getGameVar(uint variable) {
|
|
debug(0, "ScriptInterpreter::getGameVar(%d{%s})", variable, getVarName(variable));
|
|
|
|
int16 value = 0;
|
|
|
|
switch (variable) {
|
|
case 0:
|
|
value = _vm->_mouseDisabled;
|
|
break;
|
|
case 1:
|
|
value = _vm->_mouseY;
|
|
break;
|
|
case 2:
|
|
value = _vm->_mouseX;
|
|
break;
|
|
case 3:
|
|
value = _vm->_mouseButton;
|
|
break;
|
|
case 4:
|
|
value = _vm->_screen->_verbLineY;
|
|
break;
|
|
case 5:
|
|
value = _vm->_screen->_verbLineX;
|
|
break;
|
|
case 6:
|
|
value = _vm->_screen->_verbLineWidth;
|
|
break;
|
|
case 7:
|
|
value = _vm->_screen->_verbLineCount;
|
|
break;
|
|
case 8:
|
|
value = _vm->_screen->_verbLineNum;
|
|
break;
|
|
case 9:
|
|
value = _vm->_screen->_talkTextItemNum;
|
|
break;
|
|
case 10:
|
|
value = _vm->_screen->_talkTextY;
|
|
break;
|
|
case 11:
|
|
value = _vm->_screen->_talkTextX;
|
|
break;
|
|
case 12:
|
|
value = _vm->_screen->_talkTextFontColor;
|
|
break;
|
|
case 13:
|
|
value = _vm->_cameraY;
|
|
break;
|
|
case 14:
|
|
value = _vm->_cameraX;
|
|
break;
|
|
case 15:
|
|
value = _vm->_walkSpeedY;
|
|
break;
|
|
case 16:
|
|
value = _vm->_walkSpeedX;
|
|
break;
|
|
case 17:
|
|
value = _vm->_flag01;
|
|
break;
|
|
case 18:
|
|
value = _vm->_sceneResIndex;
|
|
break;
|
|
case 19:
|
|
value = _vm->_guiHeight;
|
|
break;
|
|
case 20:
|
|
value = _vm->_sceneHeight;
|
|
break;
|
|
case 21:
|
|
value = _vm->_sceneWidth;
|
|
break;
|
|
default:
|
|
warning("Getting unimplemented game variable %s (%d)", getVarName(variable), variable);
|
|
break;
|
|
}
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
void ScriptInterpreter::setGameVar(uint variable, int16 value) {
|
|
debug(0, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, getVarName(variable), value);
|
|
|
|
switch (variable) {
|
|
case 0:
|
|
_vm->_mouseDisabled = value;
|
|
_vm->_system->showMouse(value == 0);
|
|
break;
|
|
case 3:
|
|
_vm->_mouseButton = value;
|
|
break;
|
|
case 4:
|
|
_vm->_screen->_verbLineY = value;
|
|
break;
|
|
case 5:
|
|
_vm->_screen->_verbLineX = value;
|
|
break;
|
|
case 6:
|
|
_vm->_screen->_verbLineWidth = value;
|
|
break;
|
|
case 7:
|
|
_vm->_screen->_verbLineCount = value;
|
|
break;
|
|
case 8:
|
|
_vm->_screen->_verbLineNum = value;
|
|
break;
|
|
case 9:
|
|
_vm->_screen->_talkTextItemNum = value;
|
|
break;
|
|
case 10:
|
|
_vm->_screen->_talkTextY = value;
|
|
break;
|
|
case 11:
|
|
_vm->_screen->_talkTextX = value;
|
|
break;
|
|
case 12:
|
|
_vm->_screen->_talkTextFontColor = value;
|
|
break;
|
|
case 13:
|
|
_vm->_cameraY = value;
|
|
break;
|
|
case 14:
|
|
_vm->_cameraX = value;
|
|
break;
|
|
case 15:
|
|
_vm->_walkSpeedY = value;
|
|
break;
|
|
case 16:
|
|
_vm->_walkSpeedX = value;
|
|
break;
|
|
case 17:
|
|
_vm->_flag01 = value != 0;
|
|
break;
|
|
case 18:
|
|
_vm->_sceneResIndex = value;
|
|
break;
|
|
case 19:
|
|
_vm->_guiHeight = value;
|
|
break;
|
|
case 20:
|
|
_vm->_sceneHeight = value;
|
|
break;
|
|
case 21:
|
|
_vm->_sceneWidth = value;
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
default:
|
|
warning("Setting unimplemented game variable %s (%d) to %d", getVarName(variable), variable, value);
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
byte ScriptInterpreter::arg8(int16 offset) {
|
|
return _subCode[offset];
|
|
}
|
|
|
|
int16 ScriptInterpreter::arg16(int16 offset) {
|
|
return READ_LE_UINT16(&_subCode[offset]);
|
|
}
|
|
|
|
int32 ScriptInterpreter::arg32(int16 offset) {
|
|
return READ_LE_UINT32(&_subCode[offset]);
|
|
}
|
|
|
|
void ScriptInterpreter::pushByte(byte value) {
|
|
_stack[_regs.sp] = value;
|
|
_regs.sp--;
|
|
}
|
|
|
|
byte ScriptInterpreter::popByte() {
|
|
_regs.sp++;
|
|
return _stack[_regs.sp];
|
|
}
|
|
|
|
void ScriptInterpreter::pushInt16(int16 value) {
|
|
WRITE_LE_UINT16(_stack + _regs.sp, value);
|
|
_regs.sp -= 2;
|
|
}
|
|
|
|
int16 ScriptInterpreter::popInt16() {
|
|
_regs.sp += 2;
|
|
return READ_LE_UINT16(_stack + _regs.sp);
|
|
}
|
|
|
|
void ScriptInterpreter::pushInt32(int32 value) {
|
|
WRITE_LE_UINT32(_stack + _regs.sp, value);
|
|
_regs.sp -= 4;
|
|
}
|
|
|
|
int32 ScriptInterpreter::popInt32() {
|
|
_regs.sp += 4;
|
|
return READ_LE_UINT32(_stack + _regs.sp);
|
|
}
|
|
|
|
void ScriptInterpreter::localWrite8(int16 offset, byte value) {
|
|
debug(1, "localWrite8(%d, %d)", offset, value);
|
|
_localData[offset] = value;
|
|
}
|
|
|
|
byte ScriptInterpreter::localRead8(int16 offset) {
|
|
debug(1, "localRead8(%d) -> %d", offset, _localData[offset]);
|
|
return _localData[offset];
|
|
}
|
|
|
|
void ScriptInterpreter::localWrite16(int16 offset, int16 value) {
|
|
debug(1, "localWrite16(%d, %d)", offset, value);
|
|
WRITE_LE_UINT16(&_localData[offset], value);
|
|
}
|
|
|
|
int16 ScriptInterpreter::localRead16(int16 offset) {
|
|
debug(1, "localRead16(%d) -> %d", offset, (int16)READ_LE_UINT16(&_localData[offset]));
|
|
return (int16)READ_LE_UINT16(&_localData[offset]);
|
|
}
|
|
|
|
void ScriptInterpreter::localWrite32(int16 offset, int32 value) {
|
|
debug(1, "localWrite32(%d, %d)", offset, value);
|
|
WRITE_LE_UINT32(&_localData[offset], value);
|
|
}
|
|
|
|
int32 ScriptInterpreter::localRead32(int16 offset) {
|
|
debug(1, "localRead32(%d) -> %d", offset, (int32)READ_LE_UINT32(&_localData[offset]));
|
|
return (int32)READ_LE_UINT32(&_localData[offset]);
|
|
}
|
|
|
|
byte *ScriptInterpreter::localPtr(int16 offset) {
|
|
debug(1, "localPtr(%d)", offset);
|
|
return &_localData[offset];
|
|
}
|
|
|
|
void ScriptInterpreter::saveState(Common::WriteStream *out) {
|
|
|
|
// Save registers
|
|
out->writeUint16LE(_regs.reg0);
|
|
out->writeUint16LE(_regs.reg1);
|
|
out->writeUint16LE(_regs.reg2);
|
|
out->writeUint16LE(_regs.reg3);
|
|
out->writeUint16LE(_regs.reg4);
|
|
out->writeUint16LE(_regs.reg5);
|
|
out->writeUint16LE(_regs.reg6);
|
|
out->writeUint16LE(_regs.sp);
|
|
out->writeUint16LE(_regs.reg8);
|
|
|
|
// Save slots
|
|
for (int slot = 0; slot < kMaxScriptSlots; slot++) {
|
|
out->writeUint32LE(_slots[slot].size);
|
|
out->writeUint16LE(_slots[slot].resIndex);
|
|
if (_slots[slot].size > 0)
|
|
out->write(_slots[slot].data, _slots[slot].size);
|
|
}
|
|
|
|
// Save stack
|
|
out->write(_stack, kScriptStackSize);
|
|
out->writeUint16LE(_savedSp);
|
|
|
|
// Save IP
|
|
out->writeUint16LE((int16)(_code - getSlotData(_regs.reg4)));
|
|
|
|
}
|
|
|
|
void ScriptInterpreter::loadState(Common::ReadStream *in) {
|
|
|
|
// Load registers
|
|
_regs.reg0 = in->readUint16LE();
|
|
_regs.reg1 = in->readUint16LE();
|
|
_regs.reg2 = in->readUint16LE();
|
|
_regs.reg3 = in->readUint16LE();
|
|
_regs.reg4 = in->readUint16LE();
|
|
_regs.reg5 = in->readUint16LE();
|
|
_regs.reg6 = in->readUint16LE();
|
|
_regs.sp = in->readUint16LE();
|
|
_regs.reg8 = in->readUint16LE();
|
|
|
|
// Load slots
|
|
for (int slot = 0; slot < kMaxScriptSlots; slot++) {
|
|
_slots[slot].size = in->readUint32LE();
|
|
_slots[slot].resIndex = in->readUint16LE();
|
|
_slots[slot].data = NULL;
|
|
if (_slots[slot].size > 0) {
|
|
_slots[slot].data = new byte[_slots[slot].size];
|
|
in->read(_slots[slot].data, _slots[slot].size);
|
|
}
|
|
}
|
|
|
|
// Load stack
|
|
in->read(_stack, kScriptStackSize);
|
|
_savedSp = in->readUint16LE();
|
|
|
|
// Load IP
|
|
_code = getSlotData(_regs.reg4) + in->readUint16LE();
|
|
|
|
}
|
|
|
|
} // End of namespace Toltecs
|