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 #include "common/endian.h"
27 #include "common/system.h"
28 
29 #include "graphics/primitives.h"
30 
31 #include "sword2/sword2.h"
32 #include "sword2/defs.h"
33 #include "sword2/screen.h"
34 
35 namespace Sword2 {
36 
37 #define RENDERAVERAGETOTAL 4
38 
updateRect(Common::Rect * r)39 void Screen::updateRect(Common::Rect *r) {
40 	_vm->_system->copyRectToScreen(_buffer + r->top * _screenWide + r->left,
41 		_screenWide, r->left, r->top, r->right - r->left,
42 		r->bottom - r->top);
43 }
44 
blitBlockSurface(BlockSurface * s,Common::Rect * r,Common::Rect * clipRect)45 void Screen::blitBlockSurface(BlockSurface *s, Common::Rect *r, Common::Rect *clipRect) {
46 	if (!r->intersects(*clipRect))
47 		return;
48 
49 	byte *src = s->data;
50 
51 	if (r->top < clipRect->top) {
52 		src -= BLOCKWIDTH * (r->top - clipRect->top);
53 		r->top = clipRect->top;
54 	}
55 	if (r->left < clipRect->left) {
56 		src -= (r->left - clipRect->left);
57 		r->left = clipRect->left;
58 	}
59 	if (r->bottom > clipRect->bottom)
60 		r->bottom = clipRect->bottom;
61 	if (r->right > clipRect->right)
62 		r->right = clipRect->right;
63 
64 	byte *dst = _buffer + r->top * _screenWide + r->left;
65 	int i;
66 
67 	if (s->transparent) {
68 		for (i = 0; i < r->bottom - r->top; i++) {
69 			for (int j = 0; j < r->right - r->left; j++) {
70 				if (src[j])
71 					dst[j] = src[j];
72 			}
73 			src += BLOCKWIDTH;
74 			dst += _screenWide;
75 		}
76 	} else {
77 		for (i = 0; i < r->bottom - r->top; i++) {
78 			memcpy(dst, src, r->right - r->left);
79 			src += BLOCKWIDTH;
80 			dst += _screenWide;
81 		}
82 	}
83 }
84 
85 // There are two different separate functions for scaling the image - one fast
86 // and one good. Or at least that's the theory. I'm sure there are better ways
87 // to scale an image than this. The latter is used at the highest graphics
88 // quality setting. Note that the "good" scaler takes extra parameters so that
89 // it can use the background image when calculating the average pixel value.
90 //
91 // This code isn't quite like the original DrawSprite(), but the result should
92 // be close enough, I hope.
93 
scaleImageFast(byte * dst,uint16 dstPitch,uint16 dstWidth,uint16 dstHeight,byte * src,uint16 srcPitch,uint16 srcWidth,uint16 srcHeight)94 void Screen::scaleImageFast(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight) {
95 	int x, y;
96 
97 	for (x = 0; x < dstWidth; x++)
98 		_xScale[x] = (x * srcWidth) / dstWidth;
99 
100 	for (y = 0; y < dstHeight; y++)
101 		_yScale[y] = (y * srcHeight) / dstHeight;
102 
103 	for (y = 0; y < dstHeight; y++) {
104 		for (x = 0; x < dstWidth; x++) {
105 			dst[x] = src[_yScale[y] * srcPitch + _xScale[x]];
106 		}
107 		dst += dstPitch;
108 	}
109 }
110 
scaleImageGood(byte * dst,uint16 dstPitch,uint16 dstWidth,uint16 dstHeight,byte * src,uint16 srcPitch,uint16 srcWidth,uint16 srcHeight,byte * backBuf,int16 bbXPos,int16 bbYPos)111 void Screen::scaleImageGood(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backBuf, int16 bbXPos, int16 bbYPos) {
112 	for (int y = 0; y < dstHeight; y++) {
113 		for (int x = 0; x < dstWidth; x++) {
114 			uint8 c1, c2, c3, c4;
115 
116 			uint32 xPos = (x * srcWidth) / dstWidth;
117 			uint32 yPos = (y * srcHeight) / dstHeight;
118 			uint32 xFrac = dstWidth - (x * srcWidth) % dstWidth;
119 			uint32 yFrac = dstHeight - (y * srcHeight) % dstHeight;
120 
121 			byte *srcPtr = src + yPos * srcPitch + xPos;
122 
123 			bool transparent = true;
124 
125 			if (*srcPtr) {
126 				c1 = *srcPtr;
127 				transparent = false;
128 			} else {
129 				if (bbXPos + x >= 0 &&
130 				    bbXPos + x < RENDERWIDE &&
131 				    bbYPos + y >= MENUDEEP &&
132 				    bbYPos + y < MENUDEEP + RENDERDEEP) {
133 					c1 = *(backBuf + _screenWide * (bbYPos + y) + bbXPos + x);
134 				} else {
135 					c1 = 0;
136 				}
137 			}
138 
139 			if (x < dstWidth - 1) {
140 				if (*(srcPtr + 1)) {
141 					c2 = *(srcPtr + 1);
142 					transparent = false;
143 				} else {
144 					if (bbXPos + x + 1 >= 0 &&
145 					    bbXPos + x + 1 < RENDERWIDE &&
146 					    bbYPos + y >= MENUDEEP &&
147 					    bbYPos + y + 1 < MENUDEEP + RENDERDEEP) {
148 						c2 = *(backBuf + _screenWide * (bbYPos + y) + bbXPos + x + 1);
149 					} else {
150 						c2 = c1;
151 					}
152 				}
153 			} else {
154 				c2 = c1;
155 			}
156 
157 			if (y < dstHeight - 1) {
158 				if (*(srcPtr + srcPitch)) {
159 					c3 = *(srcPtr + srcPitch);
160 					transparent = false;
161 				} else {
162 					if (bbXPos + x >= 0 &&
163 					    bbXPos + x < RENDERWIDE &&
164 					    bbYPos + y + 1 >= MENUDEEP &&
165 					    bbYPos + y + 1 < MENUDEEP + RENDERDEEP) {
166 						c3 = *(backBuf + _screenWide * (bbYPos + y + 1) + bbXPos);
167 					} else {
168 						c3 = c1;
169 					}
170 				}
171 			} else {
172 				c3 = c1;
173 			}
174 
175 			if (x < dstWidth - 1 && y < dstHeight - 1) {
176 				if (*(srcPtr + srcPitch + 1)) {
177 					c4 = *(srcPtr + srcPitch + 1);
178 					transparent = false;
179 				} else {
180 					if (bbXPos + x + 1 >= 0 &&
181 					    bbXPos + x + 1 < RENDERWIDE &&
182 					    bbYPos + y + 1 >= MENUDEEP &&
183 					    bbYPos + y + 1 < MENUDEEP + RENDERDEEP) {
184 						c4 = *(backBuf + _screenWide * (bbYPos + y + 1) + bbXPos + x + 1);
185 					} else {
186 						c4 = c3;
187 					}
188 				}
189 			} else {
190 				c4 = c3;
191 			}
192 
193 			if (!transparent) {
194 				uint32 r1 = _palette[c1 * 3 + 0];
195 				uint32 g1 = _palette[c1 * 3 + 1];
196 				uint32 b1 = _palette[c1 * 3 + 2];
197 
198 				uint32 r2 = _palette[c2 * 3 + 0];
199 				uint32 g2 = _palette[c2 * 3 + 1];
200 				uint32 b2 = _palette[c2 * 3 + 2];
201 
202 				uint32 r3 = _palette[c3 * 3 + 0];
203 				uint32 g3 = _palette[c3 * 3 + 1];
204 				uint32 b3 = _palette[c3 * 3 + 2];
205 
206 				uint32 r4 = _palette[c4 * 3 + 0];
207 				uint32 g4 = _palette[c4 * 3 + 1];
208 				uint32 b4 = _palette[c4 * 3 + 2];
209 
210 				uint32 r5 = (r1 * xFrac + r2 * (dstWidth - xFrac)) / dstWidth;
211 				uint32 g5 = (g1 * xFrac + g2 * (dstWidth - xFrac)) / dstWidth;
212 				uint32 b5 = (b1 * xFrac + b2 * (dstWidth - xFrac)) / dstWidth;
213 
214 				uint32 r6 = (r3 * xFrac + r4 * (dstWidth - xFrac)) / dstWidth;
215 				uint32 g6 = (g3 * xFrac + g4 * (dstWidth - xFrac)) / dstWidth;
216 				uint32 b6 = (b3 * xFrac + b4 * (dstWidth - xFrac)) / dstWidth;
217 
218 				uint32 r = (r5 * yFrac + r6 * (dstHeight - yFrac)) / dstHeight;
219 				uint32 g = (g5 * yFrac + g6 * (dstHeight - yFrac)) / dstHeight;
220 				uint32 b = (b5 * yFrac + b6 * (dstHeight - yFrac)) / dstHeight;
221 
222 				dst[y * dstWidth + x] = quickMatch(r, g, b);
223 			} else
224 				dst[y * dstWidth + x] = 0;
225 		}
226 	}
227 }
228 
229 /**
230  * Plots a point relative to the top left corner of the screen. This is only
231  * used for debugging.
232  * @param x x-coordinate of the point
233  * @param y y-coordinate of the point
234  * @param color color of the point
235  */
236 
plotPoint(int x,int y,uint8 color)237 void Screen::plotPoint(int x, int y, uint8 color) {
238 	byte *buf = _buffer + MENUDEEP * RENDERWIDE;
239 
240 	x -= _scrollX;
241 	y -= _scrollY;
242 
243 	if (x >= 0 && x < RENDERWIDE && y >= 0 && y < RENDERDEEP) {
244 		buf[y * RENDERWIDE + x] = color;
245 		markAsDirty(x, y + MENUDEEP, x, y + MENUDEEP);
246 	}
247 }
248 
plot(int x,int y,int color,void * data)249 static void plot(int x, int y, int color, void *data) {
250 	Screen *screen = (Screen *)data;
251 	screen->plotPoint(x, y, (uint8) color);
252 }
253 
254 /**
255  * Draws a line from one point to another. This is only used for debugging.
256  * @param x0 x-coordinate of the start point
257  * @param y0 y-coordinate of the start point
258  * @param x1 x-coordinate of the end point
259  * @param y1 y-coordinate of the end point
260  * @param color color of the line
261  */
262 
drawLine(int x0,int y0,int x1,int y1,uint8 color)263 void Screen::drawLine(int x0, int y0, int x1, int y1, uint8 color) {
264 	Graphics::drawLine(x0, y0, x1, y1, color, &plot, this);
265 }
266 
267 /**
268  * This function tells the driver the size of the background screen for the
269  * current location.
270  * @param w width of the current location
271  * @param h height of the current location
272  */
273 
setLocationMetrics(uint16 w,uint16 h)274 void Screen::setLocationMetrics(uint16 w, uint16 h) {
275 	_locationWide = w;
276 	_locationDeep = h;
277 	setNeedFullRedraw();
278 }
279 
280 /**
281  * Draws a parallax layer at the current position determined by the scroll. A
282  * parallax can be either foreground, background or the main screen.
283  */
284 
renderParallax(byte * ptr,int16 l)285 void Screen::renderParallax(byte *ptr, int16 l) {
286 	int16 x, y;
287 	uint16 xRes, yRes;
288 	Common::Rect r;
289 
290 	if (!ptr)
291 		return;
292 
293 	// Fetch resolution data from parallax
294 
295 	if (Sword2Engine::isPsx()) {
296 		xRes = READ_LE_UINT16(ptr);
297 		yRes = READ_LE_UINT16(ptr + 2) * 2;
298 	} else {
299 		Parallax p;
300 
301 		p.read(ptr);
302 		xRes = p.w;
303 		yRes = p.h;
304 	}
305 
306 	if (_locationWide == _screenWide)
307 		x = 0;
308 	else
309 		x = ((int32)((xRes - _screenWide) * _scrollX) / (int32)(_locationWide - _screenWide));
310 
311 	if (_locationDeep == _screenDeep - MENUDEEP * 2)
312 		y = 0;
313 	else
314 		y = ((int32)((yRes - (_screenDeep - MENUDEEP * 2)) * _scrollY) / (int32)(_locationDeep - (_screenDeep - MENUDEEP * 2)));
315 
316 	Common::Rect clipRect;
317 
318 	// Leave enough space for the top and bottom menues
319 
320 	clipRect.left = 0;
321 	clipRect.right = _screenWide;
322 	clipRect.top = MENUDEEP;
323 	clipRect.bottom = _screenDeep - MENUDEEP;
324 
325 	for (int j = 0; j < _yBlocks[l]; j++) {
326 		for (int i = 0; i < _xBlocks[l]; i++) {
327 			if (_blockSurfaces[l][i + j * _xBlocks[l]]) {
328 				r.left = i * BLOCKWIDTH - x;
329 				r.right = r.left + BLOCKWIDTH;
330 				r.top = j * BLOCKHEIGHT - y + MENUDEEP;
331 				r.bottom = r.top + BLOCKHEIGHT;
332 				blitBlockSurface(_blockSurfaces[l][i + j * _xBlocks[l]], &r, &clipRect);
333 			}
334 		}
335 	}
336 
337 	_parallaxScrollX = _scrollX - x;
338 	_parallaxScrollY = _scrollY - y;
339 }
340 
341 // Uncomment this when benchmarking the drawing routines.
342 #define LIMIT_FRAME_RATE
343 
344 /**
345  * Initializes the timers before the render loop is entered.
346  */
347 
initializeRenderCycle()348 void Screen::initializeRenderCycle() {
349 	_initialTime = _vm->_system->getMillis();
350 	_totalTime = _initialTime + (1000 / _vm->getFramesPerSecond());
351 }
352 
353 /**
354  * This function should be called when the game engine is ready to start the
355  * render cycle.
356  */
357 
startRenderCycle()358 void Screen::startRenderCycle() {
359 	_scrollXOld = _scrollX;
360 	_scrollYOld = _scrollY;
361 
362 	_startTime = _vm->_system->getMillis();
363 
364 	if (_startTime + _renderAverageTime >= _totalTime)	{
365 		_scrollX = _scrollXTarget;
366 		_scrollY = _scrollYTarget;
367 		_renderTooSlow = true;
368 	} else {
369 		_scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
370 		_scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
371 		_renderTooSlow = false;
372 	}
373 
374 	if (_scrollXOld != _scrollX || _scrollYOld != _scrollY)
375 		setNeedFullRedraw();
376 
377 	_framesPerGameCycle = 0;
378 }
379 
380 /**
381  * This function should be called at the end of the render cycle.
382  * @return true if the render cycle is to be terminated,
383  *         or false if it should continue
384  */
385 
endRenderCycle()386 bool Screen::endRenderCycle() {
387 	static int32 renderTimeLog[4] = { 60, 60, 60, 60 };
388 	static int32 renderCountIndex = 0;
389 	int32 time;
390 
391 	time = _vm->_system->getMillis();
392 	renderTimeLog[renderCountIndex] = time - _startTime;
393 	_startTime = time;
394 	_renderAverageTime = (renderTimeLog[0] + renderTimeLog[1] + renderTimeLog[2] + renderTimeLog[3]) >> 2;
395 
396 	_framesPerGameCycle++;
397 
398 	if (++renderCountIndex == RENDERAVERAGETOTAL)
399 		renderCountIndex = 0;
400 
401 	if (_renderTooSlow) {
402 		initializeRenderCycle();
403 		return true;
404 	}
405 
406 	if (_startTime + _renderAverageTime >= _totalTime) {
407 		_totalTime += (1000 / _vm->getFramesPerSecond());
408 		_initialTime = time;
409 		return true;
410 	}
411 
412 #ifdef LIMIT_FRAME_RATE
413 	if (_scrollXTarget == _scrollX && _scrollYTarget == _scrollY) {
414 		// If we have already reached the scroll target sleep for the
415 		// rest of the render cycle.
416 		_vm->sleepUntil(_totalTime);
417 		_initialTime = _vm->_system->getMillis();
418 		_totalTime += (1000 / _vm->getFramesPerSecond());
419 		return true;
420 	}
421 #endif
422 
423 	// This is an attempt to ensure that we always reach the scroll target.
424 	// Otherwise the game frequently tries to pump out new interpolation
425 	// frames without ever getting anywhere.
426 
427 	if (ABS(_scrollX - _scrollXTarget) <= 1 && ABS(_scrollY - _scrollYTarget) <= 1) {
428 		_scrollX = _scrollXTarget;
429 		_scrollY = _scrollYTarget;
430 	} else {
431 		_scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
432 		_scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
433 	}
434 
435 	if (_scrollX != _scrollXOld || _scrollY != _scrollYOld)
436 		setNeedFullRedraw();
437 
438 #ifdef LIMIT_FRAME_RATE
439 	// Give the other threads some breathing space. This apparently helps
440 	// against bug #875683, though I was never able to reproduce it for
441 	// myself.
442 	_vm->_system->delayMillis(10);
443 #endif
444 
445 	return false;
446 }
447 
448 /**
449  * Reset scrolling stuff. This function is called from initBackground()
450  */
451 
resetRenderEngine()452 void Screen::resetRenderEngine() {
453 	_parallaxScrollX = 0;
454 	_parallaxScrollY = 0;
455 	_scrollX = 0;
456 	_scrollY = 0;
457 }
458 
459 /**
460  * This function should be called five times with either the parallax layer
461  * or a NULL pointer in order of background parallax to foreground parallax.
462  */
463 
initializeBackgroundLayer(byte * parallax)464 int32 Screen::initializeBackgroundLayer(byte *parallax) {
465 	Parallax p;
466 	uint16 i, j, k;
467 	byte *data;
468 	byte *dst;
469 
470 	debug(2, "initializeBackgroundLayer");
471 
472 	assert(_layer < MAXLAYERS);
473 
474 	if (!parallax) {
475 		_layer++;
476 		return RD_OK;
477 	}
478 
479 	p.read(parallax);
480 
481 	_xBlocks[_layer] = (p.w + BLOCKWIDTH - 1) / BLOCKWIDTH;
482 	_yBlocks[_layer] = (p.h + BLOCKHEIGHT - 1) / BLOCKHEIGHT;
483 
484 	_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
485 	if (!_blockSurfaces[_layer])
486 		return RDERR_OUTOFMEMORY;
487 
488 	// Decode the parallax layer into a large chunk of memory
489 
490 	byte *memchunk = (byte *)calloc(_xBlocks[_layer] * _yBlocks[_layer], BLOCKWIDTH * BLOCKHEIGHT);
491 	if (!memchunk)
492 		return RDERR_OUTOFMEMORY;
493 
494 	for (i = 0; i < p.h; i++) {
495 		uint32 p_offset = READ_LE_UINT32(parallax + Parallax::size() + 4 * i);
496 
497 		if (!p_offset)
498 			continue;
499 
500 		byte *pLine = parallax + p_offset;
501 		uint16 packets = READ_LE_UINT16(pLine);
502 		uint16 offset = READ_LE_UINT16(pLine + 2);
503 
504 		data = pLine + 4;
505 		dst = memchunk + i * p.w + offset;
506 
507 		if (!packets) {
508 			memcpy(dst, data, p.w);
509 			continue;
510 		}
511 
512 		bool zeros = false;
513 
514 		for (j = 0; j < packets; j++) {
515 			if (zeros) {
516 				dst += *data;
517 				offset += *data;
518 				data++;
519 				zeros = false;
520 			} else if (!*data) {
521 				data++;
522 				zeros = true;
523 			} else {
524 				uint16 count = *data++;
525 				memcpy(dst, data, count);
526 				data += count;
527 				dst += count;
528 				offset += count;
529 				zeros = true;
530 			}
531 		}
532 	}
533 
534 	// The large memory chunk is now divided into a number of smaller
535 	// surfaces. For most parallax layers, we'll end up using less memory
536 	// this way, and it will be faster to draw since completely transparent
537 	// surfaces are discarded.
538 
539 	for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
540 		bool block_has_data = false;
541 		bool block_is_transparent = false;
542 
543 		int x = BLOCKWIDTH * (i % _xBlocks[_layer]);
544 		int y = BLOCKHEIGHT * (i / _xBlocks[_layer]);
545 
546 		data = memchunk + p.w * y + x;
547 
548 		for (j = 0; j < BLOCKHEIGHT; j++) {
549 			for (k = 0; k < BLOCKWIDTH; k++) {
550 				if (x + k < p.w && y + j < p.h) {
551 					if (data[j * p.w + k])
552 						block_has_data = true;
553 					else
554 						block_is_transparent = true;
555 				}
556 			}
557 		}
558 
559 		//  Only assign a surface to the block if it contains data.
560 
561 		if (block_has_data) {
562 			_blockSurfaces[_layer][i] = (BlockSurface *)malloc(sizeof(BlockSurface));
563 
564 			//  Copy the data into the surfaces.
565 			dst = _blockSurfaces[_layer][i]->data;
566 			for (j = 0; j < BLOCKHEIGHT; j++) {
567 				memcpy(dst, data, BLOCKWIDTH);
568 				data += p.w;
569 				dst += BLOCKWIDTH;
570 			}
571 
572 			_blockSurfaces[_layer][i]->transparent = block_is_transparent;
573 
574 		} else
575 			_blockSurfaces[_layer][i] = NULL;
576 	}
577 
578 	free(memchunk);
579 	_layer++;
580 
581 	return RD_OK;
582 }
583 
584 /**
585  * This converts PSX format background data into a format that
586  * can be understood by renderParallax functions.
587  * PSX Backgrounds are divided into tiles of 64x32 (with aspect
588  * ratio correction), while PC backgrounds are in tiles of 64x64.
589  */
590 
initializePsxBackgroundLayer(byte * parallax)591 int32 Screen::initializePsxBackgroundLayer(byte *parallax) {
592 	uint16 bgXres, bgYres;
593 	uint16 trueXres, stripeNumber, totStripes;
594 	uint32 baseAddress, stripePos;
595 	uint16 i, j;
596 	byte *dst;
597 
598 	debug(2, "initializePsxBackgroundLayer");
599 
600 	assert(_layer < MAXLAYERS);
601 
602 	if (!parallax) {
603 		_layer++;
604 		return RD_OK;
605 	}
606 
607 	// Fetch data from buffer
608 
609 	bgXres = READ_LE_UINT16(parallax);
610 	bgYres = READ_LE_UINT16(parallax + 2) * 2;
611 	baseAddress = READ_LE_UINT32(parallax + 4);
612 	parallax += 8;
613 
614 	// Calculate TRUE resolution of background, must be
615 	// a multiple of 64
616 
617 	trueXres = (bgXres % 64) ? ((bgXres/64) + 1) * 64 : bgXres;
618 	totStripes = trueXres / 64;
619 
620 	_xBlocks[_layer] = (bgXres + BLOCKWIDTH - 1) / BLOCKWIDTH;
621 	_yBlocks[_layer] = (bgYres + BLOCKHEIGHT - 1) / BLOCKHEIGHT;
622 
623 	uint16 remLines = bgYres % 64;
624 
625 	byte *tileChunk = (byte *)malloc(BLOCKHEIGHT * BLOCKWIDTH);
626 	if (!tileChunk)
627 		return RDERR_OUTOFMEMORY;
628 
629 	_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
630 	if (!_blockSurfaces[_layer]) {
631 		free(tileChunk);
632 		return RDERR_OUTOFMEMORY;
633 	}
634 
635 	// Group PSX background (64x32, when stretched vertically) tiles together,
636 	// to make them compatible with pc version (composed by 64x64 tiles)
637 
638 	stripeNumber = 0;
639 	stripePos = 0;
640 	for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
641 		bool block_has_data = false;
642 		bool block_is_transparent = false;
643 
644 		int posX = i / _yBlocks[_layer];
645 		int posY = i % _yBlocks[_layer];
646 
647 		uint32 stripeOffset = READ_LE_UINT32(parallax + stripeNumber * 8 + 4) + stripePos - baseAddress;
648 
649 		memset(tileChunk, 1, BLOCKHEIGHT * BLOCKWIDTH);
650 
651 		if (!(remLines && posY == _yBlocks[_layer] - 1))
652 			remLines = 32;
653 
654 		for (j = 0; j < remLines; j++) {
655 			memcpy(tileChunk + j * BLOCKWIDTH * 2, parallax + stripeOffset + j * BLOCKWIDTH, BLOCKWIDTH);
656 			memcpy(tileChunk + j * BLOCKWIDTH * 2 + BLOCKWIDTH, parallax + stripeOffset + j * BLOCKWIDTH, BLOCKWIDTH);
657 		}
658 
659 		for (j = 0; j < BLOCKHEIGHT * BLOCKWIDTH; j++) {
660 			if (tileChunk[j])
661 				block_has_data = true;
662 			else
663 				block_is_transparent = true;
664 		}
665 
666 		int tileIndex = totStripes * posY + posX;
667 
668 		//  Only assign a surface to the block if it contains data.
669 
670 		if (block_has_data) {
671 			_blockSurfaces[_layer][tileIndex] = (BlockSurface *)malloc(sizeof(BlockSurface));
672 
673 			//  Copy the data into the surfaces.
674 			dst = _blockSurfaces[_layer][tileIndex]->data;
675 			memcpy(dst, tileChunk, BLOCKWIDTH * BLOCKHEIGHT);
676 
677 			_blockSurfaces[_layer][tileIndex]->transparent = block_is_transparent;
678 
679 		} else
680 			_blockSurfaces[_layer][tileIndex] = NULL;
681 
682 		if (posY == _yBlocks[_layer] - 1) {
683 			stripeNumber++;
684 			stripePos = 0;
685 		} else {
686 			stripePos += 0x800;
687 		}
688 	}
689 
690 	free(tileChunk);
691 	_layer++;
692 
693 	return RD_OK;
694 }
695 
696 /**
697  * This converts PSX format parallax data into a format that
698  * can be understood by renderParallax functions.
699  */
700 
initializePsxParallaxLayer(byte * parallax)701 int32 Screen::initializePsxParallaxLayer(byte *parallax) {
702 	uint16 i, j, k;
703 	byte *data;
704 	byte *dst;
705 
706 	debug(2, "initializePsxParallaxLayer");
707 
708 	assert(_layer < MAXLAYERS);
709 
710 	if (!parallax) {
711 		_layer++;
712 		return RD_OK;
713 	}
714 
715 	// uint16 plxXres = READ_LE_UINT16(parallax);
716 	// uint16 plxYres = READ_LE_UINT16(parallax + 2);
717 	uint16 xTiles = READ_LE_UINT16(parallax + 4);
718 	uint16 yTiles = READ_LE_UINT16(parallax + 6);
719 
720 	// Beginning of parallax table composed by uint32,
721 	// if word is 0, corresponding tile contains no data and must be skipped,
722 	// if word is 0x400 tile contains data.
723 	parallax += 8;
724 
725 	// Beginning if tiles data.
726 	data = parallax + xTiles * yTiles * 4;
727 
728 	_xBlocks[_layer] = xTiles;
729 	_yBlocks[_layer] = (yTiles / 2) + ((yTiles % 2) ? 1 : 0);
730 	bool oddTiles = ((yTiles % 2) ? true : false);
731 
732 	_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
733 	if (!_blockSurfaces[_layer])
734 		return RDERR_OUTOFMEMORY;
735 
736 	// We have to check two tiles for every block in PSX version, if one of those
737 	// has data in it, the whole block has data. Also, tiles must be doublelined to
738 	// get correct aspect ratio.
739 	for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
740 		bool block_has_data = false;
741 		bool block_is_transparent = false;
742 		bool firstTilePresent, secondTilePresent;
743 
744 		int posX = i / _yBlocks[_layer];
745 		int posY = i % _yBlocks[_layer];
746 
747 		if (oddTiles && posY == _yBlocks[_layer] - 1) {
748 			firstTilePresent = READ_LE_UINT32(parallax) == 0x400;
749 			secondTilePresent = false;
750 			parallax += 4;
751 		} else {
752 			firstTilePresent = READ_LE_UINT32(parallax) == 0x400;
753 			secondTilePresent = READ_LE_UINT32(parallax + 4) == 0x400;
754 			parallax += 8;
755 		}
756 
757 		// If one of the two grouped tiles has data, then the whole block has data
758 		if (firstTilePresent || secondTilePresent) {
759 			block_has_data = true;
760 
761 			// If one of the two grouped blocks is without data, then we also have transparency
762 			if (!firstTilePresent || !secondTilePresent)
763 				block_is_transparent = true;
764 		}
765 
766 		// Now do a second check to see if we have a partially transparent block
767 		if (block_has_data && !block_is_transparent) {
768 			byte *block = data;
769 			if (firstTilePresent) {
770 				for (k = 0; k < 0x400; k++) {
771 					if (*(block + k) == 0) {
772 						block_is_transparent = true;
773 						break;
774 					}
775 				}
776 				block += 0x400; // On to next block...
777 			}
778 
779 			// If we didn't find transparency in first block and we have
780 			// a second tile, check it
781 			if (secondTilePresent && !block_is_transparent) {
782 				for (k = 0; k < 0x400; k++) {
783 					if (*(block + k) == 0) {
784 						block_is_transparent = true;
785 						break;
786 					}
787 				}
788 			}
789 		}
790 
791 		int tileIndex = xTiles * posY + posX;
792 
793 		//  Only assign a surface to the block if it contains data.
794 
795 		if (block_has_data) {
796 			_blockSurfaces[_layer][tileIndex] = (BlockSurface *)malloc(sizeof(BlockSurface));
797 			memset(_blockSurfaces[_layer][tileIndex], 0, BLOCKHEIGHT * BLOCKWIDTH);
798 
799 			//  Copy the data into the surfaces.
800 			dst = _blockSurfaces[_layer][tileIndex]->data;
801 
802 			if (firstTilePresent) { //There is data in the first tile
803 				for (j = 0; j < 16; j++) {
804 					memcpy(dst, data, BLOCKWIDTH);
805 					dst += BLOCKWIDTH;
806 					memcpy(dst, data, BLOCKWIDTH);
807 					dst += BLOCKWIDTH;
808 					data += BLOCKWIDTH;
809 				}
810 			} else {
811 				dst += 0x800;
812 			}
813 
814 			if (secondTilePresent) {
815 				for (j = 0; j < 16; j++) {
816 					memcpy(dst, data, BLOCKWIDTH);
817 					dst += BLOCKWIDTH;
818 					memcpy(dst, data, BLOCKWIDTH);
819 					dst += BLOCKWIDTH;
820 					data += BLOCKWIDTH;
821 				}
822 			}
823 
824 			_blockSurfaces[_layer][tileIndex]->transparent = block_is_transparent;
825 		} else
826 			_blockSurfaces[_layer][tileIndex] = NULL;
827 	}
828 
829 	_layer++;
830 
831 	return RD_OK;
832 }
833 
834 /**
835  * Should be called once after leaving the room to free up memory.
836  */
837 
closeBackgroundLayer()838 void Screen::closeBackgroundLayer() {
839 	debug(2, "CloseBackgroundLayer");
840 
841 	if (Sword2Engine::isPsx())
842 		flushPsxScrCache();
843 
844 	for (int i = 0; i < MAXLAYERS; i++) {
845 		if (_blockSurfaces[i]) {
846 			for (int j = 0; j < _xBlocks[i] * _yBlocks[i]; j++)
847 				if (_blockSurfaces[i][j])
848 					free(_blockSurfaces[i][j]);
849 			free(_blockSurfaces[i]);
850 			_blockSurfaces[i] = NULL;
851 		}
852 	}
853 
854 	_layer = 0;
855 }
856 
857 } // End of namespace Sword2
858