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 
24 #include "common/system.h"
25 #include "common/textconsole.h"
26 #include "common/util.h"
27 
28 #include "graphics/palette.h"
29 
30 #include "sword1/screen.h"
31 #include "sword1/logic.h"
32 #include "sword1/sworddefs.h"
33 #include "sword1/text.h"
34 #include "sword1/resman.h"
35 #include "sword1/objectman.h"
36 #include "sword1/menu.h"
37 #include "sword1/swordres.h"
38 #include "sword1/sword1.h"
39 
40 namespace Sword1 {
41 
42 #define SCROLL_FRACTION 16
43 #define MAX_SCROLL_DISTANCE 8
44 #define FADE_UP 1
45 #define FADE_DOWN -1
46 
Screen(OSystem * system,ResMan * pResMan,ObjectMan * pObjMan)47 Screen::Screen(OSystem *system, ResMan *pResMan, ObjectMan *pObjMan) {
48 	_system = system;
49 	_resMan = pResMan;
50 	_objMan = pObjMan;
51 	_screenBuf = _screenGrid = NULL;
52 	_backLength = _foreLength = _sortLength = 0;
53 	_fadingStep = 0;
54 	_currentScreen = 0xFFFF;
55 	_updatePalette = false;
56 	_psxCache.decodedBackground = NULL;
57 	_psxCache.extPlxCache = NULL;
58 	_oldScrollX = 0;
59 	_oldScrollY = 0;
60 
61 	_textMan = 0;
62 
63 	for (int i = 0; i < 4; i++)
64 		_layerGrid[i] = 0;
65 
66 	for (int i = 0; i < 4; i++)
67 		_layerBlocks[i] = 0;
68 
69 	_parallax[0] = 0;
70 	_parallax[1] = 0;
71 
72 	_fullRefresh = 0;
73 
74 	for (int i = 0; i < MAX_SORT; i++) {
75 		_sortList[i].id = 0;
76 		_sortList[i].y = 0;
77 	}
78 	_scrnSizeX = 0;
79 	_scrnSizeY = 0;
80 	_gridSizeX = 0;
81 	_gridSizeY = 0;
82 	_fadingDirection = 0;
83 	_isBlack = 0;
84 }
85 
~Screen()86 Screen::~Screen() {
87 	free(_screenBuf);
88 	free(_screenGrid);
89 	if (_currentScreen != 0xFFFF)
90 		quitScreen();
91 }
92 
clearScreen()93 void Screen::clearScreen() {
94 	if (_screenBuf) {
95 		_fullRefresh = true;
96 		memset(_screenBuf, 0, _scrnSizeX * _scrnSizeY);
97 		_system->fillScreen(0);
98 	}
99 }
100 
useTextManager(Text * pTextMan)101 void Screen::useTextManager(Text *pTextMan) {
102 	_textMan = pTextMan;
103 }
104 
setScrolling(int16 offsetX,int16 offsetY)105 void Screen::setScrolling(int16 offsetX, int16 offsetY) {
106 	offsetX = CLIP<int32>(offsetX, 0, Logic::_scriptVars[MAX_SCROLL_OFFSET_X]);
107 	offsetY = CLIP<int32>(offsetY, 0, Logic::_scriptVars[MAX_SCROLL_OFFSET_Y]);
108 
109 	if (Logic::_scriptVars[SCROLL_FLAG] == 2) { // first time on this screen - need absolute scroll immediately!
110 		_oldScrollX = Logic::_scriptVars[SCROLL_OFFSET_X] = (uint32)offsetX;
111 		_oldScrollY = Logic::_scriptVars[SCROLL_OFFSET_Y] = (uint32)offsetY;
112 		Logic::_scriptVars[SCROLL_FLAG] = 1;
113 		_fullRefresh = true;
114 	} else if (Logic::_scriptVars[SCROLL_FLAG] == 1) {
115 		// Because parallax layers may be drawn on the old scroll offset, we
116 		// want a full refresh not only when the scroll offset changes, but
117 		// also on the frame where they become the same.
118 		if (_oldScrollX != Logic::_scriptVars[SCROLL_OFFSET_X] || _oldScrollY != Logic::_scriptVars[SCROLL_OFFSET_Y])
119 			_fullRefresh = true;
120 		_oldScrollX = Logic::_scriptVars[SCROLL_OFFSET_X];
121 		_oldScrollY = Logic::_scriptVars[SCROLL_OFFSET_Y];
122 		int dx = offsetX - Logic::_scriptVars[SCROLL_OFFSET_X];
123 		int dy = offsetY - Logic::_scriptVars[SCROLL_OFFSET_Y];
124 		int scrlDistX = CLIP<int32>((((SCROLL_FRACTION - 1) + ABS(dx)) / SCROLL_FRACTION) * ((dx > 0) ? 1 : -1), -MAX_SCROLL_DISTANCE, MAX_SCROLL_DISTANCE);
125 		int scrlDistY = CLIP<int32>((((SCROLL_FRACTION - 1) + ABS(dy)) / SCROLL_FRACTION) * ((dy > 0) ? 1 : -1), -MAX_SCROLL_DISTANCE, MAX_SCROLL_DISTANCE);
126 		if ((scrlDistX != 0) || (scrlDistY != 0))
127 			_fullRefresh = true;
128 		Logic::_scriptVars[SCROLL_OFFSET_X] = CLIP<int32>(Logic::_scriptVars[SCROLL_OFFSET_X] + scrlDistX, 0, Logic::_scriptVars[MAX_SCROLL_OFFSET_X]);
129 		Logic::_scriptVars[SCROLL_OFFSET_Y] = CLIP<int32>(Logic::_scriptVars[SCROLL_OFFSET_Y] + scrlDistY, 0, Logic::_scriptVars[MAX_SCROLL_OFFSET_Y]);
130 	} else {
131 		// SCROLL_FLAG == 0, this usually means that the screen is smaller than 640x400 and doesn't need scrolling at all
132 		// however, it can also mean that the gamescript overwrote the scrolling flag to take care of scrolling directly,
133 		// (see bug report #1345130) so we ignore the offset arguments in this case
134 		Logic::_scriptVars[SCROLL_OFFSET_X] = CLIP<int32>(Logic::_scriptVars[SCROLL_OFFSET_X], 0, Logic::_scriptVars[MAX_SCROLL_OFFSET_X]);
135 		Logic::_scriptVars[SCROLL_OFFSET_Y] = CLIP<int32>(Logic::_scriptVars[SCROLL_OFFSET_Y], 0, Logic::_scriptVars[MAX_SCROLL_OFFSET_Y]);
136 		if ((Logic::_scriptVars[SCROLL_OFFSET_X] != _oldScrollX) || (Logic::_scriptVars[SCROLL_OFFSET_Y] != _oldScrollY)) {
137 			_fullRefresh = true;
138 			_oldScrollX = Logic::_scriptVars[SCROLL_OFFSET_X];
139 			_oldScrollY = Logic::_scriptVars[SCROLL_OFFSET_Y];
140 		}
141 	}
142 }
143 
fadeDownPalette()144 void Screen::fadeDownPalette() {
145 	if (!_isBlack) { // don't fade down twice
146 		_fadingStep = 15;
147 		_fadingDirection = FADE_DOWN;
148 	}
149 }
150 
fadeUpPalette()151 void Screen::fadeUpPalette() {
152 	_fadingStep = 1;
153 	_fadingDirection = FADE_UP;
154 }
155 
fnSetPalette(uint8 start,uint16 length,uint32 id,bool fadeUp)156 void Screen::fnSetPalette(uint8 start, uint16 length, uint32 id, bool fadeUp) {
157 	uint8 *palData = (uint8 *)_resMan->openFetchRes(id);
158 	if (start == 0) // force color 0 to black
159 		palData[0] = palData[1] = palData[2] = 0;
160 
161 	if (SwordEngine::isMac()) {  // see bug #1701058
162 		if (start != 0 && start + length == 256) // and force color 255 to black as well
163 			palData[(length - 1) * 3 + 0] = palData[(length - 1) * 3 + 1] = palData[(length - 1) * 3 + 2] = 0;
164 	}
165 
166 	for (uint32 cnt = 0; cnt < length; cnt++) {
167 		_targetPalette[(start + cnt) * 3 + 0] = palData[cnt * 3 + 0] << 2;
168 		_targetPalette[(start + cnt) * 3 + 1] = palData[cnt * 3 + 1] << 2;
169 		_targetPalette[(start + cnt) * 3 + 2] = palData[cnt * 3 + 2] << 2;
170 	}
171 	_resMan->resClose(id);
172 	_isBlack = false;
173 	if (fadeUp) {
174 		_fadingStep = 1;
175 		_fadingDirection = FADE_UP;
176 		memset(_currentPalette, 0, 256 * 3);
177 		_system->getPaletteManager()->setPalette(_currentPalette, 0, 256);
178 	} else
179 		_system->getPaletteManager()->setPalette(_targetPalette + 3 * start, start, length);
180 }
181 
fullRefresh()182 void Screen::fullRefresh() {
183 	_fullRefresh = true;
184 	_system->getPaletteManager()->setPalette(_targetPalette, 0, 256);
185 }
186 
stillFading()187 bool Screen::stillFading() {
188 	return (_fadingStep != 0);
189 }
190 
showScrollFrame()191 bool Screen::showScrollFrame() {
192 	if ((!_fullRefresh) || Logic::_scriptVars[NEW_PALETTE] || _updatePalette)
193 		return false; // don't draw an additional frame if we aren't scrolling or have to change the palette
194 	if ((_oldScrollX == Logic::_scriptVars[SCROLL_OFFSET_X]) &&
195 	        (_oldScrollY == Logic::_scriptVars[SCROLL_OFFSET_Y]))
196 		return false; // check again if we *really* are scrolling.
197 
198 	uint16 avgScrlX = (uint16)(_oldScrollX + Logic::_scriptVars[SCROLL_OFFSET_X]) / 2;
199 	uint16 avgScrlY = (uint16)(_oldScrollY + Logic::_scriptVars[SCROLL_OFFSET_Y]) / 2;
200 
201 	_system->copyRectToScreen(_screenBuf + avgScrlY * _scrnSizeX + avgScrlX, _scrnSizeX, 0, 40, SCREEN_WIDTH, SCREEN_DEPTH);
202 	_system->updateScreen();
203 	return true;
204 }
205 
updateScreen()206 void Screen::updateScreen() {
207 	if (Logic::_scriptVars[NEW_PALETTE]) {
208 		_fadingStep = 1;
209 		_fadingDirection = FADE_UP;
210 		_updatePalette = true;
211 		Logic::_scriptVars[NEW_PALETTE] = 0;
212 	}
213 	if (_updatePalette) {
214 		fnSetPalette(0, 184, _roomDefTable[_currentScreen].palettes[0], false);
215 		fnSetPalette(184, 72, _roomDefTable[_currentScreen].palettes[1], false);
216 		_updatePalette = false;
217 	}
218 	if (_fadingStep) {
219 		fadePalette();
220 		_system->getPaletteManager()->setPalette(_currentPalette, 0, 256);
221 	}
222 
223 	uint16 scrlX = (uint16)Logic::_scriptVars[SCROLL_OFFSET_X];
224 	uint16 scrlY = (uint16)Logic::_scriptVars[SCROLL_OFFSET_Y];
225 	if (_fullRefresh) {
226 		_fullRefresh = false;
227 		uint16 copyWidth = SCREEN_WIDTH;
228 		uint16 copyHeight = SCREEN_DEPTH;
229 		if (scrlX + copyWidth > _scrnSizeX)
230 			copyWidth = _scrnSizeX - scrlX;
231 		if (scrlY + copyHeight > _scrnSizeY)
232 			copyHeight = _scrnSizeY - scrlY;
233 		_system->copyRectToScreen(_screenBuf + scrlY * _scrnSizeX + scrlX, _scrnSizeX, 0, 40, copyWidth, copyHeight);
234 	} else {
235 		// partial screen update only. The screen coordinates probably won't fit to the
236 		// grid holding the informations on which blocks have to be updated.
237 		// as the grid will be X pixel higher and Y pixel more to the left, this can be cured
238 		// by first checking the top border, then the left column and then the remaining (aligned) part.
239 		uint8 *gridPos = _screenGrid + (scrlX / SCRNGRID_X) + (scrlY / SCRNGRID_Y) * _gridSizeX;
240 		uint8 *scrnBuf = _screenBuf + scrlY * _scrnSizeX + scrlX;
241 		uint8 diffX = (uint8)(scrlX % SCRNGRID_X);
242 		uint8 diffY = (uint8)(scrlY % SCRNGRID_Y);
243 		uint16 gridW = SCREEN_WIDTH / SCRNGRID_X;
244 		uint16 gridH = SCREEN_DEPTH / SCRNGRID_Y;
245 		if (diffY) {
246 			diffY = SCRNGRID_Y - diffY;
247 			uint16 cpWidth = 0;
248 			for (uint16 cntx = 0; cntx < gridW; cntx++)
249 				if (gridPos[cntx]) {
250 					gridPos[cntx] >>= 1;
251 					cpWidth++;
252 				} else if (cpWidth) {
253 					int16 xPos = (cntx - cpWidth) * SCRNGRID_X - diffX;
254 					if (xPos < 0)
255 						xPos = 0;
256 					_system->copyRectToScreen(scrnBuf + xPos, _scrnSizeX, xPos, 40, cpWidth * SCRNGRID_X, diffY);
257 					cpWidth = 0;
258 				}
259 			if (cpWidth) {
260 				int16 xPos = (gridW - cpWidth) * SCRNGRID_X - diffX;
261 				if (xPos < 0)
262 					xPos = 0;
263 				_system->copyRectToScreen(scrnBuf + xPos, _scrnSizeX, xPos, 40, SCREEN_WIDTH - xPos, diffY);
264 			}
265 			scrlY += diffY;
266 		}
267 		// okay, y scrolling is compensated. check x now.
268 		gridPos = _screenGrid + (scrlX / SCRNGRID_X) + (scrlY / SCRNGRID_Y) * _gridSizeX;
269 		scrnBuf = _screenBuf + scrlY * _scrnSizeX + scrlX;
270 		if (diffX) {
271 			diffX = SCRNGRID_X - diffX;
272 			uint16 cpHeight = 0;
273 			for (uint16 cnty = 0; cnty < gridH; cnty++) {
274 				if (*gridPos) {
275 					*gridPos >>= 1;
276 					cpHeight++;
277 				} else if (cpHeight) {
278 					uint16 yPos = (cnty - cpHeight) * SCRNGRID_Y;
279 					_system->copyRectToScreen(scrnBuf + yPos * _scrnSizeX, _scrnSizeX, 0, yPos + diffY + 40, diffX, cpHeight * SCRNGRID_Y);
280 					cpHeight = 0;
281 				}
282 				gridPos += _gridSizeX;
283 			}
284 			if (cpHeight) {
285 				uint16 yPos = (gridH - cpHeight) * SCRNGRID_Y;
286 				_system->copyRectToScreen(scrnBuf + yPos * _scrnSizeX, _scrnSizeX, 0, yPos + diffY + 40, diffX, SCREEN_DEPTH - (yPos + diffY));
287 			}
288 			scrlX += diffX;
289 		}
290 		// x scroll is compensated, too. check the rest of the screen, now.
291 		scrnBuf = _screenBuf + scrlY * _scrnSizeX + scrlX;
292 		gridPos = _screenGrid + (scrlX / SCRNGRID_X) + (scrlY / SCRNGRID_Y) * _gridSizeX;
293 		for (uint16 cnty = 0; cnty < gridH; cnty++) {
294 			uint16 cpWidth = 0;
295 			uint16 cpHeight = SCRNGRID_Y;
296 			if (cnty == gridH - 1)
297 				cpHeight = SCRNGRID_Y - diffY;
298 			for (uint16 cntx = 0; cntx < gridW; cntx++)
299 				if (gridPos[cntx]) {
300 					gridPos[cntx] >>= 1;
301 					cpWidth++;
302 				} else if (cpWidth) {
303 					_system->copyRectToScreen(scrnBuf + (cntx - cpWidth) * SCRNGRID_X, _scrnSizeX, (cntx - cpWidth) * SCRNGRID_X + diffX, cnty * SCRNGRID_Y + diffY + 40, cpWidth * SCRNGRID_X, cpHeight);
304 					cpWidth = 0;
305 				}
306 			if (cpWidth) {
307 				uint16 xPos = (gridW - cpWidth) * SCRNGRID_X;
308 				_system->copyRectToScreen(scrnBuf + xPos, _scrnSizeX, xPos + diffX, cnty * SCRNGRID_Y + diffY + 40, SCREEN_WIDTH - (xPos + diffX), cpHeight);
309 			}
310 			gridPos += _gridSizeX;
311 			scrnBuf += _scrnSizeX * SCRNGRID_Y;
312 		}
313 	}
314 	_system->updateScreen();
315 }
316 
newScreen(uint32 screen)317 void Screen::newScreen(uint32 screen) {
318 	uint8 cnt;
319 	// set sizes and scrolling, initialize/load screengrid, force screen refresh
320 	_currentScreen = screen;
321 	_scrnSizeX = _roomDefTable[screen].sizeX;
322 	_scrnSizeY = _roomDefTable[screen].sizeY;
323 	_gridSizeX = _scrnSizeX / SCRNGRID_X;
324 	_gridSizeY = _scrnSizeY / SCRNGRID_Y;
325 	if ((_scrnSizeX % SCRNGRID_X) || (_scrnSizeY % SCRNGRID_Y))
326 		error("Illegal screensize: %d: %d/%d", screen, _scrnSizeX, _scrnSizeY);
327 	if ((_scrnSizeX > SCREEN_WIDTH) || (_scrnSizeY > SCREEN_DEPTH)) {
328 		Logic::_scriptVars[SCROLL_FLAG] = 2;
329 		Logic::_scriptVars[MAX_SCROLL_OFFSET_X] = _scrnSizeX - SCREEN_WIDTH;
330 		Logic::_scriptVars[MAX_SCROLL_OFFSET_Y] = _scrnSizeY - SCREEN_DEPTH;
331 	} else {
332 		Logic::_scriptVars[SCROLL_FLAG] = 0;
333 		Logic::_scriptVars[MAX_SCROLL_OFFSET_X] = 0;
334 		Logic::_scriptVars[MAX_SCROLL_OFFSET_Y] = 0;
335 	}
336 	Logic::_scriptVars[SCROLL_OFFSET_X] = 0;
337 	Logic::_scriptVars[SCROLL_OFFSET_Y] = 0;
338 
339 	free(_screenBuf);
340 	free(_screenGrid);
341 
342 	if (SwordEngine::isPsx())
343 		flushPsxCache();
344 
345 	_screenBuf = (uint8 *)malloc(_scrnSizeX * _scrnSizeY);
346 	_screenGrid = (uint8 *)malloc(_gridSizeX * _gridSizeY);
347 	memset(_screenGrid, 0, _gridSizeX * _gridSizeY);
348 	for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers; cnt++) {
349 		// open and lock all resources, will be closed in quitScreen()
350 		_layerBlocks[cnt] = (uint8 *)_resMan->openFetchRes(_roomDefTable[_currentScreen].layers[cnt]);
351 		if (cnt > 0)
352 			_layerBlocks[cnt] += sizeof(Header);
353 	}
354 	for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers - 1; cnt++) {
355 		// there's no grid for the background layer, so it's totalLayers - 1
356 		_layerGrid[cnt] = (uint16 *)_resMan->openFetchRes(_roomDefTable[_currentScreen].grids[cnt]);
357 		_layerGrid[cnt] += 14;
358 	}
359 	_parallax[0] = _parallax[1] = NULL;
360 	if (_roomDefTable[_currentScreen].parallax[0])
361 		_parallax[0] = (uint8 *)_resMan->openFetchRes(_roomDefTable[_currentScreen].parallax[0]);
362 	if (_roomDefTable[_currentScreen].parallax[1])
363 		_parallax[1] = (uint8 *)_resMan->openFetchRes(_roomDefTable[_currentScreen].parallax[1]);
364 
365 	_updatePalette = true;
366 	_fullRefresh = true;
367 }
368 
quitScreen()369 void Screen::quitScreen() {
370 	uint8 cnt;
371 	if (SwordEngine::isPsx())
372 		flushPsxCache();
373 	for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers; cnt++)
374 		_resMan->resClose(_roomDefTable[_currentScreen].layers[cnt]);
375 	for (cnt = 0; cnt < _roomDefTable[_currentScreen].totalLayers - 1; cnt++)
376 		_resMan->resClose(_roomDefTable[_currentScreen].grids[cnt]);
377 	if (_roomDefTable[_currentScreen].parallax[0])
378 		_resMan->resClose(_roomDefTable[_currentScreen].parallax[0]);
379 	if (_roomDefTable[_currentScreen].parallax[1])
380 		_resMan->resClose(_roomDefTable[_currentScreen].parallax[1]);
381 	_currentScreen = 0xFFFF;
382 }
383 
draw()384 void Screen::draw() {
385 	uint8 cnt;
386 
387 	debug(8, "Screen::draw() -> _currentScreen %u", _currentScreen);
388 
389 	if (_currentScreen == 54) {
390 		// rm54 has a BACKGROUND parallax layer in parallax[0]
391 		if (_parallax[0] && !SwordEngine::isPsx()) //Avoid drawing this parallax on PSX edition, it gets occluded by background
392 			renderParallax(_parallax[0]);
393 		uint8 *src = _layerBlocks[0];
394 		uint8 *dest = _screenBuf;
395 
396 		if (SwordEngine::isPsx()) {
397 			if (!_psxCache.decodedBackground)
398 				_psxCache.decodedBackground = psxShrinkedBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY);
399 			memcpy(_screenBuf, _psxCache.decodedBackground, _scrnSizeX * _scrnSizeY);
400 		} else {
401 			uint16 scrnScrlY = MIN((uint32)_oldScrollY, Logic::_scriptVars[SCROLL_OFFSET_Y]);
402 			uint16 scrnHeight = SCREEN_DEPTH + ABS((int32)_oldScrollY - (int32)Logic::_scriptVars[SCROLL_OFFSET_Y]);
403 
404 			src += scrnScrlY * _scrnSizeX;
405 			dest += scrnScrlY * _scrnSizeX;
406 
407 			// In this background to create transparency we have to iterate through all pixels, avoid checking those out of screen
408 			for (uint16 cnty = scrnScrlY; (cnty < _scrnSizeY) && (cnty < scrnHeight + scrnScrlY); cnty++)
409 				for (uint16 cntx = 0; cntx < _scrnSizeX; cntx++) {
410 					if (*src)
411 						if (!(SwordEngine::isMac()) || *src != 255) // see bug #1701058
412 							*dest = *src;
413 					src++;
414 					dest++;
415 				}
416 		}
417 
418 	} else if (!(SwordEngine::isPsx())) {
419 		memcpy(_screenBuf, _layerBlocks[0], _scrnSizeX * _scrnSizeY);
420 	} else { //We are using PSX version
421 		if (_currentScreen == 45 || _currentScreen == 55 ||
422 		        _currentScreen == 57 || _currentScreen == 63 || _currentScreen == 71) { // Width shrinked backgrounds
423 			if (!_psxCache.decodedBackground)
424 				_psxCache.decodedBackground = psxShrinkedBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY);
425 		} else {
426 			if (!_psxCache.decodedBackground)
427 				_psxCache.decodedBackground = psxBackgroundToIndexed(_layerBlocks[0], _scrnSizeX, _scrnSizeY);
428 		}
429 		memcpy(_screenBuf, _psxCache.decodedBackground, _scrnSizeX * _scrnSizeY);
430 	}
431 
432 	for (cnt = 0; cnt < _backLength; cnt++)
433 		processImage(_backList[cnt]);
434 
435 	for (cnt = 0; cnt < _sortLength - 1; cnt++)
436 		for (uint8 sCnt = 0; sCnt < _sortLength - 1; sCnt++)
437 			if (_sortList[sCnt].y > _sortList[sCnt + 1].y) {
438 				SWAP(_sortList[sCnt], _sortList[sCnt + 1]);
439 			}
440 	for (cnt = 0; cnt < _sortLength; cnt++)
441 		processImage(_sortList[cnt].id);
442 
443 	if ((_currentScreen != 54) && _parallax[0])
444 		renderParallax(_parallax[0]); // screens other than 54 have FOREGROUND parallax layer in parallax[0]
445 	if (_parallax[1])
446 		renderParallax(_parallax[1]);
447 
448 	// PSX version has parallax layer for this room in an external file (TRAIN.PLX)
449 	if (SwordEngine::isPsx() && _currentScreen == 63) {
450 		// FIXME: this should be handled in a cleaner way...
451 		if (!_psxCache.extPlxCache) {
452 			Common::File parallax;
453 			parallax.open("TRAIN.PLX");
454 			_psxCache.extPlxCache = (uint8 *)malloc(parallax.size());
455 			parallax.read(_psxCache.extPlxCache, parallax.size());
456 			parallax.close();
457 		}
458 		renderParallax(_psxCache.extPlxCache);
459 	}
460 
461 	for (cnt = 0; cnt < _foreLength; cnt++)
462 		processImage(_foreList[cnt]);
463 
464 	_backLength = _sortLength = _foreLength = 0;
465 }
466 
processImage(uint32 id)467 void Screen::processImage(uint32 id) {
468 	Object *compact;
469 	FrameHeader *frameHead;
470 	int scale;
471 
472 	compact = _objMan->fetchObject(id);
473 
474 	if (compact->o_type == TYPE_TEXT)
475 		frameHead = _textMan->giveSpriteData((uint8)compact->o_target);
476 	else
477 		frameHead = _resMan->fetchFrame(_resMan->openFetchRes(compact->o_resource), compact->o_frame);
478 
479 	uint8 *sprData = ((uint8 *)frameHead) + sizeof(FrameHeader);
480 
481 	uint16 spriteX = compact->o_anim_x;
482 	uint16 spriteY = compact->o_anim_y;
483 
484 	if (compact->o_status & STAT_SHRINK) {
485 		scale = (compact->o_scale_a * compact->o_ycoord + compact->o_scale_b) / 256;
486 		spriteX += ((int16)_resMan->readUint16(&frameHead->offsetX) * scale) / 256;
487 		spriteY += ((int16)_resMan->readUint16(&frameHead->offsetY) * scale) / 256;
488 	} else {
489 		scale = 256;
490 		spriteX += (int16)_resMan->readUint16(&frameHead->offsetX);
491 		spriteY += (int16)_resMan->readUint16(&frameHead->offsetY);
492 	}
493 
494 	uint8 *tonyBuf = NULL;
495 	uint8 *hifBuf = NULL;
496 	if (SwordEngine::isPsx() && compact->o_type != TYPE_TEXT) { // PSX sprites are compressed with HIF
497 		hifBuf = (uint8 *)malloc(_resMan->readUint16(&frameHead->width) * _resMan->readUint16(&frameHead->height) / 2);
498 		memset(hifBuf, 0x00, (_resMan->readUint16(&frameHead->width) * _resMan->readUint16(&frameHead->height) / 2));
499 		decompressHIF(sprData, hifBuf);
500 		sprData = hifBuf;
501 	} else if (frameHead->runTimeComp[3] == '7') { // RLE7 encoded?
502 		decompressRLE7(sprData, _resMan->readUint32(&frameHead->compSize), _rleBuffer);
503 		sprData = _rleBuffer;
504 	} else if (frameHead->runTimeComp[3] == '0') { // RLE0 encoded?
505 		decompressRLE0(sprData, _resMan->readUint32(&frameHead->compSize), _rleBuffer);
506 		sprData = _rleBuffer;
507 	} else if (frameHead->runTimeComp[1] == 'I') { // new type
508 		tonyBuf = (uint8 *)malloc(_resMan->readUint16(&frameHead->width) * _resMan->readUint16(&frameHead->height));
509 		decompressTony(sprData, _resMan->readUint32(&frameHead->compSize), tonyBuf);
510 		sprData = tonyBuf;
511 	}
512 
513 	uint16 sprSizeX, sprSizeY;
514 	if (compact->o_status & STAT_SHRINK) {
515 		memset(_shrinkBuffer, 0, SHRINK_BUFFER_SIZE); //Clean shrink buffer to avoid corruption
516 		if (SwordEngine::isPsx() && (compact->o_resource != GEORGE_MEGA)) { //PSX Height shrinked sprites
517 			sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256;
518 			sprSizeY = (scale * (_resMan->readUint16(&frameHead->height))) / 256 / 2;
519 			fastShrink(sprData, _resMan->readUint16(&frameHead->width), (_resMan->readUint16(&frameHead->height)) / 2, scale, _shrinkBuffer);
520 		} else if (SwordEngine::isPsx()) { //PSX width/height shrinked sprites
521 			sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256 / 2;
522 			sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256 / 2;
523 			fastShrink(sprData, _resMan->readUint16(&frameHead->width) / 2, _resMan->readUint16(&frameHead->height) / 2, scale, _shrinkBuffer);
524 		} else {
525 			sprSizeX = (scale * _resMan->readUint16(&frameHead->width)) / 256;
526 			sprSizeY = (scale * _resMan->readUint16(&frameHead->height)) / 256;
527 			fastShrink(sprData, _resMan->readUint16(&frameHead->width), _resMan->readUint16(&frameHead->height), scale, _shrinkBuffer);
528 		}
529 		sprData = _shrinkBuffer;
530 	} else {
531 		sprSizeX = _resMan->readUint16(&frameHead->width);
532 		if (SwordEngine::isPsx()) { //PSX sprites are half height
533 			sprSizeY = _resMan->readUint16(&frameHead->height) / 2;
534 		} else
535 			sprSizeY = (_resMan->readUint16(&frameHead->height));
536 	}
537 
538 	if (!(compact->o_status & STAT_OVERRIDE)) {
539 		//mouse size linked to exact size & coordinates of sprite box - shrink friendly
540 		if (_resMan->readUint16(&frameHead->offsetX) || _resMan->readUint16(&frameHead->offsetY)) {
541 			//for megas the mouse area is reduced to account for sprite not
542 			//filling the box size is reduced to 1/2 width, 4/5 height
543 			compact->o_mouse_x1 = spriteX + sprSizeX / 4;
544 			compact->o_mouse_x2 = spriteX + (3 * sprSizeX) / 4;
545 			compact->o_mouse_y1 = spriteY + sprSizeY / 10;
546 			compact->o_mouse_y2 = spriteY + (9 * sprSizeY) / 10;
547 		} else {
548 			compact->o_mouse_x1 = spriteX;
549 			compact->o_mouse_x2 = spriteX + sprSizeX;
550 			compact->o_mouse_y1 = spriteY;
551 			compact->o_mouse_y2 = spriteY + sprSizeY;
552 		}
553 	}
554 
555 	uint16 sprPitch = sprSizeX;
556 	uint16 incr;
557 	spriteClipAndSet(&spriteX, &spriteY, &sprSizeX, &sprSizeY, &incr);
558 
559 	if ((sprSizeX > 0) && (sprSizeY > 0)) {
560 		if ((!(SwordEngine::isPsx()) || (compact->o_type == TYPE_TEXT)
561 		        || (compact->o_resource == LVSFLY) || (!(compact->o_resource == GEORGE_MEGA) && (sprSizeX < 260))))
562 			drawSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch);
563 		else if (((sprSizeX >= 260) && (sprSizeX < 450)) || ((compact->o_resource == GMWRITH) && (sprSizeX < 515))  // a psx shrinked sprite (1/2 width)
564 		         || ((compact->o_resource == GMPOWER) && (sprSizeX < 515)))                                         // some needs to be hardcoded, headers don't give useful infos
565 			drawPsxHalfShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 2, sprSizeY, sprPitch / 2);
566 		else if (sprSizeX >= 450) // A PSX double shrinked sprite (1/3 width)
567 			drawPsxFullShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX / 3, sprSizeY, sprPitch / 3);
568 		else // This is for psx half shrinked, walking george and remaining sprites
569 			drawPsxHalfShrinkedSprite(sprData + incr, spriteX, spriteY, sprSizeX, sprSizeY, sprPitch);
570 		if (!(compact->o_status & STAT_FORE) && !(SwordEngine::isPsx() && (compact->o_resource == MOUBUSY))) // Check fixes moue sprite being masked by layer, happens only on psx
571 			verticalMask(spriteX, spriteY, sprSizeX, sprSizeY);
572 	}
573 
574 	if (compact->o_type != TYPE_TEXT)
575 		_resMan->resClose(compact->o_resource);
576 
577 	free(tonyBuf);
578 	free(hifBuf);
579 }
580 
verticalMask(uint16 x,uint16 y,uint16 bWidth,uint16 bHeight)581 void Screen::verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight) {
582 	if (_roomDefTable[_currentScreen].totalLayers <= 1)
583 		return;
584 
585 	if (SwordEngine::isPsx()) { // PSX sprites are vertical shrinked, and some width shrinked
586 		bHeight *= 2;
587 		bWidth *= 2;
588 	}
589 
590 	bWidth = (bWidth + (x & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X;
591 	bHeight = (bHeight + (y & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y;
592 
593 	x /= SCRNGRID_X;
594 	y /= SCRNGRID_Y;
595 	if (x + bWidth > _gridSizeX)
596 		bWidth = _gridSizeX - x;
597 	if (y + bHeight > _gridSizeY)
598 		bHeight = _gridSizeY - y;
599 
600 	uint16 gridY = y + SCREEN_TOP_EDGE / SCRNGRID_Y; // imaginary screen on top
601 	gridY += bHeight - 1; // we start from the bottom edge
602 	uint16 gridX = x + SCREEN_LEFT_EDGE / SCRNGRID_X; // imaginary screen left
603 	uint16 lGridSizeX = _gridSizeX + 2 * (SCREEN_LEFT_EDGE / SCRNGRID_X); // width of the grid for the imaginary screen
604 
605 	for (uint16 blkx = 0; blkx < bWidth; blkx++) {
606 		// A sprite can be masked by several layers at the same time,
607 		// so we have to check them all. See bug #917427.
608 		for (int16 level = _roomDefTable[_currentScreen].totalLayers - 2; level >= 0; level--) {
609 			if (_layerGrid[level][gridX + blkx + gridY * lGridSizeX]) {
610 				uint16 *grid = _layerGrid[level] + gridX + blkx + gridY * lGridSizeX;
611 				for (int16 blky = bHeight - 1; blky >= 0; blky--) {
612 					if (*grid) {
613 						uint8 *blkData;
614 						if (SwordEngine::isPsx())
615 							blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 64; //PSX layers are half height too...
616 						else
617 							blkData = _layerBlocks[level + 1] + (_resMan->readUint16(grid) - 1) * 128;
618 						blitBlockClear(x + blkx, y + blky, blkData);
619 					} else
620 						break;
621 					grid -= lGridSizeX;
622 				}
623 			}
624 		}
625 	}
626 }
627 
blitBlockClear(uint16 x,uint16 y,uint8 * data)628 void Screen::blitBlockClear(uint16 x, uint16 y, uint8 *data) {
629 	uint8 *dest = _screenBuf + (y * SCRNGRID_Y) * _scrnSizeX + (x * SCRNGRID_X);
630 
631 	for (uint8 cnty = 0; cnty < (SwordEngine::isPsx() ? SCRNGRID_Y / 2 : SCRNGRID_Y); cnty++) {
632 		for (uint8 cntx = 0; cntx < SCRNGRID_X; cntx++)
633 			if (data[cntx])
634 				dest[cntx] = data[cntx];
635 
636 		if (SwordEngine::isPsx()) {
637 			dest += _scrnSizeX;
638 			for (uint8 cntx = 0; cntx < SCRNGRID_X; cntx++)
639 				if (data[cntx])
640 					dest[cntx] = data[cntx];
641 		}
642 
643 		data += SCRNGRID_X;
644 		dest += _scrnSizeX;
645 	}
646 }
647 
renderParallax(uint8 * data)648 void Screen::renderParallax(uint8 *data) {
649 	uint16 paraScrlX, paraScrlY;
650 	uint16 scrnScrlX, scrnScrlY;
651 	uint16 scrnWidth, scrnHeight;
652 	uint16 paraSizeX, paraSizeY;
653 	ParallaxHeader *header = NULL;
654 	uint32 *lineIndexes = NULL;
655 
656 	if (SwordEngine::isPsx()) //Parallax headers are different in PSX version
657 		fetchPsxParallaxSize(data, &paraSizeX, &paraSizeY);
658 	else {
659 		header = (ParallaxHeader *)data;
660 		lineIndexes = (uint32 *)(data + sizeof(ParallaxHeader));
661 		paraSizeX = _resMan->getUint16(header->sizeX);
662 		paraSizeY = _resMan->getUint16(header->sizeY);
663 	}
664 
665 	assert((paraSizeX >= SCREEN_WIDTH) && (paraSizeY >= SCREEN_DEPTH));
666 
667 	// we have to render more than the visible screen part for displaying scroll frames
668 	scrnScrlX = MIN((uint32)_oldScrollX, Logic::_scriptVars[SCROLL_OFFSET_X]);
669 	scrnWidth = SCREEN_WIDTH + ABS((int32)_oldScrollX - (int32)Logic::_scriptVars[SCROLL_OFFSET_X]);
670 	scrnScrlY = MIN((uint32)_oldScrollY, Logic::_scriptVars[SCROLL_OFFSET_Y]);
671 	scrnHeight = SCREEN_DEPTH + ABS((int32)_oldScrollY - (int32)Logic::_scriptVars[SCROLL_OFFSET_Y]);
672 
673 
674 	if (_scrnSizeX != SCREEN_WIDTH) {
675 		double scrlfx = (paraSizeX - SCREEN_WIDTH) / ((double)(_scrnSizeX - SCREEN_WIDTH));
676 		paraScrlX = (uint16)(scrnScrlX * scrlfx);
677 	} else
678 		paraScrlX = 0;
679 
680 	if (_scrnSizeY != SCREEN_DEPTH) {
681 		double scrlfy = (paraSizeY - SCREEN_DEPTH) / ((double)(_scrnSizeY - SCREEN_DEPTH));
682 		paraScrlY = (uint16)(scrnScrlY * scrlfy);
683 	} else
684 		paraScrlY = 0;
685 
686 	if (SwordEngine::isPsx())
687 		drawPsxParallax(data, paraScrlX, scrnScrlX, scrnWidth);
688 	else
689 		for (uint16 cnty = 0; cnty < scrnHeight; cnty++) {
690 			uint8 *src = data + _resMan->readUint32(lineIndexes + cnty + paraScrlY);
691 			uint8 *dest = _screenBuf + scrnScrlX + (cnty + scrnScrlY) * _scrnSizeX;
692 			uint16 remain = paraScrlX;
693 			uint16 xPos = 0;
694 			while (remain) { // skip past the first part of the parallax to get to the right scrolling position
695 				uint8 doSkip = *src++;
696 				if (doSkip <= remain)
697 					remain -= doSkip;
698 				else {
699 					xPos = doSkip - remain;
700 					dest += xPos;
701 					remain = 0;
702 				}
703 				uint8 doCopy = *src++;
704 				if (doCopy <= remain) {
705 					remain -= doCopy;
706 					src += doCopy;
707 				} else {
708 					uint16 remCopy = doCopy - remain;
709 					memcpy(dest, src + remain, remCopy);
710 					dest += remCopy;
711 					src += doCopy;
712 					xPos = remCopy;
713 					remain = 0;
714 				}
715 			}
716 			while (xPos < scrnWidth) {
717 				if (uint8 skip = *src++) {
718 					dest += skip;
719 					xPos += skip;
720 				}
721 				if (xPos < scrnWidth) {
722 					if (uint8 doCopy = *src++) {
723 						if (xPos + doCopy > scrnWidth)
724 							doCopy = scrnWidth - xPos;
725 						memcpy(dest, src, doCopy);
726 						dest += doCopy;
727 						xPos += doCopy;
728 						src += doCopy;
729 					}
730 				}
731 			}
732 		}
733 }
734 
drawSprite(uint8 * sprData,uint16 sprX,uint16 sprY,uint16 sprWidth,uint16 sprHeight,uint16 sprPitch)735 void Screen::drawSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) {
736 	uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX;
737 
738 	for (uint16 cnty = 0; cnty < sprHeight; cnty++) {
739 		for (uint16 cntx = 0; cntx < sprWidth; cntx++)
740 			if (sprData[cntx])
741 				dest[cntx] = sprData[cntx];
742 
743 		if (SwordEngine::isPsx()) { //On PSX version we need to double horizontal lines
744 			dest += _scrnSizeX;
745 			for (uint16 cntx = 0; cntx < sprWidth; cntx++)
746 				if (sprData[cntx])
747 					dest[cntx] = sprData[cntx];
748 		}
749 
750 		sprData += sprPitch;
751 		dest += _scrnSizeX;
752 	}
753 }
754 
755 // Used to draw psx sprites which are 1/2 of original width
drawPsxHalfShrinkedSprite(uint8 * sprData,uint16 sprX,uint16 sprY,uint16 sprWidth,uint16 sprHeight,uint16 sprPitch)756 void Screen::drawPsxHalfShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) {
757 	uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX;
758 
759 	for (uint16 cnty = 0; cnty < sprHeight; cnty++) {
760 		for (uint16 cntx = 0; cntx < sprWidth; cntx++)
761 			if (sprData[cntx]) {
762 				dest[cntx * 2] = sprData[cntx]; //In these sprites we need to double vetical lines too...
763 				dest[cntx * 2 + 1] = sprData[cntx];
764 			}
765 
766 		dest += _scrnSizeX;
767 		for (uint16 cntx = 0; cntx < sprWidth; cntx++)
768 			if (sprData[cntx]) {
769 				dest[cntx * 2] = sprData[cntx];
770 				dest[cntx * 2 + 1] = sprData[cntx];
771 			}
772 
773 		sprData += sprPitch;
774 		dest += _scrnSizeX;
775 	}
776 }
777 
778 // Used to draw psx sprites which are 1/3 of original width
drawPsxFullShrinkedSprite(uint8 * sprData,uint16 sprX,uint16 sprY,uint16 sprWidth,uint16 sprHeight,uint16 sprPitch)779 void Screen::drawPsxFullShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch) {
780 	uint8 *dest = _screenBuf + (sprY * _scrnSizeX) + sprX;
781 
782 	for (uint16 cnty = 0; cnty < sprHeight; cnty++) {
783 		for (uint16 cntx = 0; cntx < sprWidth; cntx++)
784 			if (sprData[cntx]) {
785 				dest[cntx * 3] = sprData[cntx]; //In these sprites we need to double vertical lines too...
786 				dest[cntx * 3 + 1] = sprData[cntx];
787 				dest[cntx * 3 + 2] = sprData[cntx];
788 			}
789 
790 		dest += _scrnSizeX;
791 		for (uint16 cntx = 0; cntx < sprWidth; cntx++)
792 			if (sprData[cntx]) {
793 				dest[cntx * 3] = sprData[cntx];
794 				dest[cntx * 3 + 1] = sprData[cntx];
795 				dest[cntx * 3 + 2] = sprData[cntx];
796 			}
797 
798 		sprData += sprPitch;
799 		dest += _scrnSizeX;
800 	}
801 }
802 
803 // nearest neighbor filter:
fastShrink(uint8 * src,uint32 width,uint32 height,uint32 scale,uint8 * dest)804 void Screen::fastShrink(uint8 *src, uint32 width, uint32 height, uint32 scale, uint8 *dest) {
805 	uint32 resHeight = (height * scale) >> 8;
806 	uint32 resWidth = (width * scale) >> 8;
807 	uint32 step = 0x10000 / scale;
808 	uint8 columnTab[160];
809 	uint32 res = step >> 1;
810 
811 	for (uint16 cnt = 0; cnt < resWidth; cnt++) {
812 		columnTab[cnt] = (uint8)(res >> 8);
813 		res += step;
814 	}
815 
816 	uint32 newRow = step >> 1;
817 	uint32 oldRow = 0;
818 
819 	uint8 *destPos = dest;
820 	uint16 lnCnt;
821 	for (lnCnt = 0; lnCnt < resHeight; lnCnt++) {
822 		while (oldRow < (newRow >> 8)) {
823 			oldRow++;
824 			src += width;
825 		}
826 		for (uint16 colCnt = 0; colCnt < resWidth; colCnt++) {
827 			*destPos++ = src[columnTab[colCnt]];
828 		}
829 		newRow += step;
830 	}
831 	// scaled, now stipple shadows if there are any
832 	for (lnCnt = 0; lnCnt < resHeight; lnCnt++) {
833 		uint16 xCnt = lnCnt & 1;
834 		destPos = dest + lnCnt * resWidth + (lnCnt & 1);
835 		while (xCnt < resWidth) {
836 			if (*destPos == 200)
837 				*destPos = 0;
838 			destPos += 2;
839 			xCnt += 2;
840 		}
841 	}
842 }
843 
addToGraphicList(uint8 listId,uint32 objId)844 void Screen::addToGraphicList(uint8 listId, uint32 objId) {
845 	if (listId == 0) {
846 		assert(_foreLength < MAX_FORE);
847 		_foreList[_foreLength++] = objId;
848 	}
849 	if (listId == 1) {
850 		assert(_sortLength < MAX_SORT);
851 		Object *cpt = _objMan->fetchObject(objId);
852 		_sortList[_sortLength].id = objId;
853 		_sortList[_sortLength].y = cpt->o_anim_y; // gives feet coords if boxed mega, otherwise top of sprite box
854 		if (!(cpt->o_status & STAT_SHRINK)) {     // not a boxed mega using shrinking
855 			Header *frameRaw = (Header *)_resMan->openFetchRes(cpt->o_resource);
856 			FrameHeader *frameHead = _resMan->fetchFrame(frameRaw, cpt->o_frame);
857 			_sortList[_sortLength].y += _resMan->readUint16(&frameHead->height) - 1; // now pointing to base of sprite
858 			_resMan->resClose(cpt->o_resource);
859 		}
860 		_sortLength++;
861 	}
862 	if (listId == 2) {
863 		assert(_backLength < MAX_BACK);
864 		_backList[_backLength++] = objId;
865 	}
866 }
867 
psxBackgroundToIndexed(uint8 * psxBackground,uint32 bakXres,uint32 bakYres)868 uint8 *Screen::psxBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint32 bakYres) {
869 	uint32 xresInTiles = bakXres / 16;
870 	uint32 yresInTiles = ((bakYres / 2) % 16) ? (bakYres / 32) + 1 : (bakYres / 32);
871 	uint32 totTiles = xresInTiles * yresInTiles;
872 	uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid
873 	uint32 tileXpos = 0;
874 	uint32 tag = READ_LE_UINT32(psxBackground);
875 
876 	uint8 *decomp_tile = (uint8 *)malloc(16 * 16); //Tiles are always 16 * 16
877 	uint8 *fullres_buffer = (uint8 *)malloc(bakXres * yresInTiles * 32);
878 	memset(fullres_buffer, 0, bakXres * yresInTiles * 32);
879 
880 	bool isCompressed = (tag == 0x434F4D50);
881 
882 	psxBackground += 4; //We skip the id tag
883 
884 	for (uint32 currentTile = 0; currentTile < totTiles; currentTile++) {
885 		uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile);
886 
887 		if (isCompressed)
888 			decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile
889 		else
890 			memcpy(decomp_tile, psxBackground + tileOffset - 4, 16 * 16);
891 
892 		if (currentTile > 0 && !(currentTile % xresInTiles)) { //Finished a line of tiles, going down
893 			tileYpos++;
894 			tileXpos = 0;
895 		}
896 
897 		for (byte tileLine = 0; tileLine < 16; tileLine++) { // Copy data to destination buffer
898 			memcpy(fullres_buffer + tileLine * bakXres * 2 + tileXpos * 16 + tileYpos * bakXres * 16 * 2, decomp_tile + tileLine * 16, 16);
899 			memcpy(fullres_buffer + tileLine * bakXres * 2 + bakXres + tileXpos * 16 + tileYpos * bakXres * 16 * 2, decomp_tile + tileLine * 16, 16);
900 		}
901 		tileXpos++;
902 	}
903 
904 	free(decomp_tile);
905 
906 	return fullres_buffer;
907 }
908 
909 // needed because some psx backgrounds are half width and half height
psxShrinkedBackgroundToIndexed(uint8 * psxBackground,uint32 bakXres,uint32 bakYres)910 uint8 *Screen::psxShrinkedBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint32 bakYres) {
911 	uint32 xresInTiles = ((bakXres / 2) % 16) ? (bakXres / 32) + 1 : (bakXres / 32);
912 	uint32 yresInTiles = ((bakYres / 2) % 16) ? (bakYres / 32) + 1 : (bakYres / 32);
913 	uint32 totTiles = xresInTiles * yresInTiles;
914 	uint32 tileYpos = 0; //tile position in a virtual xresInTiles * yresInTiles grid
915 	uint32 tileXpos = 0;
916 	uint32 dataBegin = READ_LE_UINT32(psxBackground + 4);
917 
918 	uint8 *decomp_tile = (uint8 *)malloc(16 * 16); //Tiles are always 16 * 16
919 	uint8 *fullres_buffer = (uint8 *)malloc(bakXres * (yresInTiles + 1) * 32);
920 	memset(fullres_buffer, 0, bakXres * (yresInTiles + 1) * 32);
921 
922 	bool isCompressed = (READ_LE_UINT32(psxBackground) == MKTAG('C', 'O', 'M', 'P'));
923 
924 	totTiles -= xresInTiles;
925 	psxBackground += 4; //We skip the id tag
926 
927 	uint32 currentTile;
928 	for (currentTile = 0; currentTile < totTiles; currentTile++) {
929 		uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile);
930 
931 		if (isCompressed)
932 			decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile
933 		else
934 			memcpy(decomp_tile, psxBackground + tileOffset - 4, 16 * 16);
935 
936 		if (currentTile > 0 && !(currentTile % xresInTiles)) { //Finished a line of tiles, going down
937 			tileYpos++;
938 			tileXpos = 0;
939 		}
940 
941 		for (byte tileLine = 0; tileLine < 16; tileLine++) {
942 			uint8 *dest = fullres_buffer + tileLine * bakXres * 2 + tileXpos * 32 + tileYpos * bakXres * 16 * 2;
943 			for (byte tileColumn = 0; tileColumn < 16; tileColumn++) {
944 				uint8 pixData = *(decomp_tile + tileColumn + tileLine * 16);
945 				*(dest + tileColumn * 2) = pixData;
946 				*(dest + tileColumn * 2 + 1) = pixData;
947 			}
948 			dest += bakXres;
949 			for (byte tileColumn = 0; tileColumn < 16; tileColumn++) {
950 				uint8 pixData = *(decomp_tile + tileColumn + tileLine * 16);
951 				*(dest + tileColumn * 2) = pixData;
952 				*(dest + tileColumn * 2 + 1) = pixData;
953 			}
954 
955 		}
956 		tileXpos++;
957 	}
958 
959 	//Calculate number of remaining tiles
960 	uint32 remainingTiles = (dataBegin - (currentTile * 4 + 4)) / 4;
961 
962 	// Last line of tiles is full width!
963 	uint32 tileHeight = (remainingTiles == xresInTiles * 2) ? 16 : 8;
964 
965 	tileXpos = 0;
966 	for (; currentTile < totTiles + remainingTiles; currentTile++) {
967 		uint32 tileOffset = READ_LE_UINT32(psxBackground + 4 * currentTile);
968 
969 		if (isCompressed)
970 			decompressHIF(psxBackground + tileOffset - 4, decomp_tile); //Decompress the tile into decomp_tile
971 		else
972 			memcpy(decomp_tile, psxBackground + tileOffset - 4, 256);
973 
974 		for (byte tileLine = 0; tileLine < tileHeight; tileLine++) { // Write the decoded tiles into last lines of background
975 			memcpy(fullres_buffer + tileXpos * 16 + (tileLine + (yresInTiles - 1) * 16) * bakXres * 2, decomp_tile + tileLine * 16, 16);
976 			memcpy(fullres_buffer + tileXpos * 16 + (tileLine + (yresInTiles - 1) * 16) * bakXres * 2 + bakXres, decomp_tile + tileLine * 16, 16);
977 		}
978 		tileXpos++;
979 	}
980 
981 	free(decomp_tile);
982 
983 	return fullres_buffer;
984 }
985 
fetchPsxParallaxSize(uint8 * psxParallax,uint16 * paraSizeX,uint16 * paraSizeY)986 void Screen::fetchPsxParallaxSize(uint8 *psxParallax, uint16 *paraSizeX, uint16 *paraSizeY) {
987 	uint16 xresInTiles = READ_LE_UINT16(psxParallax + 10);
988 	uint16 yresInTiles = READ_LE_UINT16(psxParallax + 12);
989 
990 	*paraSizeX = xresInTiles * 16;
991 	*paraSizeY = yresInTiles * 32; // Vertical resolution needs to be doubled
992 }
993 
drawPsxParallax(uint8 * psxParallax,uint16 paraScrlX,uint16 scrnScrlX,uint16 scrnWidth)994 void Screen::drawPsxParallax(uint8 *psxParallax, uint16 paraScrlX, uint16 scrnScrlX, uint16 scrnWidth) {
995 	uint16 totTiles = READ_LE_UINT16(psxParallax + 14); // Total tiles
996 
997 	uint16 skipRow = paraScrlX / 16; // Rows of tiles we have to skip
998 	uint8  leftPixelSkip = paraScrlX % 16; // Pixel columns we have to skip while drawing the first row
999 
1000 	uint8 *plxPos = psxParallax + 16; // Pointer to tile position header section
1001 	uint8 *plxOff = psxParallax + 16 + totTiles * 2; // Pointer to tile relative offsets section
1002 	uint8 *plxData = psxParallax + 16 + totTiles * 2 + totTiles * 4; //Pointer to beginning of tiles data section
1003 
1004 	uint8 *tile_buffer = (uint8 *)malloc(16 * 16); // Buffer for 16x16 pix tile
1005 
1006 	/* For parallax rendering we should check both horizontal and vertical scrolling,
1007 	 * but in PSX edition of the game, the only vertical scrolling parallax is disabled.
1008 	 * So, in this function i'll only check for horizontal scrolling.
1009 	 */
1010 
1011 	for (uint16 currentTile = 0; currentTile < totTiles - 1; currentTile++) {
1012 		uint8 tileXpos = *(plxPos + 2 * currentTile); // Fetch tile X and Y position in the grid
1013 		uint8 tileYpos = *(plxPos + 2 * currentTile + 1) * 2;
1014 		int32 tileBegin = (tileXpos * 16) - paraScrlX;
1015 		tileBegin = (tileBegin < 0) ? 0 : tileBegin;
1016 		uint16 currentLine = (tileYpos * 16); //Current line of the image we are drawing upon, used to avoid going out of screen
1017 
1018 		if (tileXpos >= skipRow) { // Tiles not needed in the screen buffer are not uncompressed
1019 			uint32 tileOffset = READ_LE_UINT32(plxOff + 4 * currentTile);
1020 			uint16 rightScreenLimit = _scrnSizeX - scrnScrlX; // Do not write over and beyond this limit, lest we get memory corruption
1021 			uint8 *dest = _screenBuf + (tileYpos * 16 * _scrnSizeX) + tileBegin + scrnScrlX;
1022 			uint8 *src = tile_buffer;
1023 
1024 			decompressHIF(plxData + tileOffset, tile_buffer); // Decompress the tile
1025 
1026 			if (tileXpos != skipRow) { // This tile will surely be drawn fully in the buffer
1027 				for (byte tileLine = 0; (tileLine < 16) && (currentLine < SCREEN_DEPTH); tileLine++) { // Check that we are not going outside the bottom screen part
1028 					for (byte tileColumn = 0; (tileColumn < 16) && (tileBegin + tileColumn) < rightScreenLimit; tileColumn++)
1029 						if (*(src + tileColumn)) *(dest + tileColumn) = *(src + tileColumn);
1030 					dest += _scrnSizeX;
1031 					currentLine++;
1032 
1033 					if (currentLine < SCREEN_DEPTH) {
1034 						for (byte tileColumn = 0; (tileColumn < 16) && (tileBegin + tileColumn) < rightScreenLimit; tileColumn++)
1035 							if (*(src + tileColumn)) *(dest + tileColumn) = *(src + tileColumn);
1036 						dest += _scrnSizeX;
1037 						currentLine++;
1038 					}
1039 					src += 16; // get to next line of decoded tile
1040 				}
1041 			} else { // This tile may be drawn only partially
1042 				src += leftPixelSkip; //Skip hidden pixels
1043 				for (byte tileLine = 0; (tileLine < 16) && (currentLine < SCREEN_DEPTH); tileLine++) {
1044 					for (byte tileColumn = 0; tileColumn < (16 - leftPixelSkip); tileColumn++)
1045 						if (*(src + tileColumn)) *(dest + tileColumn) = *(src + tileColumn);
1046 					dest += _scrnSizeX;
1047 					currentLine++;
1048 
1049 					if (currentLine < SCREEN_DEPTH) {
1050 						for (byte tileColumn = 0; tileColumn < (16 - leftPixelSkip); tileColumn++)
1051 							if (*(src + tileColumn)) *(dest + tileColumn) = *(src + tileColumn);
1052 						dest += _scrnSizeX;
1053 						currentLine++;
1054 					}
1055 					src += 16;
1056 				}
1057 			}
1058 		}
1059 	}
1060 
1061 	free(tile_buffer);
1062 }
1063 
decompressTony(uint8 * src,uint32 compSize,uint8 * dest)1064 void Screen::decompressTony(uint8 *src, uint32 compSize, uint8 *dest) {
1065 	uint8 *endOfData = src + compSize;
1066 	while (src < endOfData) {
1067 		uint8 numFlat = *src++;
1068 		if (numFlat) {
1069 			memset(dest, *src, numFlat);
1070 			src++;
1071 			dest += numFlat;
1072 		}
1073 		if (src < endOfData) {
1074 			uint8 numNoFlat = *src++;
1075 			memcpy(dest, src, numNoFlat);
1076 			src += numNoFlat;
1077 			dest += numNoFlat;
1078 		}
1079 	}
1080 }
1081 
decompressRLE7(uint8 * src,uint32 compSize,uint8 * dest)1082 void Screen::decompressRLE7(uint8 *src, uint32 compSize, uint8 *dest) {
1083 	uint8 *compBufEnd = src + compSize;
1084 	while (src < compBufEnd) {
1085 		uint8 code = *src++;
1086 		if ((code > 127) || (code == 0))
1087 			*dest++ = code;
1088 		else {
1089 			code++;
1090 			memset(dest, *src++, code);
1091 			dest += code;
1092 		}
1093 	}
1094 }
1095 
decompressRLE0(uint8 * src,uint32 compSize,uint8 * dest)1096 void Screen::decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest) {
1097 	uint8 *srcBufEnd = src + compSize;
1098 	while (src < srcBufEnd) {
1099 		uint8 color = *src++;
1100 		if (color) {
1101 			*dest++ = color;
1102 		} else {
1103 			uint8 skip = *src++;
1104 			memset(dest, 0, skip);
1105 			dest += skip;
1106 		}
1107 	}
1108 }
1109 
decompressHIF(uint8 * src,uint8 * dest)1110 void Screen::decompressHIF(uint8 *src, uint8 *dest) {
1111 	for (;;) { //Main loop
1112 		byte control_byte = *src++;
1113 		uint32 byte_count = 0;
1114 		while (byte_count < 8) {
1115 			if (control_byte & 0x80) {
1116 				uint16 info_word = READ_BE_UINT16(src); //Read the info word
1117 				src += 2;
1118 				if (info_word == 0xFFFF) return; //Got 0xFFFF code, finished.
1119 
1120 				int32 repeat_count = (info_word >> 12) + 2; //How many time data needs to be refetched
1121 				while (repeat_count >= 0) {
1122 					uint8 *old_data_src = dest - ((info_word & 0xFFF) + 1);
1123 					*dest++ = *old_data_src;
1124 					repeat_count--;
1125 				}
1126 			} else
1127 				*dest++ = *src++;
1128 			byte_count++;
1129 			control_byte <<= 1; //Shifting left the control code one bit
1130 		}
1131 	}
1132 }
1133 
flushPsxCache()1134 void Screen::flushPsxCache() {
1135 	if (_psxCache.decodedBackground) {
1136 		free(_psxCache.decodedBackground);
1137 		_psxCache.decodedBackground = NULL;
1138 	}
1139 
1140 	if (_psxCache.extPlxCache) {
1141 		free(_psxCache.extPlxCache);
1142 		_psxCache.extPlxCache = NULL;
1143 	}
1144 }
1145 
fadePalette()1146 void Screen::fadePalette() {
1147 	if (_fadingStep == 16)
1148 		memcpy(_currentPalette, _targetPalette, 256 * 3);
1149 	else if ((_fadingStep == 1) && (_fadingDirection == FADE_DOWN)) {
1150 		memset(_currentPalette, 0, 3 * 256);
1151 	} else
1152 		for (uint16 cnt = 0; cnt < 256 * 3; cnt++)
1153 			_currentPalette[cnt] = (_targetPalette[cnt] * _fadingStep) >> 4;
1154 
1155 	_fadingStep += _fadingDirection;
1156 	if (_fadingStep == 17) {
1157 		_fadingStep = 0;
1158 		_isBlack = false;
1159 	} else if (_fadingStep == 0)
1160 		_isBlack = true;
1161 }
1162 
fnSetParallax(uint32 screen,uint32 resId)1163 void Screen::fnSetParallax(uint32 screen, uint32 resId) {
1164 	_roomDefTable[screen].parallax[0] = resId;
1165 }
1166 
spriteClipAndSet(uint16 * pSprX,uint16 * pSprY,uint16 * pSprWidth,uint16 * pSprHeight,uint16 * incr)1167 void Screen::spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *pSprWidth, uint16 *pSprHeight, uint16 *incr) {
1168 	int16 sprX = *pSprX - SCREEN_LEFT_EDGE;
1169 	int16 sprY = *pSprY - SCREEN_TOP_EDGE;
1170 	int16 sprW = *pSprWidth;
1171 	int16 sprH = *pSprHeight;
1172 
1173 	if (sprY < 0) {
1174 		*incr = (uint16)((-sprY) * sprW);
1175 		sprH += sprY;
1176 		sprY = 0;
1177 	} else
1178 		*incr = 0;
1179 	if (sprX < 0) {
1180 		*incr -= sprX;
1181 		sprW += sprX;
1182 		sprX = 0;
1183 	}
1184 
1185 	if (sprY + sprH > _scrnSizeY)
1186 		sprH = _scrnSizeY - sprY;
1187 	if (sprX + sprW > _scrnSizeX)
1188 		sprW = _scrnSizeX - sprX;
1189 
1190 	if (sprH < 0)
1191 		*pSprHeight = 0;
1192 	else
1193 		*pSprHeight = (uint16)sprH;
1194 	if (sprW < 0)
1195 		*pSprWidth = 0;
1196 	else
1197 		*pSprWidth = (uint16)sprW;
1198 
1199 	*pSprX = (uint16)sprX;
1200 	*pSprY = (uint16)sprY;
1201 
1202 	if (*pSprWidth && *pSprHeight) {
1203 		// sprite will be drawn, so mark it in the grid buffer
1204 		uint16 gridH = (*pSprHeight + (sprY & (SCRNGRID_Y - 1)) + (SCRNGRID_Y - 1)) / SCRNGRID_Y;
1205 		uint16 gridW = (*pSprWidth + (sprX & (SCRNGRID_X - 1)) + (SCRNGRID_X - 1)) / SCRNGRID_X;
1206 
1207 		if (SwordEngine::isPsx()) {
1208 			gridH *= 2; // This will correct the PSX sprite being cut at half height
1209 			gridW *= 2; // and masking problems when sprites are stretched in width
1210 
1211 			uint16 bottomSprPos = (*pSprY + (*pSprHeight) * 2); //Position of bottom line of sprite
1212 			if (bottomSprPos > _scrnSizeY) { //Check that resized psx sprite isn't drawn outside of screen boundaries
1213 				uint16 outScreen = bottomSprPos - _scrnSizeY;
1214 				*pSprHeight -= (outScreen % 2) ? (outScreen + 1) / 2 : outScreen / 2;
1215 			}
1216 
1217 		}
1218 
1219 		uint16 gridX = sprX / SCRNGRID_X;
1220 		uint16 gridY = sprY / SCRNGRID_Y;
1221 		uint8 *gridBuf = _screenGrid + gridX + gridY * _gridSizeX;
1222 		if (gridX + gridW > _gridSizeX)
1223 			gridW = _gridSizeX - gridX;
1224 		if (gridY + gridH > _gridSizeY)
1225 			gridH = _gridSizeY - gridY;
1226 
1227 		for (uint16 cnty = 0; cnty < gridH; cnty++) {
1228 			for (uint16 cntx = 0; cntx < gridW; cntx++)
1229 				gridBuf[cntx] = 2;
1230 			gridBuf += _gridSizeX;
1231 		}
1232 	}
1233 }
1234 
fnFlash(uint8 color)1235 void Screen::fnFlash(uint8 color) {
1236 	warning("stub: Screen::fnFlash(%d)", color);
1237 }
1238 
1239 // ------------------- Menu screen interface ---------------------------
1240 
showFrame(uint16 x,uint16 y,uint32 resId,uint32 frameNo,const byte * fadeMask,int8 fadeStatus)1241 void Screen::showFrame(uint16 x, uint16 y, uint32 resId, uint32 frameNo, const byte *fadeMask, int8 fadeStatus) {
1242 	uint8 frame[40 * 40];
1243 	int i, j;
1244 
1245 	if (SwordEngine::isPsx())
1246 		memset(frame, 0, sizeof(frame)); // PSX top menu is black
1247 	else
1248 		memset(frame, 199, sizeof(frame)); // Dark gray background
1249 
1250 	if (resId != 0xffffffff) {
1251 		FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(resId), frameNo);
1252 		uint8 *frameData = ((uint8 *)frameHead) + sizeof(FrameHeader);
1253 
1254 		if (SwordEngine::isPsx()) { //We need to decompress PSX frames
1255 			uint8 *frameBufferPSX = (uint8 *)malloc(_resMan->getUint16(frameHead->width) *  _resMan->getUint16(frameHead->height) / 2);
1256 			decompressHIF(frameData, frameBufferPSX);
1257 
1258 			for (i = 0; i < _resMan->getUint16(frameHead->height) / 2; i++) {
1259 				for (j = 0; j < _resMan->getUint16(frameHead->width); j++) {
1260 					uint8 data = frameBufferPSX[i * _resMan->getUint16(frameHead->width) + j];
1261 					frame[(i * 2 + 4) * 40 + j + 2] = data;
1262 					frame[(i * 2 + 1 + 4) * 40 + j + 2] = data; //Linedoubling the sprite
1263 				}
1264 			}
1265 
1266 			free(frameBufferPSX);
1267 		} else {
1268 			for (i = 0; i < _resMan->getUint16(frameHead->height); i++)
1269 				for (j = 0; j < _resMan->getUint16(frameHead->height); j++)
1270 					frame[(i + 4) * 40 + j + 2] = frameData[i * _resMan->getUint16(frameHead->width) + j];
1271 		}
1272 
1273 		_resMan->resClose(resId);
1274 	}
1275 
1276 	if (fadeMask) {
1277 		for (i = 0; i < 40; i++) {
1278 			for (j = 0; j < 40; j++) {
1279 				if (fadeMask[((i % 8) * 8) + (j % 8)] >= fadeStatus)
1280 					frame[i * 40 + j] = 0;
1281 			}
1282 		}
1283 	}
1284 
1285 	_system->copyRectToScreen(frame, 40, x, y, 40, 40);
1286 }
1287 
1288 // ------------------- router debugging code --------------------------------
1289 
vline(uint16 x,uint16 y1,uint16 y2)1290 void Screen::vline(uint16 x, uint16 y1, uint16 y2) {
1291 	for (uint16 cnty = y1; cnty <= y2; cnty++)
1292 		_screenBuf[x + _scrnSizeX * cnty] = 0;
1293 }
1294 
hline(uint16 x1,uint16 x2,uint16 y)1295 void Screen::hline(uint16 x1, uint16 x2, uint16 y) {
1296 	for (uint16 cntx = x1; cntx <= x2; cntx++)
1297 		_screenBuf[y * _scrnSizeX + cntx] = 0;
1298 }
1299 
bsubline_1(uint16 x1,uint16 y1,uint16 x2,uint16 y2)1300 void Screen::bsubline_1(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
1301 	int x, y, ddx, ddy, e;
1302 	ddx = ABS(x2 - x1);
1303 	ddy = ABS(y2 - y1) << 1;
1304 	e = ddx - ddy;
1305 	ddx <<= 1;
1306 
1307 	if (x1 > x2) {
1308 		uint16 tmp;
1309 		tmp = x1; x1 = x2; x2 = tmp;
1310 		tmp = y1; y1 = y2; y2 = tmp;
1311 	}
1312 
1313 	for (x = x1, y = y1; x <= x2; x++) {
1314 		_screenBuf[y * _scrnSizeX + x] = 0;
1315 		if (e < 0) {
1316 			y++;
1317 			e += ddx - ddy;
1318 		} else {
1319 			e -= ddy;
1320 		}
1321 	}
1322 }
1323 
bsubline_2(uint16 x1,uint16 y1,uint16 x2,uint16 y2)1324 void Screen::bsubline_2(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
1325 	int x, y, ddx, ddy, e;
1326 	ddx = ABS(x2 - x1) << 1;
1327 	ddy = ABS(y2 - y1);
1328 	e = ddy - ddx;
1329 	ddy <<= 1;
1330 
1331 	if (y1 > y2) {
1332 		uint16 tmp;
1333 		tmp = x1; x1 = x2; x2 = tmp;
1334 		tmp = y1; y1 = y2; y2 = tmp;
1335 	}
1336 
1337 	for (y = y1, x = x1; y <= y2; y++) {
1338 		_screenBuf[y * _scrnSizeX + x] = 0;
1339 		if (e < 0) {
1340 			x++;
1341 			e += ddy - ddx;
1342 		} else {
1343 			e -= ddx;
1344 		}
1345 	}
1346 }
1347 
bsubline_3(uint16 x1,uint16 y1,uint16 x2,uint16 y2)1348 void Screen::bsubline_3(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
1349 	int x, y, ddx, ddy, e;
1350 	ddx = ABS(x1 - x2) << 1;
1351 	ddy = ABS(y2 - y1);
1352 	e = ddy - ddx;
1353 	ddy <<= 1;
1354 
1355 	if (y1 > y2) {
1356 		uint16 tmp;
1357 		tmp = x1; x1 = x2; x2 = tmp;
1358 		tmp = y1; y1 = y2; y2 = tmp;
1359 	}
1360 
1361 	for (y = y1, x = x1; y <= y2; y++) {
1362 		_screenBuf[y * _scrnSizeX + x] = 0;
1363 		if (e < 0) {
1364 			x--;
1365 			e += ddy - ddx;
1366 		} else {
1367 			e -= ddx;
1368 		}
1369 	}
1370 }
1371 
bsubline_4(uint16 x1,uint16 y1,uint16 x2,uint16 y2)1372 void Screen::bsubline_4(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
1373 	int x, y, ddx, ddy, e;
1374 	ddy = ABS(y2 - y1) << 1;
1375 	ddx = ABS(x1 - x2);
1376 	e = ddx - ddy;
1377 	ddx <<= 1;
1378 
1379 	if (x1 > x2) {
1380 		uint16 tmp;
1381 		tmp = x1; x1 = x2; x2 = tmp;
1382 		tmp = y1; y1 = y2; y2 = tmp;
1383 	}
1384 
1385 	for (x = x1, y = y1; x <= x2; x++) {
1386 		_screenBuf[y * _scrnSizeX + x] = 0;
1387 		if (e < 0) {
1388 			y--;
1389 			e += ddx - ddy;
1390 		} else {
1391 			e -= ddy;
1392 		}
1393 	}
1394 }
1395 
drawLine(uint16 x1,uint16 y1,uint16 x2,uint16 y2)1396 void Screen::drawLine(uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
1397 	if ((x1 == x2) && (y1 == y2)) {
1398 		_screenBuf[x1 + y1 * _scrnSizeX] = 0;
1399 	}
1400 	if (x1 == x2) {
1401 		vline(x1, MIN(y1, y2), MAX(y1, y2));
1402 		return;
1403 	}
1404 
1405 	if (y1 == y2) {
1406 		hline(MIN(x1, x2), MAX(x1, x2), y1);
1407 		return;
1408 	}
1409 
1410 	float k = float(y2 - y1) / float(x2 - x1);
1411 
1412 	if ((k >= 0) && (k <= 1)) {
1413 		bsubline_1(x1, y1, x2, y2);
1414 	} else if (k > 1) {
1415 		bsubline_2(x1, y1, x2, y2);
1416 	} else if ((k < 0) && (k >= -1)) {
1417 		bsubline_4(x1, y1, x2, y2);
1418 	} else {
1419 		bsubline_3(x1, y1, x2, y2);
1420 	}
1421 }
1422 
1423 } // End of namespace Sword1
1424