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 #ifdef ENABLE_HE
24 
25 #include "common/config-manager.h"
26 #include "common/savefile.h"
27 #include "common/system.h"
28 
29 #include "scumm/actor.h"
30 #include "scumm/charset.h"
31 #include "scumm/dialogs.h"
32 #include "scumm/file.h"
33 #include "scumm/he/intern_he.h"
34 #include "scumm/object.h"
35 #include "scumm/resource.h"
36 #include "scumm/scumm.h"
37 #include "scumm/he/sound_he.h"
38 #include "scumm/util.h"
39 #include "scumm/verbs.h"
40 
41 namespace Scumm {
42 
43 #define OPCODE(i, x)	_opcodes[i]._OPCODE(ScummEngine_v72he, x)
44 
setupOpcodes()45 void ScummEngine_v72he::setupOpcodes() {
46 	ScummEngine_v71he::setupOpcodes();
47 
48 	OPCODE(0x02, o72_pushDWord);
49 	OPCODE(0x04, o72_getScriptString);
50 	_opcodes[0x0a].setProc(0, 0);
51 	OPCODE(0x1b, o72_isAnyOf);
52 	_opcodes[0x42].setProc(0, 0);
53 	_opcodes[0x46].setProc(0, 0);
54 	_opcodes[0x4a].setProc(0, 0);
55 	_opcodes[0x4e].setProc(0, 0);
56 	OPCODE(0x50, o72_resetCutscene);
57 	OPCODE(0x52, o72_findObjectWithClassOf);
58 	OPCODE(0x54, o72_getObjectImageX);
59 	OPCODE(0x55, o72_getObjectImageY);
60 	OPCODE(0x56, o72_captureWizImage);
61 	OPCODE(0x58, o72_getTimer);
62 	OPCODE(0x59, o72_setTimer);
63 	OPCODE(0x5a, o72_getSoundPosition);
64 	OPCODE(0x5e, o72_startScript);
65 	OPCODE(0x60, o72_startObject);
66 	OPCODE(0x61, o72_drawObject);
67 	OPCODE(0x62, o72_printWizImage);
68 	OPCODE(0x63, o72_getArrayDimSize);
69 	OPCODE(0x64, o72_getNumFreeArrays);
70 	_opcodes[0x97].setProc(0, 0);	// was: o6_setObjectName
71 	OPCODE(0x9c, o72_roomOps);
72 	OPCODE(0x9d, o72_actorOps);
73 	OPCODE(0x9e, o72_verbOps);
74 	OPCODE(0xa0, o72_findObject);
75 	OPCODE(0xa4, o72_arrayOps);
76 	OPCODE(0xae, o72_systemOps);
77 	OPCODE(0xba, o72_talkActor);
78 	OPCODE(0xbb, o72_talkEgo);
79 	OPCODE(0xbc, o72_dimArray);
80 	OPCODE(0xc0, o72_dim2dimArray);
81 	OPCODE(0xc1, o72_traceStatus);
82 	OPCODE(0xc8, o72_kernelGetFunctions);
83 	OPCODE(0xce, o72_drawWizImage);
84 	OPCODE(0xcf, o72_debugInput);
85 	OPCODE(0xd5, o72_jumpToScript);
86 	OPCODE(0xda, o72_openFile);
87 	OPCODE(0xdb, o72_readFile);
88 	OPCODE(0xdc, o72_writeFile);
89 	OPCODE(0xdd, o72_findAllObjects);
90 	OPCODE(0xde, o72_deleteFile);
91 	OPCODE(0xdf, o72_rename);
92 	OPCODE(0xe1, o72_getPixel);
93 	OPCODE(0xe3, o72_pickVarRandom);
94 	OPCODE(0xea, o72_redimArray);
95 	OPCODE(0xf3, o72_readINI);
96 	OPCODE(0xf4, o72_writeINI);
97 	OPCODE(0xf8, o72_getResourceSize);
98 	OPCODE(0xf9, o72_createDirectory);
99 	OPCODE(0xfa, o72_setSystemMessage);
100 }
101 
102 static const int arrayDataSizes[] = { 0, 1, 4, 8, 8, 16, 32 };
103 
defineArray(int array,int type,int dim2start,int dim2end,int dim1start,int dim1end)104 byte *ScummEngine_v72he::defineArray(int array, int type, int dim2start, int dim2end,
105 											int dim1start, int dim1end) {
106 	int id;
107 	int size;
108 	ArrayHeader *ah;
109 
110 	assert(dim2start >= 0 && dim2start <= dim2end);
111 	assert(dim1start >= 0 && dim1start <= dim1end);
112 	assert(0 <= type && type <= 6);
113 
114 
115 	if (type == kBitArray || type == kNibbleArray)
116 		type = kByteArray;
117 
118 	nukeArray(array);
119 
120 	id = findFreeArrayId();
121 
122 	debug(9, "defineArray (array %d, dim2start %d, dim2end %d dim1start %d dim1end %d", id, dim2start, dim2end, dim1start, dim1end);
123 
124 	if (array & 0x80000000) {
125 		error("Can't define bit variable as array pointer");
126 	}
127 
128 	size = arrayDataSizes[type];
129 
130 	if (_game.heversion >= 80)
131 		id |= 0x33539000;
132 
133 	writeVar(array, id);
134 
135 	if (_game.heversion >= 80)
136 		id &= ~0x33539000;
137 
138 	size *= dim2end - dim2start + 1;
139 	size *= dim1end - dim1start + 1;
140 	size >>= 3;
141 
142 	ah = (ArrayHeader *)_res->createResource(rtString, id, size + sizeof(ArrayHeader));
143 
144 	ah->type = TO_LE_32(type);
145 	ah->dim1start = TO_LE_32(dim1start);
146 	ah->dim1end = TO_LE_32(dim1end);
147 	ah->dim2start = TO_LE_32(dim2start);
148 	ah->dim2end = TO_LE_32(dim2end);
149 
150 	return ah->data;
151 }
152 
readArray(int array,int idx2,int idx1)153 int ScummEngine_v72he::readArray(int array, int idx2, int idx1) {
154 	debug(9, "readArray (array %d, idx2 %d, idx1 %d)", readVar(array), idx2, idx1);
155 
156 	if (readVar(array) == 0)
157 		error("readArray: Reference to zeroed array pointer");
158 
159 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
160 
161 	if (!ah)
162 		error("readArray: invalid array %d (%d)", array, readVar(array));
163 
164 	if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
165 		idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
166 		error("readArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
167 			  array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
168 			  FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
169 	}
170 
171 	const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
172 		(idx2 - FROM_LE_32(ah->dim2start)) + (idx1 - FROM_LE_32(ah->dim1start));
173 
174 	switch (FROM_LE_32(ah->type)) {
175 	case kByteArray:
176 	case kStringArray:
177 		return ah->data[offset];
178 
179 	case kIntArray:
180 		return (int16)READ_LE_UINT16(ah->data + offset * 2);
181 
182 	case kDwordArray:
183 		return (int32)READ_LE_UINT32(ah->data + offset * 4);
184 	}
185 
186 	return 0;
187 }
188 
writeArray(int array,int idx2,int idx1,int value)189 void ScummEngine_v72he::writeArray(int array, int idx2, int idx1, int value) {
190 	debug(9, "writeArray (array %d, idx2 %d, idx1 %d, value %d)", readVar(array), idx2, idx1, value);
191 
192 	if (readVar(array) == 0)
193 		error("writeArray: Reference to zeroed array pointer");
194 
195 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
196 
197 	if (!ah)
198 		error("writeArray: Invalid array (%d) reference", readVar(array));
199 
200 	if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
201 		idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
202 		error("writeArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
203 			  array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
204 			  FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
205 	}
206 
207 	const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
208 		(idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
209 
210 	switch (FROM_LE_32(ah->type)) {
211 	case kByteArray:
212 	case kStringArray:
213 		ah->data[offset] = value;
214 		break;
215 
216 	case kIntArray:
217 		WRITE_LE_UINT16(ah->data + offset * 2, value);
218 		break;
219 
220 	case kDwordArray:
221 		WRITE_LE_UINT32(ah->data + offset * 4, value);
222 		break;
223 	}
224 }
225 
setupStringArray(int size)226 int ScummEngine_v72he::setupStringArray(int size) {
227 	writeVar(0, 0);
228 	defineArray(0, kStringArray, 0, 0, 0, size + 1);
229 	writeArray(0, 0, 0, 0);
230 	return readVar(0);
231 }
232 
setupStringArrayFromString(const char * cStr)233 int ScummEngine_v72he::setupStringArrayFromString(const char *cStr) {
234 	// this is PUI_ScummStringArrayFromCString() found in PUSERMAC.cpp
235 	// I can see how its done up there in setupStringArray()
236 	// yet I'd note that 'SCUMMVAR_user_reserved' var was used instead of 0
237 	// and strlen(), not strlen() + 1 was used
238 	// plus, this function actually copies the string, not just 'sets up' the array
239 
240 	writeVar(0, 0);
241 
242 	int len = strlen(cStr) + 1;
243 	byte *ptr = defineArray(0, kStringArray, 0, 0, 0, len);
244 	if (ptr != nullptr)
245 		Common::strlcpy((char*)ptr, cStr, len);
246 
247 	return readVar(0);
248 }
249 
readArrayFromIndexFile()250 void ScummEngine_v72he::readArrayFromIndexFile() {
251 	int num;
252 	int a, b, c;
253 
254 	while ((num = _fileHandle->readUint16LE()) != 0) {
255 		a = _fileHandle->readUint16LE();
256 		b = _fileHandle->readUint16LE();
257 		c = _fileHandle->readUint16LE();
258 
259 		if (c == 1)
260 			defineArray(num, kBitArray, 0, a, 0, b);
261 		else
262 			defineArray(num, kDwordArray, 0, a, 0, b);
263 	}
264 }
265 
copyScriptString(byte * dst,int dstSize)266 void ScummEngine_v72he::copyScriptString(byte *dst, int dstSize) {
267 	byte string[1024];
268 	byte chr;
269 	int pos = 0;
270 
271 	int array = pop();
272 	if (array == -1) {
273 		if (_stringLength == 1)
274 			error("String stack underflow");
275 
276 		_stringLength -= 2;
277 		 while ((chr = _stringBuffer[_stringLength]) != 0) {
278 			string[pos] = chr;
279 			pos++;
280 
281 			if (pos > dstSize)
282 				error("String too long to pop");
283 
284 			_stringLength--;
285 		}
286 
287 		string[pos] = 0;
288 		_stringLength++;
289 
290 		// Reverse string
291 		int len = resStrLen(string);
292 		while (len--)
293 			*dst++ = string[len];
294 	} else {
295 		writeVar(0, array);
296 		while ((chr = readArray(0, 0, pos)) != 0) {
297 			*dst++ = chr;
298 			pos++;
299 		}
300 	}
301 	*dst = 0;
302 }
303 
decodeScriptString(byte * dst,bool scriptString)304 void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) {
305 	const byte *src;
306 	int args[31];
307 	int num, len, val;
308 	byte chr, string[1024];
309 	memset(args, 0, sizeof(args));
310 	memset(string, 0, sizeof(string));
311 
312 	// Get stack list, plus one
313 	num = pop();
314 	for (int i = num; i >= 0; i--)
315 		args[i] = pop();
316 
317 	// Get string
318 	if (scriptString) {
319 		len = resStrLen(_scriptPointer) + 1;
320 		memcpy(string, _scriptPointer, len);
321 		_scriptPointer += len;
322 	} else {
323 		copyScriptString(string, sizeof(string));
324 		len = resStrLen(string) + 1;
325 	}
326 
327 	// Decode string
328 	num = 0;
329 	val = 0;
330 	while (len--) {
331 		chr = string[num++];
332 		if (chr == '%') {
333 			chr = string[num++];
334 			switch (chr) {
335 			case 'b':
336 				//dst += sprintf((char *)dst, "%b", args[val++]);
337 				break;
338 			case 'c':
339 				*dst++ = args[val++];
340 				break;
341 			case 'd':
342 				dst += sprintf((char *)dst, "%d", args[val++]);
343 				break;
344 			case 's':
345 				src = getStringAddress(args[val++]);
346 				if (src) {
347 					while (*src != 0)
348 						*dst++ = *src++;
349 				}
350 				break;
351 			case 'x':
352 				dst += sprintf((char *)dst, "%x", args[val++]);
353 				break;
354 			default:
355 				*dst++ = '%';
356 				num--;
357 				break;
358 			}
359 		} else {
360 			*dst++ = chr;
361 		}
362 	}
363 	*dst = 0;
364 }
365 
findObject(int x,int y,int num,int * args)366 int ScummEngine_v72he::findObject(int x, int y, int num, int *args) {
367 	int b, cls, i, result;
368 
369 	for (i = 1; i < _numLocalObjects; i++) {
370 		result = 0;
371 		if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))
372 			continue;
373 
374 		// Check polygon bounds
375 		if (_wiz->polygonDefined(_objs[i].obj_nr)) {
376 			if (_wiz->polygonHit(_objs[i].obj_nr, x, y))
377 				result = _objs[i].obj_nr;
378 			else if (VAR_POLYGONS_ONLY != 0xFF && VAR(VAR_POLYGONS_ONLY))
379 				continue;
380 		}
381 
382 		if (!result) {
383 			// Check object bounds
384 			if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
385 			    _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y)
386 					result = _objs[i].obj_nr;
387 		}
388 
389 		if (result) {
390 			if (!num)
391 				return result;
392 
393 			// Check object class
394 			cls = args[0];
395 			b = getClass(_objs[i].obj_nr, cls);
396 			if ((cls & 0x80 && b) || (!(cls & 0x80) && !b))
397 				return result;
398 		}
399 	}
400 
401 	return 0;
402 }
403 
o72_pushDWord()404 void ScummEngine_v72he::o72_pushDWord() {
405 	push(fetchScriptDWordSigned());
406 }
407 
o72_getScriptString()408 void ScummEngine_v72he::o72_getScriptString() {
409 	byte chr;
410 
411 	while ((chr = fetchScriptByte()) != 0) {
412 		_stringBuffer[_stringLength] = chr;
413 		_stringLength++;
414 
415 		if (_stringLength >= 4096)
416 			error("String stack overflow");
417 	}
418 
419 	_stringBuffer[_stringLength] = 0;
420 	_stringLength++;
421 }
422 
o72_isAnyOf()423 void ScummEngine_v72he::o72_isAnyOf() {
424 	int args[128];
425 	int num, value;
426 
427 	num = getStackList(args, ARRAYSIZE(args));
428 	value = pop();
429 
430 	for (int i = 0; i < num; i++) {
431 		if (args[i] == value) {
432 			push(1);
433 			return;
434 		}
435 	}
436 
437 	push(0);
438 }
439 
o72_resetCutscene()440 void ScummEngine_v72he::o72_resetCutscene() {
441 	int idx;
442 
443 	idx = vm.cutSceneStackPointer;
444 	vm.cutSceneStackPointer = 0;
445 	vm.cutScenePtr[idx] = 0;
446 	vm.cutSceneScript[idx] = 0;
447 
448 	VAR(VAR_OVERRIDE) = 0;
449 }
450 
o72_findObjectWithClassOf()451 void ScummEngine_v72he::o72_findObjectWithClassOf() {
452 	int args[16], num;
453 
454 	num = getStackList(args, ARRAYSIZE(args));
455 	int y = pop();
456 	int x = pop();
457 	int r = findObject(x, y, num, args);
458 	push(r);
459 }
460 
o72_getObjectImageX()461 void ScummEngine_v72he::o72_getObjectImageX() {
462 	int object = pop();
463 	int objnum = getObjectIndex(object);
464 
465 	if (objnum == -1) {
466 		push(0);
467 		return;
468 	}
469 
470 	push(_objs[objnum].x_pos / 8);
471 }
472 
o72_getObjectImageY()473 void ScummEngine_v72he::o72_getObjectImageY() {
474 	int object = pop();
475 	int objnum = getObjectIndex(object);
476 
477 	if (objnum == -1) {
478 		push(0);
479 		return;
480 	}
481 
482 	push(_objs[objnum].y_pos / 8);
483 }
484 
o72_captureWizImage()485 void ScummEngine_v72he::o72_captureWizImage() {
486 	Common::Rect grab;
487 	grab.bottom = pop() + 1;
488 	grab.right = pop() + 1;
489 	grab.top = pop();
490 	grab.left = pop();
491 	_wiz->captureWizImage(pop(), grab, false, true);
492 }
493 
o72_getTimer()494 void ScummEngine_v72he::o72_getTimer() {
495 	int timer = pop();
496 	byte cmd = fetchScriptByte();
497 
498 	if (cmd == 10 || cmd == 50) {
499 		push(getHETimer(timer));
500 	} else {
501 		push(0);
502 	}
503 }
504 
o72_setTimer()505 void ScummEngine_v72he::o72_setTimer() {
506 	int timer = pop();
507 	byte cmd = fetchScriptByte();
508 
509 	if (cmd == 158 || cmd == 61) {
510 		setHETimer(timer);
511 	} else {
512 		error("TIMER command %d?", cmd);
513 	}
514 }
515 
o72_getSoundPosition()516 void ScummEngine_v72he::o72_getSoundPosition() {
517 	int snd = pop();
518 	push(((SoundHE *)_sound)->getSoundPos(snd));
519 }
520 
o72_startScript()521 void ScummEngine_v72he::o72_startScript() {
522 	int args[25];
523 	int script;
524 	byte flags;
525 
526 	getStackList(args, ARRAYSIZE(args));
527 	script = pop();
528 	flags = fetchScriptByte();
529 
530 	// HACK: The credits script in Russian HE99 version of Freddi Fish 3
531 	// uses null strings, causing various errors, so skip it.
532 	if (_game.id == GID_FREDDI3 && _game.heversion == 99 && _language == Common::RU_RUS &&
533 		_currentRoom == 40 && script == 2057) {
534 			return;
535 	}
536 
537 	runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
538 }
539 
o72_startObject()540 void ScummEngine_v72he::o72_startObject() {
541 	int args[25];
542 	int script, entryp;
543 	byte flags;
544 
545 	getStackList(args, ARRAYSIZE(args));
546 	entryp = pop();
547 	script = pop();
548 	flags = fetchScriptByte();
549 	runObjectScript(script, entryp, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
550 }
551 
o72_drawObject()552 void ScummEngine_v72he::o72_drawObject() {
553 	byte subOp = fetchScriptByte();
554 	int state, y, x;
555 
556 	switch (subOp) {
557 	case 62:
558 		state = pop();
559 		y = pop();
560 		x = pop();
561 		break;
562 	case 63:
563 		state = pop();
564 		if (state == 0)
565 			state = 1;
566 		y = x = -100;
567 		break;
568 	case 65:
569 		state = 1;
570 		y = pop();
571 		x = pop();
572 		break;
573 	default:
574 		error("o72_drawObject: default case %d", subOp);
575 	}
576 
577 	int object = pop();
578 	int objnum = getObjectIndex(object);
579 	if (objnum == -1)
580 		return;
581 
582 	if (y != -100 && x != -100) {
583 		_objs[objnum].x_pos = x * 8;
584 		_objs[objnum].y_pos = y * 8;
585 	}
586 
587 	if (state != -1) {
588 		addObjectToDrawQue(objnum);
589 		putState(object, state);
590 	}
591 }
592 
o72_printWizImage()593 void ScummEngine_v72he::o72_printWizImage() {
594 	WizImage wi;
595 	wi.resNum = pop();
596 	wi.x1 = wi.y1 = 0;
597 	wi.state = 0;
598 	wi.flags = kWIFPrint;
599 	_wiz->displayWizImage(&wi);
600 }
601 
o72_getArrayDimSize()602 void ScummEngine_v72he::o72_getArrayDimSize() {
603 	byte subOp = fetchScriptByte();
604 	int32 val1, val2;
605 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(fetchScriptWord()));
606 	if (!ah) {
607 		push(0);
608 		return;
609 	}
610 
611 	switch (subOp) {
612 	case 1:
613 	case 3:
614 		val1 = FROM_LE_32(ah->dim1end);
615 		val2 = FROM_LE_32(ah->dim1start);
616 		push(val1 - val2 + 1);
617 		break;
618 	case 2:
619 		val1 = FROM_LE_32(ah->dim2end);
620 		val2 = FROM_LE_32(ah->dim2start);
621 		push(val1 - val2 + 1);
622 		break;
623 	case 4:
624 		push(FROM_LE_32(ah->dim1start));
625 		break;
626 	case 5:
627 		push(FROM_LE_32(ah->dim1end));
628 		break;
629 	case 6:
630 		push(FROM_LE_32(ah->dim2start));
631 		break;
632 	case 7:
633 		push(FROM_LE_32(ah->dim2end));
634 		break;
635 	default:
636 		error("o72_getArrayDimSize: default case %d", subOp);
637 	}
638 }
639 
o72_getNumFreeArrays()640 void ScummEngine_v72he::o72_getNumFreeArrays() {
641 	const ResourceManager::ResTypeData &rtd = _res->_types[rtString];
642 	int i, num = 0;
643 
644 	for (i = 1; i < _numArray; i++) {
645 		if (!rtd[i]._address)
646 			num++;
647 	}
648 
649 	push(num);
650 }
651 
o72_roomOps()652 void ScummEngine_v72he::o72_roomOps() {
653 	int a, b, c, d, e;
654 
655 	byte subOp = fetchScriptByte();
656 
657 	switch (subOp) {
658 	case 172:		// SO_ROOM_SCROLL
659 		b = pop();
660 		a = pop();
661 		if (a < (_screenWidth / 2))
662 			a = (_screenWidth / 2);
663 		if (b < (_screenWidth / 2))
664 			b = (_screenWidth / 2);
665 		if (a > _roomWidth - (_screenWidth / 2))
666 			a = _roomWidth - (_screenWidth / 2);
667 		if (b > _roomWidth - (_screenWidth / 2))
668 			b = _roomWidth - (_screenWidth / 2);
669 		VAR(VAR_CAMERA_MIN_X) = a;
670 		VAR(VAR_CAMERA_MAX_X) = b;
671 		break;
672 
673 	case 174:		// SO_ROOM_SCREEN
674 		b = pop();
675 		a = pop();
676 		initScreens(a, _screenHeight);
677 		break;
678 
679 	case 175:		// SO_ROOM_PALETTE
680 		d = pop();
681 		c = pop();
682 		b = pop();
683 		a = pop();
684 		setPalColor(d, a, b, c);
685 		break;
686 
687 	case 179:		// SO_ROOM_INTENSITY
688 		c = pop();
689 		b = pop();
690 		a = pop();
691 		darkenPalette(a, a, a, b, c);
692 		break;
693 
694 	case 180:		// SO_ROOM_SAVEGAME
695 		_saveTemporaryState = true;
696 		_saveLoadSlot = pop();
697 		_saveLoadFlag = pop();
698 		break;
699 
700 	case 181:		// SO_ROOM_FADE
701 		// Defaults to 1 but doesn't use fade effects
702 		a = pop();
703 		break;
704 
705 	case 182:		// SO_RGB_ROOM_INTENSITY
706 		e = pop();
707 		d = pop();
708 		c = pop();
709 		b = pop();
710 		a = pop();
711 		darkenPalette(a, b, c, d, e);
712 		break;
713 
714 	case 213:		// SO_ROOM_NEW_PALETTE
715 		a = pop();
716 		setCurrentPalette(a);
717 		break;
718 
719 	case 220:		// SO_ROOM_COPY_PALETTE
720 		a = pop();
721 		b = pop();
722 		copyPalColor(a, b);
723 		break;
724 
725 	case 221:		// SO_ROOM_SAVEGAME_BY_NAME
726 		byte buffer[256];
727 
728 		copyScriptString((byte *)buffer, sizeof(buffer));
729 
730 		_saveLoadFileName = (char *)buffer;
731 		debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName.c_str());
732 
733 		_saveLoadFlag = pop();
734 		_saveLoadSlot = 255;
735 		_saveTemporaryState = true;
736 		break;
737 
738 	case 234:		// SO_OBJECT_ORDER
739 		b = pop();
740 		a = pop();
741 		swapObjects(a, b);
742 		break;
743 
744 	case 236:		// SO_ROOM_PALETTE_IN_ROOM
745 		b = pop();
746 		a = pop();
747 		setRoomPalette(a, b);
748 		break;
749 
750 	default:
751 		error("o72_roomOps: default case %d", subOp);
752 	}
753 }
754 
o72_actorOps()755 void ScummEngine_v72he::o72_actorOps() {
756 	ActorHE *a;
757 	int i, j, k;
758 	int args[32];
759 	byte string[256];
760 
761 	byte subOp = fetchScriptByte();
762 	if (subOp == 197) {
763 		_curActor = pop();
764 		return;
765 	}
766 
767 	a = (ActorHE *)derefActorSafe(_curActor, "o72_actorOps");
768 	if (!a)
769 		return;
770 
771 	switch (subOp) {
772 	case 21: 		// SO_CONDITION (HE 80+)
773 		k = getStackList(args, ARRAYSIZE(args));
774 		for (i = 0; i < k; ++i) {
775 			a->setUserCondition(args[i] & 0x7F, args[i] & 0x80);
776 		}
777 		break;
778 	case 24: 		// SO_TALK_CONDITION (HE 80+)
779 		k = pop();
780 		if (k == 0)
781 			k = _rnd.getRandomNumberRng(1, 10);
782 		a->_heNoTalkAnimation = 1;
783 		a->setTalkCondition(k);
784 		break;
785 	case 43: 		// SO_PRIORITY (HE 90+)
786 		a->_layer = pop();
787 		a->_needRedraw = true;
788 		break;
789 	case 64:		// SO_ACTOR_DEFAULT_CLIPPED
790 		_actorClipOverride.bottom = pop();
791 		_actorClipOverride.right = pop();
792 		_actorClipOverride.top = pop();
793 		_actorClipOverride.left = pop();
794 		adjustRect(_actorClipOverride);
795 		break;
796 	case 65: 		// SO_AT (HE 98+)
797 		j = pop();
798 		i = pop();
799 		a->putActor(i, j);
800 		break;
801 	case 67:		// SO_CLIPPED (HE 99+)
802 		a->_clipOverride.bottom = pop();
803 		a->_clipOverride.right = pop();
804 		a->_clipOverride.top = pop();
805 		a->_clipOverride.left = pop();
806 		adjustRect(a->_clipOverride);
807 		break;
808 	case 68: // 	// SO_ERASE (HE 90+)
809 		k = pop();
810 		a->setHEFlag(1, k);
811 		break;
812 	case 76:		// SO_COSTUME
813 		a->setActorCostume(pop());
814 		break;
815 	case 77:		// SO_STEP_DIST
816 		j = pop();
817 		i = pop();
818 		a->setActorWalkSpeed(i, j);
819 		break;
820 	case 78:		// SO_SOUND
821 		k = getStackList(args, ARRAYSIZE(args));
822 		for (i = 0; i < k; i++)
823 			a->_sound[i] = args[i];
824 		break;
825 	case 79:		// SO_WALK_ANIMATION
826 		a->_walkFrame = pop();
827 		break;
828 	case 80:		// SO_TALK_ANIMATION
829 		a->_talkStopFrame = pop();
830 		a->_talkStartFrame = pop();
831 		break;
832 	case 81:		// SO_STAND_ANIMATION
833 		a->_standFrame = pop();
834 		break;
835 	case 82:		// SO_ANIMATION
836 		// dummy case in scumm6
837 		pop();
838 		pop();
839 		pop();
840 		break;
841 	case 83:		// SO_DEFAULT
842 		a->initActor(0);
843 		break;
844 	case 84:		// SO_ELEVATION
845 		a->setElevation(pop());
846 		break;
847 	case 85:		// SO_ANIMATION_DEFAULT
848 		a->_initFrame = 1;
849 		a->_walkFrame = 2;
850 		a->_standFrame = 3;
851 		a->_talkStartFrame = 4;
852 		a->_talkStopFrame = 5;
853 		break;
854 	case 86:		// SO_PALETTE
855 		j = pop();
856 		i = pop();
857 		assertRange(0, i, 255, "palette slot");
858 		a->remapActorPaletteColor(i, j);
859 		a->_needRedraw = true;
860 		break;
861 	case 87:		// SO_TALK_COLOR
862 		a->_talkColor = pop();
863 		break;
864 	case 88:		// SO_ACTOR_NAME
865 		copyScriptString(string, sizeof(string));
866 		loadPtrToResource(rtActorName, a->_number, string);
867 		break;
868 	case 89:		// SO_INIT_ANIMATION
869 		a->_initFrame = pop();
870 		break;
871 	case 91:		// SO_ACTOR_WIDTH
872 		a->_width = pop();
873 		break;
874 	case 92:		// SO_SCALE
875 		i = pop();
876 		a->setScale(i, i);
877 		break;
878 	case 93:		// SO_NEVER_ZCLIP
879 		a->_forceClip = 0;
880 		break;
881 	case 94:		// SO_ALWAYS_ZCLIP
882 		a->_forceClip = pop();
883 		break;
884 	case 95:		// SO_IGNORE_BOXES
885 		a->_ignoreBoxes = 1;
886 		a->_forceClip = 0;
887 		if (a->isInCurrentRoom())
888 			a->putActor();
889 		break;
890 	case 96:		// SO_FOLLOW_BOXES
891 		a->_ignoreBoxes = 0;
892 		a->_forceClip = 0;
893 		if (a->isInCurrentRoom())
894 			a->putActor();
895 		break;
896 	case 97:		// SO_ANIMATION_SPEED
897 		a->setAnimSpeed(pop());
898 		break;
899 	case 98:		// SO_SHADOW
900 		a->_heXmapNum = pop();
901 		a->_needRedraw = true;
902 		break;
903 	case 99:		// SO_TEXT_OFFSET
904 		a->_talkPosY = pop();
905 		a->_talkPosX = pop();
906 		break;
907 	case 156:		// SO_CHARSET (HE 72+)
908 		a->_charset = pop();
909 		break;
910 	case 175:		// SO_ROOM_PALETTE (HE 99+)
911 		a->_hePaletteNum = pop();
912 		a->_needRedraw = true;
913 		break;
914 	case 198:		// SO_ACTOR_VARIABLE
915 		i = pop();
916 		a->setAnimVar(pop(), i);
917 		break;
918 	case 215:		// SO_ACTOR_IGNORE_TURNS_ON
919 		a->_ignoreTurns = true;
920 		break;
921 	case 216:		// SO_ACTOR_IGNORE_TURNS_OFF
922 		a->_ignoreTurns = false;
923 		break;
924 	case 217:		// SO_ACTOR_NEW
925 		a->initActor(2);
926 		break;
927 	case 218:		// SO_BACKGROUND_ON
928 		a->drawActorToBackBuf(a->getPos().x, a->getPos().y);
929 		break;
930 	case 219:		// SO_BACKGROUND_OFF
931 		a->_drawToBackBuf = false;
932 		a->_needRedraw = true;
933 		a->_needBgReset = true;
934 		break;
935 	case 225:		// SO_TALKIE
936 		{
937 		copyScriptString(string, sizeof(string));
938 		int slot = pop();
939 
940 		int len = resStrLen(string) + 1;
941 		memcpy(a->_heTalkQueue[slot].sentence, string, len);
942 
943 		a->_heTalkQueue[slot].posX = a->_talkPosX;
944 		a->_heTalkQueue[slot].posY = a->_talkPosY;
945 		a->_heTalkQueue[slot].color = a->_talkColor;
946 		break;
947 		}
948 	default:
949 		error("o72_actorOps: default case %d", subOp);
950 	}
951 }
952 
o72_verbOps()953 void ScummEngine_v72he::o72_verbOps() {
954 	int slot, a, b;
955 	VerbSlot *vs;
956 	byte name[200];
957 
958 	byte subOp = fetchScriptByte();
959 	if (subOp == 196) {
960 		_curVerb = pop();
961 		_curVerbSlot = getVerbSlot(_curVerb, 0);
962 		assertRange(0, _curVerbSlot, _numVerbs - 1, "new verb slot");
963 		return;
964 	}
965 	vs = &_verbs[_curVerbSlot];
966 	slot = _curVerbSlot;
967 	switch (subOp) {
968 	case 124:		// SO_VERB_IMAGE
969 		a = pop();
970 		if (_curVerbSlot) {
971 			setVerbObject(_roomResource, a, slot);
972 			vs->type = kImageVerbType;
973 			vs->imgindex = a;
974 		}
975 		break;
976 	case 125:		// SO_VERB_NAME
977 		copyScriptString(name, sizeof(name));
978 		loadPtrToResource(rtVerb, slot, name);
979 		vs->type = kTextVerbType;
980 		vs->imgindex = 0;
981 		break;
982 	case 126:		// SO_VERB_COLOR
983 		vs->color = pop();
984 		break;
985 	case 127:		// SO_VERB_HICOLOR
986 		vs->hicolor = pop();
987 		break;
988 	case 128:		// SO_VERB_AT
989 		vs->curRect.top = pop();
990 		vs->curRect.left = pop();
991 		break;
992 	case 129:		// SO_VERB_ON
993 		vs->curmode = 1;
994 		break;
995 	case 130:		// SO_VERB_OFF
996 		vs->curmode = 0;
997 		break;
998 	case 131:		// SO_VERB_DELETE
999 		slot = getVerbSlot(pop(), 0);
1000 		killVerb(slot);
1001 		break;
1002 	case 132:		// SO_VERB_NEW
1003 		slot = getVerbSlot(_curVerb, 0);
1004 		if (slot == 0) {
1005 			for (slot = 1; slot < _numVerbs; slot++) {
1006 				if (_verbs[slot].verbid == 0)
1007 					break;
1008 			}
1009 			if (slot == _numVerbs)
1010 				error("Too many verbs");
1011 			_curVerbSlot = slot;
1012 		}
1013 		vs = &_verbs[slot];
1014 		vs->verbid = _curVerb;
1015 		vs->color = 2;
1016 		vs->hicolor = 0;
1017 		vs->dimcolor = 8;
1018 		vs->type = kTextVerbType;
1019 		vs->charset_nr = _string[0]._default.charset;
1020 		vs->curmode = 0;
1021 		vs->saveid = 0;
1022 		vs->key = 0;
1023 		vs->center = 0;
1024 		vs->imgindex = 0;
1025 		break;
1026 	case 133:		// SO_VERB_DIMCOLOR
1027 		vs->dimcolor = pop();
1028 		break;
1029 	case 134:		// SO_VERB_DIM
1030 		vs->curmode = 2;
1031 		break;
1032 	case 135:		// SO_VERB_KEY
1033 		vs->key = pop();
1034 		break;
1035 	case 136:		// SO_VERB_CENTER
1036 		vs->center = 1;
1037 		break;
1038 	case 137:		// SO_VERB_NAME_STR
1039 		a = pop();
1040 		if (a == 0) {
1041 			loadPtrToResource(rtVerb, slot, (const byte *)"");
1042 		} else {
1043 			loadPtrToResource(rtVerb, slot, getStringAddress(a));
1044 		}
1045 		vs->type = kTextVerbType;
1046 		vs->imgindex = 0;
1047 		break;
1048 	case 139:		// SO_VERB_IMAGE_IN_ROOM
1049 		b = pop();
1050 		a = pop();
1051 
1052 		if (slot && a != vs->imgindex) {
1053 			setVerbObject(b, a, slot);
1054 			vs->type = kImageVerbType;
1055 			vs->imgindex = a;
1056 		}
1057 		break;
1058 	case 140:		// SO_VERB_BAKCOLOR
1059 		vs->bkcolor = pop();
1060 		break;
1061 	case 255:
1062 		drawVerb(slot, 0);
1063 		verbMouseOver(0);
1064 		break;
1065 	default:
1066 		error("o72_verbops: default case %d", subOp);
1067 	}
1068 }
1069 
o72_findObject()1070 void ScummEngine_v72he::o72_findObject() {
1071 	int y = pop();
1072 	int x = pop();
1073 	int r = findObject(x, y, 0, 0);
1074 	push(r);
1075 }
1076 
o72_arrayOps()1077 void ScummEngine_v72he::o72_arrayOps() {
1078 	byte *data;
1079 	byte string[1024];
1080 	int dim1end, dim1start, dim2end, dim2start;
1081 	int id, len, b, c, list[128];
1082 	int offs, tmp, tmp2;
1083 	uint tmp3;
1084 
1085 	byte subOp = fetchScriptByte();
1086 	int array = fetchScriptWord();
1087 	debug(9,"o72_arrayOps: array %d case %d", array, subOp);
1088 
1089 	switch (subOp) {
1090 	case 7:			// SO_ASSIGN_STRING
1091 		copyScriptString(string, sizeof(string));
1092 		len = resStrLen(string);
1093 		data = defineArray(array, kStringArray, 0, 0, 0, len);
1094 		memcpy(data, string, len);
1095 		break;
1096 
1097 	case 126:		// SO_COMPLEX_ARRAY_ASSIGNMENT
1098 		len = getStackList(list, ARRAYSIZE(list));
1099 		dim1end = pop();
1100 		dim1start = pop();
1101 		dim2end = pop();
1102 		dim2start = pop();
1103 		id = readVar(array);
1104 		if (id == 0) {
1105 			defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
1106 		}
1107 		checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
1108 
1109 		tmp2 = 0;
1110 		while (dim2start <= dim2end) {
1111 			tmp = dim1start;
1112 			while (tmp <= dim1end) {
1113 				writeArray(array, dim2start, tmp, list[tmp2++]);
1114 				if (tmp2 == len)
1115 					tmp2 = 0;
1116 				tmp++;
1117 			}
1118 			dim2start++;
1119 		}
1120 		break;
1121 	case 127:		// SO_COMPLEX_ARRAY_COPY_OPERATION
1122 		{
1123 			int a2_dim1end = pop();
1124 			int a2_dim1start = pop();
1125 			int a2_dim2end = pop();
1126 			int a2_dim2start = pop();
1127 			int array2 = fetchScriptWord();
1128 			int a1_dim1end = pop();
1129 			int a1_dim1start = pop();
1130 			int a1_dim2end = pop();
1131 			int a1_dim2start = pop();
1132 			if (a1_dim1end - a1_dim1start != a2_dim1end - a2_dim1start || a2_dim2end - a2_dim2start != a1_dim2end - a1_dim2start) {
1133 				error("Source and dest ranges size are mismatched");
1134 			}
1135 			copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
1136 		}
1137 		break;
1138 	case 128:		// SO_RANGE_ARRAY_ASSIGNMENT
1139 		b = pop();
1140 		c = pop();
1141 		dim1end = pop();
1142 		dim1start = pop();
1143 		dim2end = pop();
1144 		dim2start = pop();
1145 		id = readVar(array);
1146 		if (id == 0) {
1147 			defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
1148 		}
1149 		checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
1150 
1151 		offs = (b >= c) ? 1 : -1;
1152 		tmp2 = c;
1153 		tmp3 = c - b + 1;
1154 		while (dim2start <= dim2end) {
1155 			tmp = dim1start;
1156 			while (tmp <= dim1end) {
1157 				writeArray(array, dim2start, tmp, tmp2);
1158 				if (--tmp3 == 0) {
1159 					tmp2 = c;
1160 					tmp3 = c - b + 1;
1161 				} else {
1162 					tmp2 += offs;
1163 				}
1164 				tmp++;
1165 			}
1166 			dim2start++;
1167 		}
1168 		break;
1169 	case 194:		// SO_FORMATTED_STRING
1170 		decodeScriptString(string);
1171 		len = resStrLen(string);
1172 		data = defineArray(array, kStringArray, 0, 0, 0, len);
1173 		memcpy(data, string, len);
1174 		break;
1175 	case 208:		// SO_ASSIGN_INT_LIST
1176 		b = pop();
1177 		c = pop();
1178 		id = readVar(array);
1179 		if (id == 0) {
1180 			defineArray(array, kDwordArray, 0, 0, 0, b + c - 1);
1181 		}
1182 		while (c--) {
1183 			writeArray(array, 0, b + c, pop());
1184 		}
1185 		break;
1186 	case 212:		// SO_ASSIGN_2DIM_LIST
1187 		len = getStackList(list, ARRAYSIZE(list));
1188 		id = readVar(array);
1189 		if (id == 0)
1190 			error("Must DIM a two dimensional array before assigning");
1191 		c = pop();
1192 		while (--len >= 0) {
1193 			writeArray(array, c, len, list[len]);
1194 		}
1195 		break;
1196 	default:
1197 		error("o72_arrayOps: default case %d (array %d)", subOp, array);
1198 	}
1199 }
1200 
o72_systemOps()1201 void ScummEngine_v72he::o72_systemOps() {
1202 	byte string[1024];
1203 
1204 	byte subOp = fetchScriptByte();
1205 
1206 	switch (subOp) {
1207 	case 22: // HE80+
1208 		clearDrawObjectQueue();
1209 		break;
1210 	case 26: // HE80+
1211 		restoreBackgroundHE(Common::Rect(_screenWidth, _screenHeight));
1212 		updatePalette();
1213 		break;
1214 	case 158:
1215 		restart();
1216 		break;
1217 	case 160:
1218 		// Confirm shutdown
1219 		confirmExitDialog();
1220 		break;
1221 	case 244:
1222 		quitGame();
1223 		break;
1224 	case 251:
1225 		copyScriptString(string, sizeof(string));
1226 		debug(0, "Start executable (%s)", string);
1227 		break;
1228 	case 252:
1229 		copyScriptString(string, sizeof(string));
1230 		debug(0, "Start game (%s)", string);
1231 		break;
1232 	default:
1233 		error("o72_systemOps invalid case %d", subOp);
1234 	}
1235 }
1236 
o72_talkActor()1237 void ScummEngine_v72he::o72_talkActor() {
1238 	Actor *a;
1239 
1240 	int act = pop();
1241 
1242 	_string[0].loadDefault();
1243 
1244 	// A value of 225 can occur when examining the gold in the mine of pajama, after mining the gold.
1245 	// This is a script bug, the script should set the subtitle color, not actor number.
1246 	// This script bug was fixed in the updated version of pajama.
1247 	if (act == 225) {
1248 		_string[0].color = act;
1249 	} else {
1250 		_actorToPrintStrFor = act;
1251 		if (_actorToPrintStrFor != 0xFF) {
1252 			a = derefActor(_actorToPrintStrFor, "o72_talkActor");
1253 			_string[0].color = a->_talkColor;
1254 		}
1255 	}
1256 
1257 	actorTalk(_scriptPointer);
1258 
1259 	_scriptPointer += resStrLen(_scriptPointer) + 1;
1260 }
1261 
o72_talkEgo()1262 void ScummEngine_v72he::o72_talkEgo() {
1263 	push(VAR(VAR_EGO));
1264 	o72_talkActor();
1265 }
1266 
o72_dimArray()1267 void ScummEngine_v72he::o72_dimArray() {
1268 	int data;
1269 
1270 	byte subOp = fetchScriptByte();
1271 
1272 	switch (subOp) {
1273 	case 2:		// SO_BIT_ARRAY
1274 		data = kBitArray;
1275 		break;
1276 	case 3:		// SO_NIBBLE_ARRAY
1277 		data = kNibbleArray;
1278 		break;
1279 	case 4:		// SO_BYTE_ARRAY
1280 		data = kByteArray;
1281 		break;
1282 	case 5:		// SO_INT_ARRAY
1283 		data = kIntArray;
1284 		break;
1285 	case 6:
1286 		data = kDwordArray;
1287 		break;
1288 	case 7:		// SO_STRING_ARRAY
1289 		data = kStringArray;
1290 		break;
1291 	case 204:		// SO_UNDIM_ARRAY
1292 		nukeArray(fetchScriptWord());
1293 		return;
1294 	default:
1295 		error("o72_dimArray: default case %d", subOp);
1296 	}
1297 
1298 	defineArray(fetchScriptWord(), data, 0, 0, 0, pop());
1299 }
1300 
1301 
o72_dim2dimArray()1302 void ScummEngine_v72he::o72_dim2dimArray() {
1303 	int data, dim1end, dim2end;
1304 
1305 	byte subOp = fetchScriptByte();
1306 
1307 	switch (subOp) {
1308 	case 2:		// SO_BIT_ARRAY
1309 		data = kBitArray;
1310 		break;
1311 	case 3:		// SO_NIBBLE_ARRAY
1312 		data = kNibbleArray;
1313 		break;
1314 	case 4:		// SO_BYTE_ARRAY
1315 		data = kByteArray;
1316 		break;
1317 	case 5:		// SO_INT_ARRAY
1318 		data = kIntArray;
1319 		break;
1320 	case 6:
1321 		data = kDwordArray;
1322 		break;
1323 	case 7:		// SO_STRING_ARRAY
1324 		data = kStringArray;
1325 		break;
1326 	default:
1327 		error("o72_dim2dimArray: default case %d", subOp);
1328 	}
1329 
1330 	dim1end = pop();
1331 	dim2end = pop();
1332 	defineArray(fetchScriptWord(), data, 0, dim2end, 0, dim1end);
1333 }
1334 
o72_traceStatus()1335 void ScummEngine_v72he::o72_traceStatus() {
1336 	byte string[80];
1337 
1338 	copyScriptString(string, sizeof(string));
1339 	pop();
1340 }
1341 
o72_kernelGetFunctions()1342 void ScummEngine_v72he::o72_kernelGetFunctions() {
1343 	int args[29];
1344 	byte *data;
1345 	getStackList(args, ARRAYSIZE(args));
1346 
1347 	switch (args[0]) {
1348 	case 1:
1349 		writeVar(0, 0);
1350 		data = defineArray(0, kByteArray, 0, 0, 0, virtScreenSave(0, args[1], args[2], args[3], args[4]));
1351 		virtScreenSave(data, args[1], args[2], args[3], args[4]);
1352 		push(readVar(0));
1353 		break;
1354 	default:
1355 		error("o72_kernelGetFunctions: default case %d", args[0]);
1356 	}
1357 }
1358 
o72_drawWizImage()1359 void ScummEngine_v72he::o72_drawWizImage() {
1360 	WizImage wi;
1361 	wi.flags = pop();
1362 	wi.y1 = pop();
1363 	wi.x1 = pop();
1364 	wi.resNum = pop();
1365 	wi.state = 0;
1366 	_wiz->displayWizImage(&wi);
1367 }
1368 
debugInput(byte * string)1369 void ScummEngine_v72he::debugInput(byte* string) {
1370 	byte *debugInputString;
1371 
1372 	DebugInputDialog dialog(this, (char *)string);
1373 	runDialog(dialog);
1374 	while (!dialog.done) {
1375 		parseEvents();
1376 		dialog.handleKeyDown(_keyPressed);
1377 	}
1378 
1379 	writeVar(0, 0);
1380 	debugInputString = defineArray(0, kStringArray, 0, 0, 0, dialog.buffer.size());
1381 	memcpy(debugInputString, dialog.buffer.c_str(), dialog.buffer.size());
1382 	push(readVar(0));
1383 }
1384 
o72_debugInput()1385 void ScummEngine_v72he::o72_debugInput() {
1386 	byte string[255];
1387 
1388 	copyScriptString(string, sizeof(string));
1389 	debugInput(string);
1390 }
1391 
o72_jumpToScript()1392 void ScummEngine_v72he::o72_jumpToScript() {
1393 	int args[25];
1394 	int script;
1395 	byte flags;
1396 
1397 	getStackList(args, ARRAYSIZE(args));
1398 	script = pop();
1399 	flags = fetchScriptByte();
1400 	stopObjectCode();
1401 	runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
1402 }
1403 
o72_openFile()1404 void ScummEngine_v72he::o72_openFile() {
1405 	int mode, slot, i;
1406 	byte buffer[256];
1407 
1408 	mode = pop();
1409 	copyScriptString(buffer, sizeof(buffer));
1410 	debug(1, "Trying to open file '%s'", (char *)buffer);
1411 
1412 	slot = -1;
1413 	for (i = 1; i < 17; i++) {
1414 		if (_hInFileTable[i] == 0 && _hOutFileTable[i] == 0) {
1415 			slot = i;
1416 			break;
1417 		}
1418 	}
1419 
1420 	if (slot != -1) {
1421 		switch (mode) {
1422 		case 1:   // Read mode
1423 			_hInFileTable[slot] = openFileForReading(buffer);
1424 			break;
1425 		case 2:   // Write mode
1426 			if (!strchr((char *)buffer, '/')) {
1427 				_hOutFileTable[slot] = openSaveFileForWriting(buffer);
1428 			}
1429 			break;
1430 		case 6: // Append mode
1431 			if (!strchr((char *)buffer, '/'))
1432 				_hOutFileTable[slot] = openSaveFileForAppending(buffer);
1433 			break;
1434 		default:
1435 			error("o72_openFile(): wrong open file mode %d", mode);
1436 		}
1437 
1438 		if (_hInFileTable[slot] == 0 && _hOutFileTable[slot] == 0)
1439 			slot = -1;
1440 
1441 	}
1442 	debug(1, "o72_openFile: slot %d, mode %d", slot, mode);
1443 	push(slot);
1444 }
1445 
readFileToArray(int slot,int32 size)1446 int ScummEngine_v72he::readFileToArray(int slot, int32 size) {
1447 	writeVar(0, 0);
1448 	byte *data = defineArray(0, kByteArray, 0, 0, 0, size);
1449 
1450 	if (slot != -1) {
1451 		_hInFileTable[slot]->read(data, size + 1);
1452 	}
1453 
1454 	return readVar(0);
1455 }
1456 
o72_readFile()1457 void ScummEngine_v72he::o72_readFile() {
1458 	int slot, val;
1459 	int32 size;
1460 	byte subOp = fetchScriptByte();
1461 
1462 	switch (subOp) {
1463 	case 4:
1464 		slot = pop();
1465 		assert(_hInFileTable[slot]);
1466 		val = _hInFileTable[slot]->readByte();
1467 		push(val);
1468 		break;
1469 	case 5:
1470 		slot = pop();
1471 		assert(_hInFileTable[slot]);
1472 		val = _hInFileTable[slot]->readUint16LE();
1473 		push(val);
1474 		break;
1475 	case 6:
1476 		slot = pop();
1477 		assert(_hInFileTable[slot]);
1478 		val = _hInFileTable[slot]->readUint32LE();
1479 		push(val);
1480 		break;
1481 	case 8:
1482 		fetchScriptByte();
1483 		size = pop();
1484 		slot = pop();
1485 		assert(_hInFileTable[slot]);
1486 		val = readFileToArray(slot, size);
1487 		push(val);
1488 		break;
1489 	default:
1490 		error("o72_readFile: default case %d", subOp);
1491 	}
1492 }
1493 
writeFileFromArray(int slot,int32 resID)1494 void ScummEngine_v72he::writeFileFromArray(int slot, int32 resID) {
1495 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resID);
1496 	int32 size = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
1497 		(FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
1498 
1499 	if (slot != -1) {
1500 		_hOutFileTable[slot]->write(ah->data, size);
1501 	}
1502 }
1503 
getStringFromArray(int arrayNumber,char * buffer,int maxLength)1504 void ScummEngine_v72he::getStringFromArray(int arrayNumber, char *buffer, int maxLength) {
1505 	// I'm not really sure it belongs here and not some other version
1506 	// this is ARRAY_GetStringFromArray() from ARRAYS.cpp of SPUTM
1507 
1508 	// this function makes a C-string out of <arrayNumber> contents
1509 
1510 	VAR(0) = arrayNumber; // it was 0 in original code, but I've seen ScummVM Moonbase code which uses VAR_U32_ARRAY_UNK
1511 
1512 	int i, ch;
1513 	for (i = 0; i < maxLength; ++i) {
1514 		if (!(ch = readArray(0, 0, i))) {
1515 			break;
1516 		}
1517 
1518 		buffer[i] = ch;
1519 	}
1520 
1521 	buffer[i] = 0;
1522 }
1523 
o72_writeFile()1524 void ScummEngine_v72he::o72_writeFile() {
1525 	int32 resID = pop();
1526 	int slot = pop();
1527 	byte subOp = fetchScriptByte();
1528 
1529 	assert(_hOutFileTable[slot]);
1530 	switch (subOp) {
1531 	case 4:
1532 		_hOutFileTable[slot]->writeByte(resID);
1533 		break;
1534 	case 5:
1535 		_hOutFileTable[slot]->writeUint16LE(resID);
1536 		break;
1537 	case 6:
1538 		_hOutFileTable[slot]->writeUint32LE(resID);
1539 		break;
1540 	case 8:
1541 		fetchScriptByte();
1542 		writeFileFromArray(slot, resID);
1543 		break;
1544 	default:
1545 		error("o72_writeFile: default case %d", subOp);
1546 	}
1547 }
1548 
o72_findAllObjects()1549 void ScummEngine_v72he::o72_findAllObjects() {
1550 	int room = pop();
1551 	int i;
1552 
1553 	if (room != _currentRoom)
1554 		error("o72_findAllObjects: current room is not %d", room);
1555 
1556 	writeVar(0, 0);
1557 	defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects);
1558 	writeArray(0, 0, 0, _numLocalObjects);
1559 
1560 	for (i = 1; i < _numLocalObjects; i++) {
1561 		writeArray(0, 0, i, _objs[i].obj_nr);
1562 	}
1563 
1564 	push(readVar(0));
1565 }
1566 
o72_deleteFile()1567 void ScummEngine_v72he::o72_deleteFile() {
1568 	byte buffer[256];
1569 
1570 	copyScriptString(buffer, sizeof(buffer));
1571 
1572 	debug(1, "o72_deleteFile(%s)", (char *)buffer);
1573 
1574 	deleteSaveFile(buffer);
1575 }
1576 
o72_rename()1577 void ScummEngine_v72he::o72_rename() {
1578 	byte buffer1[100],buffer2[100];
1579 
1580 	copyScriptString(buffer1, sizeof(buffer1));
1581 	copyScriptString(buffer2, sizeof(buffer2));
1582 
1583 	debug(1, "o72_rename(%s to %s)", (char *)buffer2, (char *)buffer1);
1584 
1585 	renameSaveFile(buffer2, buffer1);
1586 }
1587 
o72_getPixel()1588 void ScummEngine_v72he::o72_getPixel() {
1589 	uint16 area;
1590 
1591 	int y = pop();
1592 	int x = pop();
1593 	byte subOp = fetchScriptByte();
1594 
1595 	VirtScreen *vs = findVirtScreen(y);
1596 	if (vs == NULL || x > _screenWidth - 1 || x < 0) {
1597 		push(-1);
1598 		return;
1599 	}
1600 
1601 	switch (subOp) {
1602 	case 9: // HE 100
1603 	case 218:
1604 		if (_game.features & GF_16BIT_COLOR)
1605 			area = READ_UINT16(vs->getBackPixels(x, y - vs->topline));
1606 		else
1607 			area = *vs->getBackPixels(x, y - vs->topline);
1608 		break;
1609 	case 8: // HE 100
1610 	case 219:
1611 		if (_game.features & GF_16BIT_COLOR)
1612 			area = READ_UINT16(vs->getPixels(x, y - vs->topline));
1613 		else
1614 			area = *vs->getPixels(x, y - vs->topline);
1615 		break;
1616 	default:
1617 		error("o72_getPixel: default case %d", subOp);
1618 	}
1619 	push(area);
1620 }
1621 
o72_pickVarRandom()1622 void ScummEngine_v72he::o72_pickVarRandom() {
1623 	int num;
1624 	int args[100];
1625 	int32 dim1end;
1626 
1627 	num = getStackList(args, ARRAYSIZE(args));
1628 	int value = fetchScriptWord();
1629 
1630 	if (readVar(value) == 0) {
1631 		defineArray(value, kDwordArray, 0, 0, 0, num);
1632 		if (num > 0) {
1633 			int16 counter = 0;
1634 			do {
1635 				writeArray(value, 0, counter + 1, args[counter]);
1636 			} while (++counter < num);
1637 		}
1638 
1639 		shuffleArray(value, 1, num);
1640 		writeArray(value, 0, 0, 2);
1641 		push(readArray(value, 0, 1));
1642 		return;
1643 	}
1644 
1645 	num = readArray(value, 0, 0);
1646 
1647 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
1648 	dim1end = FROM_LE_32(ah->dim1end);
1649 
1650 	if (dim1end < num) {
1651 		int32 var_2 = readArray(value, 0, num - 1);
1652 		shuffleArray(value, 1, dim1end);
1653 		if (readArray(value, 0, 1) == var_2) {
1654 			num = 2;
1655 		} else {
1656 			num = 1;
1657 		}
1658 	}
1659 
1660 	writeArray(value, 0, 0, num + 1);
1661 	push(readArray(value, 0, num));
1662 }
1663 
o72_redimArray()1664 void ScummEngine_v72he::o72_redimArray() {
1665 	int newX, newY;
1666 	newY = pop();
1667 	newX = pop();
1668 
1669 	byte subOp = fetchScriptByte();
1670 
1671 	switch (subOp) {
1672 	case 4:
1673 		redimArray(fetchScriptWord(), 0, newX, 0, newY, kByteArray);
1674 		break;
1675 	case 5:
1676 		redimArray(fetchScriptWord(), 0, newX, 0, newY, kIntArray);
1677 		break;
1678 	case 6:
1679 		redimArray(fetchScriptWord(), 0, newX, 0, newY, kDwordArray);
1680 		break;
1681 	default:
1682 		error("o72_redimArray: default type %d", subOp);
1683 	}
1684 }
1685 
redimArray(int arrayId,int newDim2start,int newDim2end,int newDim1start,int newDim1end,int type)1686 void ScummEngine_v72he::redimArray(int arrayId, int newDim2start, int newDim2end,
1687 								   int newDim1start, int newDim1end, int type) {
1688 	int newSize, oldSize;
1689 
1690 	if (readVar(arrayId) == 0)
1691 		error("redimArray: Reference to zeroed array pointer");
1692 
1693 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(arrayId));
1694 
1695 	if (!ah)
1696 		error("redimArray: Invalid array (%d) reference", readVar(arrayId));
1697 
1698 	newSize = arrayDataSizes[type];
1699 	oldSize = arrayDataSizes[FROM_LE_32(ah->type)];
1700 
1701 	newSize *= (newDim1end - newDim1start + 1) * (newDim2end - newDim2start + 1);
1702 	oldSize *= (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
1703 		(FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
1704 
1705 	newSize >>= 3;
1706 	oldSize >>= 3;
1707 
1708 	if (newSize != oldSize)
1709 		error("redimArray: array %d redim mismatch", readVar(arrayId));
1710 
1711 	ah->type = TO_LE_32(type);
1712 	ah->dim1start = TO_LE_32(newDim1start);
1713 	ah->dim1end = TO_LE_32(newDim1end);
1714 	ah->dim2start = TO_LE_32(newDim2start);
1715 	ah->dim2end = TO_LE_32(newDim2end);
1716 }
1717 
checkArrayLimits(int array,int dim2start,int dim2end,int dim1start,int dim1end)1718 void ScummEngine_v72he::checkArrayLimits(int array, int dim2start, int dim2end, int dim1start, int dim1end) {
1719 	if (dim1end < dim1start) {
1720 		error("Across max %d smaller than min %d", dim1end, dim1start);
1721 	}
1722 	if (dim2end < dim2start) {
1723 		error("Down max %d smaller than min %d", dim2end, dim2start);
1724 	}
1725 	ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
1726 	assert(ah);
1727 	if ((int)FROM_LE_32(ah->dim2start) > dim2start || (int)FROM_LE_32(ah->dim2end) < dim2end || (int)FROM_LE_32(ah->dim1start) > dim1start || (int)FROM_LE_32(ah->dim1end) < dim1end) {
1728 		error("Invalid array access (%d,%d,%d,%d) limit (%d,%d,%d,%d)", dim2start, dim2end, dim1start, dim1end, FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end), FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end));
1729 	}
1730 }
1731 
copyArray(int array1,int a1_dim2start,int a1_dim2end,int a1_dim1start,int a1_dim1end,int array2,int a2_dim2start,int a2_dim2end,int a2_dim1start,int a2_dim1end)1732 void ScummEngine_v72he::copyArray(int array1, int a1_dim2start, int a1_dim2end, int a1_dim1start, int a1_dim1end,
1733 				int array2, int a2_dim2start, int a2_dim2end, int a2_dim1start, int a2_dim1end)
1734 {
1735 	byte *dst, *src;
1736 	int dstPitch, srcPitch;
1737 	int rowSize;
1738 	checkArrayLimits(array1, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end);
1739 	checkArrayLimits(array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
1740 	int a12_num = a1_dim2end - a1_dim2start + 1;
1741 	int a11_num = a1_dim1end - a1_dim1start + 1;
1742 	int a22_num = a2_dim2end - a2_dim2start + 1;
1743 	int a21_num = a2_dim1end - a2_dim1start + 1;
1744 	if (a22_num != a12_num || a21_num != a11_num) {
1745 		error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num);
1746 	}
1747 
1748 	if (array1 != array2) {
1749 		ArrayHeader *ah1 = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
1750 		assert(ah1);
1751 		ArrayHeader *ah2 = (ArrayHeader *)getResourceAddress(rtString, readVar(array2));
1752 		assert(ah2);
1753 		if (FROM_LE_32(ah1->type) == FROM_LE_32(ah2->type)) {
1754 			copyArrayHelper(ah1, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
1755 			copyArrayHelper(ah2, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
1756 			for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
1757 				memcpy(dst, src, rowSize);
1758 				dst += dstPitch;
1759 				src += srcPitch;
1760 			}
1761 		} else {
1762 			for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start) {
1763 				int a2dim1 = a2_dim1start;
1764 				int a1dim1 = a1_dim1start;
1765 				for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1) {
1766 					int val = readArray(array2, a2_dim2start, a2dim1);
1767 					writeArray(array1, a1_dim2start, a1dim1, val);
1768 				}
1769 			}
1770 		}
1771 	} else {
1772 		if (a2_dim2start != a1_dim2start || a2_dim1start != a1_dim1start) {
1773 			ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
1774 			assert(ah);
1775 			if (a2_dim2start > a1_dim2start) {
1776 				copyArrayHelper(ah, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
1777 				copyArrayHelper(ah, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
1778 			} else {
1779 				// start at the end, so we copy backwards (in case the indices overlap)
1780 				copyArrayHelper(ah, a1_dim2end, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
1781 				copyArrayHelper(ah, a2_dim2end, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
1782 				dstPitch = -dstPitch;
1783 				srcPitch = -srcPitch;
1784 			}
1785 			for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
1786 				memcpy(dst, src, rowSize);
1787 				dst += dstPitch;
1788 				src += srcPitch;
1789 			}
1790 		}
1791 	}
1792 }
1793 
copyArrayHelper(ArrayHeader * ah,int idx2,int idx1,int len1,byte ** data,int * size,int * num)1794 void ScummEngine_v72he::copyArrayHelper(ArrayHeader *ah, int idx2, int idx1, int len1, byte **data, int *size, int *num) {
1795 	const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
1796 	const int offset = pitch * (idx2 - FROM_LE_32(ah->dim2start)) + idx1 - FROM_LE_32(ah->dim1start);
1797 
1798 	switch (FROM_LE_32(ah->type)) {
1799 	case kByteArray:
1800 	case kStringArray:
1801 		*num = len1 - idx1 + 1;
1802 		*size = pitch;
1803 		*data = ah->data + offset;
1804 		break;
1805 	case kIntArray:
1806 		*num = (len1 - idx1) * 2 + 2;
1807 		*size = pitch * 2;
1808 		*data = ah->data + offset * 2;
1809 		break;
1810 	case kDwordArray:
1811 		*num = (len1 - idx1) * 4 + 4;
1812 		*size = pitch * 4;
1813 		*data = ah->data + offset * 4;
1814 		break;
1815 	default:
1816 		error("Invalid array type %d", FROM_LE_32(ah->type));
1817 	}
1818 }
1819 
o72_readINI()1820 void ScummEngine_v72he::o72_readINI() {
1821 	byte option[128];
1822 	byte *data;
1823 
1824 	copyScriptString(option, sizeof(option));
1825 	byte subOp = fetchScriptByte();
1826 
1827 	switch (subOp) {
1828 	case 43: // HE 100
1829 	case 6: // number
1830 		if (!strcmp((char *)option, "DisablePrinting") || !strcmp((char *)option, "NoPrinting")) {
1831 			push(1);
1832 		} else if (!strcmp((char *)option, "TextOn")) {
1833 			push(ConfMan.getBool("subtitles"));
1834 		} else if (!strcmp((char *)option, "Disk") && (_game.id == GID_BIRTHDAYRED || _game.id == GID_BIRTHDAYYELLOW)) {
1835 			// WORKAROUND: Override the disk detection
1836 			// This removes the reliance on having the binary file around (which is
1837 			// very bad for the Mac version) just for the scripts to figure out if
1838 			// we're running Yellow or Red
1839 			if (_game.id == GID_BIRTHDAYRED)
1840 				push(4);
1841 			else
1842 				push(2);
1843 		} else {
1844 			push(ConfMan.getInt((char *)option));
1845 		}
1846 		break;
1847 	case 77: // HE 100
1848 	case 7: // string
1849 		writeVar(0, 0);
1850 		if (!strcmp((char *)option, "HE3File")) {
1851 			Common::String fileName = generateFilename(-3);
1852 			int len = resStrLen((const byte *)fileName.c_str());
1853 			data = defineArray(0, kStringArray, 0, 0, 0, len);
1854 			memcpy(data, fileName.c_str(), len);
1855 		} else if (!strcmp((char *)option, "GameResourcePath") || !strcmp((char *)option, "SaveGamePath")) {
1856 			// We set SaveGamePath in order to detect where it used
1857 			// in convertFilePath and to avoid warning about invalid
1858 			// path in Macintosh verisons.
1859 			data = defineArray(0, kStringArray, 0, 0, 0, 2);
1860 			if (_game.platform == Common::kPlatformMacintosh)
1861 				memcpy(data, (const char *)"*:", 2);
1862 			else
1863 				memcpy(data, (const char *)"*\\", 2);
1864 		} else {
1865 			const char *entry = (ConfMan.get((char *)option).c_str());
1866 			int len = resStrLen((const byte *)entry);
1867 			data = defineArray(0, kStringArray, 0, 0, 0, len);
1868 			memcpy(data, entry, len);
1869 		}
1870 		push(readVar(0));
1871 		break;
1872 	default:
1873 		error("o72_readINI: default type %d", subOp);
1874 	}
1875 
1876 	debug(1, "o72_readINI: Option %s", option);
1877 }
1878 
o72_writeINI()1879 void ScummEngine_v72he::o72_writeINI() {
1880 	int value;
1881 	byte option[256], string[1024];
1882 
1883 	byte subOp = fetchScriptByte();
1884 
1885 	switch (subOp) {
1886 	case 43: // HE 100
1887 	case 6: // number
1888 		value = pop();
1889 		copyScriptString(option, sizeof(option));
1890 		debug(1, "o72_writeINI: Option %s Value %d", option, value);
1891 
1892 		ConfMan.setInt((char *)option, value);
1893 		break;
1894 	case 77: // HE 100
1895 	case 7: // string
1896 		copyScriptString(string, sizeof(string));
1897 		copyScriptString(option, sizeof(option));
1898 		debug(1, "o72_writeINI: Option %s String %s", option, string);
1899 
1900 		// Filter out useless setting
1901 		if (!strcmp((char *)option, "HETest"))
1902 			return;
1903 
1904 		// Filter out confusing subtitle setting
1905 		if (!strcmp((char *)option, "TextOn"))
1906 			return;
1907 
1908 		// Filter out confusing path settings
1909 		if (!strcmp((char *)option, "DownLoadPath") || !strcmp((char *)option, "GameResourcePath") || !strcmp((char *)option, "SaveGamePath"))
1910 			return;
1911 
1912 		ConfMan.set((char *)option, (char *)string);
1913 		break;
1914 	default:
1915 		error("o72_writeINI: default type %d", subOp);
1916 	}
1917 
1918 	ConfMan.flushToDisk();
1919 }
1920 
o72_getResourceSize()1921 void ScummEngine_v72he::o72_getResourceSize() {
1922 	const byte *ptr;
1923 	int size;
1924 	ResType type;
1925 
1926 	int resid = pop();
1927 	if (_game.heversion == 72) {
1928 		push(getSoundResourceSize(resid));
1929 		return;
1930 	}
1931 
1932 	byte subOp = fetchScriptByte();
1933 
1934 	switch (subOp) {
1935 	case 13:
1936 		push(getSoundResourceSize(resid));
1937 		return;
1938 	case 14:
1939 		type = rtRoomImage;
1940 		break;
1941 	case 15:
1942 		type = rtImage;
1943 		break;
1944 	case 16:
1945 		type = rtCostume;
1946 		break;
1947 	case 17:
1948 		type = rtScript;
1949 		break;
1950 	default:
1951 		error("o72_getResourceSize: default type %d", subOp);
1952 	}
1953 
1954 	ptr = getResourceAddress(type, resid);
1955 	assert(ptr);
1956 	size = READ_BE_UINT32(ptr + 4) - 8;
1957 	push(size);
1958 }
1959 
o72_createDirectory()1960 void ScummEngine_v72he::o72_createDirectory() {
1961 	byte directoryName[255];
1962 
1963 	copyScriptString(directoryName, sizeof(directoryName));
1964 	debug(1,"o72_createDirectory: %s", directoryName);
1965 }
1966 
o72_setSystemMessage()1967 void ScummEngine_v72he::o72_setSystemMessage() {
1968 	byte name[1024];
1969 
1970 	copyScriptString(name, sizeof(name));
1971 	byte subOp = fetchScriptByte();
1972 
1973 	switch (subOp) {
1974 	case 240:
1975 		debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
1976 		break;
1977 	case 241: // Set Version
1978 		debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
1979 		break;
1980 	case 242:
1981 		debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
1982 		break;
1983 	case 243: // Set Window Caption
1984 		// TODO: The 'name' string can contain non-ASCII data. This can lead to
1985 		// problems, because (a) the encoding used for "name" is not clear,
1986 		// (b) OSystem::setWindowCaption only supports ASCII. As a result, odd
1987 		// behavior can occur, from strange wrong titles, up to crashes (happens
1988 		// under Mac OS X).
1989 		//
1990 		// Possible fixes/workarounds:
1991 		// - Simply stop using this. It's a rather unimportant "feature" anyway.
1992 		// - Try to translate the text to ASCII.
1993 		// - Refine OSystem to accept window captions that are non-ASCII, e.g.
1994 		//   by enhancing all backends to deal with UTF-8 data. Of course, then
1995 		//   one still would have to convert 'name' to the correct encoding.
1996 		//_system->setWindowCaption((const char *)name);
1997 		break;
1998 	default:
1999 		error("o72_setSystemMessage: default case %d", subOp);
2000 	}
2001 }
2002 
decodeParseString(int m,int n)2003 void ScummEngine_v72he::decodeParseString(int m, int n) {
2004 	Actor *a;
2005 	int i, colors, size;
2006 	int args[31];
2007 	byte name[1024];
2008 
2009 	byte b = fetchScriptByte();
2010 
2011 	switch (b) {
2012 	case 65:		// SO_AT
2013 		_string[m].ypos = pop();
2014 		_string[m].xpos = pop();
2015 		_string[m].overhead = false;
2016 		break;
2017 	case 66:		// SO_COLOR
2018 		_string[m].color = pop();
2019 		break;
2020 	case 67:		// SO_CLIPPED
2021 		_string[m].right = pop();
2022 		break;
2023 	case 69:		// SO_CENTER
2024 		_string[m].center = true;
2025 		_string[m].overhead = false;
2026 		break;
2027 	case 71:		// SO_LEFT
2028 		_string[m].center = false;
2029 		_string[m].overhead = false;
2030 		break;
2031 	case 72:		// SO_OVERHEAD
2032 		_string[m].overhead = true;
2033 		_string[m].no_talk_anim = false;
2034 		break;
2035 	case 74:		// SO_MUMBLE
2036 		_string[m].no_talk_anim = true;
2037 		break;
2038 	case 75:		// SO_TEXTSTRING
2039 		printString(m, _scriptPointer);
2040 		_scriptPointer += resStrLen(_scriptPointer) + 1;
2041 		break;
2042 	case 194:
2043 		decodeScriptString(name, true);
2044 		printString(m, name);
2045 		break;
2046 	case 0xE1:
2047 		{
2048 		byte *dataPtr = getResourceAddress(rtTalkie, pop());
2049 		byte *text = findWrappedBlock(MKTAG('T','E','X','T'), dataPtr, 0, 0);
2050 		size = getResourceDataSize(text);
2051 		memcpy(name, text, size);
2052 		printString(m, name);
2053 		}
2054 		break;
2055 	case 0xF9:
2056 		colors = pop();
2057 		if (colors == 1) {
2058 			_string[m].color = pop();
2059 		} else {
2060 			push(colors);
2061 			getStackList(args, ARRAYSIZE(args));
2062 			for (i = 0; i < 16; i++)
2063 				_charsetColorMap[i] = _charsetData[_string[m]._default.charset][i] = (unsigned char)args[i];
2064 			_string[m].color = _charsetColorMap[0];
2065 		}
2066 		break;
2067 	case 0xFE:
2068 		_string[m].loadDefault();
2069 		if (n) {
2070 			_actorToPrintStrFor = pop();
2071 			if (_actorToPrintStrFor != 0xFF) {
2072 				a = derefActor(_actorToPrintStrFor, "decodeParseString");
2073 				_string[m].color = a->_talkColor;
2074 			}
2075 		}
2076 		break;
2077 	case 0xFF:
2078 		_string[m].saveDefault();
2079 		break;
2080 	default:
2081 		error("decodeParseString: default case 0x%x", b);
2082 	}
2083 }
2084 
2085 } // End of namespace Scumm
2086 
2087 #endif // ENABLE_HE
2088