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