/* 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 "common/scummsys.h" #include "zvision/video/rlf_decoder.h" #include "common/str.h" #include "common/file.h" #include "common/textconsole.h" #include "common/debug.h" #include "common/endian.h" namespace ZVision { RLFDecoder::~RLFDecoder() { close(); } bool RLFDecoder::loadStream(Common::SeekableReadStream *stream) { close(); // Check if the stream is valid if (stream && !stream->err() && stream->readUint32BE() == MKTAG('F', 'E', 'L', 'R')) { addTrack(new RLFVideoTrack(stream)); return true; } else { return false; } } RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream) : _readStream(stream), _lastFrameRead(0), _frameCount(0), _width(0), _height(0), _frameTime(0), _frames(0), _displayedFrame(-1), _frameBufferByteSize(0) { if (!readHeader()) { warning("Not a RLF animation file. Wrong magic number"); return; } _currentFrameBuffer.create(_width, _height, getPixelFormat()); _frameBufferByteSize = _width * _height * sizeof(uint16); _frames = new Frame[_frameCount]; // Read in each frame for (uint i = 0; i < _frameCount; ++i) { _frames[i] = readNextFrame(); } } RLFDecoder::RLFVideoTrack::~RLFVideoTrack() { for (uint i = 0; i < _frameCount; ++i) { delete[] _frames[i].encodedData; } delete[] _frames; delete _readStream; _currentFrameBuffer.free(); } bool RLFDecoder::RLFVideoTrack::readHeader() { // Read the header _readStream->readUint32LE(); // Size1 _readStream->readUint32LE(); // Unknown1 _readStream->readUint32LE(); // Unknown2 _frameCount = _readStream->readUint32LE(); // Frame count // Since we don't need any of the data, we can just seek right to the // entries we need rather than read in all the individual entries. _readStream->seek(136, SEEK_CUR); //// Read CIN header //_readStream->readUint32BE(); // Magic number FNIC //_readStream->readUint32LE(); // Size2 //_readStream->readUint32LE(); // Unknown3 //_readStream->readUint32LE(); // Unknown4 //_readStream->readUint32LE(); // Unknown5 //_readStream->seek(0x18, SEEK_CUR); // VRLE //_readStream->readUint32LE(); // LRVD //_readStream->readUint32LE(); // Unknown6 //_readStream->seek(0x18, SEEK_CUR); // HRLE //_readStream->readUint32LE(); // ELHD //_readStream->readUint32LE(); // Unknown7 //_readStream->seek(0x18, SEEK_CUR); // HKEY //_readStream->readUint32LE(); // ELRH //// Read MIN info header //_readStream->readUint32BE(); // Magic number FNIM //_readStream->readUint32LE(); // Size3 //_readStream->readUint32LE(); // OEDV //_readStream->readUint32LE(); // Unknown8 //_readStream->readUint32LE(); // Unknown9 //_readStream->readUint32LE(); // Unknown10 _width = _readStream->readUint32LE(); // Width _height = _readStream->readUint32LE(); // Height // Read time header _readStream->readUint32BE(); // Magic number EMIT _readStream->readUint32LE(); // Size4 _readStream->readUint32LE(); // Unknown11 _frameTime = _readStream->readUint32LE() / 10; // Frame time in microseconds return true; } RLFDecoder::RLFVideoTrack::Frame RLFDecoder::RLFVideoTrack::readNextFrame() { RLFDecoder::RLFVideoTrack::Frame frame; _readStream->readUint32BE(); // Magic number MARF uint32 size = _readStream->readUint32LE(); // Size _readStream->readUint32LE(); // Unknown1 _readStream->readUint32LE(); // Unknown2 uint32 type = _readStream->readUint32BE(); // Either ELHD or ELRH uint32 headerSize = _readStream->readUint32LE(); // Offset from the beginning of this frame to the frame data. Should always be 28 _readStream->readUint32LE(); // Unknown3 frame.encodedSize = size - headerSize; frame.encodedData = new int8[frame.encodedSize]; _readStream->read(frame.encodedData, frame.encodedSize); if (type == MKTAG('E', 'L', 'H', 'D')) { frame.type = Masked; } else if (type == MKTAG('E', 'L', 'R', 'H')) { frame.type = Simple; _completeFrames.push_back(_lastFrameRead); } else { warning("Frame %u doesn't have type that can be decoded", _lastFrameRead); } _lastFrameRead++; return frame; } bool RLFDecoder::RLFVideoTrack::seek(const Audio::Timestamp &time) { uint frame = getFrameAtTime(time); assert(frame < _frameCount); if ((uint)_displayedFrame == frame) return true; int closestFrame = _displayedFrame; int distance = (int)frame - closestFrame; if (distance < 0) { for (uint i = 0; i < _completeFrames.size(); ++i) { if (_completeFrames[i] > frame) break; closestFrame = _completeFrames[i]; } } else { for (uint i = 0; i < _completeFrames.size(); ++i) { int newDistance = (int)frame - (int)(_completeFrames[i]); if (newDistance < 0) break; if (newDistance < distance) { closestFrame = _completeFrames[i]; distance = newDistance; } } } for (uint i = closestFrame; i < frame; ++i) { applyFrameToCurrent(i); } _displayedFrame = frame - 1; return true; } const Graphics::Surface *RLFDecoder::RLFVideoTrack::decodeNextFrame() { if (_displayedFrame >= (int)_frameCount) return NULL; _displayedFrame++; applyFrameToCurrent(_displayedFrame); return &_currentFrameBuffer; } void RLFDecoder::RLFVideoTrack::applyFrameToCurrent(uint frameNumber) { if (_frames[frameNumber].type == Masked) { decodeMaskedRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize); } else if (_frames[frameNumber].type == Simple) { decodeSimpleRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer.getPixels(), _frames[frameNumber].encodedSize, _frameBufferByteSize); } } void RLFDecoder::RLFVideoTrack::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { uint32 sourceOffset = 0; uint32 destOffset = 0; int16 numberOfCopy = 0; while (sourceOffset < sourceSize) { int8 numberOfSamples = source[sourceOffset]; sourceOffset++; // If numberOfSamples is negative, the next abs(numberOfSamples) samples should // be copied directly from source to dest if (numberOfSamples < 0) { numberOfCopy = -numberOfSamples; while (numberOfCopy > 0) { if (sourceOffset + 1 >= sourceSize) { return; } else if (destOffset + 1 >= destSize) { debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); return; } WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset)); sourceOffset += 2; destOffset += 2; numberOfCopy--; } // If numberOfSamples is >= 0, move destOffset forward ((numberOfSamples * 2) + 2) // This function assumes the dest buffer has been memset with 0's. } else { if (sourceOffset + 1 >= sourceSize) { return; } else if (destOffset + 1 >= destSize) { debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); return; } destOffset += (numberOfSamples * 2) + 2; } } } void RLFDecoder::RLFVideoTrack::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { uint32 sourceOffset = 0; uint32 destOffset = 0; int16 numberOfCopy = 0; while (sourceOffset < sourceSize) { int8 numberOfSamples = source[sourceOffset]; sourceOffset++; // If numberOfSamples is negative, the next abs(numberOfSamples) samples should // be copied directly from source to dest if (numberOfSamples < 0) { numberOfCopy = -numberOfSamples; while (numberOfCopy > 0) { if (sourceOffset + 1 >= sourceSize) { return; } else if (destOffset + 1 >= destSize) { debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); return; } WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset)); sourceOffset += 2; destOffset += 2; numberOfCopy--; } // If numberOfSamples is >= 0, copy one sample from source to the // next (numberOfSamples + 2) dest spots } else { if (sourceOffset + 1 >= sourceSize) { return; } uint16 sampleColor = READ_LE_UINT16(source + sourceOffset); sourceOffset += 2; numberOfCopy = numberOfSamples + 2; while (numberOfCopy > 0) { if (destOffset + 1 >= destSize) { debug(2, "Frame decoding overflow\n\tsourceOffset=%u\tsourceSize=%u\n\tdestOffset=%u\tdestSize=%u", sourceOffset, sourceSize, destOffset, destSize); return; } WRITE_UINT16(dest + destOffset, sampleColor); destOffset += 2; numberOfCopy--; } } } } } // End of namespace ZVision