mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 05:34:00 +00:00
14f1529976
The original ignores invalid opcodes as well, according to johndoe
1053 lines
27 KiB
C++
1053 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/error.h"
|
|
|
|
#include "graphics/cursorman.h"
|
|
|
|
#include "toltecs/toltecs.h"
|
|
#include "toltecs/animation.h"
|
|
#include "toltecs/menu.h"
|
|
#include "toltecs/movie.h"
|
|
#include "toltecs/music.h"
|
|
#include "toltecs/palette.h"
|
|
#include "toltecs/resource.h"
|
|
#include "toltecs/script.h"
|
|
#include "toltecs/segmap.h"
|
|
#include "toltecs/sound.h"
|
|
|
|
namespace Toltecs {
|
|
|
|
static const VarType varTypes[] = {
|
|
vtByte, vtWord, vtWord, vtByte, vtWord, // 0 - 4
|
|
vtWord, vtWord, vtWord, vtWord, vtWord, // 5 - 9
|
|
vtWord, vtWord, vtByte, vtWord, vtWord, // 10 - 14
|
|
vtWord, vtWord, vtWord, vtWord, vtWord, // 15 - 19
|
|
vtWord, vtWord // 20 - 21
|
|
};
|
|
|
|
static const char *varNames[] = {
|
|
"mouseDisabled", "mouseY", "mouseX", "mouseButton", "verbLineY", // 0 - 4
|
|
"verbLineX", "verbLineWidth", "verbLineCount", "verbLineNum", "talkTextItemNum", // 5 - 9
|
|
"talkTextY", "talkTextX", "talkTextFontColor", "cameraY", "cameraX", // 10 - 14
|
|
"walkSpeedY", "walkSpeedX", "flag01", "sceneResIndex", "guiHeight", // 15 - 19
|
|
"sceneHeight", "sceneWidth" // 20 - 21
|
|
};
|
|
|
|
ScriptInterpreter::ScriptInterpreter(ToltecsEngine *vm) : _vm(vm) {
|
|
|
|
_stack = new byte[kScriptStackSize];
|
|
|
|
memset(_slots, 0, sizeof(_slots));
|
|
|
|
_savedSp = 0;
|
|
|
|
_slots[kMaxScriptSlots - 1].size = 1024;
|
|
_slots[kMaxScriptSlots - 1].data = new byte[_slots[kMaxScriptSlots - 1].size];
|
|
|
|
setupScriptFunctions();
|
|
|
|
}
|
|
|
|
ScriptInterpreter::~ScriptInterpreter() {
|
|
delete[] _stack;
|
|
for (int i = 0; i < kMaxScriptSlots; i++)
|
|
delete[] _slots[i].data;
|
|
for (uint i = 0; i < _scriptFuncs.size(); ++i)
|
|
delete _scriptFuncs[i];
|
|
}
|
|
|
|
typedef Common::Functor0Mem<void, ScriptInterpreter> ScriptFunctionF;
|
|
#define RegisterScriptFunction(x) \
|
|
_scriptFuncs.push_back(new ScriptFunctionF(this, &ScriptInterpreter::x)); \
|
|
_scriptFuncNames.push_back(#x);
|
|
void ScriptInterpreter::setupScriptFunctions() {
|
|
|
|
// 0
|
|
RegisterScriptFunction(sfNop);
|
|
RegisterScriptFunction(sfNop);
|
|
RegisterScriptFunction(sfGetGameVar);
|
|
RegisterScriptFunction(sfSetGameVar);
|
|
RegisterScriptFunction(sfUpdateScreen);
|
|
// 5
|
|
RegisterScriptFunction(sfGetRandomNumber);
|
|
RegisterScriptFunction(sfDrawGuiTextMulti);
|
|
RegisterScriptFunction(sfUpdateVerbLine);
|
|
RegisterScriptFunction(sfSetFontColor);
|
|
RegisterScriptFunction(sfGetTalkTextDuration);
|
|
// 10
|
|
RegisterScriptFunction(sfTalk);
|
|
RegisterScriptFunction(sfFindPaletteFragment);
|
|
RegisterScriptFunction(sfClearPaletteFragments);
|
|
RegisterScriptFunction(sfAddPaletteFragment);
|
|
RegisterScriptFunction(sfSetDeltaAnimPalette);
|
|
// 15
|
|
RegisterScriptFunction(sfSetUnkPaletteEffect);
|
|
RegisterScriptFunction(sfBuildColorTransTable);
|
|
RegisterScriptFunction(sfSetDeltaMainPalette);
|
|
RegisterScriptFunction(sfLoadScript);
|
|
RegisterScriptFunction(sfRegisterFont);
|
|
// 20
|
|
RegisterScriptFunction(sfLoadAddPalette);
|
|
RegisterScriptFunction(sfLoadScene);
|
|
RegisterScriptFunction(sfSetGuiHeight);
|
|
RegisterScriptFunction(sfFindMouseInRectIndex1);
|
|
RegisterScriptFunction(sfFindMouseInRectIndex2);
|
|
// 25
|
|
RegisterScriptFunction(sfDrawGuiImage);
|
|
RegisterScriptFunction(sfAddAnimatedSpriteNoLoop);
|
|
RegisterScriptFunction(sfAddAnimatedSprite);
|
|
RegisterScriptFunction(sfAddStaticSprite);
|
|
RegisterScriptFunction(sfAddAnimatedSpriteScaled);
|
|
// 30
|
|
RegisterScriptFunction(sfFindPath);
|
|
RegisterScriptFunction(sfWalk);
|
|
RegisterScriptFunction(sfScrollCameraUp);
|
|
RegisterScriptFunction(sfScrollCameraDown);
|
|
RegisterScriptFunction(sfScrollCameraLeft);
|
|
// 35
|
|
RegisterScriptFunction(sfScrollCameraRight);
|
|
RegisterScriptFunction(sfScrollCameraUpEx);
|
|
RegisterScriptFunction(sfScrollCameraDownEx);
|
|
RegisterScriptFunction(sfScrollCameraLeftEx);
|
|
RegisterScriptFunction(sfScrollCameraRightEx);
|
|
// 40
|
|
RegisterScriptFunction(sfSetCamera);
|
|
RegisterScriptFunction(sfGetCameraChanged);
|
|
RegisterScriptFunction(sfGetRgbModifiertAtPoint);
|
|
RegisterScriptFunction(sfStartAnim);
|
|
RegisterScriptFunction(sfAnimNextFrame);
|
|
// 45
|
|
RegisterScriptFunction(sfNop);
|
|
RegisterScriptFunction(sfGetAnimFrameNumber);
|
|
RegisterScriptFunction(sfGetAnimStatus);
|
|
RegisterScriptFunction(sfStartShakeScreen);
|
|
RegisterScriptFunction(sfStopShakeScreen);
|
|
// 50
|
|
RegisterScriptFunction(sfStartSequence);
|
|
RegisterScriptFunction(sfEndSequence);
|
|
RegisterScriptFunction(sfSetSequenceVolume);
|
|
RegisterScriptFunction(sfPlayPositionalSound);
|
|
RegisterScriptFunction(sfPlaySound2);
|
|
// 55
|
|
RegisterScriptFunction(sfClearScreen);
|
|
RegisterScriptFunction(sfNop);
|
|
RegisterScriptFunction(sfHandleInput);
|
|
RegisterScriptFunction(sfRunOptionsScreen);
|
|
RegisterScriptFunction(sfPrecacheSprites);
|
|
// 60
|
|
RegisterScriptFunction(sfPrecacheSounds1);
|
|
RegisterScriptFunction(sfDeletePrecachedFiles);
|
|
RegisterScriptFunction(sfPrecacheSounds2);
|
|
RegisterScriptFunction(sfRestoreStackPtr);
|
|
RegisterScriptFunction(sfSaveStackPtr);
|
|
// 65
|
|
RegisterScriptFunction(sfPlayMovie);
|
|
RegisterScriptFunction(sfNop);
|
|
|
|
}
|
|
|
|
void ScriptInterpreter::loadScript(uint resIndex, uint slotIndex) {
|
|
if (_slots[slotIndex].resIndex && _slots[slotIndex].resIndex != resIndex && _vm->_screen->isTalkTextActive(slotIndex)) {
|
|
// WORKAROUND: This happens when examining the assembled
|
|
// pickaxe. It could lead to random characters being printed,
|
|
// or possibly even crashes, when subtitles are enabled.
|
|
//
|
|
// According to johndoe and he said there may be some bug or
|
|
// missing feature that causes this situation to happen at all,
|
|
// but he was ok with this workaround for now.
|
|
warning("Possible script bug: Loading script %d into slot %d that has an active talk text, probably for script %d", resIndex, slotIndex, _slots[slotIndex].resIndex);
|
|
_vm->_screen->finishTalkTextItem(slotIndex);
|
|
}
|
|
|
|
delete[] _slots[slotIndex].data;
|
|
|
|
_slots[slotIndex].resIndex = resIndex;
|
|
Resource *scriptResource = _vm->_res->load(resIndex);
|
|
_slots[slotIndex].size = scriptResource->size;
|
|
_slots[slotIndex].data = new byte[_slots[slotIndex].size];
|
|
memcpy(_slots[slotIndex].data, scriptResource->data, _slots[slotIndex].size);
|
|
}
|
|
|
|
void ScriptInterpreter::setMainScript(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);
|
|
}
|
|
|
|
void ScriptInterpreter::runScript() {
|
|
while (!_vm->shouldQuit()) {
|
|
if (_vm->_movieSceneFlag)
|
|
_vm->_mouseButton = 0;
|
|
|
|
if (_vm->_saveLoadRequested != 0) {
|
|
if (_vm->_saveLoadRequested == 1)
|
|
_vm->loadGameState(_vm->_saveLoadSlot);
|
|
else if (_vm->_saveLoadRequested == 2)
|
|
_vm->saveGameState(_vm->_saveLoadSlot, _vm->_saveLoadDescription);
|
|
_vm->_saveLoadRequested = 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);
|
|
}
|
|
}
|
|
|
|
byte ScriptInterpreter::readByte() {
|
|
return *_code++;
|
|
}
|
|
|
|
int16 ScriptInterpreter::readInt16() {
|
|
int16 value = READ_LE_UINT16(_code);
|
|
_code += 2;
|
|
return value;
|
|
}
|
|
|
|
void ScriptInterpreter::execOpcode(byte opcode) {
|
|
int16 ofs;
|
|
|
|
debug(2, "opcode = %d", opcode);
|
|
|
|
switch (opcode) {
|
|
case 0:
|
|
{
|
|
// ok
|
|
_subCode = _code;
|
|
byte length = readByte();
|
|
if (length == 0) {
|
|
warning("Opcode length is 0 when calling script function");
|
|
return;
|
|
}
|
|
debug(2, "length = %d", length);
|
|
uint16 index = readInt16();
|
|
execScriptFunction(index);
|
|
_code += length - 2;
|
|
break;
|
|
}
|
|
case 1:
|
|
_regs.reg0 = readInt16();
|
|
break;
|
|
case 2:
|
|
_regs.reg1 = readInt16();
|
|
break;
|
|
case 3:
|
|
_regs.reg3 = readInt16();
|
|
break;
|
|
case 4:
|
|
_regs.reg5 = _regs.reg0;
|
|
break;
|
|
case 5:
|
|
_regs.reg3 = _regs.reg0;
|
|
break;
|
|
case 6:
|
|
_regs.reg1 = _regs.reg0;
|
|
break;
|
|
case 7:
|
|
_regs.reg1 = localRead16(_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);
|
|
break;
|
|
case 35:
|
|
pushInt16(_regs.reg1);
|
|
break;
|
|
case 36:
|
|
_regs.reg1 = popInt16();
|
|
break;
|
|
case 37:
|
|
_regs.reg0 = popInt16();
|
|
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:
|
|
_code = getSlotData(_regs.reg4) + _regs.reg0;
|
|
break;
|
|
case 44:
|
|
_code = getSlotData(_regs.reg5) + _regs.reg0;
|
|
_regs.reg4 = _regs.reg5;
|
|
_switchLocalDataNear = true;
|
|
break;
|
|
case 45:
|
|
pushInt16(_code - getSlotData(_regs.reg4));
|
|
pushInt16(_regs.reg4);
|
|
_code = getSlotData(_regs.reg4) + _regs.reg0;
|
|
break;
|
|
case 46:
|
|
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;
|
|
_switchLocalDataNear = true;
|
|
break;
|
|
case 48:
|
|
_regs.reg4 = popInt16();
|
|
ofs = popInt16();
|
|
_code = getSlotData(_regs.reg4) + ofs;
|
|
_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:
|
|
// Most likely a script bug. Throw a warning and ignore it.
|
|
// The original ignores invalid opcodes as well - bug #3604025.
|
|
warning("Invalid opcode %d", opcode);
|
|
}
|
|
|
|
}
|
|
|
|
void ScriptInterpreter::execScriptFunction(uint16 index) {
|
|
if (index >= _scriptFuncs.size())
|
|
error("ScriptInterpreter::execScriptFunction() Invalid script function index %d", index);
|
|
debug(1, "execScriptFunction %s (%d)", _scriptFuncNames[index], index);
|
|
(*_scriptFuncs[index])();
|
|
}
|
|
|
|
int16 ScriptInterpreter::getGameVar(uint variable) {
|
|
debug(2, "ScriptInterpreter::getGameVar(%d{%s})", variable, varNames[variable]);
|
|
|
|
switch (variable) {
|
|
case 0: return _vm->_mouseDisabled;
|
|
case 1: return _vm->_mouseY;
|
|
case 2: return _vm->_mouseX;
|
|
case 3: return _vm->_mouseButton;
|
|
case 4: return _vm->_screen->_verbLineY;
|
|
case 5: return _vm->_screen->_verbLineX;
|
|
case 6: return _vm->_screen->_verbLineWidth;
|
|
case 7: return _vm->_screen->_verbLineCount;
|
|
case 8: return _vm->_screen->_verbLineNum;
|
|
case 9: return _vm->_screen->_talkTextItemNum;
|
|
case 10: return _vm->_screen->_talkTextY;
|
|
case 11: return _vm->_screen->_talkTextX;
|
|
case 12: return _vm->_screen->_talkTextFontColor;
|
|
case 13: return _vm->_cameraY;
|
|
case 14: return _vm->_cameraX;
|
|
case 15: return _vm->_walkSpeedY;
|
|
case 16: return _vm->_walkSpeedX;
|
|
case 17: return _vm->_flag01;
|
|
case 18: return _vm->_sceneResIndex;
|
|
case 19: return _vm->_guiHeight;
|
|
case 20: return _vm->_sceneHeight;
|
|
case 21: return _vm->_sceneWidth;
|
|
default:
|
|
warning("Getting unimplemented game variable %s (%d)", varNames[variable], variable);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void ScriptInterpreter::setGameVar(uint variable, int16 value) {
|
|
debug(2, "ScriptInterpreter::setGameVar(%d{%s}, %d)", variable, varNames[variable], value);
|
|
|
|
switch (variable) {
|
|
case 0:
|
|
_vm->_mouseDisabled = value;
|
|
CursorMan.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", varNames[variable], variable, value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
byte ScriptInterpreter::arg8(int16 offset) {
|
|
return _subCode[offset];
|
|
}
|
|
|
|
int16 ScriptInterpreter::arg16(int16 offset) {
|
|
return READ_LE_UINT16(&_subCode[offset]);
|
|
}
|
|
|
|
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::localWrite8(int16 offset, byte value) {
|
|
//debug(2, "localWrite8(%d, %d)", offset, value);
|
|
_localData[offset] = value;
|
|
}
|
|
|
|
byte ScriptInterpreter::localRead8(int16 offset) {
|
|
//debug(2, "localRead8(%d) -> %d", offset, _localData[offset]);
|
|
return _localData[offset];
|
|
}
|
|
|
|
void ScriptInterpreter::localWrite16(int16 offset, int16 value) {
|
|
//debug(2, "localWrite16(%d, %d)", offset, value);
|
|
WRITE_LE_UINT16(&_localData[offset], value);
|
|
}
|
|
|
|
int16 ScriptInterpreter::localRead16(int16 offset) {
|
|
//debug(2, "localRead16(%d) -> %d", offset, (int16)READ_LE_UINT16(&_localData[offset]));
|
|
return (int16)READ_LE_UINT16(&_localData[offset]);
|
|
}
|
|
|
|
byte *ScriptInterpreter::localPtr(int16 offset) {
|
|
//debug(2, "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();
|
|
}
|
|
|
|
void ScriptInterpreter::sfNop() {
|
|
// NOP
|
|
}
|
|
|
|
void ScriptInterpreter::sfGetGameVar() {
|
|
int16 value = getGameVar(arg16(3));
|
|
localWrite16(arg16(5), value);
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetGameVar() {
|
|
int16 varIndex = arg16(3);
|
|
assert(varIndex <= 21);
|
|
|
|
VarType varType = varTypes[varIndex];
|
|
int16 value = 0;
|
|
if (varType == vtByte)
|
|
value = arg8(5);
|
|
else if (varType == vtWord)
|
|
value = arg16(5);
|
|
setGameVar(varIndex, value);
|
|
}
|
|
|
|
void ScriptInterpreter::sfUpdateScreen() {
|
|
_vm->updateScreen();
|
|
}
|
|
|
|
void ScriptInterpreter::sfGetRandomNumber() {
|
|
localWrite16(arg16(5), _vm->_rnd->getRandomNumber(arg16(3) - 1));
|
|
}
|
|
|
|
void ScriptInterpreter::sfDrawGuiTextMulti() {
|
|
_vm->_screen->drawGuiTextMulti((byte *)localPtr(arg16(3)));
|
|
}
|
|
|
|
void ScriptInterpreter::sfUpdateVerbLine() {
|
|
_vm->_screen->updateVerbLine(arg16(5), arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetFontColor() {
|
|
_vm->_screen->_fontColor1 = 0;
|
|
_vm->_screen->_fontColor2 = arg8(3);
|
|
}
|
|
|
|
void ScriptInterpreter::sfGetTalkTextDuration() {
|
|
localWrite16(arg16(3), _vm->_screen->getTalkTextDuration());
|
|
}
|
|
|
|
void ScriptInterpreter::sfTalk() {
|
|
_vm->talk(arg16(5), arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfFindPaletteFragment() {
|
|
localWrite16(arg16(5), _vm->_palette->findFragment(arg16(3)));
|
|
}
|
|
|
|
void ScriptInterpreter::sfClearPaletteFragments() {
|
|
_vm->_palette->clearFragments();
|
|
}
|
|
|
|
void ScriptInterpreter::sfAddPaletteFragment() {
|
|
_vm->_palette->addFragment(arg16(3), arg16(5));
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetDeltaAnimPalette() {
|
|
_vm->_palette->setDeltaPalette(_vm->_palette->getAnimPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetUnkPaletteEffect() {
|
|
error("ScriptInterpreter::sfSetUnkPaletteEffect called"); // unused
|
|
}
|
|
|
|
void ScriptInterpreter::sfBuildColorTransTable() {
|
|
_vm->_palette->buildColorTransTable(arg8(4), (char)arg8(3), arg8(5));
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetDeltaMainPalette() {
|
|
_vm->_palette->setDeltaPalette(_vm->_palette->getMainPalette(), arg8(6), (char)arg8(5), arg8(4), arg8(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfLoadScript() {
|
|
int16 codeOfs = _code - getSlotData(_regs.reg4);
|
|
loadScript(arg16(4), arg8(3));
|
|
_code = getSlotData(_regs.reg4) + codeOfs;
|
|
_switchLocalDataNear = true;
|
|
}
|
|
|
|
void ScriptInterpreter::sfRegisterFont() {
|
|
_vm->_screen->registerFont(arg8(3), arg16(4));
|
|
}
|
|
|
|
void ScriptInterpreter::sfLoadAddPalette() {
|
|
_vm->_palette->loadAddPalette(arg16(4), arg8(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfLoadScene() {
|
|
if (arg8(3) == 0) {
|
|
// FIXME: Originally, this was stopSpeech(). However, we need to stop
|
|
// ALL sounds here (including sound effects and background sounds)
|
|
// before purgeCache() is called, otherwise the sound buffers will be
|
|
// invalidated. This is apparent when moving from a scene that has
|
|
// background sounds (such as the canyon at the beginning), to another
|
|
// one that doesn't (such as the map), and does not stop the sounds
|
|
// already playing. In this case, the engine will either crash or
|
|
// garbage will be heard through the speakers.
|
|
// TODO: We should either move purgeCache() elsewhere, or monitor
|
|
// which resources are still used before purging the cache.
|
|
_vm->_sound->stopAll();
|
|
_vm->_res->purgeCache();
|
|
_vm->loadScene(arg16(4));
|
|
} else {
|
|
_vm->_screen->loadMouseCursor(arg16(4));
|
|
}
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetGuiHeight() {
|
|
_vm->setGuiHeight(arg8(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfFindMouseInRectIndex1() {
|
|
int16 index = -1;
|
|
if (_vm->_mouseY < _vm->_cameraHeight) {
|
|
int16 slotIndex = arg16(5);
|
|
index = _vm->findRectAtPoint(getSlotData(slotIndex) + arg16(3),
|
|
_vm->_mouseX + _vm->_cameraX,
|
|
_vm->_mouseY + _vm->_cameraY,
|
|
arg16(11) + 1, arg16(7),
|
|
getSlotData(slotIndex) + _slots[slotIndex].size);
|
|
}
|
|
localWrite16(arg16(9), index);
|
|
}
|
|
|
|
void ScriptInterpreter::sfFindMouseInRectIndex2() {
|
|
int16 index = -1;
|
|
if (_vm->_sceneResIndex != 0) {
|
|
if (_vm->_mouseY < _vm->_cameraHeight) {
|
|
int16 slotIndex = arg16(5);
|
|
index = _vm->findRectAtPoint(getSlotData(slotIndex) + arg16(3),
|
|
_vm->_mouseX + _vm->_cameraX,
|
|
_vm->_mouseY + _vm->_cameraY,
|
|
0, arg16(7),
|
|
getSlotData(slotIndex) + _slots[slotIndex].size);
|
|
}
|
|
}
|
|
localWrite16(arg16(9), index);
|
|
}
|
|
|
|
void ScriptInterpreter::sfDrawGuiImage() {
|
|
_vm->_screen->drawGuiImage(arg16(5), arg16(3), arg16(7));
|
|
}
|
|
|
|
void ScriptInterpreter::sfAddAnimatedSpriteNoLoop() {
|
|
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte *)localPtr(0), (int16 *)localPtr(arg16(9)), false, 2);
|
|
}
|
|
|
|
void ScriptInterpreter::sfAddAnimatedSprite() {
|
|
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte *)localPtr(0), (int16 *)localPtr(arg16(9)), true, 2);
|
|
}
|
|
|
|
void ScriptInterpreter::sfAddStaticSprite() {
|
|
_vm->_screen->addStaticSprite(_subCode + 3);
|
|
}
|
|
|
|
void ScriptInterpreter::sfAddAnimatedSpriteScaled() {
|
|
_vm->_screen->addAnimatedSprite(arg16(5), arg16(3), arg16(7), (byte *)localPtr(0), (int16 *)localPtr(arg16(9)), true, 1);
|
|
}
|
|
|
|
void ScriptInterpreter::sfFindPath() {
|
|
_vm->_segmap->findPath((int16 *)(getSlotData(arg16(13)) + arg16(11)), arg16(9), arg16(7), arg16(5), arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfWalk() {
|
|
_vm->walk(getSlotData(arg16(5)) + arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraUp() {
|
|
_vm->scrollCameraUp(4);
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraDown() {
|
|
_vm->scrollCameraDown(4);
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraLeft() {
|
|
_vm->scrollCameraLeft(4);
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraRight() {
|
|
_vm->scrollCameraRight(4);
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraUpEx() {
|
|
_vm->scrollCameraUp(arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraDownEx() {
|
|
_vm->scrollCameraDown(arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraLeftEx() {
|
|
_vm->scrollCameraLeft(arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfScrollCameraRightEx() {
|
|
_vm->scrollCameraRight(arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetCamera() {
|
|
_vm->setCamera(arg16(5), arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfGetCameraChanged() {
|
|
localWrite16(arg16(3), _vm->getCameraChanged() ? 1 : 0);
|
|
}
|
|
|
|
void ScriptInterpreter::sfGetRgbModifiertAtPoint() {
|
|
byte *rgb = getSlotData(arg16(11)) + arg16(9);
|
|
_vm->_segmap->getRgbModifiertAtPoint(arg16(5), arg16(3), arg16(7), rgb[0], rgb[1], rgb[2]);
|
|
}
|
|
|
|
void ScriptInterpreter::sfStartAnim() {
|
|
_vm->_anim->start(arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfAnimNextFrame() {
|
|
_vm->_anim->nextFrame();
|
|
}
|
|
|
|
void ScriptInterpreter::sfGetAnimFrameNumber() {
|
|
localWrite16(arg16(3), _vm->_anim->getFrameNumber());
|
|
}
|
|
|
|
void ScriptInterpreter::sfGetAnimStatus() {
|
|
int16 status = _vm->_anim->getStatus();
|
|
if (status == 0 || status == 1) {
|
|
// TODO mov screenFlag01, 0
|
|
}
|
|
localWrite16(arg16(3), status);
|
|
}
|
|
|
|
void ScriptInterpreter::sfStartShakeScreen() {
|
|
_vm->_screen->startShakeScreen(arg16(3));
|
|
}
|
|
|
|
void ScriptInterpreter::sfStopShakeScreen() {
|
|
_vm->_screen->stopShakeScreen();
|
|
}
|
|
|
|
void ScriptInterpreter::sfStartSequence() {
|
|
int16 sequenceResIndex = arg16(3);
|
|
debug(1, "ScriptInterpreter::sfStartSequence(%d)", sequenceResIndex);
|
|
|
|
if (sequenceResIndex >= 0) {
|
|
//_vm->_arc->dump(sequenceResIndex, "music"); // DEBUG: Dump music so we know what's in there
|
|
|
|
_vm->_music->playSequence(sequenceResIndex);
|
|
}
|
|
}
|
|
|
|
void ScriptInterpreter::sfEndSequence() {
|
|
_vm->_music->stopSequence();
|
|
}
|
|
|
|
void ScriptInterpreter::sfSetSequenceVolume() {
|
|
// TODO
|
|
//debug("ScriptInterpreter::sfSetSequenceVolume");
|
|
}
|
|
|
|
void ScriptInterpreter::sfPlayPositionalSound() {
|
|
_vm->_sound->playSoundAtPos(arg16(3), arg16(9), arg16(7));
|
|
}
|
|
|
|
void ScriptInterpreter::sfPlaySound2() {
|
|
_vm->_sound->playSound(arg16(3), arg16(5), arg16(7));
|
|
}
|
|
|
|
void ScriptInterpreter::sfClearScreen() {
|
|
// TODO: Occurs on every scene change, but seems unneeded
|
|
//debug("ScriptInterpreter::sfClearScreen");
|
|
}
|
|
|
|
void ScriptInterpreter::sfHandleInput() {
|
|
int16 varOfs = arg16(3);
|
|
int16 keyCode = 0;
|
|
if (_vm->_rightButtonDown) {
|
|
keyCode = 1;
|
|
} else {
|
|
// Convert keyboard scancode to IBM PC scancode.
|
|
// Only scancodes known to be used (so far) are converted.
|
|
switch (_vm->_keyState.keycode) {
|
|
case Common::KEYCODE_ESCAPE:
|
|
keyCode = 1;
|
|
break;
|
|
case Common::KEYCODE_F10:
|
|
keyCode = 68;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
localWrite16(varOfs, keyCode);
|
|
}
|
|
|
|
void ScriptInterpreter::sfRunOptionsScreen() {
|
|
_vm->showMenu(kMenuIdMain);
|
|
}
|
|
|
|
/**
|
|
* NOTE: The opcodes sfPrecacheSprites, sfPrecacheSounds1, sfPrecacheSounds2 and
|
|
* sfDeletePrecachedFiles were used by the original engine to handle precaching
|
|
* of data so the game doesn't stall while playing (due to the slow speed of
|
|
* CD-Drives back then). This is not needed in ScummVM since all supported
|
|
* systems are fast enough to load data in-game.
|
|
*/
|
|
|
|
void ScriptInterpreter::sfPrecacheSprites() {
|
|
// See note above
|
|
}
|
|
|
|
void ScriptInterpreter::sfPrecacheSounds1() {
|
|
// See note above
|
|
}
|
|
|
|
void ScriptInterpreter::sfDeletePrecachedFiles() {
|
|
// See note above
|
|
}
|
|
|
|
void ScriptInterpreter::sfPrecacheSounds2() {
|
|
// See note above
|
|
}
|
|
|
|
void ScriptInterpreter::sfRestoreStackPtr() {
|
|
_regs.sp = _savedSp;
|
|
}
|
|
|
|
void ScriptInterpreter::sfSaveStackPtr() {
|
|
_savedSp = _regs.sp;
|
|
}
|
|
|
|
void ScriptInterpreter::sfPlayMovie() {
|
|
CursorMan.showMouse(false);
|
|
_vm->_moviePlayer->playMovie(arg16(3));
|
|
CursorMan.showMouse(true);
|
|
}
|
|
|
|
} // End of namespace Toltecs
|