diff options
author | Matthew Hoops | 2009-08-30 19:47:14 +0000 |
---|---|---|
committer | Matthew Hoops | 2009-08-30 19:47:14 +0000 |
commit | d8e93836c5fdba3173d0b1934520a3cc1c298851 (patch) | |
tree | 1f96080f6e5fb47d17cbf2e2e15a349ba02af220 | |
parent | cc6e21635e45f4256f6be10e668743591170fe05 (diff) | |
download | scummvm-rg350-d8e93836c5fdba3173d0b1934520a3cc1c298851.tar.gz scummvm-rg350-d8e93836c5fdba3173d0b1934520a3cc1c298851.tar.bz2 scummvm-rg350-d8e93836c5fdba3173d0b1934520a3cc1c298851.zip |
Add an AVI player and MSVideo1 codec for use with some SCI Windows game (such as kq6).
svn-id: r43833
-rw-r--r-- | graphics/module.mk | 2 | ||||
-rw-r--r-- | graphics/video/avi_player.cpp | 383 | ||||
-rw-r--r-- | graphics/video/avi_player.h | 241 | ||||
-rw-r--r-- | graphics/video/msvideo1.cpp | 139 | ||||
-rw-r--r-- | graphics/video/msvideo1.h | 52 |
5 files changed, 817 insertions, 0 deletions
diff --git a/graphics/module.mk b/graphics/module.mk index ed14051243..d8eece0440 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -21,9 +21,11 @@ MODULE_OBJS := \ thumbnail.o \ VectorRenderer.o \ VectorRendererSpec.o \ + video/avi_player.o \ video/dxa_decoder.o \ video/flic_decoder.o \ video/mpeg_player.o \ + video/msvideo1.o \ video/smk_decoder.o \ video/video_player.o \ video/coktelvideo/indeo3.o \ diff --git a/graphics/video/avi_player.cpp b/graphics/video/avi_player.cpp new file mode 100644 index 0000000000..2cae3904c9 --- /dev/null +++ b/graphics/video/avi_player.cpp @@ -0,0 +1,383 @@ +/* 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/endian.h" +#include "common/file.h" +#include "common/stream.h" +#include "common/events.h" + +#include "sound/audiostream.h" +#include "sound/mixer.h" + +#include "graphics/video/avi_player.h" + +// Codecs +#include "graphics/video/msvideo1.h" + +namespace Graphics { + +AVIPlayer::AVIPlayer(OSystem* syst) : _system(syst) { + _videoCodec = NULL; + _palette = NULL; + _decodedHeader = false; + _filesize = 0; + _curFrame = 0; + _audStream = NULL; + _dirtyPalette = false; + _stream = NULL; + _audHandle = new Audio::SoundHandle(); + memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT)); + memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER)); + memset(&_vidsHeader, 0, sizeof(AVIStreamHeader)); + memset(&_audsHeader, 0, sizeof(AVIStreamHeader)); + memset(&_ixInfo, 0, sizeof(AVIOLDINDEX)); +} + +AVIPlayer::~AVIPlayer() { + close(); + delete _audHandle; +} + +void AVIPlayer::runHandle(uint32 tag) { + assert (_stream); + if (_stream->eos()) + return; + + debug (3, "Decoding tag %s", tag2str(tag)); + + switch (tag) { + case ID_RIFF: + _filesize = _stream->readUint32LE(); + assert(_stream->readUint32BE() == ID_AVI); + break; + case ID_LIST: + handleList(); + break; + case ID_AVIH: + _header.size = _stream->readUint32LE(); + _header.microSecondsPerFrame = _stream->readUint32LE(); + _header.maxBytesPerSecond = _stream->readUint32LE(); + _header.padding = _stream->readUint32LE(); + _header.flags = _stream->readUint32LE(); + _header.totalFrames = _stream->readUint32LE(); + _header.initialFrames = _stream->readUint32LE(); + _header.streams = _stream->readUint32LE(); + _header.bufferSize = _stream->readUint32LE(); + _header.width = _stream->readUint32LE(); + _header.height = _stream->readUint32LE(); + //Ignore 16 bytes of reserved data + _stream->skip(16); + break; + case ID_STRH: + handleStreamHeader(); + break; + case ID_STRD: // Extra stream info, safe to ignore + case ID_VEDT: // Unknown, safe to ignore + case ID_JUNK: // Alignment bytes, should be ignored + { + uint32 junkSize = _stream->readUint32LE(); + _stream->skip(junkSize + (junkSize & 1)); // Alignment + } break; + case ID_IDX1: + _ixInfo.size = _stream->readUint32LE(); + _ixInfo.indices = new AVIOLDINDEX::Index[_ixInfo.size / 16]; + debug (0, "%d Indices", (_ixInfo.size / 16)); + for (uint32 i = 0; i < (_ixInfo.size / 16); i++) { + _ixInfo.indices[i].id = _stream->readUint32BE(); + _ixInfo.indices[i].flags = _stream->readUint32LE(); + _ixInfo.indices[i].offset = _stream->readUint32LE(); + _ixInfo.indices[i].size = _stream->readUint32LE(); + debug (0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size); + } + break; + default: error ("Unknown tag \'%s\' found", tag2str(tag)); + } +} + +void AVIPlayer::handleList() { + uint32 listSize = _stream->readUint32LE() - 4; // Subtract away listType's 4 bytes + uint32 listType = _stream->readUint32BE(); + uint32 curPos = _stream->pos(); + + debug (0, "Found LIST of type %s", tag2str(listType)); + + while ((_stream->pos() - curPos) < listSize) + runHandle(_stream->readUint32BE()); + + // We now have all the header data + if (listType == ID_HDRL) + _decodedHeader = true; +} + +void AVIPlayer::handleStreamHeader() { + AVIStreamHeader sHeader; + sHeader.size = _stream->readUint32LE(); + sHeader.streamType = _stream->readUint32BE(); + if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS) + error ("Unhandled MIDI/Text stream"); + sHeader.streamHandler = _stream->readUint32BE(); + sHeader.flags = _stream->readUint32LE(); + sHeader.priority = _stream->readUint16LE(); + sHeader.language = _stream->readUint16LE(); + sHeader.initialFrames = _stream->readUint32LE(); + sHeader.scale = _stream->readUint32LE(); + sHeader.rate = _stream->readUint32LE(); + sHeader.start = _stream->readUint32LE(); + sHeader.length = _stream->readUint32LE(); + sHeader.bufferSize = _stream->readUint32LE(); + sHeader.quality = _stream->readUint32LE(); + sHeader.sampleSize = _stream->readUint32LE(); + sHeader.frame.left = _stream->readSint16LE(); + sHeader.frame.top = _stream->readSint16LE(); + sHeader.frame.right = _stream->readSint16LE(); + sHeader.frame.bottom = _stream->readSint16LE(); + + assert (_stream->readUint32BE() == ID_STRF); + /* uint32 strfSize = */ _stream->readUint32LE(); + + if (sHeader.streamType == ID_VIDS) { + _vidsHeader = sHeader; + + _bmInfo.size = _stream->readUint32LE(); + _bmInfo.width = _stream->readUint32LE(); + assert (_header.width == _bmInfo.width); + _bmInfo.height = _stream->readUint32LE(); + assert (_header.height == _bmInfo.height); + _bmInfo.planes = _stream->readUint16LE(); + _bmInfo.bitCount = _stream->readUint16LE(); + _bmInfo.compression = _stream->readUint32BE(); + _bmInfo.sizeImage = _stream->readUint32LE(); + _bmInfo.xPelsPerMeter = _stream->readUint32LE(); + _bmInfo.yPelsPerMeter = _stream->readUint32LE(); + _bmInfo.clrUsed = _stream->readUint32LE(); + _bmInfo.clrImportant = _stream->readUint32LE(); + + if (_bmInfo.bitCount == 8) { + if (_bmInfo.clrUsed == 0) + _bmInfo.clrUsed = 256; + + _palette = (byte *)malloc(256 * 4); + + for (uint32 i = 0; i < _bmInfo.clrUsed; i++) { + _palette[i * 4 + 2] = _stream->readByte(); + _palette[i * 4 + 1] = _stream->readByte(); + _palette[i * 4] = _stream->readByte(); + _palette[i * 4 + 3] = _stream->readByte(); + } + + // Assign the palette to be dirty + _dirtyPalette = true; + } + } else if (sHeader.streamType == ID_AUDS) { + _audsHeader = sHeader; + + _wvInfo.tag = _stream->readUint16LE(); + _wvInfo.channels = _stream->readUint16LE(); + _wvInfo.samplesPerSec = _stream->readUint32LE(); + _wvInfo.avgBytesPerSec = _stream->readUint32LE(); + _wvInfo.blockAlign = _stream->readUint16LE(); + _wvInfo.size = _stream->readUint16LE(); + } +} + +bool AVIPlayer::open(Common::String filename) { + Common::File *file = new Common::File(); + + if (!file->open(filename.c_str())) + return false; + + open(file); + + return true; +} + +void AVIPlayer::open(Common::SeekableReadStream *stream) { + close(); + + assert(stream); + _stream = stream; + + _decodedHeader = false; + _curFrame = 0; + + // Read chunks until we have decoded the header + while (!_decodedHeader) + runHandle(_stream->readUint32BE()); + + uint32 nextTag = _stream->readUint32BE(); + + // Throw out any JUNK section + if (nextTag == ID_JUNK) { + runHandle(ID_JUNK); + nextTag = _stream->readUint32BE(); + } + + // Ignore the 'movi' LIST + if (nextTag == ID_LIST) { + _stream->readUint32BE(); // Skip size + if (_stream->readUint32BE() != ID_MOVI) + error ("Expected 'movi' LIST"); + } else + error ("Expected 'movi' LIST"); + + // Now, create the codec + _videoCodec = createCodec(); + + // Initialize the video stuff too + _audStream = createAudioStream(); + if (_audStream) + _system->getMixer()->playInputStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream); + + debug (0, "Frames = %d, Dimensions = %d x %d", _header.totalFrames, _header.width, _header.height); + debug (0, "Frame Rate = %d", getFrameRate()); + if (_header.flags & AVIF_ISINTERLEAVED) + debug (0, "Sound Rate = %d", AUDIO_RATE); + debug (0, "Video Codec = \'%s\'", tag2str(_vidsHeader.streamHandler)); +} + +void AVIPlayer::close() { + delete _stream; + + // Deinitialize sound + _system->getMixer()->stopHandle(*_audHandle); + + if (_palette) { + free(_palette); + _palette = NULL; + } + + _decodedHeader = false; + _filesize = 0; + + delete _videoCodec; + delete[] _ixInfo.indices; +} + +Surface *AVIPlayer::getNextFrame() { + uint32 nextTag = _stream->readUint32BE(); + + if (nextTag == ID_LIST) { + // A list of audio/video chunks + uint32 listSize = _stream->readUint32LE() - 4; + int32 startPos = _stream->pos(); + + if (_stream->readUint32BE() != ID_REC) + error ("Expected 'rec ' LIST"); + + // Decode chunks in the list and see if we get a frame + Surface *frame = NULL; + while (_stream->pos() < startPos + (int32)listSize) { + Surface *temp = getNextFrame(); + if (temp) + frame = temp; + } + + return frame; + } else if (getStreamType(nextTag) == 'wb') { + // Audio Chunk + uint32 chunkSize = _stream->readUint32LE(); + byte *data = new byte[chunkSize]; + _stream->read(data, chunkSize); + _audStream->queueBuffer(data, chunkSize); + _stream->skip(chunkSize & 1); // Alignment + } else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id') { + // Compressed Frame + _curFrame++; + uint32 chunkSize = _stream->readUint32LE(); + + if (chunkSize == 0) // Keep last frame on screen + return NULL; + + Common::SeekableReadStream *frameData = _stream->readStream(chunkSize); + Graphics::Surface *surface = _videoCodec->decodeImage(frameData); + delete frameData; + _stream->skip(chunkSize & 1); // Alignment + return surface; + } else if (getStreamType(nextTag) == 'pc') { + // Palette Change + _stream->readUint32LE(); // Chunk size, not needed here + byte firstEntry = _stream->readByte(); + uint16 numEntries = _stream->readByte(); + _stream->readUint16LE(); // Reserved + + // 0 entries means all colors are going to be changed + if (numEntries == 0) + numEntries = 256; + + for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) { + _palette[i * 4] = _stream->readByte(); + _palette[i * 4 + 1] = _stream->readByte(); + _palette[i * 4 + 2] = _stream->readByte(); + _stream->readByte(); // Flags that don't serve us any purpose + } + + // Mark the palette as dirty + _dirtyPalette = true; + + // No alignment necessary. It's always even. + } else if (nextTag == ID_JUNK) { + runHandle(ID_JUNK); + } else + error ("Tag = \'%s\'", tag2str(nextTag)); + + return NULL; +} + +Codec *AVIPlayer::createCodec() { + switch (_vidsHeader.streamHandler) { + case ID_CRAM: + case ID_MSVC: + case ID_WHAM: + return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); + default: + warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); + } + + return NULL; +} + +Audio::AppendableAudioStream *AVIPlayer::createAudioStream() { + if (_wvInfo.tag == WAVE_FORMAT_PCM) + return Audio::makeAppendableAudioStream(AUDIO_RATE, Audio::Mixer::FLAG_UNSIGNED|Audio::Mixer::FLAG_AUTOFREE); + + if (_wvInfo.tag != 0) // No sound + warning ("Unsupported AVI audio format %d", _wvInfo.tag); + + return NULL; +} + +byte AVIPlayer::char2num(char c) { + return (c >= 48 && c <= 57) ? c - 48 : 0; +} + +byte AVIPlayer::getStreamNum(uint32 tag) { + return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); +} + +uint16 AVIPlayer::getStreamType(uint32 tag) { + return tag & 0xffff; +} + +} // End of namespace JMP diff --git a/graphics/video/avi_player.h b/graphics/video/avi_player.h new file mode 100644 index 0000000000..19126fe15d --- /dev/null +++ b/graphics/video/avi_player.h @@ -0,0 +1,241 @@ +/* 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$ + * + */ + +#ifndef GRAPHICS_AVI_PLAYER_H +#define GRAPHICS_AVI_PLAYER_H + +#include "common/file.h" +#include "common/system.h" +#include "common/rect.h" +#include "common/util.h" + +#include "sound/audiostream.h" +#include "sound/mixer.h" + +namespace Graphics { + +#define UNKNOWN_HEADER(a) error("Unknown header found -- \'%s\'", tag2str(a)) +#define AUDIO_RATE (_audsHeader.rate / _audsHeader.scale) + +// ID's That are used throughout the AVI files +// that will be handled by this player +#define ID_RIFF MKID_BE('RIFF') +#define ID_AVI MKID_BE('AVI ') +#define ID_LIST MKID_BE('LIST') +#define ID_HDRL MKID_BE('hdrl') +#define ID_AVIH MKID_BE('avih') +#define ID_STRL MKID_BE('strl') +#define ID_STRH MKID_BE('strh') +#define ID_VIDS MKID_BE('vids') +#define ID_AUDS MKID_BE('auds') +#define ID_MIDS MKID_BE('mids') +#define ID_TXTS MKID_BE('txts') +#define ID_JUNK MKID_BE('JUNK') +#define ID_STRF MKID_BE('strf') +#define ID_MOVI MKID_BE('movi') +#define ID_REC MKID_BE('rec ') +#define ID_VEDT MKID_BE('vedt') +#define ID_IDX1 MKID_BE('idx1') +#define ID_STRD MKID_BE('strd') +//#define ID_INFO MKID_BE('INFO') + +// Codec tags +#define ID_RLE MKID_BE('RLE ') +#define ID_CRAM MKID_BE('CRAM') +#define ID_MSVC MKID_BE('msvc') +#define ID_WHAM MKID_BE('WHAM') +#define ID_CVID MKID_BE('cvid') +#define ID_IV32 MKID_BE('iv32') + +struct BITMAPINFOHEADER { + uint32 size; + uint32 width; + uint32 height; + uint16 planes; + uint16 bitCount; + uint32 compression; + uint32 sizeImage; + uint32 xPelsPerMeter; + uint32 yPelsPerMeter; + uint32 clrUsed; + uint32 clrImportant; +}; + +struct WAVEFORMAT { + uint16 tag; + uint16 channels; + uint32 samplesPerSec; + uint32 avgBytesPerSec; + uint16 blockAlign; +}; + +struct PCMWAVEFORMAT : public WAVEFORMAT { + uint16 size; +}; + +struct WAVEFORMATEX : public WAVEFORMAT { + uint16 bitsPerSample; + uint16 size; +}; + +struct AVIOLDINDEX { + uint32 size; + struct Index { + uint32 id; + uint32 flags; + uint32 offset; + uint32 size; + } *indices; +}; + +// Index Flags +enum { + AVIIF_INDEX = 0x10 +}; + +enum { + WAVE_INVALIDFORMAT = 0, + WAVE_FORMAT_PCM = 1, + WAVE_FORMAT_1M08 = 1, + WAVE_FORMAT_1S08 = 2, + WAVE_FORMAT_1M16 = 4, + WAVE_FORMAT_1S16 = 8, + WAVE_FORMAT_2M08 = 16, + WAVE_FORMAT_2S08 = 32, + WAVE_FORMAT_2M16 = 64, + WAVE_FORMAT_2S16 = 128, + WAVE_FORMAT_4M08 = 256, + WAVE_FORMAT_4S08 = 512, + WAVE_FORMAT_4M16 = 1024, + WAVE_FORMAT_4S16 = 2048 +}; + +struct AVIHeader { + uint32 size; + uint32 microSecondsPerFrame; + uint32 maxBytesPerSecond; + uint32 padding; + uint32 flags; + uint32 totalFrames; + uint32 initialFrames; + uint32 streams; + uint32 bufferSize; + uint32 width; + uint32 height; +}; + +// Flags from the AVIHeader +enum { + AVIF_HASINDEX = 0x00000010, + AVIF_MUSTUSEINDEX = 0x00000020, + AVIF_ISINTERLEAVED = 0x00000100, + AVIF_TRUSTCKTYPE = 0x00000800, + AVIF_WASCAPTUREFILE = 0x00010000, + AVIF_WASCOPYRIGHTED = 0x00020000 +}; + +struct AVIStreamHeader { + uint32 size; + uint32 streamType; + uint32 streamHandler; + uint32 flags; + uint16 priority; + uint16 language; + uint32 initialFrames; + uint32 scale; + uint32 rate; + uint32 start; + uint32 length; + uint32 bufferSize; + uint32 quality; + uint32 sampleSize; + Common::Rect frame; +}; + +class Codec { +public: + Codec() {} + virtual ~Codec() {} + virtual Graphics::Surface *decodeImage(Common::SeekableReadStream *stream) = 0; +}; + +class AVIPlayer { +public: + AVIPlayer(OSystem* syst); + ~AVIPlayer(); + + // Open/Load an AVI video from "filename" + bool open(Common::String filename); + + // Open/Load an AVI video from a stream + void open(Common::SeekableReadStream *stream); + + // Close the current AVI video and release any data + void close(); + + uint32 getFrameRate() { return _vidsHeader.rate / _vidsHeader.scale; } + byte *getPalette() { return _palette; _dirtyPalette = false; } + Surface *getNextFrame(); + bool dirtyPalette() { return _dirtyPalette; } + uint32 getCurFrame() { return _curFrame; } + uint32 getFrameCount() { return _header.totalFrames; } +private: + OSystem *_system; + Common::SeekableReadStream *_stream; + BITMAPINFOHEADER _bmInfo; + PCMWAVEFORMAT _wvInfo; + AVIOLDINDEX _ixInfo; + AVIHeader _header; + AVIStreamHeader _vidsHeader; + AVIStreamHeader _audsHeader; + byte *_palette; + + bool _decodedHeader; + uint32 _curFrame; + bool _dirtyPalette; + + Codec *_videoCodec; + Codec *createCodec(); + + void runHandle(uint32 tag); + void handleList(); + void handleStreamHeader(); + void handlePalChange(); + + Audio::SoundHandle *_audHandle; + Audio::AppendableAudioStream *_audStream; + Audio::AppendableAudioStream *createAudioStream(); + + uint32 _filesize; + + // Helper functions + static byte char2num(char c); + static byte getStreamNum(uint32 tag); + static uint16 getStreamType(uint32 tag); +}; + +} // End of namespace JMP + +#endif diff --git a/graphics/video/msvideo1.cpp b/graphics/video/msvideo1.cpp new file mode 100644 index 0000000000..9e57f9a4ab --- /dev/null +++ b/graphics/video/msvideo1.cpp @@ -0,0 +1,139 @@ +/* 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 off ffmpeg's msvideo.cpp + +#include "graphics/video/msvideo1.h" + +namespace Graphics { + +#define CHECK_STREAM_PTR(n) \ + if ((stream->pos() + n) > stream->size() ) { \ + warning ("MS Video-1: Stream out of bounds (%d >= %d)", stream->pos() + n, stream->size()); \ + return; \ + } + +MSVideo1Decoder::MSVideo1Decoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() { + _surface = new Graphics::Surface(); + _surface->create(width, height, (bitsPerPixel == 8) ? 1 : 2); + _bitsPerPixel = bitsPerPixel; +} + +MSVideo1Decoder::~MSVideo1Decoder() { + _surface->free(); + delete _surface; +} + +void MSVideo1Decoder::decode8(Common::SeekableReadStream *stream) { + byte colors[8]; + byte *pixels = (byte *)_surface->pixels; + uint16 stride = _surface->w; + + int skipBlocks = 0; + uint16 blocks_wide = _surface->w / 4; + uint16 blocks_high = _surface->h / 4; + uint32 totalBlocks = blocks_wide * blocks_high; + uint32 blockInc = 4; + uint16 rowDec = stride + 4; + + for (uint16 block_y = blocks_high; block_y > 0; block_y--) { + uint32 blockPtr = (block_y * 4 - 1) * stride; + for (uint16 block_x = blocks_wide; block_x > 0; block_x--) { + // check if this block should be skipped + if (skipBlocks > 0) { + blockPtr += blockInc; + skipBlocks--; + totalBlocks--; + continue; + } + + uint32 pixelPtr = blockPtr; + + /* get the next two bytes in the encoded data stream */ + CHECK_STREAM_PTR(2); + byte byte_a = stream->readByte(); + byte byte_b = stream->readByte(); + + /* check if the decode is finished */ + if (byte_a == 0 && byte_b == 0 && totalBlocks == 0) { + return; + } else if ((byte_b & 0xFC) == 0x84) { + // skip code, but don't count the current block + skipBlocks = ((byte_b - 0x84) << 8) + byte_a - 1; + } else if (byte_b < 0x80) { + // 2-color encoding + uint16 flags = (byte_b << 8) | byte_a; + + CHECK_STREAM_PTR(2); + colors[0] = stream->readByte(); + colors[1] = stream->readByte(); + + for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { + for (byte pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1) + pixels[pixelPtr++] = colors[(flags & 0x1) ^ 1]; + pixelPtr -= rowDec; + } + } else if (byte_b >= 0x90) { + // 8-color encoding + uint16 flags = (byte_b << 8) | byte_a; + + CHECK_STREAM_PTR(8); + for (byte i = 0; i < 8; i++) + colors[i] = stream->readByte(); + + for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { + for (byte pixel_x = 0; pixel_x < 4; pixel_x++, flags >>= 1) + pixels[pixelPtr++] = colors[((pixel_y & 0x2) << 1) + (pixel_x & 0x2) + ((flags & 0x1) ^ 1)]; + pixelPtr -= rowDec; + } + } else { + // 1-color encoding + colors[0] = byte_a; + + for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { + for (byte pixel_x = 0; pixel_x < 4; pixel_x++) + pixels[pixelPtr++] = colors[0]; + pixelPtr -= rowDec; + } + } + + blockPtr += blockInc; + totalBlocks--; + } + } +} + +Graphics::Surface *MSVideo1Decoder::decodeImage(Common::SeekableReadStream *stream) { + if (_bitsPerPixel == 8) + decode8(stream); + else { + // decode16(stream); + error ("Unhandled MS Video-1 16bpp encoding"); + } + + return _surface; +} + +} // End of namespace JMP diff --git a/graphics/video/msvideo1.h b/graphics/video/msvideo1.h new file mode 100644 index 0000000000..676bc72c9f --- /dev/null +++ b/graphics/video/msvideo1.h @@ -0,0 +1,52 @@ +/* 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$ + * + */ + +#ifndef GRAPHICS_MSVIDEO1_H +#define GRAPHICS_MSVIDEO1_H + +#include "graphics/video/avi_player.h" +#include "graphics/surface.h" + +namespace Graphics { + +class MSVideo1Decoder : public Codec { +public: + MSVideo1Decoder(uint16 width, uint16 height, byte bitsPerPixel); + ~MSVideo1Decoder(); + + Surface *decodeImage(Common::SeekableReadStream *stream); + +private: + byte _bitsPerPixel; + + Surface *_surface; + + void decode8(Common::SeekableReadStream *stream); + //void decode16(Common::SeekableReadStream *stream); +}; + +} + +#endif |