From c4a56dbdb87ef347106ae9a16426df5306d8a91f Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sat, 22 Jun 2013 11:57:51 -0400 Subject: VOYEUR: Implemented RL2 video decoder --- engines/voyeur/animation.cpp | 279 +++++++++++++++++++++++++++++++++++++++++++ engines/voyeur/animation.h | 100 ++++++++++++++++ engines/voyeur/graphics.cpp | 4 + engines/voyeur/graphics.h | 1 + engines/voyeur/module.mk | 1 + engines/voyeur/voyeur.cpp | 35 +++++- engines/voyeur/voyeur.h | 2 + 7 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 engines/voyeur/animation.cpp create mode 100644 engines/voyeur/animation.h (limited to 'engines') diff --git a/engines/voyeur/animation.cpp b/engines/voyeur/animation.cpp new file mode 100644 index 0000000000..b2bcb2a35c --- /dev/null +++ b/engines/voyeur/animation.cpp @@ -0,0 +1,279 @@ +/* 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. + * + */ + +#include "voyeur/animation.h" +#include "common/system.h" +#include "graphics/surface.h" + +namespace Video { + +RL2Decoder::RL2Decoder() { +} + +RL2Decoder::~RL2Decoder() { + close(); +} + +bool RL2Decoder::loadStream(Common::SeekableReadStream *stream) { + close(); + + stream->seek(8); + + // Check RL2 magic number + uint32 signature = stream->readUint32LE(); + if (signature != 0x33564c52 /* RLV3 */ && signature != 0x32564c52 /* RLV2 */) { + warning("RL2Decoder::loadStream(): attempted to load non-RL2 data (type = 0x%08X)", signature); + return false; + } + + addTrack(new RL2VideoTrack(stream)); + return true; +} + +const Common::List *RL2Decoder::getDirtyRects() const { + const Track *track = getTrack(0); + + if (track) + return ((const RL2VideoTrack *)track)->getDirtyRects(); + + return 0; +} + +void RL2Decoder::clearDirtyRects() { + Track *track = getTrack(0); + + if (track) + ((RL2VideoTrack *)track)->clearDirtyRects(); +} + +void RL2Decoder::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { + Track *track = getTrack(0); + + if (track) + ((RL2VideoTrack *)track)->copyDirtyRectsToBuffer(dst, pitch); +} + +RL2Decoder::RL2VideoTrack::RL2VideoTrack(Common::SeekableReadStream *stream) { + _fileStream = stream; + + stream->seek(4); + uint32 backSize = stream->readUint32LE(); + assert(backSize == 0 || backSize == (320 * 200)); + + stream->seek(16); + _frameCount = stream->readUint16LE(); + + // Calculate the frame rate + stream->seek(22); + int soundRate = stream->readUint16LE(); + int rate = stream->readUint16LE(); + stream->skip(2); + int defSoundSize = stream->readUint16LE(); + + int fps = (soundRate > 0) ? rate / defSoundSize : 11025 / 1103; + _frameDelay = 1000 / fps; + + _surface = new Graphics::Surface(); + _surface->create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); + if (backSize == 0) { + _backSurface = NULL; + } else { + _backSurface = new Graphics::Surface(); + _backSurface->create(320, 200, Graphics::PixelFormat::createFormatCLUT8()); + + stream->seek(0x324); + _fileStream->read((byte *)_backSurface->pixels, 320 * 200); + } + + stream->seek(30); + _videoBase = stream->readUint16LE(); + stream->seek(36); + _palette = new byte[3 * 256]; + stream->read(_palette, 256 * 3); + _dirtyPalette = true; + + _curFrame = 0; + _nextFrameStartTime = 0; + + // Read in the frame offsets. Since we're only worried about the video data + // here, we'll calculate video offsets taking into account sound data size + stream->seek(0x324 + backSize + 4 * _frameCount); + + _frameOffset = new uint32[_frameCount]; + for (uint i = 0; i < _frameCount; ++i) + _frameOffset[i] = _fileStream->readUint32LE(); + + // Adust frame offsets to skip sound data + for (uint i = 0; i < _frameCount; ++i) + _frameOffset[i] += _fileStream->readUint32LE() & 0xffff; +} + +RL2Decoder::RL2VideoTrack::~RL2VideoTrack() { + delete _fileStream; + delete[] _palette; + delete[] _frameOffset; + + _surface->free(); + delete _surface; + if (_backSurface) { + _backSurface->free(); + delete _backSurface; + } +} + +bool RL2Decoder::RL2VideoTrack::endOfTrack() const { + return getCurFrame() >= getFrameCount(); +} + +bool RL2Decoder::RL2VideoTrack::rewind() { + _curFrame = 0; + _nextFrameStartTime = 0; + + _fileStream->seek(_frameOffset[0]); + + return true; +} + +uint16 RL2Decoder::RL2VideoTrack::getWidth() const { + return _surface->w; +} + +uint16 RL2Decoder::RL2VideoTrack::getHeight() const { + return _surface->h; +} + +Graphics::PixelFormat RL2Decoder::RL2VideoTrack::getPixelFormat() const { + return _surface->format; +} + +const Graphics::Surface *RL2Decoder::RL2VideoTrack::decodeNextFrame() { + if (_curFrame == 0 && _backSurface) { + // Read in the background frame + _fileStream->seek(0x324); + rl2DecodeFrameWithoutBackground(0); + _dirtyRects.push_back(Common::Rect(0, 0, _surface->w, _surface->h)); + } + + _fileStream->seek(_frameOffset[_curFrame]); + + if (_backSurface) + rl2DecodeFrameWithBackground(); + else + rl2DecodeFrameWithoutBackground(); + + _curFrame++; + _nextFrameStartTime += _frameDelay; + + return _surface; +} + +void RL2Decoder::RL2VideoTrack::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { + for (Common::List::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { + for (int y = (*it).top; y < (*it).bottom; ++y) { + const int x = (*it).left; + memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x); + } + } + + clearDirtyRects(); +} + +void RL2Decoder::RL2VideoTrack::copyFrame(uint8 *data) { + memcpy((byte *)_surface->pixels, data, getWidth() * getHeight()); + + // Redraw + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight())); +} + +void RL2Decoder::RL2VideoTrack::rl2DecodeFrameWithoutBackground(int screenOffset) { + if (screenOffset == -1) + screenOffset = _videoBase; + byte *destP = (byte *)_surface->pixels + screenOffset; + int frameSize = _surface->w * _surface->h - screenOffset; + + _fileStream->seek(_frameOffset[_curFrame]); + while (frameSize > 0) { + byte nextByte = _fileStream->readByte(); + + if (nextByte < 0x80) { + *destP++ = nextByte; + --frameSize; + } else if (nextByte == 0x80) { + int runLength = _fileStream->readByte(); + if (runLength == 0) + return; + + runLength = MIN(runLength, frameSize); + Common::fill(destP, destP + runLength, 0); + destP += runLength; + frameSize -= runLength; + } else { + int runLength = _fileStream->readByte(); + + runLength = MIN(runLength, frameSize); + Common::fill(destP, destP + runLength, nextByte & 0x7f); + destP += runLength; + frameSize -= runLength; + } + } +} + +void RL2Decoder::RL2VideoTrack::rl2DecodeFrameWithBackground() { + int screenOffset = _videoBase; + int frameSize = _surface->w * _surface->h - _videoBase; + byte *src = (byte *)_backSurface->pixels; + byte *dest = (byte *)_surface->pixels; + + _fileStream->seek(_frameOffset[_curFrame]); + while (frameSize > 0) { + byte nextByte = _fileStream->readByte(); + + if (nextByte == 0) { + dest[screenOffset] = src[screenOffset]; + ++screenOffset; + --frameSize; + } else if (nextByte < 0x80) { + dest[screenOffset] = nextByte | 0x80; + ++screenOffset; + --frameSize; + } else if (nextByte == 0x80) { + byte runLength = _fileStream->readByte(); + if (runLength == 0) + return; + + assert(runLength <= frameSize); + Common::copy(src + screenOffset, src + screenOffset + runLength, dest); + screenOffset += runLength; + frameSize -= runLength; + } else { + byte runLength = _fileStream->readByte(); + + assert(runLength <= frameSize); + Common::fill(dest + screenOffset, dest + screenOffset + runLength, nextByte & 0x7f); + screenOffset += runLength; + frameSize -= runLength; + } + } +} + +} // End of namespace Video diff --git a/engines/voyeur/animation.h b/engines/voyeur/animation.h new file mode 100644 index 0000000000..dccf409e92 --- /dev/null +++ b/engines/voyeur/animation.h @@ -0,0 +1,100 @@ +/* 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. + * + */ + +#ifndef VOYEUR_ANIMATION_H +#define VOYEUR_ANIMATION_H + +#include "video/video_decoder.h" +#include "common/list.h" +#include "common/rect.h" +#include "common/stream.h" + +namespace Video { + +/** + * Decoder for RL2 videos. + * + * Video decoder used in engines: + * - voyeur + */ +class RL2Decoder : public VideoDecoder { +public: + RL2Decoder(); + virtual ~RL2Decoder(); + + bool loadStream(Common::SeekableReadStream *stream); + + const Common::List *getDirtyRects() const; + void clearDirtyRects(); + void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); + +private: + class RL2VideoTrack : public VideoTrack { + public: + RL2VideoTrack(Common::SeekableReadStream *stream); + ~RL2VideoTrack(); + + bool endOfTrack() const; + bool isRewindable() const { return true; } + bool rewind(); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + uint32 getNextFrameStartTime() const { return _nextFrameStartTime; } + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + const Common::List *getDirtyRects() const { return &_dirtyRects; } + void clearDirtyRects() { _dirtyRects.clear(); } + void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); + + private: + Common::SeekableReadStream *_fileStream; + Graphics::Surface *_surface; + Graphics::Surface *_backSurface; + + int _curFrame; + + byte *_palette; + mutable bool _dirtyPalette; + + uint32 _frameCount; + uint32 _videoBase; + uint32 *_frameOffset; + uint32 _frameDelay; + uint32 _nextFrameStartTime; + + Common::List _dirtyRects; + + void copyFrame(uint8 *data); + void rl2DecodeFrameWithBackground(); + void rl2DecodeFrameWithoutBackground(int screenOffset = -1); + }; +}; + +} // End of namespace Video + +#endif /* VOYEUR_ANIMATION_H */ diff --git a/engines/voyeur/graphics.cpp b/engines/voyeur/graphics.cpp index 9cf255306f..c79081ceef 100644 --- a/engines/voyeur/graphics.cpp +++ b/engines/voyeur/graphics.cpp @@ -604,6 +604,10 @@ void GraphicsManager::clearPalette() { g_system->getPaletteManager()->setPalette(&palette[0], 0, 256); } +void GraphicsManager::setPalette(const byte *palette, int start, int count) { + g_system->getPaletteManager()->setPalette(palette, start, count); +} + void GraphicsManager::resetPalette() { for (int i = 0; i < 256; ++i) setColor(i, 0, 0, 0); diff --git a/engines/voyeur/graphics.h b/engines/voyeur/graphics.h index f6ad837b1a..23a29657f1 100644 --- a/engines/voyeur/graphics.h +++ b/engines/voyeur/graphics.h @@ -107,6 +107,7 @@ public: void sDisplayPic(PictureResource *pic); void flipPage(); void clearPalette(); + void setPalette(const byte *palette, int start, int count); void resetPalette(); void setColor(int idx, byte r, byte g, byte b); void screenReset(); diff --git a/engines/voyeur/module.mk b/engines/voyeur/module.mk index 48f63dc2f0..645b62ff49 100644 --- a/engines/voyeur/module.mk +++ b/engines/voyeur/module.mk @@ -1,6 +1,7 @@ MODULE := engines/voyeur MODULE_OBJS := \ + animation.o \ debugger.o \ detection.o \ events.o \ diff --git a/engines/voyeur/voyeur.cpp b/engines/voyeur/voyeur.cpp index 8be57e496f..1e140f8621 100644 --- a/engines/voyeur/voyeur.cpp +++ b/engines/voyeur/voyeur.cpp @@ -21,6 +21,7 @@ */ #include "voyeur/voyeur.h" +#include "voyeur/animation.h" #include "voyeur/graphics.h" #include "voyeur/utils.h" #include "common/scummsys.h" @@ -156,8 +157,13 @@ void VoyeurEngine::doHeadTitle() { if (shouldQuit()) return; - doLock(); + if (ConfMan.getBool("copy_protection")) { + bool result = doLock(); + if (!result || shouldQuit()) + return; + } + playRL2Video("a1100100.rl2"); // TODO } @@ -196,6 +202,8 @@ void VoyeurEngine::showConversionScreen() { _graphicsManager.screenReset(); _bVoy->freeBoltGroup(0x10500); + + } bool VoyeurEngine::doLock() { @@ -376,10 +384,35 @@ bool VoyeurEngine::doLock() { _bVoy->freeBoltGroup(0x10700); } + _eventsManager.mouseOff(); + delete[] buttonVoc; delete[] wrongVoc; return result; } +void VoyeurEngine::playRL2Video(const Common::String &filename) { + ::Video::RL2Decoder decoder; + decoder.loadFile(filename); + decoder.start(); + + while (!shouldQuit() && !decoder.endOfVideo() && !_voy._incriminate) { + if (decoder.hasDirtyPalette()) { + const byte *palette = decoder.getPalette(); + _graphicsManager.setPalette(palette, 0, 256); + } + + if (decoder.needsUpdate()) { + const Graphics::Surface *frame = decoder.decodeNextFrame(); + + Common::copy((byte *)frame->pixels, (byte *)frame->pixels + 320 * 200, + (byte *)_graphicsManager._screenSurface.pixels); + } + + _eventsManager.pollEvents(); + g_system->delayMillis(10); + } +} + } // End of namespace Voyeur diff --git a/engines/voyeur/voyeur.h b/engines/voyeur/voyeur.h index 95a664e7aa..06ac691569 100644 --- a/engines/voyeur/voyeur.h +++ b/engines/voyeur/voyeur.h @@ -107,6 +107,8 @@ public: virtual bool canSaveGameStateCurrently(); virtual Common::Error loadGameState(int slot); virtual Common::Error saveGameState(int slot, const Common::String &desc); + + void playRL2Video(const Common::String &filename); }; } // End of namespace Voyeur -- cgit v1.2.3