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/str.h"
24 #include "common/system.h"
25 #include "engines/util.h"
26 #include "graphics/cursorman.h"
27 #include "graphics/palette.h"
28 #include "graphics/surface.h"
29 #include "common/config-manager.h"
30 
31 #include "supernova/imageid.h"
32 #include "supernova/resman.h"
33 #include "supernova/game-manager.h"
34 #include "supernova/screen.h"
35 #include "supernova/supernova.h"
36 
37 #include "supernova/screenstatic.cpp"
38 
39 namespace Supernova {
40 
ScreenBuffer()41 ScreenBuffer::ScreenBuffer()
42 	: _x(0)
43 	, _y(0)
44 	, _width(0)
45 	, _height(0)
46 	, _pixels(nullptr) {
47 }
48 
ScreenBufferStack()49 ScreenBufferStack::ScreenBufferStack()
50 	: _last(_buffer) {
51 }
52 
push(int x,int y,int width,int height)53 void ScreenBufferStack::push(int x, int y, int width, int height) {
54 	if (_last == ARRAYEND(_buffer))
55 		return;
56 
57 	Graphics::Surface *screenSurface = g_system->lockScreen();
58 
59 	if (x < 0) {
60 		width += x;
61 		x = 0;
62 	}
63 
64 	if (x + width > screenSurface->w)
65 		width = screenSurface->w - x;
66 
67 	if (y < 0) {
68 		height += y;
69 		y = 0;
70 	}
71 
72 	if (y + height > screenSurface->h)
73 		height = screenSurface->h - y;
74 
75 	_last->_pixels = new byte[width * height];
76 	byte *pixels = _last->_pixels;
77 	const byte *screen = static_cast<const byte *>(screenSurface->getBasePtr(x, y));
78 	for (int i = 0; i < height; ++i) {
79 		Common::copy(screen, screen + width, pixels);
80 		screen += screenSurface->pitch;
81 		pixels += width;
82 	}
83 	g_system->unlockScreen();
84 
85 	_last->_x = x;
86 	_last->_y = y;
87 	_last->_width = width;
88 	_last->_height = height;
89 
90 	++_last;
91 }
92 
restore()93 void ScreenBufferStack::restore() {
94 	if (_last == _buffer)
95 		return;
96 
97 	--_last;
98 	g_system->lockScreen()->copyRectToSurface(_last->_pixels, _last->_width, _last->_x,
99 											  _last->_y, _last->_width, _last->_height);
100 	g_system->unlockScreen();
101 
102 	delete[] _last->_pixels;
103 }
104 
Marquee(Screen * screen,MarqueeId id,const char * text)105 Marquee::Marquee(Screen *screen, MarqueeId id, const char *text)
106 	: _text(text)
107 	, _textBegin(text)
108 	, _delay(0)
109 	, _loop(false)
110 	, _oldColor(nullptr)
111 	, _screen(screen) {
112 	if (_screen->_vm->_MSPart == 1) {
113 		_color = kColorLightBlue;
114 		if (id == kMarqueeIntro) {
115 			_y = 191;
116 			_loop = true;
117 		} else if (id == kMarqueeOutro) {
118 			_y = 1;
119 		}
120 	} else if (_screen->_vm->_MSPart == 2) {
121 		byte purple[3] = {0x9b, 0x00, 0xfb};
122 		_oldColor = new byte[3];
123 		_screen->_vm->_system->getPaletteManager()->grabPalette(_oldColor, kColorPurple, 1);
124 		_screen->_vm->_system->getPaletteManager()->setPalette(purple, kColorPurple, 1);
125 		_color = kColorPurple;
126 		if (id == kMarqueeIntro) {
127 			_y = 191;
128 			_loop = true;
129 		} else if (id == kMarqueeOutro) {
130 			_y = 191;
131 		}
132 	}
133 
134 	_textWidth = Screen::textWidth(_text);
135 	_x = kScreenWidth / 2 - _textWidth / 2;
136 	_screen->_textCursorX = _x;
137 	_screen->_textCursorY = _y;
138 	_screen->_textColor = _color;
139 }
140 
~Marquee()141 Marquee::~Marquee() {
142 	if (_screen->_vm->_MSPart == 2) {
143 		_screen->_vm->_system->getPaletteManager()->setPalette(_oldColor, kColorPurple, 1);
144 		delete _oldColor;
145 	}
146 }
147 
clearText()148 void Marquee::clearText() {
149 	_screen->renderBox(_x, _y - 1, _textWidth + 1, 9, kColorBlack);
150 }
151 
reset()152 void Marquee::reset() {
153 	_text = _textBegin;
154 	clearText();
155 	_textWidth = Screen::textWidth(_text);
156 	_x = kScreenWidth / 2 - _textWidth / 2;
157 	_screen->_textCursorX = _x;
158 }
159 
renderCharacter()160 bool Marquee::renderCharacter() {
161 	if (_delay != 0) {
162 		_delay--;
163 		return true;
164 	}
165 
166 	switch (*_text) {
167 	case '\233':
168 		if (_loop) {
169 			_loop = false;
170 			_text = _textBegin;
171 			clearText();
172 			_textWidth = Screen::textWidth(_text);
173 			_x = kScreenWidth / 2 - _textWidth / 2;
174 			_screen->_textCursorX = _x;
175 		} else
176 			return false;
177 		break;
178 	case '\1':
179 		clearText();
180 		_text++;
181 		_textWidth = Screen::textWidth(_text);
182 		_x = kScreenWidth / 2 - _textWidth / 2;
183 		_screen->_textCursorX = _x;
184 		if (_screen->_vm->_MSPart == 1) {
185 			_color = kColorLightBlue;
186 			_screen->_textColor = _color;
187 		} else if (_screen->_vm->_MSPart == 2) {
188 			_color = kColorPurple;
189 			_screen->_textColor = _color;
190 		}
191 		break;
192 	case '^':
193 		_color = kColorLightYellow;
194 		_screen->_textColor = _color;
195 		_text++;
196 		break;
197 	case '#':
198 		_delay = 50;
199 		_text++;
200 		break;
201 	default:
202 		_screen->renderText((uint16)*_text++);
203 		_delay = 1;
204 		break;
205 	}
206 	return true;
207 }
208 
Screen(SupernovaEngine * vm,ResourceManager * resMan)209 Screen::Screen(SupernovaEngine *vm, ResourceManager *resMan)
210 	: _vm(vm)
211 	, _resMan(resMan)
212 	, _currentImage(nullptr)
213 	, _viewportBrightness(255)
214 	, _guiBrightness(255)
215 	, _screenWidth(320)
216 	, _screenHeight(200)
217 	, _textColor(kColorBlack)
218 	, _textCursorX(0)
219 	, _textCursorY(0)
220 	, _messageShown(false) {
221 
222 	changeCursor(ResourceManager::kCursorNormal);
223 }
224 
getScreenWidth() const225 int Screen::getScreenWidth() const {
226 	return _screenWidth;
227 }
228 
getScreenHeight() const229 int Screen::getScreenHeight() const {
230 	return _screenHeight;
231 }
232 
getGuiBrightness() const233 int Screen::getGuiBrightness() const {
234 	return _guiBrightness;
235 }
236 
setViewportBrightness(int brightness)237 void Screen::setViewportBrightness(int brightness) {
238 	_viewportBrightness = brightness;
239 }
240 
getViewportBrightness() const241 int Screen::getViewportBrightness() const {
242 	return _viewportBrightness;
243 }
244 
setGuiBrightness(int brightness)245 void Screen::setGuiBrightness(int brightness) {
246 	_guiBrightness = brightness;
247 }
248 
getCurrentImage()249 MSNImage *Screen::getCurrentImage() {
250 	return _currentImage;
251 }
252 
getImageInfo(ImageId id) const253 const Screen::ImageInfo *Screen::getImageInfo(ImageId id) const {
254 	return &imageInfo[(int)id];
255 }
256 
isMessageShown() const257 bool Screen::isMessageShown() const {
258 	return _messageShown;
259 }
260 
getTextCursorPos()261 Common::Point Screen::getTextCursorPos() {
262 	return Common::Point(_textCursorX, _textCursorY);
263 }
264 
setTextCursorPos(int x,int y)265 void Screen::setTextCursorPos(int x, int y) {
266 	_textCursorX = x;
267 	_textCursorY = y;
268 }
269 
getTextCursorColor()270 byte Screen::getTextCursorColor() {
271 	return _textColor;
272 }
273 
setTextCursorColor(byte color)274 void Screen::setTextCursorColor(byte color) {
275 	_textColor = color;
276 }
277 
renderMessage(int stringId,MessagePosition position,Common::String var1,Common::String var2)278 void Screen::renderMessage(int stringId, MessagePosition position,
279 						   Common::String var1, Common::String var2) {
280 	Common::String text = _vm->getGameString(stringId);
281 
282 	if (!var1.empty()) {
283 		if (!var2.empty())
284 			text = Common::String::format(text.c_str(), var1.c_str(), var2.c_str());
285 		else
286 			text = Common::String::format(text.c_str(), var1.c_str());
287 	}
288 
289 	renderMessage(text, position);
290 }
291 
renderMessage(const Common::String & text,MessagePosition position)292 void Screen::renderMessage(const Common::String &text, MessagePosition position) {
293 	if (!text.empty())
294 		renderMessage(text.c_str(), position);
295 }
296 
renderText(const uint16 character)297 void Screen::renderText(const uint16 character) {
298 	char text[2];
299 	text[0] = character & 0xFF;
300 	text[1] = 0;
301 	renderText(text, _textCursorX, _textCursorY, _textColor);
302 }
303 
renderText(const char * text)304 void Screen::renderText(const char *text) {
305 	renderText(text, _textCursorX, _textCursorY, _textColor);
306 }
307 
renderText(int stringId)308 void Screen::renderText(int stringId) {
309 	renderText(_vm->getGameString(stringId));
310 }
311 
renderText(const Common::String & text)312 void Screen::renderText(const Common::String &text) {
313 	if (!text.empty())
314 		renderText(text.c_str());
315 }
316 
renderText(const GuiElement & guiElement)317 void Screen::renderText(const GuiElement &guiElement) {
318 	renderText(guiElement.getText(), guiElement.getTextPos().x,
319 			   guiElement.getTextPos().y, guiElement.getTextColor());
320 }
321 
renderText(const uint16 character,int x,int y,byte color)322 void Screen::renderText(const uint16 character, int x, int y, byte color) {
323 	char text[2];
324 	text[0] = character & 0xFF;
325 	text[1] = 0;
326 	renderText(text, x, y, color);
327 }
328 
renderText(const char * text,int x,int y,byte color)329 void Screen::renderText(const char *text, int x, int y, byte color) {
330 	Graphics::Surface *screen = _vm->_system->lockScreen();
331 	byte *cursor = static_cast<byte *>(screen->getBasePtr(x, y));
332 	const byte *basePtr = cursor;
333 
334 	byte c;
335 	while ((c = *text++) != '\0') {
336 		if (c < 32) {
337 			continue;
338 		} else if (c == 225) {
339 			c = 128;
340 		}
341 
342 		for (uint i = 0; i < 5; ++i) {
343 			if (font[c - 32][i] == 0xff) {
344 				break;
345 			}
346 
347 			byte *ascentLine = cursor;
348 			for (byte j = font[c - 32][i]; j != 0; j >>= 1) {
349 				if (j & 1) {
350 					*cursor = color;
351 				}
352 				cursor += kScreenWidth;
353 			}
354 			cursor = ++ascentLine;
355 		}
356 		++cursor;
357 	}
358 	_vm->_system->unlockScreen();
359 
360 	uint numChars = cursor - basePtr;
361 	uint absPosition = y * kScreenWidth + x + numChars;
362 	_textCursorX = absPosition % kScreenWidth;
363 	_textCursorY = absPosition / kScreenWidth;
364 	_textColor = color;
365 }
366 
renderText(const Common::String & text,int x,int y,byte color)367 void Screen::renderText(const Common::String &text, int x, int y, byte color) {
368 	if (!text.empty())
369 		renderText(text.c_str(), x, y, color);
370 }
371 
renderText(int stringId,int x,int y,byte color)372 void Screen::renderText(int stringId, int x, int y, byte color) {
373 	renderText(_vm->getGameString(stringId), x, y, color);
374 }
375 
renderImageSection(const MSNImage * image,int section,bool invert)376 void Screen::renderImageSection(const MSNImage *image, int section, bool invert) {
377 	// Note: inverting means we are removing the section. So we should get the rect for that
378 	// section but draw the background (section 0) instead.
379 	if (section > image->_numSections - 1)
380 		return;
381 
382 	Common::Rect sectionRect(image->_section[section].x1,
383 							 image->_section[section].y1,
384 							 image->_section[section].x2 + 1,
385 							 image->_section[section].y2 + 1);
386 	bool bigImage = false;
387 	if (_vm->_MSPart == 1)
388 		bigImage = image->_filenumber == 1 || image->_filenumber == 2;
389 	else if (_vm->_MSPart == 2)
390 		bigImage = image->_filenumber == 38;
391 	if (bigImage) {
392 		sectionRect.setWidth(640);
393 		sectionRect.setHeight(480);
394 		if (_screenWidth != 640) {
395 			_screenWidth = 640;
396 			_screenHeight = 480;
397 			initGraphics(_screenWidth, _screenHeight);
398 		}
399 	} else {
400 		if (_screenWidth != 320) {
401 			_screenWidth = 320;
402 			_screenHeight = 200;
403 			initGraphics(_screenWidth, _screenHeight);
404 		}
405 	}
406 
407 	uint offset = 0;
408 	int pitch = sectionRect.width();
409 	if (invert) {
410 		pitch = image->_pitch;
411 		offset = image->_section[section].y1 * pitch +
412 				 image->_section[section].x1;
413 		section = 0;
414 	}
415 
416 	void *pixels = image->_sectionSurfaces[section]->getPixels();
417 	_vm->_system->copyRectToScreen(static_cast<const byte *>(pixels) + offset,
418 								   pitch, sectionRect.left, sectionRect.top,
419 								   sectionRect.width(), sectionRect.height());
420 }
421 
renderImage(ImageId id,bool removeImage)422 void Screen::renderImage(ImageId id, bool removeImage) {
423 	ImageInfo info = imageInfo[id];
424 	const MSNImage *image = _resMan->getImage(info.filenumber);
425 
426 	if (_currentImage != image)
427 		setCurrentImage(info.filenumber);
428 
429 	do {
430 		renderImageSection(image, info.section, removeImage);
431 		info.section = image->_section[info.section].next;
432 	} while (info.section != 0);
433 }
434 
renderImage(int section)435 void Screen::renderImage(int section) {
436 	bool removeImage = false;
437 	if (section > kSectionInvert) {
438 		removeImage = true;
439 		section -= kSectionInvert;
440 	}
441 
442 	if (!_currentImage || section >= kMaxSection)
443 		return;
444 
445 	do {
446 		renderImageSection(_currentImage, section, removeImage);
447 		section = _currentImage->_section[section].next;
448 	} while (section != 0);
449 }
450 
setCurrentImage(int filenumber)451 bool Screen::setCurrentImage(int filenumber) {
452 	_currentImage = _resMan->getImage(filenumber);
453 	_vm->_system->getPaletteManager()->setPalette(_currentImage->getPalette(), 16, 239);
454 	paletteBrightness();
455 
456 	return true;
457 }
458 
saveScreen(int x,int y,int width,int height)459 void Screen::saveScreen(int x, int y, int width, int height) {
460 	_screenBuffer.push(x, y, width, height);
461 }
462 
saveScreen(const GuiElement & guiElement)463 void Screen::saveScreen(const GuiElement &guiElement) {
464 	saveScreen(guiElement.left, guiElement.top, guiElement.width(), guiElement.height());
465 }
466 
restoreScreen()467 void Screen::restoreScreen() {
468 	_screenBuffer.restore();
469 }
470 
renderRoom(Room & room)471 void Screen::renderRoom(Room &room) {
472 	if (room.getId() == INTRO1 || room.getId() == INTRO2)
473 		return;
474 
475 	if (setCurrentImage(room.getFileNumber())) {
476 		for (int i = 0; i < _currentImage->_numSections; ++i) {
477 			int section = i;
478 			if (room.isSectionVisible(section)) {
479 				do {
480 					renderImageSection(_currentImage, section, false);
481 					section = _currentImage->_section[section].next;
482 				} while (section != 0);
483 			}
484 		}
485 	}
486 }
487 
textWidth(const uint16 key)488 int Screen::textWidth(const uint16 key) {
489 	char text[2];
490 	text[0] = key & 0xFF;
491 	text[1] = 0;
492 	return textWidth(text);
493 }
494 
textWidth(const char * text)495 int Screen::textWidth(const char *text) {
496 	int charWidth = 0;
497 	while (*text != '\0' && *text != '\1') {
498 		byte c = *text++;
499 		if (c < 32 || c == 155) {
500 			// 155 is used for looping in Marquee text and is not used otherwise
501 			// (it is beyond the end of the font).
502 			continue;
503 		} else if (c == 225) {
504 			c = 35;
505 		}
506 
507 		for (uint i = 0; i < 5; ++i) {
508 			if (font[c - 32][i] == 0xff) {
509 				break;
510 			}
511 			++charWidth;
512 		}
513 		++charWidth;
514 	}
515 
516 	return charWidth;
517 }
518 
textWidth(const Common::String & text)519 int Screen::textWidth(const Common::String &text) {
520 	return Screen::textWidth(text.c_str());
521 }
522 
renderMessage(const char * text,MessagePosition position,int positionX,int positionY)523 void Screen::renderMessage(const char *text, MessagePosition position, int positionX, int positionY) {
524 	Common::String t(text);
525 	char *row[20];
526 	Common::String::iterator p = t.begin();
527 	uint numRows = 0;
528 	int rowWidthMax = 0;
529 	int x = 0;
530 	int y = 0;
531 	byte textColor = 0;
532 
533 	while (*p != '\0') {
534 		row[numRows] = p;
535 		++numRows;
536 		while ((*p != '\0') && (*p != '|')) {
537 			++p;
538 		}
539 		if (*p == '|') {
540 			*p = '\0';
541 			++p;
542 		}
543 	}
544 	for (uint i = 0; i < numRows; ++i) {
545 		int rowWidth = textWidth(row[i]);
546 		if (rowWidth > rowWidthMax)
547 			rowWidthMax = rowWidth;
548 	}
549 
550 	switch (position) {
551 	case kMessageNormal:
552 	default:
553 		x = 160 - rowWidthMax / 2;
554 		textColor = kColorWhite99;
555 		break;
556 	case kMessageTop:
557 		x = 160 - rowWidthMax / 2;
558 		textColor = kColorLightYellow;
559 		break;
560 	case kMessageCenter:
561 		x = 160 - rowWidthMax / 2;
562 		textColor = kColorLightRed;
563 		break;
564 	case kMessageLeft:
565 		x = 3;
566 		textColor = kColorLightYellow;
567 		break;
568 	case kMessageRight:
569 		x = 317 - rowWidthMax;
570 		textColor = kColorLightGreen;
571 		break;
572 	}
573 
574 	if (position == kMessageNormal) {
575 		y = 70 - ((numRows * 9) / 2);
576 	} else if (position == kMessageTop) {
577 		y = 5;
578 	} else {
579 		y = 142;
580 	}
581 
582 	if (positionX != -1 && positionY != -1) {
583 		x = positionX;
584 		y = positionY;
585 	}
586 
587 	int message_columns = x - 3;
588 	int message_rows = y - 3;
589 	int message_width = rowWidthMax + 6;
590 	int message_height = numRows * 9 + 5;
591 	saveScreen(message_columns, message_rows, message_width, message_height);
592 	renderBox(message_columns, message_rows, message_width, message_height, kColorWhite35);
593 	for (uint i = 0; i < numRows; ++i) {
594 		renderText(row[i], x, y, textColor);
595 		y += 9;
596 	}
597 
598 	_messageShown = true;
599 }
600 
removeMessage()601 void Screen::removeMessage() {
602 	if (_messageShown) {
603 		restoreScreen();
604 		_messageShown = false;
605 	}
606 }
607 
renderBox(int x,int y,int width,int height,byte color)608 void Screen::renderBox(int x, int y, int width, int height, byte color) {
609 	Graphics::Surface *screen = _vm->_system->lockScreen();
610 	screen->fillRect(Common::Rect(x, y, x + width, y + height), color);
611 	_vm->_system->unlockScreen();
612 }
613 
renderBox(const GuiElement & guiElement)614 void Screen::renderBox(const GuiElement &guiElement) {
615 	renderBox(guiElement.left, guiElement.top, guiElement.width(),
616 			  guiElement.height(), guiElement.getBackgroundColor());
617 }
618 
initPalette()619 void Screen::initPalette() {
620 	g_system->getPaletteManager()->setPalette(initVGAPalette, 0, 256);
621 }
622 
paletteBrightness()623 void Screen::paletteBrightness() {
624 	byte palette[768];
625 
626 	_vm->_system->getPaletteManager()->grabPalette(palette, 0, 255);
627 	for (uint i = 0; i < 48; ++i) {
628 		palette[i] = (initVGAPalette[i] * _guiBrightness) >> 8;
629 	}
630 	for (uint i = 0; i < 717; ++i) {
631 		const byte *imagePalette;
632 		if (_currentImage && _currentImage->getPalette()) {
633 			imagePalette = _currentImage->getPalette();
634 		} else {
635 			imagePalette = palette + 48;
636 		}
637 		palette[i + 48] = (imagePalette[i] * _viewportBrightness) >> 8;
638 	}
639 	_vm->_system->getPaletteManager()->setPalette(palette, 0, 255);
640 }
641 
paletteFadeOut(int minBrightness)642 void Screen::paletteFadeOut(int minBrightness) {
643 	while (_guiBrightness > minBrightness + 10) {
644 		_guiBrightness -= 10;
645 		if (_viewportBrightness > _guiBrightness)
646 			_viewportBrightness = _guiBrightness;
647 		paletteBrightness();
648 		_vm->_system->updateScreen();
649 		_vm->_system->delayMillis(_vm->_delay);
650 	}
651 	_guiBrightness = minBrightness;
652 	_viewportBrightness = minBrightness;
653 	paletteBrightness();
654 	_vm->_system->updateScreen();
655 }
656 
paletteFadeIn(int maxViewportBrightness)657 void Screen::paletteFadeIn(int maxViewportBrightness) {
658 	while (_guiBrightness < 245) {
659 		if (_viewportBrightness < maxViewportBrightness)
660 			_viewportBrightness += 10;
661 		_guiBrightness += 10;
662 		paletteBrightness();
663 		_vm->_system->updateScreen();
664 		_vm->_system->delayMillis(_vm->_delay);
665 	}
666 	_guiBrightness = 255;
667 	_viewportBrightness = maxViewportBrightness;
668 	paletteBrightness();
669 	_vm->_system->updateScreen();
670 }
671 
setColor63(byte value)672 void Screen::setColor63(byte value) {
673 	byte color[3] = {value, value, value};
674 	_vm->_system->getPaletteManager()->setPalette(color, 63, 1);
675 }
676 
changeCursor(ResourceManager::CursorId id)677 void Screen::changeCursor(ResourceManager::CursorId id) {
678 	CursorMan.replaceCursor(_resMan->getCursor(id),
679 							16, 16, 0, 0, kColorCursorTransparent);
680 	CursorMan.replaceCursorPalette(initVGAPalette, 0, 16);
681 	CursorMan.showMouse(true);
682 }
683 
684 
685 }
686