From b7f8990ba81f81db3243ae3f9069c4d9f12754aa Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Thu, 21 May 2009 13:02:56 +0000 Subject: Renamed the DXA, SMK and FLIC video decoders to reflect the fact that they're decoders, not players svn-id: r40759 --- graphics/video/dxa_decoder.cpp | 563 ++++++++++++++++++++++++++ graphics/video/dxa_decoder.h | 89 +++++ graphics/video/dxa_player.cpp | 563 -------------------------- graphics/video/dxa_player.h | 89 ----- graphics/video/flic_decoder.cpp | 312 +++++++++++++++ graphics/video/flic_decoder.h | 89 +++++ graphics/video/flic_player.cpp | 312 --------------- graphics/video/flic_player.h | 89 ----- graphics/video/smk_decoder.cpp | 859 ++++++++++++++++++++++++++++++++++++++++ graphics/video/smk_decoder.h | 129 ++++++ graphics/video/smk_player.cpp | 859 ---------------------------------------- graphics/video/smk_player.h | 129 ------ 12 files changed, 2041 insertions(+), 2041 deletions(-) create mode 100644 graphics/video/dxa_decoder.cpp create mode 100644 graphics/video/dxa_decoder.h delete mode 100644 graphics/video/dxa_player.cpp delete mode 100644 graphics/video/dxa_player.h create mode 100644 graphics/video/flic_decoder.cpp create mode 100644 graphics/video/flic_decoder.h delete mode 100644 graphics/video/flic_player.cpp delete mode 100644 graphics/video/flic_player.h create mode 100644 graphics/video/smk_decoder.cpp create mode 100644 graphics/video/smk_decoder.h delete mode 100644 graphics/video/smk_player.cpp delete mode 100644 graphics/video/smk_player.h (limited to 'graphics/video') diff --git a/graphics/video/dxa_decoder.cpp b/graphics/video/dxa_decoder.cpp new file mode 100644 index 0000000000..e514050942 --- /dev/null +++ b/graphics/video/dxa_decoder.cpp @@ -0,0 +1,563 @@ +/* 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 "common/debug.h" +#include "common/endian.h" +#include "common/archive.h" +#include "common/system.h" +#include "common/util.h" + +#include "graphics/video/dxa_decoder.h" + +#ifdef USE_ZLIB + #include "common/zlib.h" +#endif + +namespace Graphics { + +DXADecoder::DXADecoder() { + _fileStream = 0; + + _frameBuffer1 = 0; + _frameBuffer2 = 0; + _scaledBuffer = 0; + _videoFrameBuffer = 0; + + _inBuffer = 0; + _inBufferSize = 0; + + _decompBuffer = 0; + _decompBufferSize = 0; + + _videoInfo.width = 0; + _videoInfo.height = 0; + + _frameSize = 0; + _videoInfo.frameCount = 0; + _videoInfo.currentFrame = 0; + _videoInfo.frameRate = 0; + _videoInfo.frameDelay = 0; + + _scaleMode = S_NONE; +} + +DXADecoder::~DXADecoder() { + closeFile(); +} + +bool DXADecoder::loadFile(const char *fileName) { + uint32 tag; + int32 frameRate; + + closeFile(); + + _fileStream = SearchMan.createReadStreamForMember(fileName); + if (!_fileStream) + return false; + + tag = _fileStream->readUint32BE(); + assert(tag == MKID_BE('DEXA')); + + uint8 flags = _fileStream->readByte(); + _videoInfo.frameCount = _fileStream->readUint16BE(); + frameRate = _fileStream->readSint32BE(); + + if (frameRate > 0) { + _videoInfo.frameRate = 1000 / frameRate; + _videoInfo.frameDelay = frameRate * 100; + } else if (frameRate < 0) { + _videoInfo.frameRate = 100000 / (-frameRate); + _videoInfo.frameDelay = -frameRate; + } else { + _videoInfo.frameRate = 10; + _videoInfo.frameDelay = 10000; + } + + _videoInfo.width = _fileStream->readUint16BE(); + _videoInfo.height = _fileStream->readUint16BE(); + + if (flags & 0x80) { + _scaleMode = S_INTERLACED; + _curHeight = _videoInfo.height / 2; + } else if (flags & 0x40) { + _scaleMode = S_DOUBLE; + _curHeight = _videoInfo.height / 2; + } else { + _scaleMode = S_NONE; + _curHeight = _videoInfo.height; + } + + debug(2, "flags 0x0%x framesCount %d width %d height %d rate %d ticks %d", flags, getFrameCount(), getWidth(), getHeight(), getFrameRate(), getFrameDelay()); + + _frameSize = _videoInfo.width * _videoInfo.height; + _decompBufferSize = _frameSize; + _frameBuffer1 = (uint8 *)malloc(_frameSize); + memset(_frameBuffer1, 0, _frameSize); + _frameBuffer2 = (uint8 *)malloc(_frameSize); + memset(_frameBuffer2, 0, _frameSize); + if (!_frameBuffer1 || !_frameBuffer2) + error("DXADecoder: Error allocating frame buffers (size %u)", _frameSize); + + _scaledBuffer = 0; + if (_scaleMode != S_NONE) { + _scaledBuffer = (uint8 *)malloc(_frameSize); + memset(_scaledBuffer, 0, _frameSize); + if (!_scaledBuffer) + error("Error allocating scale buffer (size %u)", _frameSize); + } + +#ifdef DXA_EXPERIMENT_MAXD + // Check for an extended header + if (flags & 1) { + uint32 size; + + do { + tag = _fileStream->readUint32BE(); + if (tag != 0) { + size = _fileStream->readUint32BE(); + } + switch (tag) { + case 0: // No more tags + break; + case MKID_BE('MAXD'): + assert(size == 4); + _decompBufferSize = _fileStream->readUint32BE(); + break; + default: // Unknown tag - skip it. + while (size > 0) { + byte dummy = _fileStream->readByte(); + size--; + } + break; + } + } while (tag != 0); + } +#endif + + // Read the sound header + _soundTag = _fileStream->readUint32BE(); + + _videoInfo.currentFrame = 0; + + _videoInfo.firstframeOffset = _fileStream->pos(); + + return true; +} + +void DXADecoder::closeFile() { + if (!_fileStream) + return; + + delete _fileStream; + _fileStream = 0; + + free(_frameBuffer1); + free(_frameBuffer2); + free(_scaledBuffer); + free(_inBuffer); + free(_decompBuffer); + + _inBuffer = 0; + _decompBuffer = 0; +} + +void DXADecoder::decodeZlib(byte *data, int size, int totalSize) { +#ifdef USE_ZLIB + unsigned long dstLen = totalSize; + Common::uncompress(data, &dstLen, _inBuffer, size); +#endif +} + +#define BLOCKW 4 +#define BLOCKH 4 + +void DXADecoder::decode12(int size) { +#ifdef USE_ZLIB + if (_decompBuffer == NULL) { + _decompBuffer = (byte *)malloc(_decompBufferSize); + memset(_decompBuffer, 0, _decompBufferSize); + if (_decompBuffer == NULL) + error("Error allocating decomp buffer (size %u)", _decompBufferSize); + } + /* decompress the input data */ + decodeZlib(_decompBuffer, size, _decompBufferSize); + + byte *dat = _decompBuffer; + + memcpy(_frameBuffer2, _frameBuffer1, _frameSize); + + for (uint32 by = 0; by < _videoInfo.height; by += BLOCKH) { + for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) { + byte type = *dat++; + byte *b2 = _frameBuffer1 + bx + by * _videoInfo.width; + + switch (type) { + case 0: + break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 1: { + unsigned short diffMap; + if (type >= 10 && type <= 15) { + static const struct { uint8 sh1, sh2; } shiftTbl[6] = { + {0, 0}, {8, 0}, {8, 8}, {8, 4}, {4, 0}, {4, 4} + }; + diffMap = ((*dat & 0xF0) << shiftTbl[type-10].sh1) | + ((*dat & 0x0F) << shiftTbl[type-10].sh2); + dat++; + } else { + diffMap = *(unsigned short*)dat; + dat += 2; + } + + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + if (diffMap & 0x8000) { + b2[xc] = *dat++; + } + diffMap <<= 1; + } + b2 += _videoInfo.width; + } + break; + } + case 2: { + byte color = *dat++; + + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + b2[xc] = color; + } + b2 += _videoInfo.width; + } + break; + } + case 3: { + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + b2[xc] = *dat++; + } + b2 += _videoInfo.width; + } + break; + } + case 4: { + byte mbyte = *dat++; + int mx = (mbyte >> 4) & 0x07; + if (mbyte & 0x80) + mx = -mx; + int my = mbyte & 0x07; + if (mbyte & 0x08) + my = -my; + byte *b1 = _frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width; + for (int yc = 0; yc < BLOCKH; yc++) { + memcpy(b2, b1, BLOCKW); + b1 += _videoInfo.width; + b2 += _videoInfo.width; + } + break; + } + case 5: + break; + default: + error("decode12: Unknown type %d", type); + } + } + } +#endif +} + +void DXADecoder::decode13(int size) { +#ifdef USE_ZLIB + uint8 *codeBuf, *dataBuf, *motBuf, *maskBuf; + + if (_decompBuffer == NULL) { + _decompBuffer = (byte *)malloc(_decompBufferSize); + memset(_decompBuffer, 0, _decompBufferSize); + if (_decompBuffer == NULL) + error("Error allocating decomp buffer (size %u)", _decompBufferSize); + } + + /* decompress the input data */ + decodeZlib(_decompBuffer, size, _decompBufferSize); + + memcpy(_frameBuffer2, _frameBuffer1, _frameSize); + + int codeSize = _videoInfo.width * _curHeight / 16; + int dataSize, motSize, maskSize; + + dataSize = READ_BE_UINT32(&_decompBuffer[0]); + motSize = READ_BE_UINT32(&_decompBuffer[4]); + maskSize = READ_BE_UINT32(&_decompBuffer[8]); + + codeBuf = &_decompBuffer[12]; + dataBuf = &codeBuf[codeSize]; + motBuf = &dataBuf[dataSize]; + maskBuf = &motBuf[motSize]; + + for (uint32 by = 0; by < _curHeight; by += BLOCKH) { + for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) { + uint8 type = *codeBuf++; + uint8 *b2 = (uint8*)_frameBuffer1 + bx + by * _videoInfo.width; + + switch (type) { + case 0: + break; + + case 1: { + uint16 diffMap = READ_BE_UINT16(maskBuf); + maskBuf += 2; + + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + if (diffMap & 0x8000) { + b2[xc] = *dataBuf++; + } + diffMap <<= 1; + } + b2 += _videoInfo.width; + } + break; + } + case 2: { + uint8 color = *dataBuf++; + + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + b2[xc] = color; + } + b2 += _videoInfo.width; + } + break; + } + case 3: { + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + b2[xc] = *dataBuf++; + } + b2 += _videoInfo.width; + } + break; + } + case 4: { + uint8 mbyte = *motBuf++; + + int mx = (mbyte >> 4) & 0x07; + if (mbyte & 0x80) + mx = -mx; + int my = mbyte & 0x07; + if (mbyte & 0x08) + my = -my; + + uint8 *b1 = (uint8*)_frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width; + for (int yc = 0; yc < BLOCKH; yc++) { + memcpy(b2, b1, BLOCKW); + b1 += _videoInfo.width; + b2 += _videoInfo.width; + } + break; + } + case 8: { + static const int subX[4] = {0, 2, 0, 2}; + static const int subY[4] = {0, 0, 2, 2}; + + uint8 subMask = *maskBuf++; + + for (int subBlock = 0; subBlock < 4; subBlock++) { + int sx = bx + subX[subBlock], sy = by + subY[subBlock]; + b2 = (uint8*)_frameBuffer1 + sx + sy * _videoInfo.width; + switch (subMask & 0xC0) { + // 00: skip + case 0x00: + break; + // 01: solid color + case 0x40: { + uint8 subColor = *dataBuf++; + for (int yc = 0; yc < BLOCKH / 2; yc++) { + for (int xc = 0; xc < BLOCKW / 2; xc++) { + b2[xc] = subColor; + } + b2 += _videoInfo.width; + } + break; + } + // 02: motion vector + case 0x80: { + uint8 mbyte = *motBuf++; + + int mx = (mbyte >> 4) & 0x07; + if (mbyte & 0x80) + mx = -mx; + + int my = mbyte & 0x07; + if (mbyte & 0x08) + my = -my; + + uint8 *b1 = (uint8*)_frameBuffer2 + (sx+mx) + (sy+my) * _videoInfo.width; + for (int yc = 0; yc < BLOCKH / 2; yc++) { + memcpy(b2, b1, BLOCKW / 2); + b1 += _videoInfo.width; + b2 += _videoInfo.width; + } + break; + } + // 03: raw + case 0xC0: + for (int yc = 0; yc < BLOCKH / 2; yc++) { + for (int xc = 0; xc < BLOCKW / 2; xc++) { + b2[xc] = *dataBuf++; + } + b2 += _videoInfo.width; + } + break; + } + subMask <<= 2; + } + break; + } + case 32: + case 33: + case 34: { + int count = type - 30; + uint8 pixels[4]; + + memcpy(pixels, dataBuf, count); + dataBuf += count; + + if (count == 2) { + uint16 code = READ_BE_UINT16(maskBuf); + maskBuf += 2; + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + b2[xc] = pixels[code & 1]; + code >>= 1; + } + b2 += _videoInfo.width; + } + } else { + uint32 code = READ_BE_UINT32(maskBuf); + maskBuf += 4; + for (int yc = 0; yc < BLOCKH; yc++) { + for (int xc = 0; xc < BLOCKW; xc++) { + b2[xc] = pixels[code & 3]; + code >>= 2; + } + b2 += _videoInfo.width; + } + } + break; + } + default: + error("decode13: Unknown type %d", type); + } + } + } +#endif +} + +bool DXADecoder::decodeNextFrame() { + uint32 tag; + + if (_videoInfo.currentFrame == 0) + _videoInfo.startTime = g_system->getMillis(); + + tag = _fileStream->readUint32BE(); + if (tag == MKID_BE('CMAP')) { + byte rgb[768]; + + _fileStream->read(rgb, ARRAYSIZE(rgb)); + setPalette(rgb); + } + + tag = _fileStream->readUint32BE(); + if (tag == MKID_BE('FRAM')) { + byte type = _fileStream->readByte(); + uint32 size = _fileStream->readUint32BE(); + if ((_inBuffer == NULL) || (_inBufferSize < size)) { + free(_inBuffer); + _inBuffer = (byte *)malloc(size); + memset(_inBuffer, 0, size); + if (_inBuffer == NULL) + error("Error allocating input buffer (size %u)", size); + _inBufferSize = size; + } + + _fileStream->read(_inBuffer, size); + + switch (type) { + case 2: + decodeZlib(_frameBuffer1, size, _frameSize); + break; + case 3: + decodeZlib(_frameBuffer2, size, _frameSize); + break; + case 12: + decode12(size); + break; + case 13: + decode13(size); + break; + default: + error("decodeFrame: Unknown compression type %d", type); + } + + if (type == 3) { + for (uint32 j = 0; j < _curHeight; ++j) { + for (uint32 i = 0; i < _videoInfo.width; ++i) { + const int offs = j * _videoInfo.width + i; + _frameBuffer1[offs] ^= _frameBuffer2[offs]; + } + } + } + } + + switch (_scaleMode) { + case S_INTERLACED: + for (int cy = 0; cy < _curHeight; cy++) { + memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); + memset(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], 0, _videoInfo.width); + } + _videoFrameBuffer = _scaledBuffer; + break; + case S_DOUBLE: + for (int cy = 0; cy < _curHeight; cy++) { + memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); + memcpy(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); + } + _videoFrameBuffer = _scaledBuffer; + break; + case S_NONE: + _videoFrameBuffer = _frameBuffer1; + break; + } + + return ++_videoInfo.currentFrame < _videoInfo.frameCount; +} + +} // End of namespace Graphics diff --git a/graphics/video/dxa_decoder.h b/graphics/video/dxa_decoder.h new file mode 100644 index 0000000000..384a057c3f --- /dev/null +++ b/graphics/video/dxa_decoder.h @@ -0,0 +1,89 @@ +/* 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$ + * + */ + +/** + * Video decoder used in engines: + * - agos + * - sword1 + * - sword2 + */ + +#ifndef GRAPHICS_VIDEO_DXA_PLAYER_H +#define GRAPHICS_VIDEO_DXA_PLAYER_H + +#include "graphics/video/video_player.h" + +namespace Graphics { + +class DXADecoder : public VideoDecoder { +public: + DXADecoder(); + virtual ~DXADecoder(); + + /** + * Load a DXA encoded video file + * @param filename the filename to load + */ + bool loadFile(const char *fileName); + + /** + * Close a DXA encoded video file + */ + void closeFile(); + + bool decodeNextFrame(); + + /** + * Get the sound chunk tag of the loaded DXA file + */ + uint32 getSoundTag() { return _soundTag; } + +private: + void decodeZlib(byte *data, int size, int totalSize); + void decode12(int size); + void decode13(int size); + + enum ScaleMode { + S_NONE, + S_INTERLACED, + S_DOUBLE + }; + + byte *_frameBuffer1; + byte *_frameBuffer2; + byte *_scaledBuffer; + byte *_inBuffer; + uint32 _inBufferSize; + byte *_decompBuffer; + uint32 _decompBufferSize; + uint16 _curHeight; + uint32 _frameSize; + ScaleMode _scaleMode; + uint32 _soundTag; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/video/dxa_player.cpp b/graphics/video/dxa_player.cpp deleted file mode 100644 index d0d4d1c67b..0000000000 --- a/graphics/video/dxa_player.cpp +++ /dev/null @@ -1,563 +0,0 @@ -/* 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 "common/debug.h" -#include "common/endian.h" -#include "common/archive.h" -#include "common/system.h" -#include "common/util.h" - -#include "graphics/video/dxa_player.h" - -#ifdef USE_ZLIB - #include "common/zlib.h" -#endif - -namespace Graphics { - -DXADecoder::DXADecoder() { - _fileStream = 0; - - _frameBuffer1 = 0; - _frameBuffer2 = 0; - _scaledBuffer = 0; - _videoFrameBuffer = 0; - - _inBuffer = 0; - _inBufferSize = 0; - - _decompBuffer = 0; - _decompBufferSize = 0; - - _videoInfo.width = 0; - _videoInfo.height = 0; - - _frameSize = 0; - _videoInfo.frameCount = 0; - _videoInfo.currentFrame = 0; - _videoInfo.frameRate = 0; - _videoInfo.frameDelay = 0; - - _scaleMode = S_NONE; -} - -DXADecoder::~DXADecoder() { - closeFile(); -} - -bool DXADecoder::loadFile(const char *fileName) { - uint32 tag; - int32 frameRate; - - closeFile(); - - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; - - tag = _fileStream->readUint32BE(); - assert(tag == MKID_BE('DEXA')); - - uint8 flags = _fileStream->readByte(); - _videoInfo.frameCount = _fileStream->readUint16BE(); - frameRate = _fileStream->readSint32BE(); - - if (frameRate > 0) { - _videoInfo.frameRate = 1000 / frameRate; - _videoInfo.frameDelay = frameRate * 100; - } else if (frameRate < 0) { - _videoInfo.frameRate = 100000 / (-frameRate); - _videoInfo.frameDelay = -frameRate; - } else { - _videoInfo.frameRate = 10; - _videoInfo.frameDelay = 10000; - } - - _videoInfo.width = _fileStream->readUint16BE(); - _videoInfo.height = _fileStream->readUint16BE(); - - if (flags & 0x80) { - _scaleMode = S_INTERLACED; - _curHeight = _videoInfo.height / 2; - } else if (flags & 0x40) { - _scaleMode = S_DOUBLE; - _curHeight = _videoInfo.height / 2; - } else { - _scaleMode = S_NONE; - _curHeight = _videoInfo.height; - } - - debug(2, "flags 0x0%x framesCount %d width %d height %d rate %d ticks %d", flags, getFrameCount(), getWidth(), getHeight(), getFrameRate(), getFrameDelay()); - - _frameSize = _videoInfo.width * _videoInfo.height; - _decompBufferSize = _frameSize; - _frameBuffer1 = (uint8 *)malloc(_frameSize); - memset(_frameBuffer1, 0, _frameSize); - _frameBuffer2 = (uint8 *)malloc(_frameSize); - memset(_frameBuffer2, 0, _frameSize); - if (!_frameBuffer1 || !_frameBuffer2) - error("DXADecoder: Error allocating frame buffers (size %u)", _frameSize); - - _scaledBuffer = 0; - if (_scaleMode != S_NONE) { - _scaledBuffer = (uint8 *)malloc(_frameSize); - memset(_scaledBuffer, 0, _frameSize); - if (!_scaledBuffer) - error("Error allocating scale buffer (size %u)", _frameSize); - } - -#ifdef DXA_EXPERIMENT_MAXD - // Check for an extended header - if (flags & 1) { - uint32 size; - - do { - tag = _fileStream->readUint32BE(); - if (tag != 0) { - size = _fileStream->readUint32BE(); - } - switch (tag) { - case 0: // No more tags - break; - case MKID_BE('MAXD'): - assert(size == 4); - _decompBufferSize = _fileStream->readUint32BE(); - break; - default: // Unknown tag - skip it. - while (size > 0) { - byte dummy = _fileStream->readByte(); - size--; - } - break; - } - } while (tag != 0); - } -#endif - - // Read the sound header - _soundTag = _fileStream->readUint32BE(); - - _videoInfo.currentFrame = 0; - - _videoInfo.firstframeOffset = _fileStream->pos(); - - return true; -} - -void DXADecoder::closeFile() { - if (!_fileStream) - return; - - delete _fileStream; - _fileStream = 0; - - free(_frameBuffer1); - free(_frameBuffer2); - free(_scaledBuffer); - free(_inBuffer); - free(_decompBuffer); - - _inBuffer = 0; - _decompBuffer = 0; -} - -void DXADecoder::decodeZlib(byte *data, int size, int totalSize) { -#ifdef USE_ZLIB - unsigned long dstLen = totalSize; - Common::uncompress(data, &dstLen, _inBuffer, size); -#endif -} - -#define BLOCKW 4 -#define BLOCKH 4 - -void DXADecoder::decode12(int size) { -#ifdef USE_ZLIB - if (_decompBuffer == NULL) { - _decompBuffer = (byte *)malloc(_decompBufferSize); - memset(_decompBuffer, 0, _decompBufferSize); - if (_decompBuffer == NULL) - error("Error allocating decomp buffer (size %u)", _decompBufferSize); - } - /* decompress the input data */ - decodeZlib(_decompBuffer, size, _decompBufferSize); - - byte *dat = _decompBuffer; - - memcpy(_frameBuffer2, _frameBuffer1, _frameSize); - - for (uint32 by = 0; by < _videoInfo.height; by += BLOCKH) { - for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) { - byte type = *dat++; - byte *b2 = _frameBuffer1 + bx + by * _videoInfo.width; - - switch (type) { - case 0: - break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 1: { - unsigned short diffMap; - if (type >= 10 && type <= 15) { - static const struct { uint8 sh1, sh2; } shiftTbl[6] = { - {0, 0}, {8, 0}, {8, 8}, {8, 4}, {4, 0}, {4, 4} - }; - diffMap = ((*dat & 0xF0) << shiftTbl[type-10].sh1) | - ((*dat & 0x0F) << shiftTbl[type-10].sh2); - dat++; - } else { - diffMap = *(unsigned short*)dat; - dat += 2; - } - - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - if (diffMap & 0x8000) { - b2[xc] = *dat++; - } - diffMap <<= 1; - } - b2 += _videoInfo.width; - } - break; - } - case 2: { - byte color = *dat++; - - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - b2[xc] = color; - } - b2 += _videoInfo.width; - } - break; - } - case 3: { - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - b2[xc] = *dat++; - } - b2 += _videoInfo.width; - } - break; - } - case 4: { - byte mbyte = *dat++; - int mx = (mbyte >> 4) & 0x07; - if (mbyte & 0x80) - mx = -mx; - int my = mbyte & 0x07; - if (mbyte & 0x08) - my = -my; - byte *b1 = _frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width; - for (int yc = 0; yc < BLOCKH; yc++) { - memcpy(b2, b1, BLOCKW); - b1 += _videoInfo.width; - b2 += _videoInfo.width; - } - break; - } - case 5: - break; - default: - error("decode12: Unknown type %d", type); - } - } - } -#endif -} - -void DXADecoder::decode13(int size) { -#ifdef USE_ZLIB - uint8 *codeBuf, *dataBuf, *motBuf, *maskBuf; - - if (_decompBuffer == NULL) { - _decompBuffer = (byte *)malloc(_decompBufferSize); - memset(_decompBuffer, 0, _decompBufferSize); - if (_decompBuffer == NULL) - error("Error allocating decomp buffer (size %u)", _decompBufferSize); - } - - /* decompress the input data */ - decodeZlib(_decompBuffer, size, _decompBufferSize); - - memcpy(_frameBuffer2, _frameBuffer1, _frameSize); - - int codeSize = _videoInfo.width * _curHeight / 16; - int dataSize, motSize, maskSize; - - dataSize = READ_BE_UINT32(&_decompBuffer[0]); - motSize = READ_BE_UINT32(&_decompBuffer[4]); - maskSize = READ_BE_UINT32(&_decompBuffer[8]); - - codeBuf = &_decompBuffer[12]; - dataBuf = &codeBuf[codeSize]; - motBuf = &dataBuf[dataSize]; - maskBuf = &motBuf[motSize]; - - for (uint32 by = 0; by < _curHeight; by += BLOCKH) { - for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) { - uint8 type = *codeBuf++; - uint8 *b2 = (uint8*)_frameBuffer1 + bx + by * _videoInfo.width; - - switch (type) { - case 0: - break; - - case 1: { - uint16 diffMap = READ_BE_UINT16(maskBuf); - maskBuf += 2; - - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - if (diffMap & 0x8000) { - b2[xc] = *dataBuf++; - } - diffMap <<= 1; - } - b2 += _videoInfo.width; - } - break; - } - case 2: { - uint8 color = *dataBuf++; - - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - b2[xc] = color; - } - b2 += _videoInfo.width; - } - break; - } - case 3: { - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - b2[xc] = *dataBuf++; - } - b2 += _videoInfo.width; - } - break; - } - case 4: { - uint8 mbyte = *motBuf++; - - int mx = (mbyte >> 4) & 0x07; - if (mbyte & 0x80) - mx = -mx; - int my = mbyte & 0x07; - if (mbyte & 0x08) - my = -my; - - uint8 *b1 = (uint8*)_frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width; - for (int yc = 0; yc < BLOCKH; yc++) { - memcpy(b2, b1, BLOCKW); - b1 += _videoInfo.width; - b2 += _videoInfo.width; - } - break; - } - case 8: { - static const int subX[4] = {0, 2, 0, 2}; - static const int subY[4] = {0, 0, 2, 2}; - - uint8 subMask = *maskBuf++; - - for (int subBlock = 0; subBlock < 4; subBlock++) { - int sx = bx + subX[subBlock], sy = by + subY[subBlock]; - b2 = (uint8*)_frameBuffer1 + sx + sy * _videoInfo.width; - switch (subMask & 0xC0) { - // 00: skip - case 0x00: - break; - // 01: solid color - case 0x40: { - uint8 subColor = *dataBuf++; - for (int yc = 0; yc < BLOCKH / 2; yc++) { - for (int xc = 0; xc < BLOCKW / 2; xc++) { - b2[xc] = subColor; - } - b2 += _videoInfo.width; - } - break; - } - // 02: motion vector - case 0x80: { - uint8 mbyte = *motBuf++; - - int mx = (mbyte >> 4) & 0x07; - if (mbyte & 0x80) - mx = -mx; - - int my = mbyte & 0x07; - if (mbyte & 0x08) - my = -my; - - uint8 *b1 = (uint8*)_frameBuffer2 + (sx+mx) + (sy+my) * _videoInfo.width; - for (int yc = 0; yc < BLOCKH / 2; yc++) { - memcpy(b2, b1, BLOCKW / 2); - b1 += _videoInfo.width; - b2 += _videoInfo.width; - } - break; - } - // 03: raw - case 0xC0: - for (int yc = 0; yc < BLOCKH / 2; yc++) { - for (int xc = 0; xc < BLOCKW / 2; xc++) { - b2[xc] = *dataBuf++; - } - b2 += _videoInfo.width; - } - break; - } - subMask <<= 2; - } - break; - } - case 32: - case 33: - case 34: { - int count = type - 30; - uint8 pixels[4]; - - memcpy(pixels, dataBuf, count); - dataBuf += count; - - if (count == 2) { - uint16 code = READ_BE_UINT16(maskBuf); - maskBuf += 2; - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - b2[xc] = pixels[code & 1]; - code >>= 1; - } - b2 += _videoInfo.width; - } - } else { - uint32 code = READ_BE_UINT32(maskBuf); - maskBuf += 4; - for (int yc = 0; yc < BLOCKH; yc++) { - for (int xc = 0; xc < BLOCKW; xc++) { - b2[xc] = pixels[code & 3]; - code >>= 2; - } - b2 += _videoInfo.width; - } - } - break; - } - default: - error("decode13: Unknown type %d", type); - } - } - } -#endif -} - -bool DXADecoder::decodeNextFrame() { - uint32 tag; - - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); - - tag = _fileStream->readUint32BE(); - if (tag == MKID_BE('CMAP')) { - byte rgb[768]; - - _fileStream->read(rgb, ARRAYSIZE(rgb)); - setPalette(rgb); - } - - tag = _fileStream->readUint32BE(); - if (tag == MKID_BE('FRAM')) { - byte type = _fileStream->readByte(); - uint32 size = _fileStream->readUint32BE(); - if ((_inBuffer == NULL) || (_inBufferSize < size)) { - free(_inBuffer); - _inBuffer = (byte *)malloc(size); - memset(_inBuffer, 0, size); - if (_inBuffer == NULL) - error("Error allocating input buffer (size %u)", size); - _inBufferSize = size; - } - - _fileStream->read(_inBuffer, size); - - switch (type) { - case 2: - decodeZlib(_frameBuffer1, size, _frameSize); - break; - case 3: - decodeZlib(_frameBuffer2, size, _frameSize); - break; - case 12: - decode12(size); - break; - case 13: - decode13(size); - break; - default: - error("decodeFrame: Unknown compression type %d", type); - } - - if (type == 3) { - for (uint32 j = 0; j < _curHeight; ++j) { - for (uint32 i = 0; i < _videoInfo.width; ++i) { - const int offs = j * _videoInfo.width + i; - _frameBuffer1[offs] ^= _frameBuffer2[offs]; - } - } - } - } - - switch (_scaleMode) { - case S_INTERLACED: - for (int cy = 0; cy < _curHeight; cy++) { - memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); - memset(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], 0, _videoInfo.width); - } - _videoFrameBuffer = _scaledBuffer; - break; - case S_DOUBLE: - for (int cy = 0; cy < _curHeight; cy++) { - memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); - memcpy(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width); - } - _videoFrameBuffer = _scaledBuffer; - break; - case S_NONE: - _videoFrameBuffer = _frameBuffer1; - break; - } - - return ++_videoInfo.currentFrame < _videoInfo.frameCount; -} - -} // End of namespace Graphics diff --git a/graphics/video/dxa_player.h b/graphics/video/dxa_player.h deleted file mode 100644 index 384a057c3f..0000000000 --- a/graphics/video/dxa_player.h +++ /dev/null @@ -1,89 +0,0 @@ -/* 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$ - * - */ - -/** - * Video decoder used in engines: - * - agos - * - sword1 - * - sword2 - */ - -#ifndef GRAPHICS_VIDEO_DXA_PLAYER_H -#define GRAPHICS_VIDEO_DXA_PLAYER_H - -#include "graphics/video/video_player.h" - -namespace Graphics { - -class DXADecoder : public VideoDecoder { -public: - DXADecoder(); - virtual ~DXADecoder(); - - /** - * Load a DXA encoded video file - * @param filename the filename to load - */ - bool loadFile(const char *fileName); - - /** - * Close a DXA encoded video file - */ - void closeFile(); - - bool decodeNextFrame(); - - /** - * Get the sound chunk tag of the loaded DXA file - */ - uint32 getSoundTag() { return _soundTag; } - -private: - void decodeZlib(byte *data, int size, int totalSize); - void decode12(int size); - void decode13(int size); - - enum ScaleMode { - S_NONE, - S_INTERLACED, - S_DOUBLE - }; - - byte *_frameBuffer1; - byte *_frameBuffer2; - byte *_scaledBuffer; - byte *_inBuffer; - uint32 _inBufferSize; - byte *_decompBuffer; - uint32 _decompBufferSize; - uint16 _curHeight; - uint32 _frameSize; - ScaleMode _scaleMode; - uint32 _soundTag; -}; - -} // End of namespace Graphics - -#endif diff --git a/graphics/video/flic_decoder.cpp b/graphics/video/flic_decoder.cpp new file mode 100644 index 0000000000..bef232b6e6 --- /dev/null +++ b/graphics/video/flic_decoder.cpp @@ -0,0 +1,312 @@ +/* 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/flic_decoder.h" +#include "common/archive.h" +#include "common/stream.h" +#include "common/endian.h" +#include "common/system.h" + +namespace Graphics { + +FlicDecoder::FlicDecoder() { + _paletteChanged = false; + _fileStream = 0; + _videoFrameBuffer = 0; + memset(&_videoInfo, 0, sizeof(_videoInfo)); +} + +FlicDecoder::~FlicDecoder() { + closeFile(); +} + +bool FlicDecoder::loadFile(const char *fileName) { + closeFile(); + + _fileStream = SearchMan.createReadStreamForMember(fileName); + if (!_fileStream) + return false; + + /* uint32 frameSize = */ _fileStream->readUint32LE(); + uint16 frameType = _fileStream->readUint16LE(); + + // Check FLC magic number + if (frameType != 0xAF12) { + warning("FlicDecoder::FlicDecoder(): attempted to load non-FLC data (type = 0x%04X)", frameType); + delete _fileStream; + _fileStream = 0; + return false; + } + + _videoInfo.frameCount = _fileStream->readUint16LE(); + _videoInfo.width = _fileStream->readUint16LE(); + _videoInfo.height = _fileStream->readUint16LE(); + uint16 colorDepth = _fileStream->readUint16LE(); + if (colorDepth != 8) { + warning("FlicDecoder::FlicDecoder(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", frameType); + delete _fileStream; + _fileStream = 0; + return false; + } + _fileStream->readUint16LE(); // flags + // Note: The normal delay is a 32-bit integer (dword), whereas the overriden delay is a 16-bit integer (word) + // frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here + _videoInfo.frameDelay = 100 * _fileStream->readUint32LE(); + _videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay; + + _fileStream->seek(80); + _offsetFrame1 = _fileStream->readUint32LE(); + _offsetFrame2 = _fileStream->readUint32LE(); + + _videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height]; + _palette = (byte *)malloc(3 * 256); + memset(_palette, 0, 3 * 256); + _paletteChanged = false; + + // Seek to the first frame + _videoInfo.currentFrame = 0; + _fileStream->seek(_offsetFrame1); + return true; +} + +void FlicDecoder::closeFile() { + if (!_fileStream) + return; + + delete _fileStream; + _fileStream = 0; + + delete[] _videoFrameBuffer; + _videoFrameBuffer = 0; + + free(_palette); + + _dirtyRects.clear(); +} + +void FlicDecoder::decodeByteRun(uint8 *data) { + byte *ptr = (uint8 *)_videoFrameBuffer; + while ((uint32)(ptr - _videoFrameBuffer) < (_videoInfo.width * _videoInfo.height)) { + int chunks = *data++; + while (chunks--) { + int count = (int8)*data++; + if (count > 0) { + memset(ptr, *data++, count); + } else { + count = -count; + memcpy(ptr, data, count); + data += count; + } + ptr += count; + } + } + + // Redraw + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(0, 0, _videoInfo.width, _videoInfo.height)); +} + +#define OP_PACKETCOUNT 0 +#define OP_UNDEFINED 1 +#define OP_LASTPIXEL 2 +#define OP_LINESKIPCOUNT 3 + +void FlicDecoder::decodeDeltaFLC(uint8 *data) { + uint16 linesInChunk = READ_LE_UINT16(data); data += 2; + uint16 currentLine = 0; + uint16 packetCount = 0; + + while (linesInChunk--) { + uint16 opcode; + + // First process all the opcodes. + do { + opcode = READ_LE_UINT16(data); data += 2; + + switch ((opcode >> 14) & 3) { + case OP_PACKETCOUNT: + packetCount = opcode; + break; + case OP_UNDEFINED: + break; + case OP_LASTPIXEL: + _videoFrameBuffer[currentLine * _videoInfo.width + _videoInfo.width - 1] = (opcode & 0xFF); + _dirtyRects.push_back(Common::Rect(_videoInfo.width - 1, currentLine, _videoInfo.width, currentLine + 1)); + break; + case OP_LINESKIPCOUNT: + currentLine += -(int16)opcode; + break; + } + } while (((opcode >> 14) & 3) != OP_PACKETCOUNT); + + uint16 column = 0; + + // Now interpret the RLE data + while (packetCount--) { + column += *data++; + int rleCount = (int8)*data++; + if (rleCount > 0) { + memcpy(_videoFrameBuffer + (currentLine * _videoInfo.width) + column, data, rleCount * 2); + data += rleCount * 2; + _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); + } else if (rleCount < 0) { + rleCount = -rleCount; + uint16 dataWord = READ_UINT16(data); data += 2; + for (int i = 0; i < rleCount; ++i) { + WRITE_UINT16(_videoFrameBuffer + currentLine * _videoInfo.width + column + i * 2, dataWord); + } + _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); + } else { // End of cutscene ? + return; + } + column += rleCount * 2; + } + + currentLine++; + } +} + +#define FLI_SETPAL 4 +#define FLI_SS2 7 +#define FLI_BRUN 15 +#define PSTAMP 18 +#define FRAME_TYPE 0xF1FA + +bool FlicDecoder::decodeNextFrame() { + if (_videoInfo.currentFrame == 0) + _videoInfo.startTime = g_system->getMillis(); + + // Read chunk + uint32 frameSize = _fileStream->readUint32LE(); + uint16 frameType = _fileStream->readUint16LE(); + uint16 chunkCount = 0; + + switch (frameType) { + case FRAME_TYPE: { + chunkCount = _fileStream->readUint16LE(); + // Note: The overriden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword) + // frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here + uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds + if (newFrameDelay > 0) { + _videoInfo.frameDelay = 100 * newFrameDelay; + _videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay; + } + _fileStream->readUint16LE(); // reserved, always 0 + uint16 newWidth = _fileStream->readUint16LE(); + uint16 newHeight = _fileStream->readUint16LE(); + if (newWidth > 0) + _videoInfo.width = newWidth; + if (newHeight > 0) + _videoInfo.height = newHeight; + + _videoInfo.currentFrame++; + } + break; + default: + error("FlicDecoder::decodeFrame(): unknown main chunk type (type = 0x%02X)", frameType); + break; + } + + // Read subchunks + if (frameType == FRAME_TYPE) { + for (uint32 i = 0; i < chunkCount; ++i) { + frameSize = _fileStream->readUint32LE(); + frameType = _fileStream->readUint16LE(); + uint8 *data = new uint8[frameSize - 6]; + _fileStream->read(data, frameSize - 6); + switch (frameType) { + case FLI_SETPAL: + unpackPalette(data); + setPalette(_palette); + _paletteChanged = true; + break; + case FLI_SS2: + decodeDeltaFLC(data); + break; + case FLI_BRUN: + decodeByteRun(data); + break; + case PSTAMP: + /* PSTAMP - skip for now */ + break; + default: + error("FlicDecoder::decodeFrame(): unknown subchunk type (type = 0x%02X)", frameType); + break; + } + + delete[] data; + } + } + + // If we just processed the ring frame, set the next frame + if (_videoInfo.currentFrame == _videoInfo.frameCount + 1) { + _videoInfo.currentFrame = 1; + _fileStream->seek(_offsetFrame2); + } + + return _videoInfo.currentFrame < _videoInfo.frameCount; +} + +void FlicDecoder::reset() { + _videoInfo.currentFrame = 0; + _fileStream->seek(_offsetFrame1); +} + +void FlicDecoder::unpackPalette(uint8 *data) { + uint16 numPackets = READ_LE_UINT16(data); data += 2; + + if (0 == READ_LE_UINT16(data)) { //special case + data += 2; + for (int i = 0; i < 256; ++i) { + memcpy(_palette + i * 3, data + i * 3, 3); + } + } else { + uint8 palPos = 0; + + while (numPackets--) { + palPos += *data++; + uint8 change = *data++; + + for (int i = 0; i < change; ++i) { + memcpy(_palette + (palPos + i) * 3, data + i * 3, 3); + } + + palPos += change; + data += (change * 3); + } + } +} + +void FlicDecoder::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, _videoFrameBuffer + y * _videoInfo.width + x, (*it).right - x); + } + } + _dirtyRects.clear(); +} + +} // End of namespace Graphics diff --git a/graphics/video/flic_decoder.h b/graphics/video/flic_decoder.h new file mode 100644 index 0000000000..68bf3fb688 --- /dev/null +++ b/graphics/video/flic_decoder.h @@ -0,0 +1,89 @@ +/* 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$ + * + */ + +/** + * Video decoder used in engines: + * - tucker + */ + +#ifndef GRAPHICS_VIDEO_FlicDecoder_H +#define GRAPHICS_VIDEO_FlicDecoder_H + +#include "graphics/video/video_player.h" +#include "common/list.h" +#include "common/rect.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Graphics { + +class FlicDecoder : public VideoDecoder { +public: + FlicDecoder(); + virtual ~FlicDecoder(); + + /** + * Load a FLIC encoded video file + * @param filename the filename to load + */ + bool loadFile(const char *fileName); + + /** + * Close a FLIC encoded video file + */ + void closeFile(); + + /** + * Decode the next frame + */ + bool decodeNextFrame(); + + const Common::List *getDirtyRects() const { return &_dirtyRects; } + void clearDirtyRects() { _dirtyRects.clear(); } + void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); + + const byte *getPalette() { _paletteChanged = false; return _palette; } + bool paletteChanged() { return _paletteChanged; } + void reset(); + +private: + uint16 _offsetFrame1; + uint16 _offsetFrame2; + byte *_palette; + bool _paletteChanged; + + void decodeByteRun(uint8 *data); + void decodeDeltaFLC(uint8 *data); + void unpackPalette(uint8 *mem); + + Common::List _dirtyRects; + +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/video/flic_player.cpp b/graphics/video/flic_player.cpp deleted file mode 100644 index e58cf0bdbc..0000000000 --- a/graphics/video/flic_player.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/* 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/flic_player.h" -#include "common/archive.h" -#include "common/stream.h" -#include "common/endian.h" -#include "common/system.h" - -namespace Graphics { - -FlicDecoder::FlicDecoder() { - _paletteChanged = false; - _fileStream = 0; - _videoFrameBuffer = 0; - memset(&_videoInfo, 0, sizeof(_videoInfo)); -} - -FlicDecoder::~FlicDecoder() { - closeFile(); -} - -bool FlicDecoder::loadFile(const char *fileName) { - closeFile(); - - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; - - /* uint32 frameSize = */ _fileStream->readUint32LE(); - uint16 frameType = _fileStream->readUint16LE(); - - // Check FLC magic number - if (frameType != 0xAF12) { - warning("FlicDecoder::FlicDecoder(): attempted to load non-FLC data (type = 0x%04X)", frameType); - delete _fileStream; - _fileStream = 0; - return false; - } - - _videoInfo.frameCount = _fileStream->readUint16LE(); - _videoInfo.width = _fileStream->readUint16LE(); - _videoInfo.height = _fileStream->readUint16LE(); - uint16 colorDepth = _fileStream->readUint16LE(); - if (colorDepth != 8) { - warning("FlicDecoder::FlicDecoder(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", frameType); - delete _fileStream; - _fileStream = 0; - return false; - } - _fileStream->readUint16LE(); // flags - // Note: The normal delay is a 32-bit integer (dword), whereas the overriden delay is a 16-bit integer (word) - // frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here - _videoInfo.frameDelay = 100 * _fileStream->readUint32LE(); - _videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay; - - _fileStream->seek(80); - _offsetFrame1 = _fileStream->readUint32LE(); - _offsetFrame2 = _fileStream->readUint32LE(); - - _videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height]; - _palette = (byte *)malloc(3 * 256); - memset(_palette, 0, 3 * 256); - _paletteChanged = false; - - // Seek to the first frame - _videoInfo.currentFrame = 0; - _fileStream->seek(_offsetFrame1); - return true; -} - -void FlicDecoder::closeFile() { - if (!_fileStream) - return; - - delete _fileStream; - _fileStream = 0; - - delete[] _videoFrameBuffer; - _videoFrameBuffer = 0; - - free(_palette); - - _dirtyRects.clear(); -} - -void FlicDecoder::decodeByteRun(uint8 *data) { - byte *ptr = (uint8 *)_videoFrameBuffer; - while ((uint32)(ptr - _videoFrameBuffer) < (_videoInfo.width * _videoInfo.height)) { - int chunks = *data++; - while (chunks--) { - int count = (int8)*data++; - if (count > 0) { - memset(ptr, *data++, count); - } else { - count = -count; - memcpy(ptr, data, count); - data += count; - } - ptr += count; - } - } - - // Redraw - _dirtyRects.clear(); - _dirtyRects.push_back(Common::Rect(0, 0, _videoInfo.width, _videoInfo.height)); -} - -#define OP_PACKETCOUNT 0 -#define OP_UNDEFINED 1 -#define OP_LASTPIXEL 2 -#define OP_LINESKIPCOUNT 3 - -void FlicDecoder::decodeDeltaFLC(uint8 *data) { - uint16 linesInChunk = READ_LE_UINT16(data); data += 2; - uint16 currentLine = 0; - uint16 packetCount = 0; - - while (linesInChunk--) { - uint16 opcode; - - // First process all the opcodes. - do { - opcode = READ_LE_UINT16(data); data += 2; - - switch ((opcode >> 14) & 3) { - case OP_PACKETCOUNT: - packetCount = opcode; - break; - case OP_UNDEFINED: - break; - case OP_LASTPIXEL: - _videoFrameBuffer[currentLine * _videoInfo.width + _videoInfo.width - 1] = (opcode & 0xFF); - _dirtyRects.push_back(Common::Rect(_videoInfo.width - 1, currentLine, _videoInfo.width, currentLine + 1)); - break; - case OP_LINESKIPCOUNT: - currentLine += -(int16)opcode; - break; - } - } while (((opcode >> 14) & 3) != OP_PACKETCOUNT); - - uint16 column = 0; - - // Now interpret the RLE data - while (packetCount--) { - column += *data++; - int rleCount = (int8)*data++; - if (rleCount > 0) { - memcpy(_videoFrameBuffer + (currentLine * _videoInfo.width) + column, data, rleCount * 2); - data += rleCount * 2; - _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); - } else if (rleCount < 0) { - rleCount = -rleCount; - uint16 dataWord = READ_UINT16(data); data += 2; - for (int i = 0; i < rleCount; ++i) { - WRITE_UINT16(_videoFrameBuffer + currentLine * _videoInfo.width + column + i * 2, dataWord); - } - _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); - } else { // End of cutscene ? - return; - } - column += rleCount * 2; - } - - currentLine++; - } -} - -#define FLI_SETPAL 4 -#define FLI_SS2 7 -#define FLI_BRUN 15 -#define PSTAMP 18 -#define FRAME_TYPE 0xF1FA - -bool FlicDecoder::decodeNextFrame() { - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); - - // Read chunk - uint32 frameSize = _fileStream->readUint32LE(); - uint16 frameType = _fileStream->readUint16LE(); - uint16 chunkCount = 0; - - switch (frameType) { - case FRAME_TYPE: { - chunkCount = _fileStream->readUint16LE(); - // Note: The overriden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword) - // frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here - uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds - if (newFrameDelay > 0) { - _videoInfo.frameDelay = 100 * newFrameDelay; - _videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay; - } - _fileStream->readUint16LE(); // reserved, always 0 - uint16 newWidth = _fileStream->readUint16LE(); - uint16 newHeight = _fileStream->readUint16LE(); - if (newWidth > 0) - _videoInfo.width = newWidth; - if (newHeight > 0) - _videoInfo.height = newHeight; - - _videoInfo.currentFrame++; - } - break; - default: - error("FlicDecoder::decodeFrame(): unknown main chunk type (type = 0x%02X)", frameType); - break; - } - - // Read subchunks - if (frameType == FRAME_TYPE) { - for (uint32 i = 0; i < chunkCount; ++i) { - frameSize = _fileStream->readUint32LE(); - frameType = _fileStream->readUint16LE(); - uint8 *data = new uint8[frameSize - 6]; - _fileStream->read(data, frameSize - 6); - switch (frameType) { - case FLI_SETPAL: - unpackPalette(data); - setPalette(_palette); - _paletteChanged = true; - break; - case FLI_SS2: - decodeDeltaFLC(data); - break; - case FLI_BRUN: - decodeByteRun(data); - break; - case PSTAMP: - /* PSTAMP - skip for now */ - break; - default: - error("FlicDecoder::decodeFrame(): unknown subchunk type (type = 0x%02X)", frameType); - break; - } - - delete[] data; - } - } - - // If we just processed the ring frame, set the next frame - if (_videoInfo.currentFrame == _videoInfo.frameCount + 1) { - _videoInfo.currentFrame = 1; - _fileStream->seek(_offsetFrame2); - } - - return _videoInfo.currentFrame < _videoInfo.frameCount; -} - -void FlicDecoder::reset() { - _videoInfo.currentFrame = 0; - _fileStream->seek(_offsetFrame1); -} - -void FlicDecoder::unpackPalette(uint8 *data) { - uint16 numPackets = READ_LE_UINT16(data); data += 2; - - if (0 == READ_LE_UINT16(data)) { //special case - data += 2; - for (int i = 0; i < 256; ++i) { - memcpy(_palette + i * 3, data + i * 3, 3); - } - } else { - uint8 palPos = 0; - - while (numPackets--) { - palPos += *data++; - uint8 change = *data++; - - for (int i = 0; i < change; ++i) { - memcpy(_palette + (palPos + i) * 3, data + i * 3, 3); - } - - palPos += change; - data += (change * 3); - } - } -} - -void FlicDecoder::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, _videoFrameBuffer + y * _videoInfo.width + x, (*it).right - x); - } - } - _dirtyRects.clear(); -} - -} // End of namespace Graphics diff --git a/graphics/video/flic_player.h b/graphics/video/flic_player.h deleted file mode 100644 index 68bf3fb688..0000000000 --- a/graphics/video/flic_player.h +++ /dev/null @@ -1,89 +0,0 @@ -/* 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$ - * - */ - -/** - * Video decoder used in engines: - * - tucker - */ - -#ifndef GRAPHICS_VIDEO_FlicDecoder_H -#define GRAPHICS_VIDEO_FlicDecoder_H - -#include "graphics/video/video_player.h" -#include "common/list.h" -#include "common/rect.h" - -namespace Common { - class SeekableReadStream; -} - -namespace Graphics { - -class FlicDecoder : public VideoDecoder { -public: - FlicDecoder(); - virtual ~FlicDecoder(); - - /** - * Load a FLIC encoded video file - * @param filename the filename to load - */ - bool loadFile(const char *fileName); - - /** - * Close a FLIC encoded video file - */ - void closeFile(); - - /** - * Decode the next frame - */ - bool decodeNextFrame(); - - const Common::List *getDirtyRects() const { return &_dirtyRects; } - void clearDirtyRects() { _dirtyRects.clear(); } - void copyDirtyRectsToBuffer(uint8 *dst, uint pitch); - - const byte *getPalette() { _paletteChanged = false; return _palette; } - bool paletteChanged() { return _paletteChanged; } - void reset(); - -private: - uint16 _offsetFrame1; - uint16 _offsetFrame2; - byte *_palette; - bool _paletteChanged; - - void decodeByteRun(uint8 *data); - void decodeDeltaFLC(uint8 *data); - void unpackPalette(uint8 *mem); - - Common::List _dirtyRects; - -}; - -} // End of namespace Graphics - -#endif diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp new file mode 100644 index 0000000000..3d83bbd8ea --- /dev/null +++ b/graphics/video/smk_decoder.cpp @@ -0,0 +1,859 @@ +/* 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$ + * + */ + +// Based on http://wiki.multimedia.cx/index.php?title=Smacker +// and the FFmpeg Smacker decoder (libavcodec/smacker.c), revision 16143 +// http://svn.ffmpeg.org/ffmpeg/trunk/libavcodec/smacker.c?revision=16143&view=markup + +#include "graphics/video/smk_decoder.h" + +#include "common/archive.h" +#include "common/array.h" +#include "common/endian.h" +#include "common/util.h" +#include "common/stream.h" +#include "common/system.h" + +#include "sound/mixer.h" +#include "sound/audiostream.h" + +namespace Graphics { + +enum SmkBlockTypes { + SMK_BLOCK_MONO = 0, + SMK_BLOCK_FULL = 1, + SMK_BLOCK_SKIP = 2, + SMK_BLOCK_FILL = 3 +}; + +/* + * class BitStream + * Keeps a two-byte lookahead, so overallocate buf by 2 bytes if + * you want to avoid OOB reads. + */ + +class BitStream { +public: + BitStream(byte *buf, uint32 length) + : _buf(buf), _end(buf+length), _curBit(8) { + _curBytes = *_buf++; + _curBytes |= *_buf++ << 8; + } + + bool getBit(); + byte getBits8(); + + byte peek8() const; + void skip(int n); + +private: + byte *_buf; + byte *_end; + uint16 _curBytes; + byte _curBit; +}; + +bool BitStream::getBit() { + bool v = _curBytes & 1; + + _curBytes >>= 1; + + if (--_curBit == 0) { + _curBytes |= *_buf++ << 8; + _curBit = 8; + } + + return v; +} + +byte BitStream::getBits8() { + byte v = _curBytes & 0xff; + _curBytes >>= 8; + _curBytes |= *_buf++ << _curBit; + return v; +} + +byte BitStream::peek8() const { + return _curBytes & 0xff; +} + +void BitStream::skip(int n) { + assert(n <= 8); + _curBytes >>= n; + + if (_curBit > n) { + _curBit -= n; + } else { + _curBit = _curBit + 8 - n; + _curBytes |= *_buf++ << _curBit; + } +} + +/* + * class SmallHuffmanTree + * A Huffman-tree to hold 8-bit values. + * Unoptimized since it's only used during smk initialization. + */ + +class SmallHuffmanTree { +public: + SmallHuffmanTree(BitStream &bs) : _bs(bs) { + uint32 bit = _bs.getBit(); + assert(bit); + + decodeTree(0); + + bit = _bs.getBit(); + assert(!bit); + } + + uint16 getCode(BitStream &bs); +private: + enum { + SMK_NODE = 0x8000 + }; + + int decodeTree(int length); + + Common::Array _tree; + BitStream &_bs; +}; + +int SmallHuffmanTree::decodeTree(int length) { + if (!_bs.getBit()) { // Leaf + uint16 v = _bs.getBits8(); + + _tree.push_back(v); + return 1; + } + + _tree.push_back(0); // placeholder for r1 + int t = _tree.size() - 1; + + int r1 = decodeTree(length + 1); + + _tree[t] = (SMK_NODE | r1); + + int r2 = decodeTree(length + 1); + + return r1+r2+1; +} + +uint16 SmallHuffmanTree::getCode(BitStream &bs) { + uint16 *p = &_tree[0]; + + while (*p & SMK_NODE) { + if (bs.getBit()) + p += *p & ~SMK_NODE; + p++; + } + + return *p; +} + +/* + * class BigHuffmanTree + * A Huffman-tree to hold 16-bit values. + * Contains the beginnings of an optimization. + */ + +class BigHuffmanTree { +public: + BigHuffmanTree(BitStream &bs); + + void reset(); + uint32 getCode(BitStream &bs); +private: + enum { + SMK_NODE = 0x80000000 + }; + + int decodeTree(uint32 prefix, int length); + + Common::Array _tree; + uint32 _last[3]; + + int _prefixtree[256]; + int _prefixlength[256]; + + /* Used during construction */ + BitStream &_bs; + uint32 _markers[3]; + SmallHuffmanTree *_loBytes; + SmallHuffmanTree *_hiBytes; +}; + +BigHuffmanTree::BigHuffmanTree(BitStream &bs) + : _bs(bs) { + uint32 bit = _bs.getBit(); + if (!bit) { + _tree.push_back(0); + _last[0] = _last[1] = _last[2] = 0; + return; + } + + int i; + for (i = 0; i < 256; ++i) + _prefixtree[i] = 0; + + _loBytes = new SmallHuffmanTree(_bs); + _hiBytes = new SmallHuffmanTree(_bs); + + _markers[0] = _bs.getBits8() | (_bs.getBits8() << 8); + _markers[1] = _bs.getBits8() | (_bs.getBits8() << 8); + _markers[2] = _bs.getBits8() | (_bs.getBits8() << 8); + + _last[0] = _last[1] = _last[2] = 0xffffffff; + + decodeTree(0, 0); + bit = _bs.getBit(); + assert(!bit); + + for (i = 0; i < 3; ++i) { + if (_last[i] == 0xffffffff) { + _tree.push_back(0); + _last[i] = _tree.size() - 1; + } + } + + delete _loBytes; + delete _hiBytes; +} + +void BigHuffmanTree::reset() { + _tree[_last[0]] = _tree[_last[1]] = _tree[_last[2]] = 0; +} + +int BigHuffmanTree::decodeTree(uint32 prefix, int length) { + uint32 bit = _bs.getBit(); + + if (!bit) { // Leaf + uint32 lo = _loBytes->getCode(_bs); + uint32 hi = _hiBytes->getCode(_bs); + + uint32 v = (hi << 8) | lo; + _tree.push_back(v); + + int t = _tree.size() - 1; + + if (length <= 8) { + uint32 i; + for (i = 0; i < 256; i += (1 << length)) { + _prefixtree[prefix | i] = t; + _prefixlength[prefix | i] = length; + } + } + + int i; + for (i = 0; i < 3; ++i) { + if (_markers[i] == v) { + _last[i] = t; + _tree[t] = 0; + } + } + + return 1; + } + + _tree.push_back(0); // placeholder for r1 + int t = _tree.size() - 1; + + if (length == 8) { + _prefixtree[prefix] = t; + _prefixlength[prefix] = 8; + } + + int r1 = decodeTree(prefix, length + 1); + + _tree[t] = SMK_NODE | r1; + + int r2 = decodeTree(prefix | (1 << length), length + 1); + return r1+r2+1; +} + +uint32 BigHuffmanTree::getCode(BitStream &bs) { + uint32 *p = &_tree[0]; + + byte peek = bs.peek8(); + p = &_tree[_prefixtree[peek]]; + bs.skip(_prefixlength[peek]); + + while (*p & SMK_NODE) { + if (bs.getBit()) + p += (*p) & ~SMK_NODE; + p++; + } + + uint32 v = *p; + if (v != _tree[_last[0]]) { + _tree[_last[2]] = _tree[_last[1]]; + _tree[_last[1]] = _tree[_last[0]]; + _tree[_last[0]] = v; + } + + return v; +} + +SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer) + : _audioStarted(false), _audioStream(0), _mixer(mixer) { +} + +SmackerDecoder::~SmackerDecoder() { + closeFile(); +} + +int SmackerDecoder::getHeight() { + if (!_fileStream) + return 0; + return (_header.flags ? 2 : 1) * _videoInfo.height; +} + +int32 SmackerDecoder::getAudioLag() { + if (!_fileStream) + return 0; + + int32 frameDelay = getFrameDelay(); + int32 videoTime = _videoInfo.currentFrame * frameDelay; + int32 audioTime; + + if (!_audioStream) { + /* No audio. + Calculate the lag by how much time has gone by since the first frame + and how much time *should* have passed. + */ + + audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100; + } else + audioTime = (((int32) _mixer->getSoundElapsedTime(_audioHandle)) * 100); + + return videoTime - audioTime; +} + +bool SmackerDecoder::loadFile(const char *fileName) { + int32 frameRate; + + closeFile(); + + _fileStream = SearchMan.createReadStreamForMember(fileName); + if (!_fileStream) + return false; + + // Seek to the first frame + _videoInfo.currentFrame = 0; + _header.signature = _fileStream->readUint32BE(); + + // No BINK support available + if (_header.signature == MKID_BE('BIKi')) { + delete _fileStream; + _fileStream = 0; + return false; + } + + assert(_header.signature == MKID_BE('SMK2') || _header.signature == MKID_BE('SMK4')); + + _videoInfo.width = _fileStream->readUint32LE(); + _videoInfo.height = _fileStream->readUint32LE(); + _videoInfo.frameCount = _fileStream->readUint32LE(); + frameRate = _fileStream->readSint32LE(); + + if (frameRate > 0) { + _videoInfo.frameRate = 1000 / frameRate; + _videoInfo.frameDelay = frameRate * 100; + } else if (frameRate < 0) { + _videoInfo.frameRate = 100000 / (-frameRate); + _videoInfo.frameDelay = -frameRate; + } else { + _videoInfo.frameRate = 10; + _videoInfo.frameDelay = 10000; + } + + // Flags are determined by which bit is set, which can be one of the following: + // 0 - set to 1 if file contains a ring frame. + // 1 - set to 1 if file is Y-interlaced + // 2 - set to 1 if file is Y-doubled + // If bits 1 or 2 are set, the frame should be scaled to twice its height + // before it is displayed. + _header.flags = _fileStream->readUint32LE(); + + // TODO: should we do any extra processing for Smacker files with ring frames? + + // TODO: should we do any extra processing for Y-doubled videos? Are they the + // same as Y-interlaced videos? + + uint32 i; + for (i = 0; i < 7; ++i) + _header.audioSize[i] = _fileStream->readUint32LE(); + + _header.treesSize = _fileStream->readUint32LE(); + _header.mMapSize = _fileStream->readUint32LE(); + _header.mClrSize = _fileStream->readUint32LE(); + _header.fullSize = _fileStream->readUint32LE(); + _header.typeSize = _fileStream->readUint32LE(); + + uint32 audioInfo; + for (i = 0; i < 7; ++i) { + // AudioRate - Frequency and format information for each sound track, up to 7 audio tracks. + // The 32 constituent bits have the following meaning: + // * bit 31 - data is compressed + // * bit 30 - indicates that audio data is present for this track + // * bit 29 - 1 = 16-bit audio; 0 = 8-bit audio + // * bit 28 - 1 = stereo audio; 0 = mono audio + // * bits 27-26 - if both set to zero - use v2 sound decompression + // * bits 25-24 - unused + // * bits 23-0 - audio sample rate + audioInfo = _fileStream->readUint32LE(); + _header.audioInfo[i].isCompressed = audioInfo & 0x80000000; + _header.audioInfo[i].hasAudio = audioInfo & 0x40000000; + _header.audioInfo[i].is16Bits = audioInfo & 0x20000000; + _header.audioInfo[i].isStereo = audioInfo & 0x10000000; + _header.audioInfo[i].hasV2Compression = !(audioInfo & 0x8000000) && + !(audioInfo & 0x4000000); + _header.audioInfo[i].sampleRate = audioInfo & 0xFFFFFF; + + if (_header.audioInfo[i].hasAudio && i == 0) { + byte flags = 0; + + if (_header.audioInfo[i].is16Bits) + flags = flags | Audio::Mixer::FLAG_16BITS; + + if (_header.audioInfo[i].isStereo) + flags = flags | Audio::Mixer::FLAG_STEREO; + + _audioStream = Audio::makeAppendableAudioStream(_header.audioInfo[i].sampleRate, flags); + } + } + + _header.dummy = _fileStream->readUint32LE(); + + _frameSizes = (uint32 *)malloc(_videoInfo.frameCount * sizeof(uint32)); + for (i = 0; i < _videoInfo.frameCount; ++i) + _frameSizes[i] = _fileStream->readUint32LE(); + + _frameTypes = (byte *)malloc(_videoInfo.frameCount); + for (i = 0; i < _videoInfo.frameCount; ++i) + _frameTypes[i] = _fileStream->readByte(); + + Common::Array huffmanTrees; + huffmanTrees.resize(_header.treesSize + 2); + _fileStream->read(&huffmanTrees[0], _header.treesSize); + huffmanTrees[_header.treesSize] = 0; + huffmanTrees[_header.treesSize + 1] = 0; + + BitStream bs(&huffmanTrees[0], _header.treesSize); + + _MMapTree = new BigHuffmanTree(bs); + _MClrTree = new BigHuffmanTree(bs); + _FullTree = new BigHuffmanTree(bs); + _TypeTree = new BigHuffmanTree(bs); + + _videoFrameBuffer = (byte *)malloc(2 * _videoInfo.width * _videoInfo.height); + memset(_videoFrameBuffer, 0, 2 * _videoInfo.width * _videoInfo.height); + _palette = (byte *)malloc(3 * 256); + memset(_palette, 0, 3 * 256); + + _videoInfo.firstframeOffset = _fileStream->pos(); + + return true; +} + +void SmackerDecoder::closeFile() { + if (!_fileStream) + return; + + if (_audioStarted && _audioStream) { + _mixer->stopHandle(_audioHandle); + _audioStream = 0; + _audioStarted = false; + } + + delete _fileStream; + _fileStream = 0; + + delete _MMapTree; + delete _MClrTree; + delete _FullTree; + delete _TypeTree; + + free(_frameSizes); + free(_frameTypes); + free(_videoFrameBuffer); + free(_palette); +} + +bool SmackerDecoder::decodeNextFrame() { + uint i; + uint32 chunkSize = 0; + uint32 dataSizeUnpacked = 0; + + uint32 startPos = _fileStream->pos(); + + if (_videoInfo.currentFrame == 0) + _videoInfo.startTime = g_system->getMillis(); + + // Check if we got a frame with palette data, and + // call back the virtual setPalette function to set + // the current palette + if (_frameTypes[_videoInfo.currentFrame] & 1) { + unpackPalette(); + setPalette(_palette); + } + + // Load audio tracks + for (i = 0; i < 7; ++i) { + if (!(_frameTypes[_videoInfo.currentFrame] & (2 << i))) + continue; + + chunkSize = _fileStream->readUint32LE(); + chunkSize -= 4; // subtract the first 4 bytes (chunk size) + + if (_header.audioInfo[i].isCompressed) { + dataSizeUnpacked = _fileStream->readUint32LE(); + chunkSize -= 4; // subtract the next 4 bytes (unpacked data size) + } else { + dataSizeUnpacked = 0; + } + + if (_header.audioInfo[i].hasAudio && chunkSize > 0 && i == 0) { + // If it's track 0, play the audio data + byte *soundBuffer = new byte[chunkSize + 2]; + + _fileStream->read(soundBuffer, chunkSize); + soundBuffer[chunkSize] = 0; + soundBuffer[chunkSize + 1] = 0; + + if (_header.audioInfo[i].isCompressed) { + // Compressed audio (Huffman DPCM encoded) + queueCompressedBuffer(soundBuffer, chunkSize, dataSizeUnpacked, i); + delete[] soundBuffer; + } else { + // Uncompressed audio (PCM) + _audioStream->queueBuffer(soundBuffer, chunkSize); + // The sound buffer will be deleted by AppendableAudioStream + } + + if (!_audioStarted) { + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_audioHandle, _audioStream, -1, 255); + _audioStarted = true; + } + } else { + // Ignore the rest of the audio tracks, if they exist + // TODO: Are there any Smacker videos with more than one audio stream? + // If yes, we should play the rest of the audio streams as well + if (chunkSize > 0) + _fileStream->skip(chunkSize); + } + } + + uint32 frameSize = _frameSizes[_videoInfo.currentFrame] & ~3; + + if (_fileStream->pos() - startPos > frameSize) + exit(1); + + uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos); + + _frameData = (byte *)malloc(frameDataSize + 2); + _fileStream->read(_frameData, frameDataSize); + _frameData[frameDataSize] = 0; + _frameData[frameDataSize + 1] = 0; + + BitStream bs(_frameData, frameDataSize); + + _MMapTree->reset(); + _MClrTree->reset(); + _FullTree->reset(); + _TypeTree->reset(); + + uint bw = _videoInfo.width / 4; + uint bh = _videoInfo.height / 4; + uint stride = _videoInfo.width; + uint block = 0, blocks = bw*bh; + + uint doubleY = _header.flags ? 2 : 1; + + byte *out; + uint type, run, j, mode; + uint32 p1, p2, clr, map; + byte hi, lo; + + while (block < blocks) { + type = _TypeTree->getCode(bs); + run = getBlockRun((type >> 2) & 0x3f); + + switch (type & 3) { + case SMK_BLOCK_MONO: + while (run-- && block < blocks) { + clr = _MClrTree->getCode(bs); + map = _MMapTree->getCode(bs); + out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; + hi = clr >> 8; + lo = clr & 0xff; + for (i = 0; i < 4; i++) { + for (j = 0; j < doubleY; j++) { + out[0] = (map & 1) ? hi : lo; + out[1] = (map & 2) ? hi : lo; + out[2] = (map & 4) ? hi : lo; + out[3] = (map & 8) ? hi : lo; + out += stride; + } + map >>= 4; + } + ++block; + } + break; + case SMK_BLOCK_FULL: + // Smacker v2 has one mode, Smacker v4 has three + if (_header.signature == MKID_BE('SMK2')) { + mode = 0; + } else { + // 00 - mode 0 + // 10 - mode 1 + // 01 - mode 2 + mode = 0; + if (bs.getBit()) { + mode = 1; + } else if (bs.getBit()) { + mode = 2; + } + } + + while (run-- && block < blocks) { + out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; + switch (mode) { + case 0: + for (i = 0; i < 4; ++i) { + p1 = _FullTree->getCode(bs); + p2 = _FullTree->getCode(bs); + for (j = 0; j < doubleY; ++j) { + out[2] = p1 & 0xff; + out[3] = p1 >> 8; + out[0] = p2 & 0xff; + out[1] = p2 >> 8; + out += stride; + } + } + break; + case 1: + p1 = _FullTree->getCode(bs); + out[0] = out[1] = p1 & 0xFF; + out[2] = out[3] = p1 >> 8; + out += stride; + out[0] = out[1] = p1 & 0xFF; + out[2] = out[3] = p1 >> 8; + out += stride; + p2 = _FullTree->getCode(bs); + out[0] = out[1] = p2 & 0xFF; + out[2] = out[3] = p2 >> 8; + out += stride; + out[0] = out[1] = p2 & 0xFF; + out[2] = out[3] = p2 >> 8; + out += stride; + break; + case 2: + for (i = 0; i < 2; i++) { + // We first get p2 and then p1 + // Check ffmpeg thread "[PATCH] Smacker video decoder bug fix" + // http://article.gmane.org/gmane.comp.video.ffmpeg.devel/78768 + p2 = _FullTree->getCode(bs); + p1 = _FullTree->getCode(bs); + for (j = 0; j < doubleY; ++j) { + out[0] = p1 & 0xff; + out[1] = p1 >> 8; + out[2] = p2 & 0xff; + out[3] = p2 >> 8; + out += stride; + } + for (j = 0; j < doubleY; ++j) { + out[0] = p1 & 0xff; + out[1] = p1 >> 8; + out[2] = p2 & 0xff; + out[3] = p2 >> 8; + out += stride; + } + } + break; + } + ++block; + } + break; + case SMK_BLOCK_SKIP: + while (run-- && block < blocks) + block++; + break; + case SMK_BLOCK_FILL: + uint32 col; + mode = type >> 8; + while (run-- && block < blocks) { + out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; + col = mode * 0x01010101; + for (i = 0; i < 4 * doubleY; ++i) { + out[0] = out[1] = out[2] = out[3] = col; + out += stride; + } + ++block; + } + break; + } + } + + _fileStream->seek(startPos + frameSize); + + free(_frameData); + + return ++_videoInfo.currentFrame < _videoInfo.frameCount; +} + +void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, + uint32 unpackedSize, int streamNum) { + + BitStream audioBS(buffer, bufferSize); + bool dataPresent = audioBS.getBit(); + + if (!dataPresent) + return; + + bool isStereo = audioBS.getBit(); + assert(isStereo == _header.audioInfo[streamNum].isStereo); + bool is16Bits = audioBS.getBit(); + assert(is16Bits == _header.audioInfo[streamNum].is16Bits); + + int numBytes = 1 * (isStereo ? 2 : 1) * (is16Bits ? 2 : 1); + + byte *unpackedBuffer = new byte[unpackedSize]; + byte *curPointer = unpackedBuffer; + uint32 curPos = 0; + + SmallHuffmanTree *audioTrees[4]; + for (int k = 0; k < numBytes; k++) + audioTrees[k] = new SmallHuffmanTree(audioBS); + + // Base values, stored as big endian + + int32 bases[2]; + + if (isStereo) + bases[1] = (!is16Bits) ? audioBS.getBits8() : + ((int16) (((audioBS.getBits8() << 8) | audioBS.getBits8()))); + + bases[0] = (!is16Bits) ? audioBS.getBits8() : + ((int16) (((audioBS.getBits8() << 8) | audioBS.getBits8()))); + + + // The bases are the first samples, too + for (int i = 0; i < (isStereo ? 2 : 1); i++, curPointer += (is16Bits ? 2 : 1), curPos += (is16Bits ? 2 : 1)) { + if (is16Bits) + WRITE_BE_UINT16(curPointer, bases[i]); + else + *curPointer = (bases[i] & 0xFF) ^ 0x80; + } + + // Next follow the deltas, which are added to the corresponding base values and + // are stored as little endian + // We store the unpacked bytes in big endian format + + while (curPos < unpackedSize) { + // If the sample is stereo, the data is stored for the left and right channel, respectively + // (the exact opposite to the base values) + if (!is16Bits) { + + for (int k = 0; k < (isStereo ? 2 : 1); k++) { + bases[k] += (int8) ((int16) audioTrees[k]->getCode(audioBS)); + *curPointer++ = CLIP(bases[k], 0, 255) ^ 0x80; + curPos++; + } + + } else { + + for (int k = 0; k < (isStereo ? 2 : 1); k++) { + bases[k] += (int16) (audioTrees[k * 2]->getCode(audioBS) | + (audioTrees[k * 2 + 1]->getCode(audioBS) << 8)); + + WRITE_BE_UINT16(curPointer, CLIP(bases[k], -32768, 32767)); + curPointer += 2; + curPos += 2; + } + } + + } + + for (int k = 0; k < numBytes; k++) + delete audioTrees[k]; + + _audioStream->queueBuffer(unpackedBuffer, unpackedSize); + // unpackedBuffer will be deleted by AppendableAudioStream +} + +void SmackerDecoder::unpackPalette() { + uint startPos = _fileStream->pos(); + uint32 len = 4 * _fileStream->readByte(); + + byte *chunk = (byte *)malloc(len); + _fileStream->read(&chunk[0], len); + byte *p = &chunk[0]; + + byte oldPalette[3*256]; + memcpy(oldPalette, _palette, 3 * 256); + + byte *pal = _palette; + + int sz = 0; + byte b0; + while (sz < 256) { + b0 = *p++; + if (b0 & 0x80) { // if top bit is 1 (0x80 = 10000000) + sz += (b0 & 0x7f) + 1; // get lower 7 bits + 1 (0x7f = 01111111) + pal += 3 * ((b0 & 0x7f) + 1); + } else if (b0 & 0x40) { // if top 2 bits are 01 (0x40 = 01000000) + byte c = (b0 & 0x3f) + 1; // get lower 6 bits + 1 (0x3f = 00111111) + uint s = 3 * *p++; + sz += c; + + while (c--) { + *pal++ = oldPalette[s + 0]; + *pal++ = oldPalette[s + 1]; + *pal++ = oldPalette[s + 2]; + s += 3; + } + } else { // top 2 bits are 00 + sz++; + // get the lower 6 bits for each component (0x3f = 00111111) + byte b = b0 & 0x3f; + byte g = (*p++) & 0x3f; + byte r = (*p++) & 0x3f; + + assert(g < 0xc0 && b < 0xc0); + + // upscale to full 8-bit color values by multiplying by 4 + *pal++ = b * 4; + *pal++ = g * 4; + *pal++ = r * 4; + } + } + + _fileStream->seek(startPos + len); + + free(chunk); +} + +} // End of namespace Graphics diff --git a/graphics/video/smk_decoder.h b/graphics/video/smk_decoder.h new file mode 100644 index 0000000000..e70d0e6454 --- /dev/null +++ b/graphics/video/smk_decoder.h @@ -0,0 +1,129 @@ +/* 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$ + * + */ + +/** + * Video decoder used in engines: + * - agos + * - saga + * - scumm (he) + * - sword1 + * - sword2 + */ + +// Based on http://wiki.multimedia.cx/index.php?title=Smacker +// and the FFmpeg Smacker decoder (libavcodec/smacker.c), revision 16143 +// http://svn.ffmpeg.org/ffmpeg/trunk/libavcodec/smacker.c?revision=16143&view=markup + +#ifndef GRAPHICS_VIDEO_SMK_PLAYER_H +#define GRAPHICS_VIDEO_SMK_PLAYER_H + +#include "graphics/video/video_player.h" +#include "sound/mixer.h" + +namespace Audio { + class AppendableAudioStream; +} + +namespace Graphics { + +class BigHuffmanTree; + +/** + * Implementation of a Smacker v2/v4 video decoder + */ +class SmackerDecoder : public VideoDecoder { +public: + SmackerDecoder(Audio::Mixer *mixer); + virtual ~SmackerDecoder(); + + int getHeight(); + int32 getAudioLag(); + + /** + * Load an SMK encoded video file + * @param filename the filename to load + */ + bool loadFile(const char *filename); + + /** + * Close an SMK encoded video file + */ + void closeFile(); + + bool decodeNextFrame(); + +private: + void unpackPalette(); + // Possible runs of blocks + uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } + void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum); + + struct AudioInfo { + bool isCompressed; + bool hasAudio; + bool is16Bits; + bool isStereo; + bool hasV2Compression; + uint32 sampleRate; + }; + + struct { + uint32 signature; + uint32 flags; + uint32 audioSize[7]; + uint32 treesSize; + uint32 mMapSize; + uint32 mClrSize; + uint32 fullSize; + uint32 typeSize; + AudioInfo audioInfo[7]; + uint32 dummy; + } _header; + + uint32 *_frameSizes; + // The FrameTypes section of a Smacker file contains an array of bytes, where + // the 8 bits of each byte describe the contents of the corresponding frame. + // The highest 7 bits correspond to audio frames (bit 7 is track 6, bit 6 track 5 + // and so on), so there can be up to 7 different audio tracks. When the lowest bit + // (bit 0) is set, it denotes a frame that contains a palette record + byte *_frameTypes; + byte *_frameData; + // The RGB palette + byte *_palette; + + Audio::Mixer *_mixer; + bool _audioStarted; + Audio::AppendableAudioStream *_audioStream; + Audio::SoundHandle _audioHandle; + + BigHuffmanTree *_MMapTree; + BigHuffmanTree *_MClrTree; + BigHuffmanTree *_FullTree; + BigHuffmanTree *_TypeTree; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/video/smk_player.cpp b/graphics/video/smk_player.cpp deleted file mode 100644 index ebbf61d25a..0000000000 --- a/graphics/video/smk_player.cpp +++ /dev/null @@ -1,859 +0,0 @@ -/* 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$ - * - */ - -// Based on http://wiki.multimedia.cx/index.php?title=Smacker -// and the FFmpeg Smacker decoder (libavcodec/smacker.c), revision 16143 -// http://svn.ffmpeg.org/ffmpeg/trunk/libavcodec/smacker.c?revision=16143&view=markup - -#include "graphics/video/smk_player.h" - -#include "common/archive.h" -#include "common/array.h" -#include "common/endian.h" -#include "common/util.h" -#include "common/stream.h" -#include "common/system.h" - -#include "sound/mixer.h" -#include "sound/audiostream.h" - -namespace Graphics { - -enum SmkBlockTypes { - SMK_BLOCK_MONO = 0, - SMK_BLOCK_FULL = 1, - SMK_BLOCK_SKIP = 2, - SMK_BLOCK_FILL = 3 -}; - -/* - * class BitStream - * Keeps a two-byte lookahead, so overallocate buf by 2 bytes if - * you want to avoid OOB reads. - */ - -class BitStream { -public: - BitStream(byte *buf, uint32 length) - : _buf(buf), _end(buf+length), _curBit(8) { - _curBytes = *_buf++; - _curBytes |= *_buf++ << 8; - } - - bool getBit(); - byte getBits8(); - - byte peek8() const; - void skip(int n); - -private: - byte *_buf; - byte *_end; - uint16 _curBytes; - byte _curBit; -}; - -bool BitStream::getBit() { - bool v = _curBytes & 1; - - _curBytes >>= 1; - - if (--_curBit == 0) { - _curBytes |= *_buf++ << 8; - _curBit = 8; - } - - return v; -} - -byte BitStream::getBits8() { - byte v = _curBytes & 0xff; - _curBytes >>= 8; - _curBytes |= *_buf++ << _curBit; - return v; -} - -byte BitStream::peek8() const { - return _curBytes & 0xff; -} - -void BitStream::skip(int n) { - assert(n <= 8); - _curBytes >>= n; - - if (_curBit > n) { - _curBit -= n; - } else { - _curBit = _curBit + 8 - n; - _curBytes |= *_buf++ << _curBit; - } -} - -/* - * class SmallHuffmanTree - * A Huffman-tree to hold 8-bit values. - * Unoptimized since it's only used during smk initialization. - */ - -class SmallHuffmanTree { -public: - SmallHuffmanTree(BitStream &bs) : _bs(bs) { - uint32 bit = _bs.getBit(); - assert(bit); - - decodeTree(0); - - bit = _bs.getBit(); - assert(!bit); - } - - uint16 getCode(BitStream &bs); -private: - enum { - SMK_NODE = 0x8000 - }; - - int decodeTree(int length); - - Common::Array _tree; - BitStream &_bs; -}; - -int SmallHuffmanTree::decodeTree(int length) { - if (!_bs.getBit()) { // Leaf - uint16 v = _bs.getBits8(); - - _tree.push_back(v); - return 1; - } - - _tree.push_back(0); // placeholder for r1 - int t = _tree.size() - 1; - - int r1 = decodeTree(length + 1); - - _tree[t] = (SMK_NODE | r1); - - int r2 = decodeTree(length + 1); - - return r1+r2+1; -} - -uint16 SmallHuffmanTree::getCode(BitStream &bs) { - uint16 *p = &_tree[0]; - - while (*p & SMK_NODE) { - if (bs.getBit()) - p += *p & ~SMK_NODE; - p++; - } - - return *p; -} - -/* - * class BigHuffmanTree - * A Huffman-tree to hold 16-bit values. - * Contains the beginnings of an optimization. - */ - -class BigHuffmanTree { -public: - BigHuffmanTree(BitStream &bs); - - void reset(); - uint32 getCode(BitStream &bs); -private: - enum { - SMK_NODE = 0x80000000 - }; - - int decodeTree(uint32 prefix, int length); - - Common::Array _tree; - uint32 _last[3]; - - int _prefixtree[256]; - int _prefixlength[256]; - - /* Used during construction */ - BitStream &_bs; - uint32 _markers[3]; - SmallHuffmanTree *_loBytes; - SmallHuffmanTree *_hiBytes; -}; - -BigHuffmanTree::BigHuffmanTree(BitStream &bs) - : _bs(bs) { - uint32 bit = _bs.getBit(); - if (!bit) { - _tree.push_back(0); - _last[0] = _last[1] = _last[2] = 0; - return; - } - - int i; - for (i = 0; i < 256; ++i) - _prefixtree[i] = 0; - - _loBytes = new SmallHuffmanTree(_bs); - _hiBytes = new SmallHuffmanTree(_bs); - - _markers[0] = _bs.getBits8() | (_bs.getBits8() << 8); - _markers[1] = _bs.getBits8() | (_bs.getBits8() << 8); - _markers[2] = _bs.getBits8() | (_bs.getBits8() << 8); - - _last[0] = _last[1] = _last[2] = 0xffffffff; - - decodeTree(0, 0); - bit = _bs.getBit(); - assert(!bit); - - for (i = 0; i < 3; ++i) { - if (_last[i] == 0xffffffff) { - _tree.push_back(0); - _last[i] = _tree.size() - 1; - } - } - - delete _loBytes; - delete _hiBytes; -} - -void BigHuffmanTree::reset() { - _tree[_last[0]] = _tree[_last[1]] = _tree[_last[2]] = 0; -} - -int BigHuffmanTree::decodeTree(uint32 prefix, int length) { - uint32 bit = _bs.getBit(); - - if (!bit) { // Leaf - uint32 lo = _loBytes->getCode(_bs); - uint32 hi = _hiBytes->getCode(_bs); - - uint32 v = (hi << 8) | lo; - _tree.push_back(v); - - int t = _tree.size() - 1; - - if (length <= 8) { - uint32 i; - for (i = 0; i < 256; i += (1 << length)) { - _prefixtree[prefix | i] = t; - _prefixlength[prefix | i] = length; - } - } - - int i; - for (i = 0; i < 3; ++i) { - if (_markers[i] == v) { - _last[i] = t; - _tree[t] = 0; - } - } - - return 1; - } - - _tree.push_back(0); // placeholder for r1 - int t = _tree.size() - 1; - - if (length == 8) { - _prefixtree[prefix] = t; - _prefixlength[prefix] = 8; - } - - int r1 = decodeTree(prefix, length + 1); - - _tree[t] = SMK_NODE | r1; - - int r2 = decodeTree(prefix | (1 << length), length + 1); - return r1+r2+1; -} - -uint32 BigHuffmanTree::getCode(BitStream &bs) { - uint32 *p = &_tree[0]; - - byte peek = bs.peek8(); - p = &_tree[_prefixtree[peek]]; - bs.skip(_prefixlength[peek]); - - while (*p & SMK_NODE) { - if (bs.getBit()) - p += (*p) & ~SMK_NODE; - p++; - } - - uint32 v = *p; - if (v != _tree[_last[0]]) { - _tree[_last[2]] = _tree[_last[1]]; - _tree[_last[1]] = _tree[_last[0]]; - _tree[_last[0]] = v; - } - - return v; -} - -SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer) - : _audioStarted(false), _audioStream(0), _mixer(mixer) { -} - -SmackerDecoder::~SmackerDecoder() { - closeFile(); -} - -int SmackerDecoder::getHeight() { - if (!_fileStream) - return 0; - return (_header.flags ? 2 : 1) * _videoInfo.height; -} - -int32 SmackerDecoder::getAudioLag() { - if (!_fileStream) - return 0; - - int32 frameDelay = getFrameDelay(); - int32 videoTime = _videoInfo.currentFrame * frameDelay; - int32 audioTime; - - if (!_audioStream) { - /* No audio. - Calculate the lag by how much time has gone by since the first frame - and how much time *should* have passed. - */ - - audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100; - } else - audioTime = (((int32) _mixer->getSoundElapsedTime(_audioHandle)) * 100); - - return videoTime - audioTime; -} - -bool SmackerDecoder::loadFile(const char *fileName) { - int32 frameRate; - - closeFile(); - - _fileStream = SearchMan.createReadStreamForMember(fileName); - if (!_fileStream) - return false; - - // Seek to the first frame - _videoInfo.currentFrame = 0; - _header.signature = _fileStream->readUint32BE(); - - // No BINK support available - if (_header.signature == MKID_BE('BIKi')) { - delete _fileStream; - _fileStream = 0; - return false; - } - - assert(_header.signature == MKID_BE('SMK2') || _header.signature == MKID_BE('SMK4')); - - _videoInfo.width = _fileStream->readUint32LE(); - _videoInfo.height = _fileStream->readUint32LE(); - _videoInfo.frameCount = _fileStream->readUint32LE(); - frameRate = _fileStream->readSint32LE(); - - if (frameRate > 0) { - _videoInfo.frameRate = 1000 / frameRate; - _videoInfo.frameDelay = frameRate * 100; - } else if (frameRate < 0) { - _videoInfo.frameRate = 100000 / (-frameRate); - _videoInfo.frameDelay = -frameRate; - } else { - _videoInfo.frameRate = 10; - _videoInfo.frameDelay = 10000; - } - - // Flags are determined by which bit is set, which can be one of the following: - // 0 - set to 1 if file contains a ring frame. - // 1 - set to 1 if file is Y-interlaced - // 2 - set to 1 if file is Y-doubled - // If bits 1 or 2 are set, the frame should be scaled to twice its height - // before it is displayed. - _header.flags = _fileStream->readUint32LE(); - - // TODO: should we do any extra processing for Smacker files with ring frames? - - // TODO: should we do any extra processing for Y-doubled videos? Are they the - // same as Y-interlaced videos? - - uint32 i; - for (i = 0; i < 7; ++i) - _header.audioSize[i] = _fileStream->readUint32LE(); - - _header.treesSize = _fileStream->readUint32LE(); - _header.mMapSize = _fileStream->readUint32LE(); - _header.mClrSize = _fileStream->readUint32LE(); - _header.fullSize = _fileStream->readUint32LE(); - _header.typeSize = _fileStream->readUint32LE(); - - uint32 audioInfo; - for (i = 0; i < 7; ++i) { - // AudioRate - Frequency and format information for each sound track, up to 7 audio tracks. - // The 32 constituent bits have the following meaning: - // * bit 31 - data is compressed - // * bit 30 - indicates that audio data is present for this track - // * bit 29 - 1 = 16-bit audio; 0 = 8-bit audio - // * bit 28 - 1 = stereo audio; 0 = mono audio - // * bits 27-26 - if both set to zero - use v2 sound decompression - // * bits 25-24 - unused - // * bits 23-0 - audio sample rate - audioInfo = _fileStream->readUint32LE(); - _header.audioInfo[i].isCompressed = audioInfo & 0x80000000; - _header.audioInfo[i].hasAudio = audioInfo & 0x40000000; - _header.audioInfo[i].is16Bits = audioInfo & 0x20000000; - _header.audioInfo[i].isStereo = audioInfo & 0x10000000; - _header.audioInfo[i].hasV2Compression = !(audioInfo & 0x8000000) && - !(audioInfo & 0x4000000); - _header.audioInfo[i].sampleRate = audioInfo & 0xFFFFFF; - - if (_header.audioInfo[i].hasAudio && i == 0) { - byte flags = 0; - - if (_header.audioInfo[i].is16Bits) - flags = flags | Audio::Mixer::FLAG_16BITS; - - if (_header.audioInfo[i].isStereo) - flags = flags | Audio::Mixer::FLAG_STEREO; - - _audioStream = Audio::makeAppendableAudioStream(_header.audioInfo[i].sampleRate, flags); - } - } - - _header.dummy = _fileStream->readUint32LE(); - - _frameSizes = (uint32 *)malloc(_videoInfo.frameCount * sizeof(uint32)); - for (i = 0; i < _videoInfo.frameCount; ++i) - _frameSizes[i] = _fileStream->readUint32LE(); - - _frameTypes = (byte *)malloc(_videoInfo.frameCount); - for (i = 0; i < _videoInfo.frameCount; ++i) - _frameTypes[i] = _fileStream->readByte(); - - Common::Array huffmanTrees; - huffmanTrees.resize(_header.treesSize + 2); - _fileStream->read(&huffmanTrees[0], _header.treesSize); - huffmanTrees[_header.treesSize] = 0; - huffmanTrees[_header.treesSize + 1] = 0; - - BitStream bs(&huffmanTrees[0], _header.treesSize); - - _MMapTree = new BigHuffmanTree(bs); - _MClrTree = new BigHuffmanTree(bs); - _FullTree = new BigHuffmanTree(bs); - _TypeTree = new BigHuffmanTree(bs); - - _videoFrameBuffer = (byte *)malloc(2 * _videoInfo.width * _videoInfo.height); - memset(_videoFrameBuffer, 0, 2 * _videoInfo.width * _videoInfo.height); - _palette = (byte *)malloc(3 * 256); - memset(_palette, 0, 3 * 256); - - _videoInfo.firstframeOffset = _fileStream->pos(); - - return true; -} - -void SmackerDecoder::closeFile() { - if (!_fileStream) - return; - - if (_audioStarted && _audioStream) { - _mixer->stopHandle(_audioHandle); - _audioStream = 0; - _audioStarted = false; - } - - delete _fileStream; - _fileStream = 0; - - delete _MMapTree; - delete _MClrTree; - delete _FullTree; - delete _TypeTree; - - free(_frameSizes); - free(_frameTypes); - free(_videoFrameBuffer); - free(_palette); -} - -bool SmackerDecoder::decodeNextFrame() { - uint i; - uint32 chunkSize = 0; - uint32 dataSizeUnpacked = 0; - - uint32 startPos = _fileStream->pos(); - - if (_videoInfo.currentFrame == 0) - _videoInfo.startTime = g_system->getMillis(); - - // Check if we got a frame with palette data, and - // call back the virtual setPalette function to set - // the current palette - if (_frameTypes[_videoInfo.currentFrame] & 1) { - unpackPalette(); - setPalette(_palette); - } - - // Load audio tracks - for (i = 0; i < 7; ++i) { - if (!(_frameTypes[_videoInfo.currentFrame] & (2 << i))) - continue; - - chunkSize = _fileStream->readUint32LE(); - chunkSize -= 4; // subtract the first 4 bytes (chunk size) - - if (_header.audioInfo[i].isCompressed) { - dataSizeUnpacked = _fileStream->readUint32LE(); - chunkSize -= 4; // subtract the next 4 bytes (unpacked data size) - } else { - dataSizeUnpacked = 0; - } - - if (_header.audioInfo[i].hasAudio && chunkSize > 0 && i == 0) { - // If it's track 0, play the audio data - byte *soundBuffer = new byte[chunkSize + 2]; - - _fileStream->read(soundBuffer, chunkSize); - soundBuffer[chunkSize] = 0; - soundBuffer[chunkSize + 1] = 0; - - if (_header.audioInfo[i].isCompressed) { - // Compressed audio (Huffman DPCM encoded) - queueCompressedBuffer(soundBuffer, chunkSize, dataSizeUnpacked, i); - delete[] soundBuffer; - } else { - // Uncompressed audio (PCM) - _audioStream->queueBuffer(soundBuffer, chunkSize); - // The sound buffer will be deleted by AppendableAudioStream - } - - if (!_audioStarted) { - _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_audioHandle, _audioStream, -1, 255); - _audioStarted = true; - } - } else { - // Ignore the rest of the audio tracks, if they exist - // TODO: Are there any Smacker videos with more than one audio stream? - // If yes, we should play the rest of the audio streams as well - if (chunkSize > 0) - _fileStream->skip(chunkSize); - } - } - - uint32 frameSize = _frameSizes[_videoInfo.currentFrame] & ~3; - - if (_fileStream->pos() - startPos > frameSize) - exit(1); - - uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos); - - _frameData = (byte *)malloc(frameDataSize + 2); - _fileStream->read(_frameData, frameDataSize); - _frameData[frameDataSize] = 0; - _frameData[frameDataSize + 1] = 0; - - BitStream bs(_frameData, frameDataSize); - - _MMapTree->reset(); - _MClrTree->reset(); - _FullTree->reset(); - _TypeTree->reset(); - - uint bw = _videoInfo.width / 4; - uint bh = _videoInfo.height / 4; - uint stride = _videoInfo.width; - uint block = 0, blocks = bw*bh; - - uint doubleY = _header.flags ? 2 : 1; - - byte *out; - uint type, run, j, mode; - uint32 p1, p2, clr, map; - byte hi, lo; - - while (block < blocks) { - type = _TypeTree->getCode(bs); - run = getBlockRun((type >> 2) & 0x3f); - - switch (type & 3) { - case SMK_BLOCK_MONO: - while (run-- && block < blocks) { - clr = _MClrTree->getCode(bs); - map = _MMapTree->getCode(bs); - out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; - hi = clr >> 8; - lo = clr & 0xff; - for (i = 0; i < 4; i++) { - for (j = 0; j < doubleY; j++) { - out[0] = (map & 1) ? hi : lo; - out[1] = (map & 2) ? hi : lo; - out[2] = (map & 4) ? hi : lo; - out[3] = (map & 8) ? hi : lo; - out += stride; - } - map >>= 4; - } - ++block; - } - break; - case SMK_BLOCK_FULL: - // Smacker v2 has one mode, Smacker v4 has three - if (_header.signature == MKID_BE('SMK2')) { - mode = 0; - } else { - // 00 - mode 0 - // 10 - mode 1 - // 01 - mode 2 - mode = 0; - if (bs.getBit()) { - mode = 1; - } else if (bs.getBit()) { - mode = 2; - } - } - - while (run-- && block < blocks) { - out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; - switch (mode) { - case 0: - for (i = 0; i < 4; ++i) { - p1 = _FullTree->getCode(bs); - p2 = _FullTree->getCode(bs); - for (j = 0; j < doubleY; ++j) { - out[2] = p1 & 0xff; - out[3] = p1 >> 8; - out[0] = p2 & 0xff; - out[1] = p2 >> 8; - out += stride; - } - } - break; - case 1: - p1 = _FullTree->getCode(bs); - out[0] = out[1] = p1 & 0xFF; - out[2] = out[3] = p1 >> 8; - out += stride; - out[0] = out[1] = p1 & 0xFF; - out[2] = out[3] = p1 >> 8; - out += stride; - p2 = _FullTree->getCode(bs); - out[0] = out[1] = p2 & 0xFF; - out[2] = out[3] = p2 >> 8; - out += stride; - out[0] = out[1] = p2 & 0xFF; - out[2] = out[3] = p2 >> 8; - out += stride; - break; - case 2: - for (i = 0; i < 2; i++) { - // We first get p2 and then p1 - // Check ffmpeg thread "[PATCH] Smacker video decoder bug fix" - // http://article.gmane.org/gmane.comp.video.ffmpeg.devel/78768 - p2 = _FullTree->getCode(bs); - p1 = _FullTree->getCode(bs); - for (j = 0; j < doubleY; ++j) { - out[0] = p1 & 0xff; - out[1] = p1 >> 8; - out[2] = p2 & 0xff; - out[3] = p2 >> 8; - out += stride; - } - for (j = 0; j < doubleY; ++j) { - out[0] = p1 & 0xff; - out[1] = p1 >> 8; - out[2] = p2 & 0xff; - out[3] = p2 >> 8; - out += stride; - } - } - break; - } - ++block; - } - break; - case SMK_BLOCK_SKIP: - while (run-- && block < blocks) - block++; - break; - case SMK_BLOCK_FILL: - uint32 col; - mode = type >> 8; - while (run-- && block < blocks) { - out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4; - col = mode * 0x01010101; - for (i = 0; i < 4 * doubleY; ++i) { - out[0] = out[1] = out[2] = out[3] = col; - out += stride; - } - ++block; - } - break; - } - } - - _fileStream->seek(startPos + frameSize); - - free(_frameData); - - return ++_videoInfo.currentFrame < _videoInfo.frameCount; -} - -void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, - uint32 unpackedSize, int streamNum) { - - BitStream audioBS(buffer, bufferSize); - bool dataPresent = audioBS.getBit(); - - if (!dataPresent) - return; - - bool isStereo = audioBS.getBit(); - assert(isStereo == _header.audioInfo[streamNum].isStereo); - bool is16Bits = audioBS.getBit(); - assert(is16Bits == _header.audioInfo[streamNum].is16Bits); - - int numBytes = 1 * (isStereo ? 2 : 1) * (is16Bits ? 2 : 1); - - byte *unpackedBuffer = new byte[unpackedSize]; - byte *curPointer = unpackedBuffer; - uint32 curPos = 0; - - SmallHuffmanTree *audioTrees[4]; - for (int k = 0; k < numBytes; k++) - audioTrees[k] = new SmallHuffmanTree(audioBS); - - // Base values, stored as big endian - - int32 bases[2]; - - if (isStereo) - bases[1] = (!is16Bits) ? audioBS.getBits8() : - ((int16) (((audioBS.getBits8() << 8) | audioBS.getBits8()))); - - bases[0] = (!is16Bits) ? audioBS.getBits8() : - ((int16) (((audioBS.getBits8() << 8) | audioBS.getBits8()))); - - - // The bases are the first samples, too - for (int i = 0; i < (isStereo ? 2 : 1); i++, curPointer += (is16Bits ? 2 : 1), curPos += (is16Bits ? 2 : 1)) { - if (is16Bits) - WRITE_BE_UINT16(curPointer, bases[i]); - else - *curPointer = (bases[i] & 0xFF) ^ 0x80; - } - - // Next follow the deltas, which are added to the corresponding base values and - // are stored as little endian - // We store the unpacked bytes in big endian format - - while (curPos < unpackedSize) { - // If the sample is stereo, the data is stored for the left and right channel, respectively - // (the exact opposite to the base values) - if (!is16Bits) { - - for (int k = 0; k < (isStereo ? 2 : 1); k++) { - bases[k] += (int8) ((int16) audioTrees[k]->getCode(audioBS)); - *curPointer++ = CLIP(bases[k], 0, 255) ^ 0x80; - curPos++; - } - - } else { - - for (int k = 0; k < (isStereo ? 2 : 1); k++) { - bases[k] += (int16) (audioTrees[k * 2]->getCode(audioBS) | - (audioTrees[k * 2 + 1]->getCode(audioBS) << 8)); - - WRITE_BE_UINT16(curPointer, CLIP(bases[k], -32768, 32767)); - curPointer += 2; - curPos += 2; - } - } - - } - - for (int k = 0; k < numBytes; k++) - delete audioTrees[k]; - - _audioStream->queueBuffer(unpackedBuffer, unpackedSize); - // unpackedBuffer will be deleted by AppendableAudioStream -} - -void SmackerDecoder::unpackPalette() { - uint startPos = _fileStream->pos(); - uint32 len = 4 * _fileStream->readByte(); - - byte *chunk = (byte *)malloc(len); - _fileStream->read(&chunk[0], len); - byte *p = &chunk[0]; - - byte oldPalette[3*256]; - memcpy(oldPalette, _palette, 3 * 256); - - byte *pal = _palette; - - int sz = 0; - byte b0; - while (sz < 256) { - b0 = *p++; - if (b0 & 0x80) { // if top bit is 1 (0x80 = 10000000) - sz += (b0 & 0x7f) + 1; // get lower 7 bits + 1 (0x7f = 01111111) - pal += 3 * ((b0 & 0x7f) + 1); - } else if (b0 & 0x40) { // if top 2 bits are 01 (0x40 = 01000000) - byte c = (b0 & 0x3f) + 1; // get lower 6 bits + 1 (0x3f = 00111111) - uint s = 3 * *p++; - sz += c; - - while (c--) { - *pal++ = oldPalette[s + 0]; - *pal++ = oldPalette[s + 1]; - *pal++ = oldPalette[s + 2]; - s += 3; - } - } else { // top 2 bits are 00 - sz++; - // get the lower 6 bits for each component (0x3f = 00111111) - byte b = b0 & 0x3f; - byte g = (*p++) & 0x3f; - byte r = (*p++) & 0x3f; - - assert(g < 0xc0 && b < 0xc0); - - // upscale to full 8-bit color values by multiplying by 4 - *pal++ = b * 4; - *pal++ = g * 4; - *pal++ = r * 4; - } - } - - _fileStream->seek(startPos + len); - - free(chunk); -} - -} // End of namespace Graphics diff --git a/graphics/video/smk_player.h b/graphics/video/smk_player.h deleted file mode 100644 index e70d0e6454..0000000000 --- a/graphics/video/smk_player.h +++ /dev/null @@ -1,129 +0,0 @@ -/* 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$ - * - */ - -/** - * Video decoder used in engines: - * - agos - * - saga - * - scumm (he) - * - sword1 - * - sword2 - */ - -// Based on http://wiki.multimedia.cx/index.php?title=Smacker -// and the FFmpeg Smacker decoder (libavcodec/smacker.c), revision 16143 -// http://svn.ffmpeg.org/ffmpeg/trunk/libavcodec/smacker.c?revision=16143&view=markup - -#ifndef GRAPHICS_VIDEO_SMK_PLAYER_H -#define GRAPHICS_VIDEO_SMK_PLAYER_H - -#include "graphics/video/video_player.h" -#include "sound/mixer.h" - -namespace Audio { - class AppendableAudioStream; -} - -namespace Graphics { - -class BigHuffmanTree; - -/** - * Implementation of a Smacker v2/v4 video decoder - */ -class SmackerDecoder : public VideoDecoder { -public: - SmackerDecoder(Audio::Mixer *mixer); - virtual ~SmackerDecoder(); - - int getHeight(); - int32 getAudioLag(); - - /** - * Load an SMK encoded video file - * @param filename the filename to load - */ - bool loadFile(const char *filename); - - /** - * Close an SMK encoded video file - */ - void closeFile(); - - bool decodeNextFrame(); - -private: - void unpackPalette(); - // Possible runs of blocks - uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } - void queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum); - - struct AudioInfo { - bool isCompressed; - bool hasAudio; - bool is16Bits; - bool isStereo; - bool hasV2Compression; - uint32 sampleRate; - }; - - struct { - uint32 signature; - uint32 flags; - uint32 audioSize[7]; - uint32 treesSize; - uint32 mMapSize; - uint32 mClrSize; - uint32 fullSize; - uint32 typeSize; - AudioInfo audioInfo[7]; - uint32 dummy; - } _header; - - uint32 *_frameSizes; - // The FrameTypes section of a Smacker file contains an array of bytes, where - // the 8 bits of each byte describe the contents of the corresponding frame. - // The highest 7 bits correspond to audio frames (bit 7 is track 6, bit 6 track 5 - // and so on), so there can be up to 7 different audio tracks. When the lowest bit - // (bit 0) is set, it denotes a frame that contains a palette record - byte *_frameTypes; - byte *_frameData; - // The RGB palette - byte *_palette; - - Audio::Mixer *_mixer; - bool _audioStarted; - Audio::AppendableAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - - BigHuffmanTree *_MMapTree; - BigHuffmanTree *_MClrTree; - BigHuffmanTree *_FullTree; - BigHuffmanTree *_TypeTree; -}; - -} // End of namespace Graphics - -#endif -- cgit v1.2.3