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 // Video script opcodes for Simon1/Simon2
24 
25 
26 #include "agos/agos.h"
27 #include "agos/intern.h"
28 #include "agos/sound.h"
29 #include "agos/vga.h"
30 
31 #include "common/debug-channels.h"
32 #include "common/endian.h"
33 #include "common/system.h"
34 #include "common/textconsole.h"
35 
36 #include "graphics/surface.h"
37 
38 namespace AGOS {
39 
40 // Opcode tables
setupVideoOpcodes(VgaOpcodeProc * op)41 void AGOSEngine::setupVideoOpcodes(VgaOpcodeProc *op) {
42 	op[1] = &AGOSEngine::vc1_fadeOut;
43 	op[2] = &AGOSEngine::vc2_call;
44 	op[3] = &AGOSEngine::vc3_loadSprite;
45 	op[4] = &AGOSEngine::vc4_fadeIn;
46 	op[5] = &AGOSEngine::vc5_ifEqual;
47 	op[6] = &AGOSEngine::vc6_ifObjectHere;
48 	op[7] = &AGOSEngine::vc7_ifObjectNotHere;
49 	op[8] = &AGOSEngine::vc8_ifObjectIsAt;
50 	op[9] = &AGOSEngine::vc9_ifObjectStateIs;
51 	op[10] = &AGOSEngine::vc10_draw;
52 	op[12] = &AGOSEngine::vc12_delay;
53 	op[13] = &AGOSEngine::vc13_addToSpriteX;
54 	op[14] = &AGOSEngine::vc14_addToSpriteY;
55 	op[15] = &AGOSEngine::vc15_sync;
56 	op[16] = &AGOSEngine::vc16_waitSync;
57 	op[18] = &AGOSEngine::vc18_jump;
58 	op[20] = &AGOSEngine::vc20_setRepeat;
59 	op[21] = &AGOSEngine::vc21_endRepeat;
60 	op[23] = &AGOSEngine::vc23_setPriority;
61 	op[24] = &AGOSEngine::vc24_setSpriteXY;
62 	op[25] = &AGOSEngine::vc25_halt_sprite;
63 	op[26] = &AGOSEngine::vc26_setSubWindow;
64 	op[27] = &AGOSEngine::vc27_resetSprite;
65 	op[29] = &AGOSEngine::vc29_stopAllSounds;
66 	op[30] = &AGOSEngine::vc30_setFrameRate;
67 	op[31] = &AGOSEngine::vc31_setWindow;
68 	op[33] = &AGOSEngine::vc33_setMouseOn;
69 	op[34] = &AGOSEngine::vc34_setMouseOff;
70 	op[35] = &AGOSEngine::vc35_clearWindow;
71 	op[36] = &AGOSEngine::vc36_setWindowImage;
72 	op[38] = &AGOSEngine::vc38_ifVarNotZero;
73 	op[39] = &AGOSEngine::vc39_setVar;
74 	op[40] = &AGOSEngine::vc40_scrollRight;
75 	op[41] = &AGOSEngine::vc41_scrollLeft;
76 	op[42] = &AGOSEngine::vc42_delayIfNotEQ;
77 	op[43] = &AGOSEngine::vc43_ifBitSet;
78 	op[44] = &AGOSEngine::vc44_ifBitClear;
79 	op[45] = &AGOSEngine::vc45_setSpriteX;
80 	op[46] = &AGOSEngine::vc46_setSpriteY;
81 	op[47] = &AGOSEngine::vc47_addToVar;
82 	op[49] = &AGOSEngine::vc49_setBit;
83 	op[50] = &AGOSEngine::vc50_clearBit;
84 	op[51] = &AGOSEngine::vc51_enableBox;
85 	op[52] = &AGOSEngine::vc52_playSound;
86 	op[55] = &AGOSEngine::vc55_moveBox;
87 }
88 
setupVideoOpcodes(VgaOpcodeProc * op)89 void AGOSEngine_Elvira1::setupVideoOpcodes(VgaOpcodeProc *op) {
90 	op[1] = &AGOSEngine::vc1_fadeOut;
91 	op[2] = &AGOSEngine::vc2_call;
92 	op[3] = &AGOSEngine::vc3_loadSprite;
93 	op[4] = &AGOSEngine::vc4_fadeIn;
94 	op[5] = &AGOSEngine::vc5_ifEqual;
95 	op[6] = &AGOSEngine::vc6_ifObjectHere;
96 	op[7] = &AGOSEngine::vc7_ifObjectNotHere;
97 	op[8] = &AGOSEngine::vc8_ifObjectIsAt;
98 	op[9] = &AGOSEngine::vc9_ifObjectStateIs;
99 	op[10] = &AGOSEngine::vc10_draw;
100 	op[13] = &AGOSEngine::vc12_delay;
101 	op[14] = &AGOSEngine::vc13_addToSpriteX;
102 	op[15] = &AGOSEngine::vc14_addToSpriteY;
103 	op[16] = &AGOSEngine::vc15_sync;
104 	op[17] = &AGOSEngine::vc16_waitSync;
105 	op[18] = &AGOSEngine::vc17_waitEnd;
106 	op[19] = &AGOSEngine::vc18_jump;
107 	op[20] = &AGOSEngine::vc19_loop;
108 	op[21] = &AGOSEngine::vc20_setRepeat;
109 	op[22] = &AGOSEngine::vc21_endRepeat;
110 	op[23] = &AGOSEngine::vc22_setPalette;
111 	op[24] = &AGOSEngine::vc23_setPriority;
112 	op[25] = &AGOSEngine::vc24_setSpriteXY;
113 	op[26] = &AGOSEngine::vc25_halt_sprite;
114 	op[27] = &AGOSEngine::vc26_setSubWindow;
115 	op[28] = &AGOSEngine::vc27_resetSprite;
116 	op[29] = &AGOSEngine::vc28_playSFX;
117 	op[30] = &AGOSEngine::vc29_stopAllSounds;
118 	op[31] = &AGOSEngine::vc30_setFrameRate;
119 	op[32] = &AGOSEngine::vc31_setWindow;
120 	op[33] = &AGOSEngine::vc32_saveScreen;
121 	op[34] = &AGOSEngine::vc33_setMouseOn;
122 	op[35] = &AGOSEngine::vc34_setMouseOff;
123 	op[38] = &AGOSEngine::vc35_clearWindow;
124 	op[40] = &AGOSEngine::vc36_setWindowImage;
125 	op[41] = &AGOSEngine::vc37_pokePalette;
126 	op[51] = &AGOSEngine::vc38_ifVarNotZero;
127 	op[52] = &AGOSEngine::vc39_setVar;
128 	op[53] = &AGOSEngine::vc40_scrollRight;
129 	op[54] = &AGOSEngine::vc41_scrollLeft;
130 	op[56] = &AGOSEngine::vc42_delayIfNotEQ;
131 }
132 
setupVgaOpcodes()133 void AGOSEngine::setupVgaOpcodes() {
134 	memset(_vga_opcode_table, 0, sizeof(_vga_opcode_table));
135 
136 	switch (getGameType()) {
137 	case GType_PN:
138 	case GType_ELVIRA1:
139 	case GType_ELVIRA2:
140 	case GType_WW:
141 	case GType_SIMON1:
142 	case GType_SIMON2:
143 	case GType_FF:
144 	case GType_PP:
145 		setupVideoOpcodes(_vga_opcode_table);
146 		break;
147 	default:
148 		error("setupVgaOpcodes: Unknown game");
149 	}
150 }
151 
152 // VGA Script parser
runVgaScript()153 void AGOSEngine::runVgaScript() {
154 	for (;;) {
155 		uint opcode;
156 
157 		if (DebugMan.isDebugChannelEnabled(kDebugVGAOpcode)) {
158 			if (_vcPtr != (const byte *)&_vcGetOutOfCode) {
159 				debugN("%.5d %.5X: %5d %4d ", _vgaTickCounter, (unsigned int)(_vcPtr - _curVgaFile1), _vgaCurSpriteId, _vgaCurZoneNum);
160 				dumpVideoScript(_vcPtr, true);
161 			}
162 		}
163 
164 		if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) {
165 			opcode = *_vcPtr++;
166 		} else {
167 			opcode = READ_BE_UINT16(_vcPtr);
168 			_vcPtr += 2;
169 		}
170 
171 		if (opcode == 0)
172 			return;
173 
174 		if (opcode >= _numVideoOpcodes || !_vga_opcode_table[opcode])
175 			error("runVgaScript: Invalid VGA opcode '%d' encountered", opcode);
176 
177 		(this->*_vga_opcode_table[opcode]) ();
178 	}
179 }
180 
ifObjectHere(uint16 a)181 bool AGOSEngine::ifObjectHere(uint16 a) {
182 	Item *item;
183 
184 	CHECK_BOUNDS(a, _objectArray);
185 
186 	item = _objectArray[a];
187 	if (item == NULL)
188 		return true;
189 
190 	return me()->parent == item->parent;
191 }
192 
ifObjectAt(uint16 a,uint16 b)193 bool AGOSEngine::ifObjectAt(uint16 a, uint16 b) {
194 	Item *item_a, *item_b;
195 
196 	CHECK_BOUNDS(a, _objectArray);
197 	CHECK_BOUNDS(b, _objectArray);
198 
199 	item_a = _objectArray[a];
200 	item_b = _objectArray[b];
201 
202 	if (item_a == NULL || item_b == NULL)
203 		return true;
204 
205 	return derefItem(item_a->parent) == item_b;
206 }
207 
ifObjectState(uint16 a,int16 b)208 bool AGOSEngine::ifObjectState(uint16 a, int16 b) {
209 	Item *item;
210 
211 	CHECK_BOUNDS(a, _objectArray);
212 
213 	item = _objectArray[a];
214 	if (item == NULL)
215 		return true;
216 	return item->state == b;
217 }
218 
dirtyBackGround()219 void AGOSEngine::dirtyBackGround() {
220 	AnimTable *animTable = _screenAnim1;
221 	while (animTable->srcPtr) {
222 		if (animTable->id == _vgaCurSpriteId && animTable->zoneNum == _vgaCurZoneNum) {
223 			animTable->windowNum |= 0x8000;
224 			break;
225 		}
226 		animTable++;
227 	}
228 }
229 
findCurSprite()230 VgaSprite *AGOSEngine::findCurSprite() {
231 	VgaSprite *vsp = _vgaSprites;
232 	while (vsp->id) {
233 		if (vsp->id == _vgaCurSpriteId && vsp->zoneNum == _vgaCurZoneNum)
234 			break;
235 		vsp++;
236 	}
237 	return vsp;
238 }
239 
isSpriteLoaded(uint16 id,uint16 zoneNum)240 bool AGOSEngine::isSpriteLoaded(uint16 id, uint16 zoneNum) {
241 	VgaSprite *vsp = _vgaSprites;
242 	while (vsp->id) {
243 		if (vsp->id == id && vsp->zoneNum == zoneNum)
244 			return true;
245 		vsp++;
246 	}
247 	return false;
248 }
249 
getBitFlag(uint bit)250 bool AGOSEngine::getBitFlag(uint bit) {
251 	uint16 *bits = &_bitArray[bit / 16];
252 	return (*bits & (1 << (bit & 15))) != 0;
253 }
254 
setBitFlag(uint bit,bool value)255 void AGOSEngine::setBitFlag(uint bit, bool value) {
256 	uint16 *bits = &_bitArray[bit / 16];
257 	*bits = (*bits & ~(1 << (bit & 15))) | (value << (bit & 15));
258 }
259 
vcReadVarOrWord()260 int AGOSEngine::vcReadVarOrWord() {
261 	if (getGameType() == GType_PN || getGameType() == GType_ELVIRA1) {
262 		return vcReadNextWord();
263 	} else {
264 		int16 var = vcReadNextWord();
265 		if (var < 0)
266 			var = vcReadVar(-var);
267 		return var;
268 	}
269 }
270 
vcReadNextWord()271 uint AGOSEngine::vcReadNextWord() {
272 	uint a;
273 	a = readUint16Wrapper(_vcPtr);
274 	_vcPtr += 2;
275 	return a;
276 }
277 
vcReadNextByte()278 uint AGOSEngine::vcReadNextByte() {
279 	return *_vcPtr++;
280 }
281 
vcReadVar(uint var)282 uint AGOSEngine::vcReadVar(uint var) {
283 	assert(var < _numVars);
284 	return (uint16)_variableArrayPtr[var];
285 }
286 
vcWriteVar(uint var,int16 value)287 void AGOSEngine::vcWriteVar(uint var, int16 value) {
288 	assert(var < _numVars);
289 	_variableArrayPtr[var] = value;
290 }
291 
vcSkipNextInstruction()292 void AGOSEngine::vcSkipNextInstruction() {
293 
294 	static const byte opcodeParamLenPN[] = {
295 		0, 6,  2, 10, 6, 4, 2, 2,
296 		4, 4,  8,  2, 0, 2, 2, 2,
297 		0, 2,  2,  2, 0, 4, 2, 2,
298 		2, 8,  0, 10, 0, 8, 0, 2,
299 		2, 0,  0,  0, 0, 2, 4, 2,
300 		4, 4,  0,  0, 2, 2, 2, 4,
301 		4, 0, 18,  2, 4, 4, 4, 0,
302 		4
303 	};
304 
305 	static const byte opcodeParamLenElvira1[] = {
306 		0, 6,  2, 10, 6, 4, 2, 2,
307 		4, 4,  8,  2, 0, 2, 2, 2,
308 		2, 2,  2,  2, 0, 4, 2, 2,
309 		2, 8,  0, 10, 0, 8, 0, 2,
310 		2, 0,  0,  0, 0, 2, 4, 2,
311 		4, 4,  0,  0, 2, 2, 2, 4,
312 		4, 0, 18,  2, 4, 4, 4, 0,
313 		4
314 	};
315 
316 	static const byte opcodeParamLenWW[] = {
317 		0, 6,  2, 10, 6, 4, 2, 2,
318 		4, 4,  8,  2, 2, 2, 2, 2,
319 		2, 2,  2,  0, 4, 2, 2, 2,
320 		8, 0, 10,  0, 8, 0, 2, 2,
321 		0, 0,  0,  4, 4, 4, 2, 4,
322 		4, 4,  4,  2, 2, 4, 2, 2,
323 		2, 2,  2,  2, 2, 4, 6, 6,
324 		0, 0,  0,  0, 2, 2, 0, 0,
325 	};
326 
327 	static const byte opcodeParamLenSimon1[] = {
328 		0, 6,  2, 10, 6, 4, 2, 2,
329 		4, 4, 10,  0, 2, 2, 2, 2,
330 		2, 0,  2,  0, 4, 2, 4, 2,
331 		8, 0, 10,  0, 8, 0, 2, 2,
332 		4, 0,  0,  4, 4, 2, 2, 4,
333 		4, 4,  4,  2, 2, 2, 2, 4,
334 		0, 2,  2,  2, 2, 4, 6, 6,
335 		0, 0,  0,  0, 2, 6, 0, 0,
336 	};
337 
338 	static const byte opcodeParamLenSimon2[] = {
339 		0, 6,  2, 12, 6, 4, 2, 2,
340 		4, 4,  9,  0, 1, 2, 2, 2,
341 		2, 0,  2,  0, 4, 2, 4, 2,
342 		7, 0, 10,  0, 8, 0, 2, 2,
343 		4, 0,  0,  4, 4, 2, 2, 4,
344 		4, 4,  4,  2, 2, 2, 2, 4,
345 		0, 2,  2,  2, 2, 4, 6, 6,
346 		2, 0,  6,  6, 4, 6, 0, 0,
347 		0, 0,  4,  4, 4, 4, 4, 0,
348 		4, 2,  2
349 	};
350 
351 	static const byte opcodeParamLenFeebleFiles[] = {
352 		0, 6, 2, 12, 6, 4, 2, 2,
353 		4, 4, 9, 0, 1, 2, 2, 2,
354 		2, 0, 2, 0, 4, 2, 4, 2,
355 		7, 0, 10, 0, 8, 0, 2, 2,
356 		4, 0, 0, 4, 4, 2, 2, 4,
357 		4, 4, 4, 2, 2, 2, 2, 4,
358 		0, 2, 2, 2, 6, 6, 6, 6,
359 		2, 0, 6, 6, 4, 6, 0, 0,
360 		0, 0, 4, 4, 4, 4, 4, 0,
361 		4, 2, 2, 4, 6, 6, 0, 0,
362 		6, 4, 2, 6, 0
363 	};
364 
365 	uint16 opcode;
366 	if (getGameType() == GType_FF || getGameType() == GType_PP) {
367 		opcode = vcReadNextByte();
368 		_vcPtr += opcodeParamLenFeebleFiles[opcode];
369 	} else if (getGameType() == GType_SIMON2) {
370 		opcode = vcReadNextByte();
371 		_vcPtr += opcodeParamLenSimon2[opcode];
372 	} else if (getGameType() == GType_SIMON1) {
373 		opcode = vcReadNextWord();
374 		_vcPtr += opcodeParamLenSimon1[opcode];
375 	} else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
376 		opcode = vcReadNextWord();
377 		_vcPtr += opcodeParamLenWW[opcode];
378 	} else if (getGameType() == GType_ELVIRA1) {
379 		opcode = vcReadNextWord();
380 		_vcPtr += opcodeParamLenElvira1[opcode];
381 	} else {
382 		opcode = vcReadNextWord();
383 		_vcPtr += opcodeParamLenPN[opcode];
384 	}
385 
386 	debugCN(kDebugVGAOpcode, "; skipped\n");
387 }
388 
389 // VGA Script commands
vc1_fadeOut()390 void AGOSEngine::vc1_fadeOut() {
391 	/* dummy opcode */
392 	_vcPtr += 6;
393 }
394 
vc2_call()395 void AGOSEngine::vc2_call() {
396 	uint16 num;
397 	byte *old_file_1, *old_file_2;
398 
399 	if (getGameType() == GType_ELVIRA2) {
400 		num = vcReadNextWord();
401 	} else {
402 		num = vcReadVarOrWord();
403 	}
404 
405 	old_file_1 = _curVgaFile1;
406 	old_file_2 = _curVgaFile2;
407 
408 	setImage(num, true);
409 
410 	_curVgaFile1 = old_file_1;
411 	_curVgaFile2 = old_file_2;
412 }
413 
vc3_loadSprite()414 void AGOSEngine::vc3_loadSprite() {
415 	uint16 windowNum, zoneNum, palette, vgaSpriteId;
416 	int16 x, y;
417 	byte *old_file_1;
418 
419 	windowNum = vcReadNextWord();
420 	if (getGameType() == GType_SIMON1 && windowNum == 3) {
421 		_window3Flag = 1;
422 	}
423 
424 	if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) {
425 		zoneNum = vcReadNextWord();
426 		vgaSpriteId = vcReadNextWord();
427 	} else {
428 		vgaSpriteId = vcReadNextWord();
429 		zoneNum = (getGameType() == GType_PN) ? 0 : vgaSpriteId / 100;
430 	}
431 
432 	x = vcReadNextWord();
433 	y = vcReadNextWord();
434 	palette = vcReadNextWord();
435 
436 	old_file_1 = _curVgaFile1;
437 
438 	animate(windowNum, zoneNum, vgaSpriteId, x, y, palette, true);
439 
440 	_curVgaFile1 = old_file_1;
441 }
442 
vc4_fadeIn()443 void AGOSEngine::vc4_fadeIn() {
444 	/* dummy opcode */
445 	_vcPtr += 6;
446 }
447 
vc5_ifEqual()448 void AGOSEngine::vc5_ifEqual() {
449 	uint16 var;
450 
451 	if (getGameType() == GType_PP)
452 		var = vcReadVarOrWord();
453 	else
454 		var = vcReadNextWord();
455 
456 	uint16 value = vcReadNextWord();
457 	if (vcReadVar(var) != value)
458 		vcSkipNextInstruction();
459 }
460 
vc6_ifObjectHere()461 void AGOSEngine::vc6_ifObjectHere() {
462 	if (!ifObjectHere(vcReadNextWord())) {
463 		vcSkipNextInstruction();
464 	}
465 }
466 
vc7_ifObjectNotHere()467 void AGOSEngine::vc7_ifObjectNotHere() {
468 	if (ifObjectHere(vcReadNextWord()))
469 		vcSkipNextInstruction();
470 }
471 
vc8_ifObjectIsAt()472 void AGOSEngine::vc8_ifObjectIsAt() {
473 	uint16 a = vcReadNextWord();
474 	uint16 b = vcReadNextWord();
475 	if (!ifObjectAt(a, b))
476 		vcSkipNextInstruction();
477 }
478 
vc9_ifObjectStateIs()479 void AGOSEngine::vc9_ifObjectStateIs() {
480 	uint16 a = vcReadNextWord();
481 	uint16 b = vcReadNextWord();
482 	if (!ifObjectState(a, b))
483 		vcSkipNextInstruction();
484 }
485 
vc10_uncompressFlip(const byte * src,uint16 w,uint16 h)486 byte *AGOSEngine::vc10_uncompressFlip(const byte *src, uint16 w, uint16 h) {
487 	w *= 8;
488 
489 	byte *dst, *dstPtr, *srcPtr;
490 	byte color;
491 	int8 cur = -0x80;
492 	uint i, w_cur = w;
493 
494 	dstPtr = _videoBuf1 + w;
495 
496 	do {
497 		dst = dstPtr;
498 		uint h_cur = h;
499 
500 		if (cur == -0x80)
501 			cur = *src++;
502 
503 		for (;;) {
504 			if (cur >= 0) {
505 				/* rle_same */
506 				color = *src++;
507 				do {
508 					*dst = color;
509 					dst += w;
510 					if (!--h_cur) {
511 						if (--cur < 0)
512 							cur = -0x80;
513 						else
514 							src--;
515 						goto next_line;
516 					}
517 				} while (--cur >= 0);
518 			} else {
519 				/* rle_diff */
520 				do {
521 					*dst = *src++;
522 					dst += w;
523 					if (!--h_cur) {
524 						if (++cur == 0)
525 							cur = -0x80;
526 						goto next_line;
527 					}
528 				} while (++cur != 0);
529 			}
530 			cur = *src++;
531 		}
532 	next_line:
533 		dstPtr++;
534 	} while (--w_cur);
535 
536 	srcPtr = dstPtr = _videoBuf1 + w;
537 
538 	do {
539 		dst = dstPtr;
540 		for (i = 0; i != w; ++i) {
541 			byte b = srcPtr[i];
542 			b = (b >> 4) | (b << 4);
543 			*--dst = b;
544 		}
545 
546 		srcPtr += w;
547 		dstPtr += w;
548 	} while (--h);
549 
550 	return _videoBuf1;
551 }
552 
vc10_flip(const byte * src,uint16 w,uint16 h)553 byte *AGOSEngine::vc10_flip(const byte *src, uint16 w, uint16 h) {
554 	byte *dstPtr;
555 	uint i;
556 
557 	if (getFeatures() & GF_32COLOR) {
558 		w *= 16;
559 		dstPtr = _videoBuf1 + w;
560 
561 		do {
562 			byte *dst = dstPtr;
563 			for (i = 0; i != w; ++i) {
564 				*--dst = src[i];
565 			}
566 
567 			src += w;
568 			dstPtr += w;
569 		} while (--h);
570 	} else {
571 		w *= 8;
572 		dstPtr = _videoBuf1 + w;
573 
574 		do {
575 			byte *dst = dstPtr;
576 			for (i = 0; i != w; ++i) {
577 				byte b = src[i];
578 				b = (b >> 4) | (b << 4);
579 				*--dst = b;
580 			}
581 
582 			src += w;
583 			dstPtr += w;
584 		} while (--h);
585 	}
586 
587 	return _videoBuf1;
588 }
589 
vc10_draw()590 void AGOSEngine::vc10_draw() {
591 	uint16 palette, x, y, flags;
592 	int16 image;
593 
594 	image = (int16)vcReadNextWord();
595 
596 	palette = 0;
597 	if (getGameType() == GType_FF || getGameType() == GType_PP) {
598 		palette = _vcPtr[0];
599 		_vcPtr += 2;
600 	} else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
601 		palette = _vcPtr[1];
602 		_vcPtr += 2;
603 	}
604 
605 	x = (int16)vcReadNextWord();
606 	y = (int16)vcReadNextWord();
607 
608 	if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) {
609 		flags = vcReadNextByte();
610 	} else {
611 		flags = vcReadNextWord();
612 	}
613 
614 	drawImage_init(image, palette, x, y, flags);
615 }
616 
drawImage_init(int16 image,uint16 palette,int16 x,int16 y,uint16 flags)617 void AGOSEngine::drawImage_init(int16 image, uint16 palette, int16 x, int16 y, uint16 flags) {
618 	if (image == 0)
619 		return;
620 
621 	byte *src;
622 	uint width, height;
623 	VC10_state state;
624 
625 	state.image = image;
626 	if (state.image < 0)
627 		state.image = vcReadVar(-state.image);
628 
629 	state.palette = (getGameType() == GType_PN) ? 0 : palette * 16;
630 	state.paletteMod = 0;
631 
632 	state.x = x - _scrollX;
633 	state.y = y - _scrollY;
634 
635 	state.flags = flags;
636 
637 	src = _curVgaFile2 + state.image * 8;
638 	state.srcPtr = _curVgaFile2 + readUint32Wrapper(src);
639 	if (getGameType() == GType_FF || getGameType() == GType_PP) {
640 		width = READ_LE_UINT16(src + 6);
641 		height = READ_LE_UINT16(src + 4) & 0x7FFF;
642 		flags = src[5];
643 	} else {
644 		width = READ_BE_UINT16(src + 6) / 16;
645 		height = src[5];
646 		flags = src[4];
647 	}
648 
649 	if (height == 0 || width == 0)
650 		return;
651 
652 	if (DebugMan.isDebugChannelEnabled(kDebugImageDump))
653 		dumpSingleBitmap(_vgaCurZoneNum, state.image, state.srcPtr, width, height,
654 											 state.palette);
655 	state.width = state.draw_width = width;		/* cl */
656 	state.height = state.draw_height = height;	/* ch */
657 
658 	state.depack_cont = -0x80;
659 
660 	state.x_skip = 0;				/* colums to skip = bh */
661 	state.y_skip = 0;				/* rows to skip   = bl */
662 
663 	if (getFeatures() & GF_PLANAR) {
664 		if (getGameType() == GType_PN) {
665 			state.srcPtr = convertImage(&state, ((state.flags & (kDFCompressed | kDFCompressedFlip)) != 0));
666 		}
667 		else
668 			state.srcPtr = convertImage(&state, ((flags & 0x80) != 0));
669 
670 		// converted planar clip is already uncompressed
671 		if (state.flags & kDFCompressedFlip) {
672 			state.flags &= ~kDFCompressedFlip;
673 			state.flags |= kDFFlip;
674 		}
675 		if (state.flags & kDFCompressed) {
676 			state.flags &= ~kDFCompressed;
677 		}
678 	} else if (getGameType() == GType_FF || getGameType() == GType_PP) {
679 		if (flags & 0x80) {
680 			state.flags |= kDFCompressed;
681 		}
682 	} else {
683 		if (flags & 0x80 && !(state.flags & kDFCompressedFlip)) {
684 			if (state.flags & kDFFlip) {
685 				state.flags &= ~kDFFlip;
686 				state.flags |= kDFCompressedFlip;
687 			} else {
688 				state.flags |= kDFCompressed;
689 			}
690 		}
691 	}
692 
693 	uint maxWidth = (getGameType() == GType_FF || getGameType() == GType_PP) ? 640 : 20;
694 	if ((getGameType() == GType_SIMON2 || getGameType() == GType_FF) && width > maxWidth) {
695 		horizontalScroll(&state);
696 		return;
697 	}
698 	if (getGameType() == GType_FF && height > 480) {
699 		verticalScroll(&state);
700 		return;
701 	}
702 
703 	if (getGameType() != GType_FF && getGameType() != GType_PP) {
704 		if (state.flags & kDFCompressedFlip) {
705 			state.srcPtr = vc10_uncompressFlip(state.srcPtr, width, height);
706 		} else if (state.flags & kDFFlip) {
707 			state.srcPtr = vc10_flip(state.srcPtr, width, height);
708 		}
709 	}
710 
711 	drawImage(&state);
712 }
713 
checkOnStopTable()714 void AGOSEngine::checkOnStopTable() {
715 	VgaSleepStruct *vfs = _onStopTable, *vfs_tmp;
716 	while (vfs->ident != 0) {
717 		if (vfs->ident == _vgaCurSpriteId) {
718 			VgaSprite *vsp = findCurSprite();
719 			animate(vsp->windowNum, vsp->zoneNum, vfs->id, vsp->x, vsp->y, vsp->palette, true);
720 			vfs_tmp = vfs;
721 			do {
722 				memcpy(vfs_tmp, vfs_tmp + 1, sizeof(VgaSleepStruct));
723 				vfs_tmp++;
724 			} while (vfs_tmp->ident != 0);
725 		} else {
726 			vfs++;
727 		}
728 	}
729 }
730 
vc11_onStop()731 void AGOSEngine::vc11_onStop() {
732 	uint16 id = vcReadNextWord();
733 
734 	VgaSleepStruct *vfs = _onStopTable;
735 	while (vfs->ident)
736 		vfs++;
737 
738 	vfs->ident = _vgaCurSpriteId;
739 	vfs->codePtr = _vcPtr;
740 	vfs->id = id;
741 	vfs->zoneNum = _vgaCurZoneNum;
742 }
743 
vc12_delay()744 void AGOSEngine::vc12_delay() {
745 	uint16 num;
746 
747 	if (getGameType() == GType_FF || getGameType() == GType_PP) {
748 		num = vcReadNextByte();
749 	} else if (getGameType() == GType_SIMON2) {
750 		num = vcReadNextByte() * _frameCount;
751 	} else {
752 		num = vcReadVarOrWord() * _frameCount;
753 	}
754 
755 	num += _vgaBaseDelay;
756 
757 	addVgaEvent(num, ANIMATE_EVENT, _vcPtr, _vgaCurSpriteId, _vgaCurZoneNum);
758 	_vcPtr = (byte *)&_vcGetOutOfCode;
759 }
760 
vc13_addToSpriteX()761 void AGOSEngine::vc13_addToSpriteX() {
762 	VgaSprite *vsp = findCurSprite();
763 	vsp->x += (int16)vcReadNextWord();
764 
765 	vsp->windowNum |= 0x8000;
766 	dirtyBackGround();
767 	_vgaSpriteChanged++;
768 }
769 
vc14_addToSpriteY()770 void AGOSEngine::vc14_addToSpriteY() {
771 	VgaSprite *vsp = findCurSprite();
772 	vsp->y += (int16)vcReadNextWord();
773 
774 	vsp->windowNum |= 0x8000;
775 	dirtyBackGround();
776 	_vgaSpriteChanged++;
777 }
778 
vc15_sync()779 void AGOSEngine::vc15_sync() {
780 	VgaSleepStruct *vfs = _waitSyncTable, *vfs_tmp;
781 	uint16 id;
782 
783 	if (getGameType() == GType_PN)
784 		id = _vgaCurSpriteId;
785 	else
786 		id = vcReadNextWord();
787 
788 	while (vfs->ident != 0) {
789 		if (vfs->ident == id) {
790 			addVgaEvent(_vgaBaseDelay, ANIMATE_EVENT, vfs->codePtr, vfs->id, vfs->zoneNum);
791 			vfs_tmp = vfs;
792 			do {
793 				memcpy(vfs_tmp, vfs_tmp + 1, sizeof(VgaSleepStruct));
794 				vfs_tmp++;
795 			} while (vfs_tmp->ident != 0);
796 		} else {
797 			vfs++;
798 		}
799 	}
800 
801 	_lastVgaWaitFor = id;
802 	/* clear a wait event */
803 	if (id == _vgaWaitFor)
804 		_vgaWaitFor = 0;
805 }
806 
vc16_waitSync()807 void AGOSEngine::vc16_waitSync() {
808 	VgaSleepStruct *vfs = _waitSyncTable;
809 	while (vfs->ident)
810 		vfs++;
811 
812 	vfs->ident = vcReadNextWord();
813 	vfs->codePtr = _vcPtr;
814 	vfs->id = _vgaCurSpriteId;
815 	vfs->zoneNum = _vgaCurZoneNum;
816 
817 	_vcPtr = (byte *)&_vcGetOutOfCode;
818 }
819 
checkWaitEndTable()820 void AGOSEngine::checkWaitEndTable() {
821 	VgaSleepStruct *vfs = _waitEndTable, *vfs_tmp;
822 	while (vfs->ident != 0) {
823 		if (vfs->ident == _vgaCurSpriteId) {
824 			addVgaEvent(_vgaBaseDelay, ANIMATE_EVENT, vfs->codePtr, vfs->id, vfs->zoneNum);
825 			vfs_tmp = vfs;
826 			do {
827 				memcpy(vfs_tmp, vfs_tmp + 1, sizeof(VgaSleepStruct));
828 				vfs_tmp++;
829 			} while (vfs_tmp->ident != 0);
830 		} else {
831 			vfs++;
832 		}
833 	}
834 }
835 
vc17_waitEnd()836 void AGOSEngine::vc17_waitEnd() {
837 	uint16 id = vcReadNextWord();
838 	uint16 zoneNum = (getGameType() == GType_PN) ? 0 : id / 100;
839 
840 	VgaSleepStruct *vfs = _waitEndTable;
841 	while (vfs->ident)
842 		vfs++;
843 
844 	if (isSpriteLoaded(id, zoneNum)) {
845 		vfs->ident = id;
846 		vfs->codePtr = _vcPtr;
847 		vfs->id = _vgaCurSpriteId;
848 		vfs->zoneNum = _vgaCurZoneNum;
849 		_vcPtr = (byte *)&_vcGetOutOfCode;
850 	}
851 }
852 
vc18_jump()853 void AGOSEngine::vc18_jump() {
854 	int16 offs = vcReadNextWord();
855 	_vcPtr += offs;
856 }
857 
vc19_loop()858 void AGOSEngine::vc19_loop() {
859 	uint16 count;
860 	byte *b, *bb;
861 
862 	bb = _curVgaFile1;
863 	b = _curVgaFile1 + READ_BE_UINT16(bb + 10);
864 	b += 20;
865 
866 	count = READ_BE_UINT16(&((VgaFile1Header_Common *) b)->animationCount);
867 	b = bb + READ_BE_UINT16(&((VgaFile1Header_Common *) b)->animationTable);
868 
869 	while (count--) {
870 		if (READ_BE_UINT16(&((AnimationHeader_WW *) b)->id) == _vgaCurSpriteId)
871 			break;
872 		b += sizeof(AnimationHeader_WW);
873 	}
874 	assert(READ_BE_UINT16(&((AnimationHeader_WW *) b)->id) == _vgaCurSpriteId);
875 
876 	_vcPtr = _curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_WW *) b)->scriptOffs);
877 }
878 
vc20_setRepeat()879 void AGOSEngine::vc20_setRepeat() {
880 	// Sets counter used by the endRepeat opcode below.
881 	uint16 a = vcReadNextWord();
882 	WRITE_LE_UINT16(const_cast<byte *>(_vcPtr), a);
883 	_vcPtr += 2;
884 }
885 
vc21_endRepeat()886 void AGOSEngine::vc21_endRepeat() {
887 	int16 a = vcReadNextWord();
888 	const byte *tmp = _vcPtr + a;
889 	if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP)
890 		tmp += 3;
891 	else
892 		tmp += 4;
893 
894 	uint16 val = READ_LE_UINT16(tmp);
895 	if (val != 0) {
896 		// Decrement counter
897 		WRITE_LE_UINT16(const_cast<byte *>(tmp), val - 1);
898 		_vcPtr = tmp + 2;
899 	}
900 }
901 
902 static const uint8 iconPalette[64] = {
903 	0x00, 0x00, 0x00,
904 	0x77, 0x77, 0x55,
905 	0x55, 0x00, 0x00,
906 	0x77, 0x00, 0x00,
907 	0x22, 0x00, 0x00,
908 	0x00, 0x11, 0x00,
909 	0x11, 0x22, 0x11,
910 	0x22, 0x33, 0x22,
911 	0x44, 0x55, 0x44,
912 	0x33, 0x44, 0x00,
913 	0x11, 0x33, 0x00,
914 	0x00, 0x11, 0x44,
915 	0x77, 0x44, 0x00,
916 	0x66, 0x22, 0x00,
917 	0x00, 0x22, 0x66,
918 	0x77, 0x55, 0x00,
919 };
920 
vc22_setPalette()921 void AGOSEngine::vc22_setPalette() {
922 	byte *offs, *palptr, *src;
923 	uint16 b, num;
924 
925 	b = vcReadNextWord();
926 
927 	// PC EGA version of Personal Nightmare uses standard EGA palette
928 	if (getGameType() == GType_PN && (getFeatures() & GF_EGA))
929 		return;
930 
931 	num = 16;
932 
933 	palptr = _displayPalette;
934 	_bottomPalette = 1;
935 
936 	if (getGameType() == GType_PN) {
937 		if (b > 128) {
938 			b -= 128;
939 			palptr = _displayPalette + 3 * 16;
940 		}
941 	} else if (getGameType() == GType_ELVIRA1) {
942 		if (b >= 1000) {
943 			b -= 1000;
944 			_bottomPalette = 0;
945 		} else {
946 			const byte extraColors[19 * 3] = {
947 				40,  0,  0,   24, 24, 16,   48, 48, 40,
948 				 0,  0,  0,   16,  0,  0,    8,  8,  0,
949 				48, 24,  0,   56, 40,  0,    0,  0, 24,
950 				 8, 16, 24,   24, 32, 40,   16, 24,  0,
951 				24,  8,  0,   16, 16,  0,   40, 40, 32,
952 				32, 32, 24,   40,  0,  0,   24, 24, 16,
953 				48, 48, 40
954 			};
955 
956 			num = 13;
957 
958 			for (int i = 0; i < 19; i++) {
959 				palptr[(13 + i) * 3 + 0] = extraColors[i * 3 + 0] * 4;
960 				palptr[(13 + i) * 3 + 1] = extraColors[i * 3 + 1] * 4;
961 				palptr[(13 + i) * 3 + 2] = extraColors[i * 3 + 2] * 4;
962 			}
963 		}
964 	}
965 
966 	if (getGameType() == GType_ELVIRA2 && getPlatform() == Common::kPlatformAtariST) {
967 		// Custom palette used for icon area
968 		palptr = &_displayPalette[13 * 3 * 16];
969 		for (uint8 c = 0; c < 16; c++) {
970 			palptr[0] = iconPalette[c * 3 + 0] * 2;
971 			palptr[1] = iconPalette[c * 3 + 1] * 2;
972 			palptr[2] = iconPalette[c * 3 + 2] * 2;
973 
974 			palptr += 3;
975 		};
976 		palptr = _displayPalette;
977 	}
978 
979 	offs = _curVgaFile1 + READ_BE_UINT16(_curVgaFile1 + 6);
980 	src = offs + b * 32;
981 
982 	do {
983 		uint16 color = READ_BE_UINT16(src);
984 		palptr[0] = ((color & 0xf00) >> 8) * 32;
985 		palptr[1] = ((color & 0x0f0) >> 4) * 32;
986 		palptr[2] = ((color & 0x00f) >> 0) * 32;
987 
988 		palptr += 3;
989 		src += 2;
990 	} while (--num);
991 
992 	_paletteFlag = 2;
993 	_vgaSpriteChanged++;
994 }
995 
vc23_setPriority()996 void AGOSEngine::vc23_setPriority() {
997 	VgaSprite *vsp = findCurSprite(), *vus2;
998 	uint16 pri = vcReadNextWord();
999 	VgaSprite bak;
1000 
1001 	if (vsp->id == 0)
1002 		return;
1003 
1004 	memcpy(&bak, vsp, sizeof(bak));
1005 	bak.priority = pri;
1006 	bak.windowNum |= 0x8000;
1007 
1008 	vus2 = vsp;
1009 
1010 	if (vsp != _vgaSprites && pri < vsp[-1].priority) {
1011 		do {
1012 			vsp--;
1013 		} while (vsp != _vgaSprites && pri < vsp[-1].priority);
1014 		do {
1015 			memcpy(vus2, vus2 - 1, sizeof(VgaSprite));
1016 		} while (--vus2 != vsp);
1017 		memcpy(vus2, &bak, sizeof(VgaSprite));
1018 	} else if (vsp[1].id != 0 && pri >= vsp[1].priority) {
1019 		do {
1020 			vsp++;
1021 		} while (vsp[1].id != 0 && pri >= vsp[1].priority);
1022 		do {
1023 			memcpy(vus2, vus2 + 1, sizeof(VgaSprite));
1024 		} while (++vus2 != vsp);
1025 		memcpy(vus2, &bak, sizeof(VgaSprite));
1026 	} else {
1027 		vsp->priority = pri;
1028 	}
1029 	_vgaSpriteChanged++;
1030 }
1031 
vc24_setSpriteXY()1032 void AGOSEngine::vc24_setSpriteXY() {
1033 	VgaSprite *vsp = findCurSprite();
1034 
1035 	if (getGameType() == GType_ELVIRA2) {
1036 		vsp->image = vcReadNextWord();
1037 	} else {
1038 		vsp->image = vcReadVarOrWord();
1039 	}
1040 
1041 	vsp->x += (int16)vcReadNextWord();
1042 	vsp->y += (int16)vcReadNextWord();
1043 	if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) {
1044 		vsp->flags = vcReadNextByte();
1045 	} else {
1046 		vsp->flags = vcReadNextWord();
1047 	}
1048 
1049 	vsp->windowNum |= 0x8000;
1050 	dirtyBackGround();
1051 	_vgaSpriteChanged++;
1052 }
1053 
vc25_halt_sprite()1054 void AGOSEngine::vc25_halt_sprite() {
1055 	checkWaitEndTable();
1056 	checkOnStopTable();
1057 
1058 	VgaSprite *vsp = findCurSprite();
1059 	while (vsp->id != 0) {
1060 		memcpy(vsp, vsp + 1, sizeof(VgaSprite));
1061 		vsp++;
1062 	}
1063 	_vcPtr = (byte *)&_vcGetOutOfCode;
1064 
1065 	dirtyBackGround();
1066 	_vgaSpriteChanged++;
1067 }
1068 
vc26_setSubWindow()1069 void AGOSEngine::vc26_setSubWindow() {
1070 	uint16 *as = &_videoWindows[vcReadNextWord() * 4]; // number
1071 	as[0] = vcReadNextWord(); // x
1072 	as[1] = vcReadNextWord(); // y
1073 	as[2] = vcReadNextWord(); // width
1074 	as[3] = vcReadNextWord(); // height
1075 }
1076 
vc27_resetSprite()1077 void AGOSEngine::vc27_resetSprite() {
1078 	VgaSprite bak, *vsp;
1079 	VgaSleepStruct *vfs;
1080 	VgaTimerEntry *vte, *vte2;
1081 
1082 	_videoLockOut |= 8;
1083 
1084 	_lastVgaWaitFor = 0;
1085 
1086 	bak.reset();
1087 
1088 	vsp = _vgaSprites;
1089 	while (vsp->id) {
1090 		// For animated heart in Elvira 2
1091 		if (getGameType() == GType_ELVIRA2 && vsp->id == 100) {
1092 			memcpy(&bak, vsp, sizeof(VgaSprite));
1093 		}
1094 		vsp->id = 0;
1095 		vsp++;
1096 	}
1097 
1098 	if (bak.id != 0)
1099 		memcpy(_vgaSprites, &bak, sizeof(VgaSprite));
1100 
1101 	vfs = _waitEndTable;
1102 	while (vfs->ident) {
1103 		vfs->ident = 0;
1104 		vfs++;
1105 	}
1106 
1107 	vfs = _waitSyncTable;
1108 	while (vfs->ident) {
1109 		vfs->ident = 0;
1110 		vfs++;
1111 	}
1112 
1113 	vfs = _onStopTable;
1114 	while (vfs->ident) {
1115 		vfs->ident = 0;
1116 		vfs++;
1117 	}
1118 
1119 	vte = _vgaTimerList;
1120 	while (vte->delay) {
1121 		// Skip the animateSprites event in earlier games
1122 		if (vte->type == ANIMATE_INT) {
1123 			vte++;
1124 		// For animated heart in Elvira 2
1125 		} else if (getGameType() == GType_ELVIRA2 && vte->id == 100) {
1126 			vte++;
1127 		} else {
1128 			vte2 = vte;
1129 			while (vte2->delay) {
1130 				memcpy(vte2, vte2 + 1, sizeof(VgaTimerEntry));
1131 				vte2++;
1132 			}
1133 		}
1134 	}
1135 
1136 	if (_videoLockOut & 0x20) {
1137 		AnimTable *animTable = _screenAnim1;
1138 		while (animTable->srcPtr) {
1139 			animTable->srcPtr = 0;
1140 			animTable++;
1141 		}
1142 	}
1143 
1144 	if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP)
1145 		vcWriteVar(254, 0);
1146 
1147 	// Stop any OmniTV video that is currently been played
1148 	if (getGameType() == GType_FF || getGameType() == GType_PP)
1149 		setBitFlag(42, true);
1150 
1151 	_videoLockOut &= ~8;
1152 }
1153 
vc28_playSFX()1154 void AGOSEngine::vc28_playSFX() {
1155 	uint16 sound = vcReadNextWord();
1156 	uint16 chans = vcReadNextWord();
1157 	uint16 freq = vcReadNextWord();
1158 	uint16 flags = vcReadNextWord();
1159 	debug(0, "vc28_playSFX: (sound %d, channels %d, frequency %d, flags %d)", sound, chans, freq, flags);
1160 
1161 	loadSound(sound, freq, flags);
1162 }
1163 
vc29_stopAllSounds()1164 void AGOSEngine::vc29_stopAllSounds() {
1165 	if (getGameType() != GType_PP)
1166 		_sound->stopVoice();
1167 
1168 	_sound->stopAllSfx();
1169 }
1170 
vc30_setFrameRate()1171 void AGOSEngine::vc30_setFrameRate() {
1172 	_frameCount = vcReadNextWord();
1173 }
1174 
vc31_setWindow()1175 void AGOSEngine::vc31_setWindow() {
1176 	_windowNum = vcReadNextWord();
1177 }
1178 
vc32_saveScreen()1179 void AGOSEngine::vc32_saveScreen() {
1180 	if (getGameType() == GType_PN) {
1181 		Graphics::Surface *screen = _system->lockScreen();
1182 		byte *dst = getBackGround();
1183 		byte *src = (byte *)screen->getPixels();
1184 		for (int i = 0; i < _screenHeight; i++) {
1185 			memcpy(dst, src, _screenWidth);
1186 			dst += _backGroundBuf->pitch;
1187 			src += screen->pitch;
1188 		}
1189 		_system->unlockScreen();
1190 	} else {
1191 		uint16 xoffs = _videoWindows[4 * 4 + 0] * 16;
1192 		uint16 yoffs = _videoWindows[4 * 4 + 1];
1193 		uint16 width = _videoWindows[4 * 4 + 2] * 16;
1194 		uint16 height = _videoWindows[4 * 4 + 3];
1195 
1196 		byte *dst = (byte *)_backGroundBuf->getBasePtr(xoffs, yoffs);
1197 		byte *src = (byte *)_window4BackScn->getPixels();
1198 		uint16 srcWidth = _videoWindows[4 * 4 + 2] * 16;
1199 		for (; height > 0; height--) {
1200 			memcpy(dst, src, width);
1201 			dst += _backGroundBuf->pitch;
1202 			src += srcWidth;
1203 		}
1204 	}
1205 }
1206 
vc33_setMouseOn()1207 void AGOSEngine::vc33_setMouseOn() {
1208 	if (_mouseHideCount != 0) {
1209 		_mouseHideCount = 1;
1210 		if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
1211 			// Set mouse palette
1212 			_displayPalette[65 * 3 + 0] = 48 * 4;
1213 			_displayPalette[65 * 3 + 1] = 48 * 4;
1214 			_displayPalette[65 * 3 + 2] = 40 * 4;
1215 			_paletteFlag = 1;
1216 		}
1217 		mouseOn();
1218 	}
1219 }
1220 
vc34_setMouseOff()1221 void AGOSEngine::vc34_setMouseOff() {
1222 	mouseOff();
1223 	_mouseHideCount = 200;
1224 	_leftButtonDown = false;
1225 }
1226 
clearVideoBackGround(uint16 num,uint16 color)1227 void AGOSEngine::clearVideoBackGround(uint16 num, uint16 color) {
1228 	const uint16 *vlut = &_videoWindows[num * 4];
1229 	byte *dst = (byte *)_backGroundBuf->getBasePtr(vlut[0] * 16, vlut[1]);
1230 
1231 	for (uint h = 0; h < vlut[3]; h++) {
1232 		memset(dst, color, vlut[2] * 16);
1233 		dst += _backGroundBuf->pitch;
1234 	}
1235 }
1236 
clearVideoWindow(uint16 num,uint16 color)1237 void AGOSEngine::clearVideoWindow(uint16 num, uint16 color) {
1238 	if (getGameType() == GType_ELVIRA1) {
1239 		if (num == 2 || num == 6)
1240 			return;
1241 	} else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
1242 		if (num != 4 && num < 10)
1243 			return;
1244 	} else if (getGameType() == GType_SIMON1) {
1245 		if (num != 4)
1246 			return;
1247 	}
1248 
1249 	if (getGameType() == GType_ELVIRA1 && num == 3) {
1250 		Graphics::Surface *screen = _system->lockScreen();
1251 		byte *dst = (byte *)screen->getPixels();
1252 		for (int i = 0; i < _screenHeight; i++) {
1253 			memset(dst, color, _screenWidth);
1254 			dst += screen->pitch;
1255 		}
1256 		 _system->unlockScreen();
1257 	} else {
1258 		const uint16 *vlut = &_videoWindows[num * 4];
1259 		uint16 xoffs = (vlut[0] - _videoWindows[16]) * 16;
1260 		uint16 yoffs = (vlut[1] - _videoWindows[17]);
1261 		uint16 dstWidth = _videoWindows[18] * 16;
1262 		// TODO: Is there any known connection between dstWidth and the pitch
1263 		// of the _window4BackScn Surface? If so, we might be able to pass
1264 		// yoffs as proper y parameter to getBasePtr.
1265 		byte *dst = (byte *)_window4BackScn->getBasePtr(xoffs, 0) + yoffs * dstWidth;
1266 
1267 		setMoveRect(0, 0, vlut[2] * 16, vlut[3]);
1268 
1269 		for (uint h = 0; h < vlut[3]; h++) {
1270 			memset(dst, color, vlut[2] * 16);
1271 			dst += dstWidth;
1272 		}
1273 
1274 		_window4Flag = 1;
1275 	}
1276 }
1277 
vc35_clearWindow()1278 void AGOSEngine::vc35_clearWindow() {
1279 	uint16 num = vcReadNextWord();
1280 	uint16 color = vcReadNextWord();
1281 
1282 	// Clear video background
1283 	if (getGameType() == GType_ELVIRA1) {
1284 		if (num == 2 || num == 6)
1285 			return;
1286 	} else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
1287 		if (num != 4 && num < 10)
1288 			return;
1289 	} else if (getGameType() == GType_SIMON1) {
1290 		if (num != 4)
1291 			return;
1292 	}
1293 
1294 	// Clear video window
1295 	clearVideoWindow(num, color);
1296 	clearVideoBackGround(num, color);
1297 	_vgaSpriteChanged++;
1298 }
1299 
vc36_setWindowImage()1300 void AGOSEngine::vc36_setWindowImage() {
1301 	_displayFlag = 0;
1302 	uint16 vga_res = vcReadNextWord();
1303 	uint16 windowNum = vcReadNextWord();
1304 	setWindowImage(windowNum, vga_res);
1305 }
1306 
vc37_pokePalette()1307 void AGOSEngine::vc37_pokePalette() {
1308 	uint16 offs = vcReadNextWord();
1309 	uint16 color = vcReadNextWord();
1310 
1311 	// PC EGA version of Personal Nightmare uses standard EGA palette
1312 	if (getGameType() == GType_PN && (getFeatures() & GF_EGA))
1313 		return;
1314 
1315 	byte *palptr = _displayPalette + offs * 3;
1316 	palptr[0] = ((color & 0xf00) >> 8) * 32;
1317 	palptr[1] = ((color & 0x0f0) >> 4) * 32;
1318 	palptr[2] = ((color & 0x00f) >> 0) * 32;
1319 
1320 	if (!(_videoLockOut & 0x20)) {
1321 		_paletteFlag = 1;
1322 		_displayFlag++;
1323 	}
1324 }
1325 
vc38_ifVarNotZero()1326 void AGOSEngine::vc38_ifVarNotZero() {
1327 	uint16 var;
1328 	if (getGameType() == GType_PP)
1329 		var = vcReadVarOrWord();
1330 	else
1331 		var = vcReadNextWord();
1332 
1333 	if (vcReadVar(var) == 0)
1334 		vcSkipNextInstruction();
1335 }
1336 
vc39_setVar()1337 void AGOSEngine::vc39_setVar() {
1338 	uint16 var;
1339 	if (getGameType() == GType_PP)
1340 		var = vcReadVarOrWord();
1341 	else
1342 		var = vcReadNextWord();
1343 
1344 	int16 value = vcReadNextWord();
1345 	vcWriteVar(var, value);
1346 }
1347 
vc40_scrollRight()1348 void AGOSEngine::vc40_scrollRight() {
1349 	uint16 var = vcReadNextWord();
1350 	int16 value = vcReadVar(var) + vcReadNextWord();
1351 
1352 	if (getGameType() == GType_SIMON2 && var == 15 && !getBitFlag(80)) {
1353 		if ((_scrollCount < 0) || (_scrollCount == 0 && _scrollFlag == 0)) {
1354 			_scrollCount = 0;
1355 			if (value - _scrollX >= 30) {
1356 				_scrollCount = MIN(20, _scrollXMax - _scrollX);
1357 				addVgaEvent(6, SCROLL_EVENT, NULL, 0, 0);
1358 			}
1359 		}
1360 	}
1361 
1362 	vcWriteVar(var, value);
1363 }
1364 
vc41_scrollLeft()1365 void AGOSEngine::vc41_scrollLeft() {
1366 	uint16 var = vcReadNextWord();
1367 	int16 value = vcReadVar(var) - vcReadNextWord();
1368 
1369 	if (getGameType() == GType_SIMON2 && var == 15 && !getBitFlag(80)) {
1370 		if ((_scrollCount > 0) || (_scrollCount == 0 && _scrollFlag == 0)) {
1371 			_scrollCount = 0;
1372 			if ((uint16)(value - _scrollX) < 11) {
1373 				_scrollCount = -MIN(20, (int)_scrollX);
1374 				addVgaEvent(6, SCROLL_EVENT, NULL, 0, 0);
1375 			}
1376 		}
1377 	}
1378 
1379 	vcWriteVar(var, value);
1380 }
1381 
vc42_delayIfNotEQ()1382 void AGOSEngine::vc42_delayIfNotEQ() {
1383 	uint16 val = vcReadVar(vcReadNextWord());
1384 	if (val != vcReadNextWord()) {
1385 		addVgaEvent(_frameCount + 1, ANIMATE_EVENT, _vcPtr - 4, _vgaCurSpriteId, _vgaCurZoneNum);
1386 		_vcPtr = (byte *)&_vcGetOutOfCode;
1387 	}
1388 }
1389 
1390 } // End of namespace AGOS
1391