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 #include "agos/agos.h"
24 #include "agos/intern.h"
25 #include "agos/vga.h"
26
27 #include "common/endian.h"
28 #include "common/textconsole.h"
29
30 namespace AGOS {
31
32 enum {
33 kJmpClassNum = -1
34 };
35
36 #define OPCODE(x) _OPCODE(AGOSEngine_PN, x)
37
setupOpcodes()38 void AGOSEngine_PN::setupOpcodes() {
39 static const OpcodeEntryPN opcodes[] = {
40 /* 00 */
41 OPCODE(opn_opcode00),
42 OPCODE(opn_add),
43 OPCODE(opn_sub),
44 OPCODE(opn_mul),
45 /* 04 */
46 OPCODE(opn_div),
47 OPCODE(opn_opcode05),
48 OPCODE(opn_opcode06),
49 OPCODE(opn_opcode07),
50 /* 08 */
51 OPCODE(opn_opcode08),
52 OPCODE(opn_opcode09),
53 OPCODE(opn_opcode10),
54 OPCODE(opn_opcode11),
55 /* 12 */
56 OPCODE(opn_opcode12),
57 OPCODE(opn_opcode13),
58 OPCODE(opn_opcode14),
59 OPCODE(opn_opcode15),
60 /* 16 */
61 OPCODE(opn_opcode16),
62 OPCODE(opn_lt),
63 OPCODE(opn_gt),
64 OPCODE(opn_eq),
65 /* 20 */
66 OPCODE(opn_neq),
67 OPCODE(opn_opcode21),
68 OPCODE(opn_opcode22),
69 OPCODE(opn_opcode23),
70 /* 24 */
71 OPCODE(opn_opcode24),
72 OPCODE(opn_opcode25),
73 OPCODE(opn_opcode26),
74 OPCODE(opn_opcode27),
75 /* 28 */
76 OPCODE(opn_opcode28),
77 OPCODE(opn_opcode29),
78 OPCODE(opn_opcode30),
79 OPCODE(opn_opcode31),
80 /* 32 */
81 OPCODE(opn_opcode32),
82 OPCODE(opn_opcode33),
83 OPCODE(opn_opcode34),
84 OPCODE(opn_opcode35),
85 /* 36 */
86 OPCODE(opn_opcode36),
87 OPCODE(opn_opcode37),
88 OPCODE(opn_opcode38),
89 OPCODE(opn_opcode39),
90 /* 40 */
91 OPCODE(opn_opcode40),
92 OPCODE(opn_opcode41),
93 OPCODE(opn_opcode42),
94 OPCODE(opn_opcode43),
95 /* 44 */
96 OPCODE(opn_opcode44),
97 OPCODE(opn_opcode45),
98 OPCODE(opn_opcode46),
99 OPCODE(opn_opcode47),
100 /* 48 */
101 OPCODE(opn_opcode48),
102 OPCODE(opn_opcode49),
103 OPCODE(opn_opcode50),
104 OPCODE(opn_opcode51),
105 /* 52 */
106 OPCODE(opn_opcode52),
107 OPCODE(opn_opcode53),
108 OPCODE(opn_opcode54),
109 OPCODE(opn_opcode55),
110 /* 56 */
111 OPCODE(opn_opcode56),
112 OPCODE(opn_opcode57),
113 OPCODE(o_invalid),
114 OPCODE(o_invalid),
115 /* 60 */
116 OPCODE(o_invalid),
117 OPCODE(o_invalid),
118 OPCODE(opn_opcode62),
119 OPCODE(opn_opcode63),
120 };
121
122 _opcodesPN = opcodes;
123 _numOpcodes = 64;
124 }
125
executeOpcode(int opcode)126 void AGOSEngine_PN::executeOpcode(int opcode) {
127 OpcodeProcPN op = _opcodesPN[opcode].proc;
128 (this->*op)();
129 }
130
readfromline()131 int AGOSEngine_PN::readfromline() {
132 if (!_linct)
133 error("readfromline: Internal Error - Line Over-run");
134 _linct--;
135 return *_workptr++;
136 }
137
138 // -----------------------------------------------------------------------
139 // Personal Nightmare Opcodes
140 // -----------------------------------------------------------------------
141
opn_opcode00()142 void AGOSEngine_PN::opn_opcode00() {
143 uint8 *str = _workptr;
144 varval();
145 writeval(str, varval());
146 setScriptReturn(true);
147 }
148
opn_add()149 void AGOSEngine_PN::opn_add() {
150 uint8 *str = _workptr;
151 int32 sp = varval() + varval();
152 _variableArray[12] = sp % 65536;
153 _variableArray[13] = sp / 65536;
154 if (sp > 65535)
155 sp = 65535;
156 writeval(str, (int)sp);
157 setScriptReturn(true);
158 }
159
opn_sub()160 void AGOSEngine_PN::opn_sub() {
161 uint8 *str = _workptr;
162 int32 sp = varval();
163 sp -= varval();
164 _variableArray[12] = sp % 65536;
165 _variableArray[13] = sp / 65536;
166 if (sp < 0)
167 sp = 0;
168 writeval(str, (int)sp);
169 setScriptReturn(true);
170 }
171
opn_mul()172 void AGOSEngine_PN::opn_mul() {
173 uint8 *str = _workptr;
174 int32 sp = varval() * varval();
175 _variableArray[12] = sp % 65536;
176 _variableArray[13] = sp / 65536;
177 if (sp > 65535)
178 sp = 65535;
179 writeval(str, (int)sp);
180 setScriptReturn(true);
181 }
182
opn_div()183 void AGOSEngine_PN::opn_div() {
184 uint8 *str = _workptr;
185 int32 sp = varval();
186 int32 sp2 = varval();
187 if (sp2 == 0)
188 error("opn_div: Division by 0");
189 sp = sp / sp2;
190 _variableArray[12] = sp % 65536;
191 _variableArray[13] = sp / 65536;
192 writeval(str, (int)sp);
193 setScriptReturn(true);
194 }
195
opn_opcode05()196 void AGOSEngine_PN::opn_opcode05() {
197 pcf((uint8)'\n');
198 setScriptReturn(true);
199 }
200
opn_opcode06()201 void AGOSEngine_PN::opn_opcode06() {
202 pmesd(varval());
203 setScriptReturn(true);
204 }
205
opn_opcode07()206 void AGOSEngine_PN::opn_opcode07() {
207 int32 sp = varval();
208 plocd((int)sp, varval());
209 setScriptReturn(true);
210 }
211
opn_opcode08()212 void AGOSEngine_PN::opn_opcode08() {
213 int32 sp = varval();
214 pobjd((int)sp, varval());
215 setScriptReturn(true);
216 }
217
opn_opcode09()218 void AGOSEngine_PN::opn_opcode09() {
219 pmesd(varval());
220 pcf((uint8)'\n');
221 setScriptReturn(true);
222 }
223
opn_opcode10()224 void AGOSEngine_PN::opn_opcode10() {
225 int32 sp = varval();
226 plocd((int)sp, varval());
227 pcf((uint8)'\n');
228 setScriptReturn(true);
229 }
230
opn_opcode11()231 void AGOSEngine_PN::opn_opcode11() {
232 int32 sp = varval();
233 pobjd((int)sp, varval());
234 setScriptReturn(true);
235 }
236
opn_opcode12()237 void AGOSEngine_PN::opn_opcode12() {
238 char bf[8];
239 int a = 0;
240 sprintf(bf,"%d", varval());
241 while (bf[a])
242 pcf(bf[a++]);
243 setScriptReturn(true);
244 }
245
opn_opcode13()246 void AGOSEngine_PN::opn_opcode13() {
247 char bf[8];
248 int a = 0;
249 sprintf(bf,"%d", varval());
250 while (bf[a])
251 pcf(bf[a++]);
252 pcf((uint8)'\n');
253 setScriptReturn(true);
254 }
255
opn_opcode14()256 void AGOSEngine_PN::opn_opcode14() {
257 clearWindow(_windowArray[_curWindow]);
258 pcf((uint8)255);
259 setScriptReturn(true);
260 }
261
opn_opcode15()262 void AGOSEngine_PN::opn_opcode15() {
263 int32 x = varval();
264 if ((x < 0) || (x > 4))
265 x = 0;
266
267 pcf((unsigned char)254);
268 _curWindow = x;
269 _xofs = (8 * _windowArray[_curWindow]->textLength) / 6 + 1;
270 setScriptReturn(true);
271 }
272
opn_opcode16()273 void AGOSEngine_PN::opn_opcode16() {
274 int32 sp = varval();
275 setScriptReturn((sp >= 0 && sp <= 4));
276 }
277
opn_lt()278 void AGOSEngine_PN::opn_lt() {
279 int16 v1 = varval();
280 int16 v2 = varval();
281 setScriptReturn(v1 < v2);
282 }
283
opn_gt()284 void AGOSEngine_PN::opn_gt() {
285 int16 v1 = varval();
286 int16 v2 = varval();
287 setScriptReturn(v1 > v2);
288 }
289
opn_eq()290 void AGOSEngine_PN::opn_eq() {
291 int16 v1 = varval();
292 int16 v2 = varval();
293 setScriptReturn(v1 == v2);
294 }
295
opn_neq()296 void AGOSEngine_PN::opn_neq() {
297 int16 v1 = varval();
298 int16 v2 = varval();
299 setScriptReturn(v1 != v2);
300 }
301
opn_opcode21()302 void AGOSEngine_PN::opn_opcode21() {
303 setposition(_procnum, varval());
304 setScriptReturn(true);
305 }
306
opn_opcode22()307 void AGOSEngine_PN::opn_opcode22() {
308 int pf[8];
309 int n = varval();
310 funcentry(pf, n);
311 funccpy(pf);
312 setposition(n, 0);
313 setScriptReturn(true);
314 }
315
opn_opcode23()316 void AGOSEngine_PN::opn_opcode23() {
317 setScriptReturn(actCallD(varval()));
318 }
319
opn_opcode24()320 void AGOSEngine_PN::opn_opcode24() {
321 popstack(kJmpClassNum);
322 // Jump back to the last doline, which will return 2-1=1.
323 // That value then is returned to actCallD, which once again
324 // returns it. In the end, this amounts to a setScriptReturn(true)
325 // (but possibly in a different level than the current one).
326 _dolineReturnVal = 2;
327 _tagOfActiveDoline = _stackbase->tagOfParentDoline;
328 }
329
opn_opcode25()330 void AGOSEngine_PN::opn_opcode25() {
331 popstack(kJmpClassNum);
332 // Jump back to the last doline, which will return 1-1=0.
333 // That value then is returned to actCallD, which once again
334 // returns it. In the end, this amounts to a setScriptReturn(false)
335 // (but possibly in a different level than the current one).
336 _dolineReturnVal = 1;
337 _tagOfActiveDoline = _stackbase->tagOfParentDoline;
338 }
339
opn_opcode26()340 void AGOSEngine_PN::opn_opcode26() {
341 while ((_stackbase != NULL) && (_stackbase->classnum != kJmpClassNum))
342 dumpstack();
343 dumpstack();
344 setScriptReturn(true);
345 }
346
opn_opcode27()347 void AGOSEngine_PN::opn_opcode27() {
348 quitGame();
349 // Make sure the quit event is processed immediately.
350 delay(0);
351 }
352
opn_opcode28()353 void AGOSEngine_PN::opn_opcode28() {
354 addstack(varval());
355 _stackbase->tagOfParentDoline = _tagOfActiveDoline;
356 setScriptReturn(false);
357 }
358
opn_opcode29()359 void AGOSEngine_PN::opn_opcode29() {
360 popstack(varval());
361 // Jump back to the last doline indicated by the top stackframe.
362 // The -1 tells it to simply go on with its business.
363 _dolineReturnVal = -1;
364 _tagOfActiveDoline = _stackbase->tagOfParentDoline;
365 }
366
opn_opcode30()367 void AGOSEngine_PN::opn_opcode30() {
368 _variableArray[1] = varval();
369 setScriptReturn(true);
370 }
371
opn_opcode31()372 void AGOSEngine_PN::opn_opcode31() {
373 int a, slot = 0;
374 Common::String bf;
375
376 if ((a = varval()) > 2) {
377 setScriptReturn(false);
378 return;
379 }
380
381 switch (a) {
382 case 0:
383 getFilename();
384 slot = matchSaveGame(_saveFile, countSaveGames());
385 bf = genSaveName(slot);
386 break;
387 case 1:
388 bf = "pn.sav";
389 break;
390 case 2:
391 // NOTE: Is this case ever used?
392 error("opn_opcode31: case 2");
393 break;
394 }
395
396 if (slot == -1) {
397 setScriptReturn(false);
398 } else {
399 a = loadFile(bf);
400 if (a)
401 setScriptReturn(badload(a));
402 else
403 setScriptReturn(true);
404 }
405 }
406
opn_opcode32()407 void AGOSEngine_PN::opn_opcode32() {
408 Common::String bf;
409 int a, slot;
410
411 a = varval();
412 if (a > 2) {
413 setScriptReturn(true);
414 return;
415 }
416
417 uint16 curSlot = countSaveGames();
418 switch (a) {
419 case 0:
420 getFilename();
421 slot = matchSaveGame(_saveFile, curSlot);
422 if (slot != -1)
423 bf = genSaveName(slot);
424 else
425 bf = genSaveName(curSlot);
426 break;
427 case 1:
428 bf = "pn.sav";
429 break;
430 case 2:
431 // NOTE: Is this case ever used?
432 error("opn_opcode32: case 2");
433 break;
434 }
435
436 a = saveFile(bf);
437 setScriptReturn(a);
438 }
439
opn_opcode33()440 void AGOSEngine_PN::opn_opcode33() {
441 setScriptReturn((varval() < 3) ? 1 : 0);
442 }
443
opn_opcode34()444 void AGOSEngine_PN::opn_opcode34() {
445 uint16 msgNum1, msgNum2;
446 varval();
447 getResponse((int)_variableArray[166], (int)_variableArray[167], msgNum1, msgNum2);
448 _variableArray[168]= msgNum1;
449 _variableArray[169]= msgNum2;
450 setScriptReturn(true);
451 }
452
opn_opcode35()453 void AGOSEngine_PN::opn_opcode35() {
454 int a;
455 uint8 *sav = _workptr;
456 varval();
457 a = varval();
458 if ((a = gvwrd((uint8 *)_wordcp, a)) == -1) {
459 setScriptReturn(false);
460 return;
461 }
462
463 writeval(sav, a);
464 setScriptReturn(true);
465 }
466
opn_opcode36()467 void AGOSEngine_PN::opn_opcode36() {
468 for (int i = 0; i < _dataBase[57] + 1; ++i)
469 _wordcp[i] = 0;
470 if (Common::isSpace(*_inpp))
471 while ((*_inpp) && (Common::isSpace(*_inpp)))
472 _inpp++;
473 if (*_inpp == 0) {
474 setScriptReturn(false);
475 return;
476 }
477 _curwrdptr = _inpp;
478 _wordcp[0] = *_inpp++;
479 if ((_wordcp[0] == '.') || (_wordcp[0] == ',') || (_wordcp[0] == '"')) {
480 setScriptReturn(true);
481 return;
482 }
483
484 int ct = 1;
485 while ((*_inpp != '.') && (*_inpp != ',') && (!Common::isSpace(*_inpp)) && (*_inpp != '\0') &&
486 (*_inpp!='"')) {
487 if (ct < _dataBase[57])
488 _wordcp[ct++] = *_inpp;
489 _inpp++;
490 }
491 setScriptReturn(true);
492 }
493
opn_opcode37()494 void AGOSEngine_PN::opn_opcode37() {
495 _curwrdptr = NULL;
496
497 _inputReady = true;
498 interact(_inputline, 49);
499
500 if ((_inpp = strchr(_inputline,'\n')) != NULL)
501 *_inpp = '\0';
502 _inpp = _inputline;
503 setScriptReturn(true);
504 }
505
opn_opcode38()506 void AGOSEngine_PN::opn_opcode38() {
507 _noScanFlag = 1;
508 clearInputLine();
509 writeval(_workptr, _keyPressed.ascii);
510 _keyPressed.reset();
511 _noScanFlag = 0;
512 varval();
513 setScriptReturn(true);
514 }
515
opn_opcode39()516 void AGOSEngine_PN::opn_opcode39() {
517 pcf((uint8)varval());
518 setScriptReturn(true);
519 }
520
opn_opcode40()521 void AGOSEngine_PN::opn_opcode40() {
522 int a = doaction();
523 if (_dolineReturnVal != 0)
524 return;
525 int b = doaction();
526 setScriptReturn(a | b);
527 }
528
opn_opcode41()529 void AGOSEngine_PN::opn_opcode41() {
530 int a = doaction();
531 if (_dolineReturnVal != 0)
532 return;
533 int b = doaction();
534 setScriptReturn(a & b);
535 }
536
opn_opcode42()537 void AGOSEngine_PN::opn_opcode42() {
538 int a = doaction();
539 if (_dolineReturnVal != 0)
540 return;
541 int b = doaction();
542 setScriptReturn(a ^ b);
543 }
544
opn_opcode43()545 void AGOSEngine_PN::opn_opcode43() {
546 int a = doaction();
547 setScriptReturn(!a);
548 }
549
opn_opcode44()550 void AGOSEngine_PN::opn_opcode44() {
551 pcf((uint8)254);
552 setScriptReturn(true);
553 }
554
opn_opcode45()555 void AGOSEngine_PN::opn_opcode45() {
556 uint8 *myptr;
557 int x;
558
559 if (_havinit == 0) {
560 _seed = (int16)getTime();
561 _havinit = 1;
562 }
563 _seed = 1 + (75 * (_seed + 1) - 1) % 65537;
564 myptr = _workptr;
565 varval();
566 x = varval();
567 if (x == 0)
568 error("Illegal range specified for RANDOM");
569 writeval(myptr, (_seed % x));
570 setScriptReturn(true);
571 }
572
opn_opcode46()573 void AGOSEngine_PN::opn_opcode46() {
574 char *x = _curwrdptr;
575 if (x == NULL) {
576 setScriptReturn(true);
577 return;
578 }
579 pcf(*x);
580 if ((*x == '.') || (*x == '"') || (*x == ',')) {
581 setScriptReturn(true);
582 return;
583 }
584 x++;
585 while ((*x != '.') && (*x != ',') && (*x != '"') && (!Common::isSpace(*x)) && (*x != '\0'))
586 pcf(*x++);
587 setScriptReturn(true);
588 }
589
opn_opcode47()590 void AGOSEngine_PN::opn_opcode47() {
591 pmesd(varval() * 256 + varval());
592 setScriptReturn(true);
593 }
594
opn_opcode48()595 void AGOSEngine_PN::opn_opcode48() {
596 pmesd(varval() * 256 + varval());
597 pcf((uint8)'\n');
598 setScriptReturn(true);
599 }
600
opn_opcode49()601 void AGOSEngine_PN::opn_opcode49() {
602 setScriptReturn(findentry());
603 }
604
opn_opcode50()605 void AGOSEngine_PN::opn_opcode50() {
606 _fnst = 0;
607 setScriptReturn(findset());
608 }
609
opn_opcode51()610 void AGOSEngine_PN::opn_opcode51() {
611 _fnst = varval();
612 setScriptReturn(findset());
613 }
614
opn_opcode52()615 void AGOSEngine_PN::opn_opcode52() {
616 int32 mode = varval();
617 if (mode == 1) {
618 setWindowImage(mode, varval(), true);
619 } else {
620 setWindowImageEx(mode, varval());
621 }
622
623 setScriptReturn(true);
624 }
625
opn_opcode53()626 void AGOSEngine_PN::opn_opcode53() {
627 vc27_resetSprite();
628 setScriptReturn(true);
629 }
630
opn_opcode54()631 void AGOSEngine_PN::opn_opcode54() {
632 stopAnimate(varval());
633 setScriptReturn(true);
634 }
635
opn_opcode55()636 void AGOSEngine_PN::opn_opcode55() {
637 varval();
638 varval();
639 varval();
640 setScriptReturn(true);
641 }
642
opn_opcode56()643 void AGOSEngine_PN::opn_opcode56() {
644 varval();
645 varval();
646 varval();
647 setScriptReturn(true);
648 }
649
opn_opcode57()650 void AGOSEngine_PN::opn_opcode57() {
651 uint16 windowNum = varval();
652 uint16 vgaSpriteId = varval();
653 int16 x = varval();
654 int16 y = varval();
655 uint16 palette = varval();
656
657 _videoLockOut |= 0x40;
658 animate(windowNum, 0, vgaSpriteId, x, y, palette);
659 _videoLockOut &= ~0x40;
660
661 setScriptReturn(true);
662 }
663
opn_opcode62()664 void AGOSEngine_PN::opn_opcode62() {
665 int32 zoneNum = varval();
666
667 _videoLockOut |= 0x80;
668
669 vc29_stopAllSounds();
670
671 _hitCalled = 0;
672 _oneClick = 0;
673
674 loadZone(zoneNum);
675
676 setWindowImage(2, 2);
677
678 _copyScnFlag = 0;
679 _vgaSpriteChanged = 0;
680
681 _videoLockOut &= ~0x80;
682
683 setScriptReturn(true);
684 }
685
opn_opcode63()686 void AGOSEngine_PN::opn_opcode63() {
687 int a = readfromline();
688 switch (a) {
689 case 65:
690 setScriptReturn(inventoryOn(varval()));
691 break;
692 case 64:
693 setScriptReturn((_videoLockOut & 0x10) != 0);
694 break;
695 case 63:
696 setScriptReturn(inventoryOff());
697 break;
698 default:
699 error("opn_opcode63: unknown code %d", a);
700 }
701 }
702
inventoryOn(int val)703 int AGOSEngine_PN::inventoryOn(int val) {
704 writeVariable(210, val);
705 if (_videoLockOut & 0x10) {
706 iconPage();
707 } else {
708 _videoLockOut |= 0x10;
709 _hitAreaList = _invHitAreas;
710
711 _windowArray[2]->textColor = 0;
712 windowPutChar(_windowArray[2], 13);
713
714 clearVideoWindow(4, 0);
715 drawIconHitBar();
716
717 _objects = _variableArray[211];
718 _objectCountS = -1;
719 iconPage();
720 }
721 return 1;
722 }
723
inventoryOff()724 int AGOSEngine_PN::inventoryOff() {
725 if (_videoLockOut & 0x10) {
726 _windowArray[2]->textColor = 15;
727
728 restoreBlock(48, 2, 272, 130);
729
730 _hitAreaList = _hitAreas;
731 _videoLockOut &= ~0x10;
732 _vgaSpriteChanged++;
733 }
734 return 1;
735 }
736
737 // -----------------------------------------------------------------------
738 // Personal Nightmare Script Code
739 // -----------------------------------------------------------------------
740
741
bitextract(uint32 ptr,int offs)742 int AGOSEngine_PN::bitextract(uint32 ptr, int offs) {
743 const byte mask = 0x80 >> (offs % 8);
744 return ((mask & _dataBase[ptr + offs / 8]) != 0);
745 }
746
getptr(uint32 pos)747 uint16 AGOSEngine_PN::getptr(uint32 pos) {
748 if (pos > _dataBaseSize)
749 error("getptr: Read beyond EOF (%d)", pos);
750 return (int)READ_LE_UINT16(_dataBase + pos);
751 }
752
getlong(uint32 pos)753 uint32 AGOSEngine_PN::getlong(uint32 pos) {
754 // Only actually reads 24bit though
755 if (pos > _dataBaseSize)
756 error("getlong: Read beyond EOF (%d)", pos);
757 return (uint32)READ_LE_UINT24(_dataBase + pos);
758 }
759
varval()760 int AGOSEngine_PN::varval() {
761 int a;
762 int b;
763
764 a = readfromline();
765 if (a < 247) {
766 return a;
767 }
768
769 switch (a) {
770 case 249:
771 b = readfromline();
772 return (int)(b + 256 * readfromline());
773 case 250:
774 return readfromline();
775 case 251:
776 return (int)_variableArray[varval()];
777 case 252:
778 b = varval();
779 return (int)_dataBase[_quickptr[0] + b * _quickshort[0] + varval()];
780 case 254:
781 b = varval();
782 return (int)_dataBase[_quickptr[3] + b * _quickshort[2] + varval()];
783 case 247:
784 b = varval();
785 return (int)getptr(_quickptr[11] + (b * _quickshort[4]) + (2 * varval()));
786 case 248:
787 b = varval();
788 return (int)getptr(_quickptr[12] + (b * _quickshort[5]) + (2 * varval()));
789 case 253:
790 b = varval();
791 return bitextract((int32)_quickptr[1] + b * _quickshort[1], varval());
792 case 255:
793 b = varval();
794 return bitextract((int32)_quickptr[4] + b * _quickshort[3], varval());
795 default:
796 error("VARVAL : Illegal code %d encountered", a);
797 }
798 }
799
writeval(uint8 * ptr,int val)800 void AGOSEngine_PN::writeval(uint8 *ptr, int val) {
801 uint8 *savpt = _workptr;
802 int lsav = _linct, a, b, x;
803 _workptr = ptr;
804 _linct = 255;
805
806 if ((a = readfromline()) < 247)
807 error("writeval: Write to constant (%d)", a);
808
809 switch (a) {
810 case 249:
811 error("writeval: Write to constant (%d)", a);
812 break;
813 case 250:
814 error("writeval: Write to constant (%d)", a);
815 break;
816 case 251:
817 _variableArray[varval()] = val;
818 break;
819 case 252:
820 b = varval();
821 _dataBase[_quickptr[0] + b * _quickshort[0] + varval()] = val;
822 break;
823 case 254:
824 b = varval();
825 _dataBase[_quickptr[3] + b * _quickshort[2] + varval()] = val;
826 break;
827 case 247:
828 b = varval();
829 x = _quickptr[11] + b * _quickshort[4] + varval() * 2;
830 WRITE_LE_UINT16(_dataBase + x, val);
831 break;
832 case 248:
833 b = varval();
834 x = _quickptr[12] + b * _quickshort[5] + varval() * 2;
835 WRITE_LE_UINT16(_dataBase + x, val);
836 break;
837 case 253:
838 b = varval();
839 setbitf((uint32)_quickptr[1] + b * _quickshort[1], varval(), val);
840 break;
841 case 255:
842 b = varval();
843 setbitf((uint32)_quickptr[4] + b * _quickshort[3], varval(), val);
844 break;
845 default:
846 error("WRITEVAL : undefined evaluation %d", a);
847 }
848 _linct = lsav;
849 _workptr = savpt;
850 }
851
setbitf(uint32 ptr,int offs,int val)852 void AGOSEngine_PN::setbitf(uint32 ptr, int offs, int val) {
853 ptr += offs / 8;
854 const byte mask = 0x80 >> (offs % 8);
855 if (val != 0)
856 _dataBase[ptr] |= mask;
857 else
858 _dataBase[ptr] &= ~mask;
859 }
860
actCallD(int n)861 int AGOSEngine_PN::actCallD(int n) {
862 int pf[8];
863 funcentry(pf, n);
864 addstack(kJmpClassNum);
865 funccpy(pf);
866 setposition(n, 0);
867 return doline(1);
868 }
869
doaction()870 int AGOSEngine_PN::doaction() {
871 if (_linct == 0)
872 return 0;
873
874 _opcode = readfromline();
875
876 if (_opcode > 63) {
877 return (actCallD(_opcode - 64));
878 }
879
880 setScriptReturn(0);
881 executeOpcode(_opcode);
882 delay(0);
883
884 return getScriptReturn();
885 }
886
doline(int needsave)887 int AGOSEngine_PN::doline(int needsave) {
888 assert(!_stackbase == !needsave);
889
890 int x;
891 int myTag = ++_tagOfActiveDoline; // Obtain a unique tag for this doline invocation
892 _dolineReturnVal = 0;
893
894 if (_stackbase && needsave)
895 _stackbase->tagOfParentDoline = myTag;
896
897 do {
898 _linct = ((*_linebase) & 127) - 1;
899 _workptr = _linebase + 1;
900 if (*_linebase > 127) {
901 x = varval();
902 if (x != (int)_variableArray[1])
903 goto skipln;
904 }
905
906 do {
907 x = doaction();
908
909 if (_dolineReturnVal != 0) {
910 if (_tagOfActiveDoline != myTag)
911 return 0;
912
913 x = _dolineReturnVal;
914 _dolineReturnVal = 0;
915
916 if (x > 0) {
917 if (x != 3)
918 dumpstack();
919 // Restore the active jmpbuf to its previous value,
920 // then return _dolineReturnVal-1 (will be 2-1=1 or 1-1=0).
921 _tagOfActiveDoline = myTag - 1;
922 return (x - 1);
923 }
924 }
925
926 } while (x && !shouldQuit());
927
928 skipln:
929 _linebase += 127 & *_linebase;
930 _linembr++;
931 } while (!shouldQuit());
932
933 return 0;
934 }
935
findentry()936 int AGOSEngine_PN::findentry() {
937 int stepmt;
938 int curObj = 0;
939 uint32 ofs = _quickptr[11];
940 int c1, c2;
941
942 c1 = varval();
943 c2 = varval();
944 stepmt = _quickshort[4];
945
946 while (curObj < _quickshort[6]) {
947 if (((c1 == 255) || (c1 == getptr(ofs))) &&
948 (c2 == getptr(ofs + 2))) {
949 _variableArray[23] = curObj;
950 return 1;
951 }
952 curObj++;
953 ofs += stepmt;
954 }
955 return 0;
956 }
957
findset()958 int AGOSEngine_PN::findset() {
959 int curObj = _fnst;
960 int c1, c2, c3, c4;
961 int stepmt = _quickshort[4];
962 uint32 ofs = _quickptr[11] + stepmt * curObj;
963 c1 = varval();
964 c2 = varval();
965 c3 = varval();
966 c4 = varval();
967 while (curObj < _quickshort[6]) {
968 if (((c1 ==255) || (c1 == getptr(ofs))) &&
969 ((c2 == 255) || (c2 == getptr(ofs + 2))) &&
970 ((c3 == 255) || (c3 == getptr(ofs + 4))) &&
971 ((c4 == 255) || (c4 == getptr(ofs + 6)))) {
972 _variableArray[23] = curObj;
973 _fnst = curObj + 1;
974 return 1;
975 }
976 curObj++;
977 ofs += stepmt;
978 }
979 return 0;
980 }
981
funccpy(int * store)982 void AGOSEngine_PN::funccpy(int *store) {
983 for (int i = 24; i < 32; ++i) {
984 _variableArray[i] = *store++;
985 }
986 }
987
funcentry(int * store,int procn)988 void AGOSEngine_PN::funcentry(int *store, int procn) {
989 int numParams = _dataBase[getlong(_quickptr[6] + 3 * procn)];
990 for (int i = 0; i < numParams; ++i) {
991 *store++ = varval();
992 }
993 }
994
gvwrd(uint8 * wptr,int mask)995 int AGOSEngine_PN::gvwrd(uint8 *wptr, int mask) {
996 int val = 0, code = 0, q = _dataBase[57];
997 uint8 *vocbase = _dataBase + getlong(15);
998 while (*vocbase != 255) {
999 if (*vocbase < 0x80) {
1000 val = vocbase[q] + 256 * vocbase[q + 1];
1001 code = vocbase[q + 2];
1002 }
1003 if (wrdmatch(vocbase, mask, wptr, code))
1004 return val;
1005 vocbase += (*vocbase > 127) ? q : q + 3;
1006 }
1007 return -1;
1008 }
1009
1010
setposition(int process,int line)1011 int AGOSEngine_PN::setposition(int process, int line) {
1012 uint8 *ourptr;
1013 int np;
1014 int ct;
1015
1016 ourptr = _dataBase + getlong(_quickptr[6] + 3 * process);
1017 np = *ourptr++;
1018 for (ct = 0; ct < line; ++ct) {
1019 ourptr += (127 & *ourptr);
1020 }
1021
1022 while (true) {
1023 _linebase = ourptr;
1024 _linct = (127 & *ourptr) - 1;
1025 if (*ourptr++ <= 127)
1026 break;
1027
1028 ct = varval();
1029 if (ct == (int)_variableArray[1])
1030 break;
1031
1032 ourptr += _linct - 1;
1033 line++;
1034 }
1035
1036 _linembr = line;
1037 _procnum = process;
1038 _variableArray[0] = process;
1039 _workptr = ourptr;
1040 return np;
1041 }
1042
wrdmatch(uint8 * word1,int mask1,uint8 * word2,int mask2)1043 int AGOSEngine_PN::wrdmatch(uint8 *word1, int mask1, uint8 *word2, int mask2) {
1044 uint8 sv;
1045
1046 if ((mask1 & mask2) == 0)
1047 return 0;
1048
1049 sv = *word1;
1050 *word1 &= 127;
1051 if (scumm_strnicmp((const char *)word1, (const char *)word2, _dataBase[57])) {
1052 *word1 = sv;
1053 return 0;
1054 }
1055 *word1 = sv;
1056 return 1;
1057 }
1058
1059 // -----------------------------------------------------------------------
1060 // Personal Nightmare Stack Code
1061 // -----------------------------------------------------------------------
1062
addstack(int type)1063 void AGOSEngine_PN::addstack(int type) {
1064 StackFrame *a;
1065 int i;
1066
1067 a = (StackFrame *)calloc(1, sizeof(StackFrame));
1068 if (a == NULL)
1069 error("addstack: Out of memory - stack overflow");
1070
1071 a->nextframe = _stackbase;
1072 _stackbase = a;
1073
1074 for (i = 0; i < 6; ++i)
1075 a->flag[i] = _variableArray[i];
1076 for (i = 0; i < 8; ++i)
1077 a->param[i] = _variableArray[24 + i];
1078 a->classnum = type;
1079 a->ll = _linct;
1080 a->linenum = _linembr;
1081 a->linpos = _workptr;
1082 a->lbase = _linebase;
1083 a->process = _procnum;
1084 }
1085
dumpstack()1086 void AGOSEngine_PN::dumpstack() {
1087 StackFrame *a;
1088
1089 if (_stackbase == NULL)
1090 error("dumpstack: Stack underflow or unknown longjmp");
1091
1092 a = _stackbase->nextframe;
1093 free((char *)_stackbase);
1094 _stackbase = a;
1095 }
1096
popstack(int type)1097 void AGOSEngine_PN::popstack(int type) {
1098 int i = 0;
1099
1100 while ((_stackbase != NULL) && (_stackbase->classnum != type)) {
1101 dumpstack();
1102 ++i;
1103 }
1104
1105 if (_stackbase == NULL)
1106 error("popstack: Stack underflow or unknown longjmp");
1107
1108 _linct = _stackbase->ll;
1109 _linebase = _stackbase->lbase;
1110 _workptr = _stackbase->linpos;
1111 _procnum = _stackbase->process;
1112 _linembr = _stackbase->linenum;
1113 for (i = 0; i < 6; ++i)
1114 _variableArray[i] = _stackbase->flag[i];
1115 for (i = 0; i < 8; ++i)
1116 _variableArray[24 + i] = _stackbase->param[i];
1117 }
1118
1119 } // End of namespace AGOS
1120