ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

cdtoons.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "image/codecs/cdtoons.h"
00024 #include "common/rect.h"
00025 #include "common/stream.h"
00026 #include "common/textconsole.h"
00027 #include "common/array.h"
00028 
00029 namespace Image {
00030 
00031 struct CDToonsAction {
00032     uint16 blockId;
00033     Common::Rect rect;
00034 };
00035 
00036 struct CDToonsDiff {
00037     byte *data;
00038     uint32 size;
00039     Common::Rect rect;
00040 };
00041 
00042 static Common::Rect readRect(Common::SeekableReadStream &stream) {
00043     Common::Rect rect;
00044     rect.top = stream.readUint16BE();
00045     rect.left = stream.readUint16BE();
00046     rect.bottom = stream.readUint16BE();
00047     rect.right = stream.readUint16BE();
00048     return rect;
00049 }
00050 
00051 CDToonsDecoder::CDToonsDecoder(uint16 width, uint16 height) {
00052     debugN(5, "CDToons: width %d, height %d\n", width, height);
00053 
00054     _surface = new Graphics::Surface();
00055     _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
00056 
00057     _currentPaletteId = 0;
00058     memset(_palette, 0, 256 * 3);
00059     _dirtyPalette = false;
00060 }
00061 
00062 CDToonsDecoder::~CDToonsDecoder() {
00063     _surface->free();
00064     delete _surface;
00065 
00066     for (Common::HashMap<uint16, CDToonsBlock>::iterator i = _blocks.begin(); i != _blocks.end(); i++)
00067         delete[] i->_value.data;
00068 }
00069 
00070 Graphics::Surface *CDToonsDecoder::decodeFrame(Common::SeekableReadStream &stream) {
00071     uint16 u0 = stream.readUint16BE(); // always 9?
00072     uint16 frameId = stream.readUint16BE();
00073     uint16 blocksValidUntil = stream.readUint16BE();
00074     byte u6 = stream.readByte();
00075     byte backgroundColor = stream.readByte();
00076     debugN(5, "CDToons frame %d, size %d, unknown %04x (at 0), blocks valid until %d, unknown 6 is %02x, bkg color is %02x\n",
00077         frameId, stream.size(), u0, blocksValidUntil, u6, backgroundColor);
00078 
00079     Common::Rect clipRect = readRect(stream);
00080     debugN(9, "CDToons clipRect: (%d, %d) to (%d, %d)\n",
00081         clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
00082 
00083     Common::Rect dirtyRect = readRect(stream);
00084     debugN(9, "CDToons dirtyRect: (%d, %d) to (%d, %d)\n",
00085         dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
00086 
00087     uint32 flags = stream.readUint32BE();
00088     if (flags & 0x80)
00089         error("CDToons: frame already processed?");
00090     debugN(5, "CDToons flags: %08x\n", flags);
00091 
00092     uint16 blockCount = stream.readUint16BE();
00093     uint16 blockOffset = stream.readUint16BE();
00094     debugN(9, "CDToons: %d blocks at 0x%04x\n",
00095         blockCount, blockOffset);
00096 
00097     // max block id?
00098     uint16 u32 = stream.readUint16BE();
00099     debugN(5, "CDToons unknown at 32: %04x\n", u32);
00100 
00101     byte actionCount = stream.readByte();
00102     byte u35 = stream.readByte();
00103 
00104     uint16 paletteId = stream.readUint16BE();
00105     byte paletteSet = stream.readByte();
00106     debugN(9, "CDToons palette id %04x, palette byte %02x\n",
00107         paletteId, paletteSet);
00108 
00109     byte u39 = stream.readByte();
00110     uint16 u40 = stream.readUint16BE();
00111     uint16 u42 = stream.readUint16BE();
00112     debugN(5, "CDToons: unknown at 35 is %02x, unknowns at 39: %02x, %04x, %04x\n",
00113         u35, u39, u40, u42);
00114 
00115     Common::Array<CDToonsAction> actions;
00116 
00117     for (uint i = 0; i < actionCount; i++) {
00118         CDToonsAction action;
00119         action.blockId = stream.readUint16BE();
00120         action.rect = readRect(stream);
00121         debugN(9, "CDToons action: render block %d at (%d, %d) to (%d, %d)\n",
00122             action.blockId, action.rect.left, action.rect.top, action.rect.right, action.rect.bottom);
00123         actions.push_back(action);
00124     }
00125 
00126     if (stream.pos() > blockOffset)
00127         error("CDToons header ended at 0x%08x, but blocks should have started at 0x%08x",
00128             stream.pos(), blockOffset);
00129 
00130     if (stream.pos() != blockOffset)
00131         error("CDToons had %d unknown bytes after header", blockOffset - stream.pos());
00132 
00133     for (uint i = 0; i < blockCount; i++) {
00134         uint16 blockId = stream.readUint16BE();
00135         if (blockId >= 1200)
00136             error("CDToons: block id %d was too high", blockId);
00137         if (_blocks.contains(blockId))
00138             error("CDToons: new block %d was already seen", blockId);
00139 
00140         CDToonsBlock block;
00141         block.flags = stream.readUint16BE();
00142         // flag 1 = palette, flag 2 = data?
00143         if (block.flags & 0x8000)
00144             error("CDToons: block already processed?");
00145         block.size = stream.readUint32BE();
00146         if (block.size < 14)
00147             error("CDToons: block size was %d, too small", block.size);
00148         block.size -= 14;
00149         block.startFrame = stream.readUint16BE();
00150         block.endFrame = stream.readUint16BE();
00151         block.unknown12 = stream.readUint16BE();
00152         block.data = new byte[block.size];
00153         stream.read(block.data, block.size);
00154 
00155         debugN(9, "CDToons block id 0x%04x of size 0x%08x, flags %04x, from frame %d to %d, unknown at 12 is %04x\n",
00156             blockId, block.size, block.flags, block.startFrame, block.endFrame, block.unknown12);
00157 
00158         _blocks[blockId] = block;
00159     }
00160 
00161     byte xFrmBegin = 0, xFrmCount;
00162     Common::Array<CDToonsDiff> diffs;
00163 
00164     while (true) {
00165         int32 nextPos = stream.pos();
00166         uint32 tag = stream.readUint32BE();
00167         uint32 size = stream.readUint32BE();
00168         nextPos += size;
00169 
00170         switch (tag) {
00171         case MKTAG('D','i','f','f'):
00172             {
00173             debugN(5, "CDToons: Diff\n");
00174             uint16 count = stream.readUint16BE();
00175 
00176             Common::Rect diffClipRect = readRect(stream);
00177             debugN(9, "CDToons diffClipRect: (%d, %d) to (%d, %d)\n",
00178                 diffClipRect.left, diffClipRect.top, diffClipRect.right, diffClipRect.bottom);
00179 
00180             debugN(5, "CDToons Diff: %d subentries\n", count);
00181             for (uint i = 0; i < count; i++) {
00182                 CDToonsDiff diff;
00183 
00184                 diff.rect = readRect(stream);
00185                 diff.size = stream.readUint32BE();
00186                 if (diff.size < 20)
00187                     error("CDToons: Diff block size was %d, too small", diff.size);
00188 
00189                 uint16 diffWidth = stream.readUint16BE();
00190                 uint16 diffHeight = stream.readUint16BE();
00191                 uint16 unknown16 = stream.readUint16BE();
00192                 uint16 unknown18 = stream.readUint16BE();
00193                 diff.size -= 8;
00194 
00195                 if (diffWidth != diff.rect.width() || diffHeight != diff.rect.height())
00196                     error("CDToons: Diff sizes didn't match");
00197                 debugN(5, "CDToons Diff: size %d, frame from (%d, %d) to (%d, %d), unknowns %04x, %04x\n",
00198                     diff.size, diff.rect.left, diff.rect.top, diff.rect.right, diff.rect.bottom,
00199                     unknown16, unknown18);
00200 
00201                 diff.data = new byte[diff.size];
00202                 stream.read(diff.data, diff.size);
00203                 diffs.push_back(diff);
00204             }
00205             }
00206             break;
00207         case MKTAG('X','F','r','m'):
00208             {
00209             debugN(5, "CDToons: XFrm\n");
00210             if (!(flags & 0x10))
00211                 error("CDToons: useless XFrm?");
00212 
00213             if (xFrmBegin)
00214                 error("CDToons: duplicate XFrm");
00215             xFrmBegin = stream.readByte();
00216             xFrmCount = stream.readByte();
00217             debugN(9, "CDToons XFrm: run %d actions from %d\n", xFrmCount, xFrmBegin - 1);
00218 
00219             // TODO: don't ignore (if xFrmCount is non-zero)
00220             Common::Rect dirtyRectXFrm = readRect(stream);
00221             debugN(9, "CDToons XFrm dirtyRect: (%d, %d) to (%d, %d)\n",
00222                 dirtyRectXFrm.left, dirtyRectXFrm.top, dirtyRectXFrm.right, dirtyRectXFrm.bottom);
00223 
00224             // always zero?
00225             Common::Rect dirtyRect2XFrm = readRect(stream);
00226             debugN(9, "CDToons XFrm dirtyRect2: (%d, %d) to (%d, %d)\n",
00227                 dirtyRect2XFrm.left, dirtyRect2XFrm.top, dirtyRect2XFrm.right, dirtyRect2XFrm.bottom);
00228             }
00229             break;
00230         case MKTAG('M','r','k','s'):
00231             debugN(5, "CDToons: Mrks\n");
00232             if (!(flags & 0x2))
00233                 error("CDToons: useless Mrks?");
00234 
00235             // TODO
00236             warning("CDToons: encountered Mrks, not implemented yet");
00237             break;
00238         case MKTAG('S','c','a','l'):
00239             // TODO
00240             warning("CDToons: encountered Scal, not implemented yet");
00241             break;
00242         case MKTAG('W','r','M','p'):
00243             warning("CDToons: encountered WrMp, ignoring");
00244             break;
00245         case MKTAG('F','r','t','R'):
00246             {
00247             debugN(5, "CDToons: FrtR\n");
00248             if (!(flags & 0x40))
00249                 error("CDToons: useless FrtR?");
00250 
00251             uint16 count = stream.readUint16BE();
00252             debugN(9, "CDToons FrtR: %d dirty rectangles\n", count);
00253             for (uint i = 0; i < count; i++) {
00254                 Common::Rect dirtyRectFrtR = readRect(stream);
00255                 debugN(9, "CDToons FrtR dirtyRect: (%d, %d) to (%d, %d)\n",
00256                     dirtyRectFrtR.left, dirtyRectFrtR.top, dirtyRectFrtR.right, dirtyRectFrtR.bottom);
00257             }
00258             }
00259             break;
00260         case MKTAG('B','c','k','R'):
00261             {
00262             debugN(5, "CDToons: BckR\n");
00263             if (!(flags & 0x20))
00264                 error("CDToons: useless BckR?");
00265 
00266             uint16 count = stream.readUint16BE();
00267             debugN(9, "CDToons BckR: %d subentries\n", count);
00268             for (uint i = 0; i < count; i++) {
00269                 Common::Rect dirtyRectBckR = readRect(stream);
00270                 debugN(9, "CDToons BckR dirtyRect: (%d, %d) to (%d, %d)\n",
00271                     dirtyRectBckR.left, dirtyRectBckR.top, dirtyRectBckR.right, dirtyRectBckR.bottom);
00272             }
00273             }
00274             break;
00275         default:
00276             warning("Unknown CDToons tag '%s'", tag2str(tag));
00277         }
00278 
00279         if (stream.pos() > nextPos)
00280             error("CDToons ran off the end of a block while reading it (at %d, next block at %d)",
00281                 stream.pos(), nextPos);
00282         if (stream.pos() != nextPos) {
00283             warning("CDToons had %d unknown bytes after block", nextPos - stream.pos());
00284             stream.seek(nextPos);
00285         }
00286 
00287         if (stream.pos() == stream.size())
00288             break;
00289     }
00290 
00291     for (uint i = 0; i < diffs.size(); i++) {
00292         renderBlock(diffs[i].data, diffs[i].size, diffs[i].rect.left, diffs[i].rect.top, diffs[i].rect.width(), diffs[i].rect.height());
00293         delete[] diffs[i].data;
00294     }
00295     if (!diffs.empty())
00296         return _surface;
00297 
00298     for (uint i = 0; i < actions.size(); i++) {
00299         CDToonsAction &action = actions[i];
00300         if (i == 0 && action.blockId == 0)
00301             memset(_surface->getPixels(), backgroundColor, _surface->w * _surface->h);
00302         if (!_blocks.contains(action.blockId))
00303             continue;
00304         if (!action.rect.right)
00305             continue;
00306         if (i == 0 && !diffs.empty())
00307             continue;
00308 
00309         CDToonsBlock &block = _blocks[action.blockId];
00310         uint16 width = READ_BE_UINT16(block.data + 2);
00311         uint16 height = READ_BE_UINT16(block.data);
00312 
00313         renderBlock(block.data + 14, block.size - 14, action.rect.left, action.rect.top, width, height);
00314     }
00315 
00316     if (paletteId && _currentPaletteId != paletteId) {
00317         if (!_blocks.contains(paletteId))
00318             error("CDToons: no block for palette %04x", paletteId);
00319         if (_blocks[paletteId].size != 2 * 3 * 256)
00320             error("CDToons: palette %04x is wrong size (%d)", paletteId, _blocks[paletteId].size);
00321 
00322         _currentPaletteId = paletteId;
00323         if (!paletteSet)
00324             setPalette(_blocks[paletteId].data);
00325     }
00326 
00327     return _surface;
00328 }
00329 
00330 void CDToonsDecoder::renderBlock(byte *data, uint dataSize, int destX, int destY, uint width, uint height) {
00331     byte *currData = data;
00332     byte *dataEnd = data + dataSize;
00333 
00334     debugN(9, "CDToons renderBlock at (%d, %d), width %d, height %d\n",
00335         destX, destY, width, height);
00336 
00337     if (destX + width > _surface->w)
00338         width = _surface->w - destX;
00339     if (destY + height > _surface->h)
00340         height = _surface->h - destY;
00341 
00342     uint skip = 0;
00343     if (destX < 0) {
00344         skip = -destX;
00345         if (width <= skip)
00346             return;
00347         width -= skip;
00348         destX = 0;
00349     }
00350 
00351     for (uint y = 0; y < height; y++) {
00352         if (destY + (int)y >= _surface->h)
00353             break;
00354 
00355         if (currData + 2 > dataEnd)
00356             error("CDToons renderBlock overran whole data by %d bytes", (uint32)(currData - dataEnd));
00357 
00358         uint16 lineSize = READ_BE_UINT16(currData);
00359         currData += 2;
00360         byte *nextLine = currData + lineSize;
00361 
00362         if (nextLine > dataEnd)
00363             error("CDToons renderBlock was going to overrun data by %d bytes (line size %d)",
00364                 (uint32)(nextLine - dataEnd), (uint32)(nextLine - currData));
00365 
00366         if (destY + (int)y < 0) {
00367             currData = nextLine;
00368             continue;
00369         }
00370 
00371         byte *pixels = (byte *)_surface->getBasePtr(destX, destY + y);
00372 
00373         int leftToSkip = skip;
00374         uint x = 0;
00375         bool done = false;
00376         while (x < width && !done) {
00377             int size = (uint)*currData;
00378             currData++;
00379             bool raw = !(size & 0x80);
00380             size = (size & 0x7f) + 1;
00381 
00382             if (leftToSkip) {
00383                 if (leftToSkip >= size) {
00384                     leftToSkip -= size;
00385                     if (raw)
00386                         currData += size;
00387                     else
00388                         currData++;
00389                     continue;
00390                 } else {
00391                     size -= leftToSkip;
00392                     if (raw)
00393                         currData += leftToSkip;
00394                     leftToSkip = 0;
00395                 }
00396             }
00397 
00398             if (x + size >= width) {
00399                 size = width - x;
00400                 done = true;
00401             }
00402             if (destX + (int)x + size >= (int)_surface->w) {
00403                 size = MIN<int>((int)_surface->w - destX - (int)x, width - x);
00404                 done = true;
00405             }
00406             if (size <= 0) {
00407                 size = 0;
00408                 done = true;
00409             }
00410 
00411             if (raw) {
00412                 memcpy(pixels + x, currData, size);
00413                 currData += size;
00414                 x += size;
00415             } else {
00416                 byte color = *currData;
00417                 currData++;
00418                 if (color) {
00419                     memset(pixels + x, color, size);
00420                 }
00421                 x += size;
00422             }
00423 
00424             if (currData > nextLine) {
00425                 warning("CDToons renderBlock overran line by %d bytes", (uint32)(currData - nextLine));
00426                 return;
00427             }
00428         }
00429 
00430         currData = nextLine;
00431     }
00432 }
00433 
00434 void CDToonsDecoder::setPalette(byte *data) {
00435     _dirtyPalette = true;
00436 
00437     // A lovely QuickTime palette
00438     for (uint i = 0; i < 256; i++) {
00439         _palette[i * 3]     = *data;
00440         _palette[i * 3 + 1] = *(data + 2);
00441         _palette[i * 3 + 2] = *(data + 4);
00442         data += 6;
00443     }
00444 
00445     _palette[0] = _palette[1] = _palette[2] = 0;
00446 }
00447 
00448 } // End of namespace Image


Generated on Sat Feb 16 2019 05:00:46 for ResidualVM by doxygen 1.7.1
curved edge   curved edge