From a802e1e64f56b5c6b0534a481e84418078b2699f Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:33:59 +0000 Subject: VIDEO: Stub a VideoDecoder-like CoktelDecoder This creates a new CoktelDecoder class using the VideoDecoder interface, which will eventually become the new way to decode PreIMD, IMD and VMD videos. Since the VideoPlayer in gob is not yet ready for this, we're disabling all video playback in the gob engine for now. svn-id: r51850 --- graphics/video/coktel_decoder.cpp | 105 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 graphics/video/coktel_decoder.cpp (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp new file mode 100644 index 0000000000..a6c79cc1df --- /dev/null +++ b/graphics/video/coktel_decoder.cpp @@ -0,0 +1,105 @@ +/* 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. + * + * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "graphics/video/coktel_decoder.h" + +#ifdef GRAPHICS_VIDEO_COKTELDECODER_H + +namespace Graphics { + +CoktelDecoder::State::State() : left(0), top(0), right(0), bottom(0), flags(0), speechId(0) { +} + + +CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : + _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _frameCount(0), _paletteDirty(false) { + + memset(_palette, 0, 768); +} + +CoktelDecoder::~CoktelDecoder() { +} + +uint16 CoktelDecoder::getWidth() const { + return _width; +} + +uint16 CoktelDecoder::getHeight() const { + return _height; +} + +uint32 CoktelDecoder::getFrameCount() const { + return _frameCount; +} + +byte *CoktelDecoder::getPalette() { + return _palette; +} + +bool CoktelDecoder::hasDirtyPalette() const { + return _paletteDirty; +} + +Common::Rational CoktelDecoder::getFrameRate() const { + return _frameRate; +} + + +PreIMDDecoder::PreIMDDecoder(uint16 width, uint16 height, + Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType) { + + _width = width; + _height = height; +} + +PreIMDDecoder::~PreIMDDecoder() { +} + +bool PreIMDDecoder::seek(uint32 frame, int whence, bool restart) { + return false; +} + +bool PreIMDDecoder::load(Common::SeekableReadStream &stream) { + return false; +} + +void PreIMDDecoder::close() { +} + +bool PreIMDDecoder::isVideoLoaded() const { + return false; +} + +Surface *PreIMDDecoder::decodeNextFrame() { + return 0; +} + +PixelFormat PreIMDDecoder::getPixelFormat() const { + return PixelFormat::createFormatCLUT8(); +} + +} // End of namespace Graphics + +#endif // GRAPHICS_VIDEO_COKTELDECODER_H -- cgit v1.2.3 From 167c6e8787811910d2e62393231dc98f14d70d15 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:36:19 +0000 Subject: VIDEO: Bare PreIMD decoding Implemented bare PreIMD decoding using the new CoktelDecoder interface. No fancy stuff yet, only basic vieo frames. svn-id: r51854 --- graphics/video/coktel_decoder.cpp | 171 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 165 insertions(+), 6 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index a6c79cc1df..0c5b637c2b 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -68,32 +68,191 @@ Common::Rational CoktelDecoder::getFrameRate() const { PreIMDDecoder::PreIMDDecoder(uint16 width, uint16 height, - Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType) { + Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + _stream(0), _videoBuffer(0), _videoBufferSize(0) { _width = width; _height = height; } PreIMDDecoder::~PreIMDDecoder() { + close(); } -bool PreIMDDecoder::seek(uint32 frame, int whence, bool restart) { - return false; +bool PreIMDDecoder::seek(int32 frame, int whence, bool restart) { + if (!isVideoLoaded()) + // Nothing to do + return false; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _frameCount - frame - 1; + else if (whence == SEEK_SET) + frame--; + else + return false; + + if ((frame < -1) || (((uint32) frame) >= _frameCount)) + // Out of range + return false; + + if (frame == _curFrame) + // Nothing to do + return true; + + // Run through the frames + _curFrame = -1; + _stream->seek(2); + while (_curFrame != frame) { + uint16 frameSize = _stream->readUint16LE(); + + _stream->skip(frameSize + 2); + + _curFrame++; + } + + return true; } bool PreIMDDecoder::load(Common::SeekableReadStream &stream) { - return false; + // Since PreIMDs don't have any width and height values stored, + // we need them to be specified in the constructor + assert((_width > 0) && (_height > 0)); + + close(); + + _stream = &stream; + + _stream->seek(0); + + _frameCount = _stream->readUint16LE(); + + _surface.create(_width, _height, 1); + + _videoBufferSize = _width * _height; + _videoBuffer = new byte[_videoBufferSize]; + + memset(_videoBuffer, 0, _videoBufferSize); + + return true; } void PreIMDDecoder::close() { + reset(); + + _surface.free(); + + delete _stream; + + delete[] _videoBuffer; + + _stream = 0; + + _videoBuffer = 0; + _videoBufferSize = 0; } bool PreIMDDecoder::isVideoLoaded() const { - return false; + return _stream != 0; } Surface *PreIMDDecoder::decodeNextFrame() { - return 0; + if (!isVideoLoaded() || endOfVideo()) + return 0; + + processFrame(); + renderFrame(); + + _curFrame++; + + return &_surface; +} + +void PreIMDDecoder::processFrame() { + uint16 frameSize = _stream->readUint16LE(); + + uint32 nextFramePos = _stream->pos() + frameSize + 2; + + byte cmd; + + cmd = _stream->readByte(); + frameSize--; + + if (cmd == 0) { + // Palette. Ignored by Fascination, though + + _stream->skip(768); + + frameSize -= 769; + + cmd = _stream->readByte(); + } + + if (cmd != 2) { + // Partial frame data + + uint32 fSize = frameSize; + uint32 vidSize = _videoBufferSize; + + byte *vidBuffer = _videoBuffer; + + while ((fSize > 0) && (vidSize > 0)) { + uint32 n = _stream->readByte(); + fSize--; + + if ((n & 0x80) != 0) { + // Data + + n = MIN((n & 0x7F) + 1, MIN(fSize, vidSize)); + + _stream->read(vidBuffer, n); + + vidBuffer += n; + vidSize -= n; + fSize -= n; + + } else { + // Skip + + n = MIN(n + 1, vidSize); + + vidBuffer += n; + vidSize -= n; + } + } + + } else { + // Full direct frame + + uint32 vidSize = MIN(_videoBufferSize, frameSize); + + _stream->read(_videoBuffer, vidSize); + } + + _stream->seek(nextFramePos); +} + +void PreIMDDecoder::renderFrame() { + uint16 w = MIN(_surface.w, _width); + uint16 h = MIN(_surface.h, _height); + + const byte *src = _videoBuffer; + byte *dst = (byte *) _surface.pixels; // + x/y + + uint32 frameDataSize = _videoBufferSize; + + while (h-- > 0) { + uint32 n = MIN(w, frameDataSize); + + memcpy(dst, src, n); + + src += _width; + dst += _surface.pitch; + + frameDataSize -= n; + } } PixelFormat PreIMDDecoder::getPixelFormat() const { -- cgit v1.2.3 From 9255d2e2179ee6a2cd63b6d8f521e3b9d8543be6 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:37:52 +0000 Subject: VIDEO/GOB: Add setSurfaceMemory() to CoktelDecoder This allows the video player to directly draw onto its own video memory without having to blit each frame another time. Will also be needed for proper handling of transparency in Woodruff. svn-id: r51857 --- graphics/video/coktel_decoder.cpp | 62 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 0c5b637c2b..d13e678d44 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -34,7 +34,8 @@ CoktelDecoder::State::State() : left(0), top(0), right(0), bottom(0), flags(0), CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : - _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _frameCount(0), _paletteDirty(false) { + _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _frameCount(0), + _paletteDirty(false), _ownSurface(true) { memset(_palette, 0, 768); } @@ -42,6 +43,59 @@ CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundT CoktelDecoder::~CoktelDecoder() { } +void CoktelDecoder::setSurfaceMemory(void *mem, uint16 width, uint16 height, uint8 bpp) { + freeSurface(); + + assert((width > 0) && (height > 0)); + assert(bpp == getPixelFormat().bytesPerPixel); + + _surface.w = width; + _surface.h = height; + _surface.pitch = width * bpp; + _surface.pixels = mem; + _surface.bytesPerPixel = bpp; + + _ownSurface = false; +} + +void CoktelDecoder::setSurfaceMemory() { + freeSurface(); + createSurface(); + + _ownSurface = true; +} + +bool CoktelDecoder::hasSurface() { + return _surface.pixels != 0; +} + +void CoktelDecoder::createSurface() { + if (hasSurface()) + return; + + if ((_width > 0) && (_height > 0)) + _surface.create(_width, _height, getPixelFormat().bytesPerPixel); + + _ownSurface = true; +} + +void CoktelDecoder::freeSurface() { + if (!_ownSurface) { + _surface.w = 0; + _surface.h = 0; + _surface.pitch = 0; + _surface.pixels = 0; + _surface.bytesPerPixel = 0; + } else + _surface.free(); + + _ownSurface = true; +} + +void CoktelDecoder::close() { + freeSurface(); +} + uint16 CoktelDecoder::getWidth() const { return _width; } @@ -129,8 +183,6 @@ bool PreIMDDecoder::load(Common::SeekableReadStream &stream) { _frameCount = _stream->readUint16LE(); - _surface.create(_width, _height, 1); - _videoBufferSize = _width * _height; _videoBuffer = new byte[_videoBufferSize]; @@ -142,7 +194,7 @@ bool PreIMDDecoder::load(Common::SeekableReadStream &stream) { void PreIMDDecoder::close() { reset(); - _surface.free(); + CoktelDecoder::close(); delete _stream; @@ -162,6 +214,8 @@ Surface *PreIMDDecoder::decodeNextFrame() { if (!isVideoLoaded() || endOfVideo()) return 0; + createSurface(); + processFrame(); renderFrame(); -- cgit v1.2.3 From 12c9e895b559d1ca2780b5b8f156b0451eb11f11 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:38:26 +0000 Subject: VIDEO/GOB: Add CoktelDecoder::setXY() This allows for positioning the video within the video memory. svn-id: r51858 --- graphics/video/coktel_decoder.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index d13e678d44..6f846c9a07 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -34,7 +34,7 @@ CoktelDecoder::State::State() : left(0), top(0), right(0), bottom(0), flags(0), CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : - _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _frameCount(0), + _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), _frameCount(0), _paletteDirty(false), _ownSurface(true) { memset(_palette, 0, 768); @@ -92,8 +92,18 @@ void CoktelDecoder::freeSurface() { _ownSurface = true; } +void CoktelDecoder::setXY(uint16 x, uint16 y) { + _x = x; + _y = y; +} + void CoktelDecoder::close() { freeSurface(); + + _x = 0; + _y = 0; + + _frameCount = 0; } uint16 CoktelDecoder::getWidth() const { @@ -289,11 +299,11 @@ void PreIMDDecoder::processFrame() { } void PreIMDDecoder::renderFrame() { - uint16 w = MIN(_surface.w, _width); - uint16 h = MIN(_surface.h, _height); + uint16 w = CLIP(_surface.w - _x, 0, _width); + uint16 h = CLIP(_surface.h - _y, 0, _height); const byte *src = _videoBuffer; - byte *dst = (byte *) _surface.pixels; // + x/y + byte *dst = (byte *) _surface.pixels + (_y * _surface.pitch) + _x; uint32 frameDataSize = _videoBufferSize; -- cgit v1.2.3 From f7363fdb10675bf27c1a1d37d23a25590bdc344c Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:39:03 +0000 Subject: VIDEO/GOB: Add CoktelDecoder::getDirtyRects() This allows for the client code to only update the parts of the video frames that actually changed. svn-id: r51859 --- graphics/video/coktel_decoder.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 6f846c9a07..a9a2e157a3 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -29,7 +29,7 @@ namespace Graphics { -CoktelDecoder::State::State() : left(0), top(0), right(0), bottom(0), flags(0), speechId(0) { +CoktelDecoder::State::State() : flags(0), speechId(0) { } @@ -97,6 +97,10 @@ void CoktelDecoder::setXY(uint16 x, uint16 y) { _y = y; } +const Common::List &CoktelDecoder::getDirtyRects() const { + return _dirtyRects; +} + void CoktelDecoder::close() { freeSurface(); @@ -299,6 +303,8 @@ void PreIMDDecoder::processFrame() { } void PreIMDDecoder::renderFrame() { + _dirtyRects.clear(); + uint16 w = CLIP(_surface.w - _x, 0, _width); uint16 h = CLIP(_surface.h - _y, 0, _height); @@ -317,6 +323,8 @@ void PreIMDDecoder::renderFrame() { frameDataSize -= n; } + + _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); } PixelFormat PreIMDDecoder::getPixelFormat() const { -- cgit v1.2.3 From aa113e5ab3b6e96ff6d2b56839b7ac08ec0617cc Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:39:32 +0000 Subject: VIDEO: Add CoktelDecoder::setFrameRate(). Allows client code to overwrite the video's frame rate. svn-id: r51860 --- graphics/video/coktel_decoder.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index a9a2e157a3..9e4fc1edc6 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -35,7 +35,7 @@ CoktelDecoder::State::State() : flags(0), speechId(0) { CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), _frameCount(0), - _paletteDirty(false), _ownSurface(true) { + _paletteDirty(false), _ownSurface(true), _frameRate(12) { memset(_palette, 0, 768); } @@ -97,6 +97,10 @@ void CoktelDecoder::setXY(uint16 x, uint16 y) { _y = y; } +void CoktelDecoder::setFrameRate(Common::Rational frameRate) { + _frameRate = frameRate; +} + const Common::List &CoktelDecoder::getDirtyRects() const { return _dirtyRects; } @@ -235,6 +239,9 @@ Surface *PreIMDDecoder::decodeNextFrame() { _curFrame++; + if (_curFrame == 0) + _startTime = g_system->getMillis(); + return &_surface; } -- cgit v1.2.3 From dcf8298a75cfeb44c6059f03b57f0a61bc062ac0 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:40:24 +0000 Subject: VIDEO: Stub IMDDecoder svn-id: r51862 --- graphics/video/coktel_decoder.cpp | 99 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 9e4fc1edc6..220a290da6 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -338,6 +338,105 @@ PixelFormat PreIMDDecoder::getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } + +IMDDecoder::IMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + _stream(0), _videoBuffer(0), _videoBufferSize(0) { +} + +IMDDecoder::~IMDDecoder() { + close(); +} + +bool IMDDecoder::seek(int32 frame, int whence, bool restart) { + if (!isVideoLoaded()) + // Nothing to do + return false; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _frameCount - frame - 1; + else if (whence == SEEK_SET) + frame--; + else + return false; + + if ((frame < -1) || (((uint32) frame) >= _frameCount)) + // Out of range + return false; + + if (frame == _curFrame) + // Nothing to do + return true; + + // TODO + + return true; +} + +bool IMDDecoder::load(Common::SeekableReadStream &stream) { + close(); + + _stream = &stream; + + warning("IMDDecoder::load()"); + + // TODO + + return false; +} + +void IMDDecoder::close() { + reset(); + + CoktelDecoder::close(); + + delete _stream; + + delete[] _videoBuffer; + + _stream = 0; + + _videoBuffer = 0; + _videoBufferSize = 0; +} + +bool IMDDecoder::isVideoLoaded() const { + return _stream != 0; +} + +Surface *IMDDecoder::decodeNextFrame() { + if (!isVideoLoaded() || endOfVideo()) + return 0; + + createSurface(); + + processFrame(); + renderFrame(); + + _curFrame++; + + if (_curFrame == 0) + _startTime = g_system->getMillis(); + + return &_surface; +} + +void IMDDecoder::processFrame() { + // TODO +} + +void IMDDecoder::renderFrame() { + _dirtyRects.clear(); + + // TODO +} + +PixelFormat IMDDecoder::getPixelFormat() const { + return PixelFormat::createFormatCLUT8(); +} + } // End of namespace Graphics #endif // GRAPHICS_VIDEO_COKTELDECODER_H -- cgit v1.2.3 From f19be90c37c3aa402ea7efdc0fc02595fe68a122 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:41:56 +0000 Subject: VIDEO/GOB: Implement IMD loading svn-id: r51865 --- graphics/video/coktel_decoder.cpp | 342 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 330 insertions(+), 12 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 220a290da6..2b4b7182f3 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -27,6 +27,8 @@ #ifdef GRAPHICS_VIDEO_COKTELDECODER_H +#include "sound/audiostream.h" + namespace Graphics { CoktelDecoder::State::State() : flags(0), speechId(0) { @@ -34,8 +36,10 @@ CoktelDecoder::State::State() : flags(0), speechId(0) { CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : - _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), _frameCount(0), - _paletteDirty(false), _ownSurface(true), _frameRate(12) { + _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), + _defaultX(0), _defaultY(0), _features(0), _frameCount(0), _paletteDirty(false), + _ownSurface(true), _frameRate(12), _hasSound(false), _soundEnabled(false), + _soundStage(kSoundNone), _audioStream(0) { memset(_palette, 0, 768); } @@ -97,21 +101,85 @@ void CoktelDecoder::setXY(uint16 x, uint16 y) { _y = y; } +void CoktelDecoder::setXY() { + setXY(_defaultX, _defaultY); +} + void CoktelDecoder::setFrameRate(Common::Rational frameRate) { _frameRate = frameRate; } +uint16 CoktelDecoder::getDefaultX() const { + return _defaultX; +} + +uint16 CoktelDecoder::getDefaultY() const { + return _defaultY; +} + const Common::List &CoktelDecoder::getDirtyRects() const { return _dirtyRects; } +bool CoktelDecoder::hasSound() const { + return _hasSound; +} + +bool CoktelDecoder::isSoundEnabled() const { + return _soundEnabled; +} + +bool CoktelDecoder::isSoundPlaying() const { + return _audioStream && _mixer->isSoundHandleActive(_audioHandle); +} + +void CoktelDecoder::enableSound() { + if (!hasSound() || isSoundEnabled()) + return; + + // Sanity check + if (_mixer->getOutputRate() == 0) + return; + + // Only possible on the first frame + if (_curFrame > -1) + return; + + _soundEnabled = true; +} + +void CoktelDecoder::disableSound() { + if (_audioStream) { + + if (_soundStage == kSoundPlaying) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + } else + delete _audioStream; + + } + + _soundEnabled = false; + _soundStage = kSoundNone; + + _audioStream = 0; +} + void CoktelDecoder::close() { + disableSound(); freeSurface(); _x = 0; _y = 0; + _defaultX = 0; + _defaultY = 0; + + _features = 0; + _frameCount = 0; + + _hasSound = false; } uint16 CoktelDecoder::getWidth() const { @@ -131,7 +199,7 @@ byte *CoktelDecoder::getPalette() { } bool CoktelDecoder::hasDirtyPalette() const { - return _paletteDirty; + return (_features & kFeaturesPalette) && _paletteDirty; } Common::Rational CoktelDecoder::getFrameRate() const { @@ -237,8 +305,6 @@ Surface *PreIMDDecoder::decodeNextFrame() { processFrame(); renderFrame(); - _curFrame++; - if (_curFrame == 0) _startTime = g_system->getMillis(); @@ -307,6 +373,8 @@ void PreIMDDecoder::processFrame() { } _stream->seek(nextFramePos); + + _curFrame++; } void PreIMDDecoder::renderFrame() { @@ -340,7 +408,12 @@ PixelFormat PreIMDDecoder::getPixelFormat() const { IMDDecoder::IMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), - _stream(0), _videoBuffer(0), _videoBufferSize(0) { + _stream(0), _version(0), _stdX(-1), _stdY(-1), _stdWidth(-1), _stdHeight(-1), + _flags(0), _firstFramePos(0), _framePos(0), _frameCoords(0), + _frameData(0), _frameDataSize(0), _frameDataLen(0), + _videoBuffer(0), _videoBufferSize(0), + _soundFlags(0), _soundFreq(0), _soundSliceSize(0), _soundSlicesCount(0) { + } IMDDecoder::~IMDDecoder() { @@ -370,7 +443,37 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { // Nothing to do return true; - // TODO + // Try every possible way to find a file offset to that frame + uint32 framePos = 0; + if (frame == -1) { + + framePos = _firstFramePos; + + } else if (frame == 0) { + + framePos = _firstFramePos; + _stream->seek(framePos); + framePos += _stream->readUint16LE() + 4; + + } else if (_framePos) { + + framePos = _framePos[frame + 1]; + + } else if (restart && (_soundStage == kSoundNone)) { + + for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) + processFrame(); + + return true; + + } else { + warning("Imd::seek(): Frame %d is not directly accessible", frame + 1); + return false; + } + + // Seek + _stream->seek(framePos); + _curFrame = frame; return true; } @@ -380,11 +483,192 @@ bool IMDDecoder::load(Common::SeekableReadStream &stream) { _stream = &stream; - warning("IMDDecoder::load()"); + uint16 handle; - // TODO + handle = _stream->readUint16LE(); + _version = _stream->readByte(); + + // Version checking + if ((handle != 0) || (_version < 2)) { + warning("IMDDecoder::load(): Version incorrect (%d, 0x%X)", handle, _version); + close(); + return false; + } + + // Rest header + _features = _stream->readByte(); + _frameCount = _stream->readUint16LE(); + _defaultX = _stream->readSint16LE(); + _defaultY = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + _flags = _stream->readUint16LE(); + _firstFramePos = _stream->readUint16LE(); + + _x = _defaultX; + _y = _defaultY; + + // IMDs always have video + _features |= kFeaturesVideo; + // IMDs always have palettes + _features |= kFeaturesPalette; + + // Palette + _stream->read((byte *) _palette, 768); + + _paletteDirty = true; + + if (!loadCoordinates()) { + close(); + return false; + } + + uint32 framePosPos, frameCoordsPos; + if (!loadFrameTableOffsets(framePosPos, frameCoordsPos)) { + close(); + return false; + } + + if (!assessAudioProperties()) { + close(); + return false; + } + + if (!assessVideoProperties()) { + close(); + return false; + } + + if (!loadFrameTables(framePosPos, frameCoordsPos)) { + close(); + return false; + } + + // Seek to the first frame + _stream->seek(_firstFramePos); + + return true; +} + +bool IMDDecoder::loadCoordinates() { + // Standard coordinates + if (_version >= 3) { + uint16 count = _stream->readUint16LE(); + if (count > 1) { + warning("IMD: More than one standard coordinate quad found (%d)", count ); + return false; + } + + if (count != 0) { + _stdX = _stream->readSint16LE(); + _stdY = _stream->readSint16LE(); + _stdWidth = _stream->readSint16LE(); + _stdHeight = _stream->readSint16LE(); + _features |= kFeaturesStdCoords; + } else + _stdX = _stdY = _stdWidth = _stdHeight = -1; - return false; + } else + _stdX = _stdY = _stdWidth = _stdHeight = -1; + + return true; +} + +bool IMDDecoder::loadFrameTableOffsets(uint32 &framePosPos, uint32 &frameCoordsPos) { + framePosPos = 0; + frameCoordsPos = 0; + + // Frame positions + if (_version >= 4) { + framePosPos = _stream->readUint32LE(); + if (framePosPos != 0) { + _framePos = new uint32[_frameCount]; + _features |= kFeaturesFramePos; + } + } + + // Frame coordinates + if (_features & kFeaturesFrameCoords) + frameCoordsPos = _stream->readUint32LE(); + + return true; +} + +bool IMDDecoder::assessVideoProperties() { + // Sizes of the frame data and extra video buffer + if (_features & kFeaturesDataSize) { + _frameDataSize = _stream->readUint16LE(); + if (_frameDataSize == 0) { + _frameDataSize = _stream->readUint32LE(); + _videoBufferSize = _stream->readUint32LE(); + } else + _videoBufferSize = _stream->readUint16LE(); + } else { + _frameDataSize = _width * _height + 500; + if (!(_flags & 0x100) || (_flags & 0x1000)) + _videoBufferSize = _frameDataSize; + } + + // Allocating working memory + _frameData = new byte[_frameDataSize + 500]; + memset(_frameData, 0, _frameDataSize + 500); + + _videoBuffer = new byte[_videoBufferSize + 500]; + memset(_videoBuffer, 0, _videoBufferSize + 500); + + return true; +} + +bool IMDDecoder::assessAudioProperties() { + if (_features & kFeaturesSound) { + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); + _soundSlicesCount = _stream->readSint16LE(); + + if (_soundFreq < 0) + _soundFreq = -_soundFreq; + + if (_soundSlicesCount < 0) + _soundSlicesCount = -_soundSlicesCount - 1; + + if (_soundSlicesCount > 40) { + warning("IMDDecoder::assessAudioProperties(): More than 40 sound slices found (%d)", _soundSlicesCount); + return false; + } + + _frameRate = Common::Rational(_soundFreq) / _soundSliceSize; + + _hasSound = true; + _soundStage = kSoundLoaded; + + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, false); + } + + return true; +} + +bool IMDDecoder::loadFrameTables(uint32 framePosPos, uint32 frameCoordsPos) { + // Positions table + if (_framePos) { + _stream->seek(framePosPos); + for (uint32 i = 0; i < _frameCount; i++) + _framePos[i] = _stream->readUint32LE(); + } + + // Coordinates table + if (_features & kFeaturesFrameCoords) { + _stream->seek(frameCoordsPos); + _frameCoords = new Coord[_frameCount]; + assert(_frameCoords); + for (uint32 i = 0; i < _frameCount; i++) { + _frameCoords[i].left = _stream->readSint16LE(); + _frameCoords[i].top = _stream->readSint16LE(); + _frameCoords[i].right = _stream->readSint16LE(); + _frameCoords[i].bottom = _stream->readSint16LE(); + } + } + + return true; } void IMDDecoder::close() { @@ -394,12 +678,43 @@ void IMDDecoder::close() { delete _stream; + delete[] _framePos; + delete[] _frameCoords; + + delete[] _frameData; + delete[] _videoBuffer; _stream = 0; + _version = 0; + + _stdX = -1; + _stdY = -1; + _stdWidth = -1; + _stdHeight = -1; + + _flags = 0; + + _firstFramePos = 0; + _framePos = 0; + _frameCoords = 0; + + _frameData = 0; + _frameDataSize = 0; + _frameDataLen = 0; + _videoBuffer = 0; _videoBufferSize = 0; + + _soundFlags = 0; + _soundFreq = 0; + _soundSliceSize = 0; + _soundSlicesCount = 0; + + _hasSound = false; + _soundEnabled = false; + _soundStage = kSoundNone; } bool IMDDecoder::isVideoLoaded() const { @@ -415,8 +730,6 @@ Surface *IMDDecoder::decodeNextFrame() { processFrame(); renderFrame(); - _curFrame++; - if (_curFrame == 0) _startTime = g_system->getMillis(); @@ -424,13 +737,18 @@ Surface *IMDDecoder::decodeNextFrame() { } void IMDDecoder::processFrame() { + // TODO + + _curFrame++; } void IMDDecoder::renderFrame() { _dirtyRects.clear(); // TODO + + _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); } PixelFormat IMDDecoder::getPixelFormat() const { -- cgit v1.2.3 From 6172fe8ea7cf8046e5048e56a512ad0f847ac324 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:42:30 +0000 Subject: VIDEO/GOB: Implement IMD frame decoding Rendering the frame video data is still stubbed out. svn-id: r51866 --- graphics/video/coktel_decoder.cpp | 171 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 7 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 2b4b7182f3..ebb1c9492b 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -206,6 +206,10 @@ Common::Rational CoktelDecoder::getFrameRate() const { return _frameRate; } +inline void CoktelDecoder::unsignedToSigned(byte *buffer, int length) { + while (length-- > 0) *buffer++ ^= 0x80; +} + PreIMDDecoder::PreIMDDecoder(uint16 width, uint16 height, Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), @@ -638,8 +642,9 @@ bool IMDDecoder::assessAudioProperties() { _frameRate = Common::Rational(_soundFreq) / _soundSliceSize; - _hasSound = true; - _soundStage = kSoundLoaded; + _hasSound = true; + _soundEnabled = true; + _soundStage = kSoundLoaded; _audioStream = Audio::makeQueuingAudioStream(_soundFreq, false); } @@ -737,18 +742,170 @@ Surface *IMDDecoder::decodeNextFrame() { } void IMDDecoder::processFrame() { + _curFrame++; - // TODO + _dirtyRects.clear(); + + _paletteDirty = false; + + uint32 cmd = 0; + bool hasNextCmd = false; + bool startSound = false; + + do { + calcFrameCoords(_curFrame); + + cmd = _stream->readUint16LE(); + + if ((cmd & kCommandBreakMask) == kCommandBreak) { + // Flow control + + if (cmd == kCommandBreak) { + _stream->skip(2); + cmd = _stream->readUint16LE(); + } + + // Break + if (cmd == kCommandBreakSkip0) { + continue; + } else if (cmd == kCommandBreakSkip16) { + cmd = _stream->readUint16LE(); + _stream->skip(cmd); + continue; + } else if (cmd == kCommandBreakSkip32) { + cmd = _stream->readUint32LE(); + _stream->skip(cmd); + continue; + } + } + + // Audio + if (_soundStage != kSoundNone) { + if (cmd == kCommandNextSound) { + + nextSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); + + } else if (cmd == kCommandStartSound) { + + startSound = initialSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); + + } else + emptySoundSlice(hasNextCmd); + } + + // Set palette + if (cmd == kCommandPalette) { + _stream->skip(2); + + _paletteDirty = true; + + _stream->read(_palette, 768); + cmd = _stream->readUint16LE(); + } + + hasNextCmd = false; + + if (cmd == kCommandJump) { + // Jump to frame + + int16 frame = _stream->readSint16LE(); + if (_framePos) { + _curFrame = frame - 1; + _stream->seek(_framePos[frame]); + + hasNextCmd = true; + } + + } else if (cmd == kCommandVideoData) { + videoData(_stream->readUint32LE() + 2); + + } else if (cmd != 0) + videoData(cmd + 2); + else + _dirtyRects.pop_back(); + + } while (hasNextCmd); + + if (startSound && _soundEnabled) { + _mixer->playStream(_soundType, &_audioHandle, _audioStream); + _soundStage = kSoundPlaying; + } + + if ((_curFrame >= (int32)(_frameCount - 1)) && (_soundStage == kSoundPlaying)) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + _audioStream = 0; + _soundStage = kSoundNone; + } - _curFrame++; } -void IMDDecoder::renderFrame() { - _dirtyRects.clear(); +void IMDDecoder::calcFrameCoords(uint32 frame) { + if (frame == 0) + _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); + else if (_frameCoords && ((_frameCoords[frame].left != -1))) + _dirtyRects.push_back(Common::Rect(_frameCoords[frame].left , _frameCoords[frame].top, + _frameCoords[frame].right + 1, _frameCoords[frame].bottom + 1)); + else if (_stdX != -1) + _dirtyRects.push_back(Common::Rect(_stdX, _stdY, _stdX + _stdWidth, _stdY + _stdHeight)); + else + _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); +} +void IMDDecoder::videoData(uint32 size) { + _stream->read(_frameData, size); + _frameDataLen = size; + + renderFrame(); +} + +void IMDDecoder::renderFrame() { // TODO +} - _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); +void IMDDecoder::nextSoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) { + _stream->skip(_soundSliceSize); + return; + } + + byte *soundBuf = (byte *)malloc(_soundSliceSize); + + _stream->read(soundBuf, _soundSliceSize); + unsignedToSigned(soundBuf, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); +} + +bool IMDDecoder::initialSoundSlice(bool hasNextCmd) { + int dataLength = _soundSliceSize * _soundSlicesCount; + + if (hasNextCmd || !_soundEnabled) { + _stream->skip(dataLength); + return false; + } + + byte *soundBuf = (byte *)malloc(dataLength); + + _stream->read(soundBuf, dataLength); + unsignedToSigned(soundBuf, dataLength); + + _audioStream->queueBuffer(soundBuf, dataLength, DisposeAfterUse::YES, 0); + + return _soundStage == kSoundLoaded; +} + +void IMDDecoder::emptySoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) + return; + + byte *soundBuf = (byte *)malloc(_soundSliceSize); + + memset(soundBuf, 0, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); } PixelFormat IMDDecoder::getPixelFormat() const { -- cgit v1.2.3 From fecbdf60a98d2cbadfc26b406b7076ae74424066 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:42:59 +0000 Subject: VIDEO/GOB: Implement IMD frame rendering svn-id: r51867 --- graphics/video/coktel_decoder.cpp | 335 +++++++++++++++++++++++++++++++++++++- 1 file changed, 326 insertions(+), 9 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index ebb1c9492b..c8a934d5bb 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -121,6 +121,10 @@ const Common::List &CoktelDecoder::getDirtyRects() const { return _dirtyRects; } +bool CoktelDecoder::hasPalette() const { + return (_features & kFeaturesPalette) != 0; +} + bool CoktelDecoder::hasSound() const { return _hasSound; } @@ -202,6 +206,235 @@ bool CoktelDecoder::hasDirtyPalette() const { return (_features & kFeaturesPalette) && _paletteDirty; } +void CoktelDecoder::deLZ77(byte *dest, byte *src) { + int i; + byte buf[4370]; + uint16 chunkLength; + uint32 frameLength; + uint16 bufPos1; + uint16 bufPos2; + uint16 tmp; + uint8 chunkBitField; + uint8 chunkCount; + bool mode; + + frameLength = READ_LE_UINT32(src); + src += 4; + + if ((READ_LE_UINT16(src) == 0x1234) && (READ_LE_UINT16(src + 2) == 0x5678)) { + src += 4; + bufPos1 = 273; + mode = 1; // 123Ch (cmp al, 12h) + } else { + bufPos1 = 4078; + mode = 0; // 275h (jnz +2) + } + + memset(buf, 32, bufPos1); + chunkCount = 1; + chunkBitField = 0; + + while (frameLength > 0) { + chunkCount--; + if (chunkCount == 0) { + tmp = *src++; + chunkCount = 8; + chunkBitField = tmp; + } + if (chunkBitField % 2) { + chunkBitField >>= 1; + buf[bufPos1] = *src; + *dest++ = *src++; + bufPos1 = (bufPos1 + 1) % 4096; + frameLength--; + continue; + } + chunkBitField >>= 1; + + tmp = READ_LE_UINT16(src); + src += 2; + chunkLength = ((tmp & 0xF00) >> 8) + 3; + + if ((mode && ((chunkLength & 0xFF) == 0x12)) || + (!mode && (chunkLength == 0))) + chunkLength = *src++ + 0x12; + + bufPos2 = (tmp & 0xFF) + ((tmp >> 4) & 0x0F00); + if (((tmp + chunkLength) >= 4096) || + ((chunkLength + bufPos1) >= 4096)) { + + for (i = 0; i < chunkLength; i++, dest++) { + *dest = buf[bufPos2]; + buf[bufPos1] = buf[bufPos2]; + bufPos1 = (bufPos1 + 1) % 4096; + bufPos2 = (bufPos2 + 1) % 4096; + } + + } else if (((tmp + chunkLength) < bufPos1) || + ((chunkLength + bufPos1) < bufPos2)) { + + memcpy(dest, buf + bufPos2, chunkLength); + memmove(buf + bufPos1, buf + bufPos2, chunkLength); + + dest += chunkLength; + bufPos1 += chunkLength; + bufPos2 += chunkLength; + + } else { + + for (i = 0; i < chunkLength; i++, dest++, bufPos1++, bufPos2++) { + *dest = buf[bufPos2]; + buf[bufPos1] = buf[bufPos2]; + } + + } + frameLength -= chunkLength; + + } +} + +// A whole, completely filled block +void CoktelDecoder::renderBlockWhole(const byte *src) { + Common::Rect &rect = _dirtyRects.back(); + Common::Rect drawRect = rect; + + drawRect.clip(_surface.w, _surface.h); + + byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + for (int i = 0; i < drawRect.height(); i++) { + memcpy(dst, src, drawRect.width()); + + src += rect.width(); + dst += _surface.pitch; + } +} + +// A quarter-wide whole, completely filled block +void CoktelDecoder::renderBlockWhole4X(const byte *src) { + Common::Rect &rect = _dirtyRects.back(); + Common::Rect drawRect = rect; + + drawRect.clip(_surface.w, _surface.h); + + byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + for (int i = 0; i < drawRect.height(); i++) { + byte *dstRow = dst; + const byte *srcRow = src; + + int16 count = drawRect.width(); + while (count >= 0) { + memset(dstRow, *srcRow, MIN(count, 4)); + + count -= 4; + dstRow += 4; + srcRow += 1; + } + + src += rect.width() / 4; + dst += _surface.pitch; + } +} + +// A half-high whole, completely filled block +void CoktelDecoder::renderBlockWhole2Y(const byte *src) { + warning("renderBlockWhole2Y"); + + Common::Rect &rect = _dirtyRects.back(); + Common::Rect drawRect = rect; + + drawRect.clip(_surface.w, _surface.h); + + int16 height = drawRect.height(); + + byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + while (height > 1) { + memcpy(dst , src, drawRect.width()); + memcpy(dst + _surface.pitch, src, drawRect.width()); + + height -= 2; + src += rect.width(); + dst += 2 * _surface.pitch; + } + + if (height == 1) + memcpy(dst, src, drawRect.width()); +} + +// A sparse block +void CoktelDecoder::renderBlockSparse(const byte *src) { + Common::Rect &rect = _dirtyRects.back(); + Common::Rect drawRect = rect; + + drawRect.clip(_surface.w, _surface.h); + + byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + for (int i = 0; i < drawRect.height(); i++) { + byte *dstRow = dst; + int16 pixWritten = 0; + + while (pixWritten < rect.width()) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, rect.width() - pixWritten); + copyCount = CLIP(drawRect.width() - pixWritten, 0, pixCount); + memcpy(dstRow, src, copyCount); + + pixWritten += pixCount; + dstRow += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + dstRow += pixCount + 1; + } + + } + + dst += _surface.pitch; + } +} + +// A half-high sparse block +void CoktelDecoder::renderBlockSparse2Y(const byte *src) { + warning("renderBlockSparse2Y"); + + Common::Rect &rect = _dirtyRects.back(); + Common::Rect drawRect = rect; + + drawRect.clip(_surface.w, _surface.h); + + byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + for (int i = 0; i < drawRect.height(); i += 2) { + byte *dstRow = dst; + int16 pixWritten = 0; + + while (pixWritten < rect.width()) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, rect.width() - pixWritten); + copyCount = CLIP(drawRect.width() - pixWritten, 0, pixCount); + memcpy(dstRow , src, pixCount); + memcpy(dstRow + _surface.pitch, src, pixCount); + + pixWritten += pixCount; + dstRow += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + dstRow += pixCount + 1; + } + + } + + dst += _surface.pitch; + } +} + Common::Rational CoktelDecoder::getFrameRate() const { return _frameRate; } @@ -471,7 +704,7 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { return true; } else { - warning("Imd::seek(): Frame %d is not directly accessible", frame + 1); + warning("IMDDecoder::seek(): Frame %d is not directly accessible", frame + 1); return false; } @@ -482,6 +715,37 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { return true; } +void IMDDecoder::setXY(uint16 x, uint16 y) { + // Adjusting the standard coordinates + if (_stdX != -1) { + if (x != 0xFFFF) + _stdX = _stdX - _x + x; + if (y != 0xFFFF) + _stdY = _stdY - _y + y; + } + + // Going through the coordinate table as well + if (_frameCoords) { + for (uint32 i = 0; i < _frameCount; i++) { + if (_frameCoords[i].left != -1) { + if (x != 0xFFFF) { + _frameCoords[i].left = _frameCoords[i].left - _x + x; + _frameCoords[i].right = _frameCoords[i].right - _x + x; + } + if (y != 0xFFFF) { + _frameCoords[i].top = _frameCoords[i].top - _y + y; + _frameCoords[i].bottom = _frameCoords[i].bottom - _y + y; + } + } + } + } + + if (x != 0xFFFF) + _x = x; + if (y != 0xFFFF) + _y = y; +} + bool IMDDecoder::load(Common::SeekableReadStream &stream) { close(); @@ -559,7 +823,7 @@ bool IMDDecoder::loadCoordinates() { if (_version >= 3) { uint16 count = _stream->readUint16LE(); if (count > 1) { - warning("IMD: More than one standard coordinate quad found (%d)", count ); + warning("IMDDecoder::loadCoordinates(): More than one standard coordinate quad found (%d)", count); return false; } @@ -733,7 +997,6 @@ Surface *IMDDecoder::decodeNextFrame() { createSurface(); processFrame(); - renderFrame(); if (_curFrame == 0) _startTime = g_system->getMillis(); @@ -753,8 +1016,6 @@ void IMDDecoder::processFrame() { bool startSound = false; do { - calcFrameCoords(_curFrame); - cmd = _stream->readUint16LE(); if ((cmd & kCommandBreakMask) == kCommandBreak) { @@ -819,12 +1080,15 @@ void IMDDecoder::processFrame() { } } else if (cmd == kCommandVideoData) { + calcFrameCoords(_curFrame); + videoData(_stream->readUint32LE() + 2); - } else if (cmd != 0) + } else if (cmd != 0) { + calcFrameCoords(_curFrame); + videoData(cmd + 2); - else - _dirtyRects.pop_back(); + } } while (hasNextCmd); @@ -862,7 +1126,60 @@ void IMDDecoder::videoData(uint32 size) { } void IMDDecoder::renderFrame() { - // TODO + if (_dirtyRects.empty()) + return; + + Common::Rect &rect = _dirtyRects.back(); + + rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); + if (!rect.isValidRect() || rect.isEmpty()) { + _dirtyRects.pop_back(); + return; + } + + byte *dataPtr = _frameData; + + uint8 type = *dataPtr++; + + if (type & 0x10) { // Palette data + // One byte index + int index = *dataPtr++; + // 16 entries with each 3 bytes (RGB) + memcpy(_palette + index * 3, dataPtr, MIN((255 - index) * 3, 48)); + + dataPtr += 48; + type ^= 0x10; + + _paletteDirty = true; + } + + if (type & 0x80) { + // Frame data is compressed + + type &= 0x7F; + + if ((type == 2) && (rect.width() == _surface.w)) { + // Directly uncompress onto the video surface + deLZ77((byte *) _surface.pixels, dataPtr); + return; + } + + deLZ77(_videoBuffer, dataPtr); + + dataPtr = _videoBuffer; + } + + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (dataPtr); + else if (type == 0x02) + renderBlockWhole (dataPtr); + else if (type == 0x42) + renderBlockWhole4X (dataPtr); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (dataPtr); + else + renderBlockSparse2Y(dataPtr); } void IMDDecoder::nextSoundSlice(bool hasNextCmd) { -- cgit v1.2.3 From 2bcc02a27a5261fab4d769e3160ff490573a6be1 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:44:05 +0000 Subject: VIDEO/GOB: Fixing the frame rate for mult'd videos. In the gob engine, some videos are opened, and then played, at a later time, one frame at a time. In this case, we need to emulate the original's video player's behaviour, not calculating any "lag" for videos without sound. svn-id: r51870 --- graphics/video/coktel_decoder.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index c8a934d5bb..73bcc6a8d0 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -439,6 +439,13 @@ Common::Rational CoktelDecoder::getFrameRate() const { return _frameRate; } +uint32 CoktelDecoder::getTimeToNextFrame() const { + if (hasSound()) + return FixedRateVideoDecoder::getTimeToNextFrame(); + + return (Common::Rational(1000) / _frameRate).toInt(); +} + inline void CoktelDecoder::unsignedToSigned(byte *buffer, int length) { while (length-- > 0) *buffer++ ^= 0x80; } -- cgit v1.2.3 From 2296aad042aad1c9983b4a9bf2e187912c361407 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:44:33 +0000 Subject: VIDEO/GOB: Fix IMD playing Fix IMD playing for some fringe cases, especially when seeking beforehand. svn-id: r51871 --- graphics/video/coktel_decoder.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 73bcc6a8d0..4e5a8fe6e5 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -679,7 +679,7 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { else return false; - if ((frame < -1) || (((uint32) frame) >= _frameCount)) + if ((frame < -1) || (frame >= ((int32) _frameCount))) // Out of range return false; @@ -708,8 +708,6 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) processFrame(); - return true; - } else { warning("IMDDecoder::seek(): Frame %d is not directly accessible", frame + 1); return false; -- cgit v1.2.3 From 737ef0270718c2f7fb92784f337a308967d57ce3 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:46:34 +0000 Subject: VIDEO/GOB: Stub hasEmbeddedFile / getEmbeddedFile Stubbing CoktelDecoder::hasEmbeddedFile() and CoktelDecoder::getEmbeddedFile(), formerly hasExtraData/getExtraData. svn-id: r51875 --- graphics/video/coktel_decoder.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 4e5a8fe6e5..41d4664937 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -169,6 +169,14 @@ void CoktelDecoder::disableSound() { _audioStream = 0; } +bool CoktelDecoder::hasEmbeddedFile(const Common::String &fileName) const { + return false; +} + +Common::MemoryReadStream *CoktelDecoder::getEmbeddedFile(const Common::String &fileName) const { + return 0; +} + void CoktelDecoder::close() { disableSound(); freeSurface(); -- cgit v1.2.3 From 53b67deaf0332f598168af4cbccc61ff4e5944c3 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:47:25 +0000 Subject: VIDEO: Fix another IMD seek issue svn-id: r51877 --- graphics/video/coktel_decoder.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 41d4664937..c49f7480d2 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -716,6 +716,8 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) processFrame(); + return true; + } else { warning("IMDDecoder::seek(): Frame %d is not directly accessible", frame + 1); return false; -- cgit v1.2.3 From d1d772367b63a760e45ff270186215511fd936c6 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:49:15 +0000 Subject: VIDEO: Fix another IMD seek issue svn-id: r51881 --- graphics/video/coktel_decoder.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index c49f7480d2..dc27a92579 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -713,6 +713,9 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { } else if (restart && (_soundStage == kSoundNone)) { + _curFrame = 0; + _stream->seek(_firstFramePos); + for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) processFrame(); -- cgit v1.2.3 From 0cf837c1af4e55c32dff91ff88632cd0260858f5 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:50:35 +0000 Subject: VIDEO: renderBlockWhole2Y still works svn-id: r51884 --- graphics/video/coktel_decoder.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index dc27a92579..ca72f87808 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -345,8 +345,6 @@ void CoktelDecoder::renderBlockWhole4X(const byte *src) { // A half-high whole, completely filled block void CoktelDecoder::renderBlockWhole2Y(const byte *src) { - warning("renderBlockWhole2Y"); - Common::Rect &rect = _dirtyRects.back(); Common::Rect drawRect = rect; -- cgit v1.2.3 From 66aeee8ddc3affec0708e51c2ab78cad2f6b7440 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:51:33 +0000 Subject: VIDEO: Don't crash when sound is disabled :P svn-id: r51886 --- graphics/video/coktel_decoder.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index ca72f87808..15899fb13d 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1057,20 +1057,18 @@ void IMDDecoder::processFrame() { } // Audio - if (_soundStage != kSoundNone) { - if (cmd == kCommandNextSound) { + if (cmd == kCommandNextSound) { - nextSoundSlice(hasNextCmd); - cmd = _stream->readUint16LE(); + nextSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); - } else if (cmd == kCommandStartSound) { + } else if (cmd == kCommandStartSound) { - startSound = initialSoundSlice(hasNextCmd); - cmd = _stream->readUint16LE(); + startSound = initialSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); - } else - emptySoundSlice(hasNextCmd); - } + } else + emptySoundSlice(hasNextCmd); // Set palette if (cmd == kCommandPalette) { -- cgit v1.2.3 From a57e53c986f8a2d67902b5ed9079112b0f69260a Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:52:30 +0000 Subject: VIDEO: Add some comments, fix a style issue svn-id: r51888 --- graphics/video/coktel_decoder.cpp | 108 ++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 22 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 15899fb13d..28a34a34ab 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -50,9 +50,11 @@ CoktelDecoder::~CoktelDecoder() { void CoktelDecoder::setSurfaceMemory(void *mem, uint16 width, uint16 height, uint8 bpp) { freeSurface(); + // Sanity checks assert((width > 0) && (height > 0)); assert(bpp == getPixelFormat().bytesPerPixel); + // Create a surface over this memory _surface.w = width; _surface.h = height; _surface.pitch = width * bpp; @@ -308,7 +310,7 @@ void CoktelDecoder::renderBlockWhole(const byte *src) { drawRect.clip(_surface.w, _surface.h); - byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; for (int i = 0; i < drawRect.height(); i++) { memcpy(dst, src, drawRect.width()); @@ -324,7 +326,7 @@ void CoktelDecoder::renderBlockWhole4X(const byte *src) { drawRect.clip(_surface.w, _surface.h); - byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; for (int i = 0; i < drawRect.height(); i++) { byte *dstRow = dst; const byte *srcRow = src; @@ -352,7 +354,7 @@ void CoktelDecoder::renderBlockWhole2Y(const byte *src) { int16 height = drawRect.height(); - byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; while (height > 1) { memcpy(dst , src, drawRect.width()); memcpy(dst + _surface.pitch, src, drawRect.width()); @@ -373,7 +375,7 @@ void CoktelDecoder::renderBlockSparse(const byte *src) { drawRect.clip(_surface.w, _surface.h); - byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; for (int i = 0; i < drawRect.height(); i++) { byte *dstRow = dst; int16 pixWritten = 0; @@ -411,7 +413,7 @@ void CoktelDecoder::renderBlockSparse2Y(const byte *src) { drawRect.clip(_surface.w, _surface.h); - byte *dst = ((byte *) _surface.pixels) + (drawRect.top * _surface.pitch) + drawRect.left; + byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; for (int i = 0; i < drawRect.height(); i += 2) { byte *dstRow = dst; int16 pixWritten = 0; @@ -446,10 +448,19 @@ Common::Rational CoktelDecoder::getFrameRate() const { } uint32 CoktelDecoder::getTimeToNextFrame() const { - if (hasSound()) - return FixedRateVideoDecoder::getTimeToNextFrame(); + // If there is no audio, just return the static time between + // frames without any elaborate sync calculation. This is + // needed for the gob engine, since it has a lot of control + // between the videos and often plays just few frames out of + // the middle of a long video. - return (Common::Rational(1000) / _frameRate).toInt(); + if (!hasSound()) + return (Common::Rational(1000) / _frameRate).toInt(); + + // If there /is/ audio, we do need to keep video and audio + // in sync, though. + + return FixedRateVideoDecoder::getTimeToNextFrame(); } inline void CoktelDecoder::unsignedToSigned(byte *buffer, int length) { @@ -572,7 +583,10 @@ void PreIMDDecoder::processFrame() { frameSize--; if (cmd == 0) { - // Palette. Ignored by Fascination, though + // Palette. Ignored by Fascination, though. + + // NOTE: If we ever find another game using this format, + // palettes may need to be evaluated. _stream->skip(768); @@ -627,6 +641,7 @@ void PreIMDDecoder::processFrame() { _curFrame++; } +// Just a simple blit void PreIMDDecoder::renderFrame() { _dirtyRects.clear(); @@ -634,7 +649,7 @@ void PreIMDDecoder::renderFrame() { uint16 h = CLIP(_surface.h - _y, 0, _height); const byte *src = _videoBuffer; - byte *dst = (byte *) _surface.pixels + (_y * _surface.pitch) + _x; + byte *dst = (byte *)_surface.pixels + (_y * _surface.pitch) + _x; uint32 frameDataSize = _videoBufferSize; @@ -696,20 +711,25 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { // Try every possible way to find a file offset to that frame uint32 framePos = 0; if (frame == -1) { + // First frame, we know that position framePos = _firstFramePos; } else if (frame == 0) { + // Second frame, can be calculated from the first frame's position framePos = _firstFramePos; _stream->seek(framePos); framePos += _stream->readUint16LE() + 4; } else if (_framePos) { + // If we have an array of frame positions, use that framePos = _framePos[frame + 1]; } else if (restart && (_soundStage == kSoundNone)) { + // If we are asked to restart the video if necessary and have no + // audio to worry about, restart the video and run through the frames _curFrame = 0; _stream->seek(_firstFramePos); @@ -720,6 +740,8 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) { return true; } else { + // Not possible + warning("IMDDecoder::seek(): Frame %d is not directly accessible", frame + 1); return false; } @@ -798,7 +820,7 @@ bool IMDDecoder::load(Common::SeekableReadStream &stream) { _features |= kFeaturesPalette; // Palette - _stream->read((byte *) _palette, 768); + _stream->read((byte *)_palette, 768); _paletteDirty = true; @@ -1106,11 +1128,13 @@ void IMDDecoder::processFrame() { } while (hasNextCmd); + // Start the audio stream if necessary if (startSound && _soundEnabled) { _mixer->playStream(_soundType, &_audioHandle, _audioStream); _soundStage = kSoundPlaying; } + // End the audio stream if necessary if ((_curFrame >= (int32)(_frameCount - 1)) && (_soundStage == kSoundPlaying)) { _audioStream->finish(); _mixer->stopHandle(_audioHandle); @@ -1121,15 +1145,39 @@ void IMDDecoder::processFrame() { } void IMDDecoder::calcFrameCoords(uint32 frame) { - if (frame == 0) - _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); - else if (_frameCoords && ((_frameCoords[frame].left != -1))) - _dirtyRects.push_back(Common::Rect(_frameCoords[frame].left , _frameCoords[frame].top, - _frameCoords[frame].right + 1, _frameCoords[frame].bottom + 1)); - else if (_stdX != -1) - _dirtyRects.push_back(Common::Rect(_stdX, _stdY, _stdX + _stdWidth, _stdY + _stdHeight)); - else - _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); + int16 left, top, right, bottom; + + if (frame == 0) { + // First frame is always a full "keyframe" + + left = _x; + top = _y; + right = _x + _width; + bottom = _y + _height; + } else if (_frameCoords && ((_frameCoords[frame].left != -1))) { + // We have frame coordinates for that frame + + left = _frameCoords[frame].left; + top = _frameCoords[frame].top; + right = _frameCoords[frame].right + 1; + bottom = _frameCoords[frame].bottom + 1; + } else if (_stdX != -1) { + // We have standard coordinates + + left = _stdX; + top = _stdY; + right = _stdX + _stdWidth; + bottom = _stdY + _stdHeight; + } else { + // Otherwise, it must be a full "keyframe" + + left = _x; + top = _y; + right = _x + _width; + bottom = _y + _height; + } + + _dirtyRects.push_back(Common::Rect(left, top, right, bottom)); } void IMDDecoder::videoData(uint32 size) { @@ -1141,12 +1189,16 @@ void IMDDecoder::videoData(uint32 size) { void IMDDecoder::renderFrame() { if (_dirtyRects.empty()) + // Nothing to do return; + // The area for the frame Common::Rect &rect = _dirtyRects.back(); + // Clip it by the video's visible area rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); if (!rect.isValidRect() || rect.isEmpty()) { + // Result is empty => nothing to do _dirtyRects.pop_back(); return; } @@ -1155,7 +1207,9 @@ void IMDDecoder::renderFrame() { uint8 type = *dataPtr++; - if (type & 0x10) { // Palette data + if (type & 0x10) { + // Palette data + // One byte index int index = *dataPtr++; // 16 entries with each 3 bytes (RGB) @@ -1174,7 +1228,7 @@ void IMDDecoder::renderFrame() { if ((type == 2) && (rect.width() == _surface.w)) { // Directly uncompress onto the video surface - deLZ77((byte *) _surface.pixels, dataPtr); + deLZ77((byte *)_surface.pixels, dataPtr); return; } @@ -1198,10 +1252,14 @@ void IMDDecoder::renderFrame() { void IMDDecoder::nextSoundSlice(bool hasNextCmd) { if (hasNextCmd || !_soundEnabled) { + // Skip sound + _stream->skip(_soundSliceSize); return; } + // Read, convert, queue + byte *soundBuf = (byte *)malloc(_soundSliceSize); _stream->read(soundBuf, _soundSliceSize); @@ -1214,10 +1272,14 @@ bool IMDDecoder::initialSoundSlice(bool hasNextCmd) { int dataLength = _soundSliceSize * _soundSlicesCount; if (hasNextCmd || !_soundEnabled) { + // Skip sound + _stream->skip(dataLength); return false; } + // Read, convert, queue + byte *soundBuf = (byte *)malloc(dataLength); _stream->read(soundBuf, dataLength); @@ -1232,6 +1294,8 @@ void IMDDecoder::emptySoundSlice(bool hasNextCmd) { if (hasNextCmd || !_soundEnabled) return; + // Create an empty sound buffer and queue it + byte *soundBuf = (byte *)malloc(_soundSliceSize); memset(soundBuf, 0, _soundSliceSize); -- cgit v1.2.3 From 6ebec969c57029e8191689589237c1c5dd9917ee Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:52:59 +0000 Subject: VIDEO: Fix deLZ77'ing video data directly onto the video surface svn-id: r51890 --- graphics/video/coktel_decoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 28a34a34ab..629e01c2b5 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1226,9 +1226,9 @@ void IMDDecoder::renderFrame() { type &= 0x7F; - if ((type == 2) && (rect.width() == _surface.w)) { + if ((type == 2) && (rect.width() == _surface.w) && (_x == 0)) { // Directly uncompress onto the video surface - deLZ77((byte *)_surface.pixels, dataPtr); + deLZ77((byte *)_surface.pixels + (_y * _surface.pitch), dataPtr); return; } -- cgit v1.2.3 From 3c5e02900f6397add6365bea819987bf9d6e89e6 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:53:55 +0000 Subject: VIDEO: Add getSurface svn-id: r51892 --- graphics/video/coktel_decoder.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 629e01c2b5..8e8c6f9e93 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -71,6 +71,13 @@ void CoktelDecoder::setSurfaceMemory() { _ownSurface = true; } +const Surface *CoktelDecoder::getSurface() const { + if (!isVideoLoaded()) + return 0; + + return &_surface; +} + bool CoktelDecoder::hasSurface() { return _surface.pixels != 0; } -- cgit v1.2.3 From 1f630094268a76fd73e7a1f01e725aef2aa61e8e Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:56:04 +0000 Subject: VIDEO/GOB: Stubb VMDDecoder svn-id: r51896 --- graphics/video/coktel_decoder.cpp | 96 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 8e8c6f9e93..e79ea8c45d 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1314,6 +1314,102 @@ PixelFormat IMDDecoder::getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } + +VMDDecoder::VMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + _stream(0), _videoBuffer(0), _videoBufferSize(0) { + +} + +VMDDecoder::~VMDDecoder() { + close(); +} + +bool VMDDecoder::seek(int32 frame, int whence, bool restart) { + if (!isVideoLoaded()) + // Nothing to do + return false; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _frameCount - frame - 1; + else if (whence == SEEK_SET) + frame--; + else + return false; + + if ((frame < -1) || (((uint32) frame) >= _frameCount)) + // Out of range + return false; + + if (frame == _curFrame) + // Nothing to do + return true; + + // TODO + + return true; +} + +bool VMDDecoder::load(Common::SeekableReadStream &stream) { + close(); + + _stream = &stream; + + _stream->seek(0); + + warning("TODO: VMDDecoder::load()"); + + return false; +} + +void VMDDecoder::close() { + reset(); + + CoktelDecoder::close(); + + delete _stream; + + delete[] _videoBuffer; + + _stream = 0; + + _videoBuffer = 0; + _videoBufferSize = 0; +} + +bool VMDDecoder::isVideoLoaded() const { + return _stream != 0; +} + +Surface *VMDDecoder::decodeNextFrame() { + if (!isVideoLoaded() || endOfVideo()) + return 0; + + createSurface(); + + processFrame(); + renderFrame(); + + if (_curFrame == 0) + _startTime = g_system->getMillis(); + + return &_surface; +} + +void VMDDecoder::processFrame() { + _curFrame++; +} + +// Just a simple blit +void VMDDecoder::renderFrame() { +} + +PixelFormat VMDDecoder::getPixelFormat() const { + return PixelFormat::createFormatCLUT8(); +} + } // End of namespace Graphics #endif // GRAPHICS_VIDEO_COKTELDECODER_H -- cgit v1.2.3 From d081c2e20f5eb5c55de4aa6722b9ae1f57648425 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:56:29 +0000 Subject: VIDEO: Move the frame calculation out of seek() svn-id: r51897 --- graphics/video/coktel_decoder.cpp | 73 ++++++++++++++------------------------- 1 file changed, 25 insertions(+), 48 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index e79ea8c45d..414ea2c72b 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -47,6 +47,28 @@ CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundT CoktelDecoder::~CoktelDecoder() { } +bool CoktelDecoder::evaluateSeekFrame(int32 &frame, int whence) const { + if (!isVideoLoaded()) + // Nothing to do + return false; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _frameCount - frame - 1; + else if (whence == SEEK_SET) + frame--; + else + return false; + + if ((frame < -1) || (frame >= ((int32) _frameCount))) + // Out of range + return false; + + return true; +} + void CoktelDecoder::setSurfaceMemory(void *mem, uint16 width, uint16 height, uint8 bpp) { freeSurface(); @@ -488,22 +510,7 @@ PreIMDDecoder::~PreIMDDecoder() { } bool PreIMDDecoder::seek(int32 frame, int whence, bool restart) { - if (!isVideoLoaded()) - // Nothing to do - return false; - - // Find the frame to which to seek - if (whence == SEEK_CUR) - frame += _curFrame; - else if (whence == SEEK_END) - frame = _frameCount - frame - 1; - else if (whence == SEEK_SET) - frame--; - else - return false; - - if ((frame < -1) || (((uint32) frame) >= _frameCount)) - // Out of range + if (!evaluateSeekFrame(frame, whence)) return false; if (frame == _curFrame) @@ -693,22 +700,7 @@ IMDDecoder::~IMDDecoder() { } bool IMDDecoder::seek(int32 frame, int whence, bool restart) { - if (!isVideoLoaded()) - // Nothing to do - return false; - - // Find the frame to which to seek - if (whence == SEEK_CUR) - frame += _curFrame; - else if (whence == SEEK_END) - frame = _frameCount - frame - 1; - else if (whence == SEEK_SET) - frame--; - else - return false; - - if ((frame < -1) || (frame >= ((int32) _frameCount))) - // Out of range + if (!evaluateSeekFrame(frame, whence)) return false; if (frame == _curFrame) @@ -1325,22 +1317,7 @@ VMDDecoder::~VMDDecoder() { } bool VMDDecoder::seek(int32 frame, int whence, bool restart) { - if (!isVideoLoaded()) - // Nothing to do - return false; - - // Find the frame to which to seek - if (whence == SEEK_CUR) - frame += _curFrame; - else if (whence == SEEK_END) - frame = _frameCount - frame - 1; - else if (whence == SEEK_SET) - frame--; - else - return false; - - if ((frame < -1) || (((uint32) frame) >= _frameCount)) - // Out of range + if (!evaluateSeekFrame(frame, whence)) return false; if (frame == _curFrame) -- cgit v1.2.3 From 41f5d7812836f3c60532802780d93d5545b2258d Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:56:58 +0000 Subject: VIDEO: Implement VMD loading svn-id: r51898 --- graphics/video/coktel_decoder.cpp | 433 +++++++++++++++++++++++++++++++++++++- 1 file changed, 430 insertions(+), 3 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 414ea2c72b..fac32e55ff 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -24,11 +24,15 @@ */ #include "graphics/video/coktel_decoder.h" +#include "graphics/video/codecs/codec.h" +#include "graphics/video/codecs/indeo3.h" #ifdef GRAPHICS_VIDEO_COKTELDECODER_H #include "sound/audiostream.h" +static const uint32 kVideoCodecIndeo3 = MKID_BE('iv32'); + namespace Graphics { CoktelDecoder::State::State() : flags(0), speechId(0) { @@ -1307,8 +1311,81 @@ PixelFormat IMDDecoder::getPixelFormat() const { } +VMDDecoder::File::File() { + offset = 0; + size = 0; + realSize = 0; +} + + +VMDDecoder::Part::Part() { + type = kPartTypeSeparator; + field_1 = 0; + field_E = 0; + size = 0; + left = 0; + top = 0; + right = 0; + bottom = 0; + id = 0; + flags = 0; +} + + +VMDDecoder::Frame::Frame() { + parts = 0; + offset = 0; +} + +VMDDecoder::Frame::~Frame() { + delete[] parts; +} + + +const uint16 VMDDecoder::_tableDPCM[128] = { + 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, + 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, + 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, + 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, + 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280, + 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0, + 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, + 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, + 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0, + 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480, + 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700, + 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, + 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 +}; + +const int32 VMDDecoder::_tableADPCM[] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767, 0 +}; + +const int32 VMDDecoder::_tableADPCMStep[] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + VMDDecoder::VMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), - _stream(0), _videoBuffer(0), _videoBufferSize(0) { + _stream(0), _version(0), _flags(0), _frameInfoOffset(0), _partsPerFrame(0), _frames(0), + _soundFlags(0), _soundFreq(0), _soundSliceSize(0), _soundSlicesCount(0), + _soundBytesPerSample(0), _soundStereo(0), _soundHeaderSize(0), _soundDataSize(0), + _audioFormat(kAudioFormat8bitRaw), _hasVideo(false), _videoCodec(0), + _blitMode(0), _bytesPerPixel(0), _firstFramePos(0), + _frameData(0), _frameDataSize(0), _frameDataLen(0), + _videoBuffer(0), _videoBufferSize(0), _externalCodec(false), _codec(0) { } @@ -1336,9 +1413,320 @@ bool VMDDecoder::load(Common::SeekableReadStream &stream) { _stream->seek(0); - warning("TODO: VMDDecoder::load()"); + uint16 headerLength; + uint16 handle; - return false; + headerLength = _stream->readUint16LE(); + handle = _stream->readUint16LE(); + _version = _stream->readUint16LE(); + + // Version checking + if (headerLength == 50) { + // Newer version, used in Addy 5 upwards + warning("VMDDecoder::load(): TODO: Addy 5 videos"); + } else if (headerLength == 814) { + // Old version + _features |= kFeaturesPalette; + } else { + warning("VMDDecoder::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version); + close(); + return false; + } + + _frameCount = _stream->readUint16LE(); + + _defaultX = _stream->readSint16LE(); + _defaultY = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + + _x = _defaultX; + _y = _defaultY; + + if ((_width != 0) && (_height != 0)) { + + _hasVideo = true; + _features |= kFeaturesVideo; + + } else + _hasVideo = false; + + _bytesPerPixel = 1; + if (_version & 4) + _bytesPerPixel = handle + 1; + + if (_bytesPerPixel != 1) { + warning("TODO: _bytesPerPixel = %d", _bytesPerPixel); + close(); + return false; + } + + if (_bytesPerPixel > 3) { + warning("VMDDecoder::load(): Requested %d bytes per pixel (%d, %d, %d)", + _bytesPerPixel, headerLength, handle, _version); + close(); + return false; + } + + _flags = _stream->readUint16LE(); + + _partsPerFrame = _stream->readUint16LE(); + _firstFramePos = _stream->readUint32LE(); + + _videoCodec = _stream->readUint32BE(); + + if (_features & kFeaturesPalette) + _stream->read((byte *)_palette, 768); + + _frameDataSize = _stream->readUint32LE(); + _videoBufferSize = _stream->readUint32LE(); + + if (_hasVideo) { + if (!assessVideoProperties()) { + close(); + return false; + } + } + + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); + _soundSlicesCount = _stream->readSint16LE(); + _soundFlags = _stream->readUint16LE(); + + _hasSound = (_soundFreq != 0); + + if (_hasSound) { + if (!assessAudioProperties()) { + close(); + return false; + } + } else + _frameRate = 12; + + _frameInfoOffset = _stream->readUint32LE(); + + int numFiles; + if (!readFrameTable(numFiles)) { + close(); + return false; + } + + _stream->seek(_firstFramePos); + + if (numFiles == 0) + return true; + + _files.reserve(numFiles); + if (!readFiles()) { + close(); + return false; + } + + _stream->seek(_firstFramePos); + return true; +} + +bool VMDDecoder::assessVideoProperties() { + if ((_version & 2) && !(_version & 8)) { + _externalCodec = true; + _frameDataSize = _videoBufferSize = 0; + } else + _externalCodec = false; + + if (_externalCodec) { + if (_videoCodec == kVideoCodecIndeo3) { +#ifdef USE_INDEO3 + _codec = new Indeo3Decoder(_width, _height); +#else + warning("VMDDecoder::assessVideoProperties(): Indeo3 decoder not compiled in"); +#endif + } else { + char *fourcc = (char *) &_videoCodec; + + warning("VMDDecoder::assessVideoProperties(): Unknow video codec FourCC \'%c%c%c%c\'", + fourcc[3], fourcc[2], fourcc[1], fourcc[0]); + return false; + } + } + + if (_externalCodec) + _blitMode = 0; + else if (_bytesPerPixel == 1) + _blitMode = 0; + else if ((_bytesPerPixel == 2) || (_bytesPerPixel == 3)) { + int n = (_flags & 0x80) ? 2 : 3; + + _blitMode = n - 1; + _bytesPerPixel = n; + } + + if (_hasVideo) { + if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) + _frameDataSize = _width * _height + 1000; + if ((_videoBufferSize == 0) || (_videoBufferSize > 1048576)) + _videoBufferSize = _frameDataSize; + + _frameData = new byte[_frameDataSize]; + memset(_frameData, 0, _frameDataSize); + + _videoBuffer = new byte[_videoBufferSize]; + memset(_videoBuffer, 0, _videoBufferSize); + } + + return true; +} + +bool VMDDecoder::assessAudioProperties() { + bool supportedFormat = true; + + _features |= kFeaturesSound; + + _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0); + + if (_soundSliceSize < 0) { + _soundBytesPerSample = 2; + _soundSliceSize = -_soundSliceSize; + + if (_soundFlags & 0x10) { + _audioFormat = kAudioFormat16bitADPCM; + _soundHeaderSize = 3; + _soundDataSize = _soundSliceSize >> 1; + + if (_soundStereo > 0) + supportedFormat = false; + + } else { + _audioFormat = kAudioFormat16bitDPCM; + _soundHeaderSize = 1; + _soundDataSize = _soundSliceSize; + + if (_soundStereo == 1) { + supportedFormat = false; + } else if (_soundStereo == 2) { + _soundDataSize = 2 * _soundDataSize + 2; + _soundHeaderSize = 4; + } + + } + } else { + _soundBytesPerSample = 1; + _audioFormat = kAudioFormat8bitRaw; + _soundHeaderSize = 0; + _soundDataSize = _soundSliceSize; + + if (_soundStereo > 0) + supportedFormat = false; + } + + if (!supportedFormat) { + warning("VMDDecoder::assessAudioProperties(): Unsupported audio format: %d bits, encoding %d, stereo %d", + _soundBytesPerSample * 8, _audioFormat, _soundStereo); + return false; + } + + _frameRate = Common::Rational(_soundFreq) / _soundSliceSize; + + _hasSound = true; + _soundEnabled = true; + _soundStage = kSoundLoaded; + + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); + + return true; +} + +bool VMDDecoder::readFrameTable(int &numFiles) { + numFiles = 0; + + _stream->seek(_frameInfoOffset); + _frames = new Frame[_frameCount]; + for (uint16 i = 0; i < _frameCount; i++) { + _frames[i].parts = new Part[_partsPerFrame]; + _stream->skip(2); // Unknown + _frames[i].offset = _stream->readUint32LE(); + } + + for (uint16 i = 0; i < _frameCount; i++) { + bool separator = false; + + for (uint16 j = 0; j < _partsPerFrame; j++) { + + _frames[i].parts[j].type = (PartType) _stream->readByte(); + _frames[i].parts[j].field_1 = _stream->readByte(); + _frames[i].parts[j].size = _stream->readUint32LE(); + + if (_frames[i].parts[j].type == kPartTypeAudio) { + + _frames[i].parts[j].flags = _stream->readByte(); + _stream->skip(9); // Unknown + + } else if (_frames[i].parts[j].type == kPartTypeVideo) { + + _frames[i].parts[j].left = _stream->readUint16LE(); + _frames[i].parts[j].top = _stream->readUint16LE(); + _frames[i].parts[j].right = _stream->readUint16LE(); + _frames[i].parts[j].bottom = _stream->readUint16LE(); + _frames[i].parts[j].field_E = _stream->readByte(); + _frames[i].parts[j].flags = _stream->readByte(); + + } else if (_frames[i].parts[j].type == kPartTypeSpeech) { + _frames[i].parts[j].id = _stream->readUint16LE(); + // Speech text file name + _stream->skip(8); + } else if (_frames[i].parts[j].type == kPartTypeFile) { + if (!separator) + numFiles++; + _stream->skip(10); + } else if (_frames[i].parts[j].type == kPartTypeSeparator) { + separator = true; + _stream->skip(10); + } else { + // Unknow type + _stream->skip(10); + } + + } + } + + return true; +} + +bool VMDDecoder::readFiles() { + uint32 ssize = _stream->size(); + for (uint16 i = 0; i < _frameCount; i++) { + _stream->seek(_frames[i].offset); + + for (uint16 j = 0; j < _partsPerFrame; j++) { + if (_frames[i].parts[j].type == kPartTypeSeparator) + break; + + if (_frames[i].parts[j].type == kPartTypeFile) { + File file;; + + file.offset = _stream->pos() + 20; + file.size = _frames[i].parts[j].size; + file.realSize = _stream->readUint32LE(); + + char name[16]; + + _stream->read(name, 16); + name[15] = '\0'; + + file.name = name; + + _stream->skip(_frames[i].parts[j].size - 20); + + if ((((uint32) file.realSize) >= ssize) || (file.name[0] == 0)) + continue; + + _files.push_back(file); + + } else + _stream->skip(_frames[i].parts[j].size); + } + } + + return true; } void VMDDecoder::close() { @@ -1348,12 +1736,51 @@ void VMDDecoder::close() { delete _stream; + delete[] _frames; + + delete[] _frameData; delete[] _videoBuffer; + delete _codec; + + _files.clear(); + + _stream = 0; + _version = 0; + _flags = 0; + + _frameInfoOffset = 0; + _partsPerFrame = 0; + _frames = 0; + + _soundFlags = 0; + _soundFreq = 0; + _soundSliceSize = 0; + _soundSlicesCount = 0; + _soundBytesPerSample = 0; + _soundStereo = 0; + _soundHeaderSize = 0; + _soundDataSize = 0; + _audioFormat = kAudioFormat8bitRaw; + + _hasVideo = false; + _videoCodec = 0; + _blitMode = 0; + _bytesPerPixel = 0; + + _firstFramePos = 0; + + _frameData = 0; + _frameDataSize = 0; + _frameDataLen = 0; + _videoBuffer = 0; _videoBufferSize = 0; + + _externalCodec = false; + _codec = 0; } bool VMDDecoder::isVideoLoaded() const { -- cgit v1.2.3 From b13afba0536d16c3e37a2a887d582d9eeb42c8d1 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:57:27 +0000 Subject: VIDEO: Implement embedded file handling for VMDs svn-id: r51899 --- graphics/video/coktel_decoder.cpp | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index fac32e55ff..864904f252 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -204,6 +204,10 @@ void CoktelDecoder::disableSound() { _audioStream = 0; } +bool CoktelDecoder::hasEmbeddedFiles() const { + return false; +} + bool CoktelDecoder::hasEmbeddedFile(const Common::String &fileName) const { return false; } @@ -1814,6 +1818,55 @@ PixelFormat VMDDecoder::getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } +bool VMDDecoder::hasEmbeddedFiles() const { + return !_files.empty(); +} + +bool VMDDecoder::hasEmbeddedFile(const Common::String &fileName) const { + for (Common::Array::const_iterator file = _files.begin(); file != _files.end(); ++file) + if (!file->name.compareToIgnoreCase(fileName)) + return true; + + return false; +} + +Common::MemoryReadStream *VMDDecoder::getEmbeddedFile(const Common::String &fileName) const { + const File *file = 0; + + for (Common::Array::const_iterator it = _files.begin(); it != _files.end(); ++it) + if (!it->name.compareToIgnoreCase(fileName)) { + file = &*it; + break; + } + + if (!file) + return 0; + + if ((file->size - 20) != file->realSize) { + warning("VMDDecoder::getEmbeddedFile(): Sizes for \"%s\" differ! (%d, %d)", + fileName.c_str(), (file->size - 20), file->realSize); + return 0; + } + + if (!_stream->seek(file->offset)) { + warning("VMDDecoder::getEmbeddedFile(): Can't seek to offset %d to (file \"%s\")", + file->offset, fileName.c_str()); + return 0; + } + + byte *data = (byte *) malloc(file->realSize); + if (_stream->read(data, file->realSize) != file->realSize) { + free(data); + warning("VMDDecoder::getEmbeddedFile(): Couldn't read %d bytes (file \"%s\")", + file->realSize, fileName.c_str()); + } + + Common::MemoryReadStream *stream = + new Common::MemoryReadStream(data, file->realSize, DisposeAfterUse::YES); + + return stream; +} + } // End of namespace Graphics #endif // GRAPHICS_VIDEO_COKTELDECODER_H -- cgit v1.2.3 From f447ddb36a2bf8b923083e96b51279d72306d22a Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:58:00 +0000 Subject: VIDEO: Fix compilation after the VideoDecoder::load signature change in r51725 svn-id: r51900 --- graphics/video/coktel_decoder.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 864904f252..ed2ee2a46a 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -539,14 +539,14 @@ bool PreIMDDecoder::seek(int32 frame, int whence, bool restart) { return true; } -bool PreIMDDecoder::load(Common::SeekableReadStream &stream) { +bool PreIMDDecoder::load(Common::SeekableReadStream *stream) { // Since PreIMDs don't have any width and height values stored, // we need them to be specified in the constructor assert((_width > 0) && (_height > 0)); close(); - _stream = &stream; + _stream = stream; _stream->seek(0); @@ -791,10 +791,10 @@ void IMDDecoder::setXY(uint16 x, uint16 y) { _y = y; } -bool IMDDecoder::load(Common::SeekableReadStream &stream) { +bool IMDDecoder::load(Common::SeekableReadStream *stream) { close(); - _stream = &stream; + _stream = stream; uint16 handle; @@ -1410,10 +1410,10 @@ bool VMDDecoder::seek(int32 frame, int whence, bool restart) { return true; } -bool VMDDecoder::load(Common::SeekableReadStream &stream) { +bool VMDDecoder::load(Common::SeekableReadStream *stream) { close(); - _stream = &stream; + _stream = stream; _stream->seek(0); -- cgit v1.2.3 From 74bec1696b60ebe881972af24066fa34f37b9957 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:58:29 +0000 Subject: VIDEO: Implement VMDDecoder::seek() svn-id: r51901 --- graphics/video/coktel_decoder.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index ed2ee2a46a..67eea5ac4d 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1405,7 +1405,15 @@ bool VMDDecoder::seek(int32 frame, int whence, bool restart) { // Nothing to do return true; - // TODO + // Restart sound + if (_hasSound && (frame == 0) && (_soundStage == kSoundNone) && !_audioStream) { + _soundStage = kSoundLoaded; + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); + } + + // Seek + _stream->seek(_frames[frame].offset); + _curFrame = frame; return true; } -- cgit v1.2.3 From 0886f7e33cbb800c93caab79aedbef2d1cd64967 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:58:59 +0000 Subject: VIDEO: Implement VMD frame decoding svn-id: r51902 --- graphics/video/coktel_decoder.cpp | 151 +++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 4 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 67eea5ac4d..f68cf29228 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1681,7 +1681,7 @@ bool VMDDecoder::readFrameTable(int &numFiles) { _frames[i].parts[j].field_E = _stream->readByte(); _frames[i].parts[j].flags = _stream->readByte(); - } else if (_frames[i].parts[j].type == kPartTypeSpeech) { + } else if (_frames[i].parts[j].type == kPartTypeSubtitle) { _frames[i].parts[j].id = _stream->readUint16LE(); // Speech text file name _stream->skip(8); @@ -1806,7 +1806,6 @@ Surface *VMDDecoder::decodeNextFrame() { createSurface(); processFrame(); - renderFrame(); if (_curFrame == 0) _startTime = g_system->getMillis(); @@ -1816,10 +1815,154 @@ Surface *VMDDecoder::decodeNextFrame() { void VMDDecoder::processFrame() { _curFrame++; + + _dirtyRects.clear(); + + _paletteDirty = false; + + bool startSound = false; + + for (uint16 i = 0; i < _partsPerFrame; i++) { + uint32 pos = _stream->pos(); + + Part &part = _frames[_curFrame].parts[i]; + + if (part.type == kPartTypeAudio) { + + if (part.flags == 1) { + // Next sound slice data + + if (_soundEnabled) { + filledSoundSlice(part.size); + + if (_soundStage == kSoundLoaded) + startSound = true; + + } else + _stream->skip(part.size); + + } else if (part.flags == 2) { + // Initial sound data (all slices) + + if (_soundEnabled) { + uint32 mask = _stream->readUint32LE(); + filledSoundSlices(part.size - 4, mask); + + if (_soundStage == kSoundLoaded) + startSound = true; + + } else + _stream->skip(part.size); + + } else if (part.flags == 3) { + // Empty sound slice + + if (_soundEnabled) { + emptySoundSlice(_soundDataSize * _soundBytesPerSample); + + if (_soundStage == kSoundLoaded) + startSound = true; + } + + _stream->skip(part.size); + } else if (part.flags == 4) { + warning("Vmd::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size); + disableSound(); + _stream->skip(part.size); + } else { + warning("Vmd::processFrame(): Unknown sound type %d", part.flags); + _stream->skip(part.size); + } + + _stream->seek(pos + part.size); + + } else if ((part.type == kPartTypeVideo) && !_hasVideo) { + + warning("Vmd::processFrame(): Header claims there's no video, but video found (%d)", part.size); + _stream->skip(part.size); + + } else if ((part.type == kPartTypeVideo) && _hasVideo) { + + uint32 size = part.size; + + // New palette + if (part.flags & 2) { + uint8 index = _stream->readByte(); + uint8 count = _stream->readByte(); + + _stream->read(_palette + index * 3, (count + 1) * 3); + _stream->skip((255 - count) * 3); + + _paletteDirty = true; + + size -= (768 + 2); + } + + _stream->read(_frameData, size); + _frameDataLen = size; + + int16 l = part.left, t = part.top, r = part.right, b = part.bottom; + if (renderFrame(l, t, r, b)) + _dirtyRects.push_back(Common::Rect(l, t, r + 1, b + 1)); + + } else if (part.type == kPartTypeSeparator) { + + // Ignore + + } else if (part.type == kPartTypeFile) { + + // Ignore + _stream->skip(part.size); + + } else if (part.type == kPartType4) { + + // Unknown, ignore + _stream->skip(part.size); + + } else if (part.type == kPartTypeSubtitle) { + + // TODO: + // state.speechId = part.id; + // Always triggers when speech starts + _stream->skip(part.size); + + } else { + + warning("Vmd::processFrame(): Unknown frame part type %d, size %d (%d of %d)", + part.type, part.size, i + 1, _partsPerFrame); + + } + } + + if (startSound && _soundEnabled) { + if (_hasSound && _audioStream) { + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream); + _soundStage = kSoundPlaying; + } else + _soundStage = kSoundNone; + } + + if (((uint32)_curFrame == (_frameCount - 1)) && (_soundStage == 2)) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + _audioStream = 0; + _soundStage = kSoundNone; + } } -// Just a simple blit -void VMDDecoder::renderFrame() { +bool VMDDecoder::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { + // TODO + + return false; +} + +void VMDDecoder::emptySoundSlice(uint32 size) { +} + +void VMDDecoder::filledSoundSlice(uint32 size) { +} + +void VMDDecoder::filledSoundSlices(uint32 size, uint32 mask) { } PixelFormat VMDDecoder::getPixelFormat() const { -- cgit v1.2.3 From 6d31b1768500f03734d4e24dc13d088215071bde Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:59:28 +0000 Subject: VIDEO: Implement VMD sound svn-id: r51903 --- graphics/video/coktel_decoder.cpp | 240 +++++++++++++++++++++++++++++++++++++- 1 file changed, 236 insertions(+), 4 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index f68cf29228..8550325a29 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -30,6 +30,7 @@ #ifdef GRAPHICS_VIDEO_COKTELDECODER_H #include "sound/audiostream.h" +#include "sound/decoders/raw.h" static const uint32 kVideoCodecIndeo3 = MKID_BE('iv32'); @@ -1866,11 +1867,11 @@ void VMDDecoder::processFrame() { _stream->skip(part.size); } else if (part.flags == 4) { - warning("Vmd::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size); + warning("VMDDecoder::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size); disableSound(); _stream->skip(part.size); } else { - warning("Vmd::processFrame(): Unknown sound type %d", part.flags); + warning("VMDDecoder::processFrame(): Unknown sound type %d", part.flags); _stream->skip(part.size); } @@ -1878,7 +1879,7 @@ void VMDDecoder::processFrame() { } else if ((part.type == kPartTypeVideo) && !_hasVideo) { - warning("Vmd::processFrame(): Header claims there's no video, but video found (%d)", part.size); + warning("VMDDecoder::processFrame(): Header claims there's no video, but video found (%d)", part.size); _stream->skip(part.size); } else if ((part.type == kPartTypeVideo) && _hasVideo) { @@ -1928,7 +1929,7 @@ void VMDDecoder::processFrame() { } else { - warning("Vmd::processFrame(): Unknown frame part type %d, size %d (%d of %d)", + warning("VMDDecoder::processFrame(): Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame); } @@ -1957,12 +1958,243 @@ bool VMDDecoder::renderFrame(int16 &left, int16 &top, int16 &right, int16 &botto } void VMDDecoder::emptySoundSlice(uint32 size) { + byte *sound = soundEmpty(size); + + if (sound) { + uint32 flags = 0; + flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; + flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; + + _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); + } } void VMDDecoder::filledSoundSlice(uint32 size) { + byte *sound = 0; + if (_audioFormat == kAudioFormat8bitRaw) + sound = sound8bitRaw(size); + else if (_audioFormat == kAudioFormat16bitDPCM) + sound = sound16bitDPCM(size); + else if (_audioFormat == kAudioFormat16bitADPCM) + sound = sound16bitADPCM(size); + + if (sound) { + uint32 flags = 0; + flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; + flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; + + _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); + } } void VMDDecoder::filledSoundSlices(uint32 size, uint32 mask) { + bool fillInfo[32]; + + uint8 max; + uint8 n = evaluateMask(mask, fillInfo, max); + + int32 extraSize; + + extraSize = size - n * _soundDataSize; + + if (_soundSlicesCount > 32) + extraSize -= (_soundSlicesCount - 32) * _soundDataSize; + + if (n > 0) + extraSize /= n; + + for (uint8 i = 0; i < max; i++) + if (fillInfo[i]) + filledSoundSlice(_soundDataSize + extraSize); + else + emptySoundSlice(_soundDataSize * _soundBytesPerSample); + + if (_soundSlicesCount > 32) + filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize); +} + +uint8 VMDDecoder::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) { + max = MIN(_soundSlicesCount - 1, 31); + + uint8 n = 0; + for (int i = 0; i < max; i++) { + + if (!(mask & 1)) { + n++; + *fillInfo++ = true; + } else + *fillInfo++ = false; + + mask >>= 1; + } + + return n; +} + +byte *VMDDecoder::soundEmpty(uint32 &size) { + if (!_audioStream) + return 0; + + byte *soundBuf = (byte *)malloc(size); + memset(soundBuf, 0, size); + + return soundBuf; +} + +byte *VMDDecoder::sound8bitRaw(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + byte *soundBuf = (byte *)malloc(size); + _stream->read(soundBuf, size); + unsignedToSigned(soundBuf, size); + + return soundBuf; +} + +byte *VMDDecoder::sound16bitDPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + int32 init[2]; + + init[0] = _stream->readSint16LE(); + size -= 2; + + if (_soundStereo > 0) { + init[1] = _stream->readSint16LE(); + size -= 2; + } + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deDPCM(data, size, init); + + delete[] data; + + return sound; +} + +byte *VMDDecoder::sound16bitADPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + int32 init = _stream->readSint16LE(); + size -= 2; + + int32 index = _stream->readByte(); + size--; + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deADPCM(data, size, init, index); + + delete[] data; + + return sound; +} + +byte *VMDDecoder::deDPCM(const byte *data, uint32 &size, int32 init[2]) { + if (!data || (size == 0)) + return 0; + + int channels = (_soundStereo > 0) ? 2 : 1; + + uint32 inSize = size; + uint32 outSize = size + channels; + + int16 *out = (int16 *)malloc(outSize * 2); + byte *sound = (byte *) out; + + int channel = 0; + + for (int i = 0; i < channels; i++) { + *out++ = TO_BE_16(init[channel]); + + channel = (channel + 1) % channels; + } + + while (inSize-- > 0) { + if (*data & 0x80) + init[channel] -= _tableDPCM[*data++ & 0x7F]; + else + init[channel] += _tableDPCM[*data++]; + + init[channel] = CLIP(init[channel], -32768, 32767); + *out++ = TO_BE_16(init[channel]); + + channel = (channel + 1) % channels; + } + + size = outSize * 2; + return sound; +} + +// Yet another IMA ADPCM variant +byte *VMDDecoder::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) { + if (!data || (size == 0)) + return 0; + + uint32 outSize = size * 2; + + int16 *out = (int16 *)malloc(outSize * 2); + byte *sound = (byte *) out; + + index = CLIP(index, 0, 88); + + int32 predictor = _tableADPCM[index]; + + uint32 dataByte = 0; + bool newByte = true; + + size *= 2; + while (size -- > 0) { + byte code = 0; + + if (newByte) { + dataByte = *data++; + code = (dataByte >> 4) & 0xF; + } else + code = dataByte & 0xF; + + newByte = !newByte; + + index += _tableADPCMStep[code]; + index = CLIP(index, 0, 88); + + int32 value = predictor / 8; + + if (code & 4) + value += predictor; + if (code & 2) + value += predictor / 2; + if (code & 1) + value += predictor / 4; + + if (code & 8) + init -= value; + else + init += value; + + init = CLIP(init, -32768, 32767); + + predictor = _tableADPCM[index]; + + *out++ = TO_BE_16(init); + } + + size = outSize * 2; + return sound; } PixelFormat VMDDecoder::getPixelFormat() const { -- cgit v1.2.3 From 506c1e7d85773b2fe7e8a8d69edde7fc8f88fe10 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 00:59:58 +0000 Subject: VIDEO: Change the CoktelDecoder::renderBlock*'s signatures Change CoktelDecoder::renderBlock*() and IMDDecoder::renderFrame() to receive a Common::Rect instead of mucking about with _dirtyRects. svn-id: r51904 --- graphics/video/coktel_decoder.cpp | 188 +++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 96 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 8550325a29..567a51f8d1 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -342,34 +342,32 @@ void CoktelDecoder::deLZ77(byte *dest, byte *src) { } // A whole, completely filled block -void CoktelDecoder::renderBlockWhole(const byte *src) { - Common::Rect &rect = _dirtyRects.back(); - Common::Rect drawRect = rect; +void CoktelDecoder::renderBlockWhole(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; - drawRect.clip(_surface.w, _surface.h); + rect.clip(_surface.w, _surface.h); - byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; - for (int i = 0; i < drawRect.height(); i++) { - memcpy(dst, src, drawRect.width()); + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { + memcpy(dst, src, rect.width()); - src += rect.width(); + src += srcRect.width(); dst += _surface.pitch; } } // A quarter-wide whole, completely filled block -void CoktelDecoder::renderBlockWhole4X(const byte *src) { - Common::Rect &rect = _dirtyRects.back(); - Common::Rect drawRect = rect; +void CoktelDecoder::renderBlockWhole4X(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; - drawRect.clip(_surface.w, _surface.h); + rect.clip(_surface.w, _surface.h); - byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; - for (int i = 0; i < drawRect.height(); i++) { + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { byte *dstRow = dst; const byte *srcRow = src; - int16 count = drawRect.width(); + int16 count = rect.width(); while (count >= 0) { memset(dstRow, *srcRow, MIN(count, 4)); @@ -378,54 +376,52 @@ void CoktelDecoder::renderBlockWhole4X(const byte *src) { srcRow += 1; } - src += rect.width() / 4; + src += srcRect.width() / 4; dst += _surface.pitch; } } // A half-high whole, completely filled block -void CoktelDecoder::renderBlockWhole2Y(const byte *src) { - Common::Rect &rect = _dirtyRects.back(); - Common::Rect drawRect = rect; +void CoktelDecoder::renderBlockWhole2Y(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; - drawRect.clip(_surface.w, _surface.h); + rect.clip(_surface.w, _surface.h); - int16 height = drawRect.height(); + int16 height = rect.height(); - byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; while (height > 1) { - memcpy(dst , src, drawRect.width()); - memcpy(dst + _surface.pitch, src, drawRect.width()); + memcpy(dst , src, rect.width()); + memcpy(dst + _surface.pitch, src, rect.width()); height -= 2; - src += rect.width(); + src += srcRect.width(); dst += 2 * _surface.pitch; } if (height == 1) - memcpy(dst, src, drawRect.width()); + memcpy(dst, src, rect.width()); } // A sparse block -void CoktelDecoder::renderBlockSparse(const byte *src) { - Common::Rect &rect = _dirtyRects.back(); - Common::Rect drawRect = rect; +void CoktelDecoder::renderBlockSparse(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; - drawRect.clip(_surface.w, _surface.h); + rect.clip(_surface.w, _surface.h); - byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; - for (int i = 0; i < drawRect.height(); i++) { + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { byte *dstRow = dst; int16 pixWritten = 0; - while (pixWritten < rect.width()) { + while (pixWritten < srcRect.width()) { int16 pixCount = *src++; if (pixCount & 0x80) { // Data int16 copyCount; - pixCount = MIN((pixCount & 0x7F) + 1, rect.width() - pixWritten); - copyCount = CLIP(drawRect.width() - pixWritten, 0, pixCount); + pixCount = MIN((pixCount & 0x7F) + 1, srcRect.width() - pixWritten); + copyCount = CLIP(rect.width() - pixWritten, 0, pixCount); memcpy(dstRow, src, copyCount); pixWritten += pixCount; @@ -443,27 +439,26 @@ void CoktelDecoder::renderBlockSparse(const byte *src) { } // A half-high sparse block -void CoktelDecoder::renderBlockSparse2Y(const byte *src) { +void CoktelDecoder::renderBlockSparse2Y(const byte *src, Common::Rect &rect) { warning("renderBlockSparse2Y"); - Common::Rect &rect = _dirtyRects.back(); - Common::Rect drawRect = rect; + Common::Rect srcRect = rect; - drawRect.clip(_surface.w, _surface.h); + rect.clip(_surface.w, _surface.h); - byte *dst = (byte *)_surface.pixels + (drawRect.top * _surface.pitch) + drawRect.left; - for (int i = 0; i < drawRect.height(); i += 2) { + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i += 2) { byte *dstRow = dst; int16 pixWritten = 0; - while (pixWritten < rect.width()) { + while (pixWritten < srcRect.width()) { int16 pixCount = *src++; if (pixCount & 0x80) { // Data int16 copyCount; - pixCount = MIN((pixCount & 0x7F) + 1, rect.width() - pixWritten); - copyCount = CLIP(drawRect.width() - pixWritten, 0, pixCount); + pixCount = MIN((pixCount & 0x7F) + 1, srcRect.width() - pixWritten); + copyCount = CLIP(rect.width() - pixWritten, 0, pixCount); memcpy(dstRow , src, pixCount); memcpy(dstRow + _surface.pitch, src, pixCount); @@ -1124,14 +1119,25 @@ void IMDDecoder::processFrame() { } } else if (cmd == kCommandVideoData) { - calcFrameCoords(_curFrame); - videoData(_stream->readUint32LE() + 2); + _frameDataLen = _stream->readUint32LE() + 2; + _stream->read(_frameData, _frameDataLen); + + Common::Rect rect = calcFrameCoords(_curFrame); + + if (renderFrame(rect)) + _dirtyRects.push_back(rect); } else if (cmd != 0) { - calcFrameCoords(_curFrame); - videoData(cmd + 2); + _frameDataLen = cmd + 2; + _stream->read(_frameData, _frameDataLen); + + Common::Rect rect = calcFrameCoords(_curFrame); + + if (renderFrame(rect)) + _dirtyRects.push_back(rect); + } } while (hasNextCmd); @@ -1152,64 +1158,52 @@ void IMDDecoder::processFrame() { } -void IMDDecoder::calcFrameCoords(uint32 frame) { - int16 left, top, right, bottom; +Common::Rect IMDDecoder::calcFrameCoords(uint32 frame) { + Common::Rect rect; if (frame == 0) { // First frame is always a full "keyframe" - left = _x; - top = _y; - right = _x + _width; - bottom = _y + _height; + rect.left = _x; + rect.top = _y; + rect.right = _x + _width; + rect.bottom = _y + _height; } else if (_frameCoords && ((_frameCoords[frame].left != -1))) { // We have frame coordinates for that frame - left = _frameCoords[frame].left; - top = _frameCoords[frame].top; - right = _frameCoords[frame].right + 1; - bottom = _frameCoords[frame].bottom + 1; + rect.left = _frameCoords[frame].left; + rect.top = _frameCoords[frame].top; + rect.right = _frameCoords[frame].right + 1; + rect.bottom = _frameCoords[frame].bottom + 1; } else if (_stdX != -1) { // We have standard coordinates - left = _stdX; - top = _stdY; - right = _stdX + _stdWidth; - bottom = _stdY + _stdHeight; + rect.left = _stdX; + rect.top = _stdY; + rect.right = _stdX + _stdWidth; + rect.bottom = _stdY + _stdHeight; } else { // Otherwise, it must be a full "keyframe" - left = _x; - top = _y; - right = _x + _width; - bottom = _y + _height; + rect.left = _x; + rect.top = _y; + rect.right = _x + _width; + rect.bottom = _y + _height; } - _dirtyRects.push_back(Common::Rect(left, top, right, bottom)); + return rect; } -void IMDDecoder::videoData(uint32 size) { - _stream->read(_frameData, size); - _frameDataLen = size; - - renderFrame(); -} - -void IMDDecoder::renderFrame() { - if (_dirtyRects.empty()) - // Nothing to do - return; - - // The area for the frame - Common::Rect &rect = _dirtyRects.back(); +bool IMDDecoder::renderFrame(Common::Rect &rect) { + if (!rect.isValidRect()) + // Invalid rendering area + return false; - // Clip it by the video's visible area + // Clip the rendering area to the video's visible area rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); - if (!rect.isValidRect() || rect.isEmpty()) { + if (!rect.isValidRect() || rect.isEmpty()) // Result is empty => nothing to do - _dirtyRects.pop_back(); - return; - } + return false; byte *dataPtr = _frameData; @@ -1237,7 +1231,7 @@ void IMDDecoder::renderFrame() { if ((type == 2) && (rect.width() == _surface.w) && (_x == 0)) { // Directly uncompress onto the video surface deLZ77((byte *)_surface.pixels + (_y * _surface.pitch), dataPtr); - return; + return true; } deLZ77(_videoBuffer, dataPtr); @@ -1247,15 +1241,17 @@ void IMDDecoder::renderFrame() { // Evaluate the block type if (type == 0x01) - renderBlockSparse (dataPtr); + renderBlockSparse (dataPtr, rect); else if (type == 0x02) - renderBlockWhole (dataPtr); + renderBlockWhole (dataPtr, rect); else if (type == 0x42) - renderBlockWhole4X (dataPtr); + renderBlockWhole4X (dataPtr, rect); else if ((type & 0x0F) == 0x02) - renderBlockWhole2Y (dataPtr); + renderBlockWhole2Y (dataPtr, rect); else - renderBlockSparse2Y(dataPtr); + renderBlockSparse2Y(dataPtr, rect); + + return true; } void IMDDecoder::nextSoundSlice(bool hasNextCmd) { @@ -1902,9 +1898,9 @@ void VMDDecoder::processFrame() { _stream->read(_frameData, size); _frameDataLen = size; - int16 l = part.left, t = part.top, r = part.right, b = part.bottom; - if (renderFrame(l, t, r, b)) - _dirtyRects.push_back(Common::Rect(l, t, r + 1, b + 1)); + Common::Rect rect(part.left, part.top, part.right + 1, part.bottom + 1); + if (renderFrame(rect)) + _dirtyRects.push_back(rect); } else if (part.type == kPartTypeSeparator) { @@ -1951,7 +1947,7 @@ void VMDDecoder::processFrame() { } } -bool VMDDecoder::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { +bool VMDDecoder::renderFrame(Common::Rect &rect) { // TODO return false; -- cgit v1.2.3 From b796c39645276958d1e5dcd63ac13d5dde0ab7e8 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:00:23 +0000 Subject: VIDEO: Implement VMD frame rendering svn-id: r51905 --- graphics/video/coktel_decoder.cpp | 62 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 567a51f8d1..c70da85deb 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -476,6 +476,12 @@ void CoktelDecoder::renderBlockSparse2Y(const byte *src, Common::Rect &rect) { } } +void CoktelDecoder::renderBlockRLE(const byte *src, Common::Rect &rect) { + warning("renderBlockRLE"); + + // TODO +} + Common::Rational CoktelDecoder::getFrameRate() const { return _frameRate; } @@ -1948,9 +1954,61 @@ void VMDDecoder::processFrame() { } bool VMDDecoder::renderFrame(Common::Rect &rect) { - // TODO + if (!rect.isValidRect()) + // Invalid rendering area + return false; - return false; + // Clip the rendering area to the video's visible area + rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); + if (!rect.isValidRect() || rect.isEmpty()) + // Result is empty => nothing to do + return false; + + if (_externalCodec) { + // TODO + return false; + } + + if (_blitMode > 0) { + // TODO + return false; + } + + byte *dataPtr = _frameData; + + uint8 type = *dataPtr++; + + if (type & 0x80) { + // Frame data is compressed + + type &= 0x7F; + + if ((type == 2) && (rect.width() == _surface.w) && (_x == 0)) { + // Directly uncompress onto the video surface + deLZ77((byte *)_surface.pixels + (_y * _surface.pitch), dataPtr); + return true; + } + + deLZ77(_videoBuffer, dataPtr); + + dataPtr = _videoBuffer; + } + + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (dataPtr, rect); + else if (type == 0x02) + renderBlockWhole (dataPtr, rect); + else if (type == 0x03) + renderBlockRLE (dataPtr, rect); + else if (type == 0x42) + renderBlockWhole4X (dataPtr, rect); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (dataPtr, rect); + else + renderBlockSparse2Y(dataPtr, rect); + + return true; } void VMDDecoder::emptySoundSlice(uint32 size) { -- cgit v1.2.3 From b2aba5970e09aa5c862f1c0eb4ae9ab8565a5849 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:00:53 +0000 Subject: VIDEO: Fix VMD seeking svn-id: r51906 --- graphics/video/coktel_decoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index c70da85deb..019ed77019 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1409,13 +1409,13 @@ bool VMDDecoder::seek(int32 frame, int whence, bool restart) { return true; // Restart sound - if (_hasSound && (frame == 0) && (_soundStage == kSoundNone) && !_audioStream) { + if (_hasSound && (frame == -1) && (_soundStage == kSoundNone) && !_audioStream) { _soundStage = kSoundLoaded; _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); } // Seek - _stream->seek(_frames[frame].offset); + _stream->seek(_frames[frame + 1].offset); _curFrame = frame; return true; -- cgit v1.2.3 From 6d03ddef0bcc06041b385224c003d78c8214cf4f Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:01:17 +0000 Subject: VIDEO: Implement CoktelDecoder::renderBlockRLE() svn-id: r51907 --- graphics/video/coktel_decoder.cpp | 81 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 019ed77019..cc4745b535 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -341,6 +341,48 @@ void CoktelDecoder::deLZ77(byte *dest, byte *src) { } } +void CoktelDecoder::deRLE(byte *&destPtr, const byte *&srcPtr, int16 destLen, int16 srcLen) { + srcPtr++; + + if (srcLen & 1) { + byte data = *srcPtr++; + + if (destLen > 0) { + *destPtr++ = data; + destLen--; + } + } + + srcLen >>= 1; + + while (srcLen > 0) { + uint8 tmp = *srcPtr++; + if (tmp & 0x80) { // Verbatim copy + tmp &= 0x7F; + + int16 copyCount = MAX(0, MIN(destLen, tmp * 2)); + + memcpy(destPtr, srcPtr, copyCount); + + srcPtr += tmp * 2; + destPtr += copyCount; + destLen -= copyCount; + } else { // 2 bytes tmp times + for (int i = 0; (i < tmp) && (destLen > 0); i++) { + for (int j = 0; j < 2; j++) { + if (destLen <= 0) + break; + + *destPtr++ = srcPtr[j]; + destLen--; + } + } + srcPtr += 2; + } + srcLen -= tmp; + } +} + // A whole, completely filled block void CoktelDecoder::renderBlockWhole(const byte *src, Common::Rect &rect) { Common::Rect srcRect = rect; @@ -477,9 +519,44 @@ void CoktelDecoder::renderBlockSparse2Y(const byte *src, Common::Rect &rect) { } void CoktelDecoder::renderBlockRLE(const byte *src, Common::Rect &rect) { - warning("renderBlockRLE"); + Common::Rect srcRect = rect; - // TODO + rect.clip(_surface.w, _surface.h); + + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { + byte *dstRow = dst; + int16 pixWritten = 0; + + while (pixWritten < srcRect.width()) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, srcRect.width() - pixWritten); + copyCount = CLIP(rect.width() - pixWritten, 0, pixCount); + + if (*src != 0xFF) { // Normal copy + + memcpy(dstRow, src, copyCount); + dstRow += copyCount; + src += pixCount; + } else + deRLE(dstRow, src, copyCount, pixCount); + + pixWritten += pixCount; + } else { // "Hole" + int16 copyCount = CLIP(rect.width() - pixWritten, 0, pixCount + 1); + + dstRow += copyCount; + pixWritten += pixCount + 1; + } + + } + + dst += _surface.pitch; + } } Common::Rational CoktelDecoder::getFrameRate() const { -- cgit v1.2.3 From eed41aa22337571c18e08abfdf00d19fd2a5121d Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:01:45 +0000 Subject: VIDEO: Fix a failed assertion in VMDDecoder::readFiles() svn-id: r51908 --- graphics/video/coktel_decoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index cc4745b535..4af11c3f1a 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1808,7 +1808,7 @@ bool VMDDecoder::readFiles() { _stream->skip(_frames[i].parts[j].size - 20); - if ((((uint32) file.realSize) >= ssize) || (file.name[0] == 0)) + if ((((uint32) file.realSize) >= ssize) || (file.name == "")) continue; _files.push_back(file); -- cgit v1.2.3 From 945103a43c9a15a4a9a07765197b0551593b4236 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:02:19 +0000 Subject: VIDEO/GOB: Implement and use CoktelDecoder::getFrameCoords() svn-id: r51909 --- graphics/video/coktel_decoder.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 4af11c3f1a..9d056da48d 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -205,6 +205,10 @@ void CoktelDecoder::disableSound() { _audioStream = 0; } +bool CoktelDecoder::getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height) { + return false; +} + bool CoktelDecoder::hasEmbeddedFiles() const { return false; } @@ -2332,6 +2336,38 @@ PixelFormat VMDDecoder::getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } +bool VMDDecoder::getPartCoords(int16 frame, PartType type, int16 &x, int16 &y, int16 &width, int16 &height) { + if (frame >= ((int32) _frameCount)) + return false; + + Frame &f = _frames[frame]; + + // Look for a part matching the requested type, stopping at a separator + Part *part = 0; + for (int i = 0; i < _partsPerFrame; i++) { + Part &p = f.parts[i]; + + if ((p.type == kPartTypeSeparator) || (p.type == type)) { + part = &p; + break; + } + } + + if (!part) + return false; + + x = part->left; + y = part->top; + width = part->right - part->left + 1; + height = part->bottom - part->top + 1; + + return true; +} + +bool VMDDecoder::getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height) { + return getPartCoords(frame, kPartTypeVideo, x, y, width, height); +} + bool VMDDecoder::hasEmbeddedFiles() const { return !_files.empty(); } -- cgit v1.2.3 From af96a0fa3bfe0509efbdf99d15b1dd0d146f8968 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:03:50 +0000 Subject: VIDEO: Implement VMDDecoder::setXY() svn-id: r51912 --- graphics/video/coktel_decoder.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 9d056da48d..3caf7f2e7f 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1502,6 +1502,30 @@ bool VMDDecoder::seek(int32 frame, int whence, bool restart) { return true; } +void VMDDecoder::setXY(uint16 x, uint16 y) { + for (uint32 i = 0; i < _frameCount; i++) { + for (int j = 0; j < _partsPerFrame; j++) { + + if (_frames[i].parts[j].type == kPartTypeVideo) { + if (x != 0xFFFF) { + _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; + _frames[i].parts[j].right = _frames[i].parts[j].right - _x + x; + } + if (y != 0xFFFF) { + _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; + _frames[i].parts[j].bottom = _frames[i].parts[j].bottom - _y + y; + } + } + + } + } + + if (x != 0xFFFF) + _x = x; + if (y != 0xFFFF) + _y = y; +} + bool VMDDecoder::load(Common::SeekableReadStream *stream) { close(); @@ -2047,11 +2071,13 @@ bool VMDDecoder::renderFrame(Common::Rect &rect) { if (_externalCodec) { // TODO + warning("_external codec"); return false; } if (_blitMode > 0) { // TODO + warning("_blitMode == %d", _blitMode); return false; } -- cgit v1.2.3 From 0be37b6b28bf0089dd1f23bc6b81fb256c2cb4ae Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:05:16 +0000 Subject: VIDEO: Implement VMD subtitles svn-id: r51915 --- graphics/video/coktel_decoder.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 3caf7f2e7f..f9a5ad4b62 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -221,6 +221,10 @@ Common::MemoryReadStream *CoktelDecoder::getEmbeddedFile(const Common::String &f return 0; } +int32 CoktelDecoder::getSubtitleIndex() const { + return -1; +} + void CoktelDecoder::close() { disableSound(); freeSurface(); @@ -1473,7 +1477,8 @@ VMDDecoder::VMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : _audioFormat(kAudioFormat8bitRaw), _hasVideo(false), _videoCodec(0), _blitMode(0), _bytesPerPixel(0), _firstFramePos(0), _frameData(0), _frameDataSize(0), _frameDataLen(0), - _videoBuffer(0), _videoBufferSize(0), _externalCodec(false), _codec(0) { + _videoBuffer(0), _videoBufferSize(0), _externalCodec(false), _codec(0), + _subtitle(-1) { } @@ -1499,6 +1504,8 @@ bool VMDDecoder::seek(int32 frame, int whence, bool restart) { _stream->seek(_frames[frame + 1].offset); _curFrame = frame; + _subtitle = -1; + return true; } @@ -1927,6 +1934,7 @@ void VMDDecoder::processFrame() { _dirtyRects.clear(); _paletteDirty = false; + _subtitle = -1; bool startSound = false; @@ -2029,9 +2037,7 @@ void VMDDecoder::processFrame() { } else if (part.type == kPartTypeSubtitle) { - // TODO: - // state.speechId = part.id; - // Always triggers when speech starts + _subtitle = part.id; _stream->skip(part.size); } else { @@ -2443,6 +2449,10 @@ Common::MemoryReadStream *VMDDecoder::getEmbeddedFile(const Common::String &file return stream; } +int32 VMDDecoder::getSubtitleIndex() const { + return _subtitle; +} + } // End of namespace Graphics #endif // GRAPHICS_VIDEO_COKTELDECODER_H -- cgit v1.2.3 From dc3a41769976c5d25206f8a8b649ef36a4a1b51f Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:07:17 +0000 Subject: VIDEO: Change the mixer parameter from a reference to a pointer To match the other VideoDecoder classes with sound support. svn-id: r51919 --- graphics/video/coktel_decoder.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index f9a5ad4b62..daa1047879 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -40,12 +40,14 @@ CoktelDecoder::State::State() : flags(0), speechId(0) { } -CoktelDecoder::CoktelDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : - _mixer(&mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), +CoktelDecoder::CoktelDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : + _mixer(mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), _defaultX(0), _defaultY(0), _features(0), _frameCount(0), _paletteDirty(false), _ownSurface(true), _frameRate(12), _hasSound(false), _soundEnabled(false), _soundStage(kSoundNone), _audioStream(0) { + assert(_mixer); + memset(_palette, 0, 768); } @@ -593,7 +595,7 @@ inline void CoktelDecoder::unsignedToSigned(byte *buffer, int length) { PreIMDDecoder::PreIMDDecoder(uint16 width, uint16 height, - Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), _stream(0), _videoBuffer(0), _videoBufferSize(0) { _width = width; @@ -781,7 +783,7 @@ PixelFormat PreIMDDecoder::getPixelFormat() const { } -IMDDecoder::IMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), +IMDDecoder::IMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), _stream(0), _version(0), _stdX(-1), _stdY(-1), _stdWidth(-1), _stdHeight(-1), _flags(0), _firstFramePos(0), _framePos(0), _frameCoords(0), _frameData(0), _frameDataSize(0), _frameDataLen(0), @@ -1470,7 +1472,7 @@ const int32 VMDDecoder::_tableADPCMStep[] = { -1, -1, -1, -1, 2, 4, 6, 8 }; -VMDDecoder::VMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), +VMDDecoder::VMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), _stream(0), _version(0), _flags(0), _frameInfoOffset(0), _partsPerFrame(0), _frames(0), _soundFlags(0), _soundFreq(0), _soundSliceSize(0), _soundSlicesCount(0), _soundBytesPerSample(0), _soundStereo(0), _soundHeaderSize(0), _soundDataSize(0), -- cgit v1.2.3 From 8186214bc9348514c5020f4449cea2ecfb599a8a Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:07:45 +0000 Subject: VIDEO: Set _paletteDirty in VMDDecoder::load() when there is an initial palette svn-id: r51920 --- graphics/video/coktel_decoder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index daa1047879..ac0622f739 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1604,9 +1604,12 @@ bool VMDDecoder::load(Common::SeekableReadStream *stream) { _videoCodec = _stream->readUint32BE(); - if (_features & kFeaturesPalette) + if (_features & kFeaturesPalette) { _stream->read((byte *)_palette, 768); + _paletteDirty = true; + } + _frameDataSize = _stream->readUint32LE(); _videoBufferSize = _stream->readUint32LE(); -- cgit v1.2.3 From 1151676d8215f411718a28f2d2f58961560efbd1 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 01:08:17 +0000 Subject: VIDEO: Use proper palettes in CoktelDecoder Not just the 6 bits per color component used in VGA svn-id: r51921 --- graphics/video/coktel_decoder.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index ac0622f739..30ca157406 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -916,7 +916,8 @@ bool IMDDecoder::load(Common::SeekableReadStream *stream) { _features |= kFeaturesPalette; // Palette - _stream->read((byte *)_palette, 768); + for (int i = 0; i < 768; i++) + _palette[i] = _stream->readByte() << 2; _paletteDirty = true; @@ -1194,7 +1195,9 @@ void IMDDecoder::processFrame() { _paletteDirty = true; - _stream->read(_palette, 768); + for (int i = 0; i < 768; i++) + _palette[i] = _stream->readByte() << 2; + cmd = _stream->readUint16LE(); } @@ -1307,8 +1310,10 @@ bool IMDDecoder::renderFrame(Common::Rect &rect) { // One byte index int index = *dataPtr++; - // 16 entries with each 3 bytes (RGB) - memcpy(_palette + index * 3, dataPtr, MIN((255 - index) * 3, 48)); + + int count = MIN((255 - index) * 3, 48); + for (int i = 0; i < count; i++) + _palette[index * 3 + i] = dataPtr[i] << 2; dataPtr += 48; type ^= 0x10; @@ -1605,7 +1610,8 @@ bool VMDDecoder::load(Common::SeekableReadStream *stream) { _videoCodec = _stream->readUint32BE(); if (_features & kFeaturesPalette) { - _stream->read((byte *)_palette, 768); + for (int i = 0; i < 768; i++) + _palette[i] = _stream->readByte() << 2; _paletteDirty = true; } @@ -2011,7 +2017,9 @@ void VMDDecoder::processFrame() { uint8 index = _stream->readByte(); uint8 count = _stream->readByte(); - _stream->read(_palette + index * 3, (count + 1) * 3); + for (int j = 0; j < ((count + 1) * 3); j++) + _palette[index * 3 + j] = _stream->readByte() << 2; + _stream->skip((255 - count) * 3); _paletteDirty = true; -- cgit v1.2.3 From 3016de7197bc3b2d21341ddddf1a7534942c03ff Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 02:55:45 +0000 Subject: VIDEO: Fixing typos ("Unknow" -> "Unknown") svn-id: r51926 --- graphics/video/coktel_decoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 30ca157406..951000c6d4 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1681,7 +1681,7 @@ bool VMDDecoder::assessVideoProperties() { } else { char *fourcc = (char *) &_videoCodec; - warning("VMDDecoder::assessVideoProperties(): Unknow video codec FourCC \'%c%c%c%c\'", + warning("VMDDecoder::assessVideoProperties(): Unknown video codec FourCC \'%c%c%c%c\'", fourcc[3], fourcc[2], fourcc[1], fourcc[0]); return false; } @@ -1819,7 +1819,7 @@ bool VMDDecoder::readFrameTable(int &numFiles) { separator = true; _stream->skip(10); } else { - // Unknow type + // Unknown type _stream->skip(10); } -- cgit v1.2.3 From 5f8947c21dab2cc66e23cf5a2d96511e0fd0b5c8 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 02:56:11 +0000 Subject: VIDEO: Use tag2str svn-id: r51927 --- graphics/video/coktel_decoder.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 951000c6d4..2fa0e3cd38 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -1679,10 +1679,8 @@ bool VMDDecoder::assessVideoProperties() { warning("VMDDecoder::assessVideoProperties(): Indeo3 decoder not compiled in"); #endif } else { - char *fourcc = (char *) &_videoCodec; - - warning("VMDDecoder::assessVideoProperties(): Unknown video codec FourCC \'%c%c%c%c\'", - fourcc[3], fourcc[2], fourcc[1], fourcc[0]); + warning("VMDDecoder::assessVideoProperties(): Unknown video codec FourCC \"%s\"", + tag2str(_videoCodec)); return false; } } -- cgit v1.2.3 From 27f6e7106dad4c1d3ade5239ffb1a3e96ce9222b Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Sun, 8 Aug 2010 17:51:50 +0000 Subject: VIDEO: That should fix compilation with MSVC svn-id: r51931 --- graphics/video/coktel_decoder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'graphics/video/coktel_decoder.cpp') diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 2fa0e3cd38..0709288091 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -581,7 +581,7 @@ uint32 CoktelDecoder::getTimeToNextFrame() const { // the middle of a long video. if (!hasSound()) - return (Common::Rational(1000) / _frameRate).toInt(); + return Common::Rational(1000, _frameRate).toInt(); // If there /is/ audio, we do need to keep video and audio // in sync, though. @@ -1039,7 +1039,7 @@ bool IMDDecoder::assessAudioProperties() { return false; } - _frameRate = Common::Rational(_soundFreq) / _soundSliceSize; + _frameRate = Common::Rational(_soundFreq, _soundSliceSize); _hasSound = true; _soundEnabled = true; @@ -1760,7 +1760,7 @@ bool VMDDecoder::assessAudioProperties() { return false; } - _frameRate = Common::Rational(_soundFreq) / _soundSliceSize; + _frameRate = Common::Rational(_soundFreq, _soundSliceSize); _hasSound = true; _soundEnabled = true; -- cgit v1.2.3