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 "graphics/palette.h"
24 #include "neverhood/gamemodule.h"
25 #include "neverhood/smackerplayer.h"
26 #include "neverhood/palette.h"
27 #include "neverhood/resourceman.h"
28 #include "neverhood/scene.h"
29 
30 namespace Neverhood {
31 
32 // SmackerSurface
33 
SmackerSurface(NeverhoodEngine * vm)34 SmackerSurface::SmackerSurface(NeverhoodEngine *vm)
35 	: BaseSurface(vm, 0, 0, 0, "smacker"), _smackerFrame(NULL) {
36 }
37 
draw()38 void SmackerSurface::draw() {
39 	if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0)
40 		_vm->_screen->drawSurface2(_smackerFrame, _drawRect, _clipRect, false, ++_version);
41 }
42 
setSmackerFrame(const Graphics::Surface * smackerFrame)43 void SmackerSurface::setSmackerFrame(const Graphics::Surface *smackerFrame) {
44 	_drawRect.x = 0;
45 	_drawRect.y = 0;
46 	_drawRect.width = smackerFrame->w;
47 	_drawRect.height = smackerFrame->h;
48 	_sysRect.x = 0;
49 	_sysRect.y = 0;
50 	_sysRect.width = (smackerFrame->w + 3) & 0xFFFC; // align by 4 bytes
51 	_sysRect.height = smackerFrame->h;
52 	_smackerFrame = smackerFrame;
53 }
54 
unsetSmackerFrame()55 void SmackerSurface::unsetSmackerFrame() {
56 	_drawRect.x = 0;
57 	_drawRect.y = 0;
58 	_drawRect.width = 0;
59 	_drawRect.height = 0;
60 	_sysRect.x = 0;
61 	_sysRect.y = 0;
62 	_sysRect.width = 0;
63 	_sysRect.height = 0;
64 	_smackerFrame = NULL;
65 }
66 
67 // SmackerDoubleSurface
68 
SmackerDoubleSurface(NeverhoodEngine * vm)69 SmackerDoubleSurface::SmackerDoubleSurface(NeverhoodEngine *vm)
70 	: SmackerSurface(vm) {
71 }
72 
draw()73 void SmackerDoubleSurface::draw() {
74 	if (_smackerFrame && _visible && _drawRect.width > 0 && _drawRect.height > 0)
75 		_vm->_screen->drawDoubleSurface2(_smackerFrame, _drawRect);
76 }
77 
78 // NeverhoodSmackerDecoder
79 
forceSeekToFrame(uint frame)80 void NeverhoodSmackerDecoder::forceSeekToFrame(uint frame) {
81 	if (!isVideoLoaded())
82 		return;
83 
84 	if (frame >= getFrameCount())
85 		error("Can't force Smacker seek to invalid frame %d", frame);
86 
87 	if (_header.audioInfo[0].hasAudio)
88 		error("Can't force Smacker frame seek with audio");
89 	if (!rewind())
90 		error("Failed to rewind");
91 
92 	SmackerVideoTrack *videoTrack = (SmackerVideoTrack *)getTrack(0);
93 	uint32 offset = 0;
94 	for (uint32 i = 0; i < frame; i++) {
95 		videoTrack->increaseCurFrame();
96 		offset += _frameSizes[i] & ~3;
97 	}
98 
99 	_fileStream->seek(offset, SEEK_CUR);
100 }
101 
102 // SmackerPlayer
103 
SmackerPlayer(NeverhoodEngine * vm,Scene * scene,uint32 fileHash,bool doubleSurface,bool flag,bool paused)104 SmackerPlayer::SmackerPlayer(NeverhoodEngine *vm, Scene *scene, uint32 fileHash, bool doubleSurface, bool flag, bool paused)
105 	: Entity(vm, 0), _scene(scene), _doubleSurface(doubleSurface), _videoDone(false), _paused(paused),
106 	_palette(NULL), _smackerDecoder(NULL), _smackerSurface(NULL), _stream(NULL), _smackerFirst(true),
107 	_drawX(-1), _drawY(-1) {
108 
109 	SetUpdateHandler(&SmackerPlayer::update);
110 
111 	if (_doubleSurface) {
112 		_smackerSurface = new SmackerDoubleSurface(_vm);
113 	} else {
114 		_smackerSurface = new SmackerSurface(_vm);
115 	}
116 
117 	open(fileHash, flag);
118 }
119 
~SmackerPlayer()120 SmackerPlayer::~SmackerPlayer() {
121 	close();
122 	delete _smackerSurface;
123 	_smackerSurface = NULL;
124 }
125 
open(uint32 fileHash,bool keepLastFrame)126 void SmackerPlayer::open(uint32 fileHash, bool keepLastFrame) {
127 	debug(0, "SmackerPlayer::open(%08X)", fileHash);
128 
129 	_fileHash = fileHash;
130 	_keepLastFrame = keepLastFrame;
131 
132 	close();
133 
134 	_smackerFirst = true;
135 
136 	_stream = _vm->_res->createStream(fileHash);
137 
138 	_smackerDecoder = new NeverhoodSmackerDecoder();
139 	_smackerDecoder->loadStream(_stream);
140 
141 	_palette = new Palette(_vm);
142 	_palette->usePalette();
143 
144 	if (!_paused)
145 		_smackerDecoder->start();
146 
147 }
148 
close()149 void SmackerPlayer::close() {
150 	if (_smackerDecoder)
151 		_smackerDecoder->stop();
152 	delete _smackerDecoder;
153 	delete _palette;
154 	// NOTE The SmackerDecoder deletes the _stream
155 	_smackerDecoder = NULL;
156 	_palette = NULL;
157 	_stream = NULL;
158 	_smackerSurface->unsetSmackerFrame();
159 }
160 
gotoFrame(int frameNumber)161 void SmackerPlayer::gotoFrame(int frameNumber) {
162 	if (_smackerDecoder) {
163 		_smackerDecoder->forceSeekToFrame(frameNumber);
164 		updateFrame();
165 	}
166 }
167 
getFrameCount()168 uint32 SmackerPlayer::getFrameCount() {
169 	return _smackerDecoder ? _smackerDecoder->getFrameCount() : 0;
170 }
171 
getFrameNumber()172 uint32 SmackerPlayer::getFrameNumber() {
173 	return _smackerDecoder ? _smackerDecoder->getCurFrame() : 0;
174 }
175 
getStatus()176 uint SmackerPlayer::getStatus() {
177 	return 0;
178 }
179 
setDrawPos(int16 x,int16 y)180 void SmackerPlayer::setDrawPos(int16 x, int16 y) {
181 	_drawX = x;
182 	_drawY = y;
183 	if (_smackerSurface) {
184 		_smackerSurface->getDrawRect().x = _drawX;
185 		_smackerSurface->getDrawRect().y = _drawY;
186 	}
187 }
188 
rewind()189 void SmackerPlayer::rewind() {
190 	if (_smackerDecoder)
191 		_smackerDecoder->rewind();
192 }
193 
update()194 void SmackerPlayer::update() {
195 
196 	if (!_smackerDecoder)
197 		return;
198 
199 	if (_paused) {
200 		if (_smackerFirst)
201 			updateFrame();
202 	} else {
203 		if (!_smackerDecoder->endOfVideo()) {
204 			updateFrame();
205 		} else if (!_keepLastFrame) {
206 			// Inform the scene about the end of the video playback
207 			if (_scene)
208 				sendMessage(_scene, NM_ANIMATION_STOP, 0);
209 			_videoDone = true;
210 		} else {
211 			rewind();
212 			updateFrame();
213 			_videoDone = false;
214 		}
215 	}
216 
217 }
218 
updateFrame()219 void SmackerPlayer::updateFrame() {
220 
221 	if (!_smackerDecoder || !_smackerSurface)
222 		return;
223 
224 	const Graphics::Surface *smackerFrame = _smackerDecoder->decodeNextFrame();
225 
226 	if (_smackerFirst) {
227 		_smackerSurface->setSmackerFrame(smackerFrame);
228 		if (_drawX < 0 || _drawY < 0) {
229 			if (_doubleSurface) {
230 				_drawX = 320 - _smackerDecoder->getWidth();
231 				_drawY = 240 - _smackerDecoder->getHeight();
232 			} else {
233 				_drawX = (640 - _smackerDecoder->getWidth()) / 2;
234 				_drawY = (480 - _smackerDecoder->getHeight()) / 2;
235 			}
236 		}
237 		_smackerSurface->getDrawRect().x = _drawX;
238 		_smackerSurface->getDrawRect().y = _drawY;
239 		_smackerFirst = false;
240 	}
241 
242 	if (_smackerDecoder->hasDirtyPalette())
243 		updatePalette();
244 
245 }
246 
updatePalette()247 void SmackerPlayer::updatePalette() {
248 	byte tempPalette[1024];
249 	const byte *smackerPalette = _smackerDecoder->getPalette();
250 	for (int i = 0; i < 256; i++) {
251 		tempPalette[i * 4 + 0] = smackerPalette[i * 3 + 0];
252 		tempPalette[i * 4 + 1] = smackerPalette[i * 3 + 1];
253 		tempPalette[i * 4 + 2] = smackerPalette[i * 3 + 2];
254 	}
255 
256 	// WORKAROUND: Scene 3, module 3000 defines a black color 255 instead of
257 	// white, which results in the mouse cursor showing black. I'm not sure if
258 	// color 255 is always supposed to be white. It's not feasible to check
259 	// all scenes for a glitch that only seems to manifest in one, therefore
260 	// we define color 255 to be white only for that scene.
261 	if (_vm->_gameModule->getCurrentModuleNum() == 3000 && _vm->_gameState.sceneNum == 3)
262 			tempPalette[255 * 4 + 0] = tempPalette[255 * 4 + 1] = tempPalette[255 * 4 + 2] = 0xFF;
263 
264 	_palette->copyPalette(tempPalette, 0, 256, 0);
265 }
266 
267 } // End of namespace Neverhood
268