diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/sword2/animation.cpp | 714 | ||||
-rw-r--r-- | engines/sword2/animation.h | 162 | ||||
-rw-r--r-- | engines/sword2/function.cpp | 11 | ||||
-rw-r--r-- | engines/sword2/logic.h | 2 | ||||
-rw-r--r-- | engines/sword2/sound.cpp | 9 | ||||
-rw-r--r-- | engines/sword2/sound.h | 1 |
6 files changed, 249 insertions, 650 deletions
diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index a6925e9ade..c35be49865 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -25,20 +25,14 @@ * $Id$ */ - -#include "common/config-manager.h" #include "common/file.h" -#include "common/events.h" #include "common/system.h" #include "sword2/sword2.h" #include "sword2/defs.h" #include "sword2/header.h" -#include "sword2/logic.h" #include "sword2/maketext.h" -#include "sword2/mouse.h" #include "sword2/resman.h" -#include "sword2/screen.h" #include "sword2/sound.h" #include "sword2/animation.h" @@ -50,7 +44,7 @@ namespace Sword2 { // Basic movie player /////////////////////////////////////////////////////////////////////////////// -const MovieInfo MoviePlayer::_movies[19] = { +static const MovieInfo sequenceList[19] = { { "carib", 222, false }, { "escape", 187, false }, { "eye", 248, false }, @@ -72,645 +66,299 @@ const MovieInfo MoviePlayer::_movies[19] = { { "enddemo", 110, false } }; -MoviePlayer::MoviePlayer(Sword2Engine *vm, const char *name) { - _vm = vm; - _name = strdup(name); - _mixer = _vm->_mixer; - _system = _vm->_system; - _pauseTicks = 0; - _textSurface = NULL; +MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType) + : _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system), VideoPlayer(decoder) { _bgSoundStream = NULL; - _ticks = 0; - _currentFrame = 0; - _frameBuffer = NULL; - _frameWidth = 0; - _frameHeight = 0; - _frameX = 0; - _frameY = 0; - _black = 1; - _white = 255; - _numFrames = 0; - _leadOutFrame = (uint)-1; - _seamless = false; - _framesSkipped = 0; - _currentText = 0; -} - -MoviePlayer::~MoviePlayer() { - free(_name); + _decoderType = decoderType; } -uint32 MoviePlayer::getTick() { - return _system->getMillis() - _pauseTicks; +MoviePlayer:: ~MoviePlayer(void) { + delete _bgSoundHandle; + delete _decoder; } -void MoviePlayer::savePalette() { - memcpy(_originalPalette, _vm->_screen->getPalette(), sizeof(_originalPalette)); -} +/** + * Plays an animated cutscene. + * @param id the id of the file + */ +bool MoviePlayer::load(const char *name) { + _id = -1; -void MoviePlayer::restorePalette() { - _vm->_screen->setPalette(0, 256, _originalPalette, RDPAL_INSTANT); -} + for (int i = 0; i < ARRAYSIZE(sequenceList); i++) { + if (scumm_stricmp(name, sequenceList[i].name) == 0) { + _id = i; + break; + } + } -void MoviePlayer::clearFrame() { - memset(_frameBuffer, 0, _vm->_screen->getScreenWide() * _vm->_screen->getScreenDeep()); -} + if (_decoderType == kVideoDecoderDXA) { + _bgSoundStream = Audio::AudioStream::openStreamFile(name); + } else { + _bgSoundStream = NULL; + } -void MoviePlayer::updateScreen() { - _system->updateScreen(); -} + _textSurface = NULL; -bool MoviePlayer::checkSkipFrame() { - if (_framesSkipped > 10) { - warning("Forced frame %d to be displayed", _currentFrame); - _framesSkipped = 0; - return false; + char filename[20]; + switch (_decoderType) { + case kVideoDecoderDXA: + snprintf(filename, sizeof(filename), "%s.dxa", name); + break; + case kVideoDecoderSMK: + snprintf(filename, sizeof(filename), "%s.smk", name); + break; } - if (_bgSoundStream) { - if ((_mixer->getSoundElapsedTime(_bgSoundHandle) * 12) / 1000 < _currentFrame + 1) + if (_decoder->loadFile(filename)) { + // The DXA animations in the Broken Sword games always use external audio tracks, + // if they have any sound at all. + if (_decoderType == kVideoDecoderDXA && _decoder->readSoundHeader() != MKID_BE('NULL')) return false; } else { - if (getTick() <= _ticks) - return false; + return false; } - _framesSkipped++; return true; } -bool MoviePlayer::syncFrame() { - _ticks += 83; +void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut) { + // This happens when quitting during the "eye" cutscene. + if (_vm->shouldQuit()) + return; + + bool seamless = false; - if (checkSkipFrame()) { - warning("Skipped frame %d", _currentFrame); - return false; + if (_id >= 0) { + seamless = sequenceList[_id].seamless; + _numFrames = sequenceList[_id].frames; + if (_numFrames > 60) + _leadOutFrame = _numFrames - 60; } - if (_bgSoundStream) { - while (_mixer->isSoundHandleActive(_bgSoundHandle) && (_mixer->getSoundElapsedTime(_bgSoundHandle) * 12) / 1000 < _currentFrame) { - _system->delayMillis(10); - } + _movieTexts = movieTexts; + _numMovieTexts = numMovieTexts; + _currentMovieText = 0; + _leadOut = leadOut; - // In case the background sound ends prematurely, update _ticks - // so that we can still fall back on the no-sound sync case for - // the subsequent frames. + if (leadIn) { + _vm->_sound->playMovieSound(leadIn, kLeadInSound); + } - _ticks = getTick(); - } else { - while (getTick() < _ticks) { - _system->delayMillis(10); - } + if (_bgSoundStream) { + _snd->playInputStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); } - return true; -} + bool terminated = false; + + 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); -void MoviePlayer::drawFrame() { - int screenWidth = _vm->_screen->getScreenWide(); + closeTextObject(_currentMovieText); - _system->copyRectToScreen(_frameBuffer + _frameY * screenWidth + _frameX, screenWidth, _frameX, _frameY, _frameWidth, _frameHeight); + if (terminated) { + _snd->stopHandle(*_bgSoundHandle); + _vm->_sound->stopMovieSounds(); + _vm->_sound->stopSpeech(); + } + + while (_snd->isSoundHandleActive(*_bgSoundHandle)) + _system->delayMillis(100); } -void MoviePlayer::openTextObject(SequenceTextInfo *t) { +void MoviePlayer::openTextObject(uint32 index) { + MovieText *text = &_movieTexts[index]; + // Pull out the text line to get the official text number (for WAV id) - uint32 res = t->textNumber / SIZE; - uint32 localText = t->textNumber & 0xffff; + uint32 res = text->_textNumber / SIZE; + uint32 localText = text->_textNumber & 0xffff; // Open text resource and get the line - byte *text = _vm->fetchTextLine(_vm->_resman->openResource(res), localText); + byte *textData = _vm->fetchTextLine(_vm->_resman->openResource(res), localText); - _textObject.speechId = READ_LE_UINT16(text); + text->_speechId = READ_LE_UINT16(textData); // Is it speech or subtitles, or both? // If we want subtitles, or there was no sound - if (_vm->getSubtitles() || !_textObject.speechId) { - _textObject.textMem = _vm->_fontRenderer->makeTextSprite(text + 2, 600, 255, _vm->_speechFontId, 1); + if (_vm->getSubtitles() || !text->_speechId) { + text->_textMem = _vm->_fontRenderer->makeTextSprite(textData + 2, 600, 255, _vm->_speechFontId, 1); } _vm->_resman->closeResource(res); - if (_textObject.textMem) { + if (text->_textMem) { FrameHeader frame; - frame.read(_textObject.textMem); + frame.read(text->_textMem); - _textObject.textSprite.x = 320 - frame.width / 2; - _textObject.textSprite.y = 440 - frame.height; - _textObject.textSprite.w = frame.width; - _textObject.textSprite.h = frame.height; - _textObject.textSprite.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION; - _textObject.textSprite.data = _textObject.textMem + FrameHeader::size(); - _vm->_screen->createSurface(&_textObject.textSprite, &_textSurface); + text->_textSprite.x = 320 - frame.width / 2; + text->_textSprite.y = 440 - frame.height; + text->_textSprite.w = frame.width; + text->_textSprite.h = frame.height; + text->_textSprite.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION; + text->_textSprite.data = text->_textMem + FrameHeader::size(); + _vm->_screen->createSurface(&text->_textSprite, &_textSurface); + + _textX = 320 - text->_textSprite.w / 2; + _textY = 420 - text->_textSprite.h; } } -void MoviePlayer::closeTextObject() { - free(_textObject.textMem); - _textObject.textMem = NULL; +void MoviePlayer::closeTextObject(uint32 index) { + if (index < _numMovieTexts) { + MovieText *text = &_movieTexts[index]; - _textObject.speechId = 0; + free(text->_textMem); + text->_textMem = NULL; - if (_textSurface) { - _vm->_screen->deleteSurface(_textSurface); - _textSurface = NULL; + if (_textSurface) { + _vm->_screen->deleteSurface(_textSurface); + _textSurface = NULL; + } } } -void MoviePlayer::calcTextPosition(int &xPos, int &yPos) { - xPos = 320 - _textObject.textSprite.w / 2; - yPos = 420 - _textObject.textSprite.h; -} +void MoviePlayer::drawTextObject(uint32 index, byte *screen) { + MovieText *text = &_movieTexts[index]; -void MoviePlayer::drawTextObject() { - if (_textObject.textMem && _textSurface) { - int screenWidth = _vm->_screen->getScreenWide(); - byte *src = _textObject.textSprite.data; - uint16 width = _textObject.textSprite.w; - uint16 height = _textObject.textSprite.h; - int xPos, yPos; + byte white = _decoder->getWhite(); + byte black = _decoder->getBlack(); - calcTextPosition(xPos, yPos); + if (text->_textMem && _textSurface) { + byte *src = text->_textSprite.data; + uint16 width = text->_textSprite.w; + uint16 height = text->_textSprite.h; - byte *dst = _frameBuffer + yPos * screenWidth + xPos; + byte *dst = screen + _textY * _decoder->getWidth() + _textX; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (src[x] == 1) - dst[x] = _black; + dst[x] = black; else if (src[x] == 255) - dst[x] = _white; + dst[x] = white; } src += width; - dst += screenWidth; - } - - if (yPos + height > _frameY + _frameHeight || width > _frameWidth) { - _system->copyRectToScreen(_frameBuffer + yPos * screenWidth + xPos, screenWidth, xPos, yPos, width, height); - } - } -} - -void MoviePlayer::undrawTextObject() { - if (_textObject.textMem) { - int xPos, yPos; - - calcTextPosition(xPos, yPos); - uint16 width = _textObject.textSprite.w; - uint16 height = _textObject.textSprite.h; - - // We only need to undraw the text if it's outside the frame. - // Otherwise the next frame will cover the old text anyway. - - if (yPos + height > _frameY + _frameHeight || width > _frameWidth) { - int screenWidth = _vm->_screen->getScreenWide(); - byte *dst = _frameBuffer + yPos * screenWidth + xPos; - - for (int y = 0; y < height; y++) { - memset(dst, 0, width); - dst += screenWidth; - } - - _system->copyRectToScreen(_frameBuffer + yPos * screenWidth + xPos, screenWidth, xPos, yPos, width, height); - } - } -} - -bool MoviePlayer::load() { - _bgSoundStream = NULL; - _currentText = 0; - _currentFrame = 0; - - for (int i = 0; i < ARRAYSIZE(_movies); i++) { - if (scumm_stricmp(_name, _movies[i].name) == 0) { - _seamless = _movies[i].seamless; - _numFrames = _movies[i].frames; - if (_numFrames > 60) - _leadOutFrame = _numFrames - 60; - - // Not all cutscenes cover the entire screen, so clear - // it. We will always clear the game screen, no matter - // how the cutscene is to be displayed. (We have to do - // this before showing the overlay.) - - _vm->_mouse->closeMenuImmediately(); - - if (!_seamless) { - _vm->_screen->clearScene(); - } - - _vm->_screen->updateDisplay(); - return true; + dst += _decoder->getWidth(); } } - - return false; } -bool MoviePlayer::userInterrupt() { - Common::Event event; - bool terminate = false; - - Common::EventManager *eventMan = _system->getEventManager(); - while (eventMan->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_SCREEN_CHANGED: - handleScreenChanged(); - break; - case Common::EVENT_RTL: - case Common::EVENT_QUIT: - terminate = true; - break; - case Common::EVENT_KEYDOWN: - if (event.kbd.keycode == Common::KEYCODE_ESCAPE) - terminate = true; - break; - default: - break; - } - } - - return terminate; -} - -void MoviePlayer::play(SequenceTextInfo *textList, uint32 numLines, int32 leadIn, int32 leadOut) { - bool terminate = false; - bool textVisible = false; - bool startNextText = false; - - // This happens if the user quits during the "eye" cutscene. - if (_vm->shouldQuit()) - return; - - _numSpeechLines = numLines; - _firstSpeechFrame = (numLines > 0) ? textList[0].startFrame : 0; - - if (leadIn) { - _vm->_sound->playMovieSound(leadIn, kLeadInSound); - } - - savePalette(); +// FIXME: This assumes that the subtitles always fit within the frame of the +// movie. In Broken Sword 2, that's a fairly safe assumption, but not +// necessarily in all other games. - _framesSkipped = 0; - _ticks = getTick(); - _bgSoundStream = Audio::AudioStream::openStreamFile(_name); +void MoviePlayer::performPostProcessing(byte *screen) { + MovieText *text; + int frame = _decoder->getCurFrame(); - if (_bgSoundStream) { - _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_bgSoundHandle, _bgSoundStream); - } - - while (!terminate && _currentFrame < _numFrames && decodeFrame()) { - _currentFrame++; - - // The frame has been decoded. Now draw the subtitles, if any, - // before drawing it to the screen. - - if (_currentText < numLines) { - SequenceTextInfo *t = &textList[_currentText]; - - if (_currentFrame == t->startFrame) { - openTextObject(t); - textVisible = true; - - if (_textObject.speechId) { - startNextText = true; - } - } - - if (startNextText && _vm->_sound->amISpeaking() == RDSE_QUIET) { - _vm->_sound->playCompSpeech(_textObject.speechId, 16, 0); - startNextText = false; - } - - if (_currentFrame == t->endFrame) { - undrawTextObject(); - closeTextObject(); - _currentText++; - textVisible = false; - } - - if (textVisible) - drawTextObject(); - } - - if (leadOut && _currentFrame == _leadOutFrame) { - _vm->_sound->playMovieSound(leadOut, kLeadOutSound); - } - - if (syncFrame()) { - drawFrame(); - updateScreen(); - } - - if (userInterrupt()) { - terminate = true; - } + if (_currentMovieText < _numMovieTexts) { + text = &_movieTexts[_currentMovieText]; + } else { + text = NULL; } - if (!_seamless) { - // Most cutscenes fade to black on their own, but not all of - // them. I think it looks better if they do. - - clearFrame(); - - // If the sound is still playing, draw the subtitles one final - // time. This happens in the "carib" cutscene. - - if (textVisible && _vm->_sound->amISpeaking() == RDSE_SPEAKING) { - drawTextObject(); + if (text && frame == text->_startFrame) { + if ((_vm->getSubtitles() || !text->_speechId) && _currentMovieText < _numMovieTexts) { + openTextObject(_currentMovieText); } - - drawFrame(); - updateScreen(); } - if (!terminate) { - // Wait for the voice and sound track to stop playing. This is - // to make sure that we don't cut off the speech in - // mid-sentence, and - even more importantly - that we don't - // free the sound buffer while it's still in use. - - while (_vm->_sound->amISpeaking() == RDSE_SPEAKING || _mixer->isSoundHandleActive(_bgSoundHandle)) { - if (userInterrupt()) { - terminate = true; - _vm->_sound->stopSpeech(); - _mixer->stopHandle(_bgSoundHandle); - } - _system->delayMillis(100); + if (text && frame >= text->_startFrame) { + if (text->_speechId && !text->_played && _vm->_sound->amISpeaking() == RDSE_QUIET) { + text->_played = true; + _vm->_sound->playCompSpeech(text->_speechId, 16, 0); } - } else { - _vm->_sound->stopSpeech(); - _mixer->stopHandle(_bgSoundHandle); - } - - // The current text object may still be open - undrawTextObject(); - closeTextObject(); - - if (!_seamless) { - clearFrame(); - drawFrame(); - updateScreen(); - } - - // Setting the palette implies a full redraw. - restorePalette(); -} - -void MoviePlayer::pauseMovie(bool pause) { - _mixer->pauseHandle(_bgSoundHandle, pause); - - if (pause) { - _pauseStartTick = _system->getMillis(); - } else { - _pauseTicks += (_system->getMillis() - _pauseStartTick); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Movie player for the original SMK movies -/////////////////////////////////////////////////////////////////////////////// - -MoviePlayerSMK::MoviePlayerSMK(Sword2Engine *vm, const char *name) - : MoviePlayer(vm, name), SMKPlayer(vm->_mixer) { - debug(0, "Creating SMK cutscene player"); -} - -MoviePlayerSMK::~MoviePlayerSMK() { - closeFile(); -} - -bool MoviePlayerSMK::decodeFrame() { - decodeNextFrame(); - copyFrameToBuffer(_frameBuffer, _frameX, _frameY, _vm->_screen->getScreenWide()); - return true; -} - -bool MoviePlayerSMK::load() { - if (!MoviePlayer::load()) - return false; - - char filename[20]; - - snprintf(filename, sizeof(filename), "%s.smk", _name); - - if (loadFile(filename)) { - _frameBuffer = _vm->_screen->getScreen(); - - _frameWidth = getWidth(); - _frameHeight = getHeight(); - - _frameX = (_vm->_screen->getScreenWide() - _frameWidth) / 2; - _frameY = (_vm->_screen->getScreenDeep() - _frameHeight) / 2; - - return true; - } - - return false; -} - -#ifdef USE_ZLIB - -/////////////////////////////////////////////////////////////////////////////// -// Movie player for the new DXA movies -/////////////////////////////////////////////////////////////////////////////// - -MoviePlayerDXA::MoviePlayerDXA(Sword2Engine *vm, const char *name) - : MoviePlayer(vm, name) { - debug(0, "Creating DXA cutscene player"); -} - -MoviePlayerDXA::~MoviePlayerDXA() { - closeFile(); -} - -bool MoviePlayerDXA::decodeFrame() { - decodeNextFrame(); - copyFrameToBuffer(_frameBuffer, _frameX, _frameY, _vm->_screen->getScreenWide()); - return true; -} - -bool MoviePlayerDXA::load() { - if (!MoviePlayer::load()) - return false; - - char filename[20]; - - snprintf(filename, sizeof(filename), "%s.dxa", _name); - - if (loadFile(filename)) { - // The Broken Sword games always use external audio tracks. - if (_fileStream->readUint32BE() != MKID_BE('NULL')) - return false; - - _frameBuffer = _vm->_screen->getScreen(); - - _frameWidth = getWidth(); - _frameHeight = getHeight(); - - _frameX = (_vm->_screen->getScreenWide() - _frameWidth) / 2; - _frameY = (_vm->_screen->getScreenDeep() - _frameHeight) / 2; - - return true; - } - - return false; -} - -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Dummy player for subtitled speech only -/////////////////////////////////////////////////////////////////////////////// - -MoviePlayerDummy::MoviePlayerDummy(Sword2Engine *vm, const char *name) - : MoviePlayer(vm, name) { - debug(0, "Creating Dummy cutscene player"); -} - -MoviePlayerDummy::~MoviePlayerDummy() { -} - -bool MoviePlayerDummy::load() { - if (!MoviePlayer::load()) - return false; - - _frameBuffer = _vm->_screen->getScreen(); - - _frameWidth = 640; - _frameHeight = 400; - _frameX = 0; - _frameY = 40; - - return true; -} - -bool MoviePlayerDummy::decodeFrame() { - if ((_currentFrame == 0 && _numSpeechLines > 0) || _mixer->isSoundHandleActive(_bgSoundHandle)) { - byte dummyPalette[] = { - 0, 0, 0, 0, - 255, 255, 255, 0, - }; - - // 0 is always black - // 1 is the border colour - black - // 255 is the pen colour - white - - _system->setPalette(dummyPalette, 0, 1); - _system->setPalette(dummyPalette, 1, 1); - _system->setPalette(dummyPalette + 4, 255, 1); - - byte msgNoCutscenesRU[] = "Po\344uk - to\344\345ko pev\345: hagmute k\344abuwy Ucke\343n, u\344u nocetute ca\343t npoekta u ckava\343te budeo po\344uku"; - -#if defined(USE_ZLIB) - byte msgNoCutscenes[] = "Cutscene - Narration Only: Press ESC to exit, or visit www.scummvm.org to download cutscene videos"; -#else - byte msgNoCutscenes[] = "Cutscene - Narration Only: Press ESC to exit, or recompile ScummVM with ZLib support"; -#endif - - byte *msg; - - // Russian version substituted latin characters with Cyrillic. - if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) { - msg = msgNoCutscenesRU; + if (frame <= text->_endFrame) { + drawTextObject(_currentMovieText, screen); } else { - msg = msgNoCutscenes; + _currentMovieText++; } - - byte *data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId); - - FrameHeader frame_head; - SpriteInfo msgSprite; - byte *msgSurface; - - frame_head.read(data); - - msgSprite.x = _vm->_screen->getScreenWide() / 2 - frame_head.width / 2; - msgSprite.y = (480 - frame_head.height) / 2; - msgSprite.w = frame_head.width; - msgSprite.h = frame_head.height; - msgSprite.type = RDSPR_NOCOMPRESSION; - msgSprite.data = data + FrameHeader::size(); - - _vm->_screen->createSurface(&msgSprite, &msgSurface); - _vm->_screen->drawSurface(&msgSprite, msgSurface); - _vm->_screen->deleteSurface(msgSurface); - - free(data); - updateScreen(); } - // If we have played the final voice-over, skip ahead to the lead out - - if (!_mixer->isSoundHandleActive(_bgSoundHandle) && - _currentText >= _numSpeechLines && - _vm->_sound->amISpeaking() == RDSE_QUIET && - _leadOutFrame != (uint)-1 && - _currentFrame < _leadOutFrame) { - _currentFrame = _leadOutFrame - 1; + if (_leadOut && _decoder->getCurFrame() == _leadOutFrame) { + _vm->_sound->playMovieSound(_leadOut, kLeadOutSound); } +} - return true; +DXAPlayerWithSound::DXAPlayerWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) + : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { } -bool MoviePlayerDummy::syncFrame() { - if ((_numSpeechLines == 0 || _currentFrame < _firstSpeechFrame) && !_mixer->isSoundHandleActive(_bgSoundHandle)) { - _ticks = getTick(); - return false; - } +int32 DXAPlayerWithSound::getAudioLag() { + if (!_fileStream) + return 0; - return MoviePlayer::syncFrame(); -} + if (!_mixer->isSoundHandleActive(*_bgSoundHandle)) + return 0; -void MoviePlayerDummy::drawFrame() { -} + int32 frameDelay = getFrameDelay(); + int32 videoTime = _videoInfo.currentFrame * frameDelay; + int32 audioTime; -void MoviePlayerDummy::drawTextObject() { - if (_textObject.textMem && _textSurface) { - _vm->_screen->drawSurface(&_textObject.textSprite, _textSurface); - } -} + audioTime = (((int32) _mixer->getSoundElapsedTime(*_bgSoundHandle)) * 100); -void MoviePlayerDummy::undrawTextObject() { - if (_textObject.textMem && _textSurface) { - memset(_textSurface, 1, _textObject.textSprite.w * _textObject.textSprite.h); - drawTextObject(); - } + return videoTime - audioTime; } /////////////////////////////////////////////////////////////////////////////// // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(Sword2Engine *vm, const char *name) { - static char filename[20]; +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system) { + char filename[20]; + char buf[60]; + Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; snprintf(filename, sizeof(filename), "%s.smk", name); if (Common::File::exists(filename)) { - return new MoviePlayerSMK(vm, name); + Graphics::SMKPlayer *smkDecoder = new Graphics::SMKPlayer(snd); + return new MoviePlayer(vm, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK); } -#ifdef USE_ZLIB snprintf(filename, sizeof(filename), "%s.dxa", name); if (Common::File::exists(filename)) { - return new MoviePlayerDXA(vm, name); - } +#ifdef USE_ZLIB + DXAPlayerWithSound *dxaDecoder = new DXAPlayerWithSound(snd, bgSoundHandle); + return new MoviePlayer(vm, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA); +#else + GUI::MessageDialog dialog("DXA cutscenes found but ScummVM has been built without zlib support", "OK"); + dialog.runModal(); + return NULL; #endif + } + // Old MPEG2 cutscenes snprintf(filename, sizeof(filename), "%s.mp2", name); if (Common::File::exists(filename)) { GUI::MessageDialog dialog("MPEG2 cutscenes are no longer supported", "OK"); dialog.runModal(); + return NULL; } - return new MoviePlayerDummy(vm, name); + sprintf(buf, "Cutscene '%s' not found", name); + GUI::MessageDialog dialog(buf, "OK"); + dialog.runModal(); + + return NULL; +} + +void MoviePlayer::pauseMovie(bool pause) { + if (_bgSoundHandle) { + _snd->pauseHandle(*_bgSoundHandle, pause); + } } } // End of namespace Sword2 diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index 8b6a015037..dde1b75a8c 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -30,147 +30,87 @@ #include "graphics/video/dxa_player.h" #include "graphics/video/smk_player.h" +#include "graphics/video/video_player.h" #include "sound/mixer.h" #include "sword2/screen.h" namespace Sword2 { -struct SequenceTextInfo { - uint32 textNumber; - uint16 startFrame; - uint16 endFrame; +enum DecoderType { + kVideoDecoderDXA = 0, + kVideoDecoderSMK = 1 }; -struct MovieTextObject { - byte *textMem; - SpriteInfo textSprite; - uint16 speechId; - - MovieTextObject() { - textMem = NULL; - speechId = 0; +struct MovieText { + uint16 _startFrame; + uint16 _endFrame; + uint32 _textNumber; + byte *_textMem; + SpriteInfo _textSprite; + uint16 _speechId; + bool _played; + + void reset() { + _textMem = NULL; + _speechId = 0; + _played = false; } }; struct MovieInfo { const char *name; - uint frames; + int frames; bool seamless; }; -class MoviePlayer { -private: - bool checkSkipFrame(); - -protected: - Sword2Engine *_vm; - Audio::Mixer *_mixer; - OSystem *_system; - - char *_name; - - byte _originalPalette[4 * 256]; - - uint32 _numSpeechLines; - uint32 _firstSpeechFrame; - MovieTextObject _textObject; - byte *_textSurface; - - Audio::SoundHandle _bgSoundHandle; - Audio::AudioStream *_bgSoundStream; - - uint32 _ticks; - - uint32 _pauseStartTick; - uint32 _pauseTicks; - - uint _currentFrame; - byte *_frameBuffer; - int _frameWidth, _frameHeight; - int _frameX, _frameY; - - byte _black, _white; - - uint _numFrames; - uint _leadOutFrame; - bool _seamless; - - int _framesSkipped; - - static const MovieInfo _movies[]; - - uint32 _currentText; - - uint32 getTick(); - - void savePalette(); - void restorePalette(); - - void openTextObject(SequenceTextInfo *t); - void closeTextObject(); - void calcTextPosition(int &xPos, int &yPos); - - virtual void handleScreenChanged() {} - - virtual void clearFrame(); - virtual void updateScreen(); - virtual bool decodeFrame() = 0; - virtual bool syncFrame(); - virtual void drawFrame(); - virtual void drawTextObject(); - virtual void undrawTextObject(); - +class DXAPlayerWithSound : public Graphics::DXAPlayer { public: - MoviePlayer(Sword2Engine *vm, const char *name); - virtual ~MoviePlayer(); + DXAPlayerWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); + ~DXAPlayerWithSound() {} - virtual bool load(); - bool userInterrupt(); - void play(SequenceTextInfo *textList, uint32 numLines, int32 leadIn, int32 leadOut); - void pauseMovie(bool pause); + int32 getAudioLag(); +private: + Audio::Mixer *_mixer; + Audio::SoundHandle *_bgSoundHandle; }; -class MoviePlayerDummy : public MoviePlayer { -protected: - bool decodeFrame(); - bool syncFrame(); - void drawFrame(); - void drawTextObject(); - void undrawTextObject(); - +class MoviePlayer : public Graphics::VideoPlayer { public: - MoviePlayerDummy(Sword2Engine *vm, const char *name); - virtual ~MoviePlayerDummy(); + MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType); + virtual ~MoviePlayer(void); - bool load(); -}; + bool load(const char *name); + void play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut); + void pauseMovie(bool pause); -class MoviePlayerSMK : public MoviePlayer, ::Graphics::SMKPlayer { protected: - bool decodeFrame(); - -public: - MoviePlayerSMK(Sword2Engine *vm, const char *name); - ~MoviePlayerSMK(); + Sword2Engine *_vm; + Audio::Mixer *_snd; + OSystem *_system; + MovieText *_movieTexts; + uint32 _numMovieTexts; + uint32 _currentMovieText; + byte *_textSurface; + int _textX, _textY; + DecoderType _decoderType; - bool load(); -}; + Audio::SoundHandle *_bgSoundHandle; + Audio::AudioStream *_bgSoundStream; -#ifdef USE_ZLIB -class MoviePlayerDXA : public MoviePlayer, ::Graphics::DXAPlayer { -protected: - bool decodeFrame(); + int _id; + uint32 _leadOut; + int _numFrames; + int _leadOutFrame; -public: - MoviePlayerDXA(Sword2Engine *vm, const char *name); - ~MoviePlayerDXA(); + void performPostProcessing(byte *screen); - bool load(); + void openTextObject(uint32 index); + void closeTextObject(uint32 index); + void drawTextObject(uint32 index, byte *screen); }; -#endif -MoviePlayer *makeMoviePlayer(Sword2Engine *vm, const char *name); +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system); } // End of namespace Sword2 diff --git a/engines/sword2/function.cpp b/engines/sword2/function.cpp index 4c08614575..87fde864a6 100644 --- a/engines/sword2/function.cpp +++ b/engines/sword2/function.cpp @@ -2139,9 +2139,9 @@ int32 Logic::fnPlaySequence(int32 *params) { // pause sfx during sequence _vm->_sound->pauseFx(); - _moviePlayer = makeMoviePlayer(_vm, filename); + _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system); - if (_moviePlayer->load()) { + if (_moviePlayer->load(filename)) { _moviePlayer->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut); } @@ -2230,9 +2230,10 @@ int32 Logic::fnAddSequenceText(int32 *params) { if (!readVar(DEMO)) { assert(_sequenceTextLines < MAX_SEQUENCE_TEXT_LINES); - _sequenceTextList[_sequenceTextLines].textNumber = params[0]; - _sequenceTextList[_sequenceTextLines].startFrame = params[1]; - _sequenceTextList[_sequenceTextLines].endFrame = params[2]; + _sequenceTextList[_sequenceTextLines].reset(); + _sequenceTextList[_sequenceTextLines]._textNumber = params[0]; + _sequenceTextList[_sequenceTextLines]._startFrame = params[1]; + _sequenceTextList[_sequenceTextLines]._endFrame = params[2]; _sequenceTextLines++; } diff --git a/engines/sword2/logic.h b/engines/sword2/logic.h index b98b369982..bd89e50e46 100644 --- a/engines/sword2/logic.h +++ b/engines/sword2/logic.h @@ -86,7 +86,7 @@ private: // keeps count of number of text lines to disaply during the sequence uint32 _sequenceTextLines; - SequenceTextInfo _sequenceTextList[MAX_SEQUENCE_TEXT_LINES]; + MovieText _sequenceTextList[MAX_SEQUENCE_TEXT_LINES]; // when not playing a wav we calculate the speech time based upon // length of ascii diff --git a/engines/sword2/sound.cpp b/engines/sword2/sound.cpp index 6af94c9cb6..ee9fa1debf 100644 --- a/engines/sword2/sound.cpp +++ b/engines/sword2/sound.cpp @@ -217,6 +217,15 @@ void Sound::playMovieSound(int32 res, int type) { _vm->_sound->playFx(handle, data, len, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType); } +void Sound::stopMovieSounds() { + if (_vm->_mixer->isSoundHandleActive(_leadInHandle)) { + _vm->_mixer->stopHandle(_leadInHandle); + } + if (_vm->_mixer->isSoundHandleActive(_leadOutHandle)) { + _vm->_mixer->stopHandle(_leadOutHandle); + } +} + /** * Queue a sound effect for playing later. * @param res the sound resource number diff --git a/engines/sword2/sound.h b/engines/sword2/sound.h index 684be3dacd..9ba7976ba4 100644 --- a/engines/sword2/sound.h +++ b/engines/sword2/sound.h @@ -266,6 +266,7 @@ public: void unpauseAllSound(); void playMovieSound(int32 res, int type); + void stopMovieSounds(); void queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan); int32 playFx(FxQueueEntry *fx); |