diff options
-rw-r--r-- | graphics/flic_player.cpp | 264 | ||||
-rw-r--r-- | graphics/flic_player.h | 100 | ||||
-rw-r--r-- | graphics/module.mk | 1 |
3 files changed, 365 insertions, 0 deletions
diff --git a/graphics/flic_player.cpp b/graphics/flic_player.cpp new file mode 100644 index 0000000000..7a69762570 --- /dev/null +++ b/graphics/flic_player.cpp @@ -0,0 +1,264 @@ +/* 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 "flic_player.h" + +namespace Graphics { + +FlicPlayer::FlicPlayer(const char *fileName) + : _paletteDirty(false), _offscreen(0), _currFrame(0) { + + memset(&_flicInfo, 0, sizeof(_flicInfo)); + _fileStream.open(fileName); + + _flicInfo.size = _fileStream.readUint32LE(); + _flicInfo.type = _fileStream.readUint16LE(); + _flicInfo.numFrames = _fileStream.readUint16LE(); + _flicInfo.width = _fileStream.readUint16LE(); + _flicInfo.height = _fileStream.readUint16LE(); + _fileStream.skip(4); + _flicInfo.speed = _fileStream.readUint32LE(); + + _fileStream.seek(80); + _flicInfo.offsetFrame1 = _fileStream.readUint32LE(); + _flicInfo.offsetFrame2 = _fileStream.readUint32LE(); + + // Check FLC magic number + if (_flicInfo.type != 0xAF12) { + error("FlicPlayer::FlicPlayer(): attempted to load non-FLC data (type = 0x%04X)", _flicInfo.type); + } + + _offscreen = new uint8[_flicInfo.width * _flicInfo.height]; + memset(_palette, 0, sizeof(_palette)); + + // Seek to the first frame + _fileStream.seek(_flicInfo.offsetFrame1); +} + +FlicPlayer::~FlicPlayer() { + delete[] _offscreen; +} + +void FlicPlayer::redraw() { + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(0, 0, _flicInfo.width, _flicInfo.height)); +} + +ChunkHeader FlicPlayer::readChunkHeader() { + ChunkHeader head; + head.size = _fileStream.readUint32LE(); + head.type = _fileStream.readUint16LE(); + + /* XXX: You'll want to read the rest of the chunk here as well! */ + + return head; +} + +FrameTypeChunkHeader FlicPlayer::readFrameTypeChunkHeader(ChunkHeader chunkHead) { + FrameTypeChunkHeader head; + + head.header = chunkHead; + head.numChunks = _fileStream.readUint16LE(); + head.delay = _fileStream.readUint16LE(); + head.reserved = _fileStream.readUint16LE(); + head.widthOverride = _fileStream.readUint16LE(); + head.heightOverride = _fileStream.readUint16LE(); + + return head; +} + +void FlicPlayer::decodeByteRun(uint8 *data) { + uint8 *ptr = (uint8 *)_offscreen; + while((ptr - _offscreen) < (_flicInfo.width * _flicInfo.height)) { + uint8 chunks = *data++; + while (chunks--) { + int8 count = *data++; + if (count > 0) { + memset(ptr, *data++, count); + ptr += count; + } else { + uint8 copyBytes = -count; + memcpy(ptr, data, copyBytes); + ptr += copyBytes; + data += copyBytes; + } + } + } + + redraw(); +} + +#define OP_PACKETCOUNT 0 +#define OP_UNDEFINED 1 +#define OP_LASTPIXEL 2 +#define OP_LINESKIPCOUNT 3 + +void FlicPlayer::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: + *(uint8 *)(_offscreen + (currentLine * _flicInfo.width) + (_flicInfo.width - 1)) = (opcode & 0xFF); + _dirtyRects.push_back(Common::Rect(_flicInfo.width - 1, currentLine, _flicInfo.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++; + int8 rleCount = (int8)*data++; + if (rleCount > 0) { + memcpy((void *)(_offscreen + (currentLine * _flicInfo.width) + column), data, rleCount * 2); + _dirtyRects.push_back(Common::Rect(column, currentLine, column + (rleCount * 2), currentLine + 1)); + data += rleCount * 2; + column += rleCount * 2; + } else if (rleCount < 0) { + uint16 dataWord = *(uint16 *)data; data += 2; + for (int i = 0; i < -(int16)rleCount; ++i) { + WRITE_LE_UINT16(_offscreen + (currentLine * _flicInfo.width) + column + (i * 2), dataWord); + } + _dirtyRects.push_back(Common::Rect(column, currentLine, column + (-(int16)rleCount * 2), currentLine + 1)); + + column += (-(int16)rleCount) * 2; + } else { // End of cutscene ? + return; + } + } + + currentLine++; + } +} + +#define COLOR_256 4 +#define FLI_SS2 7 +#define FLI_BRUN 15 +#define PSTAMP 18 +#define FRAME_TYPE 0xF1FA + +void FlicPlayer::decodeFrame() { + FrameTypeChunkHeader frameHeader; + + // Read chunk + ChunkHeader cHeader = readChunkHeader(); + switch (cHeader.type) { + case FRAME_TYPE: + frameHeader = readFrameTypeChunkHeader(cHeader); + _currFrame++; + break; + default: + error("FlicPlayer::decodeFrame(): unknown main chunk type (type = 0x%02X)", cHeader.type); + break; + } + + // Read subchunks + if (cHeader.type == FRAME_TYPE) { + for (int i = 0; i < frameHeader.numChunks; ++i) { + cHeader = readChunkHeader(); + uint8 *data = new uint8[cHeader.size - 6]; + _fileStream.read(data, cHeader.size - 6); + switch (cHeader.type) { + case COLOR_256: + setPalette(data); + _paletteDirty = true; + break; + case FLI_SS2: + decodeDeltaFLC(data); + break; + case FLI_BRUN: + decodeByteRun(data); + break; + case PSTAMP: + /* PSTAMP - skip for now */ + break; + default: + error("FlicPlayer::decodeFrame(): unknown subchunk type (type = 0x%02X)", cHeader.type); + break; + } + + delete[] data; + } + } + + // If we just processed the ring frame, set the next frame + if (_currFrame == _flicInfo.numFrames + 1) { + _currFrame = 1; + _fileStream.seek(_flicInfo.offsetFrame2); + } +} + +void FlicPlayer::reset() { + _fileStream.seek(_flicInfo.offsetFrame1); +} + +void FlicPlayer::setPalette(uint8 *mem) { + uint16 numPackets = READ_LE_UINT16(mem); mem += 2; + + if (0 == READ_LE_UINT16(mem)) { //special case + mem += 2; + for (int i = 0; i < 256; ++i) { + for (int j = 0; j < 3; ++j) + _palette[i * 4 + j] = (mem[i * 3 + j]); + _palette[i * 4 + 3] = 0; + } + } else { + uint8 palPos = 0; + + while (numPackets--) { + palPos += *mem++; + uint8 change = *mem++; + + for (int i = 0; i < change; ++i) { + for (int j = 0; j < 3; ++j) + _palette[(palPos + i) * 4 + j] = (mem[i * 3 + j]); + _palette[(palPos + i) * 4 + 3] = 0; + } + + palPos += change; + mem += (change * 3); + } + } +} + +} // End of namespace Graphics diff --git a/graphics/flic_player.h b/graphics/flic_player.h new file mode 100644 index 0000000000..64b29969c8 --- /dev/null +++ b/graphics/flic_player.h @@ -0,0 +1,100 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef GRAPHICS_FLICPLAYER_H +#define GRAPHICS_FLICPLAYER_H + +#include "common/endian.h" +#include "common/list.h" +#include "common/rect.h" +#include "common/file.h" + +namespace Graphics { + +struct FlicHeader { + uint32 size; + uint16 type; + uint16 numFrames; + uint16 width; + uint16 height; + uint32 speed; + uint16 offsetFrame1; + uint16 offsetFrame2; +}; + +struct ChunkHeader { + uint32 size; + uint16 type; +}; + +struct FrameTypeChunkHeader { + ChunkHeader header; + uint16 numChunks; + uint16 delay; + uint16 reserved; // always zero + uint16 widthOverride; + uint16 heightOverride; +}; + +class FlicPlayer { +public: + FlicPlayer(const char *fileName); + ~FlicPlayer(); + + void decodeFrame(); + int getWidth() const { return _flicInfo.width; } + int getHeight() const { return _flicInfo.height; } + bool hasFrames() const { return _flicInfo.numFrames > 0; } + int getCurFrame() const { return _currFrame; } + int getFrameCount() const { return _flicInfo.numFrames; } + uint32 getSpeed() const { return _flicInfo.speed; } + bool isPaletteDirty() const { return _paletteDirty; } + const uint8 *getPalette() { _paletteDirty = false; return _palette; } + const uint8 *getOffscreen() const { return _offscreen; } + const Common::List<Common::Rect> *getDirtyRects() const { return &_dirtyRects; } + void clearDirtyRects() { _dirtyRects.clear(); } + void redraw(); + void reset(); + +protected: + ChunkHeader readChunkHeader(); + FrameTypeChunkHeader readFrameTypeChunkHeader(ChunkHeader chunkHead); + void decodeByteRun(uint8 *data); + void decodeDeltaFLC(uint8 *data); + void setPalette(uint8 *mem); + + Common::File _fileStream; + bool _paletteDirty; + uint8 *_offscreen; + uint8 _palette[256 * 4]; + FlicHeader _flicInfo; + uint16 _currFrame; + uint32 _lastFrameTime; + Common::List<Common::Rect> _dirtyRects; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/module.mk b/graphics/module.mk index 982711b608..70fb75fabe 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -3,6 +3,7 @@ MODULE := graphics MODULE_OBJS := \ cursorman.o \ dxa_player.o \ + flic_player.o \ font.o \ fontman.o \ fonts/consolefont.o \ |