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 default:
395 break;
396 }
397
398 if (slot == -1) {
399 setScriptReturn(false);
400 } else {
401 a = loadFile(bf);
402 if (a)
403 setScriptReturn(badload(a));
404 else
405 setScriptReturn(true);
406 }
407 }
408
opn_opcode32()409 void AGOSEngine_PN::opn_opcode32() {
410 Common::String bf;
411 int a, slot;
412
413 a = varval();
414 if (a > 2) {
415 setScriptReturn(true);
416 return;
417 }
418
419 uint16 curSlot = countSaveGames();
420 switch (a) {
421 case 0:
422 getFilename();
423 slot = matchSaveGame(_saveFile, curSlot);
424 if (slot != -1)
425 bf = genSaveName(slot);
426 else
427 bf = genSaveName(curSlot);
428 break;
429 case 1:
430 bf = "pn.sav";
431 break;
432 case 2:
433 // NOTE: Is this case ever used?
434 error("opn_opcode32: case 2");
435 break;
436 default:
437 break;
438 }
439
440 a = saveFile(bf);
441 setScriptReturn(a);
442 }
443
opn_opcode33()444 void AGOSEngine_PN::opn_opcode33() {
445 setScriptReturn((varval() < 3) ? 1 : 0);
446 }
447
opn_opcode34()448 void AGOSEngine_PN::opn_opcode34() {
449 uint16 msgNum1, msgNum2;
450 varval();
451 getResponse((int)_variableArray[166], (int)_variableArray[167], msgNum1, msgNum2);
452 _variableArray[168]= msgNum1;
453 _variableArray[169]= msgNum2;
454 setScriptReturn(true);
455 }
456
opn_opcode35()457 void AGOSEngine_PN::opn_opcode35() {
458 int a;
459 uint8 *sav = _workptr;
460 varval();
461 a = varval();
462 if ((a = gvwrd((uint8 *)_wordcp, a)) == -1) {
463 setScriptReturn(false);
464 return;
465 }
466
467 writeval(sav, a);
468 setScriptReturn(true);
469 }
470
opn_opcode36()471 void AGOSEngine_PN::opn_opcode36() {
472 for (int i = 0; i < _dataBase[57] + 1; ++i)
473 _wordcp[i] = 0;
474 if (Common::isSpace(*_inpp))
475 while ((*_inpp) && (Common::isSpace(*_inpp)))
476 _inpp++;
477 if (*_inpp == 0) {
478 setScriptReturn(false);
479 return;
480 }
481 _curwrdptr = _inpp;
482 _wordcp[0] = *_inpp++;
483 if ((_wordcp[0] == '.') || (_wordcp[0] == ',') || (_wordcp[0] == '"')) {
484 setScriptReturn(true);
485 return;
486 }
487
488 int ct = 1;
489 while ((*_inpp != '.') && (*_inpp != ',') && (!Common::isSpace(*_inpp)) && (*_inpp != '\0') &&
490 (*_inpp!='"')) {
491 if (ct < _dataBase[57])
492 _wordcp[ct++] = *_inpp;
493 _inpp++;
494 }
495 setScriptReturn(true);
496 }
497
opn_opcode37()498 void AGOSEngine_PN::opn_opcode37() {
499 _curwrdptr = NULL;
500
501 _inputReady = true;
502 interact(_inputline, 49);
503
504 if ((_inpp = strchr(_inputline,'\n')) != NULL)
505 *_inpp = '\0';
506 _inpp = _inputline;
507 setScriptReturn(true);
508 }
509
opn_opcode38()510 void AGOSEngine_PN::opn_opcode38() {
511 _noScanFlag = 1;
512 clearInputLine();
513 writeval(_workptr, _keyPressed.ascii);
514 _keyPressed.reset();
515 _noScanFlag = 0;
516 varval();
517 setScriptReturn(true);
518 }
519
opn_opcode39()520 void AGOSEngine_PN::opn_opcode39() {
521 pcf((uint8)varval());
522 setScriptReturn(true);
523 }
524
opn_opcode40()525 void AGOSEngine_PN::opn_opcode40() {
526 int a = doaction();
527 if (_dolineReturnVal != 0)
528 return;
529 int b = doaction();
530 setScriptReturn(a | b);
531 }
532
opn_opcode41()533 void AGOSEngine_PN::opn_opcode41() {
534 int a = doaction();
535 if (_dolineReturnVal != 0)
536 return;
537 int b = doaction();
538 setScriptReturn(a & b);
539 }
540
opn_opcode42()541 void AGOSEngine_PN::opn_opcode42() {
542 int a = doaction();
543 if (_dolineReturnVal != 0)
544 return;
545 int b = doaction();
546 setScriptReturn(a ^ b);
547 }
548
opn_opcode43()549 void AGOSEngine_PN::opn_opcode43() {
550 int a = doaction();
551 setScriptReturn(!a);
552 }
553
opn_opcode44()554 void AGOSEngine_PN::opn_opcode44() {
555 pcf((uint8)254);
556 setScriptReturn(true);
557 }
558
opn_opcode45()559 void AGOSEngine_PN::opn_opcode45() {
560 uint8 *myptr;
561 int x;
562
563 if (_havinit == 0) {
564 _seed = (int16)getTime();
565 _havinit = 1;
566 }
567 _seed = 1 + (75 * (_seed + 1) - 1) % 65537;
568 myptr = _workptr;
569 varval();
570 x = varval();
571 if (x == 0)
572 error("Illegal range specified for RANDOM");
573 writeval(myptr, (_seed % x));
574 setScriptReturn(true);
575 }
576
opn_opcode46()577 void AGOSEngine_PN::opn_opcode46() {
578 char *x = _curwrdptr;
579 if (x == NULL) {
580 setScriptReturn(true);
581 return;
582 }
583 pcf(*x);
584 if ((*x == '.') || (*x == '"') || (*x == ',')) {
585 setScriptReturn(true);
586 return;
587 }
588 x++;
589 while ((*x != '.') && (*x != ',') && (*x != '"') && (!Common::isSpace(*x)) && (*x != '\0'))
590 pcf(*x++);
591 setScriptReturn(true);
592 }
593
opn_opcode47()594 void AGOSEngine_PN::opn_opcode47() {
595 pmesd(varval() * 256 + varval());
596 setScriptReturn(true);
597 }
598
opn_opcode48()599 void AGOSEngine_PN::opn_opcode48() {
600 pmesd(varval() * 256 + varval());
601 pcf((uint8)'\n');
602 setScriptReturn(true);
603 }
604
opn_opcode49()605 void AGOSEngine_PN::opn_opcode49() {
606 setScriptReturn(findentry());
607 }
608
opn_opcode50()609 void AGOSEngine_PN::opn_opcode50() {
610 _fnst = 0;
611 setScriptReturn(findset());
612 }
613
opn_opcode51()614 void AGOSEngine_PN::opn_opcode51() {
615 _fnst = varval();
616 setScriptReturn(findset());
617 }
618
opn_opcode52()619 void AGOSEngine_PN::opn_opcode52() {
620 int32 mode = varval();
621 if (mode == 1) {
622 setWindowImage(mode, varval(), true);
623 } else {
624 setWindowImageEx(mode, varval());
625 }
626
627 setScriptReturn(true);
628 }
629
opn_opcode53()630 void AGOSEngine_PN::opn_opcode53() {
631 vc27_resetSprite();
632 setScriptReturn(true);
633 }
634
opn_opcode54()635 void AGOSEngine_PN::opn_opcode54() {
636 stopAnimate(varval());
637 setScriptReturn(true);
638 }
639
opn_opcode55()640 void AGOSEngine_PN::opn_opcode55() {
641 varval();
642 varval();
643 varval();
644 setScriptReturn(true);
645 }
646
opn_opcode56()647 void AGOSEngine_PN::opn_opcode56() {
648 varval();
649 varval();
650 varval();
651 setScriptReturn(true);
652 }
653
opn_opcode57()654 void AGOSEngine_PN::opn_opcode57() {
655 uint16 windowNum = varval();
656 uint16 vgaSpriteId = varval();
657 int16 x = varval();
658 int16 y = varval();
659 uint16 palette = varval();
660
661 _videoLockOut |= 0x40;
662 animate(windowNum, 0, vgaSpriteId, x, y, palette);
663 _videoLockOut &= ~0x40;
664
665 setScriptReturn(true);
666 }
667
opn_opcode62()668 void AGOSEngine_PN::opn_opcode62() {
669 int32 zoneNum = varval();
670
671 _videoLockOut |= 0x80;
672
673 vc29_stopAllSounds();
674
675 _hitCalled = 0;
676 _oneClick = 0;
677
678 loadZone(zoneNum);
679
680 setWindowImage(2, 2);
681
682 _copyScnFlag = 0;
683 _vgaSpriteChanged = 0;
684
685 _videoLockOut &= ~0x80;
686
687 setScriptReturn(true);
688 }
689
opn_opcode63()690 void AGOSEngine_PN::opn_opcode63() {
691 int a = readfromline();
692 switch (a) {
693 case 65:
694 setScriptReturn(inventoryOn(varval()));
695 break;
696 case 64:
697 setScriptReturn((_videoLockOut & 0x10) != 0);
698 break;
699 case 63:
700 setScriptReturn(inventoryOff());
701 break;
702 default:
703 error("opn_opcode63: unknown code %d", a);
704 }
705 }
706
inventoryOn(int val)707 int AGOSEngine_PN::inventoryOn(int val) {
708 writeVariable(210, val);
709 if (_videoLockOut & 0x10) {
710 iconPage();
711 } else {
712 _videoLockOut |= 0x10;
713 _hitAreaList = _invHitAreas;
714
715 _windowArray[2]->textColor = 0;
716 windowPutChar(_windowArray[2], 13);
717
718 clearVideoWindow(4, 0);
719 drawIconHitBar();
720
721 _objects = _variableArray[211];
722 _objectCountS = -1;
723 iconPage();
724 }
725 return 1;
726 }
727
inventoryOff()728 int AGOSEngine_PN::inventoryOff() {
729 if (_videoLockOut & 0x10) {
730 _windowArray[2]->textColor = 15;
731
732 restoreBlock(48, 2, 272, 130);
733
734 _hitAreaList = _hitAreas;
735 _videoLockOut &= ~0x10;
736 _vgaSpriteChanged++;
737 }
738 return 1;
739 }
740
741 // -----------------------------------------------------------------------
742 // Personal Nightmare Script Code
743 // -----------------------------------------------------------------------
744
745
bitextract(uint32 ptr,int offs)746 int AGOSEngine_PN::bitextract(uint32 ptr, int offs) {
747 const byte mask = 0x80 >> (offs % 8);
748 return ((mask & _dataBase[ptr + offs / 8]) != 0);
749 }
750
getptr(uint32 pos)751 uint16 AGOSEngine_PN::getptr(uint32 pos) {
752 if (pos > _dataBaseSize)
753 error("getptr: Read beyond EOF (%d)", pos);
754 return (int)READ_LE_UINT16(_dataBase + pos);
755 }
756
getlong(uint32 pos)757 uint32 AGOSEngine_PN::getlong(uint32 pos) {
758 // Only actually reads 24bit though
759 if (pos > _dataBaseSize)
760 error("getlong: Read beyond EOF (%d)", pos);
761 return (uint32)READ_LE_UINT24(_dataBase + pos);
762 }
763
varval()764 int AGOSEngine_PN::varval() {
765 int a;
766 int b;
767
768 a = readfromline();
769 if (a < 247) {
770 return a;
771 }
772
773 switch (a) {
774 case 249:
775 b = readfromline();
776 return (int)(b + 256 * readfromline());
777 case 250:
778 return readfromline();
779 case 251:
780 return (int)_variableArray[varval()];
781 case 252:
782 b = varval();
783 return (int)_dataBase[_quickptr[0] + b * _quickshort[0] + varval()];
784 case 254:
785 b = varval();
786 return (int)_dataBase[_quickptr[3] + b * _quickshort[2] + varval()];
787 case 247:
788 b = varval();
789 return (int)getptr(_quickptr[11] + (b * _quickshort[4]) + (2 * varval()));
790 case 248:
791 b = varval();
792 return (int)getptr(_quickptr[12] + (b * _quickshort[5]) + (2 * varval()));
793 case 253:
794 b = varval();
795 return bitextract((int32)_quickptr[1] + b * _quickshort[1], varval());
796 case 255:
797 b = varval();
798 return bitextract((int32)_quickptr[4] + b * _quickshort[3], varval());
799 default:
800 error("VARVAL : Illegal code %d encountered", a);
801 }
802 }
803
writeval(uint8 * ptr,int val)804 void AGOSEngine_PN::writeval(uint8 *ptr, int val) {
805 uint8 *savpt = _workptr;
806 int lsav = _linct, a, b, x;
807 _workptr = ptr;
808 _linct = 255;
809
810 if ((a = readfromline()) < 247)
811 error("writeval: Write to constant (%d)", a);
812
813 switch (a) {
814 case 249:
815 error("writeval: Write to constant (%d)", a);
816 break;
817 case 250:
818 error("writeval: Write to constant (%d)", a);
819 break;
820 case 251:
821 _variableArray[varval()] = val;
822 break;
823 case 252:
824 b = varval();
825 _dataBase[_quickptr[0] + b * _quickshort[0] + varval()] = val;
826 break;
827 case 254:
828 b = varval();
829 _dataBase[_quickptr[3] + b * _quickshort[2] + varval()] = val;
830 break;
831 case 247:
832 b = varval();
833 x = _quickptr[11] + b * _quickshort[4] + varval() * 2;
834 WRITE_LE_UINT16(_dataBase + x, val);
835 break;
836 case 248:
837 b = varval();
838 x = _quickptr[12] + b * _quickshort[5] + varval() * 2;
839 WRITE_LE_UINT16(_dataBase + x, val);
840 break;
841 case 253:
842 b = varval();
843 setbitf((uint32)_quickptr[1] + b * _quickshort[1], varval(), val);
844 break;
845 case 255:
846 b = varval();
847 setbitf((uint32)_quickptr[4] + b * _quickshort[3], varval(), val);
848 break;
849 default:
850 error("WRITEVAL : undefined evaluation %d", a);
851 }
852 _linct = lsav;
853 _workptr = savpt;
854 }
855
setbitf(uint32 ptr,int offs,int val)856 void AGOSEngine_PN::setbitf(uint32 ptr, int offs, int val) {
857 ptr += offs / 8;
858 const byte mask = 0x80 >> (offs % 8);
859 if (val != 0)
860 _dataBase[ptr] |= mask;
861 else
862 _dataBase[ptr] &= ~mask;
863 }
864
actCallD(int n)865 int AGOSEngine_PN::actCallD(int n) {
866 int pf[8];
867 funcentry(pf, n);
868 addstack(kJmpClassNum);
869 funccpy(pf);
870 setposition(n, 0);
871 return doline(1);
872 }
873
doaction()874 int AGOSEngine_PN::doaction() {
875 if (_linct == 0)
876 return 0;
877
878 _opcode = readfromline();
879
880 if (_opcode > 63) {
881 return (actCallD(_opcode - 64));
882 }
883
884 setScriptReturn(0);
885 executeOpcode(_opcode);
886 delay(0);
887
888 return getScriptReturn();
889 }
890
doline(int needsave)891 int AGOSEngine_PN::doline(int needsave) {
892 assert(!_stackbase == !needsave);
893
894 int x;
895 int myTag = ++_tagOfActiveDoline; // Obtain a unique tag for this doline invocation
896 _dolineReturnVal = 0;
897
898 if (_stackbase && needsave)
899 _stackbase->tagOfParentDoline = myTag;
900
901 do {
902 _linct = ((*_linebase) & 127) - 1;
903 _workptr = _linebase + 1;
904 if (*_linebase > 127) {
905 x = varval();
906 if (x != (int)_variableArray[1])
907 goto skipln;
908 }
909
910 do {
911 x = doaction();
912
913 if (_dolineReturnVal != 0) {
914 if (_tagOfActiveDoline != myTag)
915 return 0;
916
917 x = _dolineReturnVal;
918 _dolineReturnVal = 0;
919
920 if (x > 0) {
921 if (x != 3)
922 dumpstack();
923 // Restore the active jmpbuf to its previous value,
924 // then return _dolineReturnVal-1 (will be 2-1=1 or 1-1=0).
925 _tagOfActiveDoline = myTag - 1;
926 return (x - 1);
927 }
928 }
929
930 } while (x && !shouldQuit());
931
932 skipln:
933 _linebase += 127 & *_linebase;
934 _linembr++;
935 } while (!shouldQuit());
936
937 return 0;
938 }
939
findentry()940 int AGOSEngine_PN::findentry() {
941 int stepmt;
942 int curObj = 0;
943 uint32 ofs = _quickptr[11];
944 int c1, c2;
945
946 c1 = varval();
947 c2 = varval();
948 stepmt = _quickshort[4];
949
950 while (curObj < _quickshort[6]) {
951 if (((c1 == 255) || (c1 == getptr(ofs))) &&
952 (c2 == getptr(ofs + 2))) {
953 _variableArray[23] = curObj;
954 return 1;
955 }
956 curObj++;
957 ofs += stepmt;
958 }
959 return 0;
960 }
961
findset()962 int AGOSEngine_PN::findset() {
963 int curObj = _fnst;
964 int c1, c2, c3, c4;
965 int stepmt = _quickshort[4];
966 uint32 ofs = _quickptr[11] + stepmt * curObj;
967 c1 = varval();
968 c2 = varval();
969 c3 = varval();
970 c4 = varval();
971 while (curObj < _quickshort[6]) {
972 if (((c1 ==255) || (c1 == getptr(ofs))) &&
973 ((c2 == 255) || (c2 == getptr(ofs + 2))) &&
974 ((c3 == 255) || (c3 == getptr(ofs + 4))) &&
975 ((c4 == 255) || (c4 == getptr(ofs + 6)))) {
976 _variableArray[23] = curObj;
977 _fnst = curObj + 1;
978 return 1;
979 }
980 curObj++;
981 ofs += stepmt;
982 }
983 return 0;
984 }
985
funccpy(int * store)986 void AGOSEngine_PN::funccpy(int *store) {
987 for (int i = 24; i < 32; ++i) {
988 _variableArray[i] = *store++;
989 }
990 }
991
funcentry(int * store,int procn)992 void AGOSEngine_PN::funcentry(int *store, int procn) {
993 int numParams = _dataBase[getlong(_quickptr[6] + 3 * procn)];
994 for (int i = 0; i < numParams; ++i) {
995 *store++ = varval();
996 }
997 }
998
gvwrd(uint8 * wptr,int mask)999 int AGOSEngine_PN::gvwrd(uint8 *wptr, int mask) {
1000 int val = 0, code = 0, q = _dataBase[57];
1001 uint8 *vocbase = _dataBase + getlong(15);
1002 while (*vocbase != 255) {
1003 if (*vocbase < 0x80) {
1004 val = vocbase[q] + 256 * vocbase[q + 1];
1005 code = vocbase[q + 2];
1006 }
1007 if (wrdmatch(vocbase, mask, wptr, code))
1008 return val;
1009 vocbase += (*vocbase > 127) ? q : q + 3;
1010 }
1011 return -1;
1012 }
1013
1014
setposition(int process,int line)1015 int AGOSEngine_PN::setposition(int process, int line) {
1016 uint8 *ourptr;
1017 int np;
1018 int ct;
1019
1020 ourptr = _dataBase + getlong(_quickptr[6] + 3 * process);
1021 np = *ourptr++;
1022 for (ct = 0; ct < line; ++ct) {
1023 ourptr += (127 & *ourptr);
1024 }
1025
1026 while (true) {
1027 _linebase = ourptr;
1028 _linct = (127 & *ourptr) - 1;
1029 if (*ourptr++ <= 127)
1030 break;
1031
1032 ct = varval();
1033 if (ct == (int)_variableArray[1])
1034 break;
1035
1036 ourptr += _linct - 1;
1037 line++;
1038 }
1039
1040 _linembr = line;
1041 _procnum = process;
1042 _variableArray[0] = process;
1043 _workptr = ourptr;
1044 return np;
1045 }
1046
wrdmatch(uint8 * word1,int mask1,uint8 * word2,int mask2)1047 int AGOSEngine_PN::wrdmatch(uint8 *word1, int mask1, uint8 *word2, int mask2) {
1048 uint8 sv;
1049
1050 if ((mask1 & mask2) == 0)
1051 return 0;
1052
1053 sv = *word1;
1054 *word1 &= 127;
1055 if (scumm_strnicmp((const char *)word1, (const char *)word2, _dataBase[57])) {
1056 *word1 = sv;
1057 return 0;
1058 }
1059 *word1 = sv;
1060 return 1;
1061 }
1062
1063 // -----------------------------------------------------------------------
1064 // Personal Nightmare Stack Code
1065 // -----------------------------------------------------------------------
1066
addstack(int type)1067 void AGOSEngine_PN::addstack(int type) {
1068 StackFrame *a;
1069 int i;
1070
1071 a = (StackFrame *)calloc(1, sizeof(StackFrame));
1072 if (a == NULL)
1073 error("addstack: Out of memory - stack overflow");
1074
1075 a->nextframe = _stackbase;
1076 _stackbase = a;
1077
1078 for (i = 0; i < 6; ++i)
1079 a->flag[i] = _variableArray[i];
1080 for (i = 0; i < 8; ++i)
1081 a->param[i] = _variableArray[24 + i];
1082 a->classnum = type;
1083 a->ll = _linct;
1084 a->linenum = _linembr;
1085 a->linpos = _workptr;
1086 a->lbase = _linebase;
1087 a->process = _procnum;
1088 }
1089
dumpstack()1090 void AGOSEngine_PN::dumpstack() {
1091 StackFrame *a;
1092
1093 if (_stackbase == NULL)
1094 error("dumpstack: Stack underflow or unknown longjmp");
1095
1096 a = _stackbase->nextframe;
1097 free((char *)_stackbase);
1098 _stackbase = a;
1099 }
1100
popstack(int type)1101 void AGOSEngine_PN::popstack(int type) {
1102 int i = 0;
1103
1104 while ((_stackbase != NULL) && (_stackbase->classnum != type)) {
1105 dumpstack();
1106 ++i;
1107 }
1108
1109 if (_stackbase == NULL)
1110 error("popstack: Stack underflow or unknown longjmp");
1111
1112 _linct = _stackbase->ll;
1113 _linebase = _stackbase->lbase;
1114 _workptr = _stackbase->linpos;
1115 _procnum = _stackbase->process;
1116 _linembr = _stackbase->linenum;
1117 for (i = 0; i < 6; ++i)
1118 _variableArray[i] = _stackbase->flag[i];
1119 for (i = 0; i < 8; ++i)
1120 _variableArray[24 + i] = _stackbase->param[i];
1121 }
1122
1123 } // End of namespace AGOS
1124