2007-05-30 21:56:52 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
2004-01-18 05:52:04 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2005-10-18 01:30:26 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2004-01-18 05:52:04 +00:00
|
|
|
*
|
2006-02-11 10:01:01 +00:00
|
|
|
* $URL$
|
|
|
|
* $Id$
|
2004-01-18 05:52:04 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-09-19 08:40:12 +00:00
|
|
|
|
2004-01-18 05:52:04 +00:00
|
|
|
#include "common/file.h"
|
2004-12-05 02:55:06 +00:00
|
|
|
#include "sword1/sword1.h"
|
2004-01-18 05:52:04 +00:00
|
|
|
#include "sword1/animation.h"
|
2007-04-28 07:27:53 +00:00
|
|
|
#include "sword1/text.h"
|
2004-12-05 02:55:06 +00:00
|
|
|
#include "sound/vorbis.h"
|
2004-02-22 14:11:16 +00:00
|
|
|
|
2004-07-11 04:41:48 +00:00
|
|
|
#include "common/config-manager.h"
|
2006-08-26 11:34:35 +00:00
|
|
|
#include "common/endian.h"
|
2004-07-11 04:41:48 +00:00
|
|
|
#include "common/str.h"
|
2007-03-17 00:53:21 +00:00
|
|
|
#include "common/events.h"
|
2005-01-10 22:06:49 +00:00
|
|
|
#include "common/system.h"
|
2009-02-15 13:29:48 +00:00
|
|
|
#include "common/list.h"
|
2008-11-18 17:48:19 +00:00
|
|
|
#include "graphics/surface.h"
|
2004-12-05 02:55:06 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
#include "gui/message.h"
|
|
|
|
|
2004-01-18 05:52:04 +00:00
|
|
|
namespace Sword1 {
|
|
|
|
|
2006-08-26 11:34:35 +00:00
|
|
|
static const char *sequenceList[20] = {
|
|
|
|
"ferrari", // 0 CD2 ferrari running down fitz in sc19
|
|
|
|
"ladder", // 1 CD2 george walking down ladder to dig sc24->sc$
|
|
|
|
"steps", // 2 CD2 george walking down steps sc23->sc24
|
|
|
|
"sewer", // 3 CD1 george entering sewer sc2->sc6
|
|
|
|
"intro", // 4 CD1 intro sequence ->sc1
|
|
|
|
"river", // 5 CD1 george being thrown into river by flap & g$
|
|
|
|
"truck", // 6 CD2 truck arriving at bull's head sc45->sc53/4
|
|
|
|
"grave", // 7 BOTH george's grave in scotland, from sc73 + from sc38 $
|
|
|
|
"montfcon", // 8 CD2 monfaucon clue in ireland dig, sc25
|
|
|
|
"tapestry", // 9 CD2 tapestry room beyond spain well, sc61
|
|
|
|
"ireland", // 10 CD2 ireland establishing shot europe_map->sc19
|
|
|
|
"finale", // 11 CD2 grand finale at very end, from sc73
|
|
|
|
"history", // 12 CD1 George's history lesson from Nico, in sc10
|
|
|
|
"spanish", // 13 CD2 establishing shot for 1st visit to Spain, europe_m$
|
|
|
|
"well", // 14 CD2 first time being lowered down well in Spai$
|
|
|
|
"candle", // 15 CD2 Candle burning down in Spain mausoleum sc59
|
|
|
|
"geodrop", // 16 CD2 from sc54, George jumping down onto truck
|
|
|
|
"vulture", // 17 CD2 from sc54, vultures circling George's dead body
|
|
|
|
"enddemo", // 18 --- for end of single CD demo
|
|
|
|
"credits", // 19 CD2 credits, to follow "finale" sequence
|
|
|
|
};
|
2004-01-18 05:52:04 +00:00
|
|
|
|
2006-08-26 11:34:35 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Basic movie player
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2004-01-18 05:52:04 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType)
|
|
|
|
: _vm(vm), _textMan(textMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system), VideoPlayer(decoder) {
|
2006-08-26 11:34:35 +00:00
|
|
|
_bgSoundStream = NULL;
|
2009-02-15 13:29:48 +00:00
|
|
|
_decoderType = decoderType;
|
2004-03-21 18:49:04 +00:00
|
|
|
}
|
2006-05-17 23:52:45 +00:00
|
|
|
|
2006-08-26 11:34:35 +00:00
|
|
|
MoviePlayer::~MoviePlayer(void) {
|
2009-02-20 23:47:11 +00:00
|
|
|
delete _decoder;
|
2009-02-21 11:27:04 +00:00
|
|
|
delete _bgSoundHandle;
|
2004-03-21 18:49:04 +00:00
|
|
|
}
|
|
|
|
|
2004-01-18 05:52:04 +00:00
|
|
|
/**
|
|
|
|
* Plays an animated cutscene.
|
2004-12-05 02:55:06 +00:00
|
|
|
* @param id the id of the file
|
2004-01-18 05:52:04 +00:00
|
|
|
*/
|
2006-08-26 11:34:35 +00:00
|
|
|
bool MoviePlayer::load(uint32 id) {
|
2007-04-28 07:27:53 +00:00
|
|
|
Common::File f;
|
2009-02-21 11:22:56 +00:00
|
|
|
char filename[20];
|
2007-04-28 07:27:53 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
if (_decoderType == kVideoDecoderDXA) {
|
|
|
|
_bgSoundStream = Audio::AudioStream::openStreamFile(sequenceList[id]);
|
|
|
|
} else {
|
|
|
|
_bgSoundStream = NULL;
|
|
|
|
}
|
2007-04-28 07:27:53 +00:00
|
|
|
|
|
|
|
if (SwordEngine::_systemVars.showText) {
|
2009-02-21 11:22:56 +00:00
|
|
|
sprintf(filename, "%s.txt", sequenceList[id]);
|
|
|
|
if (f.open(filename)) {
|
2008-12-01 21:13:02 +00:00
|
|
|
Common::String line;
|
2007-04-28 07:27:53 +00:00
|
|
|
int lineNo = 0;
|
|
|
|
int lastEnd = -1;
|
|
|
|
|
|
|
|
_movieTexts.clear();
|
2008-12-01 21:13:02 +00:00
|
|
|
while (!f.eos() && !f.err()) {
|
|
|
|
line = f.readLine();
|
2007-04-28 07:27:53 +00:00
|
|
|
lineNo++;
|
2008-12-01 21:13:02 +00:00
|
|
|
if (line.empty() || line[0] == '#') {
|
2007-04-28 07:27:53 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-12-01 21:13:02 +00:00
|
|
|
const char *ptr = line.c_str();
|
2007-04-28 07:27:53 +00:00
|
|
|
|
|
|
|
// TODO: Better error handling
|
2008-12-01 21:35:17 +00:00
|
|
|
int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
|
|
|
|
int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
|
2007-04-28 07:27:53 +00:00
|
|
|
|
|
|
|
while (*ptr && isspace(*ptr))
|
|
|
|
ptr++;
|
|
|
|
|
|
|
|
if (startFrame > endFrame) {
|
2009-02-21 11:22:56 +00:00
|
|
|
warning("%s:%d: startFrame (%d) > endFrame (%d)", filename, lineNo, startFrame, endFrame);
|
2007-04-28 07:27:53 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (startFrame <= lastEnd) {
|
2009-02-21 11:22:56 +00:00
|
|
|
warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename, lineNo, startFrame, lastEnd);
|
2007-04-28 07:27:53 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
_movieTexts.push_back(new MovieText(startFrame, endFrame, ptr));
|
|
|
|
lastEnd = endFrame;
|
|
|
|
}
|
2009-02-21 11:22:56 +00:00
|
|
|
f.close();
|
2007-04-28 07:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
switch (_decoderType) {
|
|
|
|
case kVideoDecoderDXA:
|
|
|
|
snprintf(filename, sizeof(filename), "%s.dxa", sequenceList[id]);
|
|
|
|
break;
|
|
|
|
case kVideoDecoderSMK:
|
|
|
|
snprintf(filename, sizeof(filename), "%s.smk", sequenceList[id]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_decoder->loadFile(filename)) {
|
2009-02-15 13:43:44 +00:00
|
|
|
// The DXA animations in the Broken Sword games always use external audio tracks.
|
|
|
|
if (_decoderType == kVideoDecoderDXA && _decoder->readSoundHeader() != MKID_BE('NULL'))
|
2009-02-15 13:29:48 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return false;
|
2004-12-05 02:55:06 +00:00
|
|
|
}
|
2009-02-15 13:29:48 +00:00
|
|
|
|
2006-08-26 11:34:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MoviePlayer::play(void) {
|
|
|
|
if (_bgSoundStream) {
|
2009-02-15 13:29:48 +00:00
|
|
|
_snd->playInputStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream);
|
2006-08-26 11:34:35 +00:00
|
|
|
}
|
2006-08-28 04:53:21 +00:00
|
|
|
bool terminated = false;
|
2009-02-15 13:29:48 +00:00
|
|
|
|
|
|
|
Common::List<Common::Event> stopEvents;
|
|
|
|
Common::Event stopEvent;
|
|
|
|
stopEvents.clear();
|
|
|
|
stopEvent.type = Common::EVENT_KEYDOWN;
|
|
|
|
stopEvent.kbd = Common::KEYCODE_ESCAPE;
|
|
|
|
stopEvents.push_back(stopEvent);
|
|
|
|
|
|
|
|
terminated = !playVideo(&stopEvents);
|
2007-04-28 07:27:53 +00:00
|
|
|
|
2008-09-27 17:51:22 +00:00
|
|
|
if (terminated)
|
2009-02-15 13:29:48 +00:00
|
|
|
_snd->stopHandle(*_bgSoundHandle);
|
2008-09-27 17:47:22 +00:00
|
|
|
|
2008-11-18 17:48:19 +00:00
|
|
|
_textMan->releaseText(2, false);
|
2007-04-28 07:27:53 +00:00
|
|
|
|
2008-10-13 18:41:12 +00:00
|
|
|
while (!_movieTexts.empty())
|
|
|
|
delete _movieTexts.remove_at(_movieTexts.size() - 1);
|
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
while (_snd->isSoundHandleActive(*_bgSoundHandle))
|
2007-04-01 18:05:11 +00:00
|
|
|
_system->delayMillis(100);
|
2004-12-05 02:55:06 +00:00
|
|
|
|
2006-08-26 11:34:35 +00:00
|
|
|
// It's tempting to call _screen->fullRefresh() here to restore the old
|
2009-02-16 06:02:21 +00:00
|
|
|
// palette. However, that causes glitches with DXA movies, where the
|
2006-08-26 11:34:35 +00:00
|
|
|
// previous location would be momentarily drawn, before switching to
|
|
|
|
// the new one. Work around this by setting the palette to black.
|
2004-01-18 05:52:04 +00:00
|
|
|
|
2009-02-16 06:02:21 +00:00
|
|
|
byte pal[4 * 256];
|
2006-08-26 11:34:35 +00:00
|
|
|
memset(pal, 0, sizeof(pal));
|
2009-02-16 06:02:21 +00:00
|
|
|
_system->setPalette(pal, 0, 256);
|
2004-12-05 02:55:06 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
void MoviePlayer::performPostProcessing(byte *screen) {
|
|
|
|
if (!_movieTexts.empty()) {
|
|
|
|
if (_decoder->getCurFrame() == _movieTexts[0]->_startFrame) {
|
|
|
|
_textMan->makeTextSprite(2, (uint8 *)_movieTexts[0]->_text, 600, LETTER_COL);
|
2004-12-05 02:55:06 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
FrameHeader *frame = _textMan->giveSpriteData(2);
|
|
|
|
_textWidth = frame->width;
|
|
|
|
_textHeight = frame->height;
|
|
|
|
_textX = 320 - _textWidth / 2;
|
|
|
|
_textY = 420 - _textHeight;
|
|
|
|
}
|
|
|
|
if (_decoder->getCurFrame() == _movieTexts[0]->_endFrame) {
|
|
|
|
_textMan->releaseText(2, false);
|
|
|
|
delete _movieTexts.remove_at(0);
|
2004-12-05 02:55:06 +00:00
|
|
|
}
|
|
|
|
}
|
2007-04-28 07:27:53 +00:00
|
|
|
|
2008-11-18 17:48:19 +00:00
|
|
|
if (_textMan->giveSpriteData(2)) {
|
2007-04-28 07:27:53 +00:00
|
|
|
byte *src = (byte *)_textMan->giveSpriteData(2) + sizeof(FrameHeader);
|
2009-02-15 13:29:48 +00:00
|
|
|
byte *dst = screen + _textY * _decoder->getWidth() + _textX * 1;
|
2007-04-28 07:27:53 +00:00
|
|
|
|
2008-11-18 17:48:19 +00:00
|
|
|
for (int y = 0; y < _textHeight; y++) {
|
|
|
|
for (int x = 0; x < _textWidth; x++) {
|
2007-04-28 07:27:53 +00:00
|
|
|
switch (src[x]) {
|
|
|
|
case BORDER_COL:
|
2009-02-15 13:29:48 +00:00
|
|
|
dst[x] = _decoder->getBlack();
|
2007-04-28 07:27:53 +00:00
|
|
|
break;
|
|
|
|
case LETTER_COL:
|
2009-02-15 13:29:48 +00:00
|
|
|
dst[x] = _decoder->getWhite();
|
2007-04-28 07:27:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += _textWidth;
|
2009-02-15 13:29:48 +00:00
|
|
|
dst += _decoder->getWidth();
|
2007-04-28 07:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
2006-08-26 11:34:35 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
DXAPlayerWithSound::DXAPlayerWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
|
|
|
|
: _mixer(mixer), _bgSoundHandle(bgSoundHandle) {
|
2006-08-26 11:34:35 +00:00
|
|
|
}
|
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
int32 DXAPlayerWithSound::getAudioLag() {
|
|
|
|
if (!_fileStream)
|
|
|
|
return 0;
|
2006-08-26 11:34:35 +00:00
|
|
|
|
2009-02-16 21:57:22 +00:00
|
|
|
if (!_mixer->isSoundHandleActive(*_bgSoundHandle))
|
|
|
|
return 0;
|
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
int32 frameDelay = getFrameDelay();
|
|
|
|
int32 videoTime = _videoInfo.currentFrame * frameDelay;
|
|
|
|
int32 audioTime;
|
2006-08-26 11:34:35 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
audioTime = (((int32) _mixer->getSoundElapsedTime(*_bgSoundHandle)) * 100);
|
2006-08-26 11:34:35 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
return videoTime - audioTime;
|
2006-08-26 11:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Factory function for creating the appropriate cutscene player
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system) {
|
2006-08-26 11:34:35 +00:00
|
|
|
char filename[20];
|
2009-02-15 13:29:48 +00:00
|
|
|
char buf[60];
|
2009-02-21 11:27:04 +00:00
|
|
|
Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle;
|
2006-08-26 11:34:35 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
snprintf(filename, sizeof(filename), "%s.smk", sequenceList[id]);
|
2006-08-26 11:34:35 +00:00
|
|
|
|
|
|
|
if (Common::File::exists(filename)) {
|
2009-02-15 13:29:48 +00:00
|
|
|
Graphics::SMKPlayer *smkDecoder = new Graphics::SMKPlayer(snd);
|
2009-02-21 11:27:04 +00:00
|
|
|
return new MoviePlayer(vm, textMan, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK);
|
2006-08-26 11:34:35 +00:00
|
|
|
}
|
2009-02-15 13:29:48 +00:00
|
|
|
|
|
|
|
snprintf(filename, sizeof(filename), "%s.dxa", sequenceList[id]);
|
|
|
|
|
|
|
|
if (Common::File::exists(filename)) {
|
|
|
|
#ifdef USE_ZLIB
|
2009-02-21 11:27:04 +00:00
|
|
|
DXAPlayerWithSound *dxaDecoder = new DXAPlayerWithSound(snd, bgSoundHandle);
|
|
|
|
return new MoviePlayer(vm, textMan, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA);
|
2009-02-15 13:29:48 +00:00
|
|
|
#else
|
2009-03-07 19:24:31 +00:00
|
|
|
GUI::MessageDialog dialog("DXA cutscenes found but ScummVM has been built without zlib support", "OK");
|
|
|
|
dialog.runModal();
|
2009-02-15 13:29:48 +00:00
|
|
|
return NULL;
|
2006-08-26 11:34:35 +00:00
|
|
|
#endif
|
2009-02-15 13:29:48 +00:00
|
|
|
}
|
2006-08-26 11:34:35 +00:00
|
|
|
|
2009-02-15 13:29:48 +00:00
|
|
|
// Old MPEG2 cutscenes
|
2006-08-26 11:34:35 +00:00
|
|
|
snprintf(filename, sizeof(filename), "%s.mp2", sequenceList[id]);
|
|
|
|
|
|
|
|
if (Common::File::exists(filename)) {
|
2009-03-07 19:24:31 +00:00
|
|
|
GUI::MessageDialog dialog("MPEG2 cutscenes are no longer supported", "OK");
|
|
|
|
dialog.runModal();
|
2009-02-15 13:29:48 +00:00
|
|
|
return NULL;
|
2006-08-26 11:34:35 +00:00
|
|
|
}
|
2009-02-15 13:29:48 +00:00
|
|
|
|
|
|
|
sprintf(buf, "Cutscene '%s' not found", sequenceList[id]);
|
2009-03-07 19:24:31 +00:00
|
|
|
GUI::MessageDialog dialog(buf, "OK");
|
|
|
|
dialog.runModal();
|
2006-08-26 11:34:35 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-12-05 02:55:06 +00:00
|
|
|
|
2004-07-11 04:41:48 +00:00
|
|
|
} // End of namespace Sword1
|