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/system.h"
24 #include "common/translation.h"
25 
26 #include "engines/util.h"
27 #include "graphics/cursorman.h"
28 #include "graphics/surface.h"
29 
30 #include "gui/message.h"
31 
32 #include "sci/sci.h"
33 #include "sci/event.h"
34 #include "sci/resource.h"
35 #include "sci/engine/features.h"
36 #include "sci/engine/guest_additions.h"
37 #include "sci/engine/savegame.h"
38 #include "sci/engine/state.h"
39 #include "sci/engine/selector.h"
40 #include "sci/engine/kernel.h"
41 #include "sci/graphics/animate.h"
42 #include "sci/graphics/cache.h"
43 #include "sci/graphics/compare.h"
44 #include "sci/graphics/controls16.h"
45 #include "sci/graphics/cursor.h"
46 #include "sci/graphics/palette.h"
47 #include "sci/graphics/paint16.h"
48 #include "sci/graphics/picture.h"
49 #include "sci/graphics/ports.h"
50 #include "sci/graphics/remap.h"
51 #include "sci/graphics/screen.h"
52 #include "sci/graphics/text16.h"
53 #include "sci/graphics/view.h"
54 #ifdef ENABLE_SCI32
55 #include "sci/graphics/text32.h"
56 #endif
57 
58 namespace Sci {
59 
adjustGraphColor(int16 color)60 static int16 adjustGraphColor(int16 color) {
61 	// WORKAROUND: EGA and Amiga games can set invalid colors (above 0 - 15).
62 	// It seems only the lower nibble was used in these games.
63 	// bug #3048908, #3486899.
64 	// Confirmed in EGA games KQ4(late), QFG1(ega), LB1 that
65 	// at least FillBox (only one of the functions using adjustGraphColor)
66 	// behaves like this.
67 	if (g_sci->getResMan()->getViewType() == kViewEga)
68 		return color & 0x0F;	// 0 - 15
69 	else
70 		return color;
71 }
72 
showScummVMDialog(const Common::String & message)73 void showScummVMDialog(const Common::String &message) {
74 	GUI::MessageDialog dialog(message, _("OK"));
75 	dialog.runModal();
76 }
77 
kDirLoopWorker(reg_t object,uint16 angle,EngineState * s,int argc,reg_t * argv)78 void kDirLoopWorker(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) {
79 	GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
80 	uint16 signal = readSelectorValue(s->_segMan, object, SELECTOR(signal));
81 
82 	if (signal & kSignalDoesntTurn)
83 		return;
84 
85 	int16 useLoop = -1;
86 	if (getSciVersion() > SCI_VERSION_0_EARLY) {
87 		if ((angle > 315) || (angle < 45)) {
88 			useLoop = 3;
89 		} else if ((angle > 135) && (angle < 225)) {
90 			useLoop = 2;
91 		}
92 	} else {
93 		// SCI0EARLY
94 		if ((angle > 330) || (angle < 30)) {
95 			useLoop = 3;
96 		} else if ((angle > 150) && (angle < 210)) {
97 			useLoop = 2;
98 		}
99 	}
100 	if (useLoop == -1) {
101 		if (angle >= 180) {
102 			useLoop = 1;
103 		} else {
104 			useLoop = 0;
105 		}
106 	} else {
107 		int16 loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId);
108 		if (loopCount < 4)
109 			return;
110 	}
111 
112 	writeSelectorValue(s->_segMan, object, SELECTOR(loop), useLoop);
113 }
114 
kSetCursorSci0(EngineState * s,int argc,reg_t * argv)115 static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) {
116 	Common::Point pos;
117 	GuiResourceId cursorId = argv[0].toSint16();
118 
119 	// Set pointer position, if requested
120 	if (argc >= 4) {
121 		pos.y = argv[3].toSint16();
122 		pos.x = argv[2].toSint16();
123 		g_sci->_gfxCursor->kernelSetPos(pos);
124 	}
125 
126 	if ((argc >= 2) && (argv[1].toSint16() == 0)) {
127 		cursorId = -1;
128 	}
129 
130 	g_sci->_gfxCursor->kernelSetShape(cursorId);
131 	return s->r_acc;
132 }
133 
kSetCursorSci11(EngineState * s,int argc,reg_t * argv)134 static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
135 	Common::Point pos;
136 	Common::Point *hotspot = NULL;
137 
138 	switch (argc) {
139 	case 1:
140 		switch (argv[0].toSint16()) {
141 		case 0:
142 			g_sci->_gfxCursor->kernelHide();
143 			break;
144 		case -1:
145 			g_sci->_gfxCursor->kernelClearZoomZone();
146 			break;
147 		case -2:
148 			g_sci->_gfxCursor->kernelResetMoveZone();
149 			break;
150 		default:
151 			g_sci->_gfxCursor->kernelShow();
152 			break;
153 		}
154 		break;
155 	case 2:
156 		pos.y = argv[1].toSint16();
157 		pos.x = argv[0].toSint16();
158 
159 		g_sci->_gfxCursor->kernelSetPos(pos);
160 		break;
161 	case 4: {
162 		int16 top, left, bottom, right;
163 
164 		if (getSciVersion() >= SCI_VERSION_2) {
165 			top = argv[1].toSint16();
166 			left = argv[0].toSint16();
167 			bottom = argv[3].toSint16();
168 			right = argv[2].toSint16();
169 		} else {
170 			top = argv[0].toSint16();
171 			left = argv[1].toSint16();
172 			bottom = argv[2].toSint16();
173 			right = argv[3].toSint16();
174 		}
175 		// bottom/right needs to be included into our movezone, because we compare it like any regular Common::Rect
176 		bottom++;
177 		right++;
178 
179 		if ((right >= left) && (bottom >= top)) {
180 			Common::Rect rect = Common::Rect(left, top, right, bottom);
181 			g_sci->_gfxCursor->kernelSetMoveZone(rect);
182 		} else {
183 			warning("kSetCursor: Ignoring invalid mouse zone (%i, %i)-(%i, %i)", left, top, right, bottom);
184 		}
185 		break;
186 	}
187 	case 9: // case for kq5cd, we are getting calling with 4 additional 900d parameters
188 	case 5:
189 		hotspot = new Common::Point(argv[3].toSint16(), argv[4].toSint16());
190 		// Fallthrough
191 	case 3:
192 		if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
193 			delete hotspot; // Mac cursors have their own hotspot, so ignore any we get here
194 			g_sci->_gfxCursor->kernelSetMacCursor(argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16());
195 		} else {
196 			g_sci->_gfxCursor->kernelSetView(argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), hotspot);
197 		}
198 		break;
199 	case 10:
200 		// Freddy pharkas, when using the whiskey glass to read the prescription (bug #3034973)
201 		g_sci->_gfxCursor->kernelSetZoomZone(argv[0].toUint16(),
202 			Common::Rect(argv[1].toUint16(), argv[2].toUint16(), argv[3].toUint16(), argv[4].toUint16()),
203 			argv[5].toUint16(), argv[6].toUint16(), argv[7].toUint16(),
204 			argv[8].toUint16(), argv[9].toUint16());
205 		break;
206 	default :
207 		error("kSetCursor: Unhandled case: %d arguments given", argc);
208 		break;
209 	}
210 	return s->r_acc;
211 }
212 
kSetCursor(EngineState * s,int argc,reg_t * argv)213 reg_t kSetCursor(EngineState *s, int argc, reg_t *argv) {
214 	switch (g_sci->_features->detectSetCursorType()) {
215 	case SCI_VERSION_0_EARLY:
216 		return kSetCursorSci0(s, argc, argv);
217 	case SCI_VERSION_1_1:
218 		return kSetCursorSci11(s, argc, argv);
219 	default:
220 		error("Unknown SetCursor type");
221 		return NULL_REG;
222 	}
223 }
224 
kMoveCursor(EngineState * s,int argc,reg_t * argv)225 reg_t kMoveCursor(EngineState *s, int argc, reg_t *argv) {
226 	g_sci->_gfxCursor->kernelSetPos(Common::Point(argv[0].toSint16(), argv[1].toSint16()));
227 	return s->r_acc;
228 }
229 
kPicNotValid(EngineState * s,int argc,reg_t * argv)230 reg_t kPicNotValid(EngineState *s, int argc, reg_t *argv) {
231 	int16 newPicNotValid = (argc > 0) ? argv[0].toUint16() : -1;
232 
233 	return make_reg(0, g_sci->_gfxScreen->kernelPicNotValid(newPicNotValid));
234 }
235 
getGraphRect(reg_t * argv)236 static Common::Rect getGraphRect(reg_t *argv) {
237 	int16 x = argv[1].toSint16();
238 	int16 y = argv[0].toSint16();
239 	int16 x1 = argv[3].toSint16();
240 	int16 y1 = argv[2].toSint16();
241 	if (x > x1) SWAP(x, x1);
242 	if (y > y1) SWAP(y, y1);
243 	return Common::Rect(x, y, x1, y1);
244 }
245 
getGraphPoint(reg_t * argv)246 static Common::Point getGraphPoint(reg_t *argv) {
247 	int16 x = argv[1].toSint16();
248 	int16 y = argv[0].toSint16();
249 	return Common::Point(x, y);
250 }
251 
kGraph(EngineState * s,int argc,reg_t * argv)252 reg_t kGraph(EngineState *s, int argc, reg_t *argv) {
253 	if (!s)
254 		return make_reg(0, getSciVersion());
255 	error("not supposed to call this");
256 }
257 
kGraphGetColorCount(EngineState * s,int argc,reg_t * argv)258 reg_t kGraphGetColorCount(EngineState *s, int argc, reg_t *argv) {
259 	return make_reg(0, g_sci->_gfxPalette16->getTotalColorCount());
260 }
261 
kGraphDrawLine(EngineState * s,int argc,reg_t * argv)262 reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
263 	int16 color = adjustGraphColor(argv[4].toSint16());
264 	int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
265 	int16 control = (argc > 6) ? argv[6].toSint16() : -1;
266 
267 	g_sci->_gfxPaint16->kernelGraphDrawLine(getGraphPoint(argv), getGraphPoint(argv + 2), color, priority, control);
268 	return s->r_acc;
269 }
270 
kGraphSaveBox(EngineState * s,int argc,reg_t * argv)271 reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv) {
272 	Common::Rect rect = getGraphRect(argv);
273 	uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL;
274 	return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask);
275 }
276 
kGraphRestoreBox(EngineState * s,int argc,reg_t * argv)277 reg_t kGraphRestoreBox(EngineState *s, int argc, reg_t *argv) {
278 	// This may be called with a memoryhandle from SAVE_BOX or SAVE_UPSCALEDHIRES_BOX
279 	g_sci->_gfxPaint16->kernelGraphRestoreBox(argv[0]);
280 	return s->r_acc;
281 }
282 
kGraphFillBoxBackground(EngineState * s,int argc,reg_t * argv)283 reg_t kGraphFillBoxBackground(EngineState *s, int argc, reg_t *argv) {
284 	Common::Rect rect = getGraphRect(argv);
285 	g_sci->_gfxPaint16->kernelGraphFillBoxBackground(rect);
286 	return s->r_acc;
287 }
288 
kGraphFillBoxForeground(EngineState * s,int argc,reg_t * argv)289 reg_t kGraphFillBoxForeground(EngineState *s, int argc, reg_t *argv) {
290 	Common::Rect rect = getGraphRect(argv);
291 	g_sci->_gfxPaint16->kernelGraphFillBoxForeground(rect);
292 	return s->r_acc;
293 }
294 
kGraphFillBoxAny(EngineState * s,int argc,reg_t * argv)295 reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv) {
296 	Common::Rect rect = getGraphRect(argv);
297 	int16 colorMask = argv[4].toUint16();
298 	int16 color = adjustGraphColor(argv[5].toSint16());
299 	int16 priority = argv[6].toSint16(); // yes, we may read from stack sometimes here
300 	int16 control = argv[7].toSint16(); // sierra did the same
301 
302 	g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control);
303 	return s->r_acc;
304 }
305 
kGraphUpdateBox(EngineState * s,int argc,reg_t * argv)306 reg_t kGraphUpdateBox(EngineState *s, int argc, reg_t *argv) {
307 	Common::Rect rect = getGraphRect(argv);
308 	// argv[4] is the map (1 for visual, etc.)
309 	// argc == 6 on upscaled hires
310 	bool hiresMode = (argc > 5) ? true : false;
311 	g_sci->_gfxPaint16->kernelGraphUpdateBox(rect, hiresMode);
312 	return s->r_acc;
313 }
314 
kGraphRedrawBox(EngineState * s,int argc,reg_t * argv)315 reg_t kGraphRedrawBox(EngineState *s, int argc, reg_t *argv) {
316 	Common::Rect rect = getGraphRect(argv);
317 	g_sci->_gfxPaint16->kernelGraphRedrawBox(rect);
318 	return s->r_acc;
319 }
320 
321 // Seems to be only implemented for SCI0/SCI01 games
kGraphAdjustPriority(EngineState * s,int argc,reg_t * argv)322 reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv) {
323 	g_sci->_gfxPorts->kernelGraphAdjustPriority(argv[0].toUint16(), argv[1].toUint16());
324 	return s->r_acc;
325 }
326 
kGraphSaveUpscaledHiresBox(EngineState * s,int argc,reg_t * argv)327 reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv) {
328 	Common::Rect rect = getGraphRect(argv);
329 	return g_sci->_gfxPaint16->kernelGraphSaveUpscaledHiresBox(rect);
330 }
331 
kTextSize(EngineState * s,int argc,reg_t * argv)332 reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
333 	int16 textWidth, textHeight;
334 	Common::String text = s->_segMan->getString(argv[1]);
335 	reg_t *dest = s->_segMan->derefRegPtr(argv[0], 4);
336 	int maxwidth = (argc > 3) ? argv[3].toUint16() : 0;
337 	int font_nr = argv[2].toUint16();
338 
339 	if (!dest) {
340 		debugC(kDebugLevelStrings, "GetTextSize: Empty destination");
341 		return s->r_acc;
342 	}
343 
344 	Common::String sep_str;
345 	const char *sep = NULL;
346 	if ((argc > 4) && (argv[4].getSegment())) {
347 		sep_str = s->_segMan->getString(argv[4]);
348 		sep = sep_str.c_str();
349 	}
350 
351 	dest[0] = dest[1] = NULL_REG;
352 
353 	if (text.empty()) { // Empty text
354 		dest[2] = dest[3] = make_reg(0, 0);
355 		debugC(kDebugLevelStrings, "GetTextSize: Empty string");
356 		return s->r_acc;
357 	}
358 
359 	textWidth = dest[3].toUint16(); textHeight = dest[2].toUint16();
360 
361 	uint16 languageSplitter = 0;
362 	Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, sep);
363 
364 	g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight);
365 
366 	// One of the game texts in LB2 German contains loads of spaces in
367 	// its end. We trim the text here, otherwise the graphics code will
368 	// attempt to draw a very large window (larger than the screen) to
369 	// show the text, and crash.
370 	// Fixes bug #3306417.
371 	if (textWidth >= g_sci->_gfxScreen->getDisplayWidth() ||
372 		textHeight >= g_sci->_gfxScreen->getDisplayHeight()) {
373 		// TODO: Is this needed for SCI32 as well?
374 		if (g_sci->_gfxText16) {
375 			warning("kTextSize: string would be too big to fit on screen. Trimming it");
376 			text.trim();
377 			// Copy over the trimmed string...
378 			s->_segMan->strcpy(argv[1], text.c_str());
379 			// ...and recalculate bounding box dimensions
380 			g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight);
381 		}
382 	}
383 
384 	debugC(kDebugLevelStrings, "GetTextSize '%s' -> %dx%d", text.c_str(), textWidth, textHeight);
385 	if (getSciVersion() <= SCI_VERSION_1_1) {
386 		dest[2] = make_reg(0, textHeight);
387 		dest[3] = make_reg(0, textWidth);
388 	} else {
389 		dest[2] = make_reg(0, textWidth);
390 		dest[3] = make_reg(0, textHeight);
391 	}
392 
393 	return s->r_acc;
394 }
395 
kWait(EngineState * s,int argc,reg_t * argv)396 reg_t kWait(EngineState *s, int argc, reg_t *argv) {
397 	uint16 ticks = argv[0].toUint16();
398 
399 	const uint16 delta = s->wait(ticks);
400 
401 	if (g_sci->_guestAdditions->kWaitHook()) {
402 		return NULL_REG;
403 	}
404 
405 	return make_reg(0, delta);
406 }
407 
kCoordPri(EngineState * s,int argc,reg_t * argv)408 reg_t kCoordPri(EngineState *s, int argc, reg_t *argv) {
409 	int16 y = argv[0].toSint16();
410 
411 	if ((argc < 2) || (y != 1)) {
412 		return make_reg(0, g_sci->_gfxPorts->kernelCoordinateToPriority(y));
413 	} else {
414 		int16 priority = argv[1].toSint16();
415 		return make_reg(0, g_sci->_gfxPorts->kernelPriorityToCoordinate(priority));
416 	}
417 }
418 
kPriCoord(EngineState * s,int argc,reg_t * argv)419 reg_t kPriCoord(EngineState *s, int argc, reg_t *argv) {
420 	int16 priority = argv[0].toSint16();
421 
422 	return make_reg(0, g_sci->_gfxPorts->kernelPriorityToCoordinate(priority));
423 }
424 
kDirLoop(EngineState * s,int argc,reg_t * argv)425 reg_t kDirLoop(EngineState *s, int argc, reg_t *argv) {
426 	kDirLoopWorker(argv[0], argv[1].toUint16(), s, argc, argv);
427 
428 	return s->r_acc;
429 }
430 
kCanBeHere(EngineState * s,int argc,reg_t * argv)431 reg_t kCanBeHere(EngineState *s, int argc, reg_t *argv) {
432 	reg_t curObject = argv[0];
433 	reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
434 
435 	reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
436 	return make_reg(0, canBeHere.isNull() ? 1 : 0);
437 }
438 
kCantBeHere(EngineState * s,int argc,reg_t * argv)439 reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) {
440 	reg_t curObject = argv[0];
441 	reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
442 
443 #ifdef ENABLE_SCI32
444 	if (getSciVersion() >= SCI_VERSION_2) {
445 		return g_sci->_gfxCompare->kernelCantBeHere32(curObject, listReference);
446 	} else {
447 #endif
448 		return g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
449 #ifdef ENABLE_SCI32
450 	}
451 #endif
452 }
453 
kIsItSkip(EngineState * s,int argc,reg_t * argv)454 reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) {
455 	GuiResourceId viewId = argv[0].toSint16();
456 	int16 loopNo = argv[1].toSint16();
457 	int16 celNo = argv[2].toSint16();
458 	Common::Point position(argv[4].toUint16(), argv[3].toUint16());
459 
460 	bool result = g_sci->_gfxCompare->kernelIsItSkip(viewId, loopNo, celNo, position);
461 	return make_reg(0, result);
462 }
463 
kCelHigh(EngineState * s,int argc,reg_t * argv)464 reg_t kCelHigh(EngineState *s, int argc, reg_t *argv) {
465 	GuiResourceId viewId = argv[0].toSint16();
466 	if (viewId == -1)	// Happens in SCI32
467 		return NULL_REG;
468 	int16 loopNo = argv[1].toSint16();
469 	int16 celNo = (argc >= 3) ? argv[2].toSint16() : 0;
470 	int16 celHeight;
471 
472 	celHeight = g_sci->_gfxCache->kernelViewGetCelHeight(viewId, loopNo, celNo);
473 
474 	return make_reg(0, celHeight);
475 }
476 
kCelWide(EngineState * s,int argc,reg_t * argv)477 reg_t kCelWide(EngineState *s, int argc, reg_t *argv) {
478 	GuiResourceId viewId = argv[0].toSint16();
479 	if (viewId == -1)	// Happens in SCI32
480 		return NULL_REG;
481 	int16 loopNo = argv[1].toSint16();
482 	int16 celNo = (argc >= 3) ? argv[2].toSint16() : 0;
483 	int16 celWidth;
484 
485 	celWidth = g_sci->_gfxCache->kernelViewGetCelWidth(viewId, loopNo, celNo);
486 
487 	return make_reg(0, celWidth);
488 }
489 
kNumLoops(EngineState * s,int argc,reg_t * argv)490 reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) {
491 	reg_t object = argv[0];
492 	GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
493 	int16 loopCount;
494 
495 #ifdef ENABLE_SCI32
496 	if (getSciVersion() >= SCI_VERSION_2) {
497 		loopCount = CelObjView::getNumLoops(viewId);
498 	} else
499 #endif
500 		loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId);
501 
502 	debugC(9, kDebugLevelGraphics, "NumLoops(view.%d) = %d", viewId, loopCount);
503 
504 	return make_reg(0, loopCount);
505 }
506 
kNumCels(EngineState * s,int argc,reg_t * argv)507 reg_t kNumCels(EngineState *s, int argc, reg_t *argv) {
508 	reg_t object = argv[0];
509 	GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
510 	int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop));
511 	int16 celCount;
512 
513 #ifdef ENABLE_SCI32
514 	if (getSciVersion() >= SCI_VERSION_2) {
515 		celCount = CelObjView::getNumCels(viewId, loopNo);
516 	} else
517 #endif
518 		celCount = g_sci->_gfxCache->kernelViewGetCelCount(viewId, loopNo);
519 
520 	debugC(9, kDebugLevelGraphics, "NumCels(view.%d, %d) = %d", viewId, loopNo, celCount);
521 
522 	return make_reg(0, celCount);
523 }
524 
kOnControl(EngineState * s,int argc,reg_t * argv)525 reg_t kOnControl(EngineState *s, int argc, reg_t *argv) {
526 	Common::Rect rect;
527 	byte screenMask;
528 	int argBase = 0;
529 
530 	if ((argc == 2) || (argc == 4)) {
531 		screenMask = GFX_SCREEN_MASK_CONTROL;
532 	} else {
533 		screenMask = argv[0].toUint16();
534 		argBase = 1;
535 	}
536 	rect.left = argv[argBase].toSint16();
537 	rect.top = argv[argBase + 1].toSint16();
538 	if (argc > 3) {
539 		rect.right = argv[argBase + 2].toSint16();
540 		rect.bottom = argv[argBase + 3].toSint16();
541 	} else {
542 		rect.right = rect.left + 1;
543 		rect.bottom = rect.top + 1;
544 	}
545 	uint16 result = g_sci->_gfxCompare->kernelOnControl(screenMask, rect);
546 	return make_reg(0, result);
547 }
548 
549 #define K_DRAWPIC_FLAGS_MIRRORED			(1 << 14)
550 #define K_DRAWPIC_FLAGS_ANIMATIONBLACKOUT	(1 << 15)
551 
kDrawPic(EngineState * s,int argc,reg_t * argv)552 reg_t kDrawPic(EngineState *s, int argc, reg_t *argv) {
553 	GuiResourceId pictureId = argv[0].toUint16();
554 	uint16 flags = 0;
555 	int16 animationNr = -1;
556 	bool animationBlackoutFlag = false;
557 	bool mirroredFlag = false;
558 	bool addToFlag = false;
559 	int16 EGApaletteNo = 0; // default needs to be 0
560 
561 	if (argc >= 2) {
562 		flags = argv[1].toUint16();
563 		if (flags & K_DRAWPIC_FLAGS_ANIMATIONBLACKOUT)
564 			animationBlackoutFlag = true;
565 		animationNr = flags & 0xFF;
566 		// Mac interpreters ignored the mirrored flag and didn't mirror pics.
567 		//  KQ6 PC room 390 drew pic 390 mirrored so Mac added pic 395, which
568 		//  is a mirror of 390, but the script continued to pass this flag.
569 		if (g_sci->getPlatform() != Common::kPlatformMacintosh) {
570 			if (flags & K_DRAWPIC_FLAGS_MIRRORED)
571 				mirroredFlag = true;
572 		}
573 	}
574 	if (argc >= 3) {
575 		if (!argv[2].isNull())
576 			addToFlag = true;
577 		if (!g_sci->_features->usesOldGfxFunctions())
578 			addToFlag = !addToFlag;
579 	}
580 	if (argc >= 4)
581 		EGApaletteNo = argv[3].toUint16();
582 
583 	g_sci->_gfxPaint16->kernelDrawPicture(pictureId, animationNr, animationBlackoutFlag, mirroredFlag, addToFlag, EGApaletteNo);
584 
585 	return s->r_acc;
586 }
587 
kBaseSetter(EngineState * s,int argc,reg_t * argv)588 reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
589 	reg_t object = argv[0];
590 
591 	g_sci->_gfxCompare->kernelBaseSetter(object);
592 	return s->r_acc;
593 }
594 
kSetNowSeen(EngineState * s,int argc,reg_t * argv)595 reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
596 	g_sci->_gfxCompare->kernelSetNowSeen(argv[0]);
597 	return s->r_acc;
598 }
599 
kPalette(EngineState * s,int argc,reg_t * argv)600 reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
601 	if (!s)
602 		return make_reg(0, getSciVersion());
603 	error("not supposed to call this");
604 }
605 
kPaletteSetFromResource(EngineState * s,int argc,reg_t * argv)606 reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv) {
607 	GuiResourceId resourceId = argv[0].toUint16();
608 	bool force = false;
609 	if (argc == 2)
610 		force = argv[1].toUint16() == 2 ? true : false;
611 
612 	// Non-VGA games don't use palette resources.
613 	// This has been changed to 64 colors because Longbow Amiga does have
614 	// one palette (palette 999).
615 	if (g_sci->_gfxPalette16->getTotalColorCount() < 64)
616 		return s->r_acc;
617 
618 	g_sci->_gfxPalette16->kernelSetFromResource(resourceId, force);
619 	return s->r_acc;
620 }
621 
kPaletteSetFlag(EngineState * s,int argc,reg_t * argv)622 reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv) {
623 	uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
624 	uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
625 	uint16 flags = argv[2].toUint16();
626 	g_sci->_gfxPalette16->kernelSetFlag(fromColor, toColor, flags);
627 	return s->r_acc;
628 }
629 
kPaletteUnsetFlag(EngineState * s,int argc,reg_t * argv)630 reg_t kPaletteUnsetFlag(EngineState *s, int argc, reg_t *argv) {
631 	uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
632 	uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
633 	uint16 flags = argv[2].toUint16();
634 	g_sci->_gfxPalette16->kernelUnsetFlag(fromColor, toColor, flags);
635 	return s->r_acc;
636 }
637 
kPaletteSetIntensity(EngineState * s,int argc,reg_t * argv)638 reg_t kPaletteSetIntensity(EngineState *s, int argc, reg_t *argv) {
639 	uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
640 	uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
641 	uint16 intensity = argv[2].toUint16();
642 	bool setPalette = (argc < 4) ? true : (argv[3].isNull()) ? true : false;
643 
644 	// Palette intensity in non-VGA SCI1 games has been removed
645 	if (g_sci->_gfxPalette16->getTotalColorCount() < 256)
646 		return s->r_acc;
647 
648 	g_sci->_gfxPalette16->kernelSetIntensity(fromColor, toColor, intensity, setPalette);
649 	return s->r_acc;
650 }
651 
kPaletteFindColor(EngineState * s,int argc,reg_t * argv)652 reg_t kPaletteFindColor(EngineState *s, int argc, reg_t *argv) {
653 	uint16 r = argv[0].toUint16();
654 	uint16 g = argv[1].toUint16();
655 	uint16 b = argv[2].toUint16();
656 	return make_reg(0, g_sci->_gfxPalette16->kernelFindColor(r, g, b));
657 }
658 
kPaletteAnimate(EngineState * s,int argc,reg_t * argv)659 reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) {
660 	int16 argNr;
661 	bool paletteChanged = false;
662 
663 	// Palette animation in non-VGA SCI1 games has been removed
664 	if (g_sci->_gfxPalette16->getTotalColorCount() == 256) {
665 		for (argNr = 0; argNr < argc; argNr += 3) {
666 			uint16 fromColor = argv[argNr].toUint16();
667 			uint16 toColor = argv[argNr + 1].toUint16();
668 			int16 speed = argv[argNr + 2].toSint16();
669 			if (g_sci->_gfxPalette16->kernelAnimate(fromColor, toColor, speed))
670 				paletteChanged = true;
671 		}
672 		if (paletteChanged)
673 			g_sci->_gfxPalette16->kernelAnimateSet();
674 	}
675 
676 	// WORKAROUND: The game scripts in SQ4 floppy count the number of elapsed
677 	// cycles in the intro from the number of successive kAnimate calls during
678 	// the palette cycling effect, while showing the SQ4 logo. This worked in
679 	// older computers because each animate call took awhile to complete.
680 	// Normally, such scripts are handled automatically by our speed throttler,
681 	// however in this case there are no calls to kGameIsRestarting (where the
682 	// speed throttler gets called) between the different palette animation calls.
683 	// Thus, we add a small delay between each animate call to make the whole
684 	// palette animation effect slower and visible, and not have the logo screen
685 	// get skipped because the scripts don't wait between animation steps. This
686 	// workaround is applied to non-VGA versions as well because even though they
687 	// don't use palette animation they still call this function and use it for
688 	// timing. Fixes bugs #6057, #6193.
689 	// The original workaround was for the intro SQ4 logo (room#1).
690 	// This problem also happens in the time pod (room#531).
691 	// This problem also happens in the ending cutscene time rip (room#21).
692 	// This workaround affects astro chicken's (room#290) and is also called once
693 	// right after a gameover (room#376)
694 	if (g_sci->getGameId() == GID_SQ4 && !g_sci->isCD())
695 		g_sci->sleep(10);
696 
697 	return s->r_acc;
698 }
699 
kPaletteSave(EngineState * s,int argc,reg_t * argv)700 reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv) {
701 	return g_sci->_gfxPalette16->kernelSave();
702 }
703 
kPaletteRestore(EngineState * s,int argc,reg_t * argv)704 reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv) {
705 	g_sci->_gfxPalette16->kernelRestore(argv[0]);
706 	return argv[0];
707 }
708 
kPalVary(EngineState * s,int argc,reg_t * argv)709 reg_t kPalVary(EngineState *s, int argc, reg_t *argv) {
710 	if (!s)
711 		return make_reg(0, getSciVersion());
712 	error("not supposed to call this");
713 }
714 
kPalVaryInit(EngineState * s,int argc,reg_t * argv)715 reg_t kPalVaryInit(EngineState *s, int argc, reg_t *argv) {
716 	GuiResourceId paletteId = argv[0].toUint16();
717 	uint16 ticks = argv[1].toUint16();
718 	uint16 stepStop = argc >= 3 ? argv[2].toUint16() : 64;
719 	uint16 direction = argc >= 4 ? argv[3].toUint16() : 1;
720 	if (g_sci->_gfxPalette16->kernelPalVaryInit(paletteId, ticks, stepStop, direction))
721 		return SIGNAL_REG;
722 	return NULL_REG;
723 }
724 
kPalVaryReverse(EngineState * s,int argc,reg_t * argv)725 reg_t kPalVaryReverse(EngineState *s, int argc, reg_t *argv) {
726 	int16 ticks = argc >= 1 ? argv[0].toUint16() : -1;
727 	int16 stepStop = argc >= 2 ? argv[1].toUint16() : 0;
728 	int16 direction = argc >= 3 ? argv[2].toSint16() : -1;
729 
730 	return make_reg(0, g_sci->_gfxPalette16->kernelPalVaryReverse(ticks, stepStop, direction));
731 }
732 
kPalVaryGetCurrentStep(EngineState * s,int argc,reg_t * argv)733 reg_t kPalVaryGetCurrentStep(EngineState *s, int argc, reg_t *argv) {
734 	return make_reg(0, g_sci->_gfxPalette16->kernelPalVaryGetCurrentStep());
735 }
736 
kPalVaryDeinit(EngineState * s,int argc,reg_t * argv)737 reg_t kPalVaryDeinit(EngineState *s, int argc, reg_t *argv) {
738 	g_sci->_gfxPalette16->kernelPalVaryDeinit();
739 	return NULL_REG;
740 }
741 
kPalVaryChangeTarget(EngineState * s,int argc,reg_t * argv)742 reg_t kPalVaryChangeTarget(EngineState *s, int argc, reg_t *argv) {
743 	GuiResourceId paletteId = argv[0].toUint16();
744 	int16 currentStep = g_sci->_gfxPalette16->kernelPalVaryChangeTarget(paletteId);
745 	return make_reg(0, currentStep);
746 }
747 
kPalVaryChangeTicks(EngineState * s,int argc,reg_t * argv)748 reg_t kPalVaryChangeTicks(EngineState *s, int argc, reg_t *argv) {
749 	uint16 ticks = argv[0].toUint16();
750 	g_sci->_gfxPalette16->kernelPalVaryChangeTicks(ticks);
751 	return NULL_REG;
752 }
753 
kPalVaryPauseResume(EngineState * s,int argc,reg_t * argv)754 reg_t kPalVaryPauseResume(EngineState *s, int argc, reg_t *argv) {
755 	bool pauseState = !argv[0].isNull();
756 	g_sci->_gfxPalette16->kernelPalVaryPause(pauseState);
757 	return NULL_REG;
758 }
759 
kAssertPalette(EngineState * s,int argc,reg_t * argv)760 reg_t kAssertPalette(EngineState *s, int argc, reg_t *argv) {
761 	GuiResourceId paletteId = argv[0].toUint16();
762 
763 	g_sci->_gfxPalette16->kernelAssertPalette(paletteId);
764 	return s->r_acc;
765 }
766 
767 // Used to show hires character portraits in the Windows CD version of KQ6
kPortrait(EngineState * s,int argc,reg_t * argv)768 reg_t kPortrait(EngineState *s, int argc, reg_t *argv) {
769 	uint16 operation = argv[0].toUint16();
770 
771 	switch (operation) {
772 	case 0: { // load
773 		if (argc == 2) {
774 			Common::String resourceName = s->_segMan->getString(argv[1]);
775 			s->r_acc = g_sci->_gfxPaint16->kernelPortraitLoad(resourceName);
776 		} else {
777 			error("kPortrait(loadResource) called with unsupported argc %d", argc);
778 		}
779 		break;
780 	}
781 	case 1: { // show
782 		if (argc == 10) {
783 			Common::String resourceName = s->_segMan->getString(argv[1]);
784 			Common::Point position = Common::Point(argv[2].toUint16(), argv[3].toUint16());
785 			uint resourceNum = argv[4].toUint16();
786 			uint noun = argv[5].toUint16() & 0xff;
787 			uint verb = argv[6].toUint16() & 0xff;
788 			uint cond = argv[7].toUint16() & 0xff;
789 			uint seq = argv[8].toUint16() & 0xff;
790 			// argv[9] is usually 0??!!
791 
792 			g_sci->_gfxPaint16->kernelPortraitShow(resourceName, position, resourceNum, noun, verb, cond, seq);
793 			return SIGNAL_REG;
794 		} else {
795 			error("kPortrait(show) called with unsupported argc %d", argc);
796 		}
797 		break;
798 	}
799 	case 2: { // unload
800 		if (argc == 2) {
801 			uint16 portraitId = argv[1].toUint16();
802 			g_sci->_gfxPaint16->kernelPortraitUnload(portraitId);
803 		} else {
804 			error("kPortrait(unload) called with unsupported argc %d", argc);
805 		}
806 		break;
807 	}
808 	default:
809 		error("kPortrait(%d), not implemented (argc = %d)", operation, argc);
810 	}
811 
812 	return s->r_acc;
813 }
814 
815 // Original top-left must stay on kControl rects, we adjust accordingly because
816 // sierra sci actually wont draw rects that are upside down (example: jones,
817 // when challenging jones - one button is a duplicate and also has lower-right
818 // which is 0, 0)
kControlCreateRect(int16 x,int16 y,int16 x1,int16 y1)819 Common::Rect kControlCreateRect(int16 x, int16 y, int16 x1, int16 y1) {
820 	if (x > x1) x1 = x;
821 	if (y > y1) y1 = y;
822 	return Common::Rect(x, y, x1, y1);
823 }
824 
_k_GenericDrawControl(EngineState * s,reg_t controlObject,bool hilite)825 void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
826 	int16 type = readSelectorValue(s->_segMan, controlObject, SELECTOR(type));
827 	int16 style = readSelectorValue(s->_segMan, controlObject, SELECTOR(state));
828 	int16 x = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsLeft));
829 	int16 y = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsTop));
830 	GuiResourceId fontId = readSelectorValue(s->_segMan, controlObject, SELECTOR(font));
831 	reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
832 	Common::String text;
833 	Common::Rect rect;
834 	TextAlignment alignment;
835 	int16 mode, maxChars, cursorPos, upperPos, listCount, i;
836 	uint16 upperOffset, cursorOffset;
837 	GuiResourceId viewId;
838 	int16 loopNo;
839 	int16 celNo;
840 	int16 priority;
841 	reg_t listSeeker;
842 	Common::String *listStrings = nullptr;
843 	bool isAlias = false;
844 
845 	rect = kControlCreateRect(x, y,
846 				readSelectorValue(s->_segMan, controlObject, SELECTOR(nsRight)),
847 				readSelectorValue(s->_segMan, controlObject, SELECTOR(nsBottom)));
848 
849 	if (!textReference.isNull())
850 		text = s->_segMan->getString(textReference);
851 
852 	uint16 languageSplitter = 0;
853 	Common::String splitText;
854 
855 	switch (type) {
856 	case SCI_CONTROLS_TYPE_BUTTON:
857 	case SCI_CONTROLS_TYPE_TEXTEDIT:
858 		splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, NULL);
859 		break;
860 	case SCI_CONTROLS_TYPE_TEXT:
861 		splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter);
862 		break;
863 	}
864 
865 	switch (type) {
866 	case SCI_CONTROLS_TYPE_BUTTON:
867 		debugC(kDebugLevelGraphics, "drawing button %04x:%04x to %d,%d", PRINT_REG(controlObject), x, y);
868 		g_sci->_gfxControls16->kernelDrawButton(rect, controlObject, splitText.c_str(), languageSplitter, fontId, style, hilite);
869 		return;
870 
871 	case SCI_CONTROLS_TYPE_TEXT:
872 		alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
873 		debugC(kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment);
874 		g_sci->_gfxControls16->kernelDrawText(rect, controlObject, splitText.c_str(), languageSplitter, fontId, alignment, style, hilite);
875 		s->r_acc = g_sci->_gfxText16->allocAndFillReferenceRectArray();
876 		return;
877 
878 	case SCI_CONTROLS_TYPE_TEXTEDIT:
879 		mode = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
880 		maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(max));
881 		cursorPos = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor));
882 		if (cursorPos > (int)text.size()) {
883 			// if cursor is outside of text, adjust accordingly
884 			cursorPos = text.size();
885 			writeSelectorValue(s->_segMan, controlObject, SELECTOR(cursor), cursorPos);
886 		}
887 		debugC(kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y);
888 		g_sci->_gfxControls16->kernelDrawTextEdit(rect, controlObject, splitText.c_str(), languageSplitter, fontId, mode, style, cursorPos, maxChars, hilite);
889 		return;
890 
891 	case SCI_CONTROLS_TYPE_ICON:
892 		viewId = readSelectorValue(s->_segMan, controlObject, SELECTOR(view));
893 		{
894 			int l = readSelectorValue(s->_segMan, controlObject, SELECTOR(loop));
895 			loopNo = (l & 0x80) ? l - 256 : l;
896 			int c = readSelectorValue(s->_segMan, controlObject, SELECTOR(cel));
897 			celNo = (c & 0x80) ? c - 256 : c;
898 			// Check if the control object specifies a priority selector (like in Jones)
899 			if (lookupSelector(s->_segMan, controlObject, SELECTOR(priority), NULL, NULL) == kSelectorVariable)
900 				priority = readSelectorValue(s->_segMan, controlObject, SELECTOR(priority));
901 			else
902 				priority = -1;
903 		}
904 		debugC(kDebugLevelGraphics, "drawing icon control %04x:%04x to %d,%d", PRINT_REG(controlObject), x, y - 1);
905 		g_sci->_gfxControls16->kernelDrawIcon(rect, controlObject, viewId, loopNo, celNo, priority, style, hilite);
906 		return;
907 
908 	case SCI_CONTROLS_TYPE_LIST:
909 	case SCI_CONTROLS_TYPE_LIST_ALIAS:
910 		if (type == SCI_CONTROLS_TYPE_LIST_ALIAS)
911 			isAlias = true;
912 
913 		maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry
914 		cursorOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor));
915 		if (SELECTOR(topString) != -1) {
916 			// Games from early SCI1 onwards use topString
917 			upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(topString));
918 		} else {
919 			// Earlier games use lsTop or brTop
920 			if (lookupSelector(s->_segMan, controlObject, SELECTOR(brTop), NULL, NULL) == kSelectorVariable)
921 				upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(brTop));
922 			else
923 				upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(lsTop));
924 		}
925 
926 		// Count string entries in NULL terminated string list
927 		listCount = 0; listSeeker = textReference;
928 		while (s->_segMan->strlen(listSeeker) > 0) {
929 			listCount++;
930 			listSeeker.incOffset(maxChars);
931 		}
932 
933 		// TODO: This is rather convoluted... It would be a lot cleaner
934 		// if sciw_new_list_control would take a list of Common::String
935 		cursorPos = 0; upperPos = 0;
936 		if (listCount) {
937 			// We create a pointer-list to the different strings, we also find out whats upper and cursor position
938 			listSeeker = textReference;
939 			listStrings = new Common::String[listCount];
940 			for (i = 0; i < listCount; i++) {
941 				listStrings[i] = s->_segMan->getString(listSeeker);
942 				if (listSeeker.getOffset() == upperOffset)
943 					upperPos = i;
944 				if (listSeeker.getOffset() == cursorOffset)
945 					cursorPos = i;
946 				listSeeker.incOffset(maxChars);
947 			}
948 		}
949 
950 		debugC(kDebugLevelGraphics, "drawing list control %04x:%04x to %d,%d", PRINT_REG(controlObject), x, y);
951 		g_sci->_gfxControls16->kernelDrawList(rect, controlObject, maxChars, listCount, listStrings, fontId, style, upperPos, cursorPos, isAlias, hilite);
952 		delete[] listStrings;
953 		return;
954 
955 	case SCI_CONTROLS_TYPE_DUMMY:
956 		// Actually this here does nothing at all, its required by at least QfG1/EGA that we accept this type
957 		return;
958 
959 	default:
960 		error("unsupported control type %d", type);
961 	}
962 }
963 
kDrawControl(EngineState * s,int argc,reg_t * argv)964 reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
965 	reg_t controlObject = argv[0];
966 	Common::String objName = s->_segMan->getObjectName(controlObject);
967 
968 	// Most of the time, we won't return anything to the caller
969 	//  but |r| textcodes will trigger creation of rects in memory and will then set s->r_acc
970 	s->r_acc = NULL_REG;
971 
972 	// Disable the "Change Directory" button, as we don't allow the game engine to
973 	// change the directory where saved games are placed
974 	// "changeDirItem" is used in the import windows of QFG2&3
975 	if ((objName == "changeDirI") || (objName == "changeDirItem")) {
976 		int state = readSelectorValue(s->_segMan, controlObject, SELECTOR(state));
977 		writeSelectorValue(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED);
978 	}
979 	if (objName == "DEdit") {
980 		reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
981 		if (!textReference.isNull()) {
982 			Common::String text = s->_segMan->getString(textReference);
983 			if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav") || (text == "a:gloire3.sauv")) {
984 				// Remove "a:" from hero quest / quest for glory export default filenames
985 				// The french version of Quest For Glory 3 uses "gloire3.sauv". It seems a translator translated the filename.
986 				text.deleteChar(0);
987 				text.deleteChar(0);
988 				s->_segMan->strcpy(textReference, text.c_str());
989 			}
990 		}
991 	}
992 	if (objName == "savedHeros") {
993 		// Import of QfG character files dialog is shown.
994 		// Display additional popup information before letting user use it.
995 		// For the SCI32 version of this, check kernelAddPlane().
996 		reg_t changeDirButton = s->_segMan->findObjectByName("changeDirItem");
997 		if (!changeDirButton.isNull()) {
998 			// check if checkDirButton is still enabled, in that case we are called the first time during that room
999 			if (!(readSelectorValue(s->_segMan, changeDirButton, SELECTOR(state)) & SCI_CONTROLS_STYLE_DISABLED)) {
1000 				g_sci->showQfgImportMessageBox();
1001 			}
1002 		}
1003 
1004 		// For the SCI32 version of this, check kListAt().
1005 		s->_chosenQfGImportItem = readSelectorValue(s->_segMan, controlObject, SELECTOR(mark));
1006 	}
1007 
1008 	_k_GenericDrawControl(s, controlObject, false);
1009 	return s->r_acc;
1010 }
1011 
kHiliteControl(EngineState * s,int argc,reg_t * argv)1012 reg_t kHiliteControl(EngineState *s, int argc, reg_t *argv) {
1013 	reg_t controlObject = argv[0];
1014 
1015 	_k_GenericDrawControl(s, controlObject, true);
1016 	return s->r_acc;
1017 }
1018 
kEditControl(EngineState * s,int argc,reg_t * argv)1019 reg_t kEditControl(EngineState *s, int argc, reg_t *argv) {
1020 	reg_t controlObject = argv[0];
1021 	reg_t eventObject = argv[1];
1022 
1023 	if (!controlObject.isNull()) {
1024 		int16 controlType = readSelectorValue(s->_segMan, controlObject, SELECTOR(type));
1025 
1026 		switch (controlType) {
1027 		case SCI_CONTROLS_TYPE_TEXTEDIT:
1028 			// Only process textedit controls in here
1029 			g_sci->_gfxControls16->kernelTexteditChange(controlObject, eventObject);
1030 			break;
1031 		default:
1032 			break;
1033 		}
1034 	}
1035 	return s->r_acc;
1036 }
1037 
kAddToPic(EngineState * s,int argc,reg_t * argv)1038 reg_t kAddToPic(EngineState *s, int argc, reg_t *argv) {
1039 	GuiResourceId viewId;
1040 	int16 loopNo;
1041 	int16 celNo;
1042 	int16 leftPos, topPos, priority, control;
1043 
1044 	switch (argc) {
1045 	// Is this ever really gets called with 0 parameters, we need to set _picNotValid!!
1046 	//case 0:
1047 	//	break;
1048 	case 1:
1049 		if (argv[0].isNull())
1050 			return s->r_acc;
1051 		g_sci->_gfxAnimate->kernelAddToPicList(argv[0], argc, argv);
1052 		break;
1053 	case 7:
1054 		viewId = argv[0].toUint16();
1055 		loopNo = argv[1].toSint16();
1056 		celNo = argv[2].toSint16();
1057 		leftPos = argv[3].toSint16();
1058 		topPos = argv[4].toSint16();
1059 		priority = argv[5].toSint16();
1060 		control = argv[6].toSint16();
1061 		g_sci->_gfxAnimate->kernelAddToPicView(viewId, loopNo, celNo, leftPos, topPos, priority, control);
1062 		break;
1063 	default:
1064 		error("kAddToPic with unsupported parameter count %d", argc);
1065 	}
1066 	return s->r_acc;
1067 }
1068 
kGetPort(EngineState * s,int argc,reg_t * argv)1069 reg_t kGetPort(EngineState *s, int argc, reg_t *argv) {
1070 	return g_sci->_gfxPorts->kernelGetActive();
1071 }
1072 
kSetPort(EngineState * s,int argc,reg_t * argv)1073 reg_t kSetPort(EngineState *s, int argc, reg_t *argv) {
1074 	uint16 portId;
1075 	Common::Rect picRect;
1076 	int16 picTop, picLeft;
1077 	bool initPriorityBandsFlag = false;
1078 
1079 	switch (argc) {
1080 	case 1:
1081 		portId = argv[0].toSint16();
1082 		g_sci->_gfxPorts->kernelSetActive(portId);
1083 		break;
1084 
1085 	case 7:
1086 		initPriorityBandsFlag = true;
1087 		// fall through
1088 	case 6:
1089 		picRect.top = argv[0].toSint16();
1090 		picRect.left = argv[1].toSint16();
1091 		picRect.bottom = argv[2].toSint16();
1092 		picRect.right = argv[3].toSint16();
1093 		picTop = argv[4].toSint16();
1094 		picLeft = argv[5].toSint16();
1095 		g_sci->_gfxPorts->kernelSetPicWindow(picRect, picTop, picLeft, initPriorityBandsFlag);
1096 		break;
1097 
1098 	default:
1099 		error("SetPort was called with %d parameters", argc);
1100 		break;
1101 	}
1102 	return NULL_REG;
1103 }
1104 
kDrawCel(EngineState * s,int argc,reg_t * argv)1105 reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) {
1106 	GuiResourceId viewId = argv[0].toSint16();
1107 	int16 loopNo = argv[1].toSint16();
1108 	int16 celNo = argv[2].toSint16();
1109 	uint16 x = argv[3].toUint16();
1110 	uint16 y = argv[4].toUint16();
1111 	int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
1112 	uint16 paletteNo = (argc > 6) ? argv[6].toUint16() : 0;
1113 	bool hiresMode = false;
1114 	reg_t upscaledHiresHandle = NULL_REG;
1115 	uint16 scaleX = 128;
1116 	uint16 scaleY = 128;
1117 
1118 	if (argc > 7) {
1119 		// this is either kq6 hires or scaling
1120 		if (paletteNo > 0) {
1121 			// it's scaling
1122 			scaleX = argv[6].toUint16();
1123 			scaleY = argv[7].toUint16();
1124 			paletteNo = 0;
1125 		} else {
1126 			// KQ6 hires
1127 			hiresMode = true;
1128 			upscaledHiresHandle = argv[7];
1129 		}
1130 	}
1131 
1132 	g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, scaleX, scaleY, hiresMode, upscaledHiresHandle);
1133 
1134 	return s->r_acc;
1135 }
1136 
kDisposeWindow(EngineState * s,int argc,reg_t * argv)1137 reg_t kDisposeWindow(EngineState *s, int argc, reg_t *argv) {
1138 	int windowId = argv[0].toSint16();
1139 	bool reanimate = false;
1140 	if ((argc != 2) || (argv[1].isNull()))
1141 		reanimate = true;
1142 
1143 	g_sci->_gfxPorts->kernelDisposeWindow(windowId, reanimate);
1144 	return s->r_acc;
1145 }
1146 
kNewWindow(EngineState * s,int argc,reg_t * argv)1147 reg_t kNewWindow(EngineState *s, int argc, reg_t *argv) {
1148 	Common::Rect rect1 (argv[1].toSint16(), argv[0].toSint16(), argv[3].toSint16(), argv[2].toSint16());
1149 	Common::Rect rect2;
1150 	int argextra = argc >= 13 ? 4 : 0; // Triggers in PQ3 and SCI1.1 games, argc 13 for DOS argc 15 for mac
1151 	int	style = argv[5 + argextra].toSint16();
1152 	int	priority = (argc > 6 + argextra) ? argv[6 + argextra].toSint16() : -1;
1153 	int colorPen = adjustGraphColor((argc > 7 + argextra) ? argv[7 + argextra].toSint16() : 0);
1154 	int colorBack = adjustGraphColor((argc > 8 + argextra) ? argv[8 + argextra].toSint16() : 255);
1155 
1156 	if (argc >= 13)
1157 		rect2 = Common::Rect (argv[5].toSint16(), argv[4].toSint16(), argv[7].toSint16(), argv[6].toSint16());
1158 
1159 	Common::String title;
1160 	if (argv[4 + argextra].getSegment()) {
1161 		title = s->_segMan->getString(argv[4 + argextra]);
1162 		title = g_sci->strSplit(title.c_str(), NULL);
1163 	}
1164 
1165 	return g_sci->_gfxPorts->kernelNewWindow(rect1, rect2, style, priority, colorPen, colorBack, title.c_str());
1166 }
1167 
kAnimate(EngineState * s,int argc,reg_t * argv)1168 reg_t kAnimate(EngineState *s, int argc, reg_t *argv) {
1169 	reg_t castListReference = (argc > 0) ? argv[0] : NULL_REG;
1170 	bool cycle = (argc > 1) ? ((argv[1].toUint16()) ? true : false) : false;
1171 
1172 	g_sci->_gfxAnimate->kernelAnimate(castListReference, cycle, argc, argv);
1173 
1174 	// WORKAROUND: At the end of Ecoquest 1, during the credits, the game
1175 	// doesn't call kGetEvent(), so no events are processed (e.g. window
1176 	// focusing, window moving etc). We poll events for that scene, to
1177 	// keep ScummVM responsive. Fixes ScummVM "freezing" during the credits,
1178 	// bug #3101846
1179 	if (g_sci->getGameId() == GID_ECOQUEST && s->currentRoomNumber() == 680)
1180 		g_sci->getEventManager()->getSciEvent(kSciEventPeek);
1181 
1182 	return s->r_acc;
1183 }
1184 
kShakeScreen(EngineState * s,int argc,reg_t * argv)1185 reg_t kShakeScreen(EngineState *s, int argc, reg_t *argv) {
1186 	int16 shakeCount = (argc > 0) ? argv[0].toUint16() : 1;
1187 	int16 directions = (argc > 1) ? argv[1].toUint16() : 1;
1188 
1189 	g_sci->_gfxScreen->kernelShakeScreen(shakeCount, directions);
1190 	return s->r_acc;
1191 }
1192 
kDisplay(EngineState * s,int argc,reg_t * argv)1193 reg_t kDisplay(EngineState *s, int argc, reg_t *argv) {
1194 	reg_t textp = argv[0];
1195 	int index = (argc > 1) ? argv[1].toUint16() : 0;
1196 
1197 	Common::String text;
1198 
1199 	if (textp.getSegment()) {
1200 		argc--; argv++;
1201 		text = s->_segMan->getString(textp);
1202 	} else {
1203 		argc--; argc--; argv++; argv++;
1204 		text = g_sci->getKernel()->lookupText(textp, index);
1205 	}
1206 
1207 	uint16 languageSplitter = 0;
1208 	Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter);
1209 
1210 	return g_sci->_gfxPaint16->kernelDisplay(splitText.c_str(), languageSplitter, argc, argv);
1211 }
1212 
kSetVideoMode(EngineState * s,int argc,reg_t * argv)1213 reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) {
1214 	// This call is used for KQ6's intro. It has one parameter, which is 1 when
1215 	// the intro begins, and 0 when it ends. It is suspected that this is
1216 	// actually a flag to enable video planar memory access, as the video
1217 	// decoder in KQ6 is specifically written for the planar memory model.
1218 	// Planar memory mode access was used for VGA "Mode X" (320x240 resolution,
1219 	// although the intro in KQ6 is 320x200).
1220 	// Refer to http://en.wikipedia.org/wiki/Mode_X
1221 
1222 	//warning("STUB: SetVideoMode %d", argv[0].toUint16());
1223 	return s->r_acc;
1224 }
1225 
1226 // New calls for SCI11. Using those is only needed when using text-codes so that
1227 // one is able to change font and/or color multiple times during kDisplay and
1228 // kDrawControl
kTextFonts(EngineState * s,int argc,reg_t * argv)1229 reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) {
1230 	g_sci->_gfxText16->kernelTextFonts(argc, argv);
1231 	return s->r_acc;
1232 }
1233 
kTextColors(EngineState * s,int argc,reg_t * argv)1234 reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
1235 	g_sci->_gfxText16->kernelTextColors(argc, argv);
1236 	return s->r_acc;
1237 }
1238 
1239 /**
1240  * Debug command, used by the SCI builtin debugger
1241  */
kShow(EngineState * s,int argc,reg_t * argv)1242 reg_t kShow(EngineState *s, int argc, reg_t *argv) {
1243 	uint16 map = argv[0].toUint16();
1244 
1245 	switch (map) {
1246 	case 1:	// Visual, substituted by display for us
1247 		g_sci->_gfxScreen->debugShowMap(3);
1248 		break;
1249 	case 2:	// Priority
1250 		g_sci->_gfxScreen->debugShowMap(1);
1251 		break;
1252 	case 3:	// Control
1253 	case 4:	// Control
1254 		g_sci->_gfxScreen->debugShowMap(2);
1255 		break;
1256 	default:
1257 		warning("Map %d is not available", map);
1258 	}
1259 
1260 	return s->r_acc;
1261 }
1262 
1263 // Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4
kRemapColors(EngineState * s,int argc,reg_t * argv)1264 reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
1265 	uint16 operation = argv[0].toUint16();
1266 
1267 	switch (operation) {
1268 	case 0: { // remap by percent
1269 		uint16 percent = argv[1].toUint16();
1270 		g_sci->_gfxRemap16->resetRemapping();
1271 		g_sci->_gfxRemap16->setRemappingPercent(254, percent);
1272 		}
1273 		break;
1274 	case 1:	{ // remap by range
1275 		uint16 from = argv[1].toUint16();
1276 		uint16 to = argv[2].toUint16();
1277 		uint16 base = argv[3].toUint16();
1278 		g_sci->_gfxRemap16->resetRemapping();
1279 		g_sci->_gfxRemap16->setRemappingRange(254, from, to, base);
1280 		}
1281 		break;
1282 	case 2:	// turn remapping off (unused)
1283 		error("Unused subop kRemapColors(2) has been called");
1284 		break;
1285 	default:
1286 		break;
1287 	}
1288 
1289 	return s->r_acc;
1290 }
1291 
1292 // Later SCI32-style kRemapColors, but in SCI11+.
kRemapColorsKawa(EngineState * s,int argc,reg_t * argv)1293 reg_t kRemapColorsKawa(EngineState *s, int argc, reg_t *argv) {
1294 	uint16 operation = argv[0].toUint16();
1295 
1296 	switch (operation) {
1297 	case 0: // off
1298 		break;
1299 	case 1: { // remap by percent
1300 		uint16 from = argv[1].toUint16();
1301 		uint16 percent = argv[2].toUint16();
1302 		g_sci->_gfxRemap16->resetRemapping();
1303 		g_sci->_gfxRemap16->setRemappingPercent(from, percent);
1304 		}
1305 		break;
1306 	case 2: { // remap by range
1307 		uint16 from = argv[1].toUint16();
1308 		uint16 to = argv[2].toUint16();
1309 		uint16 base = argv[3].toUint16();
1310 		g_sci->_gfxRemap16->resetRemapping();
1311 		g_sci->_gfxRemap16->setRemappingRange(254, from, to, base);
1312 		}
1313 		break;
1314 	default:
1315 		error("Unsupported SCI32-style kRemapColors(%d) has been called", operation);
1316 		break;
1317 	}
1318 	return s->r_acc;
1319 }
1320 
1321 } // End of namespace Sci
1322