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 
24 
25 #ifdef ENABLE_AGOS2
26 
27 #include "common/endian.h"
28 #include "common/events.h"
29 #include "common/file.h"
30 #include "common/system.h"
31 #include "common/textconsole.h"
32 #include "common/translation.h"
33 
34 #include "graphics/cursorman.h"
35 #include "graphics/palette.h"
36 #include "graphics/surface.h"
37 
38 #include "agos/animation.h"
39 #include "agos/intern.h"
40 #include "agos/agos.h"
41 
42 #include "audio/audiostream.h"
43 #include "audio/decoders/wave.h"
44 
45 #include "gui/message.h"
46 
47 namespace AGOS {
48 
MoviePlayer(AGOSEngine_Feeble * vm)49 MoviePlayer::MoviePlayer(AGOSEngine_Feeble *vm)
50 	: _vm(vm) {
51 	_mixer = _vm->_mixer;
52 
53 	_leftButtonDown = false;
54 	_rightButtonDown = false;
55 	_skipMovie = false;
56 
57 	memset(baseName, 0, sizeof(baseName));
58 
59 	_ticks = 0;
60 	_bgSoundStream = nullptr;
61 }
62 
~MoviePlayer()63 MoviePlayer::~MoviePlayer() {
64 }
65 
play()66 void MoviePlayer::play() {
67 	if (_vm->getBitFlag(40)) {
68 		_vm->setBitFlag(42, false);
69 		startSound();
70 		return;
71 	}
72 
73 	_leftButtonDown = false;
74 	_rightButtonDown = false;
75 	_skipMovie = false;
76 
77 	_vm->_mixer->stopAll();
78 
79 	_ticks = _vm->_system->getMillis();
80 
81 	startSound();
82 
83 	playVideo();
84 	stopVideo();
85 
86 	_vm->o_killAnimate();
87 
88 	if (_vm->getBitFlag(41)) {
89 		_vm->fillBackFromFront();
90 	} else {
91 		uint8 palette[768];
92 		memset(palette, 0, sizeof(palette));
93 		_vm->clearSurfaces();
94 		_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
95 	}
96 
97 	_vm->fillBackGroundFromBack();
98 	_vm->_fastFadeOutFlag = true;
99 }
100 
handleNextFrame()101 void MoviePlayer::handleNextFrame() {
102 	Common::Event event;
103 	Common::EventManager *eventMan = _vm->_system->getEventManager();
104 	while (eventMan->pollEvent(event)) {
105 		switch (event.type) {
106 		case Common::EVENT_KEYDOWN:
107 			if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
108 				_leftButtonDown = true;
109 				_rightButtonDown = true;
110 			} else if (event.kbd.keycode == Common::KEYCODE_PAUSE) {
111 				_vm->pause();
112 			}
113 			break;
114 		case Common::EVENT_LBUTTONDOWN:
115 			_leftButtonDown = true;
116 			break;
117 		case Common::EVENT_RBUTTONDOWN:
118 			_rightButtonDown = true;
119 			break;
120 		case Common::EVENT_LBUTTONUP:
121 			_leftButtonDown = false;
122 			break;
123 		case Common::EVENT_RBUTTONUP:
124 			_rightButtonDown = false;
125 			break;
126 		default:
127 			break;
128 		}
129 	}
130 
131 	if (_leftButtonDown && _rightButtonDown && !_vm->getBitFlag(41)) {
132 		_skipMovie = true;
133 		_mixer->stopHandle(_bgSound);
134 	}
135 }
136 
137 ///////////////////////////////////////////////////////////////////////////////
138 // Movie player for DXA movies
139 ///////////////////////////////////////////////////////////////////////////////
140 
141 const char *const MoviePlayerDXA::_sequenceList[90] = {
142 	"agent32",
143 	"Airlock",
144 	"Badluck",
145 	"bentalk1",
146 	"bentalk2",
147 	"bentalk3",
148 	"BigFight",
149 	"BLOWLAB",
150 	"breakdown",
151 	"bridge",
152 	"button2",
153 	"cargo",
154 	"COACH",
155 	"Colatalk",
156 	"cygnus2",
157 	"dream",
158 	"escape2",
159 	"FASALL",
160 	"fbikewurb",
161 	"feebdel",
162 	"Feebohno",
163 	"feebpump",
164 	"feefone1",
165 	"feefone2",
166 	"founder2",
167 	"founder3",
168 	"founder4",
169 	"fxmadsam",
170 	"fxwakeup",
171 	"gate",
172 	"Get Car",
173 	"getaxe",
174 	"getlift",
175 	"icetrench",
176 	"intomb1",
177 	"intomb2",
178 	"Jackpot",
179 	"knockout",
180 	"labocto",
181 	"longfeeb",
182 	"Mainmin",
183 	"maznat",
184 	"meetsquid",
185 	"mflirt",
186 	"mfxHappy",
187 	"Mix_Feeb1",
188 	"Mix_Feeb2",
189 	"Mix_Feeb3",
190 	"Mix_Guardscn",
191 	"Mlights1",
192 	"MLights2",
193 	"MProtest",
194 	"mudman",
195 	"munlock",
196 	"MUS5P2",
197 	"MUSOSP1",
198 	"Omenter",
199 	"Omnicofe",
200 	"OUTMIN~1",
201 	"Readbook",
202 	"Rebelhq",
203 	"RebelHQ2",
204 	"Reedin",
205 	"rescue1",
206 	"rescue2",
207 	"samcar",
208 	"Samdead",
209 	"scanner",
210 	"Sleepy",
211 	"spitbrai",
212 	"statue1",
213 	"statue2",
214 	"sva1",
215 	"sva2",
216 	"Teeter",
217 	"Temple2",
218 	"Temple3",
219 	"Temple4",
220 	"Temple5",
221 	"Temple6",
222 	"Temple7",
223 	"Temple8",
224 	"Tic-tac2",
225 	"torture",
226 	"transmit",
227 	"Typey",
228 	"ventfall",
229 	"ventoff",
230 	"wasting",
231 	"wurbatak"
232 };
233 
MoviePlayerDXA(AGOSEngine_Feeble * vm,const char * name)234 MoviePlayerDXA::MoviePlayerDXA(AGOSEngine_Feeble *vm, const char *name)
235 	: MoviePlayer(vm) {
236 	debug(0, "Creating DXA cutscene player");
237 
238 	memset(baseName, 0, sizeof(baseName));
239 	memcpy(baseName, name, strlen(name));
240 
241 	_sequenceNum = 0;
242 }
243 
load()244 bool MoviePlayerDXA::load() {
245 	if ((_vm->getPlatform() == Common::kPlatformAmiga || _vm->getPlatform() == Common::kPlatformMacintosh) &&
246 		_vm->_language != Common::EN_ANY) {
247 		_sequenceNum = 0;
248 		for (uint i = 0; i < 90; i++) {
249 			if (!scumm_stricmp(baseName, _sequenceList[i]))
250 				_sequenceNum = i;
251 		}
252 	}
253 
254 	Common::String videoName = Common::String::format("%s.dxa", baseName);
255 	Common::File *videoStream = new Common::File();
256 	if (!videoStream->open(videoName))
257 		error("Failed to load video file %s", videoName.c_str());
258 	if (!loadStream(videoStream))
259 		error("Failed to load video stream from file %s", videoName.c_str());
260 
261 	debug(0, "Playing video %s", videoName.c_str());
262 
263 	CursorMan.showMouse(false);
264 	return true;
265 }
266 
copyFrameToBuffer(byte * dst,uint x,uint y,uint pitch)267 void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
268 	uint h = getHeight();
269 	uint w = getWidth();
270 
271 	const Graphics::Surface *surface = decodeNextFrame();
272 
273 	if (!surface)
274 		return;
275 
276 	const byte *src = (const byte *)surface->getPixels();
277 	dst += y * pitch + x;
278 
279 	do {
280 		memcpy(dst, src, w);
281 		dst += pitch;
282 		src += w;
283 	} while (--h);
284 
285 	if (hasDirtyPalette())
286 		g_system->getPaletteManager()->setPalette(getPalette(), 0, 256);
287 }
288 
playVideo()289 void MoviePlayerDXA::playVideo() {
290 	// Most of the videos included in the Amiga version, reduced the
291 	// resolution to 384 x 280, so require the screen to be cleared,
292 	// before starting playing those videos.
293 	if (getWidth() == 384 && getHeight() == 280) {
294 		_vm->clearSurfaces();
295 	}
296 
297 	while (!endOfVideo() && !_skipMovie && !_vm->shouldQuit())
298 		handleNextFrame();
299 }
300 
stopVideo()301 void MoviePlayerDXA::stopVideo() {
302 	close();
303 	_mixer->stopHandle(_bgSound);
304 }
305 
startSound()306 void MoviePlayerDXA::startSound() {
307 	start();
308 
309 	if (_bgSoundStream != NULL) {
310 		_vm->_mixer->stopHandle(_bgSound);
311 		_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_bgSound, _bgSoundStream, -1, getVolume(), getBalance());
312 	}
313 }
314 
nextFrame()315 void MoviePlayerDXA::nextFrame() {
316 	if (_bgSoundStream && _vm->_mixer->isSoundHandleActive(_bgSound) && needsUpdate()) {
317 		copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
318 		return;
319 	}
320 
321 	if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) {
322 		rewind();
323 		startSound();
324 	}
325 
326 	if (!endOfVideo()) {
327 		if (_vm->_interactiveVideo == TYPE_OMNITV) {
328 			copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
329 		} else if (_vm->_interactiveVideo == TYPE_LOOPING) {
330 			copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
331 		}
332 	} else if (_vm->_interactiveVideo == TYPE_OMNITV) {
333 		close();
334 		_vm->_interactiveVideo = 0;
335 		_vm->_variableArray[254] = 6747;
336 	}
337 }
338 
handleNextFrame()339 void MoviePlayerDXA::handleNextFrame() {
340 	if (processFrame())
341 		_vm->_system->updateScreen();
342 
343 	MoviePlayer::handleNextFrame();
344 }
345 
processFrame()346 bool MoviePlayerDXA::processFrame() {
347 	Graphics::Surface *screen = _vm->getBackendSurface();
348 	copyFrameToBuffer((byte *)screen->getPixels(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
349 	_vm->updateBackendSurface();
350 
351 	uint32 soundTime = _mixer->getSoundElapsedTime(_bgSound);
352 	uint32 nextFrameStartTime = ((Video::VideoDecoder::VideoTrack *)getTrack(0))->getNextFrameStartTime();
353 
354 	if ((_bgSoundStream == NULL) || soundTime < nextFrameStartTime) {
355 
356 		if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) {
357 			while (_mixer->isSoundHandleActive(_bgSound) && soundTime < nextFrameStartTime) {
358 				_vm->_system->delayMillis(10);
359 				soundTime = _mixer->getSoundElapsedTime(_bgSound);
360 			}
361 			// In case the background sound ends prematurely, update
362 			// _ticks so that we can still fall back on the no-sound
363 			// sync case for the subsequent frames.
364 			_ticks = _vm->_system->getMillis();
365 		} else {
366 			_ticks += getTimeToNextFrame();
367 			while (_vm->_system->getMillis() < _ticks)
368 				_vm->_system->delayMillis(10);
369 		}
370 
371 		return true;
372 	}
373 
374 	warning("dropped frame %i", getCurFrame());
375 	return false;
376 }
377 
readSoundData(Common::SeekableReadStream * stream)378 void MoviePlayerDXA::readSoundData(Common::SeekableReadStream *stream) {
379 	uint32 tag = stream->readUint32BE();
380 
381 	if (tag == MKTAG('W','A','V','E')) {
382 		uint32 size = stream->readUint32BE();
383 
384 		if (_sequenceNum) {
385 			Common::File in;
386 
387 			stream->skip(size);
388 
389 			in.open("audio.wav");
390 			if (!in.isOpen()) {
391 				error("Can't read offset file 'audio.wav'");
392 			}
393 
394 			in.seek(_sequenceNum * 8, SEEK_SET);
395 			uint32 offset = in.readUint32LE();
396 			size = in.readUint32LE();
397 
398 			in.seek(offset, SEEK_SET);
399 			_bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES);
400 			in.close();
401 		} else {
402 			_bgSoundStream = Audio::makeWAVStream(stream->readStream(size), DisposeAfterUse::YES);
403 		}
404 	} else {
405 		_bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName);
406 	}
407 }
408 
409 ///////////////////////////////////////////////////////////////////////////////
410 // Movie player for Smacker movies
411 ///////////////////////////////////////////////////////////////////////////////
412 
413 
MoviePlayerSMK(AGOSEngine_Feeble * vm,const char * name)414 MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name)
415 	: MoviePlayer(vm), SmackerDecoder() {
416 	debug(0, "Creating SMK cutscene player");
417 
418 	memset(baseName, 0, sizeof(baseName));
419 	memcpy(baseName, name, strlen(name));
420 }
421 
load()422 bool MoviePlayerSMK::load() {
423 	Common::String videoName = Common::String::format("%s.smk", baseName);
424 
425 	Common::File *videoStream = new Common::File();
426 	if (!videoStream->open(videoName))
427 		error("Failed to load video file %s", videoName.c_str());
428 	if (!loadStream(videoStream))
429 		error("Failed to load video stream from file %s", videoName.c_str());
430 
431 	debug(0, "Playing video %s", videoName.c_str());
432 
433 	CursorMan.showMouse(false);
434 
435 	return true;
436 }
437 
copyFrameToBuffer(byte * dst,uint x,uint y,uint pitch)438 void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
439 	uint h = getHeight();
440 	uint w = getWidth();
441 
442 	const Graphics::Surface *surface = decodeNextFrame();
443 
444 	if (!surface)
445 		return;
446 
447 	const byte *src = (const byte *)surface->getPixels();
448 	dst += y * pitch + x;
449 
450 	do {
451 		memcpy(dst, src, w);
452 		dst += pitch;
453 		src += w;
454 	} while (--h);
455 
456 	if (hasDirtyPalette())
457 		g_system->getPaletteManager()->setPalette(getPalette(), 0, 256);
458 }
459 
playVideo()460 void MoviePlayerSMK::playVideo() {
461 	while (!endOfVideo() && !_skipMovie && !_vm->shouldQuit())
462 		handleNextFrame();
463 }
464 
stopVideo()465 void MoviePlayerSMK::stopVideo() {
466 	close();
467 }
468 
startSound()469 void MoviePlayerSMK::startSound() {
470 	start();
471 }
472 
handleNextFrame()473 void MoviePlayerSMK::handleNextFrame() {
474 	processFrame();
475 
476 	MoviePlayer::handleNextFrame();
477 }
478 
nextFrame()479 void MoviePlayerSMK::nextFrame() {
480 	if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo())
481 		rewind();
482 
483 	if (!endOfVideo()) {
484 		decodeNextFrame();
485 		if (_vm->_interactiveVideo == TYPE_OMNITV) {
486 			copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
487 		} else if (_vm->_interactiveVideo == TYPE_LOOPING) {
488 			copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
489 		}
490 	} else if (_vm->_interactiveVideo == TYPE_OMNITV) {
491 		close();
492 		_vm->_interactiveVideo = 0;
493 		_vm->_variableArray[254] = 6747;
494 	}
495 }
496 
processFrame()497 bool MoviePlayerSMK::processFrame() {
498 	Graphics::Surface *screen = _vm->getBackendSurface();
499 	copyFrameToBuffer((byte *)screen->getPixels(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
500 	_vm->updateBackendSurface();
501 
502 	uint32 waitTime = getTimeToNextFrame();
503 
504 	if (!waitTime && !endOfVideoTracks()) {
505 		warning("dropped frame %i", getCurFrame());
506 		return false;
507 	}
508 
509 	_vm->_system->updateScreen();
510 
511 	// Wait before showing the next frame
512 	_vm->_system->delayMillis(waitTime);
513 	return true;
514 }
515 
516 ///////////////////////////////////////////////////////////////////////////////
517 // Factory function for creating the appropriate cutscene player
518 ///////////////////////////////////////////////////////////////////////////////
519 
makeMoviePlayer(AGOSEngine_Feeble * vm,const char * name)520 MoviePlayer *makeMoviePlayer(AGOSEngine_Feeble *vm, const char *name) {
521 	char baseName[40];
522 	char filename[45];
523 
524 	int baseLen = strlen(name) - 4;
525 	memset(baseName, 0, sizeof(baseName));
526 	memcpy(baseName, name, baseLen);
527 
528 	if (vm->getLanguage() == Common::DE_DEU && baseLen >= 8) {
529 		// Check short filename to work around
530 		// bug in a German Windows 2CD version.
531 		char shortName[10];
532 		memset(shortName, 0, sizeof(shortName));
533 		memcpy(shortName, baseName, 6);
534 
535 		sprintf(filename, "%s~1.dxa", shortName);
536 		if (Common::File::exists(filename)) {
537 			memset(baseName, 0, sizeof(baseName));
538 			memcpy(baseName, filename, 8);
539 		}
540 
541 		sprintf(filename, "%s~1.smk", shortName);
542 		if (Common::File::exists(filename)) {
543 			memset(baseName, 0, sizeof(baseName));
544 			memcpy(baseName, filename, 8);
545 		}
546 	}
547 
548 	sprintf(filename, "%s.dxa", baseName);
549 	if (Common::File::exists(filename)) {
550 		return new MoviePlayerDXA(vm, baseName);
551 	}
552 
553 	sprintf(filename, "%s.smk", baseName);
554 	if (Common::File::exists(filename)) {
555 		return new MoviePlayerSMK(vm, baseName);
556 	}
557 
558 	Common::U32String buf = Common::U32String::format(_("Cutscene file '%s' not found!"), baseName);
559 	GUI::MessageDialog dialog(buf, _("OK"));
560 	dialog.runModal();
561 
562 	return NULL;
563 }
564 
565 } // End of namespace AGOS
566 
567 #endif // ENABLE_AGOS2
568