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
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (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, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef DIRECTOR_LINGO_LINGO_H
24 #define DIRECTOR_LINGO_LINGO_H
25 
26 #include "common/hash-ptr.h"
27 #include "common/hash-str.h"
28 #include "common/str-array.h"
29 #include "common/queue.h"
30 #include "common/rect.h"
31 
32 #include "director/types.h"
33 
34 namespace Audio {
35 class AudioStream;
36 }
37 namespace Common {
38 class SeekableReadStreamEndian;
39 }
40 
41 namespace Director {
42 
43 struct ChunkReference;
44 struct TheEntity;
45 struct TheEntityField;
46 struct LingoArchive;
47 struct LingoV4Bytecode;
48 struct LingoV4TheEntity;
49 struct Node;
50 class AbstractObject;
51 class Cast;
52 class ScriptContext;
53 class DirectorEngine;
54 class Frame;
55 class LingoCompiler;
56 
57 typedef void (*inst)(void);
58 #define	STOP (inst)0
59 #define ENTITY_INDEX(t,id) ((t) * 100000 + (id))
60 
61 int calcStringAlignment(const char *s);
62 int calcCodeAlignment(int l);
63 
64 typedef Common::Array<inst> ScriptData;
65 
66 struct FuncDesc {
67 	Common::String name;
68 	const char *proto;
69 
FuncDescFuncDesc70 	FuncDesc(Common::String n, const char *p) { name = n; proto = p; }
71 };
72 
73 typedef Common::HashMap<void *, FuncDesc *> FuncHash;
74 
75 struct BuiltinProto {
76 	const char *name;
77 	void (*func)(int);
78 	int minArgs;	// -1 -- arglist
79 	int maxArgs;
80 	int version;
81 	SymbolType type;
82 };
83 
84 struct Symbol {	/* symbol table entry */
85 	Common::String *name;
86 	SymbolType type;
87 	union {
88 		ScriptData	*defn;	/* HANDLER */
89 		void (*func)();		/* OPCODE */
90 		void (*bltin)(int);	/* BUILTIN */
91 		Common::String	*s;	/* STRING */
92 	} u;
93 
94 	int *refCount;
95 
96 	int nargs;		/* number of arguments */
97 	int maxArgs;	/* maximal number of arguments, for builtins */
98 	int targetType;	/* valid target objects, for method builtins */
99 
100 	Common::Array<Common::String> *argNames;
101 	Common::Array<Common::String> *varNames;
102 	ScriptContext *ctx;		/* optional script context to execute with */
103 	AbstractObject *target;			/* optional method target */
104 	bool anonymous;
105 
106 	Symbol();
107 	Symbol(const Symbol &s);
108 	Symbol& operator=(const Symbol &s);
109 	void reset();
110 	~Symbol();
111 };
112 
113 struct PArray {
114 	bool _sorted;
115 	PropertyArray arr;
116 
PArrayPArray117 	PArray() : _sorted(false) {}
118 
PArrayPArray119 	PArray(int size) : _sorted(false), arr(size) {}
120 };
121 
122 struct FArray {
123 	bool _sorted;
124 	DatumArray arr;
125 
FArrayFArray126 	FArray() : _sorted(false) {}
127 
FArrayFArray128 	FArray(int size) : _sorted(false), arr(size) {}
129 };
130 
131 
132 struct Datum {	/* interpreter stack type */
133 	DatumType type;
134 
135 	union {
136 		int	i;				/* INT, ARGC, ARGCNORET */
137 		double f;			/* FLOAT */
138 		Common::String *s;	/* STRING, VARREF, OBJECT */
139 		FArray *farr;	/* ARRAY, POINT, RECT */
140 		PArray *parr; /* PARRAY */
141 		AbstractObject *obj; /* OBJECT */
142 		ChunkReference *cref; /* CHUNKREF */
143 		CastMemberID *cast;	/* CASTREF, FIELDREF */
144 	} u;
145 
146 	int *refCount;
147 
148 	Datum();
149 	Datum(const Datum &d);
150 	Datum& operator=(const Datum &d);
151 	Datum(int val);
152 	Datum(double val);
153 	Datum(const Common::String &val);
154 	Datum(AbstractObject *val);
155 	Datum(const CastMemberID &val);
156 	Datum(const Common::Rect &rect);
157 	void reset();
158 
~DatumDatum159 	~Datum() {
160 		reset();
161 	}
162 
163 	Datum eval() const;
164 	double asFloat() const;
165 	int asInt() const;
166 	Common::String asString(bool printonly = false) const;
167 	CastMemberID asMemberID() const;
168 	Common::Point asPoint() const;
169 
170 	bool isRef() const;
171 	bool isVarRef() const;
172 	bool isCastRef() const;
173 
174 	const char *type2str(bool isk = false) const;
175 
176 	int equalTo(Datum &d, bool ignoreCase = false) const;
177 	int compareTo(Datum &d) const;
178 };
179 
180 struct ChunkReference {
181 	Datum source;
182 	ChunkType type;
183 	int startChunk;
184 	int endChunk;
185 	int start;
186 	int end;
187 
ChunkReferenceChunkReference188 	ChunkReference(const Datum &src, ChunkType t, int sc, int ec, int s, int e)
189 		: source(src), type(t), startChunk(sc), endChunk(ec), start(s), end(e) {}
190 };
191 
192 struct PCell {
193 	Datum p;
194 	Datum v;
195 
196 	PCell();
197 	PCell(const Datum &prop, const Datum &val);
198 };
199 
200 struct Builtin {
201 	void (*func)(void);
202 	int nargs;
203 
BuiltinBuiltin204 	Builtin(void (*func1)(void), int nargs1) : func(func1), nargs(nargs1) {}
205 };
206 
207 typedef Common::HashMap<int32, ScriptContext *> ScriptContextHash;
208 typedef Common::Array<Datum> StackData;
209 typedef Common::HashMap<Common::String, Symbol, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SymbolHash;
210 typedef Common::HashMap<Common::String, Datum, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> DatumHash;
211 typedef Common::HashMap<Common::String, Builtin *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> BuiltinHash;
212 typedef Common::HashMap<Common::String, VarType, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> VarTypeHash;
213 typedef void (*XLibFunc)(int);
214 typedef Common::HashMap<Common::String, XLibFunc, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> XLibFuncHash;
215 typedef Common::HashMap<Common::String, ObjectType, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> OpenXLibsHash;
216 
217 typedef Common::HashMap<Common::String, TheEntity *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TheEntityHash;
218 typedef Common::HashMap<Common::String, TheEntityField *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TheEntityFieldHash;
219 
220 struct CFrame {	/* proc/func call stack frame */
221 	Symbol			sp;					/* symbol table entry */
222 	int				retPC;				/* where to resume after return */
223 	ScriptData		*retScript;			/* which script to resume after return */
224 	ScriptContext	*retContext;		/* which script context to use after return */
225 	bool			retFreezeContext;	/* whether the context should be frozen after return */
226 	DatumHash		*retLocalVars;
227 	Datum			retMe;				/* which me obj to use after return */
228 	uint			stackSizeBefore;
229 	bool			allowRetVal;		/* whether to allow a return value */
230 	Datum			defaultRetVal;		/* default return value */
231 };
232 
233 struct LingoEvent {
234 	LEvent event;
235 	int eventId;
236 	ScriptType scriptType;
237 	CastMemberID scriptId;
238 	bool passByDefault;
239 	int channelId;
240 
241 	LingoEvent (LEvent e, int ei, ScriptType st, CastMemberID si, bool pass, int ci = -1) {
242 		event = e;
243 		eventId = ei;
244 		scriptType = st;
245 		scriptId = si;
246 		passByDefault = pass;
247 		channelId = ci;
248 	}
249 };
250 
251 
252 struct LingoArchive {
LingoArchiveLingoArchive253 	LingoArchive(Cast *c) : cast(c) {};
254 	~LingoArchive();
255 
256 	Cast *cast;
257 	ScriptContextHash lctxContexts;
258 	ScriptContextHash scriptContexts[kMaxScriptType + 1];
259 	Common::Array<Common::String> names;
260 	Common::HashMap<uint32, Common::String> primaryEventHandlers;
261 	SymbolHash functionHandlers;
262 
263 	ScriptContext *getScriptContext(ScriptType type, uint16 id);
264 	Common::String getName(uint16 id);
265 
266 	void addCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr);
267 	void removeCode(ScriptType type, uint16 id);
268 	void replaceCode(const Common::U32String &code, ScriptType type, uint16 id, const char *scriptName = nullptr);
269 	void addCodeV4(Common::SeekableReadStreamEndian &stream, uint16 lctxIndex, const Common::String &archName, uint16 version);
270 	void addNamesV4(Common::SeekableReadStreamEndian &stream);
271 };
272 
273 class Lingo {
274 
275 public:
276 	Lingo(DirectorEngine *vm);
277 	~Lingo();
278 
279 	void resetLingo();
280 
281 	void executeHandler(const Common::String &name);
282 	void executeScript(ScriptType type, CastMemberID id);
283 	void printStack(const char *s, uint pc);
284 	void printCallStack(uint pc);
285 	Common::String decodeInstruction(ScriptData *sd, uint pc, uint *newPC = NULL);
286 
287 	void reloadBuiltIns();
288 	void initBuiltIns();
289 	void initBuiltIns(BuiltinProto protos[]);
290 	void cleanupBuiltIns();
291 	void cleanupBuiltIns(BuiltinProto protos[]);
292 	void initFuncs();
293 	void cleanupFuncs();
294 	void initBytecode();
295 	void initMethods();
296 	void cleanupMethods();
297 	void initXLibs();
298 	void cleanupXLibs();
299 
300 	Common::String normalizeXLibName(Common::String name);
301 	void openXLib(Common::String name, ObjectType type);
302 	void closeXLib(Common::String name);
303 	void reloadOpenXLibs();
304 
305 	void runTests();
306 
307 	// lingo-events.cpp
308 private:
309 	void initEventHandlerTypes();
310 	void processEvent(LEvent event, ScriptType st, CastMemberID scriptId, int channelId = -1);
311 
312 public:
313 	ScriptType event2script(LEvent ev);
314 	Symbol getHandler(const Common::String &name);
315 
316 	void processEvents(Common::Queue<LingoEvent> &queue);
317 
318 public:
319 	void execute();
320 	void loadStateFromWindow();
321 	void saveStateToWindow();
322 	void pushContext(const Symbol funcSym, bool allowRetVal, Datum defaultRetVal);
323 	void popContext(bool aborting = false);
324 	bool hasFrozenContext();
325 	void cleanLocalVars();
326 	void varAssign(const Datum &var, const Datum &value);
327 	Datum varFetch(const Datum &var, bool silent = false);
328 	Common::U32String evalChunkRef(const Datum &var);
329 	Datum findVarV4(int varType, const Datum &id);
330 	CastMemberID resolveCastMember(const Datum &memberID, const Datum &castLib);
331 
332 	int getAlignedType(const Datum &d1, const Datum &d2, bool numsOnly);
333 
334 	void printAllVars();
335 
readInst()336 	inst readInst() { return getInst(_pc++); }
getInst(uint pc)337 	inst getInst(uint pc) { return (*_currentScript)[pc]; }
readInt()338 	int readInt() { return getInt(_pc++); }
339 	int getInt(uint pc);
readFloat()340 	double readFloat() { double d = getFloat(_pc); _pc += calcCodeAlignment(sizeof(double)); return d; }
getFloat(uint pc)341 	double getFloat(uint pc) { return *(double *)(&((*_currentScript)[pc])); }
readString()342 	char *readString() { char *s = getString(_pc); _pc += calcStringAlignment(s); return s; }
getString(uint pc)343 	char *getString(uint pc) { return (char *)(&((*_currentScript)[pc])); }
344 
345 	void pushVoid();
346 
347 	void printSTUBWithArglist(const char *funcname, int nargs, const char *prefix = "STUB:");
348 	void convertVOIDtoString(int arg, int nargs);
349 	void dropStack(int nargs);
350 	void drop(uint num);
351 
352 	void lingoError(const char *s, ...);
353 
354 	void func_mci(const Common::String &name);
355 	void func_mciwait(const Common::String &name);
356 	void func_beep(int repeats);
357 	void func_goto(Datum &frame, Datum &movie);
358 	void func_gotoloop();
359 	void func_gotonext();
360 	void func_gotoprevious();
361 	void func_play(Datum &frame, Datum &movie);
362 	void func_playdone();
363 	void func_cursor(Datum cursorDatum);
364 	int func_marker(int m);
365 	uint16 func_label(Datum &label);
366 
367 	// lingo-the.cpp
368 public:
369 	void initTheEntities();
370 	void cleanUpTheEntities();
371 	const char *entity2str(int id);
372 	const char *field2str(int id);
373 
374 	// global kTheEntity
375 	Common::u32char_type_t _itemDelimiter;
376 
377 	Datum getTheEntity(int entity, Datum &id, int field);
378 	void setTheEntity(int entity, Datum &id, int field, Datum &d);
379 	Datum getTheMenuItemEntity(int entity, Datum &menuId, int field, Datum &menuItemId);
380 	void setTheMenuItemEntity(int entity, Datum &menuId, int field, Datum &menuItemId, Datum &d);
381 	Datum getTheSprite(Datum &id, int field);
382 	void setTheSprite(Datum &id, int field, Datum &d);
383 	Datum getTheCast(Datum &id, int field);
384 	void setTheCast(Datum &id, int field, Datum &d);
385 	Datum getTheField(Datum &id1, int field);
386 	void setTheField(Datum &id1, int field, Datum &d);
387 	Datum getTheChunk(Datum &chunk, int field);
388 	void setTheChunk(Datum &chunk, int field, Datum &d);
389 	void getObjectProp(Datum &obj, Common::String &propName);
390 	void setObjectProp(Datum &obj, Common::String &propName, Datum &d);
391 	Datum getTheDate(int field);
392 	Datum getTheTime(int field);
393 
394 private:
395 	Common::StringArray _entityNames;
396 	Common::StringArray _fieldNames;
397 
398 public:
399 	LingoCompiler *_compiler;
400 
401 	int _currentChannelId;
402 	ScriptContext *_currentScriptContext;
403 	ScriptData *_currentScript;
404 	Datum _currentMe;
405 
406 	bool _freezeContext;
407 	bool _abort;
408 	bool _expectError;
409 	bool _caughtError;
410 
411 	TheEntityHash _theEntities;
412 	TheEntityFieldHash _theEntityFields;
413 
414 	int _objectEntityId;
415 
416 	SymbolHash _builtinCmds;
417 	SymbolHash _builtinFuncs;
418 	SymbolHash _builtinConsts;
419 	SymbolHash _methods;
420 	XLibFuncHash _xlibOpeners;
421 	XLibFuncHash _xlibClosers;
422 
423 	OpenXLibsHash _openXLibs;
424 
425 	Common::String _floatPrecisionFormat;
426 
427 public:
428 	void push(Datum d);
429 	Datum pop();
430 	Datum peek(uint offset);
431 
432 public:
433 	Common::HashMap<uint32, const char *> _eventHandlerTypes;
434 	Common::HashMap<Common::String, uint32, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _eventHandlerTypeIds;
435 	Common::HashMap<Common::String, Audio::AudioStream *> _audioAliases;
436 
437 	DatumHash _globalvars;
438 	DatumHash *_localvars;
439 
440 	FuncHash _functions;
441 
442 	Common::HashMap<int, LingoV4Bytecode *> _lingoV4;
443 	Common::HashMap<int, LingoV4TheEntity *> _lingoV4TheEntity;
444 
445 	uint _globalCounter;
446 	uint _pc;
447 
448 	StackData _stack;
449 
450 	DirectorEngine *_vm;
451 
452 	int _floatPrecision;
453 
454 	Datum _theResult;
455 
456 	// events
457 	bool _passEvent;
458 	Datum _perFrameHook;
459 
460 	Datum _windowList;
461 
462 public:
463 	void executeImmediateScripts(Frame *frame);
464 	void executePerFrameHook(int frame, int subframe);
465 
466 	// lingo-utils.cpp
467 private:
468 	Common::HashMap<uint32, Common::U32String> _charNormalizations;
469 	void initCharNormalizations();
470 
471 public:
472 	Common::String normalizeString(const Common::String &str);
473 };
474 
475 extern Lingo *g_lingo;
476 
477 } // End of namespace Director
478 
479 #endif
480