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