mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 14:14:43 +00:00
464 lines
11 KiB
C++
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
|