aboutsummaryrefslogtreecommitdiff
path: root/graphics/video/codecs
diff options
context:
space:
mode:
authorAlyssa Milburn2010-12-16 17:01:09 +0000
committerAlyssa Milburn2010-12-16 17:01:09 +0000
commiteac4a641435af9fa365715a0ad2e16fa4ea71db1 (patch)
tree8f5b92f2f86e3d428f563019b5613df5cf9b42b8 /graphics/video/codecs
parent3d81fc1522fd47d329103acacc23f84c6987e4fc (diff)
downloadscummvm-rg350-eac4a641435af9fa365715a0ad2e16fa4ea71db1.tar.gz
scummvm-rg350-eac4a641435af9fa365715a0ad2e16fa4ea71db1.tar.bz2
scummvm-rg350-eac4a641435af9fa365715a0ad2e16fa4ea71db1.zip
VIDEO: Add CDToons decoder
svn-id: r54936
Diffstat (limited to 'graphics/video/codecs')
-rw-r--r--graphics/video/codecs/cdtoons.cpp449
-rw-r--r--graphics/video/codecs/cdtoons.h70
2 files changed, 519 insertions, 0 deletions
diff --git a/graphics/video/codecs/cdtoons.cpp b/graphics/video/codecs/cdtoons.cpp
new file mode 100644
index 0000000000..8cd38b1744
--- /dev/null
+++ b/graphics/video/codecs/cdtoons.cpp
@@ -0,0 +1,449 @@
+/* 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/codecs/cdtoons.h"
+#include "common/stream.h"
+
+#include "common/system.h"
+
+namespace Graphics {
+
+struct CDToonsAction {
+ uint16 blockId;
+ Common::Rect rect;
+};
+
+struct CDToonsDiff {
+ byte *data;
+ uint32 size;
+ Common::Rect rect;
+};
+
+static Common::Rect readRect(Common::SeekableReadStream *stream) {
+ Common::Rect rect;
+ rect.top = stream->readUint16BE();
+ rect.left = stream->readUint16BE();
+ rect.bottom = stream->readUint16BE();
+ rect.right = stream->readUint16BE();
+ return rect;
+}
+
+CDToonsDecoder::CDToonsDecoder(uint16 width, uint16 height) {
+ debugN(5, "CDToons: width %d, height %d\n", width, height);
+
+ _surface = new Graphics::Surface();
+ _surface->create(width, height, 1);
+
+ _currentPaletteId = 0;
+ memset(_palette, 0, 256 * 3);
+ _dirtyPalette = false;
+}
+
+CDToonsDecoder::~CDToonsDecoder() {
+ delete _surface;
+
+ for (Common::HashMap<uint16, CDToonsBlock>::iterator i = _blocks.begin(); i != _blocks.end(); i++)
+ delete[] i->_value.data;
+}
+
+Graphics::Surface *CDToonsDecoder::decodeImage(Common::SeekableReadStream *stream) {
+ uint16 u0 = stream->readUint16BE(); // always 9?
+ uint16 frameId = stream->readUint16BE();
+ uint16 blocksValidUntil = stream->readUint16BE();
+ byte u6 = stream->readByte();
+ byte backgroundColor = stream->readByte();
+ debugN(5, "CDToons frame %d, size %d, unknown %04x (at 0), blocks valid until %d, unknown 6 is %02x, bkg colour is %02x\n",
+ frameId, stream->size(), u0, blocksValidUntil, u6, backgroundColor);
+
+ Common::Rect clipRect = readRect(stream);
+ debugN(9, "CDToons clipRect: (%d, %d) to (%d, %d)\n",
+ clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+
+ Common::Rect dirtyRect = readRect(stream);
+ debugN(9, "CDToons dirtyRect: (%d, %d) to (%d, %d)\n",
+ dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
+
+ uint32 flags = stream->readUint32BE();
+ if (flags & 0x80)
+ error("CDToons: frame already processed?");
+ debugN(5, "CDToons flags: %08x\n", flags);
+
+ uint16 blockCount = stream->readUint16BE();
+ uint16 blockOffset = stream->readUint16BE();
+ debugN(9, "CDToons: %d blocks at 0x%04x\n",
+ blockCount, blockOffset);
+
+ // max block id?
+ uint16 u32 = stream->readUint16BE();
+ debugN(5, "CDToons unknown at 32: %04x\n", u32);
+
+ byte actionCount = stream->readByte();
+ byte u35 = stream->readByte();
+
+ uint16 paletteId = stream->readUint16BE();
+ byte paletteSet = stream->readByte();
+ debugN(9, "CDToons palette id %04x, palette byte %02x\n",
+ paletteId, paletteSet);
+
+ byte u39 = stream->readByte();
+ uint16 u40 = stream->readUint16BE();
+ uint16 u42 = stream->readUint16BE();
+ debugN(5, "CDToons: unknown at 35 is %02x, unknowns at 39: %02x, %04x, %04x\n",
+ u35, u39, u40, u42);
+
+ Common::Array<CDToonsAction> actions;
+
+ for (uint i = 0; i < actionCount; i++) {
+ CDToonsAction action;
+ action.blockId = stream->readUint16BE();
+ action.rect = readRect(stream);
+ debugN(9, "CDToons action: render block %d at (%d, %d) to (%d, %d)\n",
+ action.blockId, action.rect.left, action.rect.top, action.rect.right, action.rect.bottom);
+ actions.push_back(action);
+ }
+
+ if (stream->pos() > blockOffset)
+ error("CDToons header ended at 0x%08x, but blocks should have started at 0x%08x",
+ stream->pos(), blockOffset);
+
+ if (stream->pos() != blockOffset)
+ error("CDToons had %d unknown bytes after header", blockOffset - stream->pos());
+
+ for (uint i = 0; i < blockCount; i++) {
+ uint16 blockId = stream->readUint16BE();
+ if (blockId >= 1200)
+ error("CDToons: block id %d was too high", blockId);
+ if (_blocks.contains(blockId))
+ error("CDToons: new block %d was already seen", blockId);
+
+ CDToonsBlock block;
+ block.flags = stream->readUint16BE();
+ // flag 1 = palette, flag 2 = data?
+ if (block.flags & 0x8000)
+ error("CDToons: block already processed?");
+ block.size = stream->readUint32BE();
+ if (block.size < 14)
+ error("CDToons: block size was %d, too small", block.size);
+ block.size -= 14;
+ block.startFrame = stream->readUint16BE();
+ block.endFrame = stream->readUint16BE();
+ block.unknown12 = stream->readUint16BE();
+ block.data = new byte[block.size];
+ stream->read(block.data, block.size);
+
+ debugN(9, "CDToons block id 0x%04x of size 0x%08x, flags %04x, from frame %d to %d, unknown at 12 is %04x\n",
+ blockId, block.size, block.flags, block.startFrame, block.endFrame, block.unknown12);
+
+ _blocks[blockId] = block;
+ }
+
+ byte xFrmBegin = 0, xFrmCount;
+ Common::Array<CDToonsDiff> diffs;
+
+ while (true) {
+ int32 nextPos = stream->pos();
+ uint32 tag = stream->readUint32BE();
+ uint32 size = stream->readUint32BE();
+ nextPos += size;
+
+ switch (tag) {
+ case MKID_BE('Diff'):
+ {
+ debugN(5, "CDToons: Diff\n");
+ uint16 count = stream->readUint16BE();
+
+ Common::Rect diffClipRect = readRect(stream);
+ debugN(9, "CDToons diffClipRect: (%d, %d) to (%d, %d)\n",
+ diffClipRect.left, diffClipRect.top, diffClipRect.right, diffClipRect.bottom);
+
+ debugN(5, "CDToons Diff: %d subentries\n", count);
+ for (uint i = 0; i < count; i++) {
+ CDToonsDiff diff;
+
+ diff.rect = readRect(stream);
+ diff.size = stream->readUint32BE();
+ if (diff.size < 20)
+ error("CDToons: Diff block size was %d, too small", diff.size);
+
+ uint16 diffWidth = stream->readUint16BE();
+ uint16 diffHeight = stream->readUint16BE();
+ uint16 unknown16 = stream->readUint16BE();
+ uint16 unknown18 = stream->readUint16BE();
+ diff.size -= 8;
+
+ if (diffWidth != diff.rect.width() || diffHeight != diff.rect.height())
+ error("CDToons: Diff sizes didn't match");
+ debugN(5, "CDToons Diff: size %d, frame from (%d, %d) to (%d, %d), unknowns %04x, %04x\n",
+ diff.size, diff.rect.left, diff.rect.top, diff.rect.right, diff.rect.bottom,
+ unknown16, unknown18);
+
+ diff.data = new byte[diff.size];
+ stream->read(diff.data, diff.size);
+ diffs.push_back(diff);
+ }
+ }
+ break;
+ case MKID_BE('XFrm'):
+ {
+ debugN(5, "CDToons: XFrm\n");
+ if (!(flags & 0x10))
+ error("CDToons: useless XFrm?");
+
+ if (xFrmBegin)
+ error("CDToons: duplicate XFrm");
+ xFrmBegin = stream->readByte();
+ xFrmCount = stream->readByte();
+ debugN(9, "CDToons XFrm: run %d actions from %d\n", xFrmCount, xFrmBegin - 1);
+
+ // TODO: don't ignore (if xFrmCount is non-zero)
+ Common::Rect dirtyRectXFrm = readRect(stream);
+ debugN(9, "CDToons XFrm dirtyRect: (%d, %d) to (%d, %d)\n",
+ dirtyRectXFrm.left, dirtyRectXFrm.top, dirtyRectXFrm.right, dirtyRectXFrm.bottom);
+
+ // always zero?
+ Common::Rect dirtyRect2XFrm = readRect(stream);
+ debugN(9, "CDToons XFrm dirtyRect2: (%d, %d) to (%d, %d)\n",
+ dirtyRect2XFrm.left, dirtyRect2XFrm.top, dirtyRect2XFrm.right, dirtyRect2XFrm.bottom);
+ }
+ break;
+ case MKID_BE('Mrks'):
+ debugN(5, "CDToons: Mrks\n");
+ if (!(flags & 0x2))
+ error("CDToons: useless Mrks?");
+
+ // TODO
+ warning("CDToons: encountered Mrks, not implemented yet");
+ break;
+ case MKID_BE('Scal'):
+ // TODO
+ warning("CDToons: encountered Scal, not implemented yet");
+ break;
+ case MKID_BE('WrMp'):
+ warning("CDToons: encountered WrMp, ignoring");
+ break;
+ case MKID_BE('FrtR'):
+ {
+ debugN(5, "CDToons: FrtR\n");
+ if (!(flags & 0x40))
+ error("CDToons: useless FrtR?");
+
+ uint16 count = stream->readUint16BE();
+ debugN(9, "CDToons FrtR: %d dirty rectangles\n", count);
+ for (uint i = 0; i < count; i++) {
+ Common::Rect dirtyRectFrtR = readRect(stream);
+ debugN(9, "CDToons FrtR dirtyRect: (%d, %d) to (%d, %d)\n",
+ dirtyRectFrtR.left, dirtyRectFrtR.top, dirtyRectFrtR.right, dirtyRectFrtR.bottom);
+ }
+ }
+ break;
+ case MKID_BE('BckR'):
+ {
+ debugN(5, "CDToons: BckR\n");
+ if (!(flags & 0x20))
+ error("CDToons: useless BckR?");
+
+ uint16 count = stream->readUint16BE();
+ debugN(9, "CDToons BckR: %d subentries\n", count);
+ for (uint i = 0; i < count; i++) {
+ Common::Rect dirtyRectBckR = readRect(stream);
+ debugN(9, "CDToons BckR dirtyRect: (%d, %d) to (%d, %d)\n",
+ dirtyRectBckR.left, dirtyRectBckR.top, dirtyRectBckR.right, dirtyRectBckR.bottom);
+ }
+ }
+ break;
+ default:
+ warning("Unknown CDToons tag '%s'", tag2str(tag));
+ }
+
+ if (stream->pos() > nextPos)
+ error("CDToons ran off the end of a block while reading it (at %d, next block at %d)",
+ stream->pos(), nextPos);
+ if (stream->pos() != nextPos) {
+ warning("CDToons had %d unknown bytes after block", nextPos - stream->pos());
+ stream->seek(nextPos);
+ }
+
+ if (stream->pos() == stream->size())
+ break;
+ }
+
+ for (uint i = 0; i < diffs.size(); i++) {
+ renderBlock(diffs[i].data, diffs[i].size, diffs[i].rect.left, diffs[i].rect.top, diffs[i].rect.width(), diffs[i].rect.height());
+ delete[] diffs[i].data;
+ }
+ if (!diffs.empty())
+ return _surface;
+
+ for (uint i = 0; i < actions.size(); i++) {
+ CDToonsAction &action = actions[i];
+ if (i == 0 && action.blockId == 0)
+ memset(_surface->pixels, backgroundColor, _surface->w * _surface->h);
+ if (!_blocks.contains(action.blockId))
+ continue;
+ if (!action.rect.right)
+ continue;
+ if (i == 0 && !diffs.empty())
+ continue;
+
+ CDToonsBlock &block = _blocks[action.blockId];
+ uint16 width = READ_BE_UINT16(block.data + 2);
+ uint16 height = READ_BE_UINT16(block.data);
+
+ renderBlock(block.data + 14, block.size - 14, action.rect.left, action.rect.top, width, height);
+ }
+
+ if (paletteId && _currentPaletteId != paletteId) {
+ if (!_blocks.contains(paletteId))
+ error("CDToons: no block for palette %04x", paletteId);
+ if (_blocks[paletteId].size != 2 * 3 * 256)
+ error("CDToons: palette %04x is wrong size (%d)", paletteId, _blocks[paletteId].size);
+
+ _currentPaletteId = paletteId;
+ if (!paletteSet)
+ setPalette(_blocks[paletteId].data);
+ }
+
+ return _surface;
+}
+
+void CDToonsDecoder::renderBlock(byte *data, uint dataSize, int destX, int destY, uint width, uint height) {
+ byte *currData = data;
+ byte *dataEnd = data + dataSize;
+
+ debugN(9, "CDToons renderBlock at (%d, %d), width %d, height %d\n",
+ destX, destY, width, height);
+
+ if (destX + width > _surface->w)
+ width = _surface->w - destX;
+ if (destY + height > _surface->h)
+ height = _surface->h - destY;
+
+ uint skip = 0;
+ if (destX < 0) {
+ skip = -destX;
+ if (width <= skip)
+ return;
+ width -= skip;
+ destX = 0;
+ }
+
+ for (uint y = 0; y < height; y++) {
+ if (destY + (int)y >= _surface->h)
+ break;
+
+ if (currData + 2 > dataEnd)
+ error("CDToons renderBlock overran whole data by %d bytes", (uint32)(currData - dataEnd));
+
+ uint16 lineSize = READ_BE_UINT16(currData);
+ currData += 2;
+ byte *nextLine = currData + lineSize;
+
+ if (nextLine > dataEnd)
+ error("CDToons renderBlock was going to overrun data by %d bytes (line size %d)",
+ (uint32)(nextLine - dataEnd), (uint32)(nextLine - currData));
+
+ if (destY + (int)y < 0) {
+ currData = nextLine;
+ continue;
+ }
+
+ byte *pixels = (byte *)_surface->getBasePtr(destX, destY + y);
+
+ int leftToSkip = skip;
+ uint x = 0;
+ bool done = false;
+ while (x < width && !done) {
+ int size = (uint)*currData;
+ currData++;
+ bool raw = !(size & 0x80);
+ size = (size & 0x7f) + 1;
+
+ if (leftToSkip) {
+ if (leftToSkip >= size) {
+ leftToSkip -= size;
+ if (raw)
+ currData += size;
+ else
+ currData++;
+ continue;
+ } else {
+ size -= leftToSkip;
+ if (raw)
+ currData += leftToSkip;
+ leftToSkip = 0;
+ }
+ }
+
+ if (x + size >= width) {
+ size = width - x;
+ done = true;
+ }
+ if (destX + (int)x + size >= (int)_surface->w) {
+ size = MIN<int>((int)_surface->w - destX - (int)x, width - x);
+ done = true;
+ }
+ if (size <= 0) {
+ size = 0;
+ done = true;
+ }
+
+ if (raw) {
+ memcpy(pixels + x, currData, size);
+ currData += size;
+ x += size;
+ } else {
+ byte color = *currData;
+ currData++;
+ if (color) {
+ memset(pixels + x, color, size);
+ }
+ x += size;
+ }
+
+ if (currData > nextLine) {
+ warning("CDToons renderBlock overran line by %d bytes", (uint32)(currData - nextLine));
+ return;
+ }
+ }
+
+ currData = nextLine;
+ }
+}
+
+void CDToonsDecoder::setPalette(byte *data) {
+ _dirtyPalette = true;
+
+ // A lovely QuickTime palette
+ for (uint i = 0; i < 256; i++) {
+ _palette[i * 3] = *data;
+ _palette[i * 3 + 1] = *(data + 2);
+ _palette[i * 3 + 2] = *(data + 4);
+ data += 6;
+ }
+
+ _palette[0] = _palette[1] = _palette[2] = 0;
+}
+
+} // End of namespace Graphics
diff --git a/graphics/video/codecs/cdtoons.h b/graphics/video/codecs/cdtoons.h
new file mode 100644
index 0000000000..3695302fec
--- /dev/null
+++ b/graphics/video/codecs/cdtoons.h
@@ -0,0 +1,70 @@
+/* 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_VIDEO_CDTOONS_H
+#define GRAPHICS_VIDEO_CDTOONS_H
+
+#include "graphics/video/codecs/codec.h"
+
+#include "common/hashmap.h"
+
+namespace Graphics {
+
+struct CDToonsBlock {
+ uint16 flags;
+ uint32 size;
+ uint16 startFrame;
+ uint16 endFrame;
+ uint16 unknown12;
+ byte *data;
+};
+
+class CDToonsDecoder : public Codec {
+public:
+ CDToonsDecoder(uint16 width, uint16 height);
+ ~CDToonsDecoder();
+
+ Surface *decodeImage(Common::SeekableReadStream *stream);
+ PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
+ bool containsPalette() const { return true; }
+ const byte *getPalette() { _dirtyPalette = false; return _palette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+
+private:
+ Surface *_surface;
+ byte _palette[256 * 3];
+ bool _dirtyPalette;
+ uint16 _currentPaletteId;
+
+ uint16 _currentFrame;
+ Common::HashMap<uint16, CDToonsBlock> _blocks;
+
+ void renderBlock(byte *data, uint size, int x, int y, uint width, uint height);
+ void setPalette(byte *data);
+};
+
+} // End of namespace Graphics
+
+#endif