scummvm/engines/cine/script_fw.cpp
Kari Salminen 1642259e4f CINE: FW: Fix 8 half speed scenes (Amiga/Atari ST)
Previously there was a fix for bug #2669415
("FW: half walking speed in a screen") which only addressed a single
scene in Amiga and Atari ST versions of Future Wars.

This fix fixes half walking speed in 8 more scenes in Amiga and Atari ST
versions of Future Wars. The scenes were identified by playing through
an Italian version of Amiga Future Wars.

3 more scenes were left over with half walking speed because they could
not be fixed as easily (i.e. using this fix on them broke the scenes in
some way, made them too fast or made the people walk into wrong
positions or something else). The scenes left over are the ones using
the following background files:
- "L10.PI1": The medieval castle's hall.
- "L18.PI1": The medieval castle's teleport room.
- "L45.PI1": Space station's computer room.
2020-09-10 00:34:32 +01:00

3258 lines
81 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.
*
*/
/** \file
* Future Wars script interpreter file
*/
#include "common/endian.h"
#include "common/textconsole.h"
#include "cine/cine.h"
#include "cine/bg_list.h"
#include "cine/object.h"
#include "cine/sound.h"
#include "cine/various.h"
#include "cine/script.h"
#include "cine/console.h"
namespace Cine {
uint16 compareVars(int16 a, int16 b);
const Opcode *FWScript::_opcodeTable = 0;
unsigned int FWScript::_numOpcodes = 0;
void FWScript::setupTable() {
static const Opcode opcodeTable[] = {
/* 00 */
{ &FWScript::o1_modifyObjectParam, "bbw" },
{ &FWScript::o1_getObjectParam, "bbb" },
{ &FWScript::o1_addObjectParam, "bbw" },
{ &FWScript::o1_subObjectParam, "bbw" },
/* 04 */
{ &FWScript::o1_mulObjectParam, "bbw" },
{ &FWScript::o1_divObjectParam, "bbw" },
{ &FWScript::o1_compareObjectParam, "bbw" },
{ &FWScript::o1_setupObject, "bwwww" },
/* 08 */
{ &FWScript::o1_checkCollision, "bwwww" },
{ &FWScript::o1_loadVar, "bc" },
{ &FWScript::o1_addVar, "bc" },
{ &FWScript::o1_subVar, "bc" },
/* 0C */
{ &FWScript::o1_mulVar, "bc" },
{ &FWScript::o1_divVar, "bc" },
{ &FWScript::o1_compareVar, "bc" },
{ &FWScript::o1_modifyObjectParam2, "bbb" },
/* 10 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ &FWScript::o1_loadMask0, "b" },
/* 14 */
{ &FWScript::o1_unloadMask0, "b" },
{ &FWScript::o1_addToBgList, "b" },
{ &FWScript::o1_loadMask1, "b" },
{ &FWScript::o1_unloadMask1, "b" },
/* 18 */
{ &FWScript::o1_loadMask4, "b" },
{ &FWScript::o1_unloadMask4, "b" },
{ &FWScript::o1_addSpriteFilledToBgList, "b" },
{ &FWScript::o1_clearBgIncrustList, "" },
/* 1C */
{ 0, 0 },
{ &FWScript::o1_label, "l" },
{ &FWScript::o1_goto, "b" },
{ &FWScript::o1_gotoIfSup, "b" },
/* 20 */
{ &FWScript::o1_gotoIfSupEqu, "b" },
{ &FWScript::o1_gotoIfInf, "b" },
{ &FWScript::o1_gotoIfInfEqu, "b" },
{ &FWScript::o1_gotoIfEqu, "b" },
/* 24 */
{ &FWScript::o1_gotoIfDiff, "b" },
{ &FWScript::o1_removeLabel, "b" },
{ &FWScript::o1_loop, "bb" },
{ 0, 0 },
/* 28 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 2C */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 30 */
{ 0, 0 },
{ &FWScript::o1_startGlobalScript, "b" },
{ &FWScript::o1_endGlobalScript, "b" },
{ 0, 0 },
/* 34 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 38 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ &FWScript::o1_loadAnim, "s" },
/* 3C */
{ &FWScript::o1_loadBg, "s" },
{ &FWScript::o1_loadCt, "s" },
{ 0, 0 },
{ &FWScript::o1_loadPart, "s" },
/* 40 */
{ &FWScript::o1_closePart, "" },
{ &FWScript::o1_loadNewPrcName, "bs" },
{ &FWScript::o1_requestCheckPendingDataLoad, "" },
{ 0, 0 },
/* 44 */
{ 0, 0 },
{ &FWScript::o1_blitAndFade, "" },
{ &FWScript::o1_fadeToBlack, "" },
{ &FWScript::o1_transformPaletteRange, "bbwww" },
/* 48 */
{ 0, 0 },
{ &FWScript::o1_setDefaultMenuBgColor, "b" },
{ &FWScript::o1_palRotate, "bbb" },
{ 0, 0 },
/* 4C */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ &FWScript::o1_break, "" },
/* 50 */
{ &FWScript::o1_endScript, "x" },
{ &FWScript::o1_message, "bwwww" },
{ &FWScript::o1_loadGlobalVar, "bc" },
{ &FWScript::o1_compareGlobalVar, "bc" },
/* 54 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 58 */
{ 0, 0 },
{ &FWScript::o1_declareFunctionName, "s" },
{ &FWScript::o1_freePartRange, "bb" },
{ &FWScript::o1_unloadAllMasks, "" },
/* 5C */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
/* 60 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ &FWScript::o1_setScreenDimensions, "wwww" },
/* 64 */
{ &FWScript::o1_displayBackground, "" },
{ &FWScript::o1_initializeZoneData, "" },
{ &FWScript::o1_setZoneDataEntry, "bw" },
{ &FWScript::o1_getZoneDataEntry, "bb" },
/* 68 */
{ &FWScript::o1_setPlayerCommandPosY, "b" },
{ &FWScript::o1_allowPlayerInput, "" },
{ &FWScript::o1_disallowPlayerInput, "" },
{ &FWScript::o1_changeDataDisk, "b" },
/* 6C */
{ 0, 0 },
{ &FWScript::o1_loadMusic, "s" },
{ &FWScript::o1_playMusic, "" },
{ &FWScript::o1_fadeOutMusic, "" },
/* 70 */
{ &FWScript::o1_stopSample, "" },
{ &FWScript::o1_op71, "bw" },
{ &FWScript::o1_op72, "wbw" },
{ &FWScript::o1_op73, "wbw" },
/* 74 */
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ &FWScript::o1_playSample, "bbwbww" },
/* 78 */
{ &FWScript::o1_playSampleSwapped, "bbwbww" },
{ &FWScript::o1_disableSystemMenu, "b" },
{ &FWScript::o1_loadMask5, "b" },
{ &FWScript::o1_unloadMask5, "b" }
};
FWScript::_opcodeTable = (const Opcode *)opcodeTable;
FWScript::_numOpcodes = ARRAYSIZE(opcodeTable);
}
FWScriptInfo *scriptInfo; ///< Script factory
/**
* @todo replace with script subsystem
*/
void setupOpcodes() {
static FWScriptInfo fw;
static OSScriptInfo os;
if (g_cine->getGameType() == Cine::GType_FW) {
FWScript::setupTable();
scriptInfo = &fw;
} else {
OSScript::setupTable();
scriptInfo = &os;
}
}
/**
* Allocate empty array
* @param len Size of array
*
* Explicit to prevent var=0 instead of var[i]=0 typos.
*/
ScriptVars::ScriptVars(unsigned int len) : _size(len), _vars(new int16[len]) {
assert(_vars);
reset();
}
/**
* Allocate array and read contents from savefile
* @param fHandle Savefile open for reading
* @param len Size of array
*/
ScriptVars::ScriptVars(Common::SeekableReadStream &fHandle, unsigned int len)
: _size(len), _vars(new int16[len]) {
assert(_vars);
load(fHandle);
}
void ScriptVars::reinit(unsigned int len) {
delete[] _vars;
_size = len;
_vars = new int16[len];
reset();
}
/**
* Copy constructor
*/
ScriptVars::ScriptVars(const ScriptVars &src) : _size(src._size), _vars(new int16[_size]) {
assert(_vars);
memcpy(_vars, src._vars, _size * sizeof(int16));
}
/**
* Destructor
*/
ScriptVars::~ScriptVars() {
delete[] _vars;
}
/**
* Assignment operator
*/
ScriptVars &ScriptVars::operator=(const ScriptVars &src) {
ScriptVars tmp(src);
int16 *tmpvars = _vars;
_vars = tmp._vars;
tmp._vars = tmpvars;
_size = src._size;
return *this;
}
/**
* Direct array item access.
* @param idx Item index
* @return Reference to item
*/
int16 &ScriptVars::operator[](unsigned int idx) {
debug(6, "assert(%d < %d)", idx, _size);
assert(idx < _size);
return _vars[idx];
}
/**
* Direct read-only array item access.
* @param idx Item index
* @return Copy of item
*/
int16 ScriptVars::operator[](unsigned int idx) const {
debug(6, "assert(%d < %d)", idx, _size);
assert(idx < _size);
return _vars[idx];
}
/**
* Savefile writer
* @param fHandle Savefile open for writing
*/
void ScriptVars::save(Common::OutSaveFile &fHandle) const {
save(fHandle, _size);
}
/**
* Savefile writer with data length limit
* @param fHandle Savefile open for writing
* @param len Length of data to be written (len <= _size)
*/
void ScriptVars::save(Common::OutSaveFile &fHandle, unsigned int len) const {
debug(6, "assert(%d <= %d)", len, _size);
assert(len <= _size);
for (unsigned int i = 0; i < len; i++) {
fHandle.writeUint16BE(_vars[i]);
}
}
/**
* Restore array from savefile
* @param fHandle Savefile open for reading
*/
void ScriptVars::load(Common::SeekableReadStream &fHandle) {
load(fHandle, _size);
}
/**
* Restore part of array from savefile
* @param fHandle Savefile open for reading
* @param len Length of data to be read
*/
void ScriptVars::load(Common::SeekableReadStream &fHandle, unsigned int len) {
debug(6, "assert(%d <= %d)", len, _size);
assert(len <= _size);
for (unsigned int i = 0; i < len; i++) {
_vars[i] = fHandle.readUint16BE();
}
}
/**
* Reset all values to 0
*/
void ScriptVars::reset() {
memset(_vars, 0, _size * sizeof(int16));
}
/**
* Constructor for partial loading
* @param s Size of bytecode which will be added later
*
* This constructor _MUST_ be followed by setdata() method call before the
* instance can be used. It leaves the instance in partially invalid state.
*/
RawScript::RawScript(uint16 s) : _size(s), _data(NULL),
_labels(SCRIPT_STACK_SIZE) { }
/**
* Complete constructor
* @param data Script bytecode
* @param s Bytecode length
*/
RawScript::RawScript(const FWScriptInfo &info, const byte *data, uint16 s) :
_size(s), _data(NULL), _labels(SCRIPT_STACK_SIZE) {
setData(info, data);
}
/**
* Copy constructor
*/
RawScript::RawScript(const RawScript &src) : _size(src._size),
_data(new byte[src._size + 1]), _labels(src._labels) {
assert(_data);
memcpy(_data, src._data, _size + 1);
}
/**
* Destructor
*/
RawScript::~RawScript() {
delete[] _data;
}
/**
* Assignment operator
*/
RawScript &RawScript::operator=(const RawScript &src) {
assert(src._data);
byte *tmp = new byte[src._size + 1];
assert(tmp);
_labels = src._labels;
_size = src._size;
delete[] _data;
_data = tmp;
memcpy(_data, src._data, _size);
_data[_size] = 0;
return *this;
}
/**
* Get the next label in bytecode
* @param info Script info instance
* @param offset Starting offset
* @return Index of the next label in bytecode or _size on end of bytecode
*
* computeScriptStackSub replacement
*/
int RawScript::getNextLabel(const FWScriptInfo &info, int offset) const {
assert(_data);
int pos = offset;
assert(pos >= 0);
while (pos < _size) {
uint8 opcode = _data[pos++];
const char *ptr = info.opcodeInfo(opcode);
if (!ptr) {
continue;
}
for (; *ptr; ++ptr) {
switch (*ptr) {
case 'b': // byte
pos++;
break;
case 'w': // word
pos += 2;
break;
case 'c': { // byte != 0 ? byte : word
uint8 test = _data[pos];
pos++;
if (test) {
pos++;
} else {
pos += 2;
}
}
break;
case 'l': // label
return pos;
case 's': // string
while (_data[pos++] != 0)
;
break;
case 'x': // exit script
return -pos - 1;
default:
break;
}
}
}
return _size;
}
/**
* Calculate initial script labels
* @param info Script info instance
*
* computeScriptStack replacement
*/
void RawScript::computeLabels(const FWScriptInfo &info) {
assert(_data);
int pos = 0;
int i;
// reset labels
for (i = 0; i < SCRIPT_STACK_SIZE; i++) {
_labels[i] = -1;
}
// parse bytecode
while ((pos = getNextLabel(info, pos)) >= 0) {
i = _data[pos];
_labels[i] = ++pos;
}
}
/**
* find the next label from current position
* @param info Script info instance
* @param index Label index to look for
* @param offset Current position in script
* @return Position of next instruction following the label
*
* computeScriptStackFromScript replacement
*/
uint16 RawScript::getLabel(const FWScriptInfo &info, byte index, uint16 offset) const {
assert(_data);
int pos = offset;
while ((pos = getNextLabel(info, pos)) >= 0) {
if (_data[pos++] == index) {
return pos;
}
}
return -pos - 1;
}
/**
* Copy bytecode and calculate labels
* @param data Bytecode to copy, must be _size long
*/
void RawScript::setData(const FWScriptInfo &info, const byte *data) {
assert(!_data); // this function should be called only once per instance
_data = new byte[_size + 1];
assert(data && _data);
memcpy(_data, data, _size * sizeof(byte));
_data[_size] = 0;
computeLabels(info);
}
/**
* Initial script labels
* @return Precalculated script labels
*/
const ScriptVars &RawScript::labels() const {
return _labels;
}
/**
* One byte of bytecode
* @param pos Index in bytecode
* @return Byte from bytecode
*/
byte RawScript::getByte(unsigned int pos) const {
assert(_data && pos < _size);
return _data[pos];
}
/**
* One word of bytecode
* @param pos Index of the first byte in bytecode
* @return Word of bytecode
*/
uint16 RawScript::getWord(unsigned int pos) const {
assert(_data && pos + 1 < _size);
return READ_BE_UINT16(_data + pos);
}
/**
* String in bytecode
* @param pos Index of the first char in string
* @return Pointer to part of bytecode
*/
const char *RawScript::getString(unsigned int pos) const {
assert(_data && pos < _size);
return (const char *)(_data + pos);
}
/**
* Constructor for partial loading
* @param size Size of bytecode which will be added later
* @param p1 First object script parameter
* @param p2 Second object script parameter
* @param p3 Third object script parameter
*
* This constructor _MUST_ be followed by setdata() method call before the
* instance can be used. It leaves the instance in partially invalid state.
*/
RawObjectScript::RawObjectScript(uint16 s, uint16 p1, uint16 p2, uint16 p3)
: RawScript(s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) {
}
/**
* Complete constructor
* @param data Script bytecode
* @param s Bytecode length
* @param p1 First object script parameter
* @param p2 Second object script parameter
* @param p3 Third object script parameter
*/
RawObjectScript::RawObjectScript(const FWScriptInfo &info, const byte *data,
uint16 s, uint16 p1, uint16 p2, uint16 p3)
: RawScript(info, data, s), _runCount(0), _param1(p1), _param2(p2), _param3(p3) {
}
/**
* Contructor for global scripts
* @param script Script bytecode reference
* @param idx Script bytecode index
*/
FWScript::FWScript(const RawScript &script, int16 idx) : _script(script),
_pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
_globalVars(g_cine->_globalVars), _info(new FWScriptInfo) {
}
/**
* Copy constructor
*/
FWScript::FWScript(const FWScript &src) : _script(src._script), _pos(src._pos),
_line(src._line), _compare(src._compare), _index(src._index),
_labels(src._labels), _localVars(src._localVars),
_globalVars(src._globalVars), _info(new FWScriptInfo) {
}
/**
* Contructor for global scripts in derived classes
* @param script Script bytecode reference
* @param idx Script bytecode index
*/
FWScript::FWScript(const RawScript &script, int16 idx, FWScriptInfo *info)
: _script(script), _pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
_globalVars(g_cine->_globalVars), _info(info) {
}
/**
* Constructor for object scripts in derived classes
* @param script Script bytecode reference
* @param idx Script bytecode index
*/
FWScript::FWScript(RawObjectScript &script, int16 idx, FWScriptInfo *info)
: _script(script), _pos(0), _line(0), _compare(0), _index(idx),
_labels(script.labels()), _localVars(LOCAL_VARS_SIZE),
_globalVars(g_cine->_globalVars), _info(info) {
_localVars[0] = script.run();
}
/**
* Copy constructor for derived classes
*/
FWScript::FWScript(const FWScript &src, FWScriptInfo *info)
: _script(src._script), _pos(src._pos), _line(src._line),
_compare(src._compare), _index(src._index), _labels(src._labels),
_localVars(src._localVars), _globalVars(src._globalVars), _info(info) { }
FWScript::~FWScript() {
delete _info;
}
/**
* Read next byte from bytecode
* @return Byte from bytecode
*/
byte FWScript::getNextByte() {
byte val = _script.getByte(_pos);
_pos++;
return val;
}
/**
* Read next word from bytecode
* @return Word from bytecode
*/
uint16 FWScript::getNextWord() {
uint16 val = _script.getWord(_pos);
_pos += 2;
return val;
}
/**
* Read next string from bytecode
* @return Pointer to string
*/
const char *FWScript::getNextString() {
const char *val = _script.getString(_pos);
_pos += strlen(val) + 1;
return val;
}
/**
* Restore script state from savefile
* @param labels Restored script labels
* @param local Restored local script variables
* @param compare Restored last comparison result
* @param pos Restored script position
*/
void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) {
assert(pos <= _script._size);
_labels = labels;
_localVars = local;
_compare = compare;
_pos = _line = pos;
}
/**
* Execute script
* @return <0 on script termination, >0 on script pause
*
* executeScript replacement.
* Instruction handler must return 0 if the script should continue or
* nonzero with the same meaning as return value of this function
*/
int FWScript::execute() {
int ret = 0;
if (_script._size) {
while (!ret) {
_line = _pos;
byte opcode = getNextByte();
OpFunc handler = _info->opcodeHandler(opcode);
if (handler) {
ret = (this->*handler)();
}
}
}
return ret;
}
/**
* Save script to savefile
* @param fHandle Savefile handle
*/
void FWScript::save(Common::OutSaveFile &fHandle) const {
_labels.save(fHandle);
_localVars.save(fHandle);
fHandle.writeUint16BE(_compare);
fHandle.writeUint16BE(_pos);
// data order sucks...
fHandle.writeUint16BE(_index);
}
/**
* Get opcode info string
* @param opcode Opcode to look for in opcode table
*/
const char *FWScriptInfo::opcodeInfo(byte opcode) const {
if (opcode == 0 || opcode > FWScript::_numOpcodes) {
return NULL;
}
if (!FWScript::_opcodeTable[opcode - 1].args) {
warning("Undefined opcode 0x%02X in FWScriptInfo::opcodeInfo", opcode - 1);
return NULL;
}
return FWScript::_opcodeTable[opcode - 1].args;
}
/**
* Get opcode handler pointer
* @param opcode Opcode to look for in opcode table
*/
OpFunc FWScriptInfo::opcodeHandler(byte opcode) const {
if (opcode == 0 || opcode > FWScript::_numOpcodes) {
return NULL;
}
if (!FWScript::_opcodeTable[opcode - 1].proc) {
warning("Undefined opcode 0x%02X in FWScriptInfo::opcodeHandler", opcode - 1);
return NULL;
}
return FWScript::_opcodeTable[opcode - 1].proc;
}
/**
* Create new FWScript instance
* @param script Script bytecode
* @param index Bytecode index
*/
FWScript *FWScriptInfo::create(const RawScript &script, int16 index) const {
return new FWScript(script, index);
}
/**
* Create new FWScript instance
* @param script Object script bytecode
* @param index Bytecode index
*/
FWScript *FWScriptInfo::create(const RawObjectScript &script, int16 index) const {
return new FWScript(script, index);
}
/**
* Load saved FWScript instance
* @param script Script bytecode
* @param index Bytecode index
* @param local Local variables
* @param labels Script labels
* @param compare Last compare result
* @param pos Position in script
*/
FWScript *FWScriptInfo::create(const RawScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const {
FWScript *tmp = new FWScript(script, index);
assert(tmp);
tmp->load(labels, local, compare, pos);
return tmp;
}
/**
* Load saved FWScript instance
* @param script Object script bytecode
* @param index Bytecode index
* @param local Local variables
* @param labels Script labels
* @param compare Last compare result
* @param pos Position in script
*/
FWScript *FWScriptInfo::create(const RawObjectScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const {
FWScript *tmp = new FWScript(script, index);
assert(tmp);
tmp->load(labels, local, compare, pos);
return tmp;
}
// ------------------------------------------------------------------------
// FUTURE WARS opcodes
// ------------------------------------------------------------------------
int FWScript::o1_modifyObjectParam() {
byte objIdx = getNextByte();
byte paramIdx = getNextByte();
int16 newValue = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: modifyObjectParam(objIdx:%d,paramIdx:%d,newValue:%d)", _line, objIdx, paramIdx, newValue);
modifyObjectParam(objIdx, paramIdx, newValue);
return 0;
}
int FWScript::o1_getObjectParam() {
byte objIdx = getNextByte();
byte paramIdx = getNextByte();
byte newValue = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: getObjectParam(objIdx:%d,paramIdx:%d,var:%d)", _line, objIdx, paramIdx, newValue);
_localVars[newValue] = getObjectParam(objIdx, paramIdx);
return 0;
}
int FWScript::o1_addObjectParam() {
byte objIdx = getNextByte();
byte paramIdx = getNextByte();
int16 newValue = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: addObjectParam(objIdx:%d,paramIdx:%d,newValue:%d)", _line, objIdx, paramIdx, newValue);
addObjectParam(objIdx, paramIdx, newValue);
return 0;
}
int FWScript::o1_subObjectParam() {
byte objIdx = getNextByte();
byte paramIdx = getNextByte();
int16 newValue = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: subObjectParam(objIdx:%d,paramIdx:%d,newValue:%d)", _line, objIdx, paramIdx, newValue);
subObjectParam(objIdx, paramIdx, newValue);
return 0;
}
int FWScript::o1_mulObjectParam() {
byte objIdx = getNextByte();
byte paramIdx = getNextByte();
int16 newValue = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: mulObjectParam(objIdx:%d,paramIdx:%d,newValue:%d)", _line, objIdx, paramIdx, newValue);
// FIXME? In PC versions of Future Wars and Operation Stealth the multiplication is done unsigned.
// (16b x 16b -> 32b, taking only 16 LSBs). The question is, does it really matter?
int16 currentValue = getObjectParam(objIdx, paramIdx);
modifyObjectParam(objIdx, paramIdx, currentValue * newValue);
return 0;
}
int FWScript::o1_divObjectParam() {
byte objIdx = getNextByte();
byte paramIdx = getNextByte();
int16 newValue = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: divObjectParam(objIdx:%d,paramIdx:%d,newValue:%d)", _line, objIdx, paramIdx, newValue);
// In PC versions of Future Wars and Operation Stealth the division is done signed.
// Dividend is first sign extended from 16 bits to 32 bits and then divided by the
// 16 bit divider using signed division. Only 16 LSBs of the quotient are saved.
int16 currentValue = getObjectParam(objIdx, paramIdx);
modifyObjectParam(objIdx, paramIdx, currentValue / newValue);
return 0;
}
int FWScript::o1_compareObjectParam() {
byte objIdx = getNextByte();
byte param1 = getNextByte();
int16 param2 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: compareObjectParam(objIdx:%d,type:%d,value:%d)", _line, objIdx, param1, param2);
_compare = compareObjectParam(objIdx, param1, param2);
return 0;
}
int FWScript::o1_setupObject() {
byte objIdx = getNextByte();
int16 param1 = getNextWord();
int16 param2 = getNextWord();
int16 param3 = getNextWord();
int16 param4 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: setupObject(objIdx:%d,%d,%d,%d,%d)", _line, objIdx, param1, param2, param3, param4);
setupObject(objIdx, param1, param2, param3, param4);
return 0;
}
int FWScript::o1_checkCollision() {
byte objIdx = getNextByte();
int16 param1 = getNextWord();
int16 param2 = getNextWord();
int16 param3 = getNextWord();
int16 param4 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: checkCollision(objIdx:%d,%d,%d,%d,%d)", _line, objIdx, param1, param2, param3, param4);
_compare = checkCollision(objIdx, param1, param2, param3, param4);
return 0;
}
int FWScript::o1_loadVar() {
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte dataIdx = getNextByte();
int16 var;
switch (varType) {
case 1:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = var[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] = _localVars[dataIdx];
break;
case 2:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = globalVars[%d] (= %d)", _line, varIdx, dataIdx, _globalVars[dataIdx]);
_localVars[varIdx] = _globalVars[dataIdx];
break;
case 3:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = mouseX", _line, varIdx);
getMouseData(mouseUpdateStatus, &dummyU16, (uint16 *)&var, &dummyU16);
_localVars[varIdx] = var;
break;
case 4:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = mouseY", _line, varIdx);
getMouseData(mouseUpdateStatus, &dummyU16, &dummyU16, (uint16 *)&var);
_localVars[varIdx] = var;
break;
case 5:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = rand mod %d", _line, varIdx, dataIdx);
_localVars[varIdx] = g_cine->_rnd.getRandomNumber(dataIdx - 1);
break;
case 8:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = file[%d].packedSize", _line, varIdx, dataIdx);
_localVars[varIdx] = g_cine->_partBuffer[dataIdx].packedSize;
break;
case 9:
debugC(5, kCineDebugScript, "Line: %d: var[%d] = file[%d].unpackedSize", _line, varIdx, dataIdx);
_localVars[varIdx] = g_cine->_partBuffer[dataIdx].unpackedSize;
break;
default:
error("executeScript: o1_loadVar: Unknown variable type %d", varType);
}
} else {
int16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: var[%d] = %d", _line, varIdx, value);
_localVars[varIdx] = value;
}
return 0;
}
int FWScript::o1_addVar() {
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte dataIdx = getNextByte();
if (varType == 1) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] += var[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] += _localVars[dataIdx];
} else if (varType == 2) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] += globalVar[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] += _globalVars[dataIdx];
}
} else {
int16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: var[%d] += %d", _line, varIdx, value);
_localVars[varIdx] += value;
}
return 0;
}
int FWScript::o1_subVar() {
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte dataIdx = getNextByte();
if (varType == 1) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] -= var[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] -= _localVars[dataIdx];
} else if (varType == 2) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] -= globalVar[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] -= _globalVars[dataIdx];
}
} else {
int16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: var[%d] -= %d", _line, varIdx, value);
_localVars[varIdx] -= value;
}
return 0;
}
int FWScript::o1_mulVar() {
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte dataIdx = getNextByte();
if (varType == 1) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] *= var[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] *= _localVars[dataIdx];
} else if (varType == 2) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] *= globalVar[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] *= _globalVars[dataIdx];
}
} else {
int16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: var[%d] *= %d", _line, varIdx, value);
_localVars[varIdx] *= value;
}
return 0;
}
int FWScript::o1_divVar() {
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte dataIdx = getNextByte();
if (varType == 1) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] /= var[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] /= _localVars[dataIdx];
} else if (varType == 2) {
debugC(5, kCineDebugScript, "Line: %d: var[%d] /= globalVar[%d]", _line, varIdx, dataIdx);
_localVars[varIdx] /= _globalVars[dataIdx];
}
} else {
int16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: var[%d] /= %d", _line, varIdx, value);
_localVars[varIdx] /= value;
}
return 0;
}
int FWScript::o1_compareVar() {
// WORKAROUND: A workaround for a script bug in script file CODE2.PRC
// in at least some of the Amiga and Atari ST versions of Future Wars.
// Fixes bug #2016647 (FW: crash with italian amiga version). A local
// variable 251 is compared against value 0 although it's quite apparent
// from the context in the script that instead global variable 251 should
// be compared against value 0. So looks like someone made a typo when
// making the scripts. Therefore we change that particular comparison
// from using the local variable 251 to using the global variable 251.
if (g_cine->getGameType() == Cine::GType_FW && scumm_stricmp(currentPrcName, "CODE2.PRC") == 0 &&
(g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) &&
_script.getByte(_pos) == 251 && _script.getByte(_pos + 1) == 0 && _script.getWord(_pos + 2) == 0) {
return o1_compareGlobalVar();
}
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte dataIdx = getNextByte();
if (varType == 1) {
debugC(5, kCineDebugScript, "Line: %d: compare var[%d] and var[%d]", _line, varIdx, dataIdx);
_compare = compareVars(_localVars[varIdx], _localVars[dataIdx]);
} else if (varType == 2) {
debugC(5, kCineDebugScript, "Line: %d: compare var[%d] and globalVar[%d]", _line, varIdx, dataIdx);
_compare = compareVars(_localVars[varIdx], _globalVars[dataIdx]);
}
} else {
int16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: compare var[%d] and %d", _line, varIdx, value);
_compare = compareVars(_localVars[varIdx], value);
}
return 0;
}
int FWScript::o1_modifyObjectParam2() {
byte objIdx = getNextByte();
byte paramIdx = getNextByte();
byte newValue = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: modifyObjectParam2(objIdx:%d,paramIdx:%d,var[%d])", _line, objIdx, paramIdx, newValue);
modifyObjectParam(objIdx, paramIdx, _localVars[newValue]);
return 0;
}
int FWScript::o1_loadMask0() {
// OP_loadV7Element
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: addSpriteOverlay(%d)", _line, param);
addOverlay(param, 0);
return 0;
}
int FWScript::o1_unloadMask0() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: removeSpriteOverlay(%d)", _line, param);
removeOverlay(param, 0);
return 0;
}
int FWScript::o1_addToBgList() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: addToBGList(%d)", _line, param);
addToBGList(param);
return 0;
}
int FWScript::o1_loadMask1() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: addOverlay1(%d)", _line, param);
addOverlay(param, 1);
return 0;
}
int FWScript::o1_unloadMask1() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: removeOverlay1(%d)", _line, param);
removeOverlay(param, 1);
return 0;
}
int FWScript::o1_loadMask4() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: addOverlayType4(%d)", _line, param);
addOverlay(param, 4);
return 0;
}
int FWScript::o1_unloadMask4() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: removeSpriteOverlay4(%d)", _line, param);
removeOverlay(param, 4);
return 0;
}
int FWScript::o1_addSpriteFilledToBgList() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: op1A(%d) -> TODO !", _line, param);
addSpriteFilledToBGList(param);
return 0;
}
int FWScript::o1_clearBgIncrustList() {
debugC(5, kCineDebugScript, "Line: %d: freeBgIncrustList", _line);
g_cine->_bgIncrustList.clear();
return 0;
}
int FWScript::o1_label() {
byte labelIdx = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: label(%d)", _line, labelIdx);
_labels[labelIdx] = _pos;
return 0;
}
int FWScript::o1_goto() {
byte labelIdx = getNextByte();
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: goto label(%d)", _line, labelIdx);
_pos = _labels[labelIdx];
return 0;
}
int FWScript::o1_gotoIfSup() {
byte labelIdx = getNextByte();
if (_compare == kCmpGT) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(>) goto %d (true)", _line, labelIdx);
_pos = _labels[labelIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: if(>) goto %d (false)", _line, labelIdx);
}
return 0;
}
int FWScript::o1_gotoIfSupEqu() {
byte labelIdx = getNextByte();
if (_compare & (kCmpGT | kCmpEQ)) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(>=) goto %d (true)", _line, labelIdx);
_pos = _labels[labelIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: if(>=) goto %d (false)", _line, labelIdx);
}
return 0;
}
int FWScript::o1_gotoIfInf() {
byte labelIdx = getNextByte();
if (_compare == kCmpLT) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(<) goto %d (true)", _line, labelIdx);
_pos = _labels[labelIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: if(<) goto %d (false)", _line, labelIdx);
}
return 0;
}
int FWScript::o1_gotoIfInfEqu() {
byte labelIdx = getNextByte();
if (_compare & (kCmpLT | kCmpEQ)) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(<=) goto %d (true)", _line, labelIdx);
_pos = _labels[labelIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: if(<=) goto %d (false)", _line, labelIdx);
}
return 0;
}
int FWScript::o1_gotoIfEqu() {
byte labelIdx = getNextByte();
if (_compare == kCmpEQ) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(==) goto %d (true)", _line, labelIdx);
_pos = _labels[labelIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: if(==) goto %d (false)", _line, labelIdx);
}
return 0;
}
int FWScript::o1_gotoIfDiff() {
byte labelIdx = getNextByte();
if (_compare != kCmpEQ) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(!=) goto %d (true)", _line, labelIdx);
_pos = _labels[labelIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: if(!=) goto %d (false)", _line, labelIdx);
}
return 0;
}
int FWScript::o1_removeLabel() {
byte labelIdx = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: removeLabel(%d)", _line, labelIdx);
_labels[labelIdx] = -1;
return 0;
}
int FWScript::o1_loop() {
byte varIdx = getNextByte();
byte labelIdx = getNextByte();
_localVars[varIdx]--;
if (_localVars[varIdx] >= 0) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: loop(var[%d]) goto %d (continue)", _line, varIdx, labelIdx);
_pos = _labels[labelIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: loop(var[%d]) goto %d (stop)", _line, varIdx, labelIdx);
}
return 0;
}
int FWScript::o1_startGlobalScript() {
// OP_startScript
byte param = getNextByte();
assert(param < NUM_MAX_SCRIPT);
debugC(5, kCineDebugScript, "Line: %d: startScript(%d)", _line, param);
// Cheat for Scene 6 Guards Labyrinth Arcade Game to disable John's Death (to aid playtesting)
if (g_cine->getGameType() == Cine::GType_OS && labyrinthCheat && scumm_stricmp(currentPrcName, "LABY.PRC") == 0 && param == 46) {
warning("LABY.PRC startScript(46) Disabled. CHEAT!");
return 0;
}
// Cheat for Scene 8 Rats Labyrinth Arcade Game to disable John's Death (to aid playtesting)
if (g_cine->getGameType() == Cine::GType_OS && labyrinthCheat && scumm_stricmp(currentPrcName, "EGOU.PRC") == 0 && param == 46) {
warning("EGOU.PRC startScript(46) Disabled. CHEAT!");
return 0;
}
addScriptToGlobalScripts(param);
return 0;
}
int FWScript::o1_endGlobalScript() {
byte scriptIdx = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: stopGlobalScript(%d)", _line, scriptIdx);
ScriptList::iterator it = g_cine->_globalScripts.begin();
for (; it != g_cine->_globalScripts.end(); ++it) {
if ((*it)->_index == scriptIdx) {
(*it)->_index = -1;
}
}
return 0;
}
int FWScript::o1_loadAnim() {
// OP_loadResource
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadResource(\"%s\")", _line, param);
loadResource(param);
return 0;
}
int FWScript::o1_loadBg() {
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadBg(\"%s\")", _line, param);
if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) {
char buffer[20];
removeExtention(buffer, param);
g_sound->setBgMusic(atoi(buffer + 1));
}
loadBg(param);
if (g_cine->getGameType() == Cine::GType_OS) {
removeBgIncrustsWithBgIdx(0);
} else {
g_cine->_bgIncrustList.clear();
}
bgVar0 = 0;
return 0;
}
/** Load collision table data */
int FWScript::o1_loadCt() {
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadCt(\"%s\")", _line, param);
loadCtFW(param);
return 0;
}
int FWScript::o1_loadPart() {
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadPart(\"%s\")", _line, param);
loadPart(param);
return 0;
}
int FWScript::o1_closePart() {
debugC(5, kCineDebugScript, "Line: %d: closePart", _line);
closePart();
return 0;
}
int FWScript::o1_loadNewPrcName() {
// OP_loadData
byte param1 = getNextByte();
const char *param2 = getNextString();
assert(param1 <= 3);
switch (param1) {
case 0:
debugC(5, kCineDebugScript, "Line: %d: loadPrc(\"%s\")", _line, param2);
Common::strlcpy(newPrcName, param2, sizeof(newPrcName));
break;
case 1:
debugC(5, kCineDebugScript, "Line: %d: loadRel(\"%s\")", _line, param2);
Common::strlcpy(newRelName, param2, sizeof(newRelName));
break;
case 2:
debugC(5, kCineDebugScript, "Line: %d: loadObject(\"%s\")", _line, param2);
Common::strlcpy(newObjectName, param2, sizeof(newObjectName));
break;
case 3:
debugC(5, kCineDebugScript, "Line: %d: loadMsg(\"%s\")", _line, param2);
Common::strlcpy(newMsgName, param2, sizeof(newMsgName));
break;
default:
break;
}
return 0;
}
int FWScript::o1_requestCheckPendingDataLoad() {
debugC(5, kCineDebugScript, "Line: %d: request data load", _line);
checkForPendingDataLoadSwitch = 1;
return 0;
}
int FWScript::o1_blitAndFade() {
debugC(5, kCineDebugScript, "Line: %d: request fadein", _line);
// HACK: This section is not present in disassembly
// but this is an attempt to prevent flashing a
// normally illuminated screen and then fading it in by
// setting the palette initially to black.
if (hacksEnabled) {
renderer->setBlackPalette(false); // Does not update _changePal
renderer->setPalette();
g_system->updateScreen();
}
gfxFadeInRequested = 1;
return 0;
}
int FWScript::o1_fadeToBlack() {
debugC(5, kCineDebugScript, "Line: %d: request fadeout", _line);
renderer->fadeToBlack();
return 0;
}
int FWScript::o1_transformPaletteRange() {
byte startColor = getNextByte();
byte endColor = getNextByte();
int16 r = getNextWord();
int16 g = getNextWord();
int16 b = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: transformPaletteRange(from:%d,to:%d,r:%d,g:%d,b:%d)", _line, startColor, endColor, r, g, b);
renderer->transformPalette(startColor, endColor, r, g, b);
return 0;
}
/** Set the default background color used for message boxes. */
int FWScript::o1_setDefaultMenuBgColor() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: setDefaultMenuBgColor(%d)", _line, param);
renderer->_messageBg = param;
return 0;
}
int FWScript::o1_palRotate() {
byte a = getNextByte();
byte b = getNextByte();
byte c = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: palRotate(%d,%d,%d)", _line, a, b, c);
renderer->rotatePalette(a, b, c);
return 0;
}
/**
* Pause script.
* @todo Make sure it works
*/
int FWScript::o1_break() {
debugC(5, kCineDebugScript, "Line: %d: break", _line);
// WORKAROUND for bug #2669415 ("FW: half walking speed in a screen").
// The problem was that in Amiga/Atari ST versions of Future Wars the
// walking speed has halved in a forest scene where a monk's robe hangs
// on a tree branch (Up and to the left from the medieval castle's front).
//
// Initialization script for the scene is PART02.PRC's 26th script (011_INIT)
// and the background used in the scene is L11.PI1. The difference between
// the PC version and the Amiga/Atari ST version of the script is that the
// PC version calls scripts 37 and 36 for handling movement of the character
// when Amiga/Atari ST version calls scripts 22 and 21 for the same purpose
// (Scripts 37 and 22 handle vertical movement, 36 and 21 the horizontal).
//
// The called scripts only differ functionally so that all BREAK opcodes have been
// doubled in the Amiga/Atari ST versions (i.e. one BREAK has become two BREAKs)
// and in script 21 after LABEL_25 there's an extra opcode that isn't in script 36:
// SET globalvars[251], 0.
//
// As a BREAK opcode stops the execution of a script it causes a pause and
// with the BREAKs doubled the pause is twice as long in the Amiga/Atari ST versions.
// Thus the longer pause is eliminated by running only one BREAK when several
// are designated (i.e. ignoring a BREAK if there's another BREAK after it).
//
// TODO: Check whether the speed is halved when running the original executable under an emulator
if (g_cine->getGameType() == Cine::GType_FW &&
(g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) &&
_pos < _script._size && _script.getByte(_pos) == (0x4F + 1)) { // Is the next opcode a BREAK too?
// Determine whether to combine breaks into a single break based on
// current procedure's part number (e.g. "PART02.PRC" -> 2) and the
// current background's number (e.g. "L11.PI1" -> 11).
if ((Common::matchString(currentPrcName, "PART0#.PRC", true) || // e.g. "PART03.PRC"
Common::matchString(currentPrcName, "PART0#?.PRC", true)) && // e.g. "PART02C.PRC"
(Common::matchString(renderer->getBgName(), "L#.PI1", true) ||
Common::matchString(renderer->getBgName(), "L##.PI1", true))) {
const int partNum = (currentPrcName[5] - '0'); // The single digit after "PART0" prefix
if (partNum >= 2 && partNum <= 4) {
Common::String bgName(renderer->getBgName());
bgName.deleteChar(0); // Remove prefix "L"
bgName.erase(bgName.find('.'), Common::String::npos); // Remove suffix ".PI1"
const int bgNum = (int)bgName.asUint64(); // The rest is the background number
// Fall through by default on each case in this switch statement:
switch (bgNum) {
case 6: // Swamp with mosquitoes
case 9: // Inside the medieval tavern
//case 10: // The medieval castle's hall (Fix breaks scene)
case 11: // Tree with monks habit
case 14: // Room with stained glass windows in monastery (Door on left)
case 16: // Father superior's room in monastery (Door on right)
//case 18: // The medieval castle's teleport room (Fix breaks scene)
case 21: // Sewers in the future
case 25: // Bathroom at the metro station
case 27: // Cell in the future
case 35: // At door of Crughons' shuttle
//case 45: // Space station's computer room (Fix breaks scene)
return 0;
}
}
}
}
// Jump over breaks when running only until o1_freePartRange(0, 200) in AUTO00.PRC.
// This is used for making sound effects work with Roland MT-32 and AdLib
// when loading savegames.
if (runOnlyUntilFreePartRangeFirst200) {
return 0;
}
return 1;
}
/**
* Terminate script
* @todo Make sure it works
*/
int FWScript::o1_endScript() {
debugC(5, kCineDebugScript, "Line: %d: endScript", _line);
return -1;
}
int FWScript::o1_message() {
byte param1 = getNextByte();
uint16 param2 = getNextWord();
uint16 param3 = getNextWord();
uint16 param4 = getNextWord();
uint16 param5 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: message(%d,%d,%d,%d,%d)", _line, param1, param2, param3, param4, param5);
addMessage(param1, param2, param3, param4, param5);
return 0;
}
int FWScript::o1_loadGlobalVar() {
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte dataIdx = getNextByte();
if (varType == 1) {
debugC(5, kCineDebugScript, "Line: %d: globalVars[%d] = var[%d]", _line, varIdx, dataIdx);
_globalVars[varIdx] = _localVars[dataIdx];
} else {
debugC(5, kCineDebugScript, "Line: %d: globalVars[%d] = globalVars[%d]", _line, varIdx, dataIdx);
_globalVars[varIdx] = _globalVars[dataIdx];
}
} else {
uint16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: globalVars[%d] = %d", _line, varIdx, value);
_globalVars[varIdx] = value;
}
return 0;
}
int FWScript::o1_compareGlobalVar() {
byte varIdx = getNextByte();
byte varType = getNextByte();
if (varType) {
byte value = getNextByte();
if (varType == 1) {
debugC(5, kCineDebugScript, "Line: %d: compare globalVars[%d] and var[%d]", _line, varIdx, value);
_compare = compareVars(_globalVars[varIdx], _localVars[value]);
} else {
debugC(5, kCineDebugScript, "Line: %d: compare globalVars[%d] and globalVars[%d]", _line, varIdx, value);
_compare = compareVars(_globalVars[varIdx], _globalVars[value]);
}
} else {
uint16 value = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: compare globalVars[%d] and %d", _line, varIdx, value);
// WORKAROUND for bug #2054882. Without this, the monks will always
// kill you as an impostor, even if you enter the monastery in disguise.
//
// TODO: Check whether this might be worked around in some other way
// like setting global variable 255 to 143 in Future Wars (This is
// supposedly what Future Wars checks for from time to time during
// gameplay to verify that copy protection was successfully passed).
if (varIdx == 255 && (g_cine->getGameType() == Cine::GType_FW)) {
_compare = kCmpEQ;
} else {
_compare = compareVars(_globalVars[varIdx], value);
}
}
return 0;
}
int FWScript::o1_declareFunctionName() {
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: comment(%s)", _line, param);
return 0;
}
int FWScript::o1_freePartRange() {
byte startIdx = getNextByte();
byte numIdx = getNextByte();
assert(startIdx + numIdx <= NUM_MAX_ANIMDATA);
debugC(5, kCineDebugScript, "Line: %d: freePartRange(%d,%d)", _line, startIdx, numIdx);
freeAnimDataRange(startIdx, numIdx);
// Used for bailing out early from AUTO00.PRC before loading a savegame.
// Used for making sound effects work using Roland MT-32 and AdLib in
// Operation Stealth after loading a savegame. The sound effects are loaded
// in AUTO00.PRC using a combination of o2_loadAbs and o2_playSample(1, ...)
// before o1_freePartRange(0, 200).
if (runOnlyUntilFreePartRangeFirst200 && startIdx == 0 && numIdx == 200) {
runOnlyUntilFreePartRangeFirst200 = false;
return o1_endScript();
}
return 0;
}
int FWScript::o1_unloadAllMasks() {
debugC(5, kCineDebugScript, "Line: %d: unloadAllMasks()", _line);
g_cine->_overlayList.clear();
return 0;
}
/**
* @todo Implement this instruction
*/
int FWScript::o1_setScreenDimensions() {
uint16 a = getNextWord();
uint16 b = getNextWord();
uint16 c = getNextWord();
uint16 d = getNextWord();
warning("STUB: o1_setScreenDimensions(%x, %x, %x, %x)", a, b, c, d);
// setupScreenParam
return 0;
}
/**
* @todo Implement this instruction
*/
int FWScript::o1_displayBackground() {
warning("STUB: o1_displayBackground()");
return 0;
}
int FWScript::o1_initializeZoneData() {
debugC(5, kCineDebugScript, "Line: %d: initializeZoneData()", _line);
for (int i = 0; i < NUM_MAX_ZONE; i++) {
g_cine->_zoneData[i] = i;
}
return 0;
}
int FWScript::o1_setZoneDataEntry() {
byte zoneIdx = getNextByte();
int16 var = getNextWord();
// HACK: Fix storage room's door animation on Dr. Why's island.
if (hacksEnabled && g_cine->getGameType() == Cine::GType_OS && zoneIdx == 2 && var == 8 &&
_script._size >= 10 && _script.getByte(9) == 0 &&
scumm_stricmp(_script.getString(0), "Z012_INIT") == 0 && _line == 34) {
return 0;
}
debugC(5, kCineDebugScript, "Line: %d: setZone[%d] = %d", _line, zoneIdx, var);
if (zoneIdx < NUM_MAX_ZONE) {
g_cine->_zoneData[zoneIdx] = var;
}
return 0;
}
int FWScript::o1_getZoneDataEntry() {
byte zoneIdx = getNextByte();
byte var = getNextByte();
_localVars[var] = g_cine->_zoneData[zoneIdx];
debugC(5, kCineDebugScript, "Line: %d: SET localVars[%d] = zoneData[%d] (= %d)", _line, var, zoneIdx, g_cine->_zoneData[zoneIdx]);
return 0;
}
/** Set the player command string's vertical position on-screen. */
int FWScript::o1_setPlayerCommandPosY() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: setPlayerCommandPosY(%d)", _line, param);
renderer->_cmdY = param;
return 0;
}
int FWScript::o1_allowPlayerInput() {
debugC(5, kCineDebugScript, "Line: %d: allowPlayerInput()", _line);
allowPlayerInput = 1;
return 0;
}
int FWScript::o1_disallowPlayerInput() {
debugC(5, kCineDebugScript, "Line: %d: dissallowPlayerInput()", _line);
allowPlayerInput = 0;
return 0;
}
int FWScript::o1_changeDataDisk() {
byte newDisk = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: changeDataDisk(%d)", _line, newDisk);
checkDataDisk(newDisk);
return 0;
}
int FWScript::o1_loadMusic() {
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadMusic(%s)", _line, param);
g_sound->loadMusic(param);
Common::strlcpy(currentDatName, param, 30);
musicIsPlaying = 0;
return 0;
}
int FWScript::o1_playMusic() {
debugC(5, kCineDebugScript, "Line: %d: playMusic()", _line);
g_sound->playMusic();
musicIsPlaying = 1;
return 0;
}
int FWScript::o1_fadeOutMusic() {
debugC(5, kCineDebugScript, "Line: %d: fadeOutMusic()", _line);
g_sound->fadeOutMusic();
musicIsPlaying = 0;
return 0;
}
int FWScript::o1_stopSample() {
debugC(5, kCineDebugScript, "Line: %d: stopSample()", _line);
g_sound->stopMusic();
musicIsPlaying = 0;
return 0;
}
/**
* @todo Implement this instruction's Amiga part (PC part already done)
* In PC versions of Future Wars and Operation Stealth this instruction does nothing else but read the parameters.
*/
int FWScript::o1_op71() {
byte a = getNextByte();
uint16 b = getNextWord();
warning("STUB: o1_op71(%x, %x)", a, b);
return 0;
}
/**
* @todo Implement this instruction's Amiga part (PC part already done)
* In PC versions of Future Wars and Operation Stealth this instruction does nothing else but read the parameters.
*/
int FWScript::o1_op72() {
uint16 a = getNextWord();
byte b = getNextByte();
uint16 c = getNextWord();
warning("STUB: o1_op72(%x, %x, %x)", a, b, c);
return 0;
}
/**
* @todo Implement this instruction's Amiga part (PC part already done)
* In PC versions of Future Wars and Operation Stealth this instruction does nothing else but read the parameters.
*/
int FWScript::o1_op73() {
// I believe this opcode is identical to o1_op72(). In fact, Operation
// Stealth doesn't even have it. It uses o1_op72() instead.
uint16 a = getNextWord();
byte b = getNextByte();
uint16 c = getNextWord();
warning("STUB: o1_op73(%x, %x, %x)", a, b, c);
return 0;
}
int FWScript::o1_playSample() {
debugC(5, kCineDebugScript, "Line: %d: playSample()", _line);
byte anim = getNextByte();
byte channel = getNextByte();
uint16 freq = getNextWord();
byte repeat = getNextByte();
int16 volume = getNextWord();
uint16 size = getNextWord();
const byte *data = g_cine->_animDataTable[anim].data();
if (!data) {
return 0;
}
if (g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) {
if (size == 0xFFFF) {
size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
} else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) {
warning("o1_playSample: Got invalid sample size %d for sample %d", size, anim);
size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
}
if (channel < 10) { // || _currentOpcode == 0x78
int channel1, channel2;
if (channel == 0) {
channel1 = 0;
channel2 = 1;
} else {
channel1 = 3;
channel2 = 2;
}
g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat);
g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat);
} else {
channel -= 10;
if (volume > 63) {
volume = 63;
}
g_sound->playSound(channel, freq, data, size, 0, 0, volume, repeat);
}
} else {
if (volume > 63 || volume < 0) {
volume = 63;
}
if (channel >= 10) {
channel -= 10;
}
if (volume < 50) {
volume = 50;
}
if (g_cine->getGameType() == Cine::GType_OS && size == 0) {
return 0;
}
// The DOS CD version of Future Wars uses CD audio for music
if (!(g_cine->getGameType() == Cine::GType_FW && (g_cine->getFeatures() & GF_CD)))
g_sound->stopMusic();
if (size == 0xFFFF) {
g_sound->playSound(channel, 0, data, 0, 0, 0, volume, 0);
} else {
g_sound->stopSound(channel);
}
}
return 0;
}
int FWScript::o1_playSampleSwapped() {
// TODO: The DOS version probably does not have any stereo support here
// since the only stereo output it supports should be the Roland MT-32.
// So it probably does the same as o1_playSample here. Checking this will
// be a good idea never the less.
if (g_cine->getPlatform() == Common::kPlatformDOS) {
return o1_playSample();
}
debugC(5, kCineDebugScript, "Line: %d: playSampleInversed()", _line);
byte anim = getNextByte();
byte channel = getNextByte();
uint16 freq = getNextWord();
byte repeat = getNextByte();
int16 volume = getNextWord();
uint16 size = getNextWord();
const byte *data = g_cine->_animDataTable[anim].data();
if (!data) {
return 0;
}
if (size == 0xFFFF) {
size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
} else if (size > g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height) {
warning("o1_playSampleSwapped: Got invalid sample size %d for sample %d", size, anim);
size = g_cine->_animDataTable[anim]._width * g_cine->_animDataTable[anim]._height;
}
int channel1, channel2;
if (channel == 0) {
channel1 = 1;
channel2 = 0;
} else {
channel1 = 2;
channel2 = 3;
}
g_sound->playSound(channel1, freq, data, size, -1, volume, 63, repeat);
g_sound->playSound(channel2, freq, data, size, 1, volume, 0, repeat);
return 0;
}
int FWScript::o1_disableSystemMenu() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: disableSystemMenu(%d)", _line, param);
disableSystemMenu = param;
return 0;
}
int FWScript::o1_loadMask5() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: addOverlay5(%d)", _line, param);
addOverlay(param, 5);
return 0;
}
int FWScript::o1_unloadMask5() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: freeOverlay5(%d)", _line, param);
removeOverlay(param, 5);
return 0;
}
//-----------------------------------------------------------------------
void addScriptToGlobalScripts(uint16 idx) {
ScriptPtr tmp(scriptInfo->create(*g_cine->_scriptTable[idx], idx));
assert(tmp);
g_cine->_globalScripts.push_back(tmp);
}
int16 getZoneFromPosition(byte *page, int16 x, int16 y, int16 width) {
byte *ptr = page + (y * width) + x / 2;
byte zoneVar;
if (!(x % 2)) {
zoneVar = (*(ptr) >> 4) & 0xF;
} else {
zoneVar = (*(ptr)) & 0xF;
}
return zoneVar;
}
byte getZoneFromPositionRaw(byte *page, int16 x, int16 y, int16 width) {
// WORKAROUND for bug #2848940 ("ScummVM crashes with Future wars"):
// Vertical positions outside the 320x200 screen (e.g. in range 200-232)
// are accessed after teleporting Lo'Ann to the future using the pendant
// and walking down the slope and out of the screen (This causes a crash
// at least on Mac OS X). The original PC version of Future Wars doesn't
// clip its coordinates in this function or in checkCollision-function
// according to reverse engineering but instead just happily reads outside
// the 320x200 buffer. Not really knowing how to properly fix this I simply
// hope that the area outside the 320x200 part is full of zero and not of
// some random values and thus I return zero here and hope nothing breaks.
if (g_cine->getGameType() == Cine::GType_FW && !Common::Rect(320, 200).contains(x, y)) {
return 0;
}
byte *ptr = page + (y * width) + x;
byte zoneVar;
zoneVar = (*(ptr)) & 0xF;
return zoneVar;
}
int16 checkCollisionFW(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneIdx) {
int16 lx = g_cine->_objectTable[objIdx].x + x;
int16 ly = g_cine->_objectTable[objIdx].y + y;
for (int16 i = 0; i < numZones; i++, lx++) {
int16 idx = getZoneFromPositionRaw(collisionPage, lx, ly, 320);
if (idx < NUM_MAX_ZONE && g_cine->_zoneData[idx] == zoneIdx) {
return 1;
}
}
return 0;
}
int16 checkCollisionOS(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneIdx) {
int16 lx = g_cine->_objectTable[objIdx].x + x;
int16 ly = g_cine->_objectTable[objIdx].y + y;
int16 result = 0;
if (ly < 0 || ly > 199) {
return result;
}
for (int16 i = 0; i < numZones; i++, lx++) {
if (lx < 0 || lx > 319) {
continue;
}
int16 idx = getZoneFromPositionRaw(collisionPage, lx, ly, 320);
if (idx < NUM_MAX_ZONE) {
idx = g_cine->_zoneData[idx];
}
if (idx >= 0 && idx < NUM_MAX_ZONE) {
g_cine->_zoneQuery[idx]++;
}
if (idx == zoneIdx) {
result = 1;
}
}
return result;
}
int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneIdx) {
debugC(1, kCineDebugCollision, "checkCollision(objIdx: %d x: %d y:%d numZones:%d zoneIdx: %d)", objIdx, x, y, numZones, zoneIdx);
if (g_cine->getGameType() == Cine::GType_OS) {
return checkCollisionOS(objIdx, x, y, numZones, zoneIdx);
} else {
return checkCollisionFW(objIdx, x, y, numZones, zoneIdx);
}
}
uint16 compareVars(int16 a, int16 b) {
uint16 flag = 0;
if (a == b) {
flag |= kCmpEQ;
} else if (a > b) {
flag |= kCmpGT;
} else if (a < b) {
flag |= kCmpLT;
}
return flag;
}
void executeObjectScripts() {
ScriptList::iterator it = g_cine->_objectScripts.begin();
for (; it != g_cine->_objectScripts.end();) {
debugC(5, kCineDebugScript, "executeObjectScripts() Executing Object Index: %d", (*it)->_index);
if ((*it)->_index < 0 || (*it)->execute() < 0) {
it = g_cine->_objectScripts.erase(it);
} else {
++it;
}
}
}
void executeGlobalScripts() {
ScriptList::iterator it = g_cine->_globalScripts.begin();
for (; it != g_cine->_globalScripts.end();) {
debugC(5, kCineDebugScript, "executeGlobalScripts() Executing Object Index: %d", (*it)->_index);
if ((*it)->_index < 0 || (*it)->execute() < 0) {
it = g_cine->_globalScripts.erase(it);
} else {
++it;
}
}
}
/**
* @todo Remove object scripts with script index of -1 (Not script position, but script index!).
* This would seem to be valid for both Future Wars and Operation Stealth.
*/
void purgeObjectScripts() {
}
/**
* @todo Remove global scripts with script index of -1 (Not script position, but script index!).
* This would seem to be valid for both Future Wars and Operation Stealth.
*/
void purgeGlobalScripts() {
}
////////////////////////////////////
// SCRIPT DECOMPILER
#ifdef DUMP_SCRIPTS
char decompileBuffer[10000][1000];
uint16 decompileBufferPosition = 0;
char bufferDec[256];
char compareString1[256];
char compareString2[256];
const char *getObjPramName(byte paramIdx) {
switch (paramIdx) {
case 1:
return ".X";
case 2:
return ".Y";
case 3:
return ".mask";
case 4:
return ".frame";
case 5:
return ".status";
case 6:
return ".costume";
default:
sprintf(bufferDec, ".param%d", paramIdx);
return bufferDec;
}
}
void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx) {
char lineBuffer[256];
const byte *localScriptPtr = scriptPtr;
uint16 exitScript;
uint32 position = 0;
assert(scriptPtr);
exitScript = 0;
sprintf(decompileBuffer[decompileBufferPosition++], "--------- SCRIPT %d ---------\n", scriptIdx);
do {
uint16 opcode = *(localScriptPtr + position);
position++;
if (position == scriptSize) {
opcode = 0;
}
strcpy(lineBuffer, "");
switch (opcode - 1) {
case -1: {
break;
}
case 0x0: {
byte param1;
byte param2;
int16 param3;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "obj[%d]%s = %d\n", param1, getObjPramName(param2), param3);
break;
}
case 0x1: {
byte param1;
byte param2;
byte param3;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "var[%d]=obj[%d]%s\n", param3, param1, getObjPramName(param2));
break;
}
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6: {
byte param1;
byte param2;
int16 param3;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
if (opcode - 1 == 0x2) {
sprintf(lineBuffer, "obj[%d]%s+=%d\n", param1, getObjPramName(param2), param3);
} else if (opcode - 1 == 0x3) {
sprintf(lineBuffer, "obj[%d]%s-=%d\n", param1, getObjPramName(param2), param3);
} else if (opcode - 1 == 0x4) {
sprintf(lineBuffer, "obj[%d]%s+=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2));
} else if (opcode - 1 == 0x5) {
sprintf(lineBuffer, "obj[%d]%s-=obj[%d]%s\n", param1, getObjPramName(param2), param3, getObjPramName(param2));
} else if (opcode - 1 == 0x6) {
sprintf(compareString1, "obj[%d]%s", param1, getObjPramName(param2));
sprintf(compareString2, "%d", param3);
}
break;
}
case 0x7:
case 0x8: {
byte param1;
int16 param2;
int16 param3;
int16 param4;
int16 param5;
param1 = *(localScriptPtr + position);
position++;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param4 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param5 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
if (opcode - 1 == 0x7) {
sprintf(lineBuffer, "setupObject(Idx:%d,X:%d,Y:%d,mask:%d,frame:%d)\n", param1, param2, param3, param4, param5);
} else if (opcode - 1 == 0x8) {
sprintf(lineBuffer, "checkCollision(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
}
break;
}
case 0x9: {
byte param1;
int16 param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
if (param2) {
byte param3;
param3 = *(localScriptPtr + position);
position++;
if (param2 == 1) {
sprintf(lineBuffer, "var[%d]=var[%d]\n", param1, param3);
} else if (param2 == 2) {
sprintf(lineBuffer, "var[%d]=globalVar[%d]\n", param1, param3);
} else if (param2 == 3) {
sprintf(lineBuffer, "var[%d]=mouse.X\n", param1);
} else if (param2 == 4) {
sprintf(lineBuffer, "var[%d]=mouse.Y\n", param1);
} else if (param2 == 5) {
sprintf(lineBuffer, "var[%d]=rand() mod %d\n", param1, param3);
} else if (param2 == 8) {
sprintf(lineBuffer, "var[%d]=file[%d].packedSize\n", param1, param3);
} else if (param2 == 9) {
sprintf(lineBuffer, "var[%d]=file[%d].unpackedSize\n", param1, param3);
} else {
error("decompileScript: 0x09: param2 = %d", param2);
}
} else {
int16 param3;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "var[%d]=%d\n", param1, param3);
}
break;
}
case 0xA:
case 0xB:
case 0xC:
case 0xD: {
byte param1;
byte param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
if (param2) {
byte param3;
param3 = *(localScriptPtr + position);
position++;
if (opcode - 1 == 0xA) {
sprintf(lineBuffer, "var[%d]+=var[%d]\n", param1, param3);
} else if (opcode - 1 == 0xB) {
sprintf(lineBuffer, "var[%d]-=var[%d]\n", param1, param3);
} else if (opcode - 1 == 0xC) {
sprintf(lineBuffer, "var[%d]*=var[%d]\n", param1, param3);
} else if (opcode - 1 == 0xD) {
sprintf(lineBuffer, "var[%d]/=var[%d]\n", param1, param3);
}
} else {
int16 param3;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
if (opcode - 1 == 0xA) {
sprintf(lineBuffer, "var[%d]+=%d\n", param1, param3);
} else if (opcode - 1 == 0xB) {
sprintf(lineBuffer, "var[%d]-=%d\n", param1, param3);
} else if (opcode - 1 == 0xC) {
sprintf(lineBuffer, "var[%d]*=%d\n", param1, param3);
} else if (opcode - 1 == 0xD) {
sprintf(lineBuffer, "var[%d]/=%d\n", param1, param3);
}
}
break;
}
case 0xE: {
byte param1;
byte param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
if (param2) {
byte param3;
param3 = *(localScriptPtr + position);
position++;
if (param2 == 1) {
sprintf(compareString1, "var[%d]", param1);
sprintf(compareString2, "var[%d]", param3);
} else if (param2 == 2) {
sprintf(compareString1, "var[%d]", param1);
sprintf(compareString2, "globalVar[%d]", param3);
} else {
error("decompileScript: 0x0E: param2 = %d", param2);
}
} else {
int16 param3;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(compareString1, "var[%d]", param1);
sprintf(compareString2, "%d", param3);
}
break;
}
case 0xF: {
byte param1;
byte param2;
byte param3;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "obj[%d]%s=var[%d]\n", param1, getObjPramName(param2), param3);
break;
}
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19: {
byte param;
param = *(localScriptPtr + position);
position++;
if (opcode - 1 == 0x13) {
sprintf(lineBuffer, "loadMask0(%d)\n", param);
} else if (opcode - 1 == 0x14) {
sprintf(lineBuffer, "unloadMask0(%d)\n", param);
} else if (opcode - 1 == 0x15) {
sprintf(lineBuffer, "OP_15(%d)\n", param);
} else if (opcode - 1 == 0x16) {
sprintf(lineBuffer, "loadMask1(%d)\n", param);
} else if (opcode - 1 == 0x17) {
sprintf(lineBuffer, "unloadMask0(%d)\n", param);
} else if (opcode - 1 == 0x18) {
sprintf(lineBuffer, "loadMask4(%d)\n", param);
} else if (opcode - 1 == 0x19) {
sprintf(lineBuffer, "unloadMask4(%d)\n", param);
}
break;
}
case 0x1A: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_1A(%d)\n", param);
break;
}
case 0x1B: {
sprintf(lineBuffer, "bgIncrustList.clear()\n");
break;
}
case 0x1D: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "label(%d)\n", param);
break;
}
case 0x1E: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "goto(%d)\n", param);
break;
}
// If cases
case 0x1F:
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24: {
byte param;
param = *(localScriptPtr + position);
position++;
if (opcode - 1 == 0x1F) {
sprintf(lineBuffer, "if(%s>%s) goto(%d)\n", compareString1, compareString2, param);
} else if (opcode - 1 == 0x20) {
sprintf(lineBuffer, "if(%s>=%s) goto(%d)\n", compareString1, compareString2, param);
} else if (opcode - 1 == 0x21) {
sprintf(lineBuffer, "if(%s<%s) goto(%d)\n", compareString1, compareString2, param);
} else if (opcode - 1 == 0x22) {
sprintf(lineBuffer, "if(%s<=%s) goto(%d)\n", compareString1, compareString2, param);
} else if (opcode - 1 == 0x23) {
sprintf(lineBuffer, "if(%s==%s) goto(%d)\n", compareString1, compareString2, param);
} else if (opcode - 1 == 0x24) {
sprintf(lineBuffer, "if(%s!=%s) goto(%d)\n", compareString1, compareString2, param);
}
break;
}
case 0x25: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "removeLabel(%d)\n", param);
break;
}
case 0x26: {
byte param1;
byte param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "loop(--var[%d]) -> label(%d)\n", param1, param2);
break;
}
case 0x31:
case 0x32: {
byte param;
param = *(localScriptPtr + position);
position++;
if (opcode - 1 == 0x31) {
sprintf(lineBuffer, "startGlobalScript(%d)\n", param);
} else if (opcode - 1 == 0x32) {
sprintf(lineBuffer, "endGlobalScript(%d)\n", param);
}
break;
}
case 0x3B:
case 0x3C:
case 0x3D:
case OP_loadPart: {
if (opcode - 1 == 0x3B) {
sprintf(lineBuffer, "loadResource(%s)\n", localScriptPtr + position);
} else if (opcode - 1 == 0x3C) {
sprintf(lineBuffer, "loadBg(%s)\n", localScriptPtr + position);
} else if (opcode - 1 == 0x3D) {
sprintf(lineBuffer, "loadCt(%s)\n", localScriptPtr + position);
} else if (opcode - 1 == OP_loadPart) {
sprintf(lineBuffer, "loadPart(%s)\n", localScriptPtr + position);
}
position += strlen((const char *)localScriptPtr + position) + 1;
break;
}
case 0x40: {
sprintf(lineBuffer, "closePart()\n");
break;
}
case OP_loadNewPrcName: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "loadPrc(%d,%s)\n", param, localScriptPtr + position);
position += strlen((const char *)localScriptPtr + position) + 1;
break;
}
case OP_requestCheckPendingDataLoad: { // nop
sprintf(lineBuffer, "requestCheckPendingDataLoad()\n");
break;
}
case 0x45: {
sprintf(lineBuffer, "blitAndFade()\n");
break;
}
case 0x46: {
sprintf(lineBuffer, "fadeToBlack()\n");
break;
}
case 0x47: {
byte param1;
byte param2;
int16 param3;
int16 param4;
int16 param5;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param4 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param5 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "transformPaletteRange(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
break;
}
case 0x49: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "setDefaultMenuBgColor(%d)\n", param);
break;
}
case 0x4A: {
byte param1, param2, param3;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "palRotate(%d,%d,%d)\n", param1, param2, param3);
break;
}
case 0x4F: {
sprintf(lineBuffer, "break()\n");
exitScript = 1;
break;
}
case 0x50: {
sprintf(lineBuffer, "endScript()\n\n");
break;
}
case 0x51: {
byte param1;
int16 param2;
int16 param3;
int16 param4;
int16 param5;
param1 = *(localScriptPtr + position);
position++;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param4 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param5 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "message(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
break;
}
case 0x52:
case 0x53: {
byte param1;
byte param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
if (param2) {
byte param3;
param3 = *(localScriptPtr + position);
position++;
if (param2 == 1) {
if (opcode - 1 == 0x52) {
sprintf(lineBuffer, "globalVar[%d] = var[%d]\n", param1, param3);
} else if (opcode - 1 == 0x53) {
sprintf(compareString1, "globalVar[%d]", param1);
sprintf(compareString2, "var[%d]", param3);
}
} else if (param2 == 2) {
if (opcode - 1 == 0x52) {
sprintf(lineBuffer, "globalVar[%d] = globalVar[%d]\n", param1, param3);
} else if (opcode - 1 == 0x53) {
sprintf(compareString1, "globalVar[%d]", param1);
sprintf(compareString2, "globalVar[%d]", param3);
}
} else {
if (opcode - 1 == 0x52) {
error("decompileScript: 0x52: param2 = %d", param2);
} else if (opcode - 1 == 0x53) {
error("decompileScript: 0x53: param2 = %d", param2);
}
}
} else {
int16 param3;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
if (opcode - 1 == 0x52) {
sprintf(lineBuffer, "globalVar[%d] = %d\n", param1, param3);
} else if (opcode - 1 == 0x53) {
sprintf(compareString1, "globalVar[%d]", param1);
sprintf(compareString2, "%d", param3);
}
}
break;
}
case 0x59: {
sprintf(lineBuffer, "comment: %s\n", localScriptPtr + position);
position += strlen((const char *)localScriptPtr + position);
break;
}
case 0x5A: {
byte param1;
byte param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "freePartRang(%d,%d)\n", param1, param2);
break;
}
case 0x5B: {
sprintf(lineBuffer, "unloadAllMasks()\n");
break;
}
case 0x65: {
sprintf(lineBuffer, "setupTableUnk1()\n");
break;
}
case 0x66: {
byte param1;
int16 param2;
param1 = *(localScriptPtr + position);
position++;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "tableUnk1[%d] = %d\n", param1, param2);
break;
}
case 0x68: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "setPlayerCommandPosY(%d)\n", param);
break;
}
case 0x69: {
sprintf(lineBuffer, "allowPlayerInput()\n");
break;
}
case 0x6A: {
sprintf(lineBuffer, "disallowPlayerInput()\n");
break;
}
case 0x6B: {
byte newDisk;
newDisk = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "changeDataDisk(%d)\n", newDisk);
break;
}
case 0x6D: {
sprintf(lineBuffer, "loadDat(%s)\n", localScriptPtr + position);
position += strlen((const char *)localScriptPtr + position) + 1;
break;
}
case 0x6E: { // nop
sprintf(lineBuffer, "updateDat()\n");
break;
}
case 0x6F: {
sprintf(lineBuffer, "OP_6F() -> dat related\n");
break;
}
case 0x70: {
sprintf(lineBuffer, "stopSample()\n");
break;
}
case 0x79: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "disableSystemMenu(%d)\n", param);
break;
}
case 0x77:
case 0x78: {
byte param1;
byte param2;
int16 param3;
byte param4;
int16 param5;
int16 param6;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param4 = *(localScriptPtr + position);
position++;
param5 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param6 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
if (opcode - 1 == 0x77) {
sprintf(lineBuffer, "playSample(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6);
} else if (opcode - 1 == 0x78) {
sprintf(lineBuffer, "playSampleSwapped(%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6);
}
break;
}
case 0x7A: {
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_7A(%d)\n", param);
break;
}
case 0x7B: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_7B(%d)\n", param);
break;
}
case 0x7F: { // OS only
byte param1;
byte param2;
byte param3;
byte param4;
int16 param5;
int16 param6;
int16 param7;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = *(localScriptPtr + position);
position++;
param4 = *(localScriptPtr + position);
position++;
param5 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param6 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param7 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "OP_7F(%d,%d,%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5, param6, param7);
break;
}
case 0x80: { // OS only
byte param1;
byte param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_80(%d,%d)\n", param1, param2);
break;
}
case 0x82: { // OS only
byte param1;
byte param2;
uint16 param3;
uint16 param4;
byte param5;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param4 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param5 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_82(%d,%d,%d,%d,%d)\n", param1, param2, param3, param4, param5);
break;
}
case 0x83: { // OS only
byte param1;
byte param2;
param1 = *(localScriptPtr + position);
position++;
param2 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_83(%d,%d)\n", param1, param2);
break;
}
case 0x89: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "if(%s!=%s) goto next label(%d)\n", compareString1, compareString2, param);
break;
}
case 0x8B: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_8B(%d)\n", param);
break;
}
case 0x8C: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_8C(%d)\n", param);
break;
}
case 0x8D: { // OS only
int16 param1;
int16 param2;
int16 param3;
int16 param4;
int16 param5;
int16 param6;
int16 param7;
int16 param8;
param1 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param3 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param4 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param5 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param6 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param7 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param8 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(compareString1, "obj[%d]", param1);
sprintf(compareString2, "{%d,%d,%d,%d,%d,%d,%d}", param2, param3, param4, param5, param6, param7, param8);
break;
}
case 0x8E: { // OS only
byte param1;
param1 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "ADDBG(%d,%s)\n", param1, localScriptPtr + position);
position += strlen((const char *)localScriptPtr + position);
break;
}
case 0x8F: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_8F(%d)\n", param);
break;
}
case 0x90: { // OS only
byte param1;
param1 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "loadABS(%d,%s)\n", param1, localScriptPtr + position);
position += strlen((const char *)localScriptPtr + position);
break;
}
case 0x91: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_91(%d)\n", param);
break;
}
case 0x9D: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_9D(%d) -> flip img idx\n", param);
break;
}
case 0x9E: { // OS only
byte param;
param = *(localScriptPtr + position);
position++;
if (param) {
byte param2;
param2 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2);
} else {
int16 param2;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "OP_9E(%d,%d)\n", param, param2);
}
break;
}
case 0xA0: { // OS only
uint16 param1;
uint16 param2;
param1 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "OP_A0(%d,%d)\n", param1, param2);
break;
}
case 0xA1: { // OS only
uint16 param1;
uint16 param2;
param1 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "OP_A1(%d,%d)\n", param1, param2);
break;
}
case 0xA2: { // OS only
uint16 param1;
uint16 param2;
param1 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
param2 = READ_BE_UINT16(localScriptPtr + position);
position += 2;
sprintf(lineBuffer, "OP_A2(%d,%d)\n", param1, param2);
break;
}
case 0x9A: { // OS only
byte param1;
param1 = *(localScriptPtr + position);
position++;
sprintf(lineBuffer, "o2_wasZoneChecked(%d)\n", param1);
break;
}
default: {
sprintf(lineBuffer, "Unsupported opcode %X in decompileScript\n\n", opcode - 1);
position = scriptSize;
break;
}
}
//printf(lineBuffer);
strcpy(decompileBuffer[decompileBufferPosition++], lineBuffer);
exitScript = 0;
if (position >= scriptSize) {
exitScript = 1;
}
} while (!exitScript);
}
void dumpScript(char *dumpName) {
Common::DumpFile fHandle;
uint16 i;
fHandle.open(dumpName);
for (i = 0; i < decompileBufferPosition; i++) {
fHandle.writeString(Common::String(decompileBuffer[i]));
}
fHandle.close();
decompileBufferPosition = 0;
}
#endif
} // End of namespace Cine