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 // Heavily inspired by hoc
24 // Copyright (C) AT&T 1995
25 // All Rights Reserved
26 //
27 // Permission to use, copy, modify, and distribute this software and
28 // its documentation for any purpose and without fee is hereby
29 // granted, provided that the above copyright notice appear in all
30 // copies and that both that the copyright notice and this
31 // permission notice and warranty disclaimer appear in supporting
32 // documentation, and that the name of AT&T or any of its entities
33 // not be used in advertising or publicity pertaining to
34 // distribution of the software without specific, written prior
35 // permission.
36 //
37 // AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
38 // INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
39 // IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
40 // SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
42 // IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 // ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
44 // THIS SOFTWARE.
45 
46 #include "director/cast.h"
47 #include "director/util.h"
48 #include "director/lingo/lingo.h"
49 #include "director/lingo/lingo-gr.h"
50 
51 namespace Director {
52 
53 static struct FuncDescr {
54 	const inst func;
55 	const char *name;
56 	const char *args;
57 } funcDescr[] = {
58 	{ 0,					"STOP",			"" },
59 	{ Lingo::c_xpop,		"c_xpop",		"" },
60 	{ Lingo::c_arraypush,	"c_arraypush",		"i" },
61 	{ Lingo::c_printtop,	"c_printtop",	"" },
62 	{ Lingo::c_constpush,	"c_constpush",	"i" },
63 	{ Lingo::c_voidpush,	"c_voidpush",	"" },
64 	{ Lingo::c_fconstpush,	"c_fconstpush",	"f" },
65 	{ Lingo::c_stringpush,	"c_stringpush",	"s" },
66 	{ Lingo::c_symbolpush,	"c_symbolpush",	"s" },	// D3
67 	{ Lingo::c_varpush,		"c_varpush",	"s" },
68 	{ Lingo::c_setImmediate,"c_setImmediate","i" },
69 	{ Lingo::c_assign,		"c_assign",		"" },
70 	{ Lingo::c_eval,		"c_eval",		"s" },
71 	{ Lingo::c_theentitypush,"c_theentitypush","ii" }, // entity, field
72 	{ Lingo::c_theentityassign,"c_theentityassign","ii" },
73 	{ Lingo::c_swap,		"c_swap",		"" },
74 	{ Lingo::c_add,			"c_add",		"" },
75 	{ Lingo::c_sub,			"c_sub",		"" },
76 	{ Lingo::c_mul,			"c_mul",		"" },
77 	{ Lingo::c_div,			"c_div",		"" },
78 	{ Lingo::c_mod,			"c_mod",		"" },
79 	{ Lingo::c_negate,		"c_negate",		"" },
80 	{ Lingo::c_ampersand,	"c_ampersand",	"" },
81 	{ Lingo::c_after,		"c_after",		"" },	// D3
82 	{ Lingo::c_before,		"c_before",		"" },	// D3
83 	{ Lingo::c_concat,		"c_concat",		"" },
84 	{ Lingo::c_contains,	"c_contains",	"" },
85 	{ Lingo::c_starts,		"c_starts",		"" },
86 	{ Lingo::c_intersects,	"c_intersects",	"" },
87 	{ Lingo::c_within,		"c_within",		"" },
88 	{ Lingo::c_charOf,		"c_charOf",		"" },	// D3
89 	{ Lingo::c_charToOf,	"c_charToOf",	"" },	// D3
90 	{ Lingo::c_itemOf,		"c_itemOf",		"" },	// D3
91 	{ Lingo::c_itemToOf,	"c_itemToOf",	"" },	// D3
92 	{ Lingo::c_lineOf,		"c_lineOf",		"" },	// D3
93 	{ Lingo::c_lineToOf,	"c_lineToOf",	"" },	// D3
94 	{ Lingo::c_wordOf,		"c_wordOf",		"" },	// D3
95 	{ Lingo::c_wordToOf,	"c_wordToOf",	"" },	// D3
96 	{ Lingo::c_and,			"c_and",		"" },
97 	{ Lingo::c_or,			"c_or",			"" },
98 	{ Lingo::c_not,			"c_not",		"" },
99 	{ Lingo::c_eq,			"c_eq",			"" },
100 	{ Lingo::c_neq,			"c_neq",		"" },
101 	{ Lingo::c_gt,			"c_gt",			"" },
102 	{ Lingo::c_lt,			"c_lt",			"" },
103 	{ Lingo::c_ge,			"c_ge",			"" },
104 	{ Lingo::c_le,			"c_le",			"" },
105 	{ Lingo::c_repeatwhilecode,"c_repeatwhilecode","oo" },
106 	{ Lingo::c_repeatwithcode,"c_repeatwithcode","ooooos" },
107 	{ Lingo::c_exitRepeat,	"c_exitRepeat",	"" },
108 	{ Lingo::c_ifcode,		"c_ifcode",		"oooi" },
109 	{ Lingo::c_tellcode,	"c_tellcode",	"o" },
110 	{ Lingo::c_whencode,	"c_whencode",	"os" },
111 	{ Lingo::c_goto,		"c_goto",		"" },
112 	{ Lingo::c_gotoloop,	"c_gotoloop",	"" },
113 	{ Lingo::c_gotonext,	"c_gotonext",	"" },
114 	{ Lingo::c_gotoprevious,"c_gotoprevious","" },
115 	{ Lingo::c_play,		"c_play",		"" },
116 	{ Lingo::c_playdone,	"c_playdone",	"" },
117 	{ Lingo::c_call,		"c_call",		"si" },
118 	{ Lingo::c_procret,		"c_procret",	"" },
119 	{ Lingo::c_global,		"c_global",		"s" },
120 	{ Lingo::c_property,	"c_property",	"s" },
121 	{ Lingo::c_instance,	"c_instance",	"s" },
122 	{ Lingo::c_open,		"c_open",		"" },
123 	{ 0, 0, 0 }
124 };
125 
initFuncs()126 void Lingo::initFuncs() {
127 	Symbol sym;
128 	for (FuncDescr *fnc = funcDescr; fnc->name; fnc++) {
129 		sym.u.func = fnc->func;
130 		_functions[(void *)sym.u.s] = new FuncDesc(fnc->name, fnc->args);
131 	}
132 }
133 
push(Datum d)134 void Lingo::push(Datum d) {
135 	_stack.push_back(d);
136 }
137 
pushVoid()138 void Lingo::pushVoid() {
139 	Datum d;
140 	d.u.s = NULL;
141 	d.type = VOID;
142 	push(d);
143 }
144 
pop(void)145 Datum Lingo::pop(void) {
146 	if (_stack.size() == 0)
147 		assert(0);
148 
149 	Datum ret = _stack.back();
150 	_stack.pop_back();
151 
152 	return ret;
153 }
154 
c_xpop()155 void Lingo::c_xpop() {
156 	g_lingo->pop();
157 }
158 
c_printtop(void)159 void Lingo::c_printtop(void) {
160 	Datum d = g_lingo->pop();
161 
162 	switch (d.type) {
163 	case VOID:
164 		warning("Void, came from %s", d.u.s ? d.u.s->c_str() : "<>");
165 		break;
166 	case INT:
167 		warning("%d", d.u.i);
168 		break;
169 	case FLOAT:
170 		warning(g_lingo->_floatPrecisionFormat.c_str(), d.u.f);
171 		break;
172 	case VAR:
173 		if (!d.u.sym) {
174 			warning("Inconsistent stack: var, val: %d", d.u.i);
175 		} else {
176 			if (!d.u.sym->name.empty())
177 				warning("var: %s", d.u.sym->name.c_str());
178 			else
179 				warning("Nameless var. val: %d", d.u.sym->u.i);
180 		}
181 		break;
182 	case STRING:
183 		warning("%s", d.u.s->c_str());
184 		break;
185 	case POINT:
186 		warning("point(%d, %d)", (int)((*d.u.arr)[0]), (int)((*d.u.arr)[1]));
187 		break;
188 	case SYMBOL:
189 		warning("%s", d.type2str(true));
190 		break;
191 	case OBJECT:
192 		warning("#%s", d.u.s->c_str());
193 		break;
194 	default:
195 		warning("--unknown--");
196 	}
197 }
198 
c_constpush()199 void Lingo::c_constpush() {
200 	Datum d;
201 	inst i = (*g_lingo->_currentScript)[g_lingo->_pc++];
202 	d.u.i = READ_UINT32(&i);
203 	d.type = INT;
204 	g_lingo->push(d);
205 }
206 
c_voidpush()207 void Lingo::c_voidpush() {
208 	Datum d;
209 	d.u.s = NULL;
210 	d.type = VOID;
211 	g_lingo->push(d);
212 }
213 
c_fconstpush()214 void Lingo::c_fconstpush() {
215 	Datum d;
216 	inst i = (*g_lingo->_currentScript)[g_lingo->_pc];
217 	d.u.f = *(double *)(&i);
218 	d.type = FLOAT;
219 
220 	g_lingo->_pc += g_lingo->calcCodeAlignment(sizeof(double));
221 
222 	g_lingo->push(d);
223 }
224 
c_stringpush()225 void Lingo::c_stringpush() {
226 	char *s = (char *)&(*g_lingo->_currentScript)[g_lingo->_pc];
227 	g_lingo->_pc += g_lingo->calcStringAlignment(s);
228 
229 	g_lingo->push(Datum(new Common::String(s)));
230 }
231 
c_symbolpush()232 void Lingo::c_symbolpush() {
233 	char *s = (char *)&(*g_lingo->_currentScript)[g_lingo->_pc];
234 	g_lingo->_pc += g_lingo->calcStringAlignment(s);
235 
236 	warning("STUB: c_symbolpush()");
237 
238 	// TODO: FIXME: Must push symbol instead of string
239 	g_lingo->push(Datum(new Common::String(s)));
240 }
241 
c_arraypush()242 void Lingo::c_arraypush() {
243 	Datum d;
244 	inst v = (*g_lingo->_currentScript)[g_lingo->_pc++];
245 	int arraySize = READ_UINT32(&v);
246 
247 	warning("STUB: c_arraypush()");
248 
249 	for (int i = 0; i < arraySize; i++)
250 		g_lingo->pop();
251 
252 	d.u.i = arraySize;
253 	d.type = INT;
254 	g_lingo->push(d);
255 }
256 
c_varpush()257 void Lingo::c_varpush() {
258 	Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
259 	Datum d;
260 
261 	g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str());
262 
263 	// In immediate mode we will push variables as strings
264 	// This is used for playAccel
265 	if (g_lingo->_immediateMode) {
266 		g_lingo->push(Datum(new Common::String(name)));
267 
268 		return;
269 	}
270 
271 	if (g_lingo->getHandler(name) != NULL) {
272 		d.type = HANDLER;
273 		d.u.s = new Common::String(name);
274 		g_lingo->push(d);
275 		return;
276 	}
277 
278 	d.u.sym = g_lingo->lookupVar(name.c_str());
279 	if (d.u.sym->type == CASTREF) {
280 		d.type = INT;
281 		int val = d.u.sym->u.i;
282 
283 		delete d.u.sym;
284 
285 		d.u.i = val;
286 	} else {
287 		d.type = VAR;
288 	}
289 
290 	g_lingo->push(d);
291 }
292 
c_setImmediate()293 void Lingo::c_setImmediate() {
294 	inst i = (*g_lingo->_currentScript)[g_lingo->_pc++];
295 
296 	g_lingo->_immediateMode = READ_UINT32(&i);
297 }
298 
c_assign()299 void Lingo::c_assign() {
300 	Datum d1, d2;
301 	d1 = g_lingo->pop();
302 	d2 = g_lingo->pop();
303 
304 	if (d1.type != VAR && d1.type != REFERENCE) {
305 		warning("assignment to non-variable");
306 		return;
307 	}
308 
309 	if (d1.type == REFERENCE) {
310 		if (!g_director->getCurrentScore()->_loadedText->contains(d1.u.i)) {
311 			if (!g_director->getCurrentScore()->_loadedText->contains(d1.u.i - 1024)) {
312 				warning("c_assign: Unknown REFERENCE %d", d1.u.i);
313 				g_lingo->pushVoid();
314 				return;
315 			} else {
316 				d1.u.i -= 1024;
317 			}
318 		}
319 
320 		warning("STUB: c_assing REFERENCE");
321 
322 		return;
323 	}
324 
325 	if (d1.u.sym->type != INT && d1.u.sym->type != VOID &&
326 			d1.u.sym->type != FLOAT && d1.u.sym->type != STRING) {
327 		warning("assignment to non-variable '%s'", d1.u.sym->name.c_str());
328 		return;
329 	}
330 
331 	if ((d1.u.sym->type == STRING || d1.u.sym->type == VOID) && d1.u.sym->u.s) // Free memory if needed
332 		delete d1.u.sym->u.s;
333 
334 	if (d1.u.sym->type == POINT || d1.u.sym->type == RECT || d1.u.sym->type == ARRAY)
335 		delete d1.u.sym->u.arr;
336 
337 	if (d2.type == INT) {
338 		d1.u.sym->u.i = d2.u.i;
339 	} else if (d2.type == FLOAT) {
340 		d1.u.sym->u.f = d2.u.f;
341 	} else if (d2.type == STRING) {
342 		d1.u.sym->u.s = new Common::String(*d2.u.s);
343 		delete d2.u.s;
344 	} else if (d2.type == POINT) {
345 		d1.u.sym->u.arr = new FloatArray(*d2.u.arr);
346 		delete d2.u.arr;
347 	} else if (d2.type == SYMBOL) {
348 		d1.u.sym->u.i = d2.u.i;
349 	} else if (d2.type == OBJECT) {
350 		d1.u.sym->u.s = d2.u.s;
351 	} else {
352 		warning("c_assign: unhandled type: %s", d2.type2str());
353 		d1.u.sym->u.s = d2.u.s;
354 	}
355 
356 	d1.u.sym->type = d2.type;
357 }
358 
verify(Symbol * s)359 bool Lingo::verify(Symbol *s) {
360 	if (s->type != INT && s->type != VOID && s->type != FLOAT && s->type != STRING && s->type != POINT && s->type != SYMBOL) {
361 		warning("attempt to evaluate non-variable '%s'", s->name.c_str());
362 
363 		return false;
364 	}
365 
366 	if (s->type == VOID)
367 		warning("Variable used before assigning a value '%s'", s->name.c_str());
368 
369 	return true;
370 }
371 
c_eval()372 void Lingo::c_eval() {
373 	g_lingo->c_varpush();
374 
375 	Datum d;
376 	d = g_lingo->pop();
377 
378 	if (d.type == HANDLER) {
379 		g_lingo->call(*d.u.s, 0);
380 		delete d.u.s;
381 		return;
382 	}
383 
384 	if (d.type != VAR) { // It could be cast ref
385 		g_lingo->push(d);
386 		return;
387 	}
388 
389 	if (!g_lingo->verify(d.u.sym))
390 		return;
391 
392 	d.type = d.u.sym->type;
393 
394 	if (d.u.sym->type == INT)
395 		d.u.i = d.u.sym->u.i;
396 	else if (d.u.sym->type == FLOAT)
397 		d.u.f = d.u.sym->u.f;
398 	else if (d.u.sym->type == STRING)
399 		d.u.s = new Common::String(*d.u.sym->u.s);
400 	else if (d.u.sym->type == POINT)
401 		d.u.arr = d.u.sym->u.arr;
402 	else if (d.u.sym->type == SYMBOL)
403 		d.u.i = d.u.sym->u.i;
404 	else if (d.u.sym->type == VOID)
405 		d.u.s = new Common::String(d.u.sym->name);
406 	else
407 		warning("c_eval: unhandled type: %s", d.type2str());
408 
409 	g_lingo->push(d);
410 }
411 
c_theentitypush()412 void Lingo::c_theentitypush() {
413 	inst e = (*g_lingo->_currentScript)[g_lingo->_pc++];
414 	inst f = (*g_lingo->_currentScript)[g_lingo->_pc++];
415 	Datum id = g_lingo->pop();
416 
417 	int entity = READ_UINT32(&e);
418 	int field  = READ_UINT32(&f);
419 
420 	Datum d = g_lingo->getTheEntity(entity, id, field);
421 	g_lingo->push(d);
422 }
423 
c_theentityassign()424 void Lingo::c_theentityassign() {
425 	inst e = (*g_lingo->_currentScript)[g_lingo->_pc++];
426 	inst f = (*g_lingo->_currentScript)[g_lingo->_pc++];
427 	Datum id = g_lingo->pop();
428 
429 	int entity = READ_UINT32(&e);
430 	int field  = READ_UINT32(&f);
431 
432 	Datum d = g_lingo->pop();
433 	g_lingo->setTheEntity(entity, id, field, d);
434 }
435 
c_swap()436 void Lingo::c_swap() {
437 	Datum d2 = g_lingo->pop();
438 	Datum d1 = g_lingo->pop();
439 	g_lingo->push(d2);
440 	g_lingo->push(d1);
441 }
442 
c_add()443 void Lingo::c_add() {
444 	Datum d2 = g_lingo->pop();
445 	Datum d1 = g_lingo->pop();
446 
447 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
448 		d1.u.f += d2.u.f;
449 	} else {
450 		d1.u.i += d2.u.i;
451 	}
452 	g_lingo->push(d1);
453 }
454 
c_sub()455 void Lingo::c_sub() {
456 	Datum d2 = g_lingo->pop();
457 	Datum d1 = g_lingo->pop();
458 
459 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
460 		d1.u.f -= d2.u.f;
461 	} else {
462 		d1.u.i -= d2.u.i;
463 	}
464 	g_lingo->push(d1);
465 }
466 
c_mul()467 void Lingo::c_mul() {
468 	Datum d2 = g_lingo->pop();
469 	Datum d1 = g_lingo->pop();
470 
471 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
472 		d1.u.f *= d2.u.f;
473 	} else {
474 		d1.u.i *= d2.u.i;
475 	}
476 	g_lingo->push(d1);
477 }
478 
c_div()479 void Lingo::c_div() {
480 	Datum d2 = g_lingo->pop();
481 
482 	if ((d2.type == INT && d2.u.i == 0) ||
483 			(d2.type == FLOAT && d2.u.f == 0.0))
484 		error("division by zero");
485 
486 	Datum d1 = g_lingo->pop();
487 
488 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
489 		d1.u.f /= d2.u.f;
490 	} else {
491 		d1.u.i /= d2.u.i;
492 	}
493 	g_lingo->push(d1);
494 }
495 
c_mod()496 void Lingo::c_mod() {
497 	Datum d2 = g_lingo->pop();
498 	d2.toInt();
499 
500 	if (d2.u.i == 0)
501 		error("division by zero");
502 
503 	Datum d1 = g_lingo->pop();
504 	d1.toInt();
505 
506 	d1.u.i %= d2.u.i;
507 
508 	g_lingo->push(d1);
509 }
510 
c_negate()511 void Lingo::c_negate() {
512 	Datum d = g_lingo->pop();
513 
514 	if (d.type == INT)
515 		d.u.i = -d.u.i;
516 	else if (d.type == FLOAT)
517 		d.u.f = -d.u.f;
518 
519 	g_lingo->push(d);
520 }
521 
c_ampersand()522 void Lingo::c_ampersand() {
523 	Datum d2 = g_lingo->pop();
524 	Datum d1 = g_lingo->pop();
525 
526 	d1.toString();
527 	d2.toString();
528 
529 	*d1.u.s += *d2.u.s;
530 
531 	delete d2.u.s;
532 
533 	g_lingo->push(d1);
534 }
535 
c_after()536 void Lingo::c_after() {
537 	Datum d2 = g_lingo->pop();
538 	Datum d1 = g_lingo->pop();
539 
540 	d1.toString();
541 	d2.toString();
542 
543 	warning("STUB: c_after");
544 
545 	delete d2.u.s;
546 
547 	g_lingo->push(d1);
548 }
549 
c_before()550 void Lingo::c_before() {
551 	Datum d2 = g_lingo->pop();
552 	Datum d1 = g_lingo->pop();
553 
554 	d1.toString();
555 	d2.toString();
556 
557 	warning("STUB: c_before");
558 
559 	delete d2.u.s;
560 
561 	g_lingo->push(d1);
562 }
563 
c_concat()564 void Lingo::c_concat() {
565 	Datum d2 = g_lingo->pop();
566 	Datum d1 = g_lingo->pop();
567 
568 	d1.toString();
569 	d2.toString();
570 
571 	*d1.u.s += " ";
572 	*d1.u.s += *d2.u.s;
573 
574 	delete d2.u.s;
575 
576 	g_lingo->push(d1);
577 }
578 
c_contains()579 void Lingo::c_contains() {
580 	Datum d2 = g_lingo->pop();
581 	Datum d1 = g_lingo->pop();
582 
583 	d1.toString();
584 	d2.toString();
585 
586 	Common::String *s1 = toLowercaseMac(d1.u.s);
587 	Common::String *s2 = toLowercaseMac(d2.u.s);
588 
589 	int res = s1->contains(*s2) ? 1 : 0;
590 
591 	delete d1.u.s;
592 	delete d2.u.s;
593 	delete s1;
594 	delete s2;
595 
596 	d1.type = INT;
597 	d1.u.i = res;
598 
599 	g_lingo->push(d1);
600 }
601 
c_starts()602 void Lingo::c_starts() {
603 	Datum d2 = g_lingo->pop();
604 	Datum d1 = g_lingo->pop();
605 
606 	d1.toString();
607 	d2.toString();
608 
609 	Common::String *s1 = toLowercaseMac(d1.u.s);
610 	Common::String *s2 = toLowercaseMac(d2.u.s);
611 
612 	int res = s1->hasPrefix(*s2) ? 1 : 0;
613 
614 	delete d1.u.s;
615 	delete d2.u.s;
616 	delete s1;
617 	delete s2;
618 
619 	d1.type = INT;
620 	d1.u.i = res;
621 
622 	g_lingo->push(d1);
623 }
624 
c_intersects()625 void Lingo::c_intersects() {
626 	Datum d2 = g_lingo->pop();
627 	Datum d1 = g_lingo->pop();
628 
629 	warning("STUB: c_intersects: %d %d", d1.u.i, d2.u.i);
630 
631 	g_lingo->push(d1);
632 }
633 
c_within()634 void Lingo::c_within() {
635 	Datum d2 = g_lingo->pop();
636 	Datum d1 = g_lingo->pop();
637 
638 	warning("STUB: c_within: %d %d", d1.u.i, d2.u.i);
639 
640 	g_lingo->push(d1);
641 }
642 
c_charOf()643 void Lingo::c_charOf() {
644 	Datum d2 = g_lingo->pop();
645 	Datum d1 = g_lingo->pop();
646 
647 	warning("STUB: c_charOf: %d %d", d1.u.i, d2.u.i);
648 
649 	g_lingo->push(d1);
650 }
651 
c_charToOf()652 void Lingo::c_charToOf() {
653 	Datum d3 = g_lingo->pop();
654 	Datum d2 = g_lingo->pop();
655 	Datum d1 = g_lingo->pop();
656 
657 	warning("STUB: c_charToOf: %d %d %d", d1.u.i, d2.u.i, d3.u.i);
658 
659 	g_lingo->push(d1);
660 }
661 
c_itemOf()662 void Lingo::c_itemOf() {
663 	Datum d2 = g_lingo->pop();
664 	Datum d1 = g_lingo->pop();
665 
666 	warning("STUB: c_itemOf: %d %d", d1.u.i, d2.u.i);
667 
668 	g_lingo->push(d1);
669 }
670 
c_itemToOf()671 void Lingo::c_itemToOf() {
672 	Datum d3 = g_lingo->pop();
673 	Datum d2 = g_lingo->pop();
674 	Datum d1 = g_lingo->pop();
675 
676 	warning("STUB: c_itemToOf: %d %d %d", d1.u.i, d2.u.i, d3.u.i);
677 
678 	g_lingo->push(d1);
679 }
680 
c_lineOf()681 void Lingo::c_lineOf() {
682 	Datum d2 = g_lingo->pop();
683 	Datum d1 = g_lingo->pop();
684 
685 	warning("STUB: c_lineOf: %d %d", d1.u.i, d2.u.i);
686 
687 	g_lingo->push(d1);
688 }
689 
c_lineToOf()690 void Lingo::c_lineToOf() {
691 	Datum d3 = g_lingo->pop();
692 	Datum d2 = g_lingo->pop();
693 	Datum d1 = g_lingo->pop();
694 
695 	warning("STUB: c_lineToOf: %d %d %d", d1.u.i, d2.u.i, d3.u.i);
696 
697 	g_lingo->push(d1);
698 }
699 
c_wordOf()700 void Lingo::c_wordOf() {
701 	Datum d2 = g_lingo->pop();
702 	Datum d1 = g_lingo->pop();
703 
704 	warning("STUB: c_wordOf: %d %d", d1.u.i, d2.u.i);
705 
706 	g_lingo->push(d1);
707 }
708 
c_wordToOf()709 void Lingo::c_wordToOf() {
710 	Datum d3 = g_lingo->pop();
711 	Datum d2 = g_lingo->pop();
712 	Datum d1 = g_lingo->pop();
713 
714 	warning("STUB: c_wordToOf: %d %d %d", d1.u.i, d2.u.i, d3.u.i);
715 
716 	g_lingo->push(d1);
717 }
718 
c_and()719 void Lingo::c_and() {
720 	Datum d2 = g_lingo->pop();
721 	Datum d1 = g_lingo->pop();
722 
723 	d1.toInt();
724 	d2.toInt();
725 
726 	d1.u.i = (d1.u.i && d2.u.i) ? 1 : 0;
727 
728 	g_lingo->push(d1);
729 }
730 
c_or()731 void Lingo::c_or() {
732 	Datum d2 = g_lingo->pop();
733 	Datum d1 = g_lingo->pop();
734 
735 	d1.toInt();
736 	d2.toInt();
737 
738 	d1.u.i = (d1.u.i || d2.u.i) ? 1 : 0;
739 
740 	g_lingo->push(d1);
741 }
742 
c_not()743 void Lingo::c_not() {
744 	Datum d = g_lingo->pop();
745 
746 	d.toInt();
747 
748 	d.u.i = ~d.u.i ? 1 : 0;
749 
750 	g_lingo->push(d);
751 }
752 
c_eq()753 void Lingo::c_eq() {
754 	Datum d2 = g_lingo->pop();
755 	Datum d1 = g_lingo->pop();
756 
757 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
758 		d1.u.i = (d1.u.f == d2.u.f) ? 1 : 0;
759 		d1.type = INT;
760 	} else {
761 		d1.u.i = (d1.u.i == d2.u.i) ? 1 : 0;
762 	}
763 	g_lingo->push(d1);
764 }
765 
c_neq()766 void Lingo::c_neq() {
767 	Datum d2 = g_lingo->pop();
768 	Datum d1 = g_lingo->pop();
769 
770 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
771 		d1.u.i = (d1.u.f != d2.u.f) ? 1 : 0;
772 		d1.type = INT;
773 	} else {
774 		d1.u.i = (d1.u.i != d2.u.i) ? 1 : 0;
775 	}
776 	g_lingo->push(d1);
777 }
778 
c_gt()779 void Lingo::c_gt() {
780 	Datum d2 = g_lingo->pop();
781 	Datum d1 = g_lingo->pop();
782 
783 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
784 		d1.u.i = (d1.u.f > d2.u.f) ? 1 : 0;
785 		d1.type = INT;
786 	} else {
787 		d1.u.i = (d1.u.i > d2.u.i) ? 1 : 0;
788 	}
789 	g_lingo->push(d1);
790 }
791 
c_lt()792 void Lingo::c_lt() {
793 	Datum d2 = g_lingo->pop();
794 	Datum d1 = g_lingo->pop();
795 
796 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
797 		d1.u.i = (d1.u.f < d2.u.f) ? 1 : 0;
798 		d1.type = INT;
799 	} else {
800 		d1.u.i = (d1.u.i < d2.u.i) ? 1 : 0;
801 	}
802 	g_lingo->push(d1);
803 }
804 
c_ge()805 void Lingo::c_ge() {
806 	Datum d2 = g_lingo->pop();
807 	Datum d1 = g_lingo->pop();
808 
809 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
810 		d1.u.i = (d1.u.f >= d2.u.f) ? 1 : 0;
811 		d1.type = INT;
812 	} else {
813 		d1.u.i = (d1.u.i >= d2.u.i) ? 1 : 0;
814 	}
815 	g_lingo->push(d1);
816 }
817 
c_le()818 void Lingo::c_le() {
819 	Datum d2 = g_lingo->pop();
820 	Datum d1 = g_lingo->pop();
821 
822 	if (g_lingo->alignTypes(d1, d2) == FLOAT) {
823 		d1.u.i = (d1.u.f <= d2.u.f) ? 1 : 0;
824 		d1.type = INT;
825 	} else {
826 		d1.u.i = (d1.u.i <= d2.u.i) ? 1 : 0;
827 	}
828 	g_lingo->push(d1);
829 }
830 
c_repeatwhilecode(void)831 void Lingo::c_repeatwhilecode(void) {
832 	Datum d;
833 	int savepc = g_lingo->_pc;
834 
835 	uint body = READ_UINT32(&(*g_lingo->_currentScript)[savepc]);
836 	uint end =  READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]);
837 
838 	g_lingo->execute(savepc + 2);	/* condition */
839 	d = g_lingo->pop();
840 	d.toInt();
841 
842 	while (d.u.i) {
843 		g_lingo->execute(body + savepc - 1);	/* body */
844 		if (g_lingo->_returning)
845 			break;
846 
847 		if (g_lingo->_exitRepeat) {
848 			g_lingo->_exitRepeat = false;
849 			break;
850 		}
851 
852 		g_lingo->execute(savepc + 2);	/* condition */
853 		d = g_lingo->pop();
854 		d.toInt();
855 	}
856 
857 	if (!g_lingo->_returning)
858 		g_lingo->_pc = end + savepc - 1; /* next stmt */
859 }
860 
c_repeatwithcode(void)861 void Lingo::c_repeatwithcode(void) {
862 	Datum d;
863 	int savepc = g_lingo->_pc;
864 
865 	uint init = READ_UINT32(&(*g_lingo->_currentScript)[savepc]);
866 	uint finish =  READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]);
867 	uint body = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]);
868 	int inc = (int32)READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]);
869 	uint end =  READ_UINT32(&(*g_lingo->_currentScript)[savepc + 4]);
870 	Common::String countername((char *)&(*g_lingo->_currentScript)[savepc + 5]);
871 	Symbol *counter = g_lingo->lookupVar(countername.c_str());
872 
873 	if (counter->type == CASTREF) {
874 		error("Cast ref used as index: %s", countername.c_str());
875 	}
876 
877 	g_lingo->execute(init + savepc - 1);	/* condition */
878 	d = g_lingo->pop();
879 	d.toInt();
880 	counter->u.i = d.u.i;
881 	counter->type = INT;
882 
883 	while (true) {
884 		g_lingo->execute(body + savepc - 1);	/* body */
885 		if (g_lingo->_returning)
886 			break;
887 
888 		if (g_lingo->_exitRepeat) {
889 			g_lingo->_exitRepeat = false;
890 			break;
891 		}
892 
893 		counter->u.i += inc;
894 		g_lingo->execute(finish + savepc - 1);	/* condition */
895 		d = g_lingo->pop();
896 		d.toInt();
897 
898 		if (counter->u.i == d.u.i + inc)
899 			break;
900 	}
901 
902 	if (!g_lingo->_returning)
903 		g_lingo->_pc = end + savepc - 1; /* next stmt */
904 }
905 
c_exitRepeat(void)906 void Lingo::c_exitRepeat(void) {
907 	g_lingo->_exitRepeat = true;
908 }
909 
c_ifcode()910 void Lingo::c_ifcode() {
911 	Datum d;
912 	int savepc = g_lingo->_pc;	/* then part */
913 
914 	uint then =    READ_UINT32(&(*g_lingo->_currentScript)[savepc]);
915 	uint elsep =   READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]);
916 	uint end =     READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]);
917 	uint skipEnd = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]);
918 
919 	debugC(8, kDebugLingoExec, "executing cond (have to %s end)", skipEnd ? "skip" : "execute");
920 	g_lingo->execute(savepc + 4);	/* condition */
921 
922 	d = g_lingo->pop();
923 
924 	if (d.toInt()) {
925 		debugC(8, kDebugLingoExec, "executing then");
926 		g_lingo->execute(then + savepc - 1);
927 	} else if (elsep) { /* else part? */
928 		debugC(8, kDebugLingoExec, "executing else");
929 		g_lingo->execute(elsep + savepc - 1);
930 	}
931 
932 	if (!g_lingo->_returning && !skipEnd) {
933 		g_lingo->_pc = end + savepc - 1; /* next stmt */
934 		debugC(8, kDebugLingoExec, "executing end");
935 	} else {
936 		debugC(8, kDebugLingoExec, "Skipped end");
937 	}
938 }
939 
c_whencode()940 void Lingo::c_whencode() {
941 	Datum d;
942 	uint start = g_lingo->_pc;
943 	uint end = READ_UINT32(&(*g_lingo->_currentScript)[start]) + start - 1;
944 	Common::String eventname((char *)&(*g_lingo->_currentScript)[start + 1]);
945 
946 	start += g_lingo->calcStringAlignment(eventname.c_str()) + 1;
947 
948 	debugC(1, kDebugLingoExec, "c_whencode([%5d][%5d], %s)", start, end, eventname.c_str());
949 
950 	int entity = g_lingo->_currentEntityId;
951 	g_lingo->_currentEntityId = 0;
952 
953 	g_lingo->define(eventname, start, 0, NULL, end);
954 
955 	g_lingo->_currentEntityId = entity;
956 
957 	if (debugChannelSet(1, kDebugLingoExec)) {
958 		uint pc = start;
959 		while (pc <= end) {
960 			Common::String instr = g_lingo->decodeInstruction(pc, &pc);
961 			debugC(1, kDebugLingoExec, "[%5d] %s", pc, instr.c_str());
962 		}
963 	}
964 
965 	g_lingo->_pc = end;
966 }
967 
c_tellcode()968 void Lingo::c_tellcode() {
969 	warning("STUB: c_tellcode");
970 }
971 
972 
973 //************************
974 // Built-in functions
975 //************************
c_goto()976 void Lingo::c_goto() {
977 	Datum mode = g_lingo->pop();
978 	Datum frame, movie;
979 
980 	if (mode.u.i == 2 || mode.u.i == 3)
981 		movie = g_lingo->pop();
982 
983 	if (mode.u.i == 1 || mode.u.i == 3)
984 		frame = g_lingo->pop();
985 
986 	g_lingo->func_goto(frame, movie);
987 }
988 
c_gotoloop()989 void Lingo::c_gotoloop() {
990 	g_lingo->func_gotoloop();
991 }
992 
c_gotonext()993 void Lingo::c_gotonext() {
994 	g_lingo->func_gotonext();
995 }
996 
c_gotoprevious()997 void Lingo::c_gotoprevious() {
998 	g_lingo->func_gotoprevious();
999 }
1000 
c_play()1001 void Lingo::c_play() {
1002 	Datum mode = g_lingo->pop();
1003 	Datum frame, movie;
1004 
1005 	if (mode.u.i == 2 || mode.u.i == 3)
1006 		movie = g_lingo->pop();
1007 
1008 	if (mode.u.i == 1 || mode.u.i == 3)
1009 		frame = g_lingo->pop();
1010 
1011 	g_lingo->func_play(frame, movie);
1012 }
1013 
c_playdone()1014 void Lingo::c_playdone() {
1015 	g_lingo->func_playdone();
1016 }
1017 
c_call()1018 void Lingo::c_call() {
1019 	Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
1020 	g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str());
1021 
1022 	int nargs = READ_UINT32(&(*g_lingo->_currentScript)[g_lingo->_pc++]);
1023 
1024 	g_lingo->call(name, nargs);
1025 }
1026 
call(Common::String name,int nargs)1027 void Lingo::call(Common::String name, int nargs) {
1028 	bool dropArgs = false;
1029 
1030 	if (debugChannelSet(3, kDebugLingoExec))
1031 		printSTUBWithArglist(name.c_str(), nargs, "call:");
1032 
1033 	Symbol *sym = g_lingo->getHandler(name);
1034 
1035 	if (!g_lingo->_eventHandlerTypeIds.contains(name)) {
1036 		Symbol *s = g_lingo->lookupVar(name.c_str(), false);
1037 		if (s && s->type == OBJECT) {
1038 			debugC(3, kDebugLingoExec,  "Dereferencing object reference: %s to %s", name.c_str(), s->u.s->c_str());
1039 			name = *s->u.s;
1040 			sym = g_lingo->getHandler(name);
1041 		}
1042 	}
1043 
1044 	if (sym == NULL) {
1045 		warning("Call to undefined handler '%s'. Dropping %d stack items", name.c_str(), nargs);
1046 		dropArgs = true;
1047 	} else {
1048 		if ((sym->type == BLTIN || sym->type == FBLTIN || sym->type == RBLTIN)
1049 				&& sym->nargs != -1 && sym->nargs != nargs && sym->maxArgs != nargs) {
1050 			if (sym->nargs == sym->maxArgs)
1051 				warning("Incorrect number of arguments to handler '%s', expecting %d. Dropping %d stack items", name.c_str(), sym->nargs, nargs);
1052 			else
1053 				warning("Incorrect number of arguments to handler '%s', expecting %d or %d. Dropping %d stack items", name.c_str(), sym->nargs, sym->maxArgs, nargs);
1054 
1055 			dropArgs = true;
1056 		}
1057 	}
1058 
1059 	if (dropArgs) {
1060 		for (int i = 0; i < nargs; i++)
1061 			g_lingo->pop();
1062 
1063 		// Push dummy value
1064 		g_lingo->pushVoid();
1065 
1066 		return;
1067 	}
1068 
1069 	if (sym->nargs != -1 && sym->maxArgs < nargs) {
1070 		warning("Incorrect number of arguments for function %s (%d, expected %d to %d). Dropping extra %d",
1071 					name.c_str(), nargs, sym->nargs, sym->maxArgs, nargs - sym->nargs);
1072 		for (int i = 0; i < nargs - sym->maxArgs; i++)
1073 			g_lingo->pop();
1074 	}
1075 
1076 	if (sym->type == BLTIN || sym->type == FBLTIN || sym->type == RBLTIN) {
1077 		if (sym->u.bltin == b_factory) {
1078 			g_lingo->factoryCall(name, nargs);
1079 		} else {
1080 			int stackSize = _stack.size() - nargs;
1081 
1082 			(*sym->u.bltin)(nargs);
1083 
1084 			int stackNewSize = _stack.size();
1085 
1086 			if (sym->type == FBLTIN || sym->type == RBLTIN) {
1087 				if (stackNewSize - stackSize != 1)
1088 					warning("built-in function %s did not return value", name.c_str());
1089 			} else {
1090 				if (stackNewSize - stackSize != 0)
1091 					warning("built-in procedure %s returned extra %d values", name.c_str(), stackNewSize - stackSize);
1092 			}
1093 		}
1094 
1095 		return;
1096 	}
1097 
1098 	for (int i = nargs; i < sym->nargs; i++) {
1099 		Datum d;
1100 
1101 		d.u.s = NULL;
1102 		d.type = VOID;
1103 		g_lingo->push(d);
1104 	}
1105 
1106 	debugC(5, kDebugLingoExec, "Pushing frame %d", g_lingo->_callstack.size() + 1);
1107 	CFrame *fp = new CFrame;
1108 
1109 	fp->sp = sym;
1110 	fp->retpc = g_lingo->_pc;
1111 	fp->retscript = g_lingo->_currentScript;
1112 	fp->localvars = g_lingo->_localvars;
1113 
1114 	// Create new set of local variables
1115 	g_lingo->_localvars = new SymbolHash;
1116 
1117 	g_lingo->_callstack.push_back(fp);
1118 
1119 	g_lingo->_currentScript = sym->u.defn;
1120 	g_lingo->execute(0);
1121 
1122 	g_lingo->_returning = false;
1123 }
1124 
c_procret()1125 void Lingo::c_procret() {
1126 	if (!g_lingo->_callstack.size()) {
1127 		warning("c_procret: Call stack underflow");
1128 		g_lingo->_returning = true;
1129 		return;
1130 	}
1131 
1132 	debugC(5, kDebugLingoExec, "Popping frame %d", g_lingo->_callstack.size() + 1);
1133 
1134 	CFrame *fp = g_lingo->_callstack.back();
1135 	g_lingo->_callstack.pop_back();
1136 
1137 	g_lingo->_currentScript = fp->retscript;
1138 	g_lingo->_pc = fp->retpc;
1139 
1140 	g_lingo->cleanLocalVars();
1141 
1142 	// Restore local variables
1143 	g_lingo->_localvars = fp->localvars;
1144 
1145 	delete fp;
1146 
1147 	g_lingo->_returning = true;
1148 }
1149 
c_global()1150 void Lingo::c_global() {
1151 	Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
1152 
1153 	Symbol *s = g_lingo->lookupVar(name.c_str(), false);
1154 	if (s && !s->global) {
1155 		warning("Local variable %s declared as global", name.c_str());
1156 	}
1157 
1158 	s = g_lingo->lookupVar(name.c_str(), true, true);
1159 	s->global = true;
1160 
1161 	g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str());
1162 }
1163 
c_property()1164 void Lingo::c_property() {
1165 	Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
1166 
1167 	g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str());
1168 
1169 	warning("STUB: c_property()");
1170 }
1171 
c_instance()1172 void Lingo::c_instance() {
1173 	Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
1174 
1175 	warning("STUB: c_instance(%s)", name.c_str());
1176 
1177 	g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str());
1178 }
1179 
c_open()1180 void Lingo::c_open() {
1181 	Datum d2 = g_lingo->pop();
1182 	Datum d1 = g_lingo->pop();
1183 
1184 	d1.toString();
1185 	d2.toString();
1186 
1187 	warning("STUB: c_open(%s, %s)", d1.u.s->c_str(), d2.u.s->c_str());
1188 }
1189 
1190 }
1191