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