scummvm/engines/m4/script.h
2011-05-12 01:16:22 +02:00

464 lines
11 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.
*
*/
#ifndef M4_SCRIPT_H
#define M4_SCRIPT_H
#include "common/file.h"
#include "common/stream.h"
#include "common/hashmap.h"
#include "common/str.h"
#include "common/stack.h"
#include "m4/woodscript.h"
namespace M4 {
const unsigned long kScriptFileMagic = 0x5845344D;
const unsigned long kScriptFileVersion = 1;
enum ScriptValueType {
kInteger,
kConstString,
kLogicVar,
kLogicVarRef,
kGameVar,
kKernelVar,
kDataRef,
kRegister,
kStackVar
};
enum ScriptDataType {
kStreamBreakSeries,
kStreamPlaySeries,
kSaidArray,
kParserArray,
kSpeechArray,
kCreditsArray,
kInvObj,
kMineRoom,
kButtonItem
};
class ScriptInterpreter;
class StringTable {
public:
StringTable();
~StringTable();
void load(Common::File *fd);
int size() { return _strings.size(); }
const char *operator[](uint32 index) const {
assert(index < _strings.size() );
return _strings[index];
}
protected:
Common::Array<const char*> _strings;
char *_stringsData;
};
struct ScriptValue {
ScriptValueType type;
union {
int value;
};
ScriptValue() : type(kInteger), value(0) {}
ScriptValue(ScriptValueType itype, int ivalue) : type(itype), value(ivalue) {}
ScriptValue(const int intValue) : type(kInteger), value(intValue) {}
ScriptValue& operator=(const int intValue) {
type = kInteger;
value = intValue;
return *this;
}
};
class ScriptDataItem {
public:
ScriptDataItem() : _inter(NULL) {}
ScriptDataItem(ScriptInterpreter *inter) : _inter(inter) {}
virtual ~ScriptDataItem() {}
virtual void load(Common::File *fd) = 0;
static int type() { return -1; }
protected:
ScriptInterpreter *_inter;
};
class ScriptDataCache {
public:
ScriptDataCache(ScriptInterpreter *inter) : _inter(inter) {
}
~ScriptDataCache() {
clear();
}
// WORKAROUND: The old prototype for this function was:
// template<class T> T *load(Common::File *fd, uint32 ofs);
// that caused a parser error in g++ 3.3.6 used by our
// "motoezx" target of our buildbot. The actual parser
// error happended, when calling the function like this:
// "T *result = _dataCache->load<T>(_scriptFile, _data[value.value]->offset);"
// in ScriptInterpreter::toData. To work around this
// we moved the return value as parameter instead.
template<class T>
void load(Common::File *fd, uint32 ofs, T *&item) {
if (_cache.contains(ofs)) {
item = (T*)(_cache[ofs]);
} else {
item = new T(_inter);
fd->seek(ofs + 4); // "+4" skips the data size
item->load(fd);
_cache[ofs] = item;
}
}
void clear() {
// TODO: Free all cached items
}
protected:
typedef Common::HashMap<uint32, ScriptDataItem*> CacheMap;
CacheMap _cache;
ScriptInterpreter *_inter;
};
struct SeriesStreamBreakItem {
int frameNum;
const char *digiName;
int digiChannel;
int digiVolume;
int trigger;
int flags;
ScriptValue variable;
int value;
};
class SeriesStreamBreakList : public ScriptDataItem {
public:
SeriesStreamBreakList(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
~SeriesStreamBreakList();
void load(Common::File *fd);
int size() const { return _items.size(); }
SeriesStreamBreakItem *operator[](int index) const { return _items[index]; }
static int type() { return 0; }
protected:
Common::Array<SeriesStreamBreakItem*> _items;
};
struct SaidArrayItem {
const char *itemName;
const char *digiNameLook;
const char *digiNameTake;
const char *digiNameGear;
};
class SaidArray : public ScriptDataItem {
public:
SaidArray(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
~SaidArray();
void load(Common::File *fd);
int size() const { return _items.size(); }
SaidArrayItem *operator[](int index) const { return _items[index]; }
static int type() { return 2; }
protected:
Common::Array<SaidArrayItem*> _items;
};
struct ParserArrayItem {
const char *w0;
const char *w1;
int trigger;
ScriptValue testVariable;
int testValue;
ScriptValue variable;
int value;
};
class ParserArray : public ScriptDataItem {
public:
ParserArray(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
~ParserArray();
void load(Common::File *fd);
int size() const { return _items.size(); }
ParserArrayItem *operator[](int index) const { return _items[index]; }
static int type() { return 3; }
protected:
Common::Array<ParserArrayItem*> _items;
};
class ScriptFunction {
public:
ScriptFunction(ScriptInterpreter *inter);
~ScriptFunction();
void load(Common::File *fd);
void jumpAbsolute(uint32 ofs);
void jumpRelative(int32 ofs);
byte readByte();
uint32 readUint32();
protected:
ScriptInterpreter *_inter;
Common::SeekableReadStream *_code;
};
struct ScriptFunctionEntry {
uint32 offset;
ScriptFunction *func;
ScriptFunctionEntry(uint32 funcOffset) : offset(funcOffset), func(NULL) {
}
};
struct ScriptDataEntry {
uint32 offset;
ScriptDataType type;
ScriptDataEntry(uint32 dataOffset, ScriptDataType dataType) : offset(dataOffset), type(dataType) {
}
};
enum ScriptKernelVariable {
kGameLanguage,
kGameVersion,
kGameCurrentRoom,
kGameNewRoom,
kGamePreviousRoom,
kGameNewSection,
kKernelTrigger,
kKernelTriggerMode,
kKernelFirstFade,
kKernelSuppressFadeUp,
kKernelContinueHandlingTrigger,
kKernelUseDebugMonitor,
kPlayerPosX,
kPlayerPosY,
kPlayerFacing,
kPlayerScale,
kPlayerDepth,
kPlayerWalkX,
kPlayerWalkY,
kPlayerReadyToWalk,
kPlayerNeedToWalk,
kPlayerCommandReady,
kPlayerWalkerInThisScene,
kPlayerVerb,
kWalkerInitialized,
kCallDaemonEveryLoop,
kConvCurrentTalker,
kConvCurrentNode,
kConvCurrentEntry,
kConvSoundToPlay,
kInterfaceVisible
};
class ScriptInterpreter {
public:
ScriptInterpreter(MadsM4Engine *vm);
~ScriptInterpreter();
/* Opens a M4 program file */
void open(const char *filename);
void close();
/* Loads a function via the index. Creates the function object if it's not already loaded. */
ScriptFunction *loadFunction(uint32 index);
/* Loads a function via the exported name. */
ScriptFunction *loadFunction(const Common::String &name);
/* Unload all loaded functions.
This should be called before entering a new room to free unused functions. */
void unloadFunctions();
//TODO void unloadData();
/* Executes a function. */
int runFunction(ScriptFunction *scriptFunction);
void push(const ScriptValue &value);
void pop(ScriptValue &value);
void dumpStack();
void dumpRegisters();
void dumpGlobalVars();
int toInteger(const ScriptValue &value);
const char *toString(const ScriptValue &value);
// Is this ok?
template<class T>
const T& toData(const ScriptValue &value) {
debugCN(kDebugScript, "ScriptInterpreter::toData() index = %d; type = %d; max = %d\n", value.value, _data[value.value]->type, _data.size());
assert((uint32)value.value < _data.size());
T *result = 0;
_dataCache->load(_scriptFile, _data[value.value]->offset, result);
return *result;
}
const char *getGlobalString(int index) const {
return _constStrings[index];
}
const char *loadGlobalString(Common::File *fd);
void test();
protected:
MadsM4Engine *_vm;
typedef Common::HashMap<Common::String, uint32, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FunctionNameMap;
Common::File *_scriptFile;
/* An array of offset/ScriptFunction* pairs for each script function */
Common::Array<ScriptFunctionEntry*> _functions;
// DEBUG only
Common::Array<Common::String> _scriptFunctionNames;
Common::Array<ScriptDataEntry*> _data;
/* Maps function name -> index of function in _functions array */
FunctionNameMap _functionNames;
StringTable _constStrings;
/* The currently running function */
ScriptFunction *_runningFunction;
int _localStackPtr;
ScriptValue _registers[8];
ScriptValue _stack[512];
int _stackPtr;
int _globalVarCount;
ScriptValue _globalVars[1024];
int _logicGlobals[512];
int _cmpFlags;
ScriptDataCache *_dataCache;
int _lineNum;
typedef int (ScriptInterpreter::*KernelFunction)();
struct KernelFunctionEntry {
KernelFunction proc;
const char *desc;
};
const KernelFunctionEntry *_kernelFunctions;
uint16 _kernelFunctionsMax;
struct KernelVariableEntry {
ScriptKernelVariable var;
const char *desc;
};
const KernelVariableEntry *_kernelVars;
int16 _kernelVarsMax;
void initScriptKernel();
void loadValue(ScriptValue &value);
void writeValue(ScriptValue &value);
void copyValue(ScriptValue &destValue, ScriptValue &sourceValue);
void derefValue(ScriptValue &value);
void callKernelFunction(uint32 index);
ScriptValue getArg(uint32 index);
void dumpArgs(uint32 count);
void callFunction(uint32 index);
bool execOpcode(byte opcode);
// Kernel functions
int o1_handleStreamBreak();
int o1_handlePlayBreak();
int o1_dispatchTriggerOnSoundState();
int o1_getRangedRandomValue();
int o1_getTicks();
int o1_preloadSound();
int o1_unloadSound();
int o1_stopSound();
int o1_playSound();
int o1_playLoopingSound();
int o1_setSoundVolume();
int o1_getSoundStatus();
int o1_getSoundDuration();
int o1_loadSeries();
int o1_unloadSeries();
int o1_showSeries();
int o1_playSeries();
int o1_setSeriesFrameRate();
int o1_playBreakSeries();
int o1_preloadBreakSeries();
int o1_unloadBreakSeries();
int o1_startBreakSeries();
int o1_dispatchTrigger();
int o1_terminateMachine();
int o1_sendWoodScriptMessage();
int o1_runConversation();
int o1_loadConversation();
int o1_exportConversationValue();
int o1_exportConversationPointer();
int o1_fadeInit();
int o1_fadeSetStart();
int o1_fadeToBlack();
int o1_initPaletteCycle();
int o1_stopPaletteCycle();
int o1_setHotspot();
int o1_hideWalker();
int o1_showWalker();
int o1_setWalkerLocation();
int o1_setWalkerFacing();
int o1_walk();
int o1_overrideCrunchTime();
int o1_addBlockingRect();
int o1_triggerTimerProc();
int o1_setPlayerCommandsAllowed();
int o1_getPlayerCommandsAllowed();
int o1_updatePlayerInfo();
int o1_hasPlayerSaid();
int o1_hasPlayerSaidAny();
int o1_playerHotspotWalkOverride();
int o1_setPlayerFacingAngle();
int o1_disablePlayerFadeToBlack();
int o1_enablePlayer();
int o1_disablePlayer();
int o1_freshenSentence();
int o1_playerHasItem();
int o1_playerGiveItem();
int o1_moveObject();
int o1_setStopSoundsBetweenRooms();
int o1_backupPalette();
int o1_unloadWilburWalker();
int o1_globalTriggerProc();
int o1_wilburSpeech();
int o1_wilburSaid();
int o1_wilburParse();
int o1_wilburTalk();
int o1_wilburFinishedTalking();
//int ();
// Kernel vars
void getKernelVar(int index, ScriptValue &value);
void setKernelVar(int index, const ScriptValue &value);
};
} // End of namespace M4
#endif