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 "common/config-manager.h"
24 #include "common/system.h"
25 
26 #include "scumm/actor.h"
27 #include "scumm/charset.h"
28 #include "scumm/file.h"
29 #include "scumm/imuse/imuse.h"
30 #include "scumm/imuse_digi/dimuse.h"
31 #include "scumm/insane/insane.h"
32 #include "scumm/object.h"
33 #include "scumm/resource.h"
34 #include "scumm/scumm.h"
35 #include "scumm/scumm_v6.h"
36 #include "scumm/scumm_v7.h"
37 #include "scumm/smush/smush_player.h"
38 #include "scumm/sound.h"
39 #include "scumm/util.h"
40 #include "scumm/verbs.h"
41 
42 namespace Scumm {
43 
44 #define OPCODE(i, x)	_opcodes[i]._OPCODE(ScummEngine_v6, x)
45 
setupOpcodes()46 void ScummEngine_v6::setupOpcodes() {
47 	/* 00 */
48 	OPCODE(0x00, o6_pushByte);
49 	OPCODE(0x01, o6_pushWord);
50 	OPCODE(0x02, o6_pushByteVar);
51 	OPCODE(0x03, o6_pushWordVar);
52 	/* 04 */
53 	OPCODE(0x06, o6_byteArrayRead);
54 	OPCODE(0x07, o6_wordArrayRead);
55 	/* 08 */
56 	OPCODE(0x0a, o6_byteArrayIndexedRead);
57 	OPCODE(0x0b, o6_wordArrayIndexedRead);
58 	/* 0C */
59 	OPCODE(0x0c, o6_dup);
60 	OPCODE(0x0d, o6_not);
61 	OPCODE(0x0e, o6_eq);
62 	OPCODE(0x0f, o6_neq);
63 	/* 10 */
64 	OPCODE(0x10, o6_gt);
65 	OPCODE(0x11, o6_lt);
66 	OPCODE(0x12, o6_le);
67 	OPCODE(0x13, o6_ge);
68 	/* 14 */
69 	OPCODE(0x14, o6_add);
70 	OPCODE(0x15, o6_sub);
71 	OPCODE(0x16, o6_mul);
72 	OPCODE(0x17, o6_div);
73 	/* 18 */
74 	OPCODE(0x18, o6_land);
75 	OPCODE(0x19, o6_lor);
76 	OPCODE(0x1a, o6_pop);
77 	/* 1C */
78 	/* 20 */
79 	/* 24 */
80 	/* 28 */
81 	/* 2C */
82 	/* 30 */
83 	/* 34 */
84 	/* 38 */
85 	/* 3C */
86 	/* 40 */
87 	OPCODE(0x42, o6_writeByteVar);
88 	OPCODE(0x43, o6_writeWordVar);
89 	/* 44 */
90 	OPCODE(0x46, o6_byteArrayWrite);
91 	OPCODE(0x47, o6_wordArrayWrite);
92 	/* 48 */
93 	OPCODE(0x4a, o6_byteArrayIndexedWrite);
94 	OPCODE(0x4b, o6_wordArrayIndexedWrite);
95 	/* 4C */
96 	OPCODE(0x4e, o6_byteVarInc);
97 	OPCODE(0x4f, o6_wordVarInc);
98 	/* 50 */
99 	OPCODE(0x52, o6_byteArrayInc);
100 	OPCODE(0x53, o6_wordArrayInc);
101 	/* 54 */
102 	OPCODE(0x56, o6_byteVarDec);
103 	OPCODE(0x57, o6_wordVarDec);
104 	/* 58 */
105 	OPCODE(0x5a, o6_byteArrayDec);
106 	OPCODE(0x5b, o6_wordArrayDec);
107 	/* 5C */
108 	OPCODE(0x5c, o6_if);
109 	OPCODE(0x5d, o6_ifNot);
110 	OPCODE(0x5e, o6_startScript);
111 	OPCODE(0x5f, o6_startScriptQuick);
112 	/* 60 */
113 	OPCODE(0x60, o6_startObject);
114 	OPCODE(0x61, o6_drawObject);
115 	OPCODE(0x62, o6_drawObjectAt);
116 	OPCODE(0x63, o6_drawBlastObject);
117 	/* 64 */
118 	OPCODE(0x64, o6_setBlastObjectWindow);
119 	OPCODE(0x65, o6_stopObjectCode);
120 	OPCODE(0x66, o6_stopObjectCode);
121 	OPCODE(0x67, o6_endCutscene);
122 	/* 68 */
123 	OPCODE(0x68, o6_cutscene);
124 	OPCODE(0x69, o6_stopMusic);
125 	OPCODE(0x6a, o6_freezeUnfreeze);
126 	OPCODE(0x6b, o6_cursorCommand);
127 	/* 6C */
128 	OPCODE(0x6c, o6_breakHere);
129 	OPCODE(0x6d, o6_ifClassOfIs);
130 	OPCODE(0x6e, o6_setClass);
131 	OPCODE(0x6f, o6_getState);
132 	/* 70 */
133 	OPCODE(0x70, o6_setState);
134 	OPCODE(0x71, o6_setOwner);
135 	OPCODE(0x72, o6_getOwner);
136 	OPCODE(0x73, o6_jump);
137 	/* 74 */
138 	OPCODE(0x74, o6_startSound);
139 	OPCODE(0x75, o6_stopSound);
140 	OPCODE(0x76, o6_startMusic);
141 	OPCODE(0x77, o6_stopObjectScript);
142 	/* 78 */
143 	OPCODE(0x78, o6_panCameraTo);
144 	OPCODE(0x79, o6_actorFollowCamera);
145 	OPCODE(0x7a, o6_setCameraAt);
146 	OPCODE(0x7b, o6_loadRoom);
147 	/* 7C */
148 	OPCODE(0x7c, o6_stopScript);
149 	OPCODE(0x7d, o6_walkActorToObj);
150 	OPCODE(0x7e, o6_walkActorTo);
151 	OPCODE(0x7f, o6_putActorAtXY);
152 	/* 80 */
153 	OPCODE(0x80, o6_putActorAtObject);
154 	OPCODE(0x81, o6_faceActor);
155 	OPCODE(0x82, o6_animateActor);
156 	OPCODE(0x83, o6_doSentence);
157 	/* 84 */
158 	OPCODE(0x84, o6_pickupObject);
159 	OPCODE(0x85, o6_loadRoomWithEgo);
160 	OPCODE(0x87, o6_getRandomNumber);
161 	/* 88 */
162 	OPCODE(0x88, o6_getRandomNumberRange);
163 	OPCODE(0x8a, o6_getActorMoving);
164 	OPCODE(0x8b, o6_isScriptRunning);
165 	/* 8C */
166 	OPCODE(0x8c, o6_getActorRoom);
167 	OPCODE(0x8d, o6_getObjectX);
168 	OPCODE(0x8e, o6_getObjectY);
169 	OPCODE(0x8f, o6_getObjectOldDir);
170 	/* 90 */
171 	OPCODE(0x90, o6_getActorWalkBox);
172 	OPCODE(0x91, o6_getActorCostume);
173 	OPCODE(0x92, o6_findInventory);
174 	OPCODE(0x93, o6_getInventoryCount);
175 	/* 94 */
176 	OPCODE(0x94, o6_getVerbFromXY);
177 	OPCODE(0x95, o6_beginOverride);
178 	OPCODE(0x96, o6_endOverride);
179 	OPCODE(0x97, o6_setObjectName);
180 	/* 98 */
181 	OPCODE(0x98, o6_isSoundRunning);
182 	OPCODE(0x99, o6_setBoxFlags);
183 	OPCODE(0x9a, o6_createBoxMatrix);
184 	OPCODE(0x9b, o6_resourceRoutines);
185 	/* 9C */
186 	OPCODE(0x9c, o6_roomOps);
187 	OPCODE(0x9d, o6_actorOps);
188 	OPCODE(0x9e, o6_verbOps);
189 	OPCODE(0x9f, o6_getActorFromXY);
190 	/* A0 */
191 	OPCODE(0xa0, o6_findObject);
192 	OPCODE(0xa1, o6_pseudoRoom);
193 	OPCODE(0xa2, o6_getActorElevation);
194 	OPCODE(0xa3, o6_getVerbEntrypoint);
195 	/* A4 */
196 	OPCODE(0xa4, o6_arrayOps);
197 	OPCODE(0xa5, o6_saveRestoreVerbs);
198 	OPCODE(0xa6, o6_drawBox);
199 	OPCODE(0xa7, o6_pop);
200 	/* A8 */
201 	OPCODE(0xa8, o6_getActorWidth);
202 	OPCODE(0xa9, o6_wait);
203 	OPCODE(0xaa, o6_getActorScaleX);
204 	OPCODE(0xab, o6_getActorAnimCounter);
205 	/* AC */
206 	OPCODE(0xac, o6_soundKludge);
207 	OPCODE(0xad, o6_isAnyOf);
208 	OPCODE(0xae, o6_systemOps);
209 	OPCODE(0xaf, o6_isActorInBox);
210 	/* B0 */
211 	OPCODE(0xb0, o6_delay);
212 	OPCODE(0xb1, o6_delaySeconds);
213 	OPCODE(0xb2, o6_delayMinutes);
214 	OPCODE(0xb3, o6_stopSentence);
215 	/* B4 */
216 	OPCODE(0xb4, o6_printLine);
217 	OPCODE(0xb5, o6_printText);
218 	OPCODE(0xb6, o6_printDebug);
219 	OPCODE(0xb7, o6_printSystem);
220 	/* B8 */
221 	OPCODE(0xb8, o6_printActor);
222 	OPCODE(0xb9, o6_printEgo);
223 	OPCODE(0xba, o6_talkActor);
224 	OPCODE(0xbb, o6_talkEgo);
225 	/* BC */
226 	OPCODE(0xbc, o6_dimArray);
227 	OPCODE(0xbd, o6_dummy);
228 	OPCODE(0xbe, o6_startObjectQuick);
229 	OPCODE(0xbf, o6_startScriptQuick2);
230 	/* C0 */
231 	OPCODE(0xc0, o6_dim2dimArray);
232 	/* C4 */
233 	OPCODE(0xc4, o6_abs);
234 	OPCODE(0xc5, o6_distObjectObject);
235 	OPCODE(0xc6, o6_distObjectPt);
236 	OPCODE(0xc7, o6_distPtPt);
237 	/* C8 */
238 	OPCODE(0xc8, o6_kernelGetFunctions);
239 	OPCODE(0xc9, o6_kernelSetFunctions);
240 	OPCODE(0xca, o6_delayFrames);
241 	OPCODE(0xcb, o6_pickOneOf);
242 	/* CC */
243 	OPCODE(0xcc, o6_pickOneOfDefault);
244 	OPCODE(0xcd, o6_stampObject);
245 	/* D0 */
246 	OPCODE(0xd0, o6_getDateTime);
247 	OPCODE(0xd1, o6_stopTalking);
248 	OPCODE(0xd2, o6_getAnimateVariable);
249 	/* D4 */
250 	OPCODE(0xd4, o6_shuffle);
251 	OPCODE(0xd5, o6_jumpToScript);
252 	OPCODE(0xd6, o6_band);
253 	OPCODE(0xd7, o6_bor);
254 	/* D8 */
255 	OPCODE(0xd8, o6_isRoomScriptRunning);
256 	/* DC */
257 	OPCODE(0xdd, o6_findAllObjects);
258 	/* E0 */
259 	OPCODE(0xe1, o6_getPixel);
260 	OPCODE(0xe3, o6_pickVarRandom);
261 	/* E4 */
262 	OPCODE(0xe4, o6_setBoxSet);
263 	/* E8 */
264 	/* EC */
265 	OPCODE(0xec, o6_getActorLayer);
266 	OPCODE(0xed, o6_getObjectNewDir);
267 }
268 
popRoomAndObj(int * room)269 int ScummEngine_v6::popRoomAndObj(int *room) {
270 	int obj;
271 
272 	if (_game.version >= 7) {
273 		obj = pop();
274 		*room = getObjectRoom(obj);
275 	} else {
276 		*room = pop();
277 		obj = pop();
278 	}
279 
280 	return obj;
281 }
282 
defineArray(int array,int type,int dim2,int dim1)283 byte *ScummEngine_v6::defineArray(int array, int type, int dim2, int dim1) {
284 	int id;
285 	int size;
286 	ArrayHeader *ah;
287 
288 	assert(0 <= type && type <= 5);
289 
290 
291 	if (_game.heversion >= 61) {
292 		if (type == kBitArray || type == kNibbleArray)
293 			type = kByteArray;
294 	} else {
295 		// NOTE: The following code turns all arrays except string arrays into
296 		// integer arrays. There seems to be no reason for this, and it wastes
297 		// space. However, we can't just remove this either, as that would
298 		// break savegame compatibility. So do not touch this unless you are
299 		// also adding code which updates old savegames, too. And of course
300 		// readArray() and writeArray() would have to be updated, too...
301 		if (type != kStringArray)
302 			type = kIntArray;
303 	}
304 
305 	nukeArray(array);
306 
307 	id = findFreeArrayId();
308 
309 	if (_game.version == 8) {
310 		if (array & 0x40000000) {
311 		}
312 
313 		if (array & 0x80000000) {
314 			error("Can't define bit variable as array pointer");
315 		}
316 
317 		size = (type == kIntArray) ? 4 : 1;
318 	} else {
319 		if (array & 0x4000) {
320 		}
321 
322 		if (array & 0x8000) {
323 			error("Can't define bit variable as array pointer");
324 		}
325 
326 		size = (type == kIntArray) ? 2 : 1;
327 	}
328 
329 	writeVar(array, id);
330 
331 	size *= dim2 + 1;
332 	size *= dim1 + 1;
333 
334 	ah = (ArrayHeader *)_res->createResource(rtString, id, size + sizeof(ArrayHeader));
335 
336 	ah->type = TO_LE_16(type);
337 	ah->dim1 = TO_LE_16(dim1 + 1);
338 	ah->dim2 = TO_LE_16(dim2 + 1);
339 
340 	return ah->data;
341 }
342 
nukeArray(int a)343 void ScummEngine_v6::nukeArray(int a) {
344 	int data;
345 
346 	data = readVar(a);
347 
348 	if (_game.heversion >= 80)
349 		data &= ~0x33539000;
350 
351 	if (data)
352 		_res->nukeResource(rtString, data);
353 	if (_game.heversion >= 60)
354 		_arraySlot[data] = 0;
355 
356 	writeVar(a, 0);
357 }
358 
findFreeArrayId()359 int ScummEngine_v6::findFreeArrayId() {
360 	const ResourceManager::ResTypeData &rtd = _res->_types[rtString];
361 	int i;
362 
363 	for (i = 1; i < _numArray; i++) {
364 		if (!rtd[i]._address)
365 			return i;
366 	}
367 	error("Out of array pointers, %d max", _numArray);
368 	return -1;
369 }
370 
371 #define SWAP16(x)  x = SWAP_BYTES_16(x)
372 
getArray(int array)373 ScummEngine_v6::ArrayHeader *ScummEngine_v6::getArray(int array) {
374 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
375 	if (!ah)
376 		return 0;
377 
378 	if (_game.heversion == 0) {
379 		// Workaround for a long standing bug where we saved array headers in native
380 		// endianness, instead of a fixed endianness. We now always store the
381 		// dimensions in little endian byte order. But to stay compatible with older
382 		// savegames, we try to detect savegames which were created on a big endian
383 		// system and convert them to the proper little endian format on the fly.
384 		if ((FROM_LE_16(ah->dim1) & 0xF000) || (FROM_LE_16(ah->dim2) & 0xF000) || (FROM_LE_16(ah->type) & 0xFF00)) {
385 			SWAP16(ah->dim1);
386 			SWAP16(ah->dim2);
387 			SWAP16(ah->type);
388 		}
389 	}
390 
391 	return ah;
392 }
393 
readArray(int array,int idx,int base)394 int ScummEngine_v6::readArray(int array, int idx, int base) {
395 	ArrayHeader *ah = getArray(array);
396 
397 	if (!ah)
398 		error("readArray: invalid array %d (%d)", array, readVar(array));
399 
400 	// WORKAROUND bug #600. This is clearly a script bug, as this script
401 	// excerpt shows nicely:
402 	// ...
403 	// [03A7] (5D)         if (isAnyOf(array-447[localvar13][localvar14],[0,4])) {
404 	// [03BD] (5D)           if ((localvar13 != -1) && (localvar14 != -1)) {
405 	// [03CF] (B6)             printDebug.begin()
406 	// ...
407 	// So it checks for invalid array indices only *after* using them to access
408 	// the array. Ouch.
409 	if (_game.id == GID_FT && array == 447 && _currentRoom == 95 && vm.slot[_currentScript].number == 2010 && idx == -1 && base == -1) {
410 		return 0;
411 	}
412 
413 	const int offset = base + idx * FROM_LE_16(ah->dim1);
414 
415 	if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
416 		error("readArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
417 			array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
418 	}
419 
420 	int val;
421 	if (FROM_LE_16(ah->type) != kIntArray) {
422 		val = ah->data[offset];
423 	} else if (_game.version == 8) {
424 		val = (int32)READ_LE_UINT32(ah->data + offset * 4);
425 	} else {
426 		val = (int16)READ_LE_UINT16(ah->data + offset * 2);
427 	}
428 	return val;
429 }
430 
writeArray(int array,int idx,int base,int value)431 void ScummEngine_v6::writeArray(int array, int idx, int base, int value) {
432 	ArrayHeader *ah = getArray(array);
433 	if (!ah)
434 		return;
435 
436 	const int offset = base + idx * FROM_LE_16(ah->dim1);
437 
438 	if (offset < 0 || offset >= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2)) {
439 		error("writeArray: array %d out of bounds: [%d,%d] exceeds [%d,%d]",
440 			array, base, idx, FROM_LE_16(ah->dim1), FROM_LE_16(ah->dim2));
441 	}
442 
443 	if (FROM_LE_16(ah->type) != kIntArray) {
444 		ah->data[offset] = value;
445 	} else if (_game.version == 8) {
446 		WRITE_LE_UINT32(ah->data + offset * 4, value);
447 	} else {
448 		WRITE_LE_UINT16(ah->data + offset * 2, value);
449 	}
450 }
451 
readArrayFromIndexFile()452 void ScummEngine_v6::readArrayFromIndexFile() {
453 	int num;
454 	int a, b, c;
455 
456 	while ((num = _fileHandle->readUint16LE()) != 0) {
457 		a = _fileHandle->readUint16LE();
458 		b = _fileHandle->readUint16LE();
459 		c = _fileHandle->readUint16LE();
460 		if (c == kBitArray)
461 			defineArray(num, kBitArray, a, b);
462 		else
463 			defineArray(num, kIntArray, a, b);
464 	}
465 }
466 
getStackList(int * args,uint maxnum)467 int ScummEngine_v6::getStackList(int *args, uint maxnum) {
468 	uint num, i;
469 
470 	for (i = 0; i < maxnum; i++)
471 		args[i] = 0;
472 
473 	num = pop();
474 
475 	if (num > maxnum)
476 		error("Too many items %d in stack list, max %d", num, maxnum);
477 
478 	i = num;
479 	while (i--) {
480 		args[i] = pop();
481 	}
482 
483 	return num;
484 }
485 
o6_pushByte()486 void ScummEngine_v6::o6_pushByte() {
487 	push(fetchScriptByte());
488 }
489 
o6_pushWord()490 void ScummEngine_v6::o6_pushWord() {
491 	push(fetchScriptWordSigned());
492 }
493 
o6_pushByteVar()494 void ScummEngine_v6::o6_pushByteVar() {
495 	push(readVar(fetchScriptByte()));
496 }
497 
o6_pushWordVar()498 void ScummEngine_v6::o6_pushWordVar() {
499 	push(readVar(fetchScriptWord()));
500 }
501 
o6_byteArrayRead()502 void ScummEngine_v6::o6_byteArrayRead() {
503 	int base = pop();
504 	push(readArray(fetchScriptByte(), 0, base));
505 }
506 
o6_wordArrayRead()507 void ScummEngine_v6::o6_wordArrayRead() {
508 	int base = pop();
509 	push(readArray(fetchScriptWord(), 0, base));
510 }
511 
o6_byteArrayIndexedRead()512 void ScummEngine_v6::o6_byteArrayIndexedRead() {
513 	int base = pop();
514 	int idx = pop();
515 	push(readArray(fetchScriptByte(), idx, base));
516 }
517 
o6_wordArrayIndexedRead()518 void ScummEngine_v6::o6_wordArrayIndexedRead() {
519 	int base = pop();
520 	int idx = pop();
521 	push(readArray(fetchScriptWord(), idx, base));
522 }
523 
o6_dup()524 void ScummEngine_v6::o6_dup() {
525 	int a = pop();
526 	push(a);
527 	push(a);
528 }
529 
o6_not()530 void ScummEngine_v6::o6_not() {
531 	push(pop() == 0);
532 }
533 
o6_eq()534 void ScummEngine_v6::o6_eq() {
535 	int a = pop();
536 	int b = pop();
537 
538 	// WORKAROUND: Forces the game version string set via script 1 to be used in both Macintosh and Windows versions,
539 	// when checking for save game compatibility. Allows saved games to be shared between Macintosh and Windows versions.
540 	// The scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a).
541 	if (_game.id == GID_BASEBALL2001 && (vm.slot[_currentScript].number == 291 || vm.slot[_currentScript].number == 292) &&
542 		a == 2 && b == 1) {
543 		push(1);
544 	} else {
545 		push(a == b);
546 	}
547 }
548 
o6_neq()549 void ScummEngine_v6::o6_neq() {
550 	push(pop() != pop());
551 }
552 
o6_gt()553 void ScummEngine_v6::o6_gt() {
554 	int a = pop();
555 	push(pop() > a);
556 }
557 
o6_lt()558 void ScummEngine_v6::o6_lt() {
559 	int a = pop();
560 	push(pop() < a);
561 }
562 
o6_le()563 void ScummEngine_v6::o6_le() {
564 	int a = pop();
565 	push(pop() <= a);
566 }
567 
o6_ge()568 void ScummEngine_v6::o6_ge() {
569 	int a = pop();
570 	push(pop() >= a);
571 }
572 
o6_add()573 void ScummEngine_v6::o6_add() {
574 	int a = pop();
575 	push(pop() + a);
576 }
577 
o6_sub()578 void ScummEngine_v6::o6_sub() {
579 	int a = pop();
580 	push(pop() - a);
581 }
582 
o6_mul()583 void ScummEngine_v6::o6_mul() {
584 	int a = pop();
585 	push(pop() * a);
586 }
587 
o6_div()588 void ScummEngine_v6::o6_div() {
589 	int a = pop();
590 	if (a == 0)
591 		error("division by zero");
592 	push(pop() / a);
593 }
594 
o6_land()595 void ScummEngine_v6::o6_land() {
596 	int a = pop();
597 	push(pop() && a);
598 }
599 
o6_lor()600 void ScummEngine_v6::o6_lor() {
601 	int a = pop();
602 	push(pop() || a);
603 }
604 
o6_bor()605 void ScummEngine_v6::o6_bor() {
606 	int a = pop();
607 	push(pop() | a);
608 }
609 
o6_band()610 void ScummEngine_v6::o6_band() {
611 	int a = pop();
612 	push(pop() & a);
613 }
614 
o6_pop()615 void ScummEngine_v6::o6_pop() {
616 	pop();
617 }
618 
o6_writeByteVar()619 void ScummEngine_v6::o6_writeByteVar() {
620 	writeVar(fetchScriptByte(), pop());
621 }
622 
o6_writeWordVar()623 void ScummEngine_v6::o6_writeWordVar() {
624 	writeVar(fetchScriptWord(), pop());
625 }
626 
o6_byteArrayWrite()627 void ScummEngine_v6::o6_byteArrayWrite() {
628 	int a = pop();
629 	writeArray(fetchScriptByte(), 0, pop(), a);
630 }
631 
o6_wordArrayWrite()632 void ScummEngine_v6::o6_wordArrayWrite() {
633 	int a = pop();
634 	writeArray(fetchScriptWord(), 0, pop(), a);
635 }
636 
o6_byteArrayIndexedWrite()637 void ScummEngine_v6::o6_byteArrayIndexedWrite() {
638 	int val = pop();
639 	int base = pop();
640 	writeArray(fetchScriptByte(), pop(), base, val);
641 }
642 
o6_wordArrayIndexedWrite()643 void ScummEngine_v6::o6_wordArrayIndexedWrite() {
644 	int val = pop();
645 	int base = pop();
646 	writeArray(fetchScriptWord(), pop(), base, val);
647 }
648 
o6_byteVarInc()649 void ScummEngine_v6::o6_byteVarInc() {
650 	int var = fetchScriptByte();
651 	writeVar(var, readVar(var) + 1);
652 }
653 
o6_wordVarInc()654 void ScummEngine_v6::o6_wordVarInc() {
655 	int var = fetchScriptWord();
656 	writeVar(var, readVar(var) + 1);
657 }
658 
o6_byteArrayInc()659 void ScummEngine_v6::o6_byteArrayInc() {
660 	int var = fetchScriptByte();
661 	int base = pop();
662 	writeArray(var, 0, base, readArray(var, 0, base) + 1);
663 }
664 
o6_wordArrayInc()665 void ScummEngine_v6::o6_wordArrayInc() {
666 	int var = fetchScriptWord();
667 	int base = pop();
668 	writeArray(var, 0, base, readArray(var, 0, base) + 1);
669 }
670 
o6_byteVarDec()671 void ScummEngine_v6::o6_byteVarDec() {
672 	int var = fetchScriptByte();
673 	writeVar(var, readVar(var) - 1);
674 }
675 
o6_wordVarDec()676 void ScummEngine_v6::o6_wordVarDec() {
677 	int var = fetchScriptWord();
678 	writeVar(var, readVar(var) - 1);
679 }
680 
o6_byteArrayDec()681 void ScummEngine_v6::o6_byteArrayDec() {
682 	int var = fetchScriptByte();
683 	int base = pop();
684 	writeArray(var, 0, base, readArray(var, 0, base) - 1);
685 }
686 
o6_wordArrayDec()687 void ScummEngine_v6::o6_wordArrayDec() {
688 	int var = fetchScriptWord();
689 	int base = pop();
690 	writeArray(var, 0, base, readArray(var, 0, base) - 1);
691 }
692 
o6_if()693 void ScummEngine_v6::o6_if() {
694 	if (pop())
695 		o6_jump();
696 	else
697 		fetchScriptWord();
698 }
699 
o6_ifNot()700 void ScummEngine_v6::o6_ifNot() {
701 	if (!pop())
702 		o6_jump();
703 	else
704 		fetchScriptWord();
705 }
706 
o6_jump()707 void ScummEngine_v6::o6_jump() {
708 	int offset = fetchScriptWordSigned();
709 
710 	// WORKAROUND bug #6097: Pressing escape at the lake side entrance of
711 	// the cave while Putt Putt is not on solid ground and still talking
712 	// will cause the raft to disappear. This is a script bug in the
713 	// original game and affects several versions.
714 	if (_game.id == GID_PUTTZOO) {
715 		if (_game.heversion == 73 && vm.slot[_currentScript].number == 206 && offset == 176 && !isScriptRunning(202))
716 			_scummVars[244] = 35;
717 		if (_game.features & GF_HE_985 && vm.slot[_currentScript].number == 2054 && offset == 178 && !isScriptRunning(2050))
718 			_scummVars[202] = 35;
719 	}
720 
721 	// WORKAROUND bug #4464: Talking to the guard at the bigfoot party, after
722 	// he's let you inside, will cause the game to hang, if you end the conversation.
723 	// This is a script bug, due to a missing jump in one segment of the script.
724 	if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 101 && readVar(0x8000 + 97) == 1 && offset == 1) {
725 		offset = -18;
726 	}
727 
728 	_scriptPointer += offset;
729 }
730 
o6_startScript()731 void ScummEngine_v6::o6_startScript() {
732 	int args[25];
733 	int script, flags;
734 
735 	getStackList(args, ARRAYSIZE(args));
736 	script = pop();
737 	flags = pop();
738 
739 	// WORKAROUND for a bug also present in the original EXE: After greasing (or oiling?)
740 	// the cannonballs in the Plunder Town Theater, during the juggling show, the game
741 	// cuts from room 18 (backstage) to room 19 (stage).
742 	//
743 	// Usually, when loading a room script 29 handles the change of background music,
744 	// based on which room we've just loaded.
745 	// Unfortunately, during this particular cutscene, script 29 is not executing,
746 	// therefore the music is unchanged from room 18 to 19 (the muffled backstage
747 	// version is played), and is not coherent with the drums fill played afterwards
748 	// (sequence 2225), which is unmuffled.
749 	//
750 	// This fix checks for this situation happening (and only this one), and makes a call
751 	// to a soundKludge operation like script 29 would have done.
752 	if (_game.id == GID_CMI && _currentRoom == 19 &&
753 		vm.slot[_currentScript].number == 168 && script == 118) {
754 		int list[16] = { 4096, 1278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
755 		_sound->soundKludge(list, 2);
756 	}
757 
758 	// WORKAROUND bug #269: At Dino Bungee National Memorial, the buttons for
759 	// the Wally and Rex dinosaurs will always restart their speech, instead of
760 	// stopping and starting their speech. This was a script bug in the original
761 	// game.
762 	if (_game.id == GID_SAMNMAX && _roomResource == 59 &&
763 		vm.slot[_currentScript].number == 201 && script == 48) {
764 		o6_breakHere();
765 	}
766 
767 	// WORKAROUND bug #1493: In Puerto Pollo, if you have Guybrush examine
768 	// the church clock, he'll read out the current time. Nice touch, only that
769 	// it sounds crap in the german version (and maybe others, too). It seems
770 	// the original engine of the german version played just a simple fixed
771 	// text in this spot, for the above reason. Since the data files are
772 	// unchanged, it must have been an engine hack job. No idea how they did
773 	// it exactly, but this here is how we do it :-)
774 	if (_game.id == GID_CMI && script == 204 &&
775 		_currentRoom == 15 && vm.slot[_currentScript].number == 421 &&
776 		_language == Common::DE_DEU) {
777 
778 		_actorToPrintStrFor = 1;
779 		_string[0].loadDefault();
780 		actorTalk((const byte *)"/VDSO325/Whoa! Look at the time. Gotta scoot.");
781 
782 		return;
783 	}
784 
785 	// WORKAROUND bug #3591: When turning pages in the recipe book
786 	// (found on Blood Island), there is a brief moment where it displays
787 	// text from two different pages at the same time.
788 	//
789 	// The content of the books is drawing (in an endless loop) by local
790 	// script 2007. Changing the page is handled by script 2006, which
791 	// first stops script 2007; then switches the page; then restarts
792 	// script 2007. But it fails to clear the blast texts beforehand.
793 	// Hence, the next time blast text is drawn, both the old one (from
794 	// the old instance of script 2007) and the new text (from the new
795 	// instance) are briefly drawn simultaneously.
796 	//
797 	// This looks like a script bug to me (a missing call to clearTextQueue).
798 	// But this could also hint at a subtle bug in ScummVM; we should check
799 	// whether this bug occurs with the original engine or not.
800 	if (_game.id == GID_CMI && script == 2007 &&
801 		_currentRoom == 62 && vm.slot[_currentScript].number == 2006) {
802 
803 		removeBlastTexts();
804 	}
805 
806 	runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
807 }
808 
o6_jumpToScript()809 void ScummEngine_v6::o6_jumpToScript() {
810 	int args[25];
811 	int script, flags;
812 
813 	getStackList(args, ARRAYSIZE(args));
814 	script = pop();
815 	flags = pop();
816 	stopObjectCode();
817 	runScript(script, (flags & 1) != 0, (flags & 2) != 0, args);
818 }
819 
o6_startScriptQuick()820 void ScummEngine_v6::o6_startScriptQuick() {
821 	int args[25];
822 	int script;
823 	getStackList(args, ARRAYSIZE(args));
824 	script = pop();
825 	runScript(script, 0, 0, args);
826 }
827 
o6_startScriptQuick2()828 void ScummEngine_v6::o6_startScriptQuick2() {
829 	int args[25];
830 	int script;
831 	getStackList(args, ARRAYSIZE(args));
832 	script = pop();
833 	runScript(script, 0, 1, args);
834 }
835 
o6_startObject()836 void ScummEngine_v6::o6_startObject() {
837 	int args[25];
838 	int script, entryp;
839 	int flags;
840 	getStackList(args, ARRAYSIZE(args));
841 	entryp = pop();
842 	script = pop();
843 	flags = pop();
844 	runObjectScript(script, entryp, (flags & 1) != 0, (flags & 2) != 0, args);
845 }
846 
o6_startObjectQuick()847 void ScummEngine_v6::o6_startObjectQuick() {
848 	int args[25];
849 	int script, entryp;
850 	getStackList(args, ARRAYSIZE(args));
851 	entryp = pop();
852 	script = pop();
853 	runObjectScript(script, entryp, 0, 1, args);
854 }
855 
o6_drawObject()856 void ScummEngine_v6::o6_drawObject() {
857 	int state = pop();
858 	int obj = pop();
859 
860 	// This is based on disassembly
861 	if (state == 0)
862 		state = 1;
863 
864 	setObjectState(obj, state, -1, -1);
865 }
866 
o6_drawObjectAt()867 void ScummEngine_v6::o6_drawObjectAt() {
868 	int y = pop();
869 	int x = pop();
870 	int obj = pop();
871 
872 	// WORKAROUND bug #3487 : Adjust x and y position of
873 	// objects in credits sequence, to match other ports
874 	if (_game.id == GID_PUTTMOON && _game.platform == Common::kPlatform3DO &&
875 		_roomResource == 38 && vm.slot[_currentScript].number == 206) {
876 		x = y = -1;
877 	}
878 
879 	setObjectState(obj, 1, x, y);
880 }
881 
o6_stopObjectCode()882 void ScummEngine_v6::o6_stopObjectCode() {
883 	stopObjectCode();
884 }
885 
o6_endCutscene()886 void ScummEngine_v6::o6_endCutscene() {
887 	endCutscene();
888 }
889 
o6_cutscene()890 void ScummEngine_v6::o6_cutscene() {
891 	int args[25];
892 	getStackList(args, ARRAYSIZE(args));
893 	beginCutscene(args);
894 }
895 
o6_stopMusic()896 void ScummEngine_v6::o6_stopMusic() {
897 	_sound->stopAllSounds();
898 }
899 
o6_freezeUnfreeze()900 void ScummEngine_v6::o6_freezeUnfreeze() {
901 	int a = pop();
902 
903 	if (a)
904 		freezeScripts(a);
905 	else
906 		unfreezeScripts();
907 }
908 
o6_cursorCommand()909 void ScummEngine_v6::o6_cursorCommand() {
910 	int a, i;
911 	int args[16];
912 
913 	byte subOp = fetchScriptByte();
914 
915 	switch (subOp) {
916 	case 0x90:		// SO_CURSOR_ON Turn cursor on
917 		_cursor.state = 1;
918 		verbMouseOver(0);
919 		break;
920 	case 0x91:		// SO_CURSOR_OFF Turn cursor off
921 		_cursor.state = 0;
922 		verbMouseOver(0);
923 		break;
924 	case 0x92:		// SO_USERPUT_ON
925 		_userPut = 1;
926 		break;
927 	case 0x93:		// SO_USERPUT_OFF
928 		_userPut = 0;
929 		break;
930 	case 0x94:		// SO_CURSOR_SOFT_ON Turn soft cursor on
931 		_cursor.state++;
932 		if (_cursor.state > 1)
933 			error("Cursor state greater than 1 in script");
934 		verbMouseOver(0);
935 		break;
936 	case 0x95:		// SO_CURSOR_SOFT_OFF Turn soft cursor off
937 		_cursor.state--;
938 		verbMouseOver(0);
939 		break;
940 	case 0x96:		// SO_USERPUT_SOFT_ON
941 		_userPut++;
942 		break;
943 	case 0x97:		// SO_USERPUT_SOFT_OFF
944 		_userPut--;
945 		break;
946 	case 0x99:		// SO_CURSOR_IMAGE Set cursor image
947 		{
948 			int room, obj;
949 			if (_game.heversion >= 70) {
950 				obj = pop();
951 				room = getObjectRoom(obj);
952 			} else {
953 				obj = popRoomAndObj(&room);
954 			}
955 			setCursorFromImg(obj, room, 1);
956 			break;
957 		}
958 	case 0x9A:		// SO_CURSOR_HOTSPOT Set cursor hotspot
959 		a = pop();
960 		setCursorHotspot(pop(), a);
961 		updateCursor();
962 		break;
963 	case 0x9C:		// SO_CHARSET_SET
964 		initCharset(pop());
965 		break;
966 	case 0x9D:		// SO_CHARSET_COLOR
967 		getStackList(args, ARRAYSIZE(args));
968 		for (i = 0; i < 16; i++)
969 			_charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
970 		break;
971 	case 0xD6:		// SO_CURSOR_TRANSPARENT Set cursor transparent color
972 		setCursorTransparency(pop());
973 		break;
974 	default:
975 		error("o6_cursorCommand: default case %x", subOp);
976 	}
977 
978 	VAR(VAR_CURSORSTATE) = _cursor.state;
979 	VAR(VAR_USERPUT) = _userPut;
980 }
981 
o6_breakHere()982 void ScummEngine_v6::o6_breakHere() {
983 	updateScriptPtr();
984 	_currentScript = 0xFF;
985 }
986 
o6_ifClassOfIs()987 void ScummEngine_v6::o6_ifClassOfIs() {
988 	int args[16];
989 	int num, obj, cls;
990 	bool b;
991 	int cond = 1;
992 
993 	num = getStackList(args, ARRAYSIZE(args));
994 	obj = pop();
995 
996 	if (_game.heversion >= 80 && num == 0) {
997 		push(_classData[obj]);
998 		return;
999 	}
1000 
1001 	while (--num >= 0) {
1002 		cls = args[num];
1003 		b = getClass(obj, cls);
1004 		if ((cls & 0x80 && !b) || (!(cls & 0x80) && b))
1005 			cond = 0;
1006 	}
1007 	push(cond);
1008 }
1009 
o6_setClass()1010 void ScummEngine_v6::o6_setClass() {
1011 	int args[16];
1012 	int num, obj, cls;
1013 
1014 	num = getStackList(args, ARRAYSIZE(args));
1015 	obj = pop();
1016 
1017 	while (--num >= 0) {
1018 		cls = args[num];
1019 		if (cls == 0)
1020 			_classData[num] = 0;
1021 		else if (cls & 0x80)
1022 			putClass(obj, cls, 1);
1023 		else
1024 			putClass(obj, cls, 0);
1025 	}
1026 }
1027 
o6_getState()1028 void ScummEngine_v6::o6_getState() {
1029 	push(getState(pop()));
1030 }
1031 
o6_setState()1032 void ScummEngine_v6::o6_setState() {
1033 	int state = pop();
1034 	int obj = pop();
1035 
1036 	putState(obj, state);
1037 	markObjectRectAsDirty(obj);
1038 	if (_bgNeedsRedraw)
1039 		clearDrawObjectQueue();
1040 }
1041 
o6_setOwner()1042 void ScummEngine_v6::o6_setOwner() {
1043 	int owner = pop();
1044 	int obj = pop();
1045 	setOwnerOf(obj, owner);
1046 }
1047 
o6_getOwner()1048 void ScummEngine_v6::o6_getOwner() {
1049 	push(getOwner(pop()));
1050 }
1051 
o6_startSound()1052 void ScummEngine_v6::o6_startSound() {
1053 	int offset = 0;
1054 
1055 	// In Fatty Bear's Birthday Surprise the piano uses offsets 1 - 23 to
1056 	// indicate which note to play, but only when using the standard piano
1057 	// sound. See also o60_soundOps()
1058 	if (_game.heversion >= 60 && (_game.id != GID_PUTTDEMO))
1059 		offset = pop();
1060 
1061 #ifdef ENABLE_SCUMM_7_8
1062 	if (_game.version >= 7)
1063 		_imuseDigital->startSfx(pop(), 64);
1064 	else
1065 #endif
1066 		_sound->addSoundToQueue(pop(), offset);
1067 }
1068 
o6_stopSound()1069 void ScummEngine_v6::o6_stopSound() {
1070 	_sound->stopSound(pop());
1071 }
1072 
o6_startMusic()1073 void ScummEngine_v6::o6_startMusic() {
1074 	if (_game.version >= 7)
1075 		error("o6_startMusic() It shouldn't be called here for imuse digital");
1076 
1077 	_sound->addSoundToQueue(pop());
1078 }
1079 
o6_stopObjectScript()1080 void ScummEngine_v6::o6_stopObjectScript() {
1081 	stopObjectScript(pop());
1082 }
1083 
o6_panCameraTo()1084 void ScummEngine_v6::o6_panCameraTo() {
1085 	if (_game.version >= 7) {
1086 		int y = pop();
1087 		int x = pop();
1088 		panCameraTo(x, y);
1089 	} else {
1090 		panCameraTo(pop(), 0);
1091 	}
1092 }
1093 
o6_actorFollowCamera()1094 void ScummEngine_v6::o6_actorFollowCamera() {
1095 	if (_game.version >= 7)
1096 		setCameraFollows(derefActor(pop(), "actorFollowCamera"));
1097 	else
1098 		actorFollowCamera(pop());
1099 }
1100 
o6_setCameraAt()1101 void ScummEngine_v6::o6_setCameraAt() {
1102 	if (_game.version >= 7) {
1103 		int x, y;
1104 
1105 		camera._follows = 0;
1106 		VAR(VAR_CAMERA_FOLLOWED_ACTOR) = 0;
1107 
1108 		y = pop();
1109 		x = pop();
1110 
1111 		setCameraAt(x, y);
1112 	} else {
1113 		setCameraAtEx(pop());
1114 	}
1115 }
1116 
o6_loadRoom()1117 void ScummEngine_v6::o6_loadRoom() {
1118 	int room = pop();
1119 	startScene(room, 0, 0);
1120 	if (_game.heversion >= 61) {
1121 		setCameraAt(camera._cur.x, 0);
1122 	}
1123 	_fullRedraw = true;
1124 }
1125 
o6_stopScript()1126 void ScummEngine_v6::o6_stopScript() {
1127 	int script = pop();
1128 	if (script == 0)
1129 		stopObjectCode();
1130 	else
1131 		stopScript(script);
1132 }
1133 
o6_walkActorToObj()1134 void ScummEngine_v6::o6_walkActorToObj() {
1135 	int act, obj, dist;
1136 	Actor *a, *a2;
1137 	int x, y;
1138 
1139 	dist = pop();
1140 	obj = pop();
1141 	act = pop();
1142 	a = derefActor(act, "o6_walkActorToObj");
1143 
1144 	if (obj >= _numActors) {
1145 		int wio = whereIsObject(obj);
1146 
1147 		if (wio != WIO_FLOBJECT && wio != WIO_ROOM)
1148 			return;
1149 
1150 		int dir;
1151 		getObjectXYPos(obj, x, y, dir);
1152 		a->startWalkActor(x, y, dir);
1153 	} else {
1154 		a2 = derefActorSafe(obj, "o6_walkActorToObj(2)");
1155 		if (_game.id == GID_SAMNMAX && a2 == 0) {
1156 			// WORKAROUND bug #801 SAM: Fish Farm. Note quite sure why it
1157 			// happens, whether it's normal or due to a bug in the ScummVM code.
1158 			debug(0, "o6_walkActorToObj: invalid actor %d", obj);
1159 			return;
1160 		}
1161 		if (!a->isInCurrentRoom() || !a2->isInCurrentRoom())
1162 			return;
1163 		if (dist == 0) {
1164 			dist = a2->_scalex * a2->_width / 0xFF;
1165 			dist += dist / 2;
1166 		}
1167 		x = a2->getPos().x;
1168 		y = a2->getPos().y;
1169 		if (x < a->getPos().x)
1170 			x += dist;
1171 		else
1172 			x -= dist;
1173 		a->startWalkActor(x, y, -1);
1174 	}
1175 }
1176 
o6_walkActorTo()1177 void ScummEngine_v6::o6_walkActorTo() {
1178 	int x, y;
1179 	y = pop();
1180 	x = pop();
1181 	Actor *a = derefActor(pop(), "o6_walkActorTo");
1182 	a->startWalkActor(x, y, -1);
1183 }
1184 
o6_putActorAtXY()1185 void ScummEngine_v6::o6_putActorAtXY() {
1186 	int room, x, y, act;
1187 	Actor *a;
1188 
1189 	room = pop();
1190 	y = pop();
1191 	x = pop();
1192 	act = pop();
1193 	a = derefActor(act, "o6_putActorAtXY");
1194 
1195 	if (room == 0xFF || room == 0x7FFFFFFF) {
1196 		room = a->_room;
1197 	} else {
1198 		if (a->_visible && _currentRoom != room && getTalkingActor() == a->_number) {
1199 			stopTalk();
1200 		}
1201 		if (room != 0)
1202 			a->_room = room;
1203 	}
1204 	a->putActor(x, y, room);
1205 }
1206 
1207 
o6_putActorAtObject()1208 void ScummEngine_v6::o6_putActorAtObject() {
1209 	int room, obj, x, y;
1210 	Actor *a;
1211 
1212 	obj = popRoomAndObj(&room);
1213 
1214 	a = derefActor(pop(), "o6_putActorAtObject");
1215 	if (whereIsObject(obj) != WIO_NOT_FOUND) {
1216 		getObjectXYPos(obj, x, y);
1217 	} else {
1218 		x = 160;
1219 		y = 120;
1220 	}
1221 	if (room == 0xFF)
1222 		room = a->_room;
1223 	a->putActor(x, y, room);
1224 }
1225 
o6_faceActor()1226 void ScummEngine_v6::o6_faceActor() {
1227 	int obj = pop();
1228 	Actor *a = derefActor(pop(), "o6_faceActor");
1229 	a->faceToObject(obj);
1230 }
1231 
o6_animateActor()1232 void ScummEngine_v6::o6_animateActor() {
1233 	int anim = pop();
1234 	int act = pop();
1235 	if (_game.id == GID_TENTACLE && _roomResource == 57 &&
1236 		vm.slot[_currentScript].number == 19 && act == 593) {
1237 		// WORKAROUND bug #813: This very odd case (animateActor(593,250))
1238 		// occurs in DOTT, in the cutscene after George cuts down the "cherry
1239 		// tree" and the tree Laverne is trapped in vanishes...
1240 		// Not sure if this means animateActor somehow also must work for objects
1241 		// (593 is the time machine in room 57), or if this is simply a script bug.
1242 		act = 6;
1243 	}
1244 	if (_game.id == GID_SAMNMAX && _roomResource == 35 &&
1245 		vm.slot[_currentScript].number == 202 && act == 4 && anim == 14) {
1246 		// WORKAROUND bug #2068 (Animation glitch at World of Fish).
1247 		// Before starting animation 14 of the fisherman, make sure he isn't
1248 		// talking anymore. This appears to be a bug in the original game as well.
1249 		if (getTalkingActor() == 4) {
1250 			stopTalk();
1251 		}
1252 	}
1253 	Actor *a = derefActor(act, "o6_animateActor");
1254 	a->animateActor(anim);
1255 }
1256 
o6_doSentence()1257 void ScummEngine_v6::o6_doSentence() {
1258 	int verb, objectA, objectB;
1259 
1260 	objectB = pop();
1261 	if (_game.version < 8)
1262 		pop();	// dummy pop (in Sam&Max, seems to be always 0 or 130)
1263 	objectA = pop();
1264 	verb = pop();
1265 
1266 	doSentence(verb, objectA, objectB);
1267 }
1268 
o6_pickupObject()1269 void ScummEngine_v6::o6_pickupObject() {
1270 	int obj, room;
1271 	int i;
1272 
1273 	obj = popRoomAndObj(&room);
1274 	if (room == 0)
1275 		room = _roomResource;
1276 
1277 	for (i = 0; i < _numInventory; i++) {
1278 		if (_inventory[i] == (uint16)obj) {
1279 			putOwner(obj, VAR(VAR_EGO));
1280 			runInventoryScript(obj);
1281 			return;
1282 		}
1283 	}
1284 
1285 	addObjectToInventory(obj, room);
1286 	putOwner(obj, VAR(VAR_EGO));
1287 	putClass(obj, kObjectClassUntouchable, 1);
1288 	putState(obj, 1);
1289 	markObjectRectAsDirty(obj);
1290 	clearDrawObjectQueue();
1291 	runInventoryScript(obj);
1292 }
1293 
o6_loadRoomWithEgo()1294 void ScummEngine_v6::o6_loadRoomWithEgo() {
1295 	Actor *a;
1296 	int obj, room, x, y;
1297 
1298 	y = pop();
1299 	x = pop();
1300 
1301 	obj = popRoomAndObj(&room);
1302 
1303 	a = derefActor(VAR(VAR_EGO), "o6_loadRoomWithEgo");
1304 	a->putActor(0, 0, room);
1305 	_egoPositioned = false;
1306 
1307 	VAR(VAR_WALKTO_OBJ) = obj;
1308 	startScene(a->_room, a, obj);
1309 	VAR(VAR_WALKTO_OBJ) = 0;
1310 
1311 	if (_game.version == 6) {
1312 		camera._cur.x = camera._dest.x = a->getPos().x;
1313 		setCameraFollows(a, (_game.heversion >= 60));
1314 	}
1315 
1316 	_fullRedraw = true;
1317 
1318 	if (x != -1 && x != 0x7FFFFFFF) {
1319 		a->startWalkActor(x, y, -1);
1320 	}
1321 }
1322 
o6_getRandomNumber()1323 void ScummEngine_v6::o6_getRandomNumber() {
1324 	int rnd;
1325 	rnd = _rnd.getRandomNumber(ABS(pop()));
1326 	if (VAR_RANDOM_NR != 0xFF)
1327 		VAR(VAR_RANDOM_NR) = rnd;
1328 	push(rnd);
1329 }
1330 
o6_getRandomNumberRange()1331 void ScummEngine_v6::o6_getRandomNumberRange() {
1332 	int max = pop();
1333 	int min = pop();
1334 	int rnd = _rnd.getRandomNumberRng(min, max);
1335 	if (VAR_RANDOM_NR != 0xFF)
1336 		VAR(VAR_RANDOM_NR) = rnd;
1337 	push(rnd);
1338 }
1339 
o6_isScriptRunning()1340 void ScummEngine_v6::o6_isScriptRunning() {
1341 	push(isScriptRunning(pop()));
1342 }
1343 
o6_isRoomScriptRunning()1344 void ScummEngine_v6::o6_isRoomScriptRunning() {
1345 	push(isRoomScriptRunning(pop()));
1346 }
1347 
o6_getActorMoving()1348 void ScummEngine_v6::o6_getActorMoving() {
1349 	Actor *a = derefActor(pop(), "o6_getActorMoving");
1350 	push(a->_moving);
1351 }
1352 
o6_getActorRoom()1353 void ScummEngine_v6::o6_getActorRoom() {
1354 	int act = pop();
1355 
1356 	if (act == 0) {
1357 		// This case occurs at the very least in COMI. That's because in COMI's script 28,
1358 		// there is a check which looks as follows:
1359 		//   if (((VAR_TALK_ACTOR != 0) && (VAR_HAVE_MSG == 1)) &&
1360 		//        (getActorRoom(VAR_TALK_ACTOR) == VAR_ROOM))
1361 		// Due to the way this is represented in bytecode, the engine cannot
1362 		// short circuit. Hence, even though this would be perfectly fine code
1363 		// in C/C++, here it can (and does) lead to getActorRoom(0) being
1364 		// invoked. We silently ignore this.
1365 		push(0);
1366 		return;
1367 	}
1368 
1369 	if (act == 255) {
1370 		// This case also occurs in COMI...
1371 		push(0);
1372 		return;
1373 	}
1374 
1375 	Actor *a = derefActor(act, "o6_getActorRoom");
1376 	push(a->_room);
1377 }
1378 
o6_getActorWalkBox()1379 void ScummEngine_v6::o6_getActorWalkBox() {
1380 	Actor *a = derefActor(pop(), "o6_getActorWalkBox");
1381 	push(a->_ignoreBoxes ? 0 : a->_walkbox);
1382 }
1383 
o6_getActorCostume()1384 void ScummEngine_v6::o6_getActorCostume() {
1385 	Actor *a = derefActor(pop(), "o6_getActorCostume");
1386 	push(a->_costume);
1387 }
1388 
o6_getActorElevation()1389 void ScummEngine_v6::o6_getActorElevation() {
1390 	Actor *a = derefActor(pop(), "o6_getActorElevation");
1391 	push(a->getElevation());
1392 }
1393 
o6_getActorWidth()1394 void ScummEngine_v6::o6_getActorWidth() {
1395 	Actor *a = derefActor(pop(), "o6_getActorWidth");
1396 	push(a->_width);
1397 }
1398 
o6_getActorScaleX()1399 void ScummEngine_v6::o6_getActorScaleX() {
1400 	Actor *a = derefActor(pop(), "o6_getActorScaleX");
1401 	push(a->_scalex);
1402 }
1403 
o6_getActorAnimCounter()1404 void ScummEngine_v6::o6_getActorAnimCounter() {
1405 	Actor *a = derefActor(pop(), "o6_getActorAnimCounter");
1406 	push(a->_cost.animCounter);
1407 }
1408 
o6_getAnimateVariable()1409 void ScummEngine_v6::o6_getAnimateVariable() {
1410 	int var = pop();
1411 	Actor *a = derefActor(pop(), "o6_getAnimateVariable");
1412 
1413 	// WORKAROUND: In Backyard Baseball 2001 and 2003,
1414 	// bunting a foul ball as Pete Wheeler may softlock the game
1415 	// with an animation loop if the ball goes way into
1416 	// the left or right field line.
1417 	//
1418 	// This is a script bug because Pete's actor variable never
1419 	// sets to 1 in this condition and script room-4-2105
1420 	// (or room-3-2105 in 2003) will always break.
1421 	// We fix that by forcing Pete to play the return animation
1422 	// regardless if the ball's foul or not.
1423 	if ((_game.id == GID_BASEBALL2001 || _game.id == GID_BASEBALL2003) && \
1424 			_currentRoom == ((_game.id == GID_BASEBALL2001) ? 4 : 3) && \
1425 			vm.slot[_currentScript].number == 2105 && \
1426 			a->_costume == ((_game.id == GID_BASEBALL2001) ? 107 : 99) && \
1427 			// Room variable 5 to ensure this workaround executes only once at
1428 			// the beginning of the script and room variable 22 to check if we
1429 			// are bunting.
1430 			readVar(0x8000 + 5) != 0 && readVar(0x8000 + 22) == 4)
1431 		push(1);
1432 	else
1433 		push(a->getAnimVar(var));
1434 }
1435 
o6_isActorInBox()1436 void ScummEngine_v6::o6_isActorInBox() {
1437 	int box = pop();
1438 	Actor *a = derefActor(pop(), "o6_isActorInBox");
1439 	push(checkXYInBoxBounds(box, a->getPos().x, a->getPos().y));
1440 }
1441 
o6_getActorLayer()1442 void ScummEngine_v6::o6_getActorLayer() {
1443 	Actor *a = derefActor(pop(), "getActorLayer");
1444 	push(a->_layer);
1445 }
1446 
o6_getObjectX()1447 void ScummEngine_v6::o6_getObjectX() {
1448 	push(getObjX(pop()));
1449 }
1450 
o6_getObjectY()1451 void ScummEngine_v6::o6_getObjectY() {
1452 	push(getObjY(pop()));
1453 }
1454 
o6_getObjectOldDir()1455 void ScummEngine_v6::o6_getObjectOldDir() {
1456 	push(getObjOldDir(pop()));
1457 }
1458 
o6_getObjectNewDir()1459 void ScummEngine_v6::o6_getObjectNewDir() {
1460 	push(getObjNewDir(pop()));
1461 }
1462 
o6_findInventory()1463 void ScummEngine_v6::o6_findInventory() {
1464 	int idx = pop();
1465 	int owner = pop();
1466 	push(findInventory(owner, idx));
1467 }
1468 
o6_getInventoryCount()1469 void ScummEngine_v6::o6_getInventoryCount() {
1470 	push(getInventoryCount(pop()));
1471 }
1472 
o6_getVerbFromXY()1473 void ScummEngine_v6::o6_getVerbFromXY() {
1474 	int y = pop();
1475 	int x = pop();
1476 	int over = findVerbAtPos(x, y);
1477 	if (over)
1478 		over = _verbs[over].verbid;
1479 	push(over);
1480 }
1481 
o6_beginOverride()1482 void ScummEngine_v6::o6_beginOverride() {
1483 	beginOverride();
1484 	_skipVideo = 0;
1485 }
1486 
o6_endOverride()1487 void ScummEngine_v6::o6_endOverride() {
1488 	endOverride();
1489 }
1490 
o6_setObjectName()1491 void ScummEngine_v6::o6_setObjectName() {
1492 	int obj = pop();
1493 	setObjectName(obj);
1494 }
1495 
o6_isSoundRunning()1496 void ScummEngine_v6::o6_isSoundRunning() {
1497 	int snd = pop();
1498 
1499 	if (snd)
1500 		snd = _sound->isSoundRunning(snd);
1501 
1502 	push(snd);
1503 }
1504 
o6_setBoxFlags()1505 void ScummEngine_v6::o6_setBoxFlags() {
1506 	int table[65];
1507 	int num, value;
1508 
1509 	value = pop();
1510 	num = getStackList(table, ARRAYSIZE(table));
1511 
1512 	while (--num >= 0) {
1513 		setBoxFlags(table[num], value);
1514 	}
1515 }
1516 
o6_createBoxMatrix()1517 void ScummEngine_v6::o6_createBoxMatrix() {
1518 	createBoxMatrix();
1519 
1520 	if ((_game.id == GID_DIG) || (_game.id == GID_CMI))
1521 		putActors();
1522 }
1523 
o6_resourceRoutines()1524 void ScummEngine_v6::o6_resourceRoutines() {
1525 	int resid;
1526 
1527 	byte subOp = fetchScriptByte();
1528 
1529 	switch (subOp) {
1530 	case 100:		// SO_LOAD_SCRIPT
1531 		resid = pop();
1532 		if (_game.version >= 7)
1533 			if (resid >= _numGlobalScripts)
1534 				break;
1535 		ensureResourceLoaded(rtScript, resid);
1536 		break;
1537 	case 101:		// SO_LOAD_SOUND
1538 		resid = pop();
1539 		ensureResourceLoaded(rtSound, resid);
1540 		break;
1541 	case 102:		// SO_LOAD_COSTUME
1542 		resid = pop();
1543 		ensureResourceLoaded(rtCostume, resid);
1544 		break;
1545 	case 103:		// SO_LOAD_ROOM
1546 		resid = pop();
1547 		ensureResourceLoaded(rtRoom, resid);
1548 		break;
1549 	case 104:		// SO_NUKE_SCRIPT
1550 		resid = pop();
1551 		if (_game.version >= 7)
1552 			if (resid >= _numGlobalScripts)
1553 				break;
1554 		_res->setResourceCounter(rtScript, resid, 0x7F);
1555 		break;
1556 	case 105:		// SO_NUKE_SOUND
1557 		resid = pop();
1558 		_res->setResourceCounter(rtSound, resid, 0x7F);
1559 		break;
1560 	case 106:		// SO_NUKE_COSTUME
1561 		resid = pop();
1562 		_res->setResourceCounter(rtCostume, resid, 0x7F);
1563 		break;
1564 	case 107:		// SO_NUKE_ROOM
1565 		resid = pop();
1566 		_res->setResourceCounter(rtRoom, resid, 0x7F);
1567 		break;
1568 	case 108:		// SO_LOCK_SCRIPT
1569 		resid = pop();
1570 		if (resid >= _numGlobalScripts)
1571 			break;
1572 		_res->lock(rtScript, resid);
1573 		break;
1574 	case 109:		// SO_LOCK_SOUND
1575 		resid = pop();
1576 		_res->lock(rtSound, resid);
1577 		break;
1578 	case 110:		// SO_LOCK_COSTUME
1579 		resid = pop();
1580 		_res->lock(rtCostume, resid);
1581 		break;
1582 	case 111:		// SO_LOCK_ROOM
1583 		resid = pop();
1584 		if (resid > 0x7F)
1585 			resid = _resourceMapper[resid & 0x7F];
1586 		_res->lock(rtRoom, resid);
1587 		break;
1588 	case 112:		// SO_UNLOCK_SCRIPT
1589 		resid = pop();
1590 		if (resid >= _numGlobalScripts)
1591 			break;
1592 		_res->unlock(rtScript, resid);
1593 		break;
1594 	case 113:		// SO_UNLOCK_SOUND
1595 		resid = pop();
1596 		_res->unlock(rtSound, resid);
1597 		break;
1598 	case 114:		// SO_UNLOCK_COSTUME
1599 		resid = pop();
1600 		_res->unlock(rtCostume, resid);
1601 		break;
1602 	case 115:		// SO_UNLOCK_ROOM
1603 		resid = pop();
1604 		if (resid > 0x7F)
1605 			resid = _resourceMapper[resid & 0x7F];
1606 		_res->unlock(rtRoom, resid);
1607 		break;
1608 	case 116:		// SO_CLEAR_HEAP
1609 		/* this is actually a scumm message */
1610 		error("clear heap not working yet");
1611 		break;
1612 	case 117:		// SO_LOAD_CHARSET
1613 		resid = pop();
1614 		loadCharset(resid);
1615 		break;
1616 	case 118:		// SO_NUKE_CHARSET
1617 		resid = pop();
1618 		nukeCharset(resid);
1619 		break;
1620 	case 119:		// SO_LOAD_OBJECT
1621 		{
1622 			int room, obj = popRoomAndObj(&room);
1623 			loadFlObject(obj, room);
1624 			break;
1625 		}
1626 	default:
1627 		error("o6_resourceRoutines: default case %d", subOp);
1628 	}
1629 }
1630 
1631 
o6_roomOps()1632 void ScummEngine_v6::o6_roomOps() {
1633 	int a, b, c, d, e;
1634 
1635 	byte subOp = fetchScriptByte();
1636 
1637 	switch (subOp) {
1638 	case 172:		// SO_ROOM_SCROLL
1639 		b = pop();
1640 		a = pop();
1641 		if (a < (_screenWidth / 2))
1642 			a = (_screenWidth / 2);
1643 		if (b < (_screenWidth / 2))
1644 			b = (_screenWidth / 2);
1645 		if (a > _roomWidth - (_screenWidth / 2))
1646 			a = _roomWidth - (_screenWidth / 2);
1647 		if (b > _roomWidth - (_screenWidth / 2))
1648 			b = _roomWidth - (_screenWidth / 2);
1649 		VAR(VAR_CAMERA_MIN_X) = a;
1650 		VAR(VAR_CAMERA_MAX_X) = b;
1651 		break;
1652 
1653 	case 174:		// SO_ROOM_SCREEN
1654 		b = pop();
1655 		a = pop();
1656 		initScreens(a, b);
1657 		break;
1658 
1659 	case 175:		// SO_ROOM_PALETTE
1660 		d = pop();
1661 		c = pop();
1662 		b = pop();
1663 		a = pop();
1664 		setPalColor(d, a, b, c);
1665 		break;
1666 
1667 	case 176:		// SO_ROOM_SHAKE_ON
1668 		setShake(1);
1669 		break;
1670 
1671 	case 177:		// SO_ROOM_SHAKE_OFF
1672 		setShake(0);
1673 		break;
1674 
1675 	case 179:		// SO_ROOM_INTENSITY
1676 		c = pop();
1677 		b = pop();
1678 		a = pop();
1679 		// Prevent assert() error with corner case, fixes bug #9871
1680 		if (_game.id == GID_FT && _roomResource == 0)
1681 			break;
1682 		darkenPalette(a, a, a, b, c);
1683 		break;
1684 
1685 	case 180:		// SO_ROOM_SAVEGAME
1686 		_saveTemporaryState = true;
1687 		_saveLoadSlot = pop();
1688 		_saveLoadFlag = pop();
1689 		if (_game.id == GID_TENTACLE)
1690 			_saveSound = (_saveLoadSlot != 0);
1691 		break;
1692 
1693 	case 181:		// SO_ROOM_FADE
1694 		a = pop();
1695 		if (a) {
1696 			_switchRoomEffect = (byte)(a & 0xFF);
1697 			_switchRoomEffect2 = (byte)(a >> 8);
1698 		} else {
1699 			fadeIn(_newEffect);
1700 		}
1701 		break;
1702 
1703 	case 182:		// SO_RGB_ROOM_INTENSITY
1704 		e = pop();
1705 		d = pop();
1706 		c = pop();
1707 		b = pop();
1708 		a = pop();
1709 		darkenPalette(a, b, c, d, e);
1710 		break;
1711 
1712 	case 183:		// SO_ROOM_SHADOW
1713 		e = pop();
1714 		d = pop();
1715 		c = pop();
1716 		b = pop();
1717 		a = pop();
1718 		setShadowPalette(a, b, c, d, e, 0, 256);
1719 		break;
1720 
1721 	case 184:		// SO_SAVE_STRING
1722 		error("save string not implemented");
1723 		break;
1724 
1725 	case 185:		// SO_LOAD_STRING
1726 		error("load string not implemented");
1727 		break;
1728 
1729 	case 186:		// SO_ROOM_TRANSFORM
1730 		d = pop();
1731 		c = pop();
1732 		b = pop();
1733 		a = pop();
1734 		palManipulateInit(a, b, c, d);
1735 		break;
1736 
1737 	case 187:		// SO_CYCLE_SPEED
1738 		b = pop();
1739 		a = pop();
1740 		assertRange(1, a, 16, "o6_roomOps: 187: color cycle");
1741 		_colorCycle[a - 1].delay = (b != 0) ? 0x4000 / (b * 0x4C) : 0;
1742 		break;
1743 
1744 	case 213:		// SO_ROOM_NEW_PALETTE
1745 		a = pop();
1746 
1747 		// This opcode is used when turning off noir mode in Sam & Max,
1748 		// but since our implementation of this feature doesn't change
1749 		// the original palette there's no need to reload it. Doing it
1750 		// this way, we avoid some graphics glitches that the original
1751 		// interpreter had.
1752 
1753 		if (_game.id == GID_SAMNMAX && vm.slot[_currentScript].number == 64)
1754 			setDirtyColors(0, 255);
1755 		else
1756 			setCurrentPalette(a);
1757 		break;
1758 	default:
1759 		error("o6_roomOps: default case %d", subOp);
1760 	}
1761 }
1762 
o6_actorOps()1763 void ScummEngine_v6::o6_actorOps() {
1764 	Actor *a;
1765 	int i, j, k;
1766 	int args[8];
1767 
1768 	byte subOp = fetchScriptByte();
1769 	if (subOp == 197) {
1770 		_curActor = pop();
1771 		return;
1772 	}
1773 
1774 	a = derefActorSafe(_curActor, "o6_actorOps");
1775 	if (!a)
1776 		return;
1777 
1778 	switch (subOp) {
1779 	case 76:		// SO_COSTUME
1780 		a->setActorCostume(pop());
1781 		break;
1782 	case 77:		// SO_STEP_DIST
1783 		j = pop();
1784 		i = pop();
1785 		a->setActorWalkSpeed(i, j);
1786 		break;
1787 	case 78:		// SO_SOUND
1788 		k = getStackList(args, ARRAYSIZE(args));
1789 		for (i = 0; i < k; i++)
1790 			a->_sound[i] = args[i];
1791 		break;
1792 	case 79:		// SO_WALK_ANIMATION
1793 		a->_walkFrame = pop();
1794 		break;
1795 	case 80:		// SO_TALK_ANIMATION
1796 		a->_talkStopFrame = pop();
1797 		a->_talkStartFrame = pop();
1798 		break;
1799 	case 81:		// SO_STAND_ANIMATION
1800 		a->_standFrame = pop();
1801 		break;
1802 	case 82:		// SO_ANIMATION
1803 		// dummy case in scumm6
1804 		pop();
1805 		pop();
1806 		pop();
1807 		break;
1808 	case 83:		// SO_DEFAULT
1809 		a->initActor(0);
1810 		break;
1811 	case 84:		// SO_ELEVATION
1812 		a->setElevation(pop());
1813 		break;
1814 	case 85:		// SO_ANIMATION_DEFAULT
1815 		a->_initFrame = 1;
1816 		a->_walkFrame = 2;
1817 		a->_standFrame = 3;
1818 		a->_talkStartFrame = 4;
1819 		a->_talkStopFrame = 5;
1820 		break;
1821 	case 86:		// SO_PALETTE
1822 		j = pop();
1823 		i = pop();
1824 		assertRange(0, i, 255, "o6_actorOps: palette slot");
1825 		a->setPalette(i, j);
1826 		break;
1827 	case 87:		// SO_TALK_COLOR
1828 		a->_talkColor = pop();
1829 		break;
1830 	case 88:		// SO_ACTOR_NAME
1831 		loadPtrToResource(rtActorName, a->_number, NULL);
1832 		break;
1833 	case 89:		// SO_INIT_ANIMATION
1834 		a->_initFrame = pop();
1835 		break;
1836 	case 91:		// SO_ACTOR_WIDTH
1837 		a->_width = pop();
1838 		break;
1839 	case 92:		// SO_SCALE
1840 		i = pop();
1841 		a->setScale(i, i);
1842 		break;
1843 	case 93:		// SO_NEVER_ZCLIP
1844 		a->_forceClip = 0;
1845 		break;
1846 	case 225:		// SO_ALWAYS_ZCLIP
1847 	case 94:		// SO_ALWAYS_ZCLIP
1848 		a->_forceClip = pop();
1849 		break;
1850 	case 95:		// SO_IGNORE_BOXES
1851 		a->_ignoreBoxes = 1;
1852 		a->_forceClip = (_game.version >= 7) ? 100 : 0;
1853 		if (a->isInCurrentRoom())
1854 			a->putActor();
1855 		break;
1856 	case 96:		// SO_FOLLOW_BOXES
1857 		a->_ignoreBoxes = 0;
1858 		a->_forceClip = (_game.version >= 7) ? 100 : 0;
1859 		if (a->isInCurrentRoom())
1860 			a->putActor();
1861 		break;
1862 	case 97:		// SO_ANIMATION_SPEED
1863 		a->setAnimSpeed(pop());
1864 		break;
1865 	case 98:		// SO_SHADOW
1866 		a->_shadowMode = pop();
1867 		break;
1868 	case 99:		// SO_TEXT_OFFSET
1869 		a->_talkPosY = pop();
1870 		a->_talkPosX = pop();
1871 		break;
1872 	case 198:		// SO_ACTOR_VARIABLE
1873 		i = pop();
1874 		a->setAnimVar(pop(), i);
1875 		break;
1876 	case 215:		// SO_ACTOR_IGNORE_TURNS_ON
1877 		a->_ignoreTurns = true;
1878 		break;
1879 	case 216:		// SO_ACTOR_IGNORE_TURNS_OFF
1880 		a->_ignoreTurns = false;
1881 		break;
1882 	case 217:		// SO_ACTOR_NEW
1883 		a->initActor(2);
1884 		break;
1885 	case 227:		// SO_ACTOR_DEPTH
1886 		a->_layer = pop();
1887 		break;
1888 	case 228:		// SO_ACTOR_WALK_SCRIPT
1889 		a->_walkScript = pop();
1890 		break;
1891 	case 229:		// SO_ACTOR_STOP
1892 		a->stopActorMoving();
1893 		a->startAnimActor(a->_standFrame);
1894 		break;
1895 	case 230:										/* set direction */
1896 		a->_moving &= ~MF_TURN;
1897 		a->setDirection(pop());
1898 		break;
1899 	case 231:										/* turn to direction */
1900 		a->turnToDirection(pop());
1901 		break;
1902 	case 233:		// SO_ACTOR_WALK_PAUSE
1903 		a->_moving |= MF_FROZEN;
1904 		break;
1905 	case 234:		// SO_ACTOR_WALK_RESUME
1906 		a->_moving &= ~MF_FROZEN;
1907 		break;
1908 	case 235:		// SO_ACTOR_TALK_SCRIPT
1909 		a->_talkScript = pop();
1910 		break;
1911 	default:
1912 		error("o6_actorOps: default case %d", subOp);
1913 	}
1914 }
1915 
o6_verbOps()1916 void ScummEngine_v6::o6_verbOps() {
1917 	int slot, a, b;
1918 	VerbSlot *vs;
1919 
1920 	byte subOp = fetchScriptByte();
1921 	if (subOp == 196) {
1922 		_curVerb = pop();
1923 		_curVerbSlot = getVerbSlot(_curVerb, 0);
1924 		assertRange(0, _curVerbSlot, _numVerbs - 1, "new verb slot");
1925 		return;
1926 	}
1927 	vs = &_verbs[_curVerbSlot];
1928 	slot = _curVerbSlot;
1929 	switch (subOp) {
1930 	case 124:		// SO_VERB_IMAGE
1931 		a = pop();
1932 		if (_curVerbSlot) {
1933 			setVerbObject(_roomResource, a, slot);
1934 			vs->type = kImageVerbType;
1935 			if (_game.heversion >= 61)
1936 				vs->imgindex = a;
1937 		}
1938 		break;
1939 	case 125:		// SO_VERB_NAME
1940 		loadPtrToResource(rtVerb, slot, NULL);
1941 		vs->type = kTextVerbType;
1942 		vs->imgindex = 0;
1943 		break;
1944 	case 126:		// SO_VERB_COLOR
1945 		vs->color = pop();
1946 		break;
1947 	case 127:		// SO_VERB_HICOLOR
1948 		vs->hicolor = pop();
1949 		break;
1950 	case 128:		// SO_VERB_AT
1951 		vs->curRect.top = pop();
1952 		vs->curRect.left = vs->origLeft = pop();
1953 		break;
1954 	case 129:		// SO_VERB_ON
1955 		vs->curmode = 1;
1956 		break;
1957 	case 130:		// SO_VERB_OFF
1958 		vs->curmode = 0;
1959 		break;
1960 	case 131:		// SO_VERB_DELETE
1961 		if (_game.heversion >= 60) {
1962 			slot = getVerbSlot(pop(), 0);
1963 		}
1964 		killVerb(slot);
1965 		break;
1966 	case 132:		// SO_VERB_NEW
1967 		slot = getVerbSlot(_curVerb, 0);
1968 		if (slot == 0) {
1969 			for (slot = 1; slot < _numVerbs; slot++) {
1970 				if (_verbs[slot].verbid == 0)
1971 					break;
1972 			}
1973 			if (slot == _numVerbs)
1974 				error("Too many verbs");
1975 			_curVerbSlot = slot;
1976 		}
1977 		vs = &_verbs[slot];
1978 		vs->verbid = _curVerb;
1979 		vs->color = 2;
1980 		vs->hicolor = 0;
1981 		vs->dimcolor = 8;
1982 		vs->type = kTextVerbType;
1983 		vs->charset_nr = _string[0]._default.charset;
1984 		vs->curmode = 0;
1985 		vs->saveid = 0;
1986 		vs->key = 0;
1987 		vs->center = 0;
1988 		vs->imgindex = 0;
1989 		break;
1990 	case 133:		// SO_VERB_DIMCOLOR
1991 		vs->dimcolor = pop();
1992 		break;
1993 	case 134:		// SO_VERB_DIM
1994 		vs->curmode = 2;
1995 		break;
1996 	case 135:		// SO_VERB_KEY
1997 		vs->key = pop();
1998 		break;
1999 	case 136:		// SO_VERB_CENTER
2000 		vs->center = 1;
2001 		break;
2002 	case 137:		// SO_VERB_NAME_STR
2003 		a = pop();
2004 		if (a == 0) {
2005 			loadPtrToResource(rtVerb, slot, (const byte *)"");
2006 		} else {
2007 			loadPtrToResource(rtVerb, slot, getStringAddress(a));
2008 		}
2009 		vs->type = kTextVerbType;
2010 		vs->imgindex = 0;
2011 		break;
2012 	case 139:		// SO_VERB_IMAGE_IN_ROOM
2013 		b = pop();
2014 		a = pop();
2015 
2016 		if (slot && a != vs->imgindex) {
2017 			setVerbObject(b, a, slot);
2018 			vs->type = kImageVerbType;
2019 			vs->imgindex = a;
2020 		}
2021 		break;
2022 	case 140:		// SO_VERB_BAKCOLOR
2023 		vs->bkcolor = pop();
2024 		break;
2025 	case 255:
2026 		drawVerb(slot, 0);
2027 		verbMouseOver(0);
2028 		break;
2029 	default:
2030 		error("o6_verbops: default case %d", subOp);
2031 	}
2032 }
2033 
o6_getActorFromXY()2034 void ScummEngine_v6::o6_getActorFromXY() {
2035 	int y = pop();
2036 	int x = pop();
2037 	int r = getActorFromPos(x, y);
2038 	push(r);
2039 }
2040 
o6_findObject()2041 void ScummEngine_v6::o6_findObject() {
2042 	int y = pop();
2043 	int x = pop();
2044 	int r = findObject(x, y);
2045 	push(r);
2046 }
2047 
o6_pseudoRoom()2048 void ScummEngine_v6::o6_pseudoRoom() {
2049 	int list[100];
2050 	int num, a, value;
2051 
2052 	num = getStackList(list, ARRAYSIZE(list));
2053 	value = pop();
2054 
2055 	while (--num >= 0) {
2056 		a = list[num];
2057 		if (a > 0x7F)
2058 			_resourceMapper[a & 0x7F] = value;
2059 	}
2060 }
2061 
o6_getVerbEntrypoint()2062 void ScummEngine_v6::o6_getVerbEntrypoint() {
2063 	int e = pop();
2064 	int v = pop();
2065 	push(getVerbEntrypoint(v, e));
2066 }
2067 
o6_arrayOps()2068 void ScummEngine_v6::o6_arrayOps() {
2069 	byte subOp = fetchScriptByte();
2070 	int array = fetchScriptWord();
2071 	int b, c, d, len;
2072 	byte *data;
2073 	int list[128];
2074 
2075 	switch (subOp) {
2076 	case 205:		// SO_ASSIGN_STRING
2077 		b = pop();
2078 		len = resStrLen(_scriptPointer);
2079 		data = defineArray(array, kStringArray, 0, len + 1);
2080 		copyScriptString(data + b);
2081 		break;
2082 	case 208:		// SO_ASSIGN_INT_LIST
2083 		b = pop();
2084 		c = pop();
2085 		d = readVar(array);
2086 		if (d == 0) {
2087 			defineArray(array, kIntArray, 0, b + c);
2088 		}
2089 		while (c--) {
2090 			writeArray(array, 0, b + c, pop());
2091 		}
2092 		break;
2093 	case 212:		// SO_ASSIGN_2DIM_LIST
2094 		b = pop();
2095 		len = getStackList(list, ARRAYSIZE(list));
2096 		d = readVar(array);
2097 		if (d == 0)
2098 			error("Must DIM a two dimensional array before assigning");
2099 		c = pop();
2100 		while (--len >= 0) {
2101 			writeArray(array, c, b + len, list[len]);
2102 		}
2103 		break;
2104 	default:
2105 		error("o6_arrayOps: default case %d (array %d)", subOp, array);
2106 	}
2107 }
2108 
o6_saveRestoreVerbs()2109 void ScummEngine_v6::o6_saveRestoreVerbs() {
2110 	int a, b, c;
2111 	int slot, slot2;
2112 
2113 	c = pop();
2114 	b = pop();
2115 	a = pop();
2116 
2117 	byte subOp = fetchScriptByte();
2118 	if (_game.version == 8) {
2119 		subOp = (subOp - 141) + 0xB4;
2120 	}
2121 
2122 	switch (subOp) {
2123 	case 141:		// SO_SAVE_VERBS
2124 		while (a <= b) {
2125 			slot = getVerbSlot(a, 0);
2126 			if (slot && _verbs[slot].saveid == 0) {
2127 				_verbs[slot].saveid = c;
2128 				drawVerb(slot, 0);
2129 				verbMouseOver(0);
2130 			}
2131 			a++;
2132 		}
2133 		break;
2134 	case 142:		// SO_RESTORE_VERBS
2135 		while (a <= b) {
2136 			slot = getVerbSlot(a, c);
2137 			if (slot) {
2138 				slot2 = getVerbSlot(a, 0);
2139 				if (slot2)
2140 					killVerb(slot2);
2141 				slot = getVerbSlot(a, c);
2142 				_verbs[slot].saveid = 0;
2143 				drawVerb(slot, 0);
2144 				verbMouseOver(0);
2145 			}
2146 			a++;
2147 		}
2148 		break;
2149 	case 143:		// SO_DELETE_VERBS
2150 		while (a <= b) {
2151 			slot = getVerbSlot(a, c);
2152 			if (slot)
2153 				killVerb(slot);
2154 			a++;
2155 		}
2156 		break;
2157 	default:
2158 		error("o6_saveRestoreVerbs: default case");
2159 	}
2160 }
2161 
o6_drawBox()2162 void ScummEngine_v6::o6_drawBox() {
2163 	int x, y, x2, y2, color;
2164 	color = pop();
2165 	y2 = pop();
2166 	x2 = pop();
2167 	y = pop();
2168 	x = pop();
2169 	drawBox(x, y, x2, y2, color);
2170 }
2171 
o6_wait()2172 void ScummEngine_v6::o6_wait() {
2173 	int actnum;
2174 	int offs = -2;
2175 	Actor *a;
2176 
2177 	byte subOp = fetchScriptByte();
2178 
2179 	switch (subOp) {
2180 	case 168:		// SO_WAIT_FOR_ACTOR Wait for actor
2181 		offs = fetchScriptWordSigned();
2182 		actnum = pop();
2183 		a = derefActor(actnum, "o6_wait:168");
2184 		if (_game.version >= 7) {
2185 			if (a->isInCurrentRoom() && a->_moving)
2186 				break;
2187 		} else {
2188 			if (a->_moving)
2189 				break;
2190 		}
2191 		return;
2192 	case 169:		// SO_WAIT_FOR_MESSAGE Wait for message
2193 		if (VAR(VAR_HAVE_MSG))
2194 			break;
2195 		return;
2196 	case 170:		// SO_WAIT_FOR_CAMERA Wait for camera
2197 		if (_game.version >= 7) {
2198 			if (camera._dest != camera._cur)
2199 				break;
2200 		} else {
2201 			if (camera._cur.x / 8 != camera._dest.x / 8)
2202 				break;
2203 		}
2204 
2205 		return;
2206 	case 171:		// SO_WAIT_FOR_SENTENCE
2207 		if (_sentenceNum) {
2208 			if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
2209 				return;
2210 			break;
2211 		}
2212 		if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
2213 			return;
2214 		break;
2215 	case 226:		// SO_WAIT_FOR_ANIMATION
2216 		offs = fetchScriptWordSigned();
2217 		actnum = pop();
2218 		a = derefActor(actnum, "o6_wait:226");
2219 		if (a->isInCurrentRoom() && a->_needRedraw)
2220 			break;
2221 		return;
2222 	case 232:		// SO_WAIT_FOR_TURN
2223 		// WORKAROUND for bug #819: An angle will often be received as the
2224 		// actor number due to script bugs in The Dig. In all cases where this
2225 		// occurs, _curActor is set just before it, so we can use it instead.
2226 		//
2227 		// For now, if the value passed in is divisible by 45, assume it is an
2228 		// angle, and use _curActor as the actor to wait for.
2229 		offs = fetchScriptWordSigned();
2230 		actnum = pop();
2231 		if (actnum % 45 == 0) {
2232 			actnum = _curActor;
2233 		}
2234 		a = derefActor(actnum, "o6_wait:232b");
2235 		if (a->isInCurrentRoom() && a->_moving & MF_TURN)
2236 			break;
2237 		return;
2238 	default:
2239 		error("o6_wait: default case 0x%x", subOp);
2240 	}
2241 
2242 	_scriptPointer += offs;
2243 	o6_breakHere();
2244 }
2245 
o6_soundKludge()2246 void ScummEngine_v6::o6_soundKludge() {
2247 	int list[16];
2248 	int num = getStackList(list, ARRAYSIZE(list));
2249 
2250 	_sound->soundKludge(list, num);
2251 
2252 	// WORKAROUND for bug #2438: The room-11-2016 script contains a
2253 	// slight bug causing it to busy-wait for a sound to finish. Even under
2254 	// the best of circumstances, this will cause the game to hang briefly.
2255 	// On platforms where threading is cooperative, it will cause the game
2256 	// to hang indefinitely. We identify the buggy part of the script by
2257 	// looking for a soundKludge() opcode immediately followed by a jump.
2258 
2259 	if (_game.id == GID_CMI && _roomResource == 11 && vm.slot[_currentScript].number == 2016 && *_scriptPointer == 0x66) {
2260 		debug(3, "Working around script bug in room-11-2016");
2261 		o6_breakHere();
2262 	}
2263 }
2264 
o6_isAnyOf()2265 void ScummEngine_v6::o6_isAnyOf() {
2266 	int list[100];
2267 	int num;
2268 	int32 val;
2269 
2270 	num = getStackList(list, ARRAYSIZE(list));
2271 	val = pop();
2272 
2273 	while (--num >= 0) {
2274 		if (list[num] == val) {
2275 			push(1);
2276 			return;
2277 		}
2278 	}
2279 
2280 	push(0);
2281 }
2282 
o6_systemOps()2283 void ScummEngine_v6::o6_systemOps() {
2284 	byte subOp = fetchScriptByte();
2285 
2286 	switch (subOp) {
2287 	case 158:		// SO_RESTART
2288 		restart();
2289 		break;
2290 	case 159:		// SO_PAUSE
2291 		pauseGame();
2292 		break;
2293 	case 160:		// SO_QUIT
2294 		quitGame();
2295 		break;
2296 	default:
2297 		error("o6_systemOps invalid case %d", subOp);
2298 	}
2299 }
2300 
o6_delay()2301 void ScummEngine_v6::o6_delay() {
2302 	uint32 delay = (uint16)pop();
2303 	vm.slot[_currentScript].delay = delay;
2304 	vm.slot[_currentScript].status = ssPaused;
2305 	o6_breakHere();
2306 }
2307 
o6_delaySeconds()2308 void ScummEngine_v6::o6_delaySeconds() {
2309 	uint32 delay = (uint32)pop();
2310 	delay = delay * 60;
2311 	vm.slot[_currentScript].delay = delay;
2312 	vm.slot[_currentScript].status = ssPaused;
2313 	o6_breakHere();
2314 }
2315 
o6_delayMinutes()2316 void ScummEngine_v6::o6_delayMinutes() {
2317 	uint32 delay = (uint16)pop() * 3600;
2318 	vm.slot[_currentScript].delay = delay;
2319 	vm.slot[_currentScript].status = ssPaused;
2320 	o6_breakHere();
2321 }
2322 
o6_stopSentence()2323 void ScummEngine_v6::o6_stopSentence() {
2324 	_sentenceNum = 0;
2325 	stopScript(VAR(VAR_SENTENCE_SCRIPT));
2326 	clearClickedStatus();
2327 }
2328 
o6_printLine()2329 void ScummEngine_v6::o6_printLine() {
2330 	_actorToPrintStrFor = 0xFF;
2331 	decodeParseString(0, 0);
2332 }
2333 
o6_printText()2334 void ScummEngine_v6::o6_printText() {
2335 	decodeParseString(1, 0);
2336 }
2337 
o6_printDebug()2338 void ScummEngine_v6::o6_printDebug() {
2339 	decodeParseString(2, 0);
2340 }
2341 
o6_printSystem()2342 void ScummEngine_v6::o6_printSystem() {
2343 	decodeParseString(3, 0);
2344 }
2345 
o6_printActor()2346 void ScummEngine_v6::o6_printActor() {
2347 	decodeParseString(0, 1);
2348 }
2349 
o6_printEgo()2350 void ScummEngine_v6::o6_printEgo() {
2351 	push(VAR(VAR_EGO));
2352 	decodeParseString(0, 1);
2353 }
2354 
o6_talkActor()2355 void ScummEngine_v6::o6_talkActor() {
2356 	int offset = _scriptPointer - _scriptOrgPointer;
2357 
2358 	// WORKAROUND for bug #1452: see below for detailed description
2359 	if (_forcedWaitForMessage) {
2360 		if (VAR(VAR_HAVE_MSG)) {
2361 			_scriptPointer--;
2362 			o6_breakHere();
2363 			return;
2364 		}
2365 
2366 		_forcedWaitForMessage = false;
2367 		_scriptPointer += resStrLen(_scriptPointer) + 1;
2368 
2369 		return;
2370 	}
2371 
2372 	_actorToPrintStrFor = pop();
2373 
2374 	// WORKAROUND for bug #3803: "DOTT: Bernard impersonating LaVerne"
2375 	// Original script did not check for VAR_EGO == 2 before executing
2376 	// a talkActor opcode.
2377 	if (_game.id == GID_TENTACLE && vm.slot[_currentScript].number == 307
2378 			&& VAR(VAR_EGO) != 2 && _actorToPrintStrFor == 2) {
2379 		_scriptPointer += resStrLen(_scriptPointer) + 1;
2380 		return;
2381 	}
2382 
2383 	_string[0].loadDefault();
2384 	actorTalk(_scriptPointer);
2385 
2386 	// WORKAROUND for bug #1452: "DIG: Missing subtitles when talking to Brink"
2387 	// Original script does not have wait.waitForMessage() after several messages:
2388 	//
2389 	// [011A] (5D)   if (getActorCostume(VAR_EGO) == 1) {
2390 	// [0126] (BA)     talkActor("/STOP.008/Low out.",3)
2391 	// [013D] (A9)     wait.waitForMessage()
2392 	// [013F] (5D)   } else if (var227 == 0) {
2393 	// [014C] (BA)     talkActor("/STOP.009/Never mind.",3)
2394 	// [0166] (73)   } else {
2395 	//
2396 	// Here we simulate that opcode.
2397 	if (_game.id == GID_DIG && vm.slot[_currentScript].number == 88) {
2398 		if (offset == 0x158 || offset == 0x214 || offset == 0x231 || offset == 0x278) {
2399 			_forcedWaitForMessage = true;
2400 			_scriptPointer--;
2401 
2402 			return;
2403 		}
2404 	}
2405 	_scriptPointer += resStrLen(_scriptPointer) + 1;
2406 }
2407 
o6_talkEgo()2408 void ScummEngine_v6::o6_talkEgo() {
2409 	push(VAR(VAR_EGO));
2410 	o6_talkActor();
2411 }
2412 
o6_dimArray()2413 void ScummEngine_v6::o6_dimArray() {
2414 	int data;
2415 
2416 	byte subOp = fetchScriptByte();
2417 
2418 	switch (subOp) {
2419 	case 199:		// SO_INT_ARRAY
2420 		data = kIntArray;
2421 		break;
2422 	case 200:		// SO_BIT_ARRAY
2423 		data = kBitArray;
2424 		break;
2425 	case 201:		// SO_NIBBLE_ARRAY
2426 		data = kNibbleArray;
2427 		break;
2428 	case 202:		// SO_BYTE_ARRAY
2429 		data = kByteArray;
2430 		break;
2431 	case 203:		// SO_STRING_ARRAY
2432 		data = kStringArray;
2433 		break;
2434 	case 204:		// SO_UNDIM_ARRAY
2435 		nukeArray(fetchScriptWord());
2436 		return;
2437 	default:
2438 		error("o6_dimArray: default case %d", subOp);
2439 	}
2440 
2441 	defineArray(fetchScriptWord(), data, 0, pop());
2442 }
2443 
o6_dummy()2444 void ScummEngine_v6::o6_dummy() {
2445 }
2446 
o6_dim2dimArray()2447 void ScummEngine_v6::o6_dim2dimArray() {
2448 	int a, b, data;
2449 
2450 	byte subOp = fetchScriptByte();
2451 
2452 	switch (subOp) {
2453 	case 199:		// SO_INT_ARRAY
2454 		data = kIntArray;
2455 		break;
2456 	case 200:		// SO_BIT_ARRAY
2457 		data = kBitArray;
2458 		break;
2459 	case 201:		// SO_NIBBLE_ARRAY
2460 		data = kNibbleArray;
2461 		break;
2462 	case 202:		// SO_BYTE_ARRAY
2463 		data = kByteArray;
2464 		break;
2465 	case 203:		// SO_STRING_ARRAY
2466 		data = kStringArray;
2467 		break;
2468 	default:
2469 		error("o6_dim2dimArray: default case %d", subOp);
2470 	}
2471 
2472 	b = pop();
2473 	a = pop();
2474 	defineArray(fetchScriptWord(), data, a, b);
2475 }
2476 
o6_abs()2477 void ScummEngine_v6::o6_abs() {
2478 	int a = pop();
2479 	push(ABS(a));
2480 }
2481 
o6_distObjectObject()2482 void ScummEngine_v6::o6_distObjectObject() {
2483 	int a, b;
2484 	b = pop();
2485 	a = pop();
2486 	push(getDistanceBetween(true, a, 0, true, b, 0));
2487 }
2488 
o6_distObjectPt()2489 void ScummEngine_v6::o6_distObjectPt() {
2490 	int a, b, c;
2491 	c = pop();
2492 	b = pop();
2493 	a = pop();
2494 	push(getDistanceBetween(true, a, 0, false, b, c));
2495 }
2496 
o6_distPtPt()2497 void ScummEngine_v6::o6_distPtPt() {
2498 	int a, b, c, d;
2499 	d = pop();
2500 	c = pop();
2501 	b = pop();
2502 	a = pop();
2503 	push(getDistanceBetween(false, a, b, false, c, d));
2504 }
2505 
o6_drawBlastObject()2506 void ScummEngine_v6::o6_drawBlastObject() {
2507 	int args[16];
2508 	int a, b, c, d, e;
2509 
2510 	getStackList(args, ARRAYSIZE(args));
2511 	e = pop();
2512 	d = pop();
2513 	c = pop();
2514 	b = pop();
2515 	a = pop();
2516 	enqueueObject(a, b, c, d, e, 0xFF, 0xFF, 1, 0);
2517 }
2518 
2519 // Set BOMP processing window
o6_setBlastObjectWindow()2520 void ScummEngine_v6::o6_setBlastObjectWindow() {
2521 	pop();
2522 	pop();
2523 	pop();
2524 	pop();
2525 
2526 	// None of the scripts of The Dig and Full Throttle use this opcode.
2527 	// Sam & Max only uses it at the beginning of the highway subgame. In
2528 	// the original interpreter pop'ed arguments are just ignored and the
2529 	// clipping blastObject window is defined with (0, 0, 320, 200)...
2530 	// which matches the screen dimensions and thus, doesn't require
2531 	// another clipping operation.
2532 	// So, we just handle this as no-op opcode.
2533 }
2534 
2535 #ifdef ENABLE_SCUMM_7_8
o6_kernelSetFunctions()2536 void ScummEngine_v7::o6_kernelSetFunctions() {
2537 	int args[30];
2538 	int num;
2539 	Actor *a;
2540 
2541 	num = getStackList(args, ARRAYSIZE(args));
2542 
2543 	switch (args[0]) {
2544 	case 4:
2545 		grabCursor(args[1], args[2], args[3], args[4]);
2546 		break;
2547 	case 6: {
2548 			// SMUSH movie playback
2549 			if (args[1] == 0 && !_skipVideo) {
2550 				const char *videoname = (const char *)getStringAddressVar(VAR_VIDEONAME);
2551 				assert(videoname);
2552 
2553 				// Correct incorrect smush filename in Macintosh FT demo
2554 				if ((_game.id == GID_FT) && (_game.features & GF_DEMO) && (_game.platform == Common::kPlatformMacintosh) &&
2555 					(!strcmp(videoname, "jumpgorge.san")))
2556 					_splayer->play("jumpgorg.san", _smushFrameRate);
2557 				// WORKAROUND: A faster frame rate is required, to keep audio/video in sync in this video
2558 				else if (_game.id == GID_DIG && !strcmp(videoname, "sq3.san"))
2559 					_splayer->play(videoname, 14);
2560 				else
2561 					_splayer->play(videoname, _smushFrameRate);
2562 
2563 				if (_game.id == GID_DIG) {
2564 					_disableFadeInEffect = true;
2565 				}
2566 			} else if (_game.id == GID_FT && !_skipVideo) {
2567 				const int insaneVarNum = ((_game.features & GF_DEMO) && (_game.platform == Common::kPlatformDOS))
2568 					? 232 : 233;
2569 
2570 				_insane->setSmushParams(_smushFrameRate);
2571 				_insane->runScene(insaneVarNum);
2572 			}
2573 		}
2574 		break;
2575 	case 12:
2576 		setCursorFromImg(args[1], (uint) - 1, args[2]);
2577 		break;
2578 	case 13:
2579 		derefActor(args[1], "o6_kernelSetFunctions:13")->remapActorPalette(args[2], args[3], args[4], -1);
2580 		break;
2581 	case 14:
2582 		derefActor(args[1], "o6_kernelSetFunctions:14")->remapActorPalette(args[2], args[3], args[4], args[5]);
2583 		break;
2584 	case 15:
2585 		_smushFrameRate = args[1];
2586 		break;
2587 	case 16:
2588 	case 17:
2589 		enqueueText(getStringAddressVar(VAR_STRING2DRAW), args[3], args[4], args[2], args[1], (args[0] == 16));
2590 		break;
2591 	case 20:
2592 		_imuseDigital->setRadioChatterSFX(args[1]);
2593 		break;
2594 	case 107:
2595 		a = derefActor(args[1], "o6_kernelSetFunctions: 107");
2596 		a->setScale((unsigned char)args[2], -1);
2597 		break;
2598 	case 108:
2599 		setShadowPalette(args[1], args[2], args[3], args[4], args[5], args[6]);
2600 		break;
2601 	case 109:
2602 		setShadowPalette(0, args[1], args[2], args[3], args[4], args[5]);
2603 		break;
2604 	case 114:
2605 		error("o6_kernelSetFunctions: stub114()");
2606 		break;
2607 	case 117:
2608 		freezeScripts(2);
2609 		break;
2610 	case 118:
2611 		enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 3);
2612 		break;
2613 	case 119:
2614 		enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
2615 		break;
2616 	case 124:
2617 		_saveSound = args[1];
2618 		break;
2619 	case 215:
2620 		ConfMan.setBool("subtitles", args[1] != 0);
2621 		break;
2622 	default:
2623 		error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
2624 		break;
2625 	}
2626 }
2627 #endif
2628 
o6_kernelSetFunctions()2629 void ScummEngine_v6::o6_kernelSetFunctions() {
2630 	int args[30];
2631 	int num;
2632 	Actor *a;
2633 
2634 	num = getStackList(args, ARRAYSIZE(args));
2635 
2636 	switch (args[0]) {
2637 	case 3:
2638 		// Dummy case
2639 		break;
2640 	case 4:
2641 		grabCursor(args[1], args[2], args[3], args[4]);
2642 		break;
2643 	case 5:
2644 		fadeOut(args[1]);
2645 		break;
2646 	case 6:
2647 		_fullRedraw = true;
2648 		redrawBGAreas();
2649 		setActorRedrawFlags();
2650 		processActors();
2651 		fadeIn(args[1]);
2652 		break;
2653 	case 8:
2654 		if (startManiac()) {
2655 			// This is so that the surprised exclamation happens
2656 			// after we return to the game again, not before.
2657 			o6_breakHere();
2658 		}
2659 		break;
2660 	case 9:
2661 		killAllScriptsExceptCurrent();
2662 		break;
2663 	case 104:									/* samnmax */
2664 		nukeFlObjects(args[2], args[3]);
2665 		break;
2666 	case 107:									/* set actor scale */
2667 		a = derefActor(args[1], "o6_kernelSetFunctions: 107");
2668 		a->setScale((unsigned char)args[2], -1);
2669 		break;
2670 	case 108:									/* create proc_special_palette */
2671 	case 109:
2672 		// Case 108 and 109 share the same function
2673 		if (num != 6)
2674 			error("o6_kernelSetFunctions sub op %d: expected 6 params but got %d", args[0], num);
2675 		setShadowPalette(args[3], args[4], args[5], args[1], args[2], 0, 256);
2676 		break;
2677 	case 110:
2678 		clearCharsetMask();
2679 		break;
2680 	case 111:
2681 		a = derefActor(args[1], "o6_kernelSetFunctions: 111");
2682 		a->_shadowMode = args[2] + args[3];
2683 		break;
2684 	case 112:									/* palette shift? */
2685 		setShadowPalette(args[3], args[4], args[5], args[1], args[2], args[6], args[7]);
2686 		break;
2687 	case 114:
2688 		// Sam & Max film noir mode
2689 		if (_game.id == GID_SAMNMAX) {
2690 			// At this point ScummVM will already have set
2691 			// variable 0x8000 to indicate that the game is
2692 			// in film noir mode. All we have to do here is
2693 			// to mark the palette as "dirty", because
2694 			// updatePalette() will desaturate the colors
2695 			// as they are uploaded to the backend.
2696 			//
2697 			// This actually works better than the original
2698 			// interpreter, where actors would sometimes
2699 			// still be drawn in color.
2700 			setDirtyColors(0, 255);
2701 		} else
2702 			error("stub o6_kernelSetFunctions_114()");
2703 		break;
2704 	case 117:
2705 		// Sam & Max uses this opcode in script-43, right
2706 		// before a screensaver is selected.
2707 		//
2708 		// Sam & Max uses variable 132 to specify the number of
2709 		// minutes of inactivity (no mouse movements) before
2710 		// starting the screensaver, so setting it to 0 will
2711 		// help in debugging.
2712 		freezeScripts(0x80);
2713 		break;
2714 	case 119:
2715 		enqueueObject(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], 0);
2716 		break;
2717 	case 120:
2718 		swapPalColors(args[1], args[2]);
2719 		break;
2720 	case 122:
2721 		VAR(VAR_SOUNDRESULT) =
2722 			(short)_imuse->doCommand (num - 1, &args[1]);
2723 		break;
2724 	case 123:
2725 		copyPalColor(args[2], args[1]);
2726 		break;
2727 	case 124:
2728 		_saveSound = args[1];
2729 		break;
2730 	default:
2731 		error("o6_kernelSetFunctions: default case %d (param count %d)", args[0], num);
2732 		break;
2733 	}
2734 }
2735 
o6_kernelGetFunctions()2736 void ScummEngine_v6::o6_kernelGetFunctions() {
2737 	int args[30];
2738 	int i;
2739 	int slot;
2740 	Actor *a;
2741 	VirtScreen *vs = &_virtscr[kMainVirtScreen];
2742 
2743 	getStackList(args, ARRAYSIZE(args));
2744 
2745 	switch (args[0]) {
2746 	case 113:
2747 		// WORKAROUND for bug #1465: The scripts used for screen savers
2748 		// in Sam & Max use hard coded values for the maximum height and width.
2749 		// This causes problems in rooms (ie. Credits) where their values are
2750 		// lower, so we set result to zero if out of bounds.
2751 		if (args[1] >= 0 && args[1] <= vs->w && args[2] >= 0 && args[2] <= vs->h) {
2752 			byte pixel = *vs->getPixels(args[1], args[2]);
2753 			push(pixel);
2754 		} else {
2755 			push(0);
2756 		}
2757 		break;
2758 	case 115:
2759 		push(getSpecialBox(args[1], args[2]));
2760 		break;
2761 	case 116:
2762 		push(checkXYInBoxBounds(args[3], args[1], args[2]));
2763 		break;
2764 	case 206:
2765 		push(remapPaletteColor(args[1], args[2], args[3], -1));
2766 		break;
2767 	case 207:
2768 		i = getObjectIndex(args[1]);
2769 		assert(i);
2770 		push(_objs[i].x_pos);
2771 		break;
2772 	case 208:
2773 		i = getObjectIndex(args[1]);
2774 		assert(i);
2775 		push(_objs[i].y_pos);
2776 		break;
2777 	case 209:
2778 		i = getObjectIndex(args[1]);
2779 		assert(i);
2780 		push(_objs[i].width);
2781 		break;
2782 	case 210:
2783 		i = getObjectIndex(args[1]);
2784 		assert(i);
2785 		push(_objs[i].height);
2786 		break;
2787 	case 211:
2788 		/*
2789 		   13 = thrust
2790 		   336 = thrust
2791 		   328 = thrust
2792 		   27 = abort
2793 		   97 = left
2794 		   331 = left
2795 		   115 = right
2796 		   333 = right
2797 		 */
2798 
2799 		push(getKeyState(args[1]));
2800 		break;
2801 	case 212:
2802 		a = derefActor(args[1], "o6_kernelGetFunctions:212");
2803 		// This is used by walk scripts
2804 		push(a->_frame);
2805 		break;
2806 	case 213:
2807 		slot = getVerbSlot(args[1], 0);
2808 		push(_verbs[slot].curRect.left);
2809 		break;
2810 	case 214:
2811 		slot = getVerbSlot(args[1], 0);
2812 		push(_verbs[slot].curRect.top);
2813 		break;
2814 	case 215:
2815 		if ((_extraBoxFlags[args[1]] & 0x00FF) == 0x00C0) {
2816 			push(_extraBoxFlags[args[1]]);
2817 		} else {
2818 			push(getBoxFlags(args[1]));
2819 		}
2820 		break;
2821 	default:
2822 		error("o6_kernelGetFunctions: default case %d", args[0]);
2823 	}
2824 }
2825 
getKeyState(int key)2826 int ScummEngine::getKeyState(int key) {
2827 	switch (key) {
2828 	case 0x147: // Home
2829 		// FIXME: There seems to be a mistake in the code here ("insert" vs. "home")
2830 		return (_keyDownMap[Common::KEYCODE_KP7] ||
2831 		        _keyDownMap[Common::KEYCODE_INSERT]) ? 1 : 0;
2832 	case 0x148: // Up
2833 		return (_keyDownMap[Common::KEYCODE_KP8] ||
2834 		        _keyDownMap[Common::KEYCODE_UP] ||
2835 				_keyDownMap[Common::KEYCODE_8]) ? 1 : 0;
2836 	case 0x149: // PgUp
2837 		return (_keyDownMap[Common::KEYCODE_KP9] ||
2838 		        _keyDownMap[Common::KEYCODE_PAGEUP]) ? 1 : 0;
2839 	case 0x14B: // Left
2840 		return (_keyDownMap[Common::KEYCODE_KP4] ||
2841 		        _keyDownMap[Common::KEYCODE_LEFT] ||
2842 				_keyDownMap[Common::KEYCODE_4]) ? 1 : 0;
2843 	case 0x14D: // Right
2844 		return (_keyDownMap[Common::KEYCODE_KP6] ||
2845 		        _keyDownMap[Common::KEYCODE_RIGHT] ||
2846 				_keyDownMap[Common::KEYCODE_6]) ? 1 : 0;
2847 	case 0x14F: // End
2848 		return (_keyDownMap[Common::KEYCODE_KP1] ||
2849 		        _keyDownMap[Common::KEYCODE_END]) ? 1 : 0;
2850 	case 0x150: // Down
2851 		return (_keyDownMap[Common::KEYCODE_KP2] ||
2852 		        _keyDownMap[Common::KEYCODE_DOWN] ||
2853 				_keyDownMap[Common::KEYCODE_2]) ? 1 : 0;
2854 	case 0x151: // PgDn
2855 		return (_keyDownMap[Common::KEYCODE_KP3] ||
2856 		        _keyDownMap[Common::KEYCODE_PAGEDOWN]) ? 1 : 0;
2857 	default:
2858 		return (_keyDownMap[key]) ? 1 : 0;
2859 	}
2860 }
2861 
o6_delayFrames()2862 void ScummEngine_v6::o6_delayFrames() {
2863 	ScriptSlot *ss = &vm.slot[_currentScript];
2864 	if (ss->delayFrameCount == 0) {
2865 		ss->delayFrameCount = pop();
2866 	} else {
2867 		ss->delayFrameCount--;
2868 	}
2869 	if (ss->delayFrameCount) {
2870 		_scriptPointer--;
2871 		o6_breakHere();
2872 	}
2873 }
2874 
o6_pickOneOf()2875 void ScummEngine_v6::o6_pickOneOf() {
2876 	int args[100];
2877 	int i, num;
2878 
2879 	num = getStackList(args, ARRAYSIZE(args));
2880 	i = pop();
2881 	if (i < 0 || i > num)
2882 		error("o6_pickOneOf: %d out of range (0, %d)", i, num - 1);
2883 	push(args[i]);
2884 }
2885 
o6_pickOneOfDefault()2886 void ScummEngine_v6::o6_pickOneOfDefault() {
2887 	int args[100];
2888 	int i, num, def;
2889 
2890 	def = pop();
2891 	num = getStackList(args, ARRAYSIZE(args));
2892 	i = pop();
2893 	if (i < 0 || i >= num)
2894 		i = def;
2895 	else
2896 		i = args[i];
2897 	push(i);
2898 }
2899 
o6_stampObject()2900 void ScummEngine_v6::o6_stampObject() {
2901 	int object, x, y, state;
2902 
2903 	state = pop();
2904 	y = pop();
2905 	x = pop();
2906 	object = pop();
2907 	if (_game.version >= 7 && object < 30) {
2908 		if (state == 0)
2909 			state = 255;
2910 
2911 		Actor *a = derefActor(object, "o6_stampObject");
2912 		a->_scalex = state;
2913 		a->_scaley = state;
2914 		a->putActor(x, y, _currentRoom);
2915 		a->_drawToBackBuf = true;
2916 		a->drawActorCostume();
2917 		a->_drawToBackBuf = false;
2918 		a->drawActorCostume();
2919 		return;
2920 	}
2921 
2922 	if (state == 0)
2923 		state = 1;
2924 
2925 	int objnum = getObjectIndex(object);
2926 	if (objnum == -1)
2927 		return;
2928 
2929 	if (x != -1) {
2930 		_objs[objnum].x_pos = x * 8;
2931 		_objs[objnum].y_pos = y * 8;
2932 	}
2933 
2934 	putState(object, state);
2935 	drawObject(objnum, 0);
2936 }
2937 
o6_stopTalking()2938 void ScummEngine_v6::o6_stopTalking() {
2939 	stopTalk();
2940 }
2941 
o6_findAllObjects()2942 void ScummEngine_v6::o6_findAllObjects() {
2943 	int room = pop();
2944 	int i = 1;
2945 
2946 	if (room != _currentRoom)
2947 		error("o6_findAllObjects: current room is not %d", room);
2948 	writeVar(0, 0);
2949 	defineArray(0, kIntArray, 0, _numLocalObjects + 1);
2950 	writeArray(0, 0, 0, _numLocalObjects);
2951 
2952 	while (i < _numLocalObjects) {
2953 		writeArray(0, 0, i, _objs[i].obj_nr);
2954 		i++;
2955 	}
2956 
2957 	push(readVar(0));
2958 }
2959 
shuffleArray(int num,int minIdx,int maxIdx)2960 void ScummEngine_v6::shuffleArray(int num, int minIdx, int maxIdx) {
2961 	int range = maxIdx - minIdx;
2962 	int count = range * 2;
2963 
2964 	// Shuffle the array 'num'
2965 	while (count--) {
2966 		// Determine two random elements...
2967 		int rand1 = _rnd.getRandomNumber(range) + minIdx;
2968 		int rand2 = _rnd.getRandomNumber(range) + minIdx;
2969 
2970 		// ...and swap them
2971 		int val1 = readArray(num, 0, rand1);
2972 		int val2 = readArray(num, 0, rand2);
2973 		writeArray(num, 0, rand1, val2);
2974 		writeArray(num, 0, rand2, val1);
2975 	}
2976 }
2977 
o6_shuffle()2978 void ScummEngine_v6::o6_shuffle() {
2979 	int b = pop();
2980 	int a = pop();
2981 	shuffleArray(fetchScriptWord(), a, b);
2982 }
2983 
o6_pickVarRandom()2984 void ScummEngine_v6::o6_pickVarRandom() {
2985 	int num;
2986 	int args[100];
2987 	int dim1;
2988 
2989 	num = getStackList(args, ARRAYSIZE(args));
2990 	int value = fetchScriptWord();
2991 
2992 	if (readVar(value) == 0) {
2993 		defineArray(value, kIntArray, 0, num);
2994 		if (num > 0) {
2995 			int16 counter = 0;
2996 			do {
2997 				writeArray(value, 0, counter + 1, args[counter]);
2998 			} while (++counter < num);
2999 		}
3000 
3001 		shuffleArray(value, 1, num);
3002 		writeArray(value, 0, 0, 2);
3003 		push(readArray(value, 0, 1));
3004 		return;
3005 	}
3006 
3007 	num = readArray(value, 0, 0);
3008 
3009 	ArrayHeader *ah = getArray(value);
3010 	dim1 = FROM_LE_16(ah->dim1) - 1;
3011 
3012 	if (dim1 < num) {
3013 		int16 var_2 = readArray(value, 0, num - 1);
3014 		shuffleArray(value, 1, dim1);
3015 		if (readArray(value, 0, 1) == var_2) {
3016 			num = 2;
3017 		} else {
3018 			num = 1;
3019 		}
3020 	}
3021 
3022 	writeArray(value, 0, 0, num + 1);
3023 	push(readArray(value, 0, num));
3024 }
3025 
o6_getDateTime()3026 void ScummEngine_v6::o6_getDateTime() {
3027 	TimeDate t;
3028 	_system->getTimeAndDate(t);
3029 
3030 	VAR(VAR_TIMEDATE_YEAR) = t.tm_year;
3031 	VAR(VAR_TIMEDATE_MONTH) = t.tm_mon;
3032 	VAR(VAR_TIMEDATE_DAY) = t.tm_mday;
3033 	VAR(VAR_TIMEDATE_HOUR) = t.tm_hour;
3034 	VAR(VAR_TIMEDATE_MINUTE) = t.tm_min;
3035 
3036 	if (_game.version == 8)
3037 		VAR(VAR_TIMEDATE_SECOND) = t.tm_sec;
3038 }
3039 
o6_getPixel()3040 void ScummEngine_v6::o6_getPixel() {
3041 	int x, y;
3042 
3043 	if (_game.heversion >= 61 && _game.heversion <= 62) {
3044 		x = pop();
3045 		y = pop();
3046 	} else {
3047 		y = pop();
3048 		x = pop();
3049 	}
3050 
3051 	VirtScreen *vs = findVirtScreen(y);
3052 
3053 	if (vs == NULL || x > _screenWidth - 1 || x < 0) {
3054 		push(-1);
3055 		return;
3056 	}
3057 
3058 	byte pixel = *vs->getPixels(x, y - vs->topline);
3059 	push(pixel);
3060 }
3061 
o6_setBoxSet()3062 void ScummEngine_v6::o6_setBoxSet() {
3063 	int arg = pop() - 1;
3064 
3065 	const byte *room = getResourceAddress(rtRoom, _roomResource);
3066 	const byte *boxd = NULL, *boxm = NULL;
3067 	int32 dboxSize, mboxSize;
3068 	int i;
3069 
3070 	ResourceIterator boxds(room, false);
3071 	for (i = 0; i < arg; i++)
3072 		boxd = boxds.findNext(MKTAG('B','O','X','D'));
3073 
3074 	if (!boxd)
3075 		error("ScummEngine_v6::o6_setBoxSet: Can't find dboxes for set %d", arg);
3076 
3077 	dboxSize = READ_BE_UINT32(boxd + 4) - 8;
3078 	byte *matrix = _res->createResource(rtMatrix, 2, dboxSize);
3079 
3080 	assert(matrix);
3081 	memcpy(matrix, boxd + 8, dboxSize);
3082 
3083 	ResourceIterator boxms(room, false);
3084 	for (i = 0; i < arg; i++)
3085 		boxm = boxms.findNext(MKTAG('B','O','X','M'));
3086 
3087 	if (!boxm)
3088 		error("ScummEngine_v6::o6_setBoxSet: Can't find mboxes for set %d", arg);
3089 
3090 	mboxSize = READ_BE_UINT32(boxm + 4) - 8;
3091 	matrix = _res->createResource(rtMatrix, 1, mboxSize);
3092 
3093 	assert(matrix);
3094 	memcpy(matrix, boxm + 8, mboxSize);
3095 
3096 	if (_game.version == 7)
3097 		putActors();
3098 }
3099 
decodeParseString(int m,int n)3100 void ScummEngine_v6::decodeParseString(int m, int n) {
3101 	byte b = fetchScriptByte();
3102 
3103 	switch (b) {
3104 	case 65:		// SO_AT
3105 		_string[m].ypos = pop();
3106 		_string[m].xpos = pop();
3107 		_string[m].overhead = false;
3108 		break;
3109 	case 66:		// SO_COLOR
3110 		_string[m].color = pop();
3111 		break;
3112 	case 67:		// SO_CLIPPED
3113 		_string[m].right = pop();
3114 		break;
3115 	case 69:		// SO_CENTER
3116 		_string[m].center = true;
3117 		_string[m].overhead = false;
3118 		break;
3119 	case 71:		// SO_LEFT
3120 		_string[m].center = false;
3121 		_string[m].overhead = false;
3122 		break;
3123 	case 72:		// SO_OVERHEAD
3124 		_string[m].overhead = true;
3125 		_string[m].no_talk_anim = false;
3126 		break;
3127 	case 74:		// SO_MUMBLE
3128 		_string[m].no_talk_anim = true;
3129 		break;
3130 	case 75:		// SO_TEXTSTRING
3131 		printString(m, _scriptPointer);
3132 		_scriptPointer += resStrLen(_scriptPointer) + 1;
3133 		break;
3134 	case 0xFE:
3135 		_string[m].loadDefault();
3136 		if (n)
3137 			_actorToPrintStrFor = pop();
3138 		break;
3139 	case 0xFF:
3140 		_string[m].saveDefault();
3141 		break;
3142 	default:
3143 		error("decodeParseString: default case 0x%x", b);
3144 	}
3145 }
3146 
3147 } // End of namespace Scumm
3148