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  * Additional copyright for this file:
8  * Copyright (C) 1994-1998 Revolution Software Ltd.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24 
25 #include "common/file.h"
26 #include "common/mutex.h"
27 #include "common/system.h"
28 #include "common/textconsole.h"
29 #include "common/translation.h"
30 
31 #include "sword2/sword2.h"
32 #include "sword2/defs.h"
33 #include "sword2/header.h"
34 #include "sword2/logic.h"
35 #include "sword2/maketext.h"
36 #include "sword2/resman.h"
37 #include "sword2/sound.h"
38 #include "sword2/screen.h"
39 #include "sword2/animation.h"
40 
41 #include "graphics/palette.h"
42 
43 #include "gui/message.h"
44 
45 #ifdef USE_MPEG2
46 #include "video/avi_decoder.h"
47 #endif
48 
49 #ifdef USE_ZLIB
50 #include "video/dxa_decoder.h"
51 #endif
52 
53 #include "video/smk_decoder.h"
54 #include "video/psx_decoder.h"
55 
56 #include "engines/util.h"
57 
58 namespace Sword2 {
59 
60 ///////////////////////////////////////////////////////////////////////////////
61 // Basic movie player
62 ///////////////////////////////////////////////////////////////////////////////
63 
MoviePlayer(Sword2Engine * vm,OSystem * system,Video::VideoDecoder * decoder,DecoderType decoderType)64 MoviePlayer::MoviePlayer(Sword2Engine *vm, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType)
65 	: _vm(vm), _system(system) {
66 	_decoderType = decoderType;
67 	_decoder = decoder;
68 
69 	_white = 255;
70 	_black = 0;
71 }
72 
~MoviePlayer()73 MoviePlayer::~MoviePlayer() {
74 	delete _decoder;
75 }
76 
77 /**
78  * Plays an animated cutscene.
79  * @param id the id of the file
80  */
load(const char * name)81 bool MoviePlayer::load(const char *name) {
82 	// This happens when quitting during the "eye" cutscene.
83 	if (_vm->shouldQuit())
84 		return false;
85 
86 	_textSurface = NULL;
87 
88 	Common::String filename;
89 	switch (_decoderType) {
90 	case kVideoDecoderDXA:
91 		filename = Common::String::format("%s.dxa", name);
92 		break;
93 	case kVideoDecoderSMK:
94 		filename = Common::String::format("%s.smk", name);
95 		break;
96 	case kVideoDecoderPSX:
97 		filename = Common::String::format("%s.str", name);
98 		break;
99 	case kVideoDecoderMP2:
100 		filename = Common::String::format("%s.mp2", name);
101 		break;
102 	}
103 
104 	// Need to switch to true color for PSX/MP2 videos
105 	if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2)
106 		initGraphics(g_system->getWidth(), g_system->getHeight(), nullptr);
107 
108 	if (!_decoder->loadFile(filename)) {
109 		// Go back to 8bpp color
110 		if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2)
111 			initGraphics(g_system->getWidth(), g_system->getHeight());
112 
113 		return false;
114 	}
115 
116 	// For DXA/MP2, also add the external sound file
117 	if (_decoderType == kVideoDecoderDXA || _decoderType == kVideoDecoderMP2)
118 		_decoder->addStreamFileTrack(name);
119 
120 	_decoder->start();
121 	return true;
122 }
123 
play(MovieText * movieTexts,uint32 numMovieTexts,uint32 leadIn,uint32 leadOut)124 void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut) {
125 	_leadOutFrame = _decoder->getFrameCount();
126 	if (_leadOutFrame > 60)
127 		_leadOutFrame -= 60;
128 
129 	_movieTexts = movieTexts;
130 	_numMovieTexts = numMovieTexts;
131 	_currentMovieText = 0;
132 	_leadOut = leadOut;
133 
134 	if (leadIn)
135 		_vm->_sound->playMovieSound(leadIn, kLeadInSound);
136 
137 	bool terminated = !playVideo();
138 
139 	closeTextObject(_currentMovieText, NULL, 0);
140 
141 	if (terminated) {
142 		_vm->_sound->stopMovieSounds();
143 		_vm->_sound->stopSpeech();
144 	}
145 
146 	// Need to jump back to paletted color
147 	if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2)
148 		initGraphics(640, 480);
149 }
150 
openTextObject(uint32 index)151 void MoviePlayer::openTextObject(uint32 index) {
152 	MovieText *text = &_movieTexts[index];
153 
154 	// Pull out the text line to get the official text number (for WAV id)
155 
156 	uint32 res = text->_textNumber / SIZE;
157 	uint32 localText = text->_textNumber & 0xffff;
158 
159 	// Open text resource and get the line
160 
161 	byte *textData = _vm->fetchTextLine(_vm->_resman->openResource(res), localText);
162 
163 	text->_speechId = READ_LE_UINT16(textData);
164 
165 	// Is it speech or subtitles, or both?
166 
167 	// If we want subtitles, or there was no sound
168 
169 	if (_vm->getSubtitles() || !text->_speechId) {
170 		text->_textMem = _vm->_fontRenderer->makeTextSprite(textData + 2, 600, 255, _vm->_speechFontId, 1);
171 	}
172 
173 	_vm->_resman->closeResource(res);
174 
175 	if (text->_textMem) {
176 		FrameHeader frame;
177 
178 		frame.read(text->_textMem);
179 
180 		text->_textSprite.x = 320 - frame.width / 2;
181 		text->_textSprite.y = 440 - frame.height;
182 		text->_textSprite.w = frame.width;
183 		text->_textSprite.h = frame.height;
184 		text->_textSprite.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION;
185 		text->_textSprite.data = text->_textMem + FrameHeader::size();
186 		text->_textSprite.isText = true;
187 		_vm->_screen->createSurface(&text->_textSprite, &_textSurface);
188 
189 		_textX = 320 - text->_textSprite.w / 2;
190 		_textY = 420 - text->_textSprite.h;
191 	}
192 }
193 
closeTextObject(uint32 index,Graphics::Surface * screen,uint16 pitch)194 void MoviePlayer::closeTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch) {
195 	if (index < _numMovieTexts) {
196 		MovieText *text = &_movieTexts[index];
197 
198 		free(text->_textMem);
199 		text->_textMem = NULL;
200 
201 		if (_textSurface) {
202 			if (screen) {
203 				// If the frame doesn't cover the entire
204 				// screen, we have to erase the subtitles
205 				// manually.
206 
207 				int frameWidth = _decoder->getWidth();
208 				int frameHeight = _decoder->getHeight();
209 
210 				if (_decoderType == kVideoDecoderPSX)
211 					frameHeight *= 2;
212 
213 				int frameX = (_system->getWidth() - frameWidth) / 2;
214 				int frameY = (_system->getHeight() - frameHeight) / 2;
215 				uint32 black = getBlackColor();
216 
217 				for (int y = 0; y < text->_textSprite.h; y++) {
218 					if (_textY + y < frameY || _textY + y >= frameY + frameHeight) {
219 						screen->hLine(_textX, _textY + y, _textX + text->_textSprite.w, black);
220 					} else {
221 						if (frameX > _textX)
222 							screen->hLine(_textX, _textY + y, frameX, black);
223 						if (frameX + frameWidth < _textX + text->_textSprite.w)
224 							screen->hLine(frameX + frameWidth, _textY + y, text->_textSprite.w, black);
225 					}
226 				}
227 			}
228 
229 			_vm->_screen->deleteSurface(_textSurface);
230 			_textSurface = NULL;
231 		}
232 	}
233 }
234 
235 #define PUT_PIXEL(c) \
236 	switch (screen->format.bytesPerPixel) { \
237 	case 1: \
238 		*dst = (c); \
239 		break; \
240 	case 2: \
241 		WRITE_UINT16(dst, (c)); \
242 		break; \
243 	case 4: \
244 		WRITE_UINT32(dst, (c)); \
245 		break; \
246 	}
247 
drawTextObject(uint32 index,Graphics::Surface * screen,uint16 pitch)248 void MoviePlayer::drawTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch) {
249 	MovieText *text = &_movieTexts[index];
250 
251 	uint32 white = getWhiteColor();
252 	uint32 black = getBlackColor();
253 
254 	if (text->_textMem && _textSurface) {
255 		byte *src = text->_textSprite.data;
256 		uint16 width = text->_textSprite.w;
257 		uint16 height = text->_textSprite.h;
258 
259 		// Resize text sprites for PSX version
260 		byte *psxSpriteBuffer = 0;
261 		if (Sword2Engine::isPsx()) {
262 			height *= 2;
263 			psxSpriteBuffer = (byte *)malloc(width * height);
264 			Screen::resizePsxSprite(psxSpriteBuffer, src, width, height);
265 			src = psxSpriteBuffer;
266 		}
267 
268 		for (int y = 0; y < height; y++) {
269 			byte *dst = (byte *)screen->getBasePtr(_textX, _textY + y);
270 
271 			for (int x = 0; x < width; x++) {
272 				if (src[x] == 1) {
273 					PUT_PIXEL(black);
274 				} else if (src[x] == 255) {
275 					PUT_PIXEL(white);
276 				}
277 
278 				dst += screen->format.bytesPerPixel;
279 			}
280 
281 			src += width;
282 		}
283 
284 		// Free buffer used to resize psx sprite
285 		if (Sword2Engine::isPsx())
286 			free(psxSpriteBuffer);
287 	}
288 }
289 
290 #undef PUT_PIXEL
291 
performPostProcessing(Graphics::Surface * screen,uint16 pitch)292 void MoviePlayer::performPostProcessing(Graphics::Surface *screen, uint16 pitch) {
293 	MovieText *text;
294 	int frame = _decoder->getCurFrame();
295 
296 	if (_currentMovieText < _numMovieTexts) {
297 		text = &_movieTexts[_currentMovieText];
298 	} else {
299 		text = NULL;
300 	}
301 
302 	if (text && frame == text->_startFrame) {
303 		if ((_vm->getSubtitles() || !text->_speechId) && _currentMovieText < _numMovieTexts) {
304 			openTextObject(_currentMovieText);
305 		}
306 	}
307 
308 	if (text && frame >= text->_startFrame) {
309 		if (text->_speechId && !text->_played && _vm->_sound->amISpeaking() == RDSE_QUIET) {
310 			text->_played = true;
311 			_vm->_sound->playCompSpeech(text->_speechId, 16, 0);
312 		}
313 		if (frame < text->_endFrame) {
314 			drawTextObject(_currentMovieText, screen, pitch);
315 		} else {
316 			closeTextObject(_currentMovieText, screen, pitch);
317 			_currentMovieText++;
318 		}
319 	}
320 
321 	if (_leadOut && _decoder->getCurFrame() == _leadOutFrame) {
322 		_vm->_sound->playMovieSound(_leadOut, kLeadOutSound);
323 	}
324 }
325 
playVideo()326 bool MoviePlayer::playVideo() {
327 	uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2;
328 	uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2;
329 
330 	while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
331 		if (_decoder->needsUpdate()) {
332 			const Graphics::Surface *frame = _decoder->decodeNextFrame();
333 			if (frame) {
334 				if (_decoderType == kVideoDecoderPSX)
335 					drawFramePSX(frame);
336 				else
337 					_vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, frame->w, frame->h);
338 			}
339 
340 			if (_decoder->hasDirtyPalette()) {
341 				_vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256);
342 
343 				uint32 maxWeight = 0;
344 				uint32 minWeight = 0xFFFFFFFF;
345 				uint32 weight;
346 				byte r, g, b;
347 
348 				const byte *palette = _decoder->getPalette();
349 
350 				for (int i = 0; i < 256; i++) {
351 					r = *palette++;
352 					g = *palette++;
353 					b = *palette++;
354 
355 					weight = 3 * r * r + 6 * g * g + 2 * b * b;
356 
357 					if (weight >= maxWeight) {
358 						maxWeight = weight;
359 						_white = i;
360 					}
361 
362 					if (weight <= minWeight) {
363 						minWeight = weight;
364 						_black = i;
365 					}
366 				}
367 			}
368 
369 			Graphics::Surface *screen = _vm->_system->lockScreen();
370 			performPostProcessing(screen, screen->pitch);
371 			_vm->_system->unlockScreen();
372 			_vm->_system->updateScreen();
373 		}
374 
375 		Common::Event event;
376 		while (_vm->_system->getEventManager()->pollEvent(event))
377 			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
378 				return false;
379 
380 		_vm->_system->delayMillis(10);
381 	}
382 
383 	return !_vm->shouldQuit();
384 }
385 
getBlackColor()386 uint32 MoviePlayer::getBlackColor() {
387 	return (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) ? g_system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : _black;
388 }
389 
getWhiteColor()390 uint32 MoviePlayer::getWhiteColor() {
391 	return (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) ? g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF) : _white;
392 }
393 
drawFramePSX(const Graphics::Surface * frame)394 void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) {
395 	// The PSX videos have half resolution
396 
397 	Graphics::Surface scaledFrame;
398 	scaledFrame.create(frame->w, frame->h * 2, frame->format);
399 
400 	for (int y = 0; y < scaledFrame.h; y++)
401 		memcpy(scaledFrame.getBasePtr(0, y), frame->getBasePtr(0, y / 2), scaledFrame.w * scaledFrame.format.bytesPerPixel);
402 
403 	uint16 x = (g_system->getWidth() - scaledFrame.w) / 2;
404 	uint16 y = (g_system->getHeight() - scaledFrame.h) / 2;
405 
406 	_vm->_system->copyRectToScreen(scaledFrame.getPixels(), scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h);
407 
408 	scaledFrame.free();
409 }
410 
411 ///////////////////////////////////////////////////////////////////////////////
412 // Factory function for creating the appropriate cutscene player
413 ///////////////////////////////////////////////////////////////////////////////
414 
makeMoviePlayer(const char * name,Sword2Engine * vm,OSystem * system,uint32 frameCount)415 MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system, uint32 frameCount) {
416 	Common::String filename;
417 
418 	filename = Common::String::format("%s.str", name);
419 
420 	if (Common::File::exists(filename)) {
421 #ifdef USE_RGB_COLOR
422 		Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x, frameCount);
423 		return new MoviePlayer(vm, system, psxDecoder, kVideoDecoderPSX);
424 #else
425 		GUI::MessageDialog dialog(_("PSX cutscenes found but ScummVM has been built without RGB color support"), _("OK"));
426 		dialog.runModal();
427 		return NULL;
428 #endif
429 	}
430 
431 	filename = Common::String::format("%s.smk", name);
432 
433 	if (Common::File::exists(filename)) {
434 		Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();
435 		return new MoviePlayer(vm, system, smkDecoder, kVideoDecoderSMK);
436 	}
437 
438 	filename = Common::String::format("%s.dxa", name);
439 
440 	if (Common::File::exists(filename)) {
441 #ifdef USE_ZLIB
442 		Video::DXADecoder *dxaDecoder = new Video::DXADecoder();
443 		return new MoviePlayer(vm, system, dxaDecoder, kVideoDecoderDXA);
444 #else
445 		GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib"), _("OK"));
446 		dialog.runModal();
447 		return NULL;
448 #endif
449 	}
450 
451 	// Old MPEG2 cutscenes
452 	filename = Common::String::format("%s.mp2", name);
453 
454 	if (Common::File::exists(filename)) {
455 #ifdef USE_MPEG2
456 		// HACK: Old ScummVM builds ignored the AVI frame rate field and forced the video
457 		// to be played back at 12fps.
458 		Video::AVIDecoder *aviDecoder = new Video::AVIDecoder(12);
459 		return new MoviePlayer(vm, system, aviDecoder, kVideoDecoderMP2);
460 #else
461 		GUI::MessageDialog dialog(_("MPEG-2 cutscenes found but ScummVM has been built without MPEG-2 support"), _("OK"));
462 		dialog.runModal();
463 		return NULL;
464 #endif
465 	}
466 
467 	// The demo tries to play some cutscenes that aren't there, so make those warnings more discreet.
468 	// In addition, some of the later re-releases of the game don't have the "eye" Virgin logo movie.
469 	if (!vm->_logic->readVar(DEMO) && strcmp(name, "eye") != 0) {
470 		Common::String buf = Common::String::format(_("Cutscene '%s' not found"), name);
471 		GUI::MessageDialog dialog(buf, _("OK"));
472 		dialog.runModal();
473 	} else
474 		warning("Cutscene '%s' not found", name);
475 
476 	return NULL;
477 }
478 
479 } // End of namespace Sword2
480