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