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