diff options
author | Colin Snover | 2017-01-02 23:30:35 -0600 |
---|---|---|
committer | Colin Snover | 2017-03-30 19:46:27 -0500 |
commit | 766d46153a285794d573d84d237aac3821431a01 (patch) | |
tree | 5d85232e856175fc20c4990ad48bf06a9db3d490 /engines | |
parent | 65fe7bcfd8431888d9fdf345759bad1a78c455b7 (diff) | |
download | scummvm-rg350-766d46153a285794d573d84d237aac3821431a01.tar.gz scummvm-rg350-766d46153a285794d573d84d237aac3821431a01.tar.bz2 scummvm-rg350-766d46153a285794d573d84d237aac3821431a01.zip |
SCI32: Implement known-used portions of kPlayDuck
Diffstat (limited to 'engines')
-rw-r--r-- | engines/sci/engine/kernel.h | 5 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 11 | ||||
-rw-r--r-- | engines/sci/engine/kscripts.cpp | 27 | ||||
-rw-r--r-- | engines/sci/engine/kvideo.cpp | 71 | ||||
-rw-r--r-- | engines/sci/graphics/video32.cpp | 258 | ||||
-rw-r--r-- | engines/sci/graphics/video32.h | 105 | ||||
-rw-r--r-- | engines/sci/sound/audio32.h | 12 |
7 files changed, 424 insertions, 65 deletions
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 51f4b5dbcb..ebd7d33655 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -657,6 +657,11 @@ reg_t kDoSoundPhantasmagoriaMac(EngineState *s, int argc, reg_t *argv); // SCI3 Kernel functions reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckPlay(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckSetFrameOut(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckOpen(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckClose(EngineState *s, int argc, reg_t *argv); +reg_t kPlayDuckSetVolume(EngineState *s, int argc, reg_t *argv); #endif reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 211d96bc2f..95f3197896 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -495,6 +495,15 @@ static const SciKernelMapSubEntry kRobot_subops[] = { }; // version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kPlayDuck_subops[] = { + { SIG_SCI3, 1, MAP_CALL(PlayDuckPlay), "iiiii", NULL }, + { SIG_SCI3, 2, MAP_CALL(PlayDuckSetFrameOut), "i", NULL }, + { SIG_SCI3, 5, MAP_CALL(PlayDuckClose), "", NULL }, + { SIG_SCI3, 6, MAP_CALL(PlayDuckSetVolume), "i", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kRemapColors_subops[] = { { SIG_SCI32, 0, MAP_CALL(RemapColorsOff), "(i)", NULL }, { SIG_SCI32, 1, MAP_CALL(RemapColorsByRange), "iiii(i)", NULL }, @@ -1008,7 +1017,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(MorphOn), SIG_EVERYWHERE, "", NULL, NULL }, // SCI3 Kernel Functions - { MAP_CALL(PlayDuck), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(PlayDuck), SIG_SCI3, SIGFOR_ALL, "(.*)", kPlayDuck_subops,NULL }, #endif { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL } diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index 77ef92b349..7c4c955824 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -119,11 +119,28 @@ reg_t kResCheck(EngineState *s, int argc, reg_t *argv) { } #ifdef ENABLE_SCI32 - // GK2 stores some VMDs inside of resource volumes, but usually they are - // streamed from the filesystem - if (res == nullptr && restype == kResourceTypeVMD) { - const Common::String fileName = Common::String::format("%u.vmd", argv[1].toUint16()); - return make_reg(0, Common::File::exists(fileName)); + // GK2 stores some VMDs inside of resource volumes, but usually videos are + // streamed from the filesystem. + if (res == nullptr) { + const char *format = nullptr; + switch (restype) { + case kResourceTypeRobot: + format = "%u.rbt"; + break; + case kResourceTypeDuck: + format = "%u.duk"; + break; + case kResourceTypeVMD: + format = "%u.vmd"; + break; + default: + format = nullptr; + } + + if (format) { + const Common::String fileName = Common::String::format(format, argv[1].toUint16()); + return make_reg(0, Common::File::exists(fileName)); + } } #endif diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 3d689f2b42..e4f2ff7a6d 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -472,52 +472,41 @@ reg_t kPlayVMDRestrictPalette(EngineState *s, int argc, reg_t *argv) { } reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) { - uint16 operation = argv[0].toUint16(); - Video::VideoDecoder *videoDecoder = 0; - bool reshowCursor = g_sci->_gfxCursor->isVisible(); - - switch (operation) { - case 1: // Play - // 6 params - s->_videoState.reset(); - s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); - - videoDecoder = new Video::AVIDecoder(); - - if (!videoDecoder->loadFile(s->_videoState.fileName)) { - warning("Could not open Duck %s", s->_videoState.fileName.c_str()); - break; - } - - if (reshowCursor) - g_sci->_gfxCursor->kernelHide(); - - { - // Duck videos are 16bpp, so we need to change the active pixel format - int oldWidth = g_system->getWidth(); - int oldHeight = g_system->getHeight(); - Common::List<Graphics::PixelFormat> formats; - formats.push_back(videoDecoder->getPixelFormat()); - initGraphics(640, 480, true, formats); + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} - if (g_system->getScreenFormat().bytesPerPixel != videoDecoder->getPixelFormat().bytesPerPixel) - error("Could not switch screen format for the duck video"); +reg_t kPlayDuckPlay(EngineState *s, int argc, reg_t *argv) { + kPlayDuckOpen(s, argc, argv); + g_sci->_video32->getDuckPlayer().play(-1); + g_sci->_video32->getDuckPlayer().close(); + return NULL_REG; +} - playVideo(videoDecoder, s->_videoState); +reg_t kPlayDuckSetFrameOut(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getDuckPlayer().setDoFrameOut((bool)argv[0].toUint16()); + return NULL_REG; +} - // Switch back to 8bpp - initGraphics(oldWidth, oldHeight, oldWidth > 320); - } +reg_t kPlayDuckOpen(EngineState *s, int argc, reg_t *argv) { + const GuiResourceId resourceId = argv[0].toUint16(); + const int displayMode = argv[1].toSint16(); + const int16 x = argv[2].toSint16(); + const int16 y = argv[3].toSint16(); + // argv[4] is a cache size argument that we do not use + g_sci->_video32->getDuckPlayer().open(resourceId, displayMode, x, y); + return NULL_REG; +} - if (reshowCursor) - g_sci->_gfxCursor->kernelShow(); - break; - default: - kStub(s, argc, argv); - break; - } +reg_t kPlayDuckClose(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getDuckPlayer().close(); + return NULL_REG; +} - return s->r_acc; +reg_t kPlayDuckSetVolume(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getDuckPlayer().setVolume(argv[0].toUint16()); + return NULL_REG; } #endif diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index f4eb2c2da0..8cd6fc7089 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -51,6 +51,25 @@ namespace Graphics { struct Surface; } namespace Sci { +/** + * @returns true if the player should quit + */ +static bool flushEvents(EventManager *eventMan) { + // Flushing all the keyboard and mouse events out of the event manager + // keeps events queued from before the start of playback from accidentally + // activating a video stop flag + for (;;) { + const SciEvent event = eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_QUIT); + if (event.type == SCI_EVENT_NONE) { + break; + } else if (event.type == SCI_EVENT_QUIT) { + return true; + } + } + + return false; +} + #pragma mark SEQPlayer SEQPlayer::SEQPlayer(SegManager *segMan) : @@ -672,16 +691,8 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co } VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { - // Flushing all the keyboard and mouse events out of the event manager - // keeps events queued from before the start of playback from accidentally - // activating a video stop flag - for (;;) { - const SciEvent event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_QUIT); - if (event.type == SCI_EVENT_NONE) { - break; - } else if (event.type == SCI_EVENT_QUIT) { - return kEventFlagEnd; - } + if (flushEvents(_eventMan)) { + return kEventFlagEnd; } if (flags & kEventFlagReverse) { @@ -914,4 +925,231 @@ void VMDPlayer::restrictPalette(const uint8 startColor, const int16 endColor) { _endColor = MIN<int16>(255, endColor); } +#pragma mark - +#pragma mark DuckPlayer + +DuckPlayer::DuckPlayer(SegManager *segMan, EventManager *eventMan) : + _segMan(segMan), + _eventMan(eventMan), + _decoder(new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)), + _plane(nullptr), + _status(kDuckClosed), + _drawRect(), + _volume(Audio::Mixer::kMaxChannelVolume), + _doFrameOut(false), + _pixelDouble(false), + _scaleBuffer(nullptr) {} + +DuckPlayer::~DuckPlayer() { + close(); + delete _decoder; +} + +void DuckPlayer::open(const GuiResourceId resourceId, const int displayMode, const int16 x, const int16 y) { + if (_status != kDuckClosed) { + error("Attempted to play %u.duk, but another video was loaded", resourceId); + } + + const Common::String fileName = Common::String::format("%u.duk", resourceId); + if (!_decoder->loadFile(fileName)) { + error("Can't open %s", fileName.c_str()); + } + + _decoder->setVolume(_volume); + _pixelDouble = displayMode != 0; + + const int16 scale = _pixelDouble ? 2 : 1; + _drawRect = Common::Rect(x, y, + (x + _decoder->getWidth()) * scale, + (y + _decoder->getHeight()) * scale); + + g_sci->_gfxCursor32->hide(); + + if (_doFrameOut) { + _plane = new Plane(_drawRect, kPlanePicColored); + g_sci->_gfxFrameout->addPlane(*_plane); + g_sci->_gfxFrameout->frameOut(true); + } + + const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); + const Graphics::PixelFormat format = _decoder->getPixelFormat(); + + if (_pixelDouble) { + assert(_scaleBuffer == nullptr); + _scaleBuffer = new byte[_drawRect.width() * _drawRect.height() * format.bytesPerPixel]; + } + + initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, true, &format); + + _status = kDuckOpen; +} + +void DuckPlayer::play(const int lastFrameNo) { + flushEvents(_eventMan); + + if (_status != kDuckPlaying) { + _status = kDuckPlaying; + _decoder->start(); + } + + while (!g_engine->shouldQuit()) { + if (_decoder->endOfVideo() || (lastFrameNo != -1 && _decoder->getCurFrame() >= lastFrameNo)) { + break; + } + + g_sci->sleep(_decoder->getTimeToNextFrame()); + while (_decoder->needsUpdate()) { + renderFrame(); + } + + SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK); + if (event.type == SCI_EVENT_MOUSE_PRESS) { + flushEvents(_eventMan); + break; + } + + event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); + if (event.type == SCI_EVENT_KEYBOARD) { + bool stop = false; + while ((event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD)), + event.type != SCI_EVENT_NONE) { + if (event.character == SCI_KEY_ESC) { + stop = true; + break; + } + } + + if (stop) { + flushEvents(_eventMan); + break; + } + } + } +} + +void DuckPlayer::close() { + if (_status == kDuckClosed) { + return; + } + + _decoder->close(); + + const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); + const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); + initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, true, &format); + + g_sci->_gfxCursor32->unhide(); + + if (_doFrameOut) { + g_sci->_gfxFrameout->deletePlane(*_plane); + g_sci->_gfxFrameout->frameOut(true); + _plane = nullptr; + } + + _pixelDouble = false; + delete[] _scaleBuffer; + _scaleBuffer = nullptr; + + _status = kDuckClosed; +} + +static inline uint16 interpolate(const Graphics::PixelFormat &format, const uint16 p1, const uint16 p2) { + uint8 r1, g1, b1, r2, g2, b2; + format.colorToRGB(p1, r1, g1, b1); + format.colorToRGB(p2, r2, g2, b2); + return format.RGBToColor((r1 + r2) >> 1, (g1 + g2) >> 1, (b1 + b2) >> 1); +} + +void DuckPlayer::renderFrame() const { + const Graphics::Surface *surface = _decoder->decodeNextFrame(); + + // Audio-only or non-updated frame + if (surface == nullptr) { + return; + } + + assert(surface->format.bytesPerPixel == 2); + + if (_pixelDouble) { + const uint16 *source = (const uint16 *)surface->getPixels(); + const Graphics::PixelFormat &format = surface->format; + uint16 *target = (uint16 *)_scaleBuffer; + +#ifndef SCI_DUCK_NO_INTERPOLATION + // divide by 2 gets pixel pitch instead of byte pitch for source + const uint16 sourcePitch = surface->pitch >> 1; +#endif + + const uint16 targetPitch = surface->pitch; + const bool blackLined = ConfMan.getBool("enable_black_lined_video"); + for (int y = 0; y < surface->h - 1; ++y) { + for (int x = 0; x < surface->w - 1; ++x) { +#ifndef SCI_DUCK_NO_INTERPOLATION + const uint16 a = source[0]; + const uint16 b = source[1]; + const uint16 c = source[sourcePitch]; + const uint16 d = source[sourcePitch + 1]; + + target[0] = a; + target[1] = interpolate(format, a, b); +#else + const uint16 value = *source; + target[0] = value; + target[1] = value; +#endif + if (!blackLined) { +#ifndef SCI_DUCK_NO_INTERPOLATION + target[targetPitch] = interpolate(format, a, c); + target[targetPitch + 1] = interpolate(format, target[1], interpolate(format, c, d)); +#else + target[targetPitch] = value; + target[targetPitch + 1] = value; +#endif + } + + target += 2; + ++source; + } + + const uint16 value = *source++; + target[0] = value; + target[1] = value; + if (!blackLined) { + target[targetPitch] = value; + target[targetPitch + 1] = value; + } + target += 2; + + if (blackLined) { + memset(target, 0, targetPitch * format.bytesPerPixel); + } + + target += targetPitch; + } + + for (int x = 0; x < surface->w; ++x) { + const uint16 lastValue = *source++; + target[0] = lastValue; + target[1] = lastValue; + + if (!blackLined) { + target[targetPitch] = lastValue; + target[targetPitch + 1] = lastValue; + target += 2; + } + } + + if (blackLined) { + memset(target, 0, targetPitch); + } + + g_system->copyRectToScreen(_scaleBuffer, surface->pitch * 2, _drawRect.left, _drawRect.top, _drawRect.width(), _drawRect.height()); + } else { + g_system->copyRectToScreen(surface->getPixels(), surface->pitch, _drawRect.left, _drawRect.top, surface->w, surface->h); + } + + g_system->updateScreen(); + g_sci->getSciDebugger()->onFrame(); +} + } // End of namespace Sci diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index 5ed8fd954a..fae5cafbbe 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -28,10 +28,11 @@ #include "common/str.h" // for String #include "sci/engine/vm_types.h" // for reg_t #include "sci/video/robot_decoder.h" // for RobotDecoder +#include "sci/sound/audio32.h" // for Audio32::kMaxVolume +#include "video/avi_decoder.h" // for AVIDecoder::setVolume namespace Video { class AdvancedVMDDecoder; -class AVIDecoder; } namespace Sci { class EventManager; @@ -531,6 +532,103 @@ private: bool _showCursor; }; +#pragma mark - +#pragma mark DuckPlayer + +class DuckPlayer { +public: + enum DuckStatus { + kDuckClosed = 0, + kDuckOpen = 1, + kDuckPlaying = 2, + kDuckPaused = 3 + }; + + DuckPlayer(SegManager *segMan, EventManager *eventMan); + + ~DuckPlayer(); + + /** + * Opens a stream to a Duck resource. + */ + void open(const GuiResourceId resourceId, const int displayMode, const int16 x, const int16 y); + + /** + * Stops playback and closes the currently open Duck stream. + */ + void close(); + + /** + * Begins playback of the current Duck video. + */ + void play(const int lastFrameNo); + + /** + * Sets a flag indicating that an opaque plane should be added + * to the graphics manager underneath the video surface during + * playback. + */ + void setDoFrameOut(const bool value) { _doFrameOut = value; } + + /** + * Sets the volume of the decoder. + */ + void setVolume(const uint8 value) { + _volume = (uint)value * Audio::Mixer::kMaxChannelVolume / Audio32::kMaxVolume; + _decoder->setVolume(_volume); + } + +private: + SegManager *_segMan; + EventManager *_eventMan; + Video::AVIDecoder *_decoder; + + /** + * An empty plane drawn behind the video when the doFrameOut + * flag is true. + */ + Plane *_plane; + + /** + * The player status. + */ + DuckStatus _status; + + /** + * The screen rect where the video should be drawn. + */ + Common::Rect _drawRect; + + /** + * The playback volume for the player. + */ + uint8 _volume; + + /** + * If true, frameOut will be called during Duck video playback to update + * other parts of the screen. + */ + bool _doFrameOut; + + /** + * If true, the video will be pixel doubled during playback. + */ + bool _pixelDouble; + + /** + * The buffer used to perform scaling of the video. + */ + byte *_scaleBuffer; + + /** + * Renders the current frame to the system video buffer. + */ + void renderFrame() const; +}; + +#pragma mark - +#pragma mark Video32 + /** * Video32 provides facilities for playing back * video in SCI engine. @@ -541,18 +639,21 @@ public: _SEQPlayer(segMan), _AVIPlayer(segMan, eventMan), _VMDPlayer(segMan, eventMan), - _robotPlayer(segMan) {} + _robotPlayer(segMan), + _duckPlayer(segMan, eventMan) {} SEQPlayer &getSEQPlayer() { return _SEQPlayer; } AVIPlayer &getAVIPlayer() { return _AVIPlayer; } VMDPlayer &getVMDPlayer() { return _VMDPlayer; } RobotDecoder &getRobotPlayer() { return _robotPlayer; } + DuckPlayer &getDuckPlayer() { return _duckPlayer; } private: SEQPlayer _SEQPlayer; AVIPlayer _AVIPlayer; VMDPlayer _VMDPlayer; RobotDecoder _robotPlayer; + DuckPlayer _duckPlayer; }; } // End of namespace Sci diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h index a9905ab6bf..9130cbe687 100644 --- a/engines/sci/sound/audio32.h +++ b/engines/sci/sound/audio32.h @@ -160,12 +160,6 @@ public: Audio32(ResourceManager *resMan); ~Audio32(); -private: - ResourceManager *_resMan; - Audio::Mixer *_mixer; - Audio::SoundHandle _handle; - Common::Mutex _mutex; - enum { /** * The maximum channel volume. @@ -173,6 +167,12 @@ private: kMaxVolume = 127 }; +private: + ResourceManager *_resMan; + Audio::Mixer *_mixer; + Audio::SoundHandle _handle; + Common::Mutex _mutex; + #pragma mark - #pragma mark AudioStream implementation public: |