1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "lure/animseq.h"
24 #include "lure/decode.h"
25 #include "lure/events.h"
26 #include "lure/lure.h"
27 #include "lure/palette.h"
28 #include "lure/sound.h"
29 #include "common/endian.h"
30 
31 namespace Lure {
32 
33 // delay
34 // Delays for a given number of milliseconds. If it returns true, it indicates that
35 // Escape has been pressed, and the introduction should be aborted.
36 
delay(uint32 milliseconds)37 AnimAbortType AnimationSequence::delay(uint32 milliseconds) {
38 	Events &events = Events::getReference();
39 	uint32 delayCtr = g_system->getMillis() + milliseconds;
40 
41 	while (g_system->getMillis() < delayCtr) {
42 		while (events.pollEvent()) {
43 			if ((events.type() == Common::EVENT_KEYDOWN) && (events.event().kbd.ascii != 0)) {
44 				if (events.event().kbd.keycode == Common::KEYCODE_ESCAPE)
45 					return ABORT_END_INTRO;
46 				else
47 					return ABORT_NEXT_SCENE;
48 			} else if (events.type() == Common::EVENT_LBUTTONDOWN) {
49 				return ABORT_NEXT_SCENE;
50 			} else if ((events.type() == Common::EVENT_QUIT) || (events.type() == Common::EVENT_RETURN_TO_LAUNCHER)) {
51 				return ABORT_END_INTRO;
52 			} else if (events.type() == Common::EVENT_MAINMENU) {
53 				return ABORT_NONE;
54 			}
55 
56 		}
57 
58 		uint32 delayAmount = delayCtr - g_system->getMillis();
59 		if (delayAmount > 10) delayAmount = 10;
60 		g_system->delayMillis(delayAmount);
61 	}
62 	return ABORT_NONE;
63 }
64 
65 // egaDecodeFrame
66 // Decodes a single frame of a EGA animation sequence
67 
egaDecodeFrame(byte * & pPixels)68 void AnimationSequence::egaDecodeFrame(byte *&pPixels) {
69 	Screen &screen = Screen::getReference();
70 	byte *screenData = screen.screen_raw();
71 
72 	// Skip over the list of blocks that are changed
73 	int numBlocks = *pPixels++;
74 	pPixels += numBlocks;
75 
76 	// Loop through the list of same/changed pixel ranges
77 	int len = *pPixels++;
78 	int offset = MENUBAR_Y_SIZE * FULL_SCREEN_WIDTH *
79 		EGA_NUM_LAYERS / EGA_PIXELS_PER_BYTE;
80 	while ((offset += len) < FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT / 2) {
81 
82 		int repeatLen = *pPixels++;
83 		if (repeatLen > 0) {
84 			byte *pDest = screenData + (offset / EGA_NUM_LAYERS) * EGA_PIXELS_PER_BYTE;
85 
86 			// Copy over the following bytes - each four bytes contain the four
87 			// planes worth of data for 8 sequential pixels
88 			while (repeatLen-- > 0) {
89 				int planeNum = offset % EGA_NUM_LAYERS;
90 				byte v = *pPixels++;
91 				for (int bitCtr = 0; bitCtr < 8; ++bitCtr, v <<= 1) {
92 					if ((v & 0x80) != 0)
93 						*(pDest + bitCtr) |= 1 << planeNum;
94 					else
95 						*(pDest + bitCtr) &= ~(1 << planeNum);
96 				}
97 
98 				if ((++offset % EGA_NUM_LAYERS) == 0)
99 					pDest += EGA_PIXELS_PER_BYTE;
100 			}
101 		}
102 
103 		// Get next skip bytes length
104 		len = *pPixels++;
105 	}
106 }
107 
108 // vgaDecodeFrame
109 // Decodes a single frame of a VGA animation sequence
110 
vgaDecodeFrame(byte * & pPixels,byte * & pLines)111 void AnimationSequence::vgaDecodeFrame(byte *&pPixels, byte *&pLines) {
112 	Screen &screen = Screen::getReference();
113 	byte *screenData = screen.screen_raw();
114 	uint16 screenPos = 0;
115 	uint16 len;
116 
117 	while (screenPos < SCREEN_SIZE) {
118 		// Get line length
119 		len = (uint16) *pLines++;
120 		if (len == 0) {
121 			len = READ_LE_UINT16(pLines);
122 			pLines += 2;
123 		}
124 
125 		// Move the splice over
126 		memcpy(screenData, pPixels, len);
127 		screenData += len;
128 		screenPos += len;
129 		pPixels += len;
130 
131 		// Get the offset inc amount
132 		len = (uint16) *pLines++;
133 		if (len == 0) {
134 			len = READ_LE_UINT16(pLines);
135 			pLines += 2;
136 		}
137 
138 		screenData += len;
139 		screenPos += len;
140 	}
141 }
142 
AnimationSequence(uint16 screenId,Palette & palette,bool fadeIn,int frameDelay,const AnimSoundSequence * soundList,uint8 loops)143 AnimationSequence::AnimationSequence(uint16 screenId, Palette &palette,  bool fadeIn, int frameDelay,
144 					 const AnimSoundSequence *soundList, uint8 loops): _screenId(screenId), _palette(palette),
145 					 _frameDelay(frameDelay), _soundList(soundList), _loops(loops) {
146 	Screen &screen = Screen::getReference();
147 	PictureDecoder decoder;
148 	Disk &d = Disk::getReference();
149 
150 	// Get the data and decode it. Note that VGA decompression is used
151 	// even if the decompressed contents is actually EGA data
152 	MemoryBlock *data = d.getEntry(_screenId);
153 	_decodedData = decoder.vgaDecode(data, MAX_ANIM_DECODER_BUFFER_SIZE);
154 	delete data;
155 
156 	_isEGA = LureEngine::getReference().isEGA();
157 	if (_isEGA) {
158 		// Setup for EGA animation
159 		_lineRefs = NULL;
160 
161 		// Reset the palette and clear the screen for EGA decoding
162 		screen.setPaletteEmpty(RES_PALETTE_ENTRIES);
163 		screen.screen().empty();
164 
165 		byte *pSrc = _decodedData->data();
166 		pSrc = showInitialScreen(pSrc);
167 		screen.setPalette(&_palette, 0, _palette.numEntries());
168 
169 		// Set pointers for animation
170 		_pPixelsStart = _pPixels = pSrc;
171 		_pPixelsEnd = _decodedData->data() + _decodedData->size() - 1;
172 		_pLinesStart = _pLines = NULL;
173 		_pLinesEnd = NULL;
174 
175 	} else {
176 		// Setup for VGA animation
177 		_lineRefs = d.getEntry(_screenId + 1);
178 
179 		// Reset the palette and set the initial starting screen
180 		screen.setPaletteEmpty(RES_PALETTE_ENTRIES);
181 		showInitialScreen();
182 
183 		// Set the palette
184 		if (fadeIn)	screen.paletteFadeIn(&_palette);
185 		else screen.setPalette(&_palette, 0, _palette.numEntries());
186 
187 		// Set up frame pointers
188 		_pPixelsStart = _pPixels = _decodedData->data() + SCREEN_SIZE;
189 		_pPixelsEnd = _decodedData->data() + _decodedData->size() - 1;
190 		_pLinesStart = _pLines = _lineRefs->data();
191 		_pLinesEnd = _lineRefs->data() + _lineRefs->size() - 1;
192 	}
193 }
194 
~AnimationSequence()195 AnimationSequence::~AnimationSequence() {
196 	delete _lineRefs;
197 	delete _decodedData;
198 
199 	// Renable GMM saving/loading now that the animation is done
200 	LureEngine::getReference()._saveLoadAllowed = true;
201 }
202 
203 // show
204 // Main method for displaying the animation
205 
show()206 AnimAbortType AnimationSequence::show() {
207 	Screen &screen = Screen::getReference();
208 	AnimAbortType result;
209 	const AnimSoundSequence *soundFrame = _soundList;
210 	int frameCtr = 0;
211 
212 	// Disable GMM saving/loading whilst animation is running
213 	LureEngine::getReference()._saveLoadAllowed = false;
214 
215 	// Loop through displaying the animations
216 	while (_loops > 0) {
217 		if (_pPixels < _pPixelsEnd && (_isEGA || _pLines < _pLinesEnd)) {
218 			if ((soundFrame != NULL) && (soundFrame->rolandSoundId != 0xFF) && (frameCtr == 0))
219 				Sound.musicInterface_Play(
220 					Sound.isRoland() ? soundFrame->rolandSoundId : soundFrame->adlibSoundId, soundFrame->music);
221 
222 			if (_isEGA)
223 				egaDecodeFrame(_pPixels);
224 			else {
225 				vgaDecodeFrame(_pPixels, _pLines);
226 			}
227 
228 			// Make the decoded frame visible
229 			screen.update();
230 		} else {
231 			// Animation has finished.
232 			_loops--;
233 			if (_loops > 0) {
234 				// Animation will be repeated, so reset
235 				// and show the first frame again.
236 				_pPixels = _pPixelsStart;
237 				_pLines = _pLinesStart;
238 				showInitialScreen(_decodedData->data());
239 			}
240 		}
241 
242 		result = delay(_frameDelay * 1000 / 50);
243 		if (result != ABORT_NONE) return result;
244 
245 		if ((soundFrame != NULL) && (++frameCtr == soundFrame->numFrames)) {
246 			frameCtr = 0;
247 			++soundFrame;
248 			if (soundFrame->numFrames == 0) soundFrame = NULL;
249 		}
250 	}
251 
252 	return ABORT_NONE;
253 }
254 
step()255 bool AnimationSequence::step() {
256 	Screen &screen = Screen::getReference();
257 	if (_pPixels >= _pPixelsEnd) return false;
258 
259 	if (_isEGA)
260 		egaDecodeFrame(_pPixels);
261 	else {
262 		if (_pLines >= _pLinesEnd) return false;
263 		vgaDecodeFrame(_pPixels, _pLines);
264 	}
265 
266 	// Make the decoded frame visible
267 	screen.update();
268 	screen.setPalette(&_palette);
269 
270 	return true;
271 }
272 
showInitialScreen(byte * pSrc)273 byte *AnimationSequence::showInitialScreen(byte *pSrc) {
274 	Screen &screen = Screen::getReference();
275 
276 	if (_isEGA) {
277 		// Load the screen - each four bytes contain the four planes
278 		// worth of data for 8 sequential pixels
279 		byte *pDest = screen.screen().data().data() +
280 			(FULL_SCREEN_WIDTH * MENUBAR_Y_SIZE);
281 
282 		for (int ctr = 0; ctr < FULL_SCREEN_WIDTH * (FULL_SCREEN_HEIGHT -
283 			MENUBAR_Y_SIZE) / 8; ++ctr, pDest += EGA_PIXELS_PER_BYTE) {
284 			for (int planeCtr = 0; planeCtr < EGA_NUM_LAYERS; ++planeCtr, ++pSrc) {
285 				byte v = *pSrc;
286 				for (int bitCtr = 0; bitCtr < 8; ++bitCtr, v <<= 1) {
287 					if ((v & 0x80) != 0)
288 						*(pDest + bitCtr) |= 1 << planeCtr;
289 				}
290 			}
291 		}
292 	} else {
293 		screen.screen().data().copyFrom(_decodedData, 0, 0, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH);
294 	}
295 	screen.update();
296 
297 	return pSrc;
298 }
299 
300 } // End of namespace Lure
301