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