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/endian.h"
25 #include "common/events.h"
26 #include "common/system.h"
27 #include "common/textconsole.h"
28 
29 #include "graphics/palette.h"
30 
31 #include "sky/disk.h"
32 #include "sky/logic.h"
33 #include "sky/screen.h"
34 #include "sky/compact.h"
35 #include "sky/sky.h"
36 #include "sky/skydefs.h"
37 #include "sky/struc.h"
38 
39 namespace Sky {
40 
41 uint8 Screen::_top16Colors[16*3] = {
42 	0, 0, 0,
43 	38, 38, 38,
44 	63, 63, 63,
45 	0, 0, 0,
46 	0, 0, 0,
47 	0, 0, 0,
48 	0, 0, 0,
49 	54, 54, 54,
50 	45, 47, 49,
51 	32, 31, 41,
52 	29, 23, 37,
53 	23, 18, 30,
54 	49, 11, 11,
55 	39, 5, 5,
56 	29, 1, 1,
57 	63, 63, 63
58 };
59 
Screen(OSystem * pSystem,Disk * pDisk,SkyCompact * skyCompact)60 Screen::Screen(OSystem *pSystem, Disk *pDisk, SkyCompact *skyCompact) {
61 	_system = pSystem;
62 	_skyDisk = pDisk;
63 	_skyCompact = skyCompact;
64 
65 	int i;
66 	uint8 tmpPal[VGA_COLORS * 3];
67 
68 	_gameGrid = (uint8 *)malloc(GRID_X * GRID_Y * 2);
69 	forceRefresh();
70 
71 	_currentScreen = NULL;
72 	_scrollScreen = NULL;
73 
74 	//blank the first 240 colors of the palette
75 	memset(tmpPal, 0, GAME_COLORS * 3);
76 
77 	//set the remaining colors
78 	for (i = 0; i < (VGA_COLORS-GAME_COLORS); i++) {
79 		tmpPal[3 * GAME_COLORS + i * 3 + 0] = (_top16Colors[i * 3 + 0] << 2) + (_top16Colors[i * 3 + 0] >> 4);
80 		tmpPal[3 * GAME_COLORS + i * 3 + 1] = (_top16Colors[i * 3 + 1] << 2) + (_top16Colors[i * 3 + 1] >> 4);
81 		tmpPal[3 * GAME_COLORS + i * 3 + 2] = (_top16Colors[i * 3 + 2] << 2) + (_top16Colors[i * 3 + 2] >> 4);
82 	}
83 
84 	//set the palette
85 	_system->getPaletteManager()->setPalette(tmpPal, 0, VGA_COLORS);
86 	_currentPalette = 0;
87 
88 	_seqInfo.nextFrame = _seqInfo.framesLeft = 0;
89 	_seqInfo.seqData = _seqInfo.seqDataPos = NULL;
90 	_seqInfo.running = false;
91 }
92 
~Screen()93 Screen::~Screen() {
94 	free(_gameGrid);
95 	free(_currentScreen);
96 	free(_scrollScreen);
97 }
98 
clearScreen(bool fullscreen)99 void Screen::clearScreen(bool fullscreen) {
100 	memset(_currentScreen, 0, FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
101 	_system->copyRectToScreen(_currentScreen, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, fullscreen ? FULL_SCREEN_HEIGHT : GAME_SCREEN_HEIGHT);
102 	_system->updateScreen();
103 }
104 
setFocusRectangle(const Common::Rect & rect)105 void Screen::setFocusRectangle(const Common::Rect& rect) {
106 	_system->setFocusRectangle(rect);
107 }
108 
109 //set a new palette, pal is a pointer to dos vga rgb components 0..63
setPalette(uint8 * pal)110 void Screen::setPalette(uint8 *pal) {
111 	convertPalette(pal, _palette);
112 	_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
113 	_system->updateScreen();
114 }
115 
setPaletteEndian(uint8 * pal)116 void Screen::setPaletteEndian(uint8 *pal) {
117 #ifdef SCUMM_BIG_ENDIAN
118 	uint8 endPalette[VGA_COLORS * 3];
119 	for (uint16 cnt = 0; cnt < VGA_COLORS * 3; cnt++)
120 		endPalette[cnt] = pal[cnt ^ 1];
121 	convertPalette(endPalette, _palette);
122 #else
123 	convertPalette(pal, _palette);
124 #endif
125 	_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
126 	_system->updateScreen();
127 }
128 
halvePalette()129 void Screen::halvePalette() {
130 	uint8 halfPalette[VGA_COLORS * 3];
131 
132 	for (uint8 cnt = 0; cnt < GAME_COLORS; cnt++) {
133 		halfPalette[cnt * 3 + 0] = _palette[cnt * 3 + 0] >> 1;
134 		halfPalette[cnt * 3 + 1] = _palette[cnt * 3 + 1] >> 1;
135 		halfPalette[cnt * 3 + 2] = _palette[cnt * 3 + 2] >> 1;
136 	}
137 	_system->getPaletteManager()->setPalette(halfPalette, 0, GAME_COLORS);
138 }
139 
setPalette(uint16 fileNum)140 void Screen::setPalette(uint16 fileNum) {
141 	uint8 *tmpPal = _skyDisk->loadFile(fileNum);
142 	if (tmpPal) {
143 		setPalette(tmpPal);
144 		free(tmpPal);
145 	} else
146 		warning("Screen::setPalette: can't load file nr. %d",fileNum);
147 }
148 
showScreen(uint16 fileNum,bool fullscreen)149   void Screen::showScreen(uint16 fileNum, bool fullscreen) {
150 	// This is only used for static images in the floppy and cd intro
151 	free(_currentScreen);
152 	_currentScreen = _skyDisk->loadFile(fileNum);
153 	if (!fullscreen) {
154 		// make sure the last 8 lines are forced to black.
155 		memset(_currentScreen + GAME_SCREEN_HEIGHT * GAME_SCREEN_WIDTH, 0, (FULL_SCREEN_HEIGHT - GAME_SCREEN_HEIGHT) * GAME_SCREEN_WIDTH);
156 	}
157 
158 	if (_currentScreen)
159 		showScreen(_currentScreen, fullscreen);
160 	else
161 		warning("Screen::showScreen: can't load file nr. %d",fileNum);
162 }
163 
showScreen(uint8 * pScreen,bool fullscreen)164 void Screen::showScreen(uint8 *pScreen, bool fullscreen) {
165 	_system->copyRectToScreen(pScreen, 320, 0, 0, GAME_SCREEN_WIDTH, fullscreen ? FULL_SCREEN_HEIGHT : GAME_SCREEN_HEIGHT);
166 	_system->updateScreen();
167 }
168 
169 //convert 3 byte 0..63 rgb to 3 byte 0..255 rgb
convertPalette(uint8 * inPal,uint8 * outPal)170 void Screen::convertPalette(uint8 *inPal, uint8* outPal) {
171 	int i;
172 
173 	for (i = 0; i < VGA_COLORS; i++) {
174 		outPal[3 * i + 0] = (inPal[3 * i + 0] << 2) + (inPal[3 * i + 0] >> 4);
175 		outPal[3 * i + 1] = (inPal[3 * i + 1] << 2) + (inPal[3 * i + 1] >> 4);
176 		outPal[3 * i + 2] = (inPal[3 * i + 2] << 2) + (inPal[3 * i + 2] >> 4);
177 	}
178 }
179 
recreate()180 void Screen::recreate() {
181 	// check the game grid for changed blocks
182 	if (!Logic::_scriptVariables[LAYER_0_ID])
183 		return;
184 	uint8 *gridPos = _gameGrid;
185 	uint8 *screenData = (uint8 *)SkyEngine::fetchItem(Logic::_scriptVariables[LAYER_0_ID]);
186 	if (!screenData) {
187 		error("Screen::recreate():\nSkyEngine::fetchItem(Logic::_scriptVariables[LAYER_0_ID](%X)) returned NULL", Logic::_scriptVariables[LAYER_0_ID]);
188 	}
189 	uint8 *screenPos = _currentScreen;
190 
191 	for (uint8 cnty = 0; cnty < GRID_Y; cnty++) {
192 		for (uint8 cntx = 0; cntx < GRID_X; cntx++) {
193 			if (gridPos[0] & 0x80) {
194 				gridPos[0] &= 0x7F; // reset recreate flag
195 				gridPos[0] |= 1;    // set bit for flip routine
196 				uint8 *savedScreenY = screenPos;
197 				for (uint8 gridCntY = 0; gridCntY < GRID_H; gridCntY++) {
198 					memcpy(screenPos, screenData, GRID_W);
199 					screenPos += GAME_SCREEN_WIDTH;
200 					screenData += GRID_W;
201 				}
202 				screenPos = savedScreenY + GRID_W;
203 			} else {
204 				screenPos += GRID_W;
205 				screenData += GRID_W * GRID_H;
206 			}
207 			gridPos++;
208 		}
209 		screenPos += (GRID_H - 1) * GAME_SCREEN_WIDTH;
210 	}
211 }
212 
flip(bool doUpdate)213 void Screen::flip(bool doUpdate) {
214 	uint32 copyX, copyWidth;
215 	copyX = copyWidth = 0;
216 	for (uint8 cnty = 0; cnty < GRID_Y; cnty++) {
217 		for (uint8 cntx = 0; cntx < GRID_X; cntx++) {
218 			if (_gameGrid[cnty * GRID_X + cntx] & 1) {
219 				_gameGrid[cnty * GRID_X + cntx] &= ~1;
220 				if (!copyWidth)
221 					copyX = cntx * GRID_W;
222 				copyWidth += GRID_W;
223 			} else if (copyWidth) {
224 				_system->copyRectToScreen(_currentScreen + cnty * GRID_H * GAME_SCREEN_WIDTH + copyX, GAME_SCREEN_WIDTH, copyX, cnty * GRID_H, copyWidth, GRID_H);
225 				copyWidth = 0;
226 			}
227 		}
228 		if (copyWidth) {
229 			_system->copyRectToScreen(_currentScreen + cnty * GRID_H * GAME_SCREEN_WIDTH + copyX, GAME_SCREEN_WIDTH, copyX, cnty * GRID_H, copyWidth, GRID_H);
230 			copyWidth = 0;
231 		}
232 	}
233 	if (doUpdate)
234 		_system->updateScreen();
235 }
236 
fnDrawScreen(uint32 palette,uint32 scroll)237 void Screen::fnDrawScreen(uint32 palette, uint32 scroll) {
238 	// set up the new screen
239 	fnFadeDown(scroll);
240 	forceRefresh();
241 	recreate();
242 	spriteEngine();
243 	flip(false);
244 	fnFadeUp(palette, scroll);
245 }
246 
fnFadeDown(uint32 scroll)247 void Screen::fnFadeDown(uint32 scroll) {
248 	if (((scroll != 123) && (scroll != 321)) || (SkyEngine::_systemVars.systemFlags & SF_NO_SCROLL)) {
249 		uint32 delayTime = _system->getMillis();
250 		for (uint8 cnt = 0; cnt < 32; cnt++) {
251 			delayTime += 20;
252 			palette_fadedown_helper(_palette, GAME_COLORS);
253 			_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
254 			_system->updateScreen();
255 			int32 waitTime = (int32)delayTime - _system->getMillis();
256 			if (waitTime < 0)
257 				waitTime = 0;
258 			_system->delayMillis((uint)waitTime);
259 		}
260 	} else {
261 		// scrolling is performed by fnFadeUp. It's just prepared here
262 		_scrollScreen = _currentScreen;
263 		_currentScreen = (uint8 *)malloc(FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
264 		// the game will draw the new room into _currentScreen which
265 		// will be scrolled into the visible screen by fnFadeUp
266 		// fnFadeUp also frees the _scrollScreen
267 	}
268 }
269 
palette_fadedown_helper(uint8 * pal,uint num)270 void Screen::palette_fadedown_helper(uint8 *pal, uint num) {
271 	do {
272 		if (pal[0] >= 8)
273 			pal[0] -= 8;
274 		else
275 			pal[0] = 0;
276 
277 		if (pal[1] >= 8)
278 			pal[1] -= 8;
279 		else
280 			pal[1] = 0;
281 
282 		if (pal[2] >= 8)
283 			pal[2] -= 8;
284 		else
285 			pal[2] = 0;
286 
287 		pal += 3;
288 	} while (--num);
289 }
290 
paletteFadeUp(uint16 fileNr)291 void Screen::paletteFadeUp(uint16 fileNr) {
292 	uint8 *pal = _skyDisk->loadFile(fileNr);
293 	if (pal) {
294 		paletteFadeUp(pal);
295 		free(pal);
296 	} else
297 		warning("Screen::paletteFadeUp: Can't load palette #%d",fileNr);
298 }
299 
paletteFadeUp(uint8 * pal)300 void Screen::paletteFadeUp(uint8 *pal) {
301 	byte tmpPal[VGA_COLORS * 3];
302 
303 	convertPalette(pal, tmpPal);
304 
305 	uint32 delayTime = _system->getMillis();
306 	for (uint8 cnt = 1; cnt <= 32; cnt++) {
307 		delayTime += 20;
308 
309 		for (uint8 colCnt = 0; colCnt < GAME_COLORS; colCnt++) {
310 			_palette[colCnt * 3 + 0] = (tmpPal[colCnt * 3 + 0] * cnt) >> 5;
311 			_palette[colCnt * 3 + 1] = (tmpPal[colCnt * 3 + 1] * cnt) >> 5;
312 			_palette[colCnt * 3 + 2] = (tmpPal[colCnt * 3 + 2] * cnt) >> 5;
313 		}
314 
315 		_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
316 		_system->updateScreen();
317 
318 		int32 waitTime = (int32)delayTime - _system->getMillis();
319 		if (waitTime < 0)
320 			waitTime = 0;
321 		_system->delayMillis((uint)waitTime);
322 	}
323 }
324 
fnFadeUp(uint32 palNum,uint32 scroll)325 void Screen::fnFadeUp(uint32 palNum, uint32 scroll) {
326 	//_currentScreen points to new screen,
327 	//_scrollScreen points to graphic showing old room
328 	if ((scroll != 123) && (scroll != 321))
329 		scroll = 0;
330 
331 	if ((scroll == 0) || (SkyEngine::_systemVars.systemFlags & SF_NO_SCROLL)) {
332 		uint8 *palette = (uint8 *)_skyCompact->fetchCpt(palNum);
333 		if (palette == NULL)
334 			error("Screen::fnFadeUp: can't fetch compact %X", palNum);
335 #ifdef SCUMM_BIG_ENDIAN
336 		byte tmpPal[VGA_COLORS * 3];
337 		for (uint16 cnt = 0; cnt < VGA_COLORS * 3; cnt++)
338 			tmpPal[cnt] = palette[cnt ^ 1];
339 		paletteFadeUp(tmpPal);
340 #else
341 		paletteFadeUp(palette);
342 #endif
343 	} else if (scroll == 123) {	// scroll left (going right)
344 		assert(_currentScreen && _scrollScreen);
345 		uint8 *scrNewPtr, *scrOldPtr;
346 		for (uint8 scrollCnt = 0; scrollCnt < (GAME_SCREEN_WIDTH / SCROLL_JUMP) - 1; scrollCnt++) {
347 			scrNewPtr = _currentScreen + scrollCnt * SCROLL_JUMP;
348 			scrOldPtr = _scrollScreen;
349 			for (uint8 lineCnt = 0; lineCnt < GAME_SCREEN_HEIGHT; lineCnt++) {
350 				memmove(scrOldPtr, scrOldPtr + SCROLL_JUMP, GAME_SCREEN_WIDTH - SCROLL_JUMP);
351 				memcpy(scrOldPtr + GAME_SCREEN_WIDTH - SCROLL_JUMP, scrNewPtr, SCROLL_JUMP);
352 				scrNewPtr += GAME_SCREEN_WIDTH;
353 				scrOldPtr += GAME_SCREEN_WIDTH;
354 			}
355 			showScreen(_scrollScreen);
356 			waitForTick();
357 		}
358 		showScreen(_currentScreen);
359 	} else if (scroll == 321) {	// scroll right (going left)
360 		assert(_currentScreen && _scrollScreen);
361 		uint8 *scrNewPtr, *scrOldPtr;
362 		for (uint8 scrollCnt = 0; scrollCnt < (GAME_SCREEN_WIDTH / SCROLL_JUMP) - 1; scrollCnt++) {
363 			scrNewPtr = _currentScreen + GAME_SCREEN_WIDTH - (scrollCnt + 1) * SCROLL_JUMP;
364 			scrOldPtr = _scrollScreen;
365 			for (uint8 lineCnt = 0; lineCnt < GAME_SCREEN_HEIGHT; lineCnt++) {
366 				memmove(scrOldPtr + SCROLL_JUMP, scrOldPtr, GAME_SCREEN_WIDTH - SCROLL_JUMP);
367 				memcpy(scrOldPtr, scrNewPtr, SCROLL_JUMP);
368 				scrNewPtr += GAME_SCREEN_WIDTH;
369 				scrOldPtr += GAME_SCREEN_WIDTH;
370 			}
371 			showScreen(_scrollScreen);
372 			waitForTick();
373 		}
374 		showScreen(_currentScreen);
375 	}
376 	if (_scrollScreen) {
377 		free(_scrollScreen);
378 		_scrollScreen = NULL;
379 	}
380 }
381 
waitForTick()382 void Screen::waitForTick() {
383 	uint32 start = _system->getMillis();
384 	uint32 end = start + 20 - (start % 20);
385 	uint32 remain;
386 
387 	Common::EventManager *eventMan = _system->getEventManager();
388 	Common::Event event;
389 
390 	while (true) {
391 		while (eventMan->pollEvent(event))
392 			;
393 
394 		start = _system->getMillis();
395 
396 		if (start >= end)
397 			return;
398 
399 		remain = end - start;
400 		if (remain < 10) {
401 			_system->delayMillis(remain);
402 			return;
403 		}
404 
405 		_system->delayMillis(10);
406 	}
407 }
408 
waitForSequence()409 void Screen::waitForSequence() {
410 	Common::EventManager *eventMan = _system->getEventManager();
411 	Common::Event event;
412 
413 	while (_seqInfo.running) {
414 		processSequence();
415 
416 		_system->delayMillis(20);
417 		while (eventMan->pollEvent(event))
418 			;
419 	}
420 }
421 
startSequence(uint16 fileNum)422 void Screen::startSequence(uint16 fileNum) {
423 	_seqInfo.seqData = _skyDisk->loadFile(fileNum);
424 	_seqInfo.nextFrame = _system->getMillis() + 60;
425 	_seqInfo.framesLeft = _seqInfo.seqData[0];
426 	_seqInfo.seqDataPos = _seqInfo.seqData + 1;
427 	_seqInfo.running = true;
428 	_seqInfo.runningItem = false;
429 }
430 
startSequenceItem(uint16 itemNum)431 void Screen::startSequenceItem(uint16 itemNum) {
432 	_seqInfo.seqData = (uint8 *)SkyEngine::fetchItem(itemNum);
433 	_seqInfo.nextFrame = _system->getMillis() + 60;
434 	_seqInfo.framesLeft = _seqInfo.seqData[0] - 1;
435 	_seqInfo.seqDataPos = _seqInfo.seqData + 1;
436 	_seqInfo.running = true;
437 	_seqInfo.runningItem = true;
438 }
439 
stopSequence()440 void Screen::stopSequence() {
441 	_seqInfo.running = false;
442 	waitForTick();
443 	waitForTick();
444 	_seqInfo.nextFrame = _seqInfo.framesLeft = 0;
445 	free(_seqInfo.seqData);
446 	_seqInfo.seqData = _seqInfo.seqDataPos = NULL;
447 }
448 
processSequence()449 void Screen::processSequence() {
450 	if (!_seqInfo.running)
451 		return;
452 
453 	if (_system->getMillis() < _seqInfo.nextFrame)
454 		return;
455 
456 	_seqInfo.nextFrame += 60;
457 
458 	memset(_seqGrid, 0, 12 * 20);
459 
460 	uint32 screenPos = 0;
461 
462 	uint8 nrToSkip, nrToDo, cnt;
463 	do {
464 		do {
465 			nrToSkip = _seqInfo.seqDataPos[0];
466 			_seqInfo.seqDataPos++;
467 			screenPos += nrToSkip;
468 		} while (nrToSkip == 0xFF);
469 
470 		do {
471 			nrToDo = _seqInfo.seqDataPos[0];
472 			_seqInfo.seqDataPos++;
473 
474 			uint8 gridSta = (uint8)((screenPos / (GAME_SCREEN_WIDTH * 16))*20 + ((screenPos % GAME_SCREEN_WIDTH) >> 4));
475 			uint8 gridEnd = (uint8)(((screenPos+nrToDo) / (GAME_SCREEN_WIDTH * 16))*20 + (((screenPos+nrToDo) % GAME_SCREEN_WIDTH) >> 4));
476 			gridSta = MIN(gridSta, (uint8)(12 * 20 - 1));
477 			gridEnd = MIN(gridEnd, (uint8)(12 * 20 - 1));
478 			if (gridEnd >= gridSta)
479 				for (cnt = gridSta; cnt <= gridEnd; cnt++)
480 					_seqGrid[cnt] = 1;
481 			else {
482 				for (cnt = gridSta; cnt < (gridSta / 20 + 1) * 20; cnt++)
483 					_seqGrid[cnt] = 1;
484 				for (cnt = (gridEnd / 20) * 20; cnt <= gridEnd; cnt++)
485 					_seqGrid[cnt] = 1;
486 			}
487 
488 			for (cnt = 0; cnt < nrToDo; cnt++) {
489 				_currentScreen[screenPos] = _seqInfo.seqDataPos[0];
490 				_seqInfo.seqDataPos++;
491 				screenPos++;
492 			}
493 		} while (nrToDo == 0xFF);
494 	} while (screenPos < (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT));
495 	uint8 *gridPtr = _seqGrid; uint8 *scrPtr = _currentScreen; uint8 *rectPtr = NULL;
496 	uint8 rectWid = 0, rectX = 0, rectY = 0;
497 	for (uint8 cnty = 0; cnty < 12; cnty++) {
498 		for (uint8 cntx = 0; cntx < 20; cntx++) {
499 			if (*gridPtr) {
500 				if (!rectWid) {
501 					rectX = cntx;
502 					rectY = cnty;
503 					rectPtr = scrPtr;
504 				}
505 				rectWid++;
506 			} else if (rectWid) {
507 				_system->copyRectToScreen(rectPtr, GAME_SCREEN_WIDTH, rectX << 4, rectY << 4, rectWid << 4, 16);
508 				rectWid = 0;
509 			}
510 			scrPtr += 16;
511 			gridPtr++;
512 		}
513 		if (rectWid) {
514 			_system->copyRectToScreen(rectPtr, GAME_SCREEN_WIDTH, rectX << 4, rectY << 4, rectWid << 4, 16);
515 			rectWid = 0;
516 		}
517 		scrPtr += 15 * GAME_SCREEN_WIDTH;
518 	}
519 	_system->updateScreen();
520 	_seqInfo.framesLeft--;
521 
522 	if (_seqInfo.framesLeft == 0) {
523 		_seqInfo.running = false;
524 		if (!_seqInfo.runningItem)
525 			free(_seqInfo.seqData);
526 		_seqInfo.seqData = _seqInfo.seqDataPos = NULL;
527 	}
528 }
529 
530 //- sprites.asm routines
531 
spriteEngine()532 void Screen::spriteEngine() {
533 	doSprites(BACK);
534 	sortSprites();
535 	doSprites(FORE);
536 }
537 
sortSprites()538 void Screen::sortSprites() {
539 	StSortList sortList[30];
540 	uint32 currDrawList = DRAW_LIST_NO;
541 	uint32 loadDrawList;
542 
543 	bool nextDrawList = false;
544 	while (Logic::_scriptVariables[currDrawList]) {
545 		// big_sort_loop
546 		uint32 spriteCnt = 0;
547 		loadDrawList = Logic::_scriptVariables[currDrawList];
548 		currDrawList++;
549 
550 		do { // a_new_draw_list:
551 			uint16 *drawListData = (uint16 *)_skyCompact->fetchCpt(loadDrawList);
552 			nextDrawList = false;
553 			while ((!nextDrawList) && (drawListData[0])) {
554 				if (drawListData[0] == 0xFFFF) {
555 					loadDrawList = drawListData[1];
556 					nextDrawList = true;
557 				} else {
558 					// process_this_id:
559 					Compact *spriteComp = _skyCompact->fetchCpt(drawListData[0]);
560 					if ((spriteComp->status & 4) && // is it sortable playfield?(!?!)
561 						(spriteComp->screen == Logic::_scriptVariables[SCREEN])) { // on current screen
562 							DataFileHeader *spriteData =
563 								(DataFileHeader *)SkyEngine::fetchItem(spriteComp->frame >> 6);
564 							if (!spriteData) {
565 								debug(9,"Missing file %d", spriteComp->frame >> 6);
566 								spriteComp->status = 0;
567 							} else {
568 								sortList[spriteCnt].yCood = spriteComp->ycood + spriteData->s_offset_y + spriteData->s_height;
569 								sortList[spriteCnt].compact = spriteComp;
570 								sortList[spriteCnt].sprite = spriteData;
571 								spriteCnt++;
572 							}
573 					}
574 					drawListData++;
575 				}
576 			}
577 		} while (nextDrawList);
578 		// made_list:
579 		if (spriteCnt > 1) { // bubble sort
580 			for (uint32 cnt1 = 0; cnt1 < spriteCnt - 1; cnt1++)
581 				for (uint32 cnt2 = cnt1 + 1; cnt2 < spriteCnt; cnt2++)
582 					if (sortList[cnt1].yCood > sortList[cnt2].yCood) {
583 						StSortList tmp;
584 						tmp.yCood = sortList[cnt1].yCood;
585 						tmp.sprite = sortList[cnt1].sprite;
586 						tmp.compact = sortList[cnt1].compact;
587 						sortList[cnt1].yCood = sortList[cnt2].yCood;
588 						sortList[cnt1].sprite = sortList[cnt2].sprite;
589 						sortList[cnt1].compact = sortList[cnt2].compact;
590 						sortList[cnt2].yCood = tmp.yCood;
591 						sortList[cnt2].sprite = tmp.sprite;
592 						sortList[cnt2].compact = tmp.compact;
593 					}
594 		}
595 		for (uint32 cnt = 0; cnt < spriteCnt; cnt++) {
596 			drawSprite((uint8 *)sortList[cnt].sprite, sortList[cnt].compact);
597 			if (sortList[cnt].compact->status & 8)
598 				vectorToGame(0x81);
599 			else
600 				vectorToGame(1);
601 			if (!(sortList[cnt].compact->status & 0x200))
602 				verticalMask();
603 		}
604 	}
605 }
606 
doSprites(uint8 layer)607 void Screen::doSprites(uint8 layer) {
608 	uint16 drawListNum = DRAW_LIST_NO;
609 	uint32 idNum;
610 	uint16* drawList;
611 	while (Logic::_scriptVariables[drawListNum]) { // std sp loop
612 		idNum = Logic::_scriptVariables[drawListNum];
613 		drawListNum++;
614 
615 		drawList = (uint16 *)_skyCompact->fetchCpt(idNum);
616 		while (drawList[0]) {
617 			// new_draw_list:
618 			while ((drawList[0] != 0) && (drawList[0] != 0xFFFF)) {
619 				// back_loop:
620 				// not_new_list
621 				Compact *spriteData = _skyCompact->fetchCpt(drawList[0]);
622 				drawList++;
623 				if ((spriteData->status & (1 << layer)) &&
624 						(spriteData->screen == Logic::_scriptVariables[SCREEN])) {
625 					uint8 *toBeDrawn = (uint8 *)SkyEngine::fetchItem(spriteData->frame >> 6);
626 					if (!toBeDrawn) {
627 						debug(9, "Spritedata %d not loaded", spriteData->frame >> 6);
628 						spriteData->status = 0;
629 					} else {
630 						drawSprite(toBeDrawn, spriteData);
631 						if (layer == BACK)
632 							verticalMask();
633 						if (spriteData->status & 8)
634 							vectorToGame(0x81);
635 						else
636 							vectorToGame(1);
637 					}
638 				}
639 			}
640 			while (drawList[0] == 0xFFFF)
641 				drawList = (uint16 *)_skyCompact->fetchCpt(drawList[1]);
642 		}
643 	}
644 }
645 
drawSprite(uint8 * spriteInfo,Compact * sprCompact)646 void Screen::drawSprite(uint8 *spriteInfo, Compact *sprCompact) {
647 	if (spriteInfo == NULL) {
648 		warning("Screen::drawSprite Can't draw sprite. Data %d was not loaded", sprCompact->frame >> 6);
649 		sprCompact->status = 0;
650 		return;
651 	}
652 	DataFileHeader *sprDataFile = (DataFileHeader *)spriteInfo;
653 	_sprWidth = sprDataFile->s_width;
654 	_sprHeight = sprDataFile->s_height;
655 	_maskX1 = _maskX2 = 0;
656 	uint8 *spriteData = spriteInfo + (sprCompact->frame & 0x3F) * sprDataFile->s_sp_size;
657 	spriteData += sizeof(DataFileHeader);
658 	int32 spriteY = sprCompact->ycood + sprDataFile->s_offset_y - TOP_LEFT_Y;
659 	if (spriteY < 0) {
660 		spriteY = -spriteY;
661 		if (_sprHeight <= (uint32)spriteY) {
662 			_sprWidth = 0;
663 			return;
664 		}
665 		_sprHeight -= spriteY;
666 		spriteData += sprDataFile->s_width * spriteY;
667 		spriteY = 0;
668 	} else {
669 		int32 botClip = GAME_SCREEN_HEIGHT - sprDataFile->s_height - spriteY;
670 		if (botClip < 0) {
671 			botClip = -botClip;
672 			if (_sprHeight <= (uint32)botClip) {
673 				_sprWidth = 0;
674 				return;
675 			}
676 			_sprHeight -= botClip;
677 		}
678 	}
679 	_sprY = (uint32)spriteY;
680 	int32 spriteX = sprCompact->xcood + sprDataFile->s_offset_x - TOP_LEFT_X;
681 	if (spriteX < 0) {
682 		spriteX = -spriteX;
683 		if (_sprWidth <= (uint32)spriteX) {
684 			_sprWidth = 0;
685 			return;
686 		}
687 		_sprWidth -= spriteX;
688 		_maskX1 = spriteX;
689 		spriteX = 0;
690 	} else {
691 		int32 rightClip = GAME_SCREEN_WIDTH - (sprDataFile->s_width + spriteX);
692 		if (rightClip < 0) {
693 			rightClip = (-rightClip) + 1;
694 			if (_sprWidth <= (uint32)rightClip) {
695 				_sprWidth = 0;
696 				return;
697 			}
698 			_sprWidth -= rightClip;
699 			_maskX2 = rightClip;
700 		}
701 	}
702 	_sprX = (uint32)spriteX;
703 	uint8 *screenPtr = _currentScreen + _sprY * GAME_SCREEN_WIDTH + _sprX;
704 	if ((_sprHeight > 192) || (_sprY > 192)) {
705 		_sprWidth = 0;
706 		return;
707 	}
708 	if ((_sprX + _sprWidth > 320) || (_sprY + _sprHeight > 192)) {
709 		warning("Screen::drawSprite fatal error: got x = %d, y = %d, w = %d, h = %d",_sprX, _sprY, _sprWidth, _sprHeight);
710 		_sprWidth = 0;
711 		return;
712 	}
713 
714 	for (uint16 cnty = 0; cnty < _sprHeight; cnty++) {
715 		for (uint16 cntx = 0; cntx < _sprWidth; cntx++)
716 			if (spriteData[cntx + _maskX1])
717 				screenPtr[cntx] = spriteData[cntx + _maskX1];
718 		spriteData += _sprWidth + _maskX2 + _maskX1;
719 		screenPtr += GAME_SCREEN_WIDTH;
720 	}
721 	// Convert the sprite coordinate/size values to blocks for vertical mask and/or vector to game
722 	_sprWidth += _sprX + GRID_W-1;
723 	_sprHeight += _sprY + GRID_H-1;
724 
725 	_sprX >>= GRID_W_SHIFT;
726 	_sprWidth >>= GRID_W_SHIFT;
727 	_sprY >>= GRID_H_SHIFT;
728 	_sprHeight >>= GRID_H_SHIFT;
729 
730 	_sprWidth -= _sprX;
731 	_sprHeight -= _sprY;
732 }
733 
vectorToGame(uint8 gridVal)734 void Screen::vectorToGame(uint8 gridVal) {
735 	if (_sprWidth == 0)
736 		return;
737 	uint8 *trgGrid = _gameGrid + _sprY * GRID_X +_sprX;
738 	for (uint32 cnty = 0; cnty < _sprHeight; cnty++) {
739 		for (uint32 cntx = 0; cntx < _sprWidth; cntx++)
740 			trgGrid[cntx] |= gridVal;
741 		trgGrid += GRID_X;
742 	}
743 }
744 
vertMaskSub(uint16 * grid,uint32 gridOfs,uint8 * screenPtr,uint32 layerId)745 void Screen::vertMaskSub(uint16 *grid, uint32 gridOfs, uint8 *screenPtr, uint32 layerId) {
746 	for (uint32 cntx = 0; cntx < _sprHeight; cntx++) { // start_x | block_loop
747 		if (grid[gridOfs]) {
748 			if (!(FROM_LE_16(grid[gridOfs]) & 0x8000)) {
749 				uint32 gridVal = FROM_LE_16(grid[gridOfs]) - 1;
750 				gridVal *= GRID_W * GRID_H;
751 				uint8 *dataSrc = (uint8 *)SkyEngine::fetchItem(Logic::_scriptVariables[layerId]) + gridVal;
752 				uint8 *dataTrg = screenPtr;
753 				for (uint32 grdCntY = 0; grdCntY < GRID_H; grdCntY++) {
754 					for (uint32 grdCntX = 0; grdCntX < GRID_W; grdCntX++)
755 						if (dataSrc[grdCntX])
756 							dataTrg[grdCntX] = dataSrc[grdCntX];
757 					dataSrc += GRID_W;
758 					dataTrg += GAME_SCREEN_WIDTH;
759 				}
760 			} // dummy_end:
761 			screenPtr -= GRID_H * GAME_SCREEN_WIDTH;
762 			gridOfs -= GRID_X;
763 		} else
764 			return;
765 	} // next_x
766 }
767 
verticalMask()768 void Screen::verticalMask() {
769 	if (_sprWidth == 0)
770 		return;
771 	uint32 startGridOfs = (_sprY + _sprHeight - 1) * GRID_X + _sprX;
772 	uint8 *startScreenPtr = (_sprY + _sprHeight - 1) * GRID_H * GAME_SCREEN_WIDTH + _sprX * GRID_W + _currentScreen;
773 
774 	for (uint32 layerCnt = LAYER_1_ID; layerCnt <= LAYER_3_ID; layerCnt++) {
775 		uint32 gridOfs = startGridOfs;
776 		uint8 *screenPtr = startScreenPtr;
777 		for (uint32 widCnt = 0; widCnt < _sprWidth; widCnt++) { // x_loop
778 			uint32 nLayerCnt = layerCnt;
779 			while (Logic::_scriptVariables[nLayerCnt + 3]) {
780 				uint16 *scrGrid;
781 				scrGrid = (uint16 *)SkyEngine::fetchItem(Logic::_scriptVariables[layerCnt + 3]);
782 				if (scrGrid[gridOfs]) {
783 					vertMaskSub(scrGrid, gridOfs, screenPtr, layerCnt);
784 					break;
785 				} else
786 					nLayerCnt++;
787 			}
788 			// next_x:
789 			screenPtr += GRID_W;
790 			gridOfs++;
791 		}
792 	}
793 }
794 
paintBox(uint16 x,uint16 y)795 void Screen::paintBox(uint16 x, uint16 y) {
796 	uint8 *screenPos = _currentScreen + y * GAME_SCREEN_WIDTH + x;
797 	memset(screenPos, 255, 8);
798 	for (uint8 cnt = 1; cnt < 8; cnt++) {
799 		*(screenPos + cnt * GAME_SCREEN_WIDTH) = 255;
800 		*(screenPos + cnt * GAME_SCREEN_WIDTH + 7) = 255;
801 	}
802 	memset(screenPos + 7 * GAME_SCREEN_WIDTH, 255, 7);
803 }
804 
showGrid(uint8 * gridBuf)805 void Screen::showGrid(uint8 *gridBuf) {
806 	uint32 gridData = 0;
807 	uint8 bitsLeft = 0;
808 	for (uint16 cnty = 0; cnty < GAME_SCREEN_HEIGHT >> 3; cnty++) {
809 		for (uint16 cntx = 0; cntx < GAME_SCREEN_WIDTH >> 3; cntx++) {
810 			if (!bitsLeft) {
811 				bitsLeft = 32;
812 				gridData = *(uint32 *)gridBuf;
813 				gridBuf += 4;
814 			}
815 			if (gridData & 0x80000000)
816 				paintBox(cntx << 3, cnty << 3);
817 			bitsLeft--;
818 			gridData <<= 1;
819 		}
820 	}
821 	_system->copyRectToScreen(_currentScreen, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
822 
823 }
824 
825 } // End of namespace Sky
826