ScummVM API documentation
lingo.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef DIRECTOR_LINGO_LINGO_H
23 #define DIRECTOR_LINGO_LINGO_H
24 
25 namespace Audio {
26 class AudioStream;
27 }
28 namespace Common {
29 class SeekableReadStreamEndian;
30 }
31 
32 namespace Director {
33 
34 struct ChunkReference;
35 struct MenuReference;
36 struct PictureReference;
37 struct TheEntity;
38 struct TheEntityField;
39 struct LingoArchive;
40 struct LingoV4Bytecode;
41 struct LingoV4TheEntity;
42 struct Node;
43 struct Picture;
44 class AbstractObject;
45 class Cast;
46 class ScriptContext;
47 class DirectorEngine;
48 class Frame;
49 class Window;
50 class LingoCompiler;
51 struct Breakpoint;
52 
53 typedef void (*inst)(void);
54 #define STOP (inst)0
55 #define ENTITY_INDEX(t,id) ((t) * 100000 + (id))
56 
57 int calcStringAlignment(const char *s);
58 int calcCodeAlignment(int l);
59 
60 typedef Common::Array<inst> ScriptData;
61 
62 struct FuncDesc {
63  Common::String name;
64  const char *proto;
65 
66  FuncDesc(Common::String n, const char *p) { name = n; proto = p; }
67 };
68 
70 
72 
73 struct BuiltinProto {
74  const char *name;
75  void (*func)(int);
76  int minArgs; // -1 -- arglist
77  int maxArgs;
78  int version;
79  SymbolType type;
80 };
81 
82 struct Symbol { /* symbol table entry */
83  Common::String *name;
84  SymbolType type;
85  union {
86  ScriptData *defn; /* HANDLER */
87  void (*func)(); /* OPCODE */
88  void (*bltin)(int); /* BUILTIN */
89  Common::String *s; /* STRING */
90  } u;
91 
92  int *refCount;
93 
94  int nargs; /* number of arguments */
95  int maxArgs; /* maximal number of arguments, for builtins */
96  int targetType; /* valid target objects, for method builtins */
97 
100  ScriptContext *ctx; /* optional script context to execute with */
101  AbstractObject *target; /* optional method target */
102  bool anonymous;
103 
104  Symbol();
105  Symbol(const Symbol &s);
106  Symbol& operator=(const Symbol &s);
107  bool operator==(Symbol &s) const;
108  void reset();
109  ~Symbol();
110 };
111 
112 struct PArray {
113  bool _sorted;
114  PropertyArray arr;
115 
116  PArray() : _sorted(false) {}
117 
118  PArray(int size) : _sorted(false), arr(size) {}
119 };
120 
121 struct FArray {
122  bool _sorted;
123  DatumArray arr;
124 
125  FArray() : _sorted(false) {}
126 
127  FArray(int size) : _sorted(false), arr(size) {}
128 };
129 
130 
131 struct Datum { /* interpreter stack type */
132  DatumType type;
133 
134  union {
135  int i; /* INT, ARGC, ARGCNORET */
136  double f; /* FLOAT */
137  Common::String *s; /* STRING, VARREF, OBJECT */
138  FArray *farr; /* ARRAY, POINT, RECT */
139  PArray *parr; /* PARRAY */
140  AbstractObject *obj; /* OBJECT */
141  ChunkReference *cref; /* CHUNKREF */
142  CastMemberID *cast; /* CASTREF, FIELDREF */
143  MenuReference *menu; /* MENUREF */
144  PictureReference *picture; /* PICTUREREF */
145  } u;
146 
147  int *refCount;
148 
149  bool ignoreGlobal; // True if this Datum should be ignored by showGlobals and clearGlobals
150 
151  Datum();
152  Datum(const Datum &d);
153  Datum& operator=(const Datum &d);
154  Datum(int val);
155  Datum(double val);
156  Datum(const Common::String &val);
157  Datum(AbstractObject *val);
158  Datum(CastMember *val);
159  Datum(const CastMemberID &val);
160  Datum(const Common::Point &point);
161  Datum(const Common::Rect &rect);
162  void reset();
163 
164  ~Datum() {
165  reset();
166  }
167 
168  Datum eval() const;
169  double asFloat() const;
170  int asInt() const;
171  Common::String asString(bool printonly = false) const;
172  CastMemberID asMemberID(CastType castType = kCastTypeAny, int castLib = 0) const;
173  Common::Point asPoint() const;
174  Datum clone() const;
175 
176  bool isRef() const;
177  bool isVarRef() const;
178  bool isCastRef() const;
179  bool isArray() const;
180  bool isNumeric() const;
181  bool isVoid() const { return type == VOID; }
182 
183  const char *type2str(bool ilk = false) const;
184 
185  int equalTo(const Datum &d, bool ignoreCase = false) const;
186  uint32 compareTo(const Datum &d) const;
187 
188  bool operator==(const Datum &d) const;
189  bool operator>(const Datum &d) const;
190  bool operator<(const Datum &d) const;
191  bool operator>=(const Datum &d) const;
192  bool operator<=(const Datum &d) const;
193 };
194 
196  Datum source;
197  ChunkType type;
198  int startChunk;
199  int endChunk;
200  int start;
201  int end;
202 
203  ChunkReference(const Datum &src, ChunkType t, int sc, int ec, int s, int e)
204  : source(src), type(t), startChunk(sc), endChunk(ec), start(s), end(e) {}
205 };
206 
208  int menuIdNum;
209  Common::String *menuIdStr;
210  int menuItemIdNum;
211  Common::String *menuItemIdStr;
212 
213  MenuReference();
214 };
215 
217  Picture *_picture = nullptr;
218  ~PictureReference();
219 };
220 
221 struct PCell {
222  Datum p;
223  Datum v;
224 
225  PCell();
226  PCell(const Datum &prop, const Datum &val);
227 };
228 
229 struct Builtin {
230  void (*func)(void);
231  int nargs;
232 
233  Builtin(void (*func1)(void), int nargs1) : func(func1), nargs(nargs1) {}
234 };
235 
243 typedef void (*XLibOpenerFunc)(ObjectType, const Common::Path &);
244 typedef void (*XLibCloserFunc)(ObjectType);
250 
253 
254 struct CFrame { /* proc/func call stack frame */
255  Symbol sp; /* symbol table entry */
256  int retPC; /* where to resume after return */
257  ScriptData *retScript; /* which script to resume after return */
258  ScriptContext *retContext; /* which script context to use after return */
259  DatumHash *retLocalVars;
260  Datum retMe; /* which me obj to use after return */
261  uint stackSizeBefore;
262  bool allowRetVal; /* whether to allow a return value */
263  Datum defaultRetVal; /* default return value */
264  int paramCount; /* original number of arguments submitted */
265  Common::Array<Datum> paramList; /* original argument list */
266  Window *retWindow = nullptr; /* window to restore on return */
267 };
268 
269 struct LingoEvent {
270  LEvent event;
271  int eventId;
272  EventHandlerSourceType eventHandlerSourceType;
273  ScriptType scriptType;
274  bool passByDefault;
275  uint16 channelId;
276  CastMemberID scriptId;
277  Common::Point mousePos;
278  int behaviorIndex;
279  AbstractObject *scriptInstance;
280 
281  LingoEvent(LEvent e, int ei, ScriptType st, bool pass, CastMemberID si = CastMemberID(), Common::Point mp = Common::Point(-1, -1), int bi = -1) {
282  event = e;
283  eventId = ei;
284  eventHandlerSourceType = kNoneHandler;
285  scriptType = st;
286  passByDefault = pass;
287  channelId = 0;
288  scriptId = si;
289  mousePos = mp;
290  behaviorIndex = bi;
291  scriptInstance = nullptr;
292 
293  debugC(7, kDebugEvents, "LingoEvent: event=%s, eventId=%d, scriptType=%d, passByDefault=%d, scriptId=(%d,%d), mousePos=(%d,%d), behaviorIndex=%d",
294  leventType2str(event), eventId, scriptType, passByDefault, scriptId.member, scriptId.castLib, mousePos.x, mousePos.y, behaviorIndex);
295  }
296 
297  LingoEvent(LEvent e, int ei, EventHandlerSourceType ehst, bool pass, Common::Point mp = Common::Point(-1, -1), uint16 ci = 0, int bi = -1) {
298  event = e;
299  eventId = ei;
300  eventHandlerSourceType = ehst;
301  scriptType = kNoneScript;
302  passByDefault = pass;
303  channelId = ci;
304  scriptId = CastMemberID();
305  mousePos = mp;
306  behaviorIndex = bi;
307  scriptInstance = nullptr;
308 
309  debugC(7, kDebugEvents, "LingoEvent: event=%s, eventId=%d, eventHandlerSourceType=%s, passByDefault=%d, channelId=%d, mousePos=(%d,%d), behaviorIndex=%d",
310  leventType2str(event), eventId, eventHandlerSourceType2str(eventHandlerSourceType), passByDefault, channelId, mousePos.x, mousePos.y, behaviorIndex);
311  }
312 };
313 
314 
315 struct LingoArchive {
316  LingoArchive(Cast *c) : cast(c) {};
317  ~LingoArchive();
318 
319  Cast *cast;
320  ScriptContextHash lctxContexts;
321  ScriptContextHash scriptContexts[kMaxScriptType + 1];
322  FactoryContextHash factoryContexts;
324  Common::HashMap<uint32, Common::String> primaryEventHandlers;
325  SymbolHash functionHandlers;
326 
327  ScriptContext *getScriptContext(ScriptType type, uint16 id);
328  ScriptContext *findScriptContext(uint16 id);
329  Common::String getName(uint16 id);
330  Common::String formatFunctionList(const char *prefix);
331 
332  void addCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr, uint32 preprocFlags = kLPPNone);
333  void patchCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr, uint32 preprocFlags = kLPPNone);
334  void removeCode(ScriptType type, uint16 id);
335  void replaceCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr);
336  void addCodeV4(Common::SeekableReadStreamEndian &stream, uint16 lctxIndex, const Common::String &archName, uint16 version);
337  void addNamesV4(Common::SeekableReadStreamEndian &stream);
338 
339  // lingo-patcher.cpp
340  void patchScriptHandler(ScriptType type, CastMemberID id);
341 };
342 
343 struct LingoState {
344  // Execution state for a Lingo process, created every time
345  // a top-level handler is called (e.g. on mouseDown).
346  // Can be swapped out when another script gets called with priority.
347  // Call frames are pushed and popped from the callstack with
348  // pushContext and popContext.
349  Common::Array<CFrame *> callstack; // call stack
350  uint pc = 0; // current program counter
351  ScriptData *script = nullptr; // current Lingo script
352  ScriptContext *context = nullptr; // current Lingo script context
353  DatumHash *localVars = nullptr; // current local variables
354  Datum me; // current me object
355  StackData stack;
356  int currentChannelId = 0;
357 
358  ~LingoState();
359 };
360 
361 enum LingoExecState {
362  kRunning,
363  kPause,
364 };
365 
366 class Lingo {
367 
368 public:
369  Lingo(DirectorEngine *vm);
370  ~Lingo();
371 
372  void resetLingo();
373  void cleanupLingo();
374  void resetLingoGo();
375 
376  int getMenuNum();
377  int getMenuItemsNum(Datum &d);
378  int getXtrasNum();
379  int getCastLibsNum();
380  int getMembersNum(uint16 castLibID);
381 
382  void executeHandler(const Common::String &name, int numargs = 0);
383  void executeScript(ScriptType type, CastMemberID id);
384  Common::String formatStack();
385  void printStack(const char *s, uint pc);
386  Common::String formatCallStack(uint pc);
387  void printCallStack(uint pc);
388  Common::String formatFrame();
389  Common::String formatCurrentInstruction();
390  Common::String decodeInstruction(ScriptData *sd, uint pc, uint *newPC = NULL);
391  Common::String decodeScript(ScriptData *sd);
392  Common::String formatFunctionName(Symbol &sym);
393  Common::String formatFunctionBody(Symbol &sym);
394 
395  void reloadBuiltIns();
396  void initBuiltIns();
397  void initBuiltIns(const BuiltinProto protos[]);
398  void cleanupBuiltIns();
399  void cleanupBuiltIns(const BuiltinProto protos[]);
400  void initFuncs();
401  void cleanupFuncs();
402  void initBytecode();
403  void initMethods();
404  void cleanupMethods();
405  void initXLibs();
406  void cleanupXLibs();
407 
408  Common::String normalizeXLibName(Common::String name);
409  void openXLib(Common::String name, ObjectType type, const Common::Path &path);
410  void closeXLib(Common::String name);
411  void closeOpenXLibs();
412  void reloadOpenXLibs();
413 
414  void runTests();
415 
416  // lingo-events.cpp
417 private:
418  void initEventHandlerTypes();
419  bool processEvent(LEvent event, ScriptType st, CastMemberID scriptId, int channelId = -1, AbstractObject *obj = nullptr);
420 
421 public:
422  ScriptType event2script(LEvent ev);
423  Symbol getHandler(const Common::String &name);
424 
425  void processEvents(Common::Queue<LingoEvent> &queue, bool isInputEvent);
426 
427 public:
428  bool execute(int targetFrame = -1);
429  void switchStateFromWindow();
430  void freezeState();
431  void freezePlayState();
432  void requeuePlayState();
433  void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal, int paramCount, int nargs);
434  void popContext(bool aborting = false);
435  void cleanLocalVars();
436  void varAssign(const Datum &var, const Datum &value);
437  Datum varFetch(const Datum &var, bool silent = false);
438  Common::U32String evalChunkRef(const Datum &var);
439  Datum findVarV4(int varType, const Datum &id);
440  CastMemberID resolveCastMember(const Datum &memberID, const Datum &castLib, CastType type);
441  CastMemberID toCastMemberID(const Datum &member, const Datum &castLib);
442  void exposeXObject(const char *name, Datum obj);
443 
444  int getAlignedType(const Datum &d1, const Datum &d2, bool equality);
445 
446  Common::String formatAllVars();
447  void printAllVars();
448 
449  inst readInst() { return getInst(_state->pc++); }
450  inst getInst(uint pc) { return (*_state->script)[pc]; }
451  int readInt() { return getInt(_state->pc++); }
452  int getInt(uint pc);
453  double readFloat() { double d = getFloat(_state->pc); _state->pc += calcCodeAlignment(sizeof(double)); return d; }
454  double getFloat(uint pc) { return *(double *)(&((*_state->script)[_state->pc])); }
455  char *readString() { char *s = getString(_state->pc); _state->pc += calcStringAlignment(s); return s; }
456  char *getString(uint pc) { return (char *)(&((*_state->script)[_state->pc])); }
457 
458  Datum getVoid();
459  void pushVoid();
460 
461  void printArgs(const char *funcname, int nargs, const char *prefix = nullptr);
462  inline void printSTUBWithArglist(const char *funcname, int nargs) { printArgs(funcname, nargs, "STUB: "); }
463  void convertVOIDtoString(int arg, int nargs);
464  void dropStack(int nargs);
465  void drop(uint num);
466 
467  void lingoError(const char *s, ...);
468 
469  void func_mci(const Common::String &name);
470  void func_mciwait(const Common::String &name);
471  void func_beep(int repeats);
472  void func_goto(Datum &frame, Datum &movie, bool commandgo = false );
473  void func_gotoloop();
474  void func_gotonext();
475  void func_gotoprevious();
476  void func_play(Datum &frame, Datum &movie);
477  void func_playdone();
478  void func_cursor(Datum cursorDatum);
479  int func_marker(int m);
480  uint16 func_label(Datum &label);
481 
482  // lingo-the.cpp
483 public:
484  void initTheEntities();
485  void cleanUpTheEntities();
486  const char *entity2str(int id);
487  const char *field2str(int id);
488 
489  // global kTheEntity
490  Datum _actorList;
491  Common::u32char_type_t _itemDelimiter;
492  bool _exitLock;
493  bool _preLoadEventAbort; // no-op, everything is always preloaded
494  Datum _searchPath;
495  bool _trace; // state of movie's trace function
496  int _traceLoad; // internal Director verbosity level
497  bool _updateMovieEnabled;
498  bool _romanLingo;
499  Common::String _soundDevice;
500 
501  Datum getTheEntity(int entity, Datum &id, int field);
502  void setTheEntity(int entity, Datum &id, int field, Datum &d);
503  Datum getTheSprite(Datum &id, int field);
504  void setTheSprite(Datum &id, int field, Datum &d);
505  Datum getTheCast(Datum &id, int field);
506  void setTheCast(Datum &id, int field, Datum &d);
507  Datum getTheCastLib(Datum &id, int field);
508  void setTheCastLib(Datum &id, int field, Datum &d);
509  Datum getTheField(Datum &id1, int field);
510  void setTheField(Datum &id1, int field, Datum &d);
511  Datum getTheChunk(Datum &chunk, int field);
512  void setTheChunk(Datum &chunk, int field, Datum &d);
513  void getObjectProp(Datum &obj, Common::String &propName);
514  void setObjectProp(Datum &obj, Common::String &propName, Datum &d);
515  Datum getTheDate(int field);
516  Datum getTheTime(int field);
517  Datum getTheDeskTopRectList();
518 
519 private:
520  Common::StringArray _entityNames;
521  Common::StringArray _fieldNames;
522 
523 public:
524  LingoCompiler *_compiler;
525  LingoState *_state;
526 
527  bool _freezeState;
528  bool _freezePlay;
529  bool _playDone;
530  bool _abort;
531  bool _expectError;
532  bool _caughtError;
533 
534  TheEntityHash _theEntities;
535  TheEntityFieldHash _theEntityFields;
536 
537  int _objectEntityId;
538 
539  SymbolHash _builtinCmds;
540  SymbolHash _builtinFuncs;
541  SymbolHash _builtinConsts;
542  SymbolHash _builtinListHandlers;
543  SymbolHash _methods;
544  XLibOpenerFuncHash _xlibOpeners;
545  XLibCloserFuncHash _xlibClosers;
546  XLibTypeHash _xlibTypes;
547 
548  OpenXLibsHash _openXLibs;
549  OpenXLibsStateHash _openXLibsState;
550  Common::StringArray _openXtras;
551  Common::Array<Datum> _openXtraObjects;
552  OpenXLibsStateHash _openXtrasState;
553 
554  Common::String _floatPrecisionFormat;
555 
556 public:
557  void push(Datum d);
558  Datum pop();
559  Datum peek(uint offset);
560 
561 public:
562  Common::HashMap<uint32, const char *> _eventHandlerTypes;
565 
566  DatumHash _globalvars;
567 
568  FuncHash _functions;
569 
572 
573  uint _globalCounter;
574 
575  DirectorEngine *_vm;
576 
577  int _floatPrecision;
578 
579  Datum _theResult;
580 
581  // events
582  bool _passEvent;
583  Datum _perFrameHook;
584 
585  Datum _windowList;
586  Symbol _currentInputEvent;
587 
588  struct {
589  LingoExecState _state = kRunning;
590  bool (*_shouldPause)() = nullptr;
591  } _exec;
592 
593 public:
594  void executeImmediateScripts(Frame *frame);
595  void executePerFrameHook(int frame, int subframe, bool stepFrame = true);
596 
597  // lingo-utils.cpp
598 private:
599  Common::HashMap<uint32, Common::U32String> _charNormalizations;
600  void initCharNormalizations();
601 
602 public:
603  Common::String normalizeString(const Common::String &str);
604 
605 public:
606  void addBreakpoint(Breakpoint &bp);
607  bool delBreakpoint(int id);
608  Breakpoint *getBreakpoint(int id);
609 
610  const Common::Array<Breakpoint> &getBreakpoints() const { return _breakpoints; }
611  Common::Array<Breakpoint> &getBreakpoints() { return _breakpoints; }
612 
613 private:
614  int _bpNextId = 1;
615  Common::Array<Breakpoint> _breakpoints;
616 };
617 
618 extern Lingo *g_lingo;
619 
620 } // End of namespace Director
621 
622 #endif
Definition: lingo.h:112
Definition: cast.h:88
Definition: lingo.h:82
Definition: str.h:59
Definition: lingo.h:315
Definition: lingo.h:62
Definition: lingo.h:121
Definition: array.h:52
Definition: picture.h:33
Definition: lingo.h:254
Definition: rect.h:536
Definition: path.h:52
Definition: window.h:106
Definition: lingo.h:269
Definition: lingo.h:195
void void void void void debugC(int level, uint32 debugChannel, MSVC_PRINTF const char *s,...) GCC_PRINTF(3
Definition: archive.h:36
Definition: queue.h:42
Definition: lingo.h:216
Definition: lingo.h:229
T y
Definition: rect.h:49
Definition: ustr.h:57
Definition: algorithm.h:29
T x
Definition: rect.h:48
Definition: rect.h:144
Definition: lingo-codegen.h:30
Definition: lingo.h:131
char32_t u32char_type_t
Definition: ustr.h:41
Definition: lingo.h:207
Definition: debugger.h:45
Definition: stream.h:944
Definition: director.h:157
Definition: lingo.h:73
Definition: lingo-object.h:42
Definition: lingo.h:221
Definition: lobject.h:332
Definition: frame.h:180
Definition: castmember.h:48
Definition: lingo.h:366
Definition: types.h:424
Definition: system.h:39
Definition: lingo-object.h:213
Definition: lingo.h:343