/* 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 3 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, see . * */ #ifndef DIRECTOR_LINGO_LINGO_H #define DIRECTOR_LINGO_LINGO_H namespace Audio { class AudioStream; } namespace Common { class SeekableReadStreamEndian; } namespace Director { struct ChunkReference; struct MenuReference; struct PictureReference; struct TheEntity; struct TheEntityField; struct LingoArchive; struct LingoV4Bytecode; struct LingoV4TheEntity; struct Node; struct Picture; class AbstractObject; class Cast; class ScriptContext; class DirectorEngine; class Frame; class LingoCompiler; struct Breakpoint; typedef void (*inst)(void); #define STOP (inst)0 #define ENTITY_INDEX(t,id) ((t) * 100000 + (id)) int calcStringAlignment(const char *s); int calcCodeAlignment(int l); typedef Common::Array ScriptData; struct FuncDesc { Common::String name; const char *proto; FuncDesc(Common::String n, const char *p) { name = n; proto = p; } }; typedef Common::HashMap FuncHash; typedef Common::HashMap MethodHash; struct BuiltinProto { const char *name; void (*func)(int); int minArgs; // -1 -- arglist int maxArgs; int version; SymbolType type; }; struct Symbol { /* symbol table entry */ Common::String *name; SymbolType type; union { ScriptData *defn; /* HANDLER */ void (*func)(); /* OPCODE */ void (*bltin)(int); /* BUILTIN */ Common::String *s; /* STRING */ } u; int *refCount; int nargs; /* number of arguments */ int maxArgs; /* maximal number of arguments, for builtins */ int targetType; /* valid target objects, for method builtins */ Common::Array *argNames; Common::Array *varNames; ScriptContext *ctx; /* optional script context to execute with */ AbstractObject *target; /* optional method target */ bool anonymous; Symbol(); Symbol(const Symbol &s); Symbol& operator=(const Symbol &s); bool operator==(Symbol &s) const; void reset(); ~Symbol(); }; struct PArray { bool _sorted; PropertyArray arr; PArray() : _sorted(false) {} PArray(int size) : _sorted(false), arr(size) {} }; struct FArray { bool _sorted; DatumArray arr; FArray() : _sorted(false) {} FArray(int size) : _sorted(false), arr(size) {} }; struct Datum { /* interpreter stack type */ DatumType type; union { int i; /* INT, ARGC, ARGCNORET */ double f; /* FLOAT */ Common::String *s; /* STRING, VARREF, OBJECT */ FArray *farr; /* ARRAY, POINT, RECT */ PArray *parr; /* PARRAY */ AbstractObject *obj; /* OBJECT */ ChunkReference *cref; /* CHUNKREF */ CastMemberID *cast; /* CASTREF, FIELDREF */ MenuReference *menu; /* MENUREF */ PictureReference *picture; /* PICTUREREF */ } u; int *refCount; bool ignoreGlobal; // True if this Datum should be ignored by showGlobals and clearGlobals Datum(); Datum(const Datum &d); Datum& operator=(const Datum &d); Datum(int val); Datum(double val); Datum(const Common::String &val); Datum(AbstractObject *val); Datum(CastMember *val); Datum(const CastMemberID &val); Datum(const Common::Point &point); Datum(const Common::Rect &rect); void reset(); ~Datum() { reset(); } Datum eval() const; double asFloat() const; int asInt() const; Common::String asString(bool printonly = false) const; CastMemberID asMemberID(CastType castType = kCastTypeAny, int castLib = 0) const; Common::Point asPoint() const; Datum clone() const; bool isRef() const; bool isVarRef() const; bool isCastRef() const; bool isArray() const; bool isNumeric() const; bool isVoid() const { return type == VOID; } const char *type2str(bool ilk = false) const; int equalTo(const Datum &d, bool ignoreCase = false) const; uint32 compareTo(const Datum &d) const; bool operator==(const Datum &d) const; bool operator>(const Datum &d) const; bool operator<(const Datum &d) const; bool operator>=(const Datum &d) const; bool operator<=(const Datum &d) const; }; struct ChunkReference { Datum source; ChunkType type; int startChunk; int endChunk; int start; int end; ChunkReference(const Datum &src, ChunkType t, int sc, int ec, int s, int e) : source(src), type(t), startChunk(sc), endChunk(ec), start(s), end(e) {} }; struct MenuReference { int menuIdNum; Common::String *menuIdStr; int menuItemIdNum; Common::String *menuItemIdStr; MenuReference(); }; struct PictureReference { Picture *_picture = nullptr; ~PictureReference(); }; struct PCell { Datum p; Datum v; PCell(); PCell(const Datum &prop, const Datum &val); }; struct Builtin { void (*func)(void); int nargs; Builtin(void (*func1)(void), int nargs1) : func(func1), nargs(nargs1) {} }; typedef Common::HashMap ScriptContextHash; typedef Common::HashMap *> FactoryContextHash; typedef Common::Array StackData; typedef Common::HashMap SymbolHash; typedef Common::HashMap DatumHash; typedef Common::HashMap BuiltinHash; typedef Common::HashMap VarTypeHash; typedef void (*XLibOpenerFunc)(ObjectType, const Common::Path &); typedef void (*XLibCloserFunc)(ObjectType); typedef Common::HashMap XLibOpenerFuncHash; typedef Common::HashMap XLibCloserFuncHash; typedef Common::HashMap XLibTypeHash; typedef Common::HashMap OpenXLibsHash; typedef Common::HashMap OpenXLibsStateHash; typedef Common::HashMap TheEntityHash; typedef Common::HashMap TheEntityFieldHash; struct CFrame { /* proc/func call stack frame */ Symbol sp; /* symbol table entry */ int retPC; /* where to resume after return */ ScriptData *retScript; /* which script to resume after return */ ScriptContext *retContext; /* which script context to use after return */ DatumHash *retLocalVars; Datum retMe; /* which me obj to use after return */ uint stackSizeBefore; bool allowRetVal; /* whether to allow a return value */ Datum defaultRetVal; /* default return value */ int paramCount; /* original number of arguments submitted */ Common::Array paramList; /* original argument list */ }; struct LingoEvent { LEvent event; int eventId; EventHandlerSourceType eventHandlerSourceType; ScriptType scriptType; bool passByDefault; uint16 channelId; CastMemberID scriptId; Common::Point mousePos; int behaviorIndex; AbstractObject *scriptInstance; LingoEvent(LEvent e, int ei, ScriptType st, bool pass, CastMemberID si = CastMemberID(), Common::Point mp = Common::Point(-1, -1), int bi = -1) { event = e; eventId = ei; eventHandlerSourceType = kNoneHandler; scriptType = st; passByDefault = pass; channelId = 0; scriptId = si; mousePos = mp; behaviorIndex = bi; scriptInstance = nullptr; } LingoEvent(LEvent e, int ei, EventHandlerSourceType ehst, bool pass, Common::Point mp = Common::Point(-1, -1), uint16 ci = 0, int bi = -1) { event = e; eventId = ei; eventHandlerSourceType = ehst; scriptType = kNoneScript; passByDefault = pass; channelId = ci; scriptId = CastMemberID(); mousePos = mp; behaviorIndex = bi; scriptInstance = nullptr; } }; struct LingoArchive { LingoArchive(Cast *c) : cast(c) {}; ~LingoArchive(); Cast *cast; ScriptContextHash lctxContexts; ScriptContextHash scriptContexts[kMaxScriptType + 1]; FactoryContextHash factoryContexts; Common::Array names; Common::HashMap primaryEventHandlers; SymbolHash functionHandlers; ScriptContext *getScriptContext(ScriptType type, uint16 id); ScriptContext *findScriptContext(uint16 id); Common::String getName(uint16 id); Common::String formatFunctionList(const char *prefix); void addCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr, uint32 preprocFlags = kLPPNone); void patchCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr, uint32 preprocFlags = kLPPNone); void removeCode(ScriptType type, uint16 id); void replaceCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr); void addCodeV4(Common::SeekableReadStreamEndian &stream, uint16 lctxIndex, const Common::String &archName, uint16 version); void addNamesV4(Common::SeekableReadStreamEndian &stream); // lingo-patcher.cpp void patchScriptHandler(ScriptType type, CastMemberID id); }; struct LingoState { // Execution state for a Lingo process, created every time // a top-level handler is called (e.g. on mouseDown). // Can be swapped out when another script gets called with priority. // Call frames are pushed and popped from the callstack with // pushContext and popContext. Common::Array callstack; // call stack uint pc = 0; // current program counter ScriptData *script = nullptr; // current Lingo script ScriptContext *context = nullptr; // current Lingo script context DatumHash *localVars = nullptr; // current local variables Datum me; // current me object StackData stack; ~LingoState(); }; enum LingoExecState { kRunning, kPause, }; class Lingo { public: Lingo(DirectorEngine *vm); ~Lingo(); void resetLingo(); void cleanupLingo(); void resetLingoGo(); int getMenuNum(); int getMenuItemsNum(Datum &d); int getXtrasNum(); int getCastLibsNum(); int getMembersNum(uint16 castLibID); void executeHandler(const Common::String &name, int numargs = 0); void executeScript(ScriptType type, CastMemberID id); Common::String formatStack(); void printStack(const char *s, uint pc); Common::String formatCallStack(uint pc); void printCallStack(uint pc); Common::String formatFrame(); Common::String formatCurrentInstruction(); Common::String decodeInstruction(ScriptData *sd, uint pc, uint *newPC = NULL); Common::String decodeScript(ScriptData *sd); Common::String formatFunctionName(Symbol &sym); Common::String formatFunctionBody(Symbol &sym); void reloadBuiltIns(); void initBuiltIns(); void initBuiltIns(const BuiltinProto protos[]); void cleanupBuiltIns(); void cleanupBuiltIns(const BuiltinProto protos[]); void initFuncs(); void cleanupFuncs(); void initBytecode(); void initMethods(); void cleanupMethods(); void initXLibs(); void cleanupXLibs(); Common::String normalizeXLibName(Common::String name); void openXLib(Common::String name, ObjectType type, const Common::Path &path); void closeXLib(Common::String name); void closeOpenXLibs(); void reloadOpenXLibs(); void runTests(); // lingo-events.cpp private: void initEventHandlerTypes(); bool processEvent(LEvent event, ScriptType st, CastMemberID scriptId, int channelId = -1, AbstractObject *obj = nullptr); public: ScriptType event2script(LEvent ev); Symbol getHandler(const Common::String &name); void processEvents(Common::Queue &queue, bool isInputEvent); public: bool execute(int targetFrame = -1); void switchStateFromWindow(); void freezeState(); void freezePlayState(); void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal, int paramCount, int nargs); void popContext(bool aborting = false); void cleanLocalVars(); void varAssign(const Datum &var, const Datum &value); Datum varFetch(const Datum &var, bool silent = false); Common::U32String evalChunkRef(const Datum &var); Datum findVarV4(int varType, const Datum &id); CastMemberID resolveCastMember(const Datum &memberID, const Datum &castLib, CastType type); CastMemberID toCastMemberID(const Datum &member, const Datum &castLib); void exposeXObject(const char *name, Datum obj); int getAlignedType(const Datum &d1, const Datum &d2, bool equality); Common::String formatAllVars(); void printAllVars(); inst readInst() { return getInst(_state->pc++); } inst getInst(uint pc) { return (*_state->script)[pc]; } int readInt() { return getInt(_state->pc++); } int getInt(uint pc); double readFloat() { double d = getFloat(_state->pc); _state->pc += calcCodeAlignment(sizeof(double)); return d; } double getFloat(uint pc) { return *(double *)(&((*_state->script)[_state->pc])); } char *readString() { char *s = getString(_state->pc); _state->pc += calcStringAlignment(s); return s; } char *getString(uint pc) { return (char *)(&((*_state->script)[_state->pc])); } Datum getVoid(); void pushVoid(); void printArgs(const char *funcname, int nargs, const char *prefix = nullptr); inline void printSTUBWithArglist(const char *funcname, int nargs) { printArgs(funcname, nargs, "STUB: "); } void convertVOIDtoString(int arg, int nargs); void dropStack(int nargs); void drop(uint num); void lingoError(const char *s, ...); void func_mci(const Common::String &name); void func_mciwait(const Common::String &name); void func_beep(int repeats); void func_goto(Datum &frame, Datum &movie, bool commandgo = false ); void func_gotoloop(); void func_gotonext(); void func_gotoprevious(); void func_play(Datum &frame, Datum &movie); void func_playdone(); void func_cursor(Datum cursorDatum); int func_marker(int m); uint16 func_label(Datum &label); // lingo-the.cpp public: void initTheEntities(); void cleanUpTheEntities(); const char *entity2str(int id); const char *field2str(int id); // global kTheEntity Datum _actorList; Common::u32char_type_t _itemDelimiter; bool _exitLock; bool _preLoadEventAbort; // no-op, everything is always preloaded Datum _searchPath; bool _trace; // state of movie's trace function int _traceLoad; // internal Director verbosity level bool _updateMovieEnabled; bool _romanLingo; Datum getTheEntity(int entity, Datum &id, int field); void setTheEntity(int entity, Datum &id, int field, Datum &d); Datum getTheSprite(Datum &id, int field); void setTheSprite(Datum &id, int field, Datum &d); Datum getTheCast(Datum &id, int field); void setTheCast(Datum &id, int field, Datum &d); Datum getTheCastLib(Datum &id, int field); void setTheCastLib(Datum &id, int field, Datum &d); Datum getTheField(Datum &id1, int field); void setTheField(Datum &id1, int field, Datum &d); Datum getTheChunk(Datum &chunk, int field); void setTheChunk(Datum &chunk, int field, Datum &d); void getObjectProp(Datum &obj, Common::String &propName); void setObjectProp(Datum &obj, Common::String &propName, Datum &d); Datum getTheDate(int field); Datum getTheTime(int field); Datum getTheDeskTopRectList(); private: Common::StringArray _entityNames; Common::StringArray _fieldNames; public: LingoCompiler *_compiler; LingoState *_state; int _currentChannelId; bool _freezeState; bool _freezePlay; bool _playDone; bool _abort; bool _expectError; bool _caughtError; TheEntityHash _theEntities; TheEntityFieldHash _theEntityFields; int _objectEntityId; SymbolHash _builtinCmds; SymbolHash _builtinFuncs; SymbolHash _builtinConsts; SymbolHash _builtinListHandlers; SymbolHash _methods; XLibOpenerFuncHash _xlibOpeners; XLibCloserFuncHash _xlibClosers; XLibTypeHash _xlibTypes; OpenXLibsHash _openXLibs; OpenXLibsStateHash _openXLibsState; Common::StringArray _openXtras; Common::Array _openXtraObjects; OpenXLibsStateHash _openXtrasState; Common::String _floatPrecisionFormat; public: void push(Datum d); Datum pop(); Datum peek(uint offset); public: Common::HashMap _eventHandlerTypes; Common::HashMap _eventHandlerTypeIds; Common::HashMap _audioAliases; DatumHash _globalvars; FuncHash _functions; Common::HashMap _lingoV4; Common::HashMap _lingoV4TheEntity; uint _globalCounter; DirectorEngine *_vm; int _floatPrecision; Datum _theResult; // events bool _passEvent; Datum _perFrameHook; Datum _windowList; Symbol _currentInputEvent; struct { LingoExecState _state = kRunning; bool (*_shouldPause)() = nullptr; } _exec; public: void executeImmediateScripts(Frame *frame); void executePerFrameHook(int frame, int subframe, bool stepFrame = true); // lingo-utils.cpp private: Common::HashMap _charNormalizations; void initCharNormalizations(); public: Common::String normalizeString(const Common::String &str); public: void addBreakpoint(Breakpoint &bp); bool delBreakpoint(int id); Breakpoint *getBreakpoint(int id); const Common::Array &getBreakpoints() const { return _breakpoints; } Common::Array &getBreakpoints() { return _breakpoints; } private: int _bpNextId = 1; Common::Array _breakpoints; }; extern Lingo *g_lingo; } // End of namespace Director #endif