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  * Additional copyright for this file:
8  * Copyright (C) 1994-1998 Revolution Software Ltd.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24 
25 // ---------------------------------------------------------------------------
26 // BUILD_DISPLAY.CPP	like the old spr_engi but slightly more aptly named
27 // ---------------------------------------------------------------------------
28 
29 
30 #include "common/system.h"
31 #include "common/events.h"
32 #include "common/textconsole.h"
33 
34 #include "sword2/sword2.h"
35 #include "sword2/defs.h"
36 #include "sword2/header.h"
37 #include "sword2/console.h"
38 #include "sword2/logic.h"
39 #include "sword2/maketext.h"
40 #include "sword2/mouse.h"
41 #include "sword2/object.h"
42 #include "sword2/resman.h"
43 #include "sword2/screen.h"
44 #include "sword2/sound.h"
45 
46 namespace Sword2 {
47 
Screen(Sword2Engine * vm,int16 width,int16 height)48 Screen::Screen(Sword2Engine *vm, int16 width, int16 height) {
49 	_vm = vm;
50 
51 	_dirtyGrid = _buffer = NULL;
52 
53 	_screenWide = width;
54 	_screenDeep = height;
55 
56 	_gridWide = width / CELLWIDE;
57 	_gridDeep = height / CELLDEEP;
58 
59 	if ((width % CELLWIDE) || (height % CELLDEEP))
60 		error("Bad cell size");
61 
62 	_dirtyGrid = (byte *)calloc(_gridWide, _gridDeep);
63 	if (!_dirtyGrid)
64 		error("Could not initialize dirty grid");
65 
66 	_buffer = (byte *)malloc(width * height);
67 	if (!_buffer)
68 		error("Could not initialize display");
69 
70 	for (int i = 0; i < ARRAYSIZE(_blockSurfaces); i++)
71 		_blockSurfaces[i] = NULL;
72 
73 	_lightMask = NULL;
74 	_needFullRedraw = false;
75 
76 	memset(&_thisScreen, 0, sizeof(_thisScreen));
77 
78 	_fps = 0;
79 	_frameCount = 0;
80 	_cycleTime = 0;
81 
82 	_lastPaletteRes = 0;
83 
84 	_scrollFraction = 16;
85 
86 	_largestLayerArea = 0;
87 	_largestSpriteArea = 0;
88 
89 	strcpy(_largestLayerInfo,  "largest layer:  none registered");
90 	strcpy(_largestSpriteInfo, "largest sprite: none registered");
91 
92 	_fadeStatus = RDFADE_NONE;
93 	_renderAverageTime = 60;
94 
95 	_layer = 0;
96 
97 	_dimPalette = false;
98 
99 	_pauseTicks = 0;
100 	_pauseStartTick = 0;
101 
102 	// Clean the cache for PSX version SCREENS.CLU
103 	_psxScrCache[0] = NULL;
104 	_psxScrCache[1] = NULL;
105 	_psxScrCache[2] = NULL;
106 	_psxCacheEnabled[0] = true;
107 	_psxCacheEnabled[1] = true;
108 	_psxCacheEnabled[2] = true;
109 }
110 
~Screen()111 Screen::~Screen() {
112 	flushPsxScrCache();
113 	free(_buffer);
114 	free(_dirtyGrid);
115 	closeBackgroundLayer();
116 	free(_lightMask);
117 }
118 
getTick()119 uint32 Screen::getTick() {
120 	return _vm->getMillis() - _pauseTicks;
121 }
122 
pauseScreen(bool pause)123 void Screen::pauseScreen(bool pause) {
124 	if (pause) {
125 		_pauseStartTick = _vm->_system->getMillis();
126 	} else {
127 		_pauseTicks += (_vm->_system->getMillis() - _pauseStartTick);
128 	}
129 }
130 
131 /**
132  * @return the graphics detail setting
133  */
134 
getRenderLevel()135 int8 Screen::getRenderLevel() {
136 	return _renderLevel;
137 }
138 
setRenderLevel(int8 level)139 void Screen::setRenderLevel(int8 level) {
140 	_renderLevel = level;
141 
142 	switch (_renderLevel) {
143 	case 0:
144 		// Lowest setting: no fancy stuff
145 		_renderCaps = 0;
146 		break;
147 	case 1:
148 		// Medium-low setting: transparency-blending
149 		_renderCaps = RDBLTFX_SPRITEBLEND;
150 		break;
151 	case 2:
152 		// Medium-high setting: transparency-blending + shading
153 		_renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND;
154 		break;
155 	case 3:
156 		// Highest setting: transparency-blending + shading +
157 		// edge-blending + improved stretching
158 		_renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND | RDBLTFX_EDGEBLEND;
159 		break;
160 	}
161 }
162 
163 /**
164  * Tell updateDisplay() that the scene needs to be completely updated.
165  */
166 
setNeedFullRedraw()167 void Screen::setNeedFullRedraw() {
168 	_needFullRedraw = true;
169 }
170 
171 /**
172  * Mark an area of the screen as dirty, first generation.
173  */
174 
markAsDirty(int16 x0,int16 y0,int16 x1,int16 y1)175 void Screen::markAsDirty(int16 x0, int16 y0, int16 x1, int16 y1) {
176 	int16 gridX0 = x0 / CELLWIDE;
177 	int16 gridY0 = y0 / CELLDEEP;
178 	int16 gridX1 = x1 / CELLWIDE;
179 	int16 gridY1 = y1 / CELLDEEP;
180 
181 	for (int16 i = gridY0; i <= gridY1; i++)
182 		for (int16 j = gridX0; j <= gridX1; j++)
183 			_dirtyGrid[i * _gridWide + j] = 2;
184 }
185 
186 /**
187  * This function has two purposes: It redraws the scene, and it handles input
188  * events, palette fading, etc. It should be called at a high rate (> 20 per
189  * second), but the scene is usually only redrawn about 12 times per second,
190  * except when then screen is scrolling.
191  *
192  * @param redrawScene If true, redraw the scene.
193  */
194 
updateDisplay(bool redrawScene)195 void Screen::updateDisplay(bool redrawScene) {
196 	_vm->parseInputEvents();
197 	fadeServer();
198 
199 	if (redrawScene) {
200 		int i;
201 
202 		// Note that the entire scene is always rendered, which is less
203 		// than optimal, but at least we can try to be intelligent
204 		// about updating the screen afterwards.
205 
206 		if (_needFullRedraw) {
207 			// Update the entire screen. This is necessary when
208 			// scrolling, fading, etc.
209 
210 			_vm->_system->copyRectToScreen(_buffer + MENUDEEP * _screenWide, _screenWide, 0, MENUDEEP, _screenWide, _screenDeep - 2 * MENUDEEP);
211 			_needFullRedraw = false;
212 		} else {
213 			// Update only the dirty areas of the screen
214 
215 			int j, x, y;
216 			int stripWide;
217 
218 			for (i = 0; i < _gridDeep; i++) {
219 				stripWide = 0;
220 
221 				for (j = 0; j < _gridWide; j++) {
222 					if (_dirtyGrid[i * _gridWide + j]) {
223 						stripWide++;
224 					} else if (stripWide) {
225 						x = CELLWIDE * (j - stripWide);
226 						y = CELLDEEP * i;
227 						_vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
228 						stripWide = 0;
229 					}
230 				}
231 
232 				if (stripWide) {
233 					x = CELLWIDE * (j - stripWide);
234 					y = CELLDEEP * i;
235 					_vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
236 					stripWide = 0;
237 				}
238 			}
239 		}
240 
241 		// Age the dirty cells one generation. This way we keep track
242 		// of both the cells that were updated this time, and the ones
243 		// that were updated the last time.
244 
245 		for (i = 0; i < _gridWide * _gridDeep; i++)
246 			_dirtyGrid[i] >>= 1;
247 	}
248 
249 	// We always need to update because of fades, menu animations, etc.
250 	_vm->_system->updateScreen();
251 }
252 
253 /**
254  * Fill the screen buffer with palette color zero. Note that it does not
255  * touch the menu areas of the screen.
256  */
257 
clearScene()258 void Screen::clearScene() {
259 	memset(_buffer + MENUDEEP * _screenWide, 0, _screenWide * RENDERDEEP);
260 	_needFullRedraw = true;
261 }
262 
buildDisplay()263 void Screen::buildDisplay() {
264 	if (_thisScreen.new_palette) {
265 		// start the layer palette fading up
266 		startNewPalette();
267 
268 		// should be reset to zero at start of each screen change
269 		_largestLayerArea = 0;
270 		_largestSpriteArea = 0;
271 	}
272 
273 	// Does this ever happen?
274 	if (!_thisScreen.background_layer_id)
275 		return;
276 
277 	// there is a valid screen to run
278 
279 	setScrollTarget(_thisScreen.scroll_offset_x, _thisScreen.scroll_offset_y);
280 	_vm->_mouse->animateMouse();
281 	startRenderCycle();
282 
283 	byte *file = _vm->_resman->openResource(_thisScreen.background_layer_id);
284 
285 	MultiScreenHeader screenLayerTable;
286 	memset(&screenLayerTable, 0, sizeof(screenLayerTable));
287 
288 	if (!Sword2Engine::isPsx()) // On PSX version, there would be nothing to read here
289 		screenLayerTable.read(file + ResHeader::size());
290 
291 	// Render at least one frame, but if the screen is scrolling, and if
292 	// there is time left, we will render extra frames to smooth out the
293 	// scrolling.
294 
295 	do {
296 		// first background parallax + related anims
297 		if (Sword2Engine::isPsx() || screenLayerTable.bg_parallax[0]) { // No need to check on PSX version
298 			renderParallax(_vm->fetchBackgroundParallaxLayer(file, 0), 0);
299 			drawBackPar0Frames();
300 		}
301 
302 		// second background parallax + related anims
303 		if (!Sword2Engine::isPsx() && screenLayerTable.bg_parallax[1]) { // Nothing here in PSX version
304 			renderParallax(_vm->fetchBackgroundParallaxLayer(file, 1), 1);
305 			drawBackPar1Frames();
306 		}
307 
308 		// normal backround layer (just the one!)
309 		renderParallax(_vm->fetchBackgroundLayer(file), 2);
310 
311 		// sprites & layers
312 		drawBackFrames();	// background sprites
313 		drawSortFrames(file);	// sorted sprites & layers
314 		drawForeFrames();	// foreground sprites
315 
316 		// first foreground parallax + related anims
317 
318 		if (Sword2Engine::isPsx() || screenLayerTable.fg_parallax[0]) {
319 			renderParallax(_vm->fetchForegroundParallaxLayer(file, 0), 3);
320 			drawForePar0Frames();
321 		}
322 
323 		// second foreground parallax + related anims
324 
325 		if (!Sword2Engine::isPsx() && screenLayerTable.fg_parallax[1]) {
326 			renderParallax(_vm->fetchForegroundParallaxLayer(file, 1), 4);
327 			drawForePar1Frames();
328 		}
329 
330 		_vm->_debugger->drawDebugGraphics();
331 		_vm->_fontRenderer->printTextBlocs();
332 		_vm->_mouse->processMenu();
333 
334 		updateDisplay();
335 
336 		_frameCount++;
337 		if (getTick() > _cycleTime) {
338 			_fps = _frameCount;
339 			_frameCount = 0;
340 			_cycleTime = getTick() + 1000;
341 		}
342 	} while (!endRenderCycle());
343 
344 	_vm->_resman->closeResource(_thisScreen.background_layer_id);
345 
346 }
347 
348 /**
349  * Fades down and displays a message on the screen.
350  * @param text The message
351  * @param time The number of seconds to display the message, or 0 to display it
352  *             until the user clicks the mouse or presses a key.
353  */
354 
displayMsg(byte * text,int time)355 void Screen::displayMsg(byte *text, int time) {
356 	byte pal[256 * 3];
357 	byte oldPal[256 * 3];
358 
359 	debug(2, "DisplayMsg: %s", text);
360 
361 	if (getFadeStatus() != RDFADE_BLACK) {
362 		fadeDown();
363 		waitForFade();
364 	}
365 
366 	_vm->_mouse->setMouse(0);
367 	_vm->_mouse->setLuggage(0);
368 	_vm->_mouse->closeMenuImmediately();
369 
370 	clearScene();
371 
372 	byte *text_spr = _vm->_fontRenderer->makeTextSprite(text, 640, 187, _vm->_speechFontId);
373 
374 	FrameHeader frame;
375 
376 	frame.read(text_spr);
377 
378 	SpriteInfo spriteInfo;
379 
380 	spriteInfo.x = _screenWide / 2 - frame.width / 2;
381 	if (!time)
382 		spriteInfo.y = _screenDeep / 2 - frame.height / 2 - MENUDEEP;
383 	else
384 		spriteInfo.y = 400 - frame.height;
385 	spriteInfo.w = frame.width;
386 	spriteInfo.h = frame.height;
387 	spriteInfo.scale = 0;
388 	spriteInfo.scaledWidth = 0;
389 	spriteInfo.scaledHeight	= 0;
390 	spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
391 	spriteInfo.blend = 0;
392 	spriteInfo.data = text_spr + FrameHeader::size();
393 	spriteInfo.colorTable = 0;
394 	spriteInfo.isText = true;
395 
396 	uint32 rv = drawSprite(&spriteInfo);
397 	if (rv)
398 		error("Driver Error %.8x (in DisplayMsg)", rv);
399 
400 	memcpy(oldPal, _palette, sizeof(oldPal));
401 	memset(pal, 0, sizeof(pal));
402 
403 	pal[187 * 3 + 0] = 255;
404 	pal[187 * 3 + 1] = 255;
405 	pal[187 * 3 + 2] = 255;
406 
407 	setPalette(0, 256, pal, RDPAL_FADE);
408 	fadeUp();
409 	free(text_spr);
410 	waitForFade();
411 
412 	if (time > 0) {
413 		uint32 targetTime = _vm->_system->getMillis() + (time * 1000);
414 		_vm->sleepUntil(targetTime);
415 	} else {
416 		while (!_vm->shouldQuit()) {
417 			MouseEvent *me = _vm->mouseEvent();
418 			if (me && (me->buttons & (RD_LEFTBUTTONDOWN | RD_RIGHTBUTTONDOWN)))
419 				break;
420 
421 			if (_vm->keyboardEvent())
422 				break;
423 
424 			updateDisplay();
425 			_vm->_system->delayMillis(50);
426 		}
427 	}
428 
429 	fadeDown();
430 	waitForFade();
431 	clearScene();
432 	setPalette(0, 256, oldPal, RDPAL_FADE);
433 	fadeUp();
434 }
435 
drawBackPar0Frames()436 void Screen::drawBackPar0Frames() {
437 	// frame attached to 1st background parallax
438 	for (uint i = 0; i < _curBgp0; i++)
439 		processImage(&_bgp0List[i]);
440 }
441 
drawBackPar1Frames()442 void Screen::drawBackPar1Frames() {
443 	// frame attached to 2nd background parallax
444 	for (uint i = 0; i < _curBgp1; i++)
445 		processImage(&_bgp1List[i]);
446 }
447 
drawBackFrames()448 void Screen::drawBackFrames() {
449 	// background sprite, fixed to main background
450 	for (uint i = 0; i < _curBack; i++)
451 		processImage(&_backList[i]);
452 }
453 
drawSortFrames(byte * file)454 void Screen::drawSortFrames(byte *file) {
455 	uint i, j;
456 
457 	// Sort the sort list. Used to be a separate function, but it was only
458 	// called once, right before calling drawSortFrames().
459 
460 	if (_curSort > 1) {
461 		for (i = 0; i < _curSort - 1; i++) {
462 			for (j = 0; j < _curSort - 1; j++) {
463 				if (_sortList[_sortOrder[j]].sort_y > _sortList[_sortOrder[j + 1]].sort_y) {
464 					SWAP(_sortOrder[j], _sortOrder[j + 1]);
465 				}
466 			}
467 		}
468 	}
469 
470 	// Draw the sorted frames - layers, shrinkers & normal flat sprites
471 
472 	for (i = 0; i < _curSort; i++) {
473 		if (_sortList[_sortOrder[i]].layer_number) {
474 			// it's a layer - minus 1 for true layer number
475 			// we need to know from the BuildUnit because the
476 			// layers will have been sorted in random order
477 			processLayer(file, _sortList[_sortOrder[i]].layer_number - 1);
478 		} else {
479 			// it's a sprite
480 			processImage(&_sortList[_sortOrder[i]]);
481 		}
482 	}
483 }
484 
drawForeFrames()485 void Screen::drawForeFrames() {
486 	// foreground sprite, fixed to main background
487 	for (uint i = 0; i < _curFore; i++)
488 		processImage(&_foreList[i]);
489 }
490 
drawForePar0Frames()491 void Screen::drawForePar0Frames() {
492 	// frame attached to 1st foreground parallax
493 	for (uint i = 0; i < _curFgp0; i++)
494 		processImage(&_fgp0List[i]);
495 }
496 
drawForePar1Frames()497 void Screen::drawForePar1Frames() {
498 	// frame attached to 2nd foreground parallax
499 	for (uint i = 0; i < _curFgp1; i++)
500 		processImage(&_fgp1List[i]);
501 }
502 
processLayer(byte * file,uint32 layer_number)503 void Screen::processLayer(byte *file, uint32 layer_number) {
504 
505 	LayerHeader layer_head;
506 
507 	layer_head.read(_vm->fetchLayerHeader(file, layer_number));
508 
509 	SpriteInfo spriteInfo;
510 
511 	spriteInfo.x = layer_head.x;
512 	spriteInfo.y = layer_head.y;
513 	spriteInfo.w = layer_head.width;
514 	spriteInfo.scale = 0;
515 	spriteInfo.scaledWidth = 0;
516 	spriteInfo.scaledHeight = 0;
517 	spriteInfo.h = layer_head.height;
518 	spriteInfo.isText = false;
519 
520 	// Layers are uncompressed in PSX version, RLE256 compressed
521 	// in PC version.
522 	if (Sword2Engine::isPsx()) {
523 		spriteInfo.type = RDSPR_TRANS | RDSPR_NOCOMPRESSION;
524 		spriteInfo.data = file + layer_head.offset;
525 	} else {
526 		spriteInfo.type = RDSPR_TRANS | RDSPR_RLE256FAST;
527 		spriteInfo.data = file + ResHeader::size() + layer_head.offset;
528 	}
529 
530 	spriteInfo.blend = 0;
531 	spriteInfo.colorTable = 0;
532 
533 	// check for largest layer for debug info
534 
535 	uint32 current_layer_area = layer_head.width * layer_head.height;
536 
537 	if (current_layer_area > _largestLayerArea) {
538 		_largestLayerArea = current_layer_area;
539 		sprintf(_largestLayerInfo,
540 			"largest layer:  %s layer(%d) is %dx%d",
541 			_vm->_resman->fetchName(_thisScreen.background_layer_id),
542 			layer_number, layer_head.width, layer_head.height);
543 	}
544 
545 	uint32 rv = drawSprite(&spriteInfo);
546 	if (rv)
547 		error("Driver Error %.8x in processLayer(%d)", rv, layer_number);
548 }
549 
processImage(BuildUnit * build_unit)550 void Screen::processImage(BuildUnit *build_unit) {
551 
552 	// We have some problematic animation frames in PSX demo (looks like there is missing data),
553 	// so we just skip them.
554 	if ( (Sword2Engine::isPsx() &&  _vm->_logic->readVar(DEMO)) &&
555 		 ((build_unit->anim_resource == 369 && build_unit->anim_pc == 0) ||
556 		 (build_unit->anim_resource == 296 && build_unit->anim_pc == 5)  ||
557 		 (build_unit->anim_resource == 534 && build_unit->anim_pc == 13)) )
558 		return;
559 
560 	byte *file = _vm->_resman->openResource(build_unit->anim_resource);
561 	byte *colTablePtr = NULL;
562 
563 	byte *frame = _vm->fetchFrameHeader(file, build_unit->anim_pc);
564 
565 	AnimHeader anim_head;
566 	CdtEntry cdt_entry;
567 	FrameHeader frame_head;
568 
569 	anim_head.read(_vm->fetchAnimHeader(file));
570 	cdt_entry.read(_vm->fetchCdtEntry(file, build_unit->anim_pc));
571 	frame_head.read(frame);
572 
573 	// so that 0-color is transparent
574 	uint32 spriteType = RDSPR_TRANS;
575 
576 	if (anim_head.blend)
577 		spriteType |= RDSPR_BLEND;
578 
579 	// if the frame is to be flipped (only really applicable to frames
580 	// using offsets)
581 	if (cdt_entry.frameType & FRAME_FLIPPED)
582 		spriteType |= RDSPR_FLIP;
583 
584 	if (cdt_entry.frameType & FRAME_256_FAST) {
585 		// scaling, shading & blending don't work with RLE256FAST
586 		// but the same compression can be decompressed using the
587 		// RLE256 routines!
588 
589 		// NOTE: If this restriction refers to drawSprite(), I don't
590 		// think we have it any more. But I'm not sure.
591 
592 		if (build_unit->scale || anim_head.blend || build_unit->shadingFlag)
593 			spriteType |= RDSPR_RLE256;
594 		else
595 			spriteType |= RDSPR_RLE256FAST;
596 	} else {
597 		switch (anim_head.runTimeComp) {
598 		case NONE:
599 			spriteType |= RDSPR_NOCOMPRESSION;
600 			break;
601 		case RLE256:
602 			spriteType |= RDSPR_RLE256;
603 			break;
604 		case RLE16:
605 			spriteType |= RDSPR_RLE16;
606 			// points to just after last cdt_entry, ie.
607 			// start of color table
608 			colTablePtr = _vm->fetchAnimHeader(file) + AnimHeader::size() + anim_head.noAnimFrames * CdtEntry::size();
609 			if (Sword2Engine::isPsx())
610 				colTablePtr++; // There is one additional byte to skip before the table in psx version
611 			break;
612 		}
613 	}
614 
615 	// if we want this frame to be affected by the shading mask,
616 	// add the status bit
617 	if (build_unit->shadingFlag)
618 		spriteType |= RDSPR_SHADOW;
619 
620 	SpriteInfo spriteInfo;
621 
622 	spriteInfo.x = build_unit->x;
623 	spriteInfo.y = build_unit->y;
624 	spriteInfo.w = frame_head.width;
625 	spriteInfo.h = frame_head.height;
626 	spriteInfo.scale = build_unit->scale;
627 	spriteInfo.scaledWidth = build_unit->scaled_width;
628 	spriteInfo.scaledHeight	= build_unit->scaled_height;
629 	spriteInfo.type = spriteType;
630 	spriteInfo.blend = anim_head.blend;
631 	// points to just after frame header, ie. start of sprite data
632 	spriteInfo.data = frame + FrameHeader::size();
633 	spriteInfo.colorTable = colTablePtr;
634 	spriteInfo.isText = false;
635 
636 	// check for largest layer for debug info
637 	uint32 current_sprite_area = frame_head.width * frame_head.height;
638 
639 	if (current_sprite_area > _largestSpriteArea) {
640 		_largestSpriteArea = current_sprite_area;
641 		sprintf(_largestSpriteInfo,
642 			"largest sprite: %s frame(%d) is %dx%d",
643 			_vm->_resman->fetchName(build_unit->anim_resource),
644 			build_unit->anim_pc,
645 			frame_head.width,
646 			frame_head.height);
647 	}
648 
649 	if (_vm->_logic->readVar(SYSTEM_TESTING_ANIMS)) { // see anims.cpp
650 		// bring the anim into the visible screen
651 		// but leave extra pixel at edge for box
652 		if (spriteInfo.x + spriteInfo.scaledWidth >= 639)
653 			spriteInfo.x = 639 - spriteInfo.scaledWidth;
654 
655 		if (spriteInfo.y + spriteInfo.scaledHeight >= 399)
656 			spriteInfo.y = 399 - spriteInfo.scaledHeight;
657 
658 		if (spriteInfo.x < 1)
659 			spriteInfo.x = 1;
660 
661 		if (spriteInfo.y < 1)
662 			spriteInfo.y = 1;
663 
664 		// create box to surround sprite - just outside sprite box
665 		_vm->_debugger->_rectX1 = spriteInfo.x - 1;
666 		_vm->_debugger->_rectY1 = spriteInfo.y - 1;
667 		_vm->_debugger->_rectX2 = spriteInfo.x + spriteInfo.scaledWidth;
668 		_vm->_debugger->_rectY2 = spriteInfo.y + spriteInfo.scaledHeight;
669 	}
670 
671 	uint32 rv = drawSprite(&spriteInfo);
672 	if (rv) {
673 		error("Driver Error %.8x with sprite %s (%d, %d) in processImage",
674 			rv,
675 			_vm->_resman->fetchName(build_unit->anim_resource),
676 			build_unit->anim_resource, build_unit->anim_pc);
677 	}
678 
679 	// release the anim resource
680 	_vm->_resman->closeResource(build_unit->anim_resource);
681 }
682 
resetRenderLists()683 void Screen::resetRenderLists() {
684 	// reset the sort lists - do this before a logic loop
685 	// takes into account the fact that the start of the list is pre-built
686 	// with the special sortable layers
687 
688 	_curBgp0 = 0;
689 	_curBgp1 = 0;
690 	_curBack = 0;
691 	// beginning of sort list is setup with the special sort layers
692 	_curSort = _thisScreen.number_of_layers;
693 	_curFore = 0;
694 	_curFgp0 = 0;
695 	_curFgp1 = 0;
696 
697 	if (_curSort) {
698 		// there are some layers - so rebuild the sort order list
699 		for (uint i = 0; i < _curSort; i++)
700 			_sortOrder[i] = i;
701 	}
702 }
703 
registerFrame(byte * ob_mouse,byte * ob_graph,byte * ob_mega,BuildUnit * build_unit)704 void Screen::registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega, BuildUnit *build_unit) {
705 	ObjectGraphic obGraph(ob_graph);
706 	ObjectMega obMega(ob_mega);
707 
708 	assert(obGraph.getAnimResource());
709 
710 	byte *file = _vm->_resman->openResource(obGraph.getAnimResource());
711 
712 	AnimHeader anim_head;
713 	CdtEntry cdt_entry;
714 	FrameHeader frame_head;
715 
716 	anim_head.read(_vm->fetchAnimHeader(file));
717 	cdt_entry.read(_vm->fetchCdtEntry(file, obGraph.getAnimPc()));
718 	frame_head.read(_vm->fetchFrameHeader(file, obGraph.getAnimPc()));
719 
720 	// update player graphic details for on-screen debug info
721 	if (_vm->_logic->readVar(ID) == CUR_PLAYER_ID) {
722 		_vm->_debugger->_graphType = obGraph.getType();
723 		_vm->_debugger->_graphAnimRes = obGraph.getAnimResource();
724 		// counting 1st frame as 'frame 1'
725 		_vm->_debugger->_graphAnimPc = obGraph.getAnimPc() + 1;
726 		_vm->_debugger->_graphNoFrames = anim_head.noAnimFrames;
727 	}
728 
729 	// fill in the BuildUnit structure for this frame
730 
731 	build_unit->anim_resource = obGraph.getAnimResource();
732 	build_unit->anim_pc = obGraph.getAnimPc();
733 	build_unit->layer_number = 0;
734 
735 	// Affected by shading mask?
736 	if (obGraph.getType() & SHADED_SPRITE)
737 		build_unit->shadingFlag = true;
738 	else
739 		build_unit->shadingFlag = false;
740 
741 	// Check if this frame has offsets ie. this is a scalable mega frame
742 
743 	int scale = 0;
744 
745 	if (cdt_entry.frameType & FRAME_OFFSET) {
746 		scale = obMega.calcScale();
747 
748 		// calc final render coordinates (top-left of sprite), based
749 		// on feet coords & scaled offsets
750 
751 		// add scaled offsets to feet coords
752 		build_unit->x = obMega.getFeetX() + (cdt_entry.x * scale) / 256;
753 		build_unit->y = obMega.getFeetY() + (cdt_entry.y * scale) / 256;
754 
755 		// Work out new width and height. Always divide by 256 after
756 		// everything else, to maintain accurary
757 		build_unit->scaled_width = ((scale * frame_head.width) / 256);
758 		build_unit->scaled_height = ((scale * frame_head.height) / 256);
759 	} else {
760 		// It's a non-scaling anim. Get render coords for sprite, from cdt
761 		build_unit->x = cdt_entry.x;
762 		build_unit->y = cdt_entry.y;
763 
764 		// Get width and height
765 		build_unit->scaled_width = frame_head.width;
766 		build_unit->scaled_height = frame_head.height;
767 	}
768 
769 	// either 0 or required scale, depending on whether 'scale' computed
770 	build_unit->scale = scale;
771 
772 	// calc the bottom y-coord for sorting purposes
773 	build_unit->sort_y = build_unit->y + build_unit->scaled_height - 1;
774 
775 	if (ob_mouse) {
776 		// passed a mouse structure, so add to the _mouseList
777 		_vm->_mouse->registerMouse(ob_mouse, build_unit);
778 
779 	}
780 
781 	_vm->_resman->closeResource(obGraph.getAnimResource());
782 }
783 
registerFrame(byte * ob_mouse,byte * ob_graph,byte * ob_mega)784 void Screen::registerFrame(byte *ob_mouse, byte *ob_graph, byte *ob_mega) {
785 	ObjectGraphic obGraph(ob_graph);
786 
787 	// check low word for sprite type
788 	switch (obGraph.getType() & 0x0000ffff) {
789 	case BGP0_SPRITE:
790 		assert(_curBgp0 < MAX_bgp0_sprites);
791 		registerFrame(ob_mouse, ob_graph, ob_mega, &_bgp0List[_curBgp0]);
792 		_curBgp0++;
793 		break;
794 	case BGP1_SPRITE:
795 		assert(_curBgp1 < MAX_bgp1_sprites);
796 		registerFrame(ob_mouse, ob_graph, ob_mega, &_bgp1List[_curBgp1]);
797 		_curBgp1++;
798 		break;
799 	case BACK_SPRITE:
800 		assert(_curBack < MAX_back_sprites);
801 		registerFrame(ob_mouse, ob_graph, ob_mega, &_backList[_curBack]);
802 		_curBack++;
803 		break;
804 	case SORT_SPRITE:
805 		assert(_curSort < MAX_sort_sprites);
806 		_sortOrder[_curSort] = _curSort;
807 		registerFrame(ob_mouse, ob_graph, ob_mega, &_sortList[_curSort]);
808 		_curSort++;
809 		break;
810 	case FORE_SPRITE:
811 		assert(_curFore < MAX_fore_sprites);
812 		registerFrame(ob_mouse, ob_graph, ob_mega, &_foreList[_curFore]);
813 		_curFore++;
814 		break;
815 	case FGP0_SPRITE:
816 		assert(_curFgp0 < MAX_fgp0_sprites);
817 		registerFrame(ob_mouse, ob_graph, ob_mega, &_fgp0List[_curFgp0]);
818 		_curFgp0++;
819 		break;
820 	case FGP1_SPRITE:
821 		assert(_curFgp1 < MAX_fgp1_sprites);
822 		registerFrame(ob_mouse, ob_graph, ob_mega, &_fgp1List[_curFgp1]);
823 		_curFgp1++;
824 		break;
825 	default:
826 		// NO_SPRITE no registering!
827 		break;
828 	}
829 }
830 
831 // FIXME:
832 //
833 // The original credits used a different font. I think it's stored in the
834 // font.clu file, but I don't know how to interpret it.
835 //
836 // The original used the entire screen. This version cuts off the top and
837 // bottom of the screen, because that's where the menus would usually be.
838 //
839 // The original had some sort of smoke effect at the bottom of the screen.
840 
841 enum {
842 	LINE_LEFT,
843 	LINE_CENTER,
844 	LINE_RIGHT
845 };
846 
847 struct CreditsLine {
848 	Common::String str;
849 	byte type;
850 	int top;
851 	int height;
852 	byte *sprite;
853 
CreditsLineSword2::CreditsLine854 	CreditsLine() {
855 		sprite = NULL;
856 	}
857 
~CreditsLineSword2::CreditsLine858 	~CreditsLine() {
859 		free(sprite);
860 	}
861 };
862 
863 #define CREDITS_FONT_HEIGHT 25
864 #define CREDITS_LINE_SPACING 20
865 
rollCredits()866 void Screen::rollCredits() {
867 	uint32 loopingMusicId = _vm->_sound->getLoopingMusicId();
868 
869 	// Prepare for the credits by fading down, stoping the music, etc.
870 
871 	_vm->_mouse->setMouse(0);
872 
873 	_vm->_sound->muteFx(true);
874 	_vm->_sound->muteSpeech(true);
875 
876 	waitForFade();
877 	fadeDown();
878 	waitForFade();
879 
880 	_vm->_mouse->closeMenuImmediately();
881 
882 	// There are three files which I believe are involved in showing the
883 	// credits:
884 	//
885 	// credits.bmp  - The "Smacker" logo, stored as follows:
886 	//
887 	//     width     2 bytes, little endian
888 	//     height    2 bytes, little endian
889 	//     palette   3 * 256 bytes
890 	//     data      width * height bytes
891 	//
892 	//     Note that the maximum color component in the palette is 0x3F.
893 	//     This is the same resolution as the _paletteMatch table. I doubt
894 	//     that this is a coincidence, but let's use the image palette
895 	//     directly anyway, just to be safe.
896 	//
897 	// credits.clu  - The credits text (credits.txt in PSX version)
898 	//
899 	//     This is simply a text file with CRLF line endings.
900 	//     '^' is not shown, but used to mark the center of the line.
901 	//     '@' is used as a placeholder for the "Smacker" logo. At least
902 	//     when it appears alone.
903 	//     Remaining lines are centered.
904 	//     The German version also contains character code 9 for no
905 	//     apparent reason. We ignore them.
906 	//
907 	// fonts.clu    - The credits font?
908 	//
909 	//     FIXME: At this time I don't know how to interpret fonts.clu. For
910 	//     now, let's just the standard speech font instead.
911 
912 	SpriteInfo spriteInfo;
913 	Common::File f;
914 	int i;
915 
916 	spriteInfo.isText = false;
917 
918 	// Read the "Smacker" logo
919 
920 	uint16 logoWidth = 0;
921 	uint16 logoHeight = 0;
922 	byte *logoData = NULL;
923 	byte palette[256 * 3];
924 
925 	if (f.open("credits.bmp")) {
926 		logoWidth = f.readUint16LE();
927 		logoHeight = f.readUint16LE();
928 
929 		for (i = 0; i < 256; i++) {
930 			palette[i * 3 + 0] = f.readByte() << 2;
931 			palette[i * 3 + 1] = f.readByte() << 2;
932 			palette[i * 3 + 2] = f.readByte() << 2;
933 		}
934 
935 		logoData = (byte *)malloc(logoWidth * logoHeight);
936 
937 		f.read(logoData, logoWidth * logoHeight);
938 		f.close();
939 	} else {
940 		warning("Can't find credits.bmp");
941 		memset(palette, 0, sizeof(palette));
942 		palette[14 * 3 + 0] = 252;
943 		palette[14 * 3 + 1] = 252;
944 		palette[14 * 3 + 2] = 252;
945 	}
946 
947 	setPalette(0, 256, palette, RDPAL_INSTANT);
948 
949 	// Read the credits text
950 
951 	Common::Array<CreditsLine *> creditsLines;
952 
953 	int lineCount = 0;
954 	int lineTop = 400;
955 	int paragraphStart = 0;
956 	bool hasCenterMark = false;
957 
958 	if (Sword2Engine::isPsx()) {
959 		if (!f.open("credits.txt")) {
960 			warning("Can't find credits.txt");
961 
962 			free(logoData);
963 			return;
964 		}
965 	} else {
966 		if (!f.open("credits.clu")) {
967 			warning("Can't find credits.clu");
968 
969 			free(logoData);
970 			return;
971 		}
972 	}
973 
974 	while (1) {
975 		char buffer[80];
976 		char *line = f.readLine(buffer, sizeof(buffer));
977 
978 		if (line) {
979 			// Replace invalid character codes prevent the 'dud'
980 			// symbol from showing up in the credits.
981 
982 			for (byte *ptr = (byte *)line; *ptr; ptr++) {
983 				switch (*ptr) {
984 				case 9:
985 					// The German credits contain these.
986 					// Convert them to spaces.
987 					*ptr = 32;
988 					break;
989 				case 10:
990 					// LF is treated as end of line.
991 					*ptr = 0;
992 					break;
993 				case 170:
994 					// The Spanish credits contain these.
995 					// Convert them to periods.
996 					*ptr = '.';
997 				default:
998 					break;
999 				}
1000 			}
1001 		}
1002 
1003 		if (!line || *line == 0) {
1004 			if (!hasCenterMark) {
1005 				for (i = paragraphStart; i < lineCount; i++)
1006 					creditsLines[i]->type = LINE_CENTER;
1007 			}
1008 			paragraphStart = lineCount;
1009 			hasCenterMark = false;
1010 			if (paragraphStart == lineCount)
1011 				lineTop += CREDITS_LINE_SPACING;
1012 
1013 			if (!line)
1014 				break;
1015 
1016 			continue;
1017 		}
1018 
1019 		char *center_mark = strchr(line, '^');
1020 
1021 		if (center_mark) {
1022 			// The current paragraph has at least one center mark.
1023 			hasCenterMark = true;
1024 
1025 			if (center_mark != line) {
1026 				creditsLines.push_back(new CreditsLine);
1027 
1028 				// The center mark is somewhere inside the
1029 				// line. Split it into left and right side.
1030 				*center_mark = 0;
1031 
1032 				creditsLines[lineCount]->top = lineTop;
1033 				creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
1034 				creditsLines[lineCount]->type = LINE_LEFT;
1035 				creditsLines[lineCount]->str = line;
1036 
1037 				lineCount++;
1038 				*center_mark = '^';
1039 			}
1040 
1041 			line = center_mark;
1042 		}
1043 
1044 		creditsLines.push_back(new CreditsLine);
1045 
1046 		creditsLines[lineCount]->top = lineTop;
1047 
1048 		if (*line == '^') {
1049 			creditsLines[lineCount]->type = LINE_RIGHT;
1050 			line++;
1051 		} else
1052 			creditsLines[lineCount]->type = LINE_LEFT;
1053 
1054 		if (strcmp(line, "@") == 0) {
1055 			creditsLines[lineCount]->height = logoHeight;
1056 			lineTop += logoHeight;
1057 		} else {
1058 			creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT;
1059 			lineTop += CREDITS_LINE_SPACING;
1060 		}
1061 
1062 		creditsLines[lineCount]->str = line;
1063 		lineCount++;
1064 	}
1065 
1066 	f.close();
1067 
1068 	// We could easily add some ScummVM stuff to the credits, if we wanted
1069 	// to. On the other hand, anyone with the attention span to actually
1070 	// read all the credits probably already knows. :-)
1071 
1072 	// Start the music and roll the credits
1073 
1074 	// The credits music (which can also be heard briefly in the "carib"
1075 	// cutscene) is played once.
1076 
1077 	_vm->_sound->streamCompMusic(309, false);
1078 
1079 	clearScene();
1080 	fadeUp(0);
1081 
1082 	spriteInfo.scale = 0;
1083 	spriteInfo.scaledWidth = 0;
1084 	spriteInfo.scaledHeight = 0;
1085 	spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
1086 	spriteInfo.blend = 0;
1087 
1088 	int startLine = 0;
1089 	int scrollPos = 0;
1090 
1091 	bool abortCredits = false;
1092 
1093 	int scrollSteps = lineTop + CREDITS_FONT_HEIGHT;
1094 	uint32 musicStart = getTick();
1095 
1096 	// Ideally the music should last just a tiny bit longer than the
1097 	// credits. Note that musicTimeRemaining() will return 0 if the music
1098 	// is muted, so we need a sensible fallback for that case.
1099 
1100 	uint32 musicLength = MAX((int32)(1000 * (_vm->_sound->musicTimeRemaining() - 3)), 25 * (int32)scrollSteps);
1101 
1102 	while (scrollPos < scrollSteps && !_vm->shouldQuit()) {
1103 		clearScene();
1104 
1105 		for (i = startLine; i < lineCount; i++) {
1106 			if (!creditsLines[i])
1107 				continue;
1108 
1109 			// Free any sprites that have scrolled off the screen
1110 
1111 			if (creditsLines[i]->top + creditsLines[i]->height < scrollPos) {
1112 				debug(2, "Freeing line %d: '%s'", i, creditsLines[i]->str.c_str());
1113 
1114 				delete creditsLines[i];
1115 				creditsLines[i] = NULL;
1116 
1117 				startLine = i + 1;
1118 			} else if (creditsLines[i]->top < scrollPos + 400) {
1119 				if (!creditsLines[i]->sprite) {
1120 					debug(2, "Creating line %d: '%s'", i, creditsLines[i]->str.c_str());
1121 					creditsLines[i]->sprite = _vm->_fontRenderer->makeTextSprite((const byte *)creditsLines[i]->str.c_str(), 600, 14, _vm->_speechFontId, 0);
1122 				}
1123 
1124 				FrameHeader frame;
1125 
1126 				frame.read(creditsLines[i]->sprite);
1127 
1128 				spriteInfo.y = creditsLines[i]->top - scrollPos;
1129 				spriteInfo.w = frame.width;
1130 				spriteInfo.h = frame.height;
1131 				spriteInfo.data = creditsLines[i]->sprite + FrameHeader::size();
1132 				spriteInfo.isText = true;
1133 
1134 				switch (creditsLines[i]->type) {
1135 				case LINE_LEFT:
1136 					spriteInfo.x = RENDERWIDE / 2 - 5 - frame.width;
1137 					break;
1138 				case LINE_RIGHT:
1139 					spriteInfo.x = RENDERWIDE / 2 + 5;
1140 					break;
1141 				case LINE_CENTER:
1142 					if (strcmp(creditsLines[i]->str.c_str(), "@") == 0) {
1143 						spriteInfo.data = logoData;
1144 						spriteInfo.x = (RENDERWIDE - logoWidth) / 2;
1145 						spriteInfo.w = logoWidth;
1146 						spriteInfo.h = logoHeight;
1147 					} else
1148 						spriteInfo.x = (RENDERWIDE - frame.width) / 2;
1149 					break;
1150 				}
1151 
1152 				if (spriteInfo.data)
1153 					drawSprite(&spriteInfo);
1154 			} else
1155 				break;
1156 		}
1157 
1158 		updateDisplay();
1159 
1160 		KeyboardEvent *ke = _vm->keyboardEvent();
1161 
1162 		if (ke && ke->kbd.keycode == Common::KEYCODE_ESCAPE) {
1163 			if (!abortCredits) {
1164 				abortCredits = true;
1165 				fadeDown();
1166 			}
1167 		}
1168 
1169 		if (abortCredits && getFadeStatus() == RDFADE_BLACK)
1170 			break;
1171 
1172 		_vm->sleepUntil(musicStart + (musicLength * scrollPos) / scrollSteps + _pauseTicks);
1173 		scrollPos++;
1174 	}
1175 
1176 	// We're done. Clean up and try to put everything back where it was
1177 	// before the credits.
1178 
1179 	for (i = 0; i < lineCount; i++) {
1180 		delete creditsLines[i];
1181 	}
1182 
1183 	free(logoData);
1184 
1185 	if (!abortCredits) {
1186 		fadeDown();
1187 
1188 		// The music should either have stopped or be about to stop, so
1189 		// wait for it to really happen.
1190 
1191 		while (_vm->_sound->musicTimeRemaining() && !_vm->shouldQuit()) {
1192 			updateDisplay(false);
1193 			_vm->_system->delayMillis(100);
1194 		}
1195 	}
1196 
1197 	if (_vm->shouldQuit())
1198 		return;
1199 
1200 	waitForFade();
1201 
1202 	_vm->_sound->muteFx(false);
1203 	_vm->_sound->muteSpeech(false);
1204 
1205 	if (loopingMusicId)
1206 		_vm->_sound->streamCompMusic(loopingMusicId, true);
1207 	else
1208 		_vm->_sound->stopMusic(false);
1209 
1210 	if (!_vm->_mouse->getMouseStatus() || _vm->_mouse->isChoosing())
1211 		_vm->_mouse->setMouse(NORMAL_MOUSE_ID);
1212 
1213 	if (_vm->_logic->readVar(DEAD))
1214 		_vm->_mouse->buildSystemMenu();
1215 }
1216 
1217 // This image used to be shown by CacheNewCluster() while copying a data file
1218 // from the CD to the hard disk. ScummVM doesn't do that, so the image is never
1219 // shown. It'd be nice if we could do something useful with it some day...
1220 
splashScreen()1221 void Screen::splashScreen() {
1222 	byte *bgfile = _vm->_resman->openResource(2950);
1223 
1224 	initializeBackgroundLayer(NULL);
1225 	initializeBackgroundLayer(NULL);
1226 	initializeBackgroundLayer(_vm->fetchBackgroundLayer(bgfile));
1227 	initializeBackgroundLayer(NULL);
1228 	initializeBackgroundLayer(NULL);
1229 
1230 	_vm->fetchPalette(bgfile, _palette);
1231 	setPalette(0, 256, _palette, RDPAL_FADE);
1232 	renderParallax(_vm->fetchBackgroundLayer(bgfile), 2);
1233 
1234 	closeBackgroundLayer();
1235 
1236 	byte *loadingBar = _vm->_resman->openResource(2951);
1237 	byte *frame = _vm->fetchFrameHeader(loadingBar, 0);
1238 
1239 	AnimHeader animHead;
1240 	CdtEntry cdt;
1241 	FrameHeader frame_head;
1242 
1243 	animHead.read(_vm->fetchAnimHeader(loadingBar));
1244 	cdt.read(_vm->fetchCdtEntry(loadingBar, 0));
1245 	frame_head.read(_vm->fetchFrameHeader(loadingBar, 0));
1246 
1247 	SpriteInfo barSprite;
1248 
1249 	barSprite.x = cdt.x;
1250 	barSprite.y = cdt.y;
1251 	barSprite.w = frame_head.width;
1252 	barSprite.h = frame_head.height;
1253 	barSprite.scale = 0;
1254 	barSprite.scaledWidth = 0;
1255 	barSprite.scaledHeight = 0;
1256 	barSprite.type = RDSPR_RLE256FAST | RDSPR_TRANS;
1257 	barSprite.blend = 0;
1258 	barSprite.colorTable = 0;
1259 	barSprite.data = frame + FrameHeader::size();
1260 	barSprite.isText = false;
1261 
1262 	drawSprite(&barSprite);
1263 
1264 	fadeUp();
1265 	waitForFade();
1266 
1267 	for (int i = 0; i < animHead.noAnimFrames; i++) {
1268 		frame = _vm->fetchFrameHeader(loadingBar, i);
1269 		barSprite.data = frame + FrameHeader::size();
1270 		drawSprite(&barSprite);
1271 		updateDisplay();
1272 		_vm->_system->delayMillis(30);
1273 	}
1274 
1275 	_vm->_resman->closeResource(2951);
1276 
1277 	fadeDown();
1278 	waitForFade();
1279 }
1280 
1281 // Following functions are used to manage screen cache for psx version.
1282 
setPsxScrCache(byte * psxScrCache,uint8 level)1283 void Screen::setPsxScrCache(byte *psxScrCache, uint8 level) {
1284 		if (level < 3) {
1285 			if (psxScrCache)
1286 				_psxCacheEnabled[level] = true;
1287 			else
1288 				_psxCacheEnabled[level] = false;
1289 
1290 			_psxScrCache[level] = psxScrCache;
1291 		}
1292 }
1293 
getPsxScrCache(uint8 level)1294 byte *Screen::getPsxScrCache(uint8 level) {
1295 	if (level > 2) {
1296 		level = 0;
1297 	}
1298 
1299 	if (_psxCacheEnabled[level])
1300 		return _psxScrCache[level];
1301 	else
1302 		return NULL;
1303 }
1304 
getPsxScrCacheStatus(uint8 level)1305 bool Screen::getPsxScrCacheStatus(uint8 level) {
1306 	if (level > 2) {
1307 		level = 0;
1308 	}
1309 
1310 	return _psxCacheEnabled[level];
1311 }
1312 
flushPsxScrCache()1313 void Screen::flushPsxScrCache() {
1314 	for (uint8 i = 0; i < 3; i++) {
1315 		free(_psxScrCache[i]);
1316 		_psxScrCache[i] = NULL;
1317 		_psxCacheEnabled[i] = true;
1318 	}
1319 }
1320 
1321 } // End of namespace Sword2
1322