From 8812f885bc95f0e4af05c333780eb77a8c2eaa19 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sat, 7 Jan 2012 00:20:27 -0500 Subject: SWORD2: Add support for PSX stream playback --- engines/sword2/animation.cpp | 140 +++++++++++++++++++++++++++++++++---------- engines/sword2/animation.h | 17 +++--- engines/sword2/function.cpp | 4 +- engines/sword2/sword2.cpp | 1 + 4 files changed, 121 insertions(+), 41 deletions(-) diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 80b4809722..e77ae98163 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -40,6 +40,11 @@ #include "gui/message.h" +#include "video/smk_decoder.h" +#include "video/psx_decoder.h" + +#include "engines/util.h" + namespace Sword2 { /////////////////////////////////////////////////////////////////////////////// @@ -66,6 +71,10 @@ MoviePlayer::~MoviePlayer() { * @param id the id of the file */ bool MoviePlayer::load(const char *name) { + // This happens when quitting during the "eye" cutscene. + if (_vm->shouldQuit()) + return false; + if (_decoderType == kVideoDecoderDXA) _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(name); else @@ -81,16 +90,26 @@ bool MoviePlayer::load(const char *name) { case kVideoDecoderSMK: filename = Common::String::format("%s.smk", name); break; + case kVideoDecoderPSX: + filename = Common::String::format("%s.str", name); + + // Need to switch to true color + initGraphics(640, 480, true, 0); + + // Need to load here in case it fails in which case we'd need + // to go back to paletted mode + if (_decoder->loadFile(filename)) { + return true; + } else { + initGraphics(640, 480, true); + return false; + } } return _decoder->loadFile(filename.c_str()); } void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut) { - // This happens when quitting during the "eye" cutscene. - if (_vm->shouldQuit()) - return; - _leadOutFrame = _decoder->getFrameCount(); if (_leadOutFrame > 60) _leadOutFrame -= 60; @@ -120,6 +139,11 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI while (_snd->isSoundHandleActive(*_bgSoundHandle)) _system->delayMillis(100); + + if (_decoderType == kVideoDecoderPSX) { + // Need to jump back to paletted color + initGraphics(640, 480, true); + } } void MoviePlayer::openTextObject(uint32 index) { @@ -165,7 +189,7 @@ void MoviePlayer::openTextObject(uint32 index) { } } -void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) { +void MoviePlayer::closeTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch) { if (index < _numMovieTexts) { MovieText *text = &_movieTexts[index]; @@ -180,23 +204,23 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) { int frameWidth = _decoder->getWidth(); int frameHeight = _decoder->getHeight(); + + if (_decoderType == kVideoDecoderPSX) + frameHeight *= 2; + int frameX = (_system->getWidth() - frameWidth) / 2; int frameY = (_system->getHeight() - frameHeight) / 2; - byte black = findBlackPalIndex(); - - byte *dst = screen + _textY * pitch; + uint32 black = getBlackColor(); for (int y = 0; y < text->_textSprite.h; y++) { if (_textY + y < frameY || _textY + y >= frameY + frameHeight) { - memset(dst + _textX, black, text->_textSprite.w); + screen->hLine(_textX, _textY + y, _textX + text->_textSprite.w, black); } else { if (frameX > _textX) - memset(dst + _textX, black, frameX - _textX); + screen->hLine(_textX, _textY + y, frameX, black); if (frameX + frameWidth < _textX + text->_textSprite.w) - memset(dst + frameX + frameWidth, black, _textX + text->_textSprite.w - (frameX + frameWidth)); + screen->hLine(frameX + frameWidth, _textY + y, text->_textSprite.w, black); } - - dst += pitch; } } @@ -206,11 +230,24 @@ void MoviePlayer::closeTextObject(uint32 index, byte *screen, uint16 pitch) { } } -void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) { +#define PUT_PIXEL(c) \ + switch (screen->format.bytesPerPixel) { \ + case 1: \ + *dst = (c); \ + break; \ + case 2: \ + WRITE_UINT16(dst, (c)); \ + break; \ + case 4: \ + WRITE_UINT32(dst, (c)); \ + break; \ + } + +void MoviePlayer::drawTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch) { MovieText *text = &_movieTexts[index]; - byte white = findWhitePalIndex(); - byte black = findBlackPalIndex(); + uint32 white = getWhiteColor(); + uint32 black = getBlackColor(); if (text->_textMem && _textSurface) { byte *src = text->_textSprite.data; @@ -226,17 +263,20 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) { src = psxSpriteBuffer; } - byte *dst = screen + _textY * pitch + _textX; - for (int y = 0; y < height; y++) { + byte *dst = (byte *)screen->getBasePtr(_textX, _textY + y); + for (int x = 0; x < width; x++) { - if (src[x] == 1) - dst[x] = black; - else if (src[x] == 255) - dst[x] = white; + if (src[x] == 1) { + PUT_PIXEL(black); + } else if (src[x] == 255) { + PUT_PIXEL(white); + } + + dst += screen->format.bytesPerPixel; } + src += width; - dst += pitch; } // Free buffer used to resize psx sprite @@ -245,7 +285,9 @@ void MoviePlayer::drawTextObject(uint32 index, byte *screen, uint16 pitch) { } } -void MoviePlayer::performPostProcessing(byte *screen, uint16 pitch) { +#undef PUT_PIXEL + +void MoviePlayer::performPostProcessing(Graphics::Surface *screen, uint16 pitch) { MovieText *text; int frame = _decoder->getCurFrame(); @@ -286,8 +328,12 @@ bool MoviePlayer::playVideo() { while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { if (_decoder->needsUpdate()) { const Graphics::Surface *frame = _decoder->decodeNextFrame(); - if (frame) - _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + if (frame) { + if (_decoderType == kVideoDecoderPSX) + drawFramePSX(frame); + else + _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + } if (_decoder->hasDirtyPalette()) { _decoder->setSystemPalette(); @@ -319,7 +365,7 @@ bool MoviePlayer::playVideo() { } Graphics::Surface *screen = _vm->_system->lockScreen(); - performPostProcessing((byte *)screen->pixels, screen->pitch); + performPostProcessing(screen, screen->pitch); _vm->_system->unlockScreen(); _vm->_system->updateScreen(); } @@ -335,12 +381,29 @@ bool MoviePlayer::playVideo() { return !_vm->shouldQuit(); } -byte MoviePlayer::findBlackPalIndex() { - return _black; +uint32 MoviePlayer::getBlackColor() { + return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : _black; } -byte MoviePlayer::findWhitePalIndex() { - return _white; +uint32 MoviePlayer::getWhiteColor() { + return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF) : _white; +} + +void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { + // The PSX videos have half resolution + + Graphics::Surface scaledFrame; + scaledFrame.create(frame->w, frame->h * 2, frame->format); + + for (int y = 0; y < scaledFrame.h; y++) + memcpy(scaledFrame.getBasePtr(0, y), frame->getBasePtr(0, y / 2), scaledFrame.w * scaledFrame.format.bytesPerPixel); + + uint16 x = (g_system->getWidth() - scaledFrame.w) / 2; + uint16 y = (g_system->getHeight() - scaledFrame.h) / 2; + + _vm->_system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h); + + scaledFrame.free(); } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) @@ -358,10 +421,23 @@ uint32 DXADecoderWithSound::getElapsedTime() const { // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system) { +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount) { Common::String filename; Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; + filename = Common::String::format("%s.str", name); + + if (Common::File::exists(filename)) { +#ifdef USE_RGB_COLOR + Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x, frameCount); + return new MoviePlayer(vm, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); +#else + GUI::MessageDialog dialog(_("PSX cutscenes found but ScummVM has been built without RGB color support"), _("OK")); + dialog.runModal(); + return NULL; +#endif + } + filename = Common::String::format("%s.smk", name); if (Common::File::exists(filename)) { diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index 1f5fced03b..3ef8dac754 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -26,7 +26,6 @@ #define SWORD2_ANIMATION_H #include "video/dxa_decoder.h" -#include "video/smk_decoder.h" #include "video/video_decoder.h" #include "audio/mixer.h" @@ -36,7 +35,8 @@ namespace Sword2 { enum DecoderType { kVideoDecoderDXA = 0, - kVideoDecoderSMK = 1 + kVideoDecoderSMK = 1, + kVideoDecoderPSX = 2 }; struct MovieText { @@ -93,18 +93,19 @@ protected: uint32 _leadOut; int _leadOutFrame; - void performPostProcessing(byte *screen, uint16 pitch); + void performPostProcessing(Graphics::Surface *screen, uint16 pitch); bool playVideo(); + void drawFramePSX(const Graphics::Surface *frame); void openTextObject(uint32 index); - void closeTextObject(uint32 index, byte *screen, uint16 pitch); - void drawTextObject(uint32 index, byte *screen, uint16 pitch); + void closeTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch); + void drawTextObject(uint32 index, Graphics::Surface *screen, uint16 pitch); - byte findBlackPalIndex(); - byte findWhitePalIndex(); + uint32 getBlackColor(); + uint32 getWhiteColor(); }; -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system); +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount); } // End of namespace Sword2 diff --git a/engines/sword2/function.cpp b/engines/sword2/function.cpp index 60ee6176a4..836b252d6c 100644 --- a/engines/sword2/function.cpp +++ b/engines/sword2/function.cpp @@ -2137,7 +2137,9 @@ int32 Logic::fnPlaySequence(int32 *params) { // pause sfx during sequence _vm->_sound->pauseFx(); - _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system); + uint32 frameCount = Sword2Engine::isPsx() ? params[1] : 0; + + _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system, frameCount); if (_moviePlayer && _moviePlayer->load(filename)) { _moviePlayer->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut); diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp index 3b7965259c..bdfc388c5f 100644 --- a/engines/sword2/sword2.cpp +++ b/engines/sword2/sword2.cpp @@ -255,6 +255,7 @@ Sword2Engine::Sword2Engine(OSystem *syst) : Engine(syst), _rnd("sword2") { SearchMan.addSubDirectoryMatching(gameDataDir, "sword2"); SearchMan.addSubDirectoryMatching(gameDataDir, "video"); SearchMan.addSubDirectoryMatching(gameDataDir, "smacks"); + SearchMan.addSubDirectoryMatching(gameDataDir, "streams"); // PSX video if (!scumm_stricmp(ConfMan.get("gameid").c_str(), "sword2demo") || !scumm_stricmp(ConfMan.get("gameid").c_str(), "sword2psxdemo")) _features = GF_DEMO; -- cgit v1.2.3