diff options
| author | Alejandro Marzini | 2010-08-16 00:21:07 +0000 |
|---|---|---|
| committer | Alejandro Marzini | 2010-08-16 00:21:07 +0000 |
| commit | b0409d673921163085d2e2fa440911080a7cf884 (patch) | |
| tree | 81b1bb895db6baed7881ca5cbc7ff3a830286189 /graphics/video | |
| parent | 503578ac1087b91dcb912fd7918454de73538b34 (diff) | |
| parent | b49761b6eae3a0aadefef4c4ffee6a7b583cc3ac (diff) | |
| download | scummvm-rg350-b0409d673921163085d2e2fa440911080a7cf884.tar.gz scummvm-rg350-b0409d673921163085d2e2fa440911080a7cf884.tar.bz2 scummvm-rg350-b0409d673921163085d2e2fa440911080a7cf884.zip | |
Merge trunk, from r51777 to r52105
svn-id: r52108
Diffstat (limited to 'graphics/video')
| -rw-r--r-- | graphics/video/avi_decoder.cpp | 7 | ||||
| -rw-r--r-- | graphics/video/codecs/cinepak.cpp | 11 | ||||
| -rw-r--r-- | graphics/video/codecs/indeo3.cpp (renamed from graphics/video/coktelvideo/indeo3.cpp) | 192 | ||||
| -rw-r--r-- | graphics/video/codecs/indeo3.h (renamed from graphics/video/coktelvideo/indeo3.h) | 56 | ||||
| -rw-r--r-- | graphics/video/coktel_decoder.cpp | 2469 | ||||
| -rw-r--r-- | graphics/video/coktel_decoder.h | 505 | ||||
| -rw-r--r-- | graphics/video/coktelvideo/coktelvideo.cpp | 2657 | ||||
| -rw-r--r-- | graphics/video/coktelvideo/coktelvideo.h | 594 | ||||
| -rw-r--r-- | graphics/video/mpeg_player.cpp | 126 |
9 files changed, 3127 insertions, 3490 deletions
diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp index 06589d99b0..4973cb3eb0 100644 --- a/graphics/video/avi_decoder.cpp +++ b/graphics/video/avi_decoder.cpp @@ -38,6 +38,7 @@ #include "graphics/video/codecs/cinepak.h" #include "graphics/video/codecs/msvideo1.h" #include "graphics/video/codecs/msrle.h" +#include "graphics/video/codecs/indeo3.h" namespace Graphics { @@ -387,10 +388,14 @@ Codec *AviDecoder::createCodec() { case ID_MSVC: case ID_WHAM: return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); - case ID_RLE : + case ID_RLE: return new MSRLEDecoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); case ID_CVID: return new CinepakDecoder(); +#ifdef USE_INDEO3 + case ID_IV32: + return new Indeo3Decoder(_bmInfo.width, _bmInfo.height); +#endif default: warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); } diff --git a/graphics/video/codecs/cinepak.cpp b/graphics/video/codecs/cinepak.cpp index 9892f9d966..a14eaf9acf 100644 --- a/graphics/video/codecs/cinepak.cpp +++ b/graphics/video/codecs/cinepak.cpp @@ -73,14 +73,12 @@ Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { debug (4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount); -#if 0 // Borrowed from FFMPEG. This should cut out the extra data Cinepak for Sega has (which is useless). // The theory behind this is that this is here to confuse standard Cinepak decoders. But, we won't let that happen! ;) if (_curFrame.length != (uint32)stream->size()) { if (stream->readUint16BE() == 0xFE00) stream->readUint32BE(); } -#endif if (!_curFrame.surface) { _curFrame.surface = new Surface(); @@ -105,8 +103,6 @@ Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { _curFrame.strips[i].rect.bottom = _y + stream->readUint16BE(); _curFrame.strips[i].rect.right = _curFrame.width; stream->readUint16BE(); // Ignore, substitute with our own - //printf ("Left = %d, Top = %d, Right = %d, Bottom = %d\n", _curFrame.strips[i].rect.left, _curFrame.strips[i].rect.top, _curFrame.strips[i].rect.right, _curFrame.strips[i].rect.bottom); - // Sanity check. Because Cinepak is based on 4x4 blocks, the width and height of each strip needs to be divisible by 4. assert(!(_curFrame.strips[i].rect.width() % 4) && !(_curFrame.strips[i].rect.height() % 4)); @@ -184,10 +180,9 @@ void CinepakDecoder::loadCodebook(Common::SeekableReadStream *stream, uint16 str codebook[i].u = stream->readByte() + 128; codebook[i].v = stream->readByte() + 128; } else { - /* this codebook type indicates either greyscale or - * palettized video; if palettized, U & V components will - * not be used so it is safe to set them to 128 for the - * benefit of greyscale rendering in YUV420P */ + // This codebook type indicates either greyscale or + // palettized video. We don't handle palettized video + // currently. codebook[i].u = 128; codebook[i].v = 128; } diff --git a/graphics/video/coktelvideo/indeo3.cpp b/graphics/video/codecs/indeo3.cpp index 983705ab9c..f59ae81e81 100644 --- a/graphics/video/coktelvideo/indeo3.cpp +++ b/graphics/video/codecs/indeo3.cpp @@ -38,33 +38,38 @@ #include "common/frac.h" #include "common/file.h" -#include "graphics/dither.h" -#include "graphics/video/coktelvideo/indeo3.h" +#include "graphics/conversion.h" + +#include "graphics/video/codecs/indeo3.h" namespace Graphics { -Indeo3::Indeo3(int16 width, int16 height, Graphics::PaletteLUT *palLUT) { - assert((width > 0) && (height > 0)); +Indeo3Decoder::Indeo3Decoder(uint16 width, uint16 height) : _ModPred(0), _corrector_type(0) { + _iv_frame[0].the_buf = 0; + _iv_frame[1].the_buf = 0; - _width = width; - _height = height; - _palLUT = palLUT; + _pixelFormat = g_system->getScreenFormat(); - _ditherSL = 0; - setDither(kDitherSierraLight); + _surface = new Surface; + _surface->create(width, height, _pixelFormat.bytesPerPixel); buildModPred(); allocFrames(); } -Indeo3::~Indeo3() { +Indeo3Decoder::~Indeo3Decoder() { + delete _surface; + delete[] _iv_frame[0].the_buf; delete[] _ModPred; delete[] _corrector_type; - delete _ditherSL; } -bool Indeo3::isIndeo3(byte *data, uint32 dataLen) { +PixelFormat Indeo3Decoder::getPixelFormat() const { + return _pixelFormat; +} + +bool Indeo3Decoder::isIndeo3(byte *data, uint32 dataLen) { // No data, no Indeo 3 if (!data) return false; @@ -90,23 +95,7 @@ bool Indeo3::isIndeo3(byte *data, uint32 dataLen) { return true; } -void Indeo3::setDither(DitherAlgorithm dither) { - delete _ditherSL; - _ditherSL = 0; - - _dither = dither; - - switch (dither) { - case kDitherSierraLight: - _ditherSL = new Graphics::SierraLight(_width, _palLUT); - break; - - default: - return; - } -} - -void Indeo3::buildModPred() { +void Indeo3Decoder::buildModPred() { _ModPred = new byte[8 * 128]; for (int i = 0; i < 128; i++) { @@ -133,12 +122,12 @@ void Indeo3::buildModPred() { } } -void Indeo3::allocFrames() { - int32 luma_width = (_width + 3) & (~3); - int32 luma_height = (_height + 3) & (~3); +void Indeo3Decoder::allocFrames() { + int32 luma_width = (_surface->w + 3) & (~3); + int32 luma_height = (_surface->h + 3) & (~3); - int32 chroma_width = ((luma_width >> 2) + 3) & (~3); - int32 chroma_height = ((luma_height>> 2) + 3) & (~3); + int32 chroma_width = ((luma_width >> 2) + 3) & (~3); + int32 chroma_height = ((luma_height >> 2) + 3) & (~3); int32 luma_pixels = luma_width * luma_height; int32 chroma_pixels = chroma_width * chroma_height; @@ -184,21 +173,23 @@ void Indeo3::allocFrames() { } } -bool Indeo3::decompressFrame(byte *inData, uint32 dataLen, - byte *outData, uint16 width, uint16 height) { +Surface *Indeo3Decoder::decodeImage(Common::SeekableReadStream *stream) { + uint32 dataLen = stream->size(); + + byte *inData = new byte[dataLen]; + + if (stream->read(inData, dataLen) != dataLen) + return 0; // Not Indeo 3? Fail if (!isIndeo3(inData, dataLen)) - return false; - - assert(outData); - assert(_palLUT); + return 0; uint32 frameDataLen = READ_LE_UINT32(inData + 12); // Less data than the frame should have? Fail if (dataLen < (frameDataLen - 16)) - return false; + return 0; Common::MemoryReadStream frame(inData, dataLen); @@ -219,7 +210,7 @@ bool Indeo3::decompressFrame(byte *inData, uint32 dataLen, } if (flags3 == 0x80) - return true; + return _surface; frame.skip(3); @@ -262,85 +253,40 @@ bool Indeo3::decompressFrame(byte *inData, uint32 dataLen, decodeChunk(_cur_frame->Ubuf, _ref_frame->Ubuf, chromaWidth, chromaHeight, buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(chromaWidth, 40)); - BlitState blitState; - - blitState.widthY = _cur_frame->y_w; - blitState.widthUV = _cur_frame->uv_w; - blitState.uwidthUV = chromaWidth; - blitState.uwidthOut = fWidth; - blitState.heightY = _cur_frame->y_h; - blitState.heightUV = _cur_frame->uv_h; - blitState.uheightUV = chromaHeight; - blitState.uheightOut = fHeight; - blitState.scaleWYUV = blitState.widthY / blitState.widthUV; - blitState.scaleHYUV = blitState.heightY / blitState.heightUV; - blitState.scaleWYOut = blitState.widthY / blitState.uwidthOut; - blitState.scaleHYOut = blitState.heightY / blitState.uheightOut; - blitState.lineWidthOut = blitState.scaleWYOut * blitState.uwidthOut; - blitState.lineHeightOut = blitState.scaleHYOut * blitState.uheightOut; - blitState.bufY = _cur_frame->Ybuf; - blitState.bufU = _cur_frame->Ubuf; - blitState.bufV = _cur_frame->Vbuf; - blitState.bufOut = outData; - - blitFrame(blitState); + // Blit the frame onto the surface + const byte *srcY = _cur_frame->Ybuf; + const byte *srcU = _cur_frame->Ubuf; + const byte *srcV = _cur_frame->Vbuf; + byte *dest = (byte *)_surface->pixels; + for (uint32 y = 0; y < fHeight; y++) { + byte *rowDest = dest; - return true; -} - -void Indeo3::blitFrame(BlitState &s) { - if (_ditherSL) - _ditherSL->newFrame(); - - for (s.curY = 0; s.curY < s.uheightOut; s.curY++) { - if (_dither == kDitherNone) - blitLine(s); - else - blitLineDither(s); - } -} + for (uint32 x = 0; x < fWidth; x++, rowDest += _surface->bytesPerPixel) { + const byte cY = srcY[x]; + const byte cU = srcU[x >> 2]; + const byte cV = srcV[x >> 2]; -void Indeo3::blitLine(BlitState &s) { - byte *lineU = s.bufU + (s.curY >> 2) * s.uwidthUV; - byte *lineV = s.bufV + (s.curY >> 2) * s.uwidthUV; + byte r = 0, g = 0, b = 0; + YUV2RGB(cY, cU, cV, r, g, b); - for (s.curX = 0; s.curX < s.uwidthOut; s.curX++) { - byte dataY = *s.bufY++; - byte dataU = lineU[s.curX >> 2]; - byte dataV = lineV[s.curX >> 2]; - - for (int n = 0; n < s.scaleWYOut; n++) - *s.bufOut++ = _palLUT->findNearest(dataY, dataU, dataV); - } + const uint32 color = _pixelFormat.RGBToColor(r, g, b); - byte *lineDest = s.bufOut - s.lineWidthOut; - for (int n = 1; n < s.scaleHYOut; n++) { - memcpy(s.bufOut, lineDest, s.lineWidthOut); - s.bufOut += s.lineWidthOut; - } -} - -void Indeo3::blitLineDither(BlitState &s) { - byte *lineU = s.bufU + (s.curY >> 2) * s.uwidthUV; - byte *lineV = s.bufV + (s.curY >> 2) * s.uwidthUV; - - for (uint16 i = 0; i < s.scaleHYOut; i++) { - byte *bufY = s.bufY; - - for (s.curX = 0; s.curX < s.uwidthOut; s.curX++) { - byte dataY = *bufY++; - byte dataU = lineU[s.curX >> 2]; - byte dataV = lineV[s.curX >> 2]; + if (_surface->bytesPerPixel == 1) + *((uint8 *)rowDest) = (uint8)color; + else if (_surface->bytesPerPixel == 2) + *((uint16 *)rowDest) = (uint16)color; + } - for (int n = 0; n < s.scaleWYOut; n++) - *s.bufOut++ = _ditherSL->dither(dataY, dataU, dataV, s.curX * s.scaleWYOut + n); + dest += _surface->pitch; + srcY += fWidth; + if ((y & 3) == 3) { + srcU += fWidth >> 2; + srcV += fWidth >> 2; } - - _ditherSL->nextLine(); } - s.bufY += s.uwidthOut; + return _surface; } typedef struct { @@ -397,7 +343,7 @@ typedef struct { } \ lp2 = 4; -void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, +void Indeo3Decoder::decodeChunk(byte *cur, byte *ref, int width, int height, const byte *buf1, uint32 fflags2, const byte *hdr, const byte *buf2, int min_width_160) { @@ -729,7 +675,7 @@ void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, break; case 9: - warning("Indeo3::decodeChunk: Untested (1)"); + warning("Indeo3Decoder::decodeChunk: Untested (1)"); lv1 = *buf1++; lv = (lv1 & 0x7F) << 1; lv += (lv << 8); @@ -906,7 +852,7 @@ void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, break; case 9: - warning("Indeo3::decodeChunk: Untested (2)"); + warning("Indeo3Decoder::decodeChunk: Untested (2)"); lv1 = *buf1; lv = (lv1 & 0x7F) << 1; lv += (lv << 8); @@ -1002,7 +948,7 @@ void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, break; case 9: - warning("Indeo3::decodeChunk: Untested (3)"); + warning("Indeo3Decoder::decodeChunk: Untested (3)"); lv1 = *buf1; lv = (lv1 & 0x7F) << 1; lv += (lv << 8); @@ -1099,7 +1045,7 @@ void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, break; case 9: - warning("Indeo3::decodeChunk: Untested (4)"); + warning("Indeo3Decoder::decodeChunk: Untested (4)"); lv1 = *buf1++; lv = (lv1 & 0x7F) << 1; lv += (lv << 8); @@ -1127,7 +1073,7 @@ void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, // FIXME: I've seen case 13 happen in Urban // Runner. Perhaps it uses a more recent form of // Indeo 3? There appears to have been several. - warning("Indeo3::decodeChunk: Unknown case %d", k); + warning("Indeo3Decoder::decodeChunk: Unknown case %d", k); return; } } @@ -1157,15 +1103,15 @@ void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, // static data -const int Indeo3::_corrector_type_0[24] = { +const int Indeo3Decoder::_corrector_type_0[24] = { 195, 159, 133, 115, 101, 93, 87, 77, 195, 159, 133, 115, 101, 93, 87, 77, 128, 79, 79, 79, 79, 79, 79, 79 }; -const int Indeo3::_corrector_type_2[8] = { 9, 7, 6, 8, 5, 4, 3, 2 }; +const int Indeo3Decoder::_corrector_type_2[8] = { 9, 7, 6, 8, 5, 4, 3, 2 }; -const uint32 Indeo3::correction[] = { +const uint32 Indeo3Decoder::correction[] = { 0x00000000, 0x00000202, 0xfffffdfe, 0x000002ff, 0xfffffd01, 0xffffff03, 0x000000fd, 0x00000404, 0xfffffbfc, 0x00000501, 0xfffffaff, 0x00000105, 0xfffffefb, 0x000003fc, 0xfffffc04, 0x000005fe, 0xfffffa02, 0xfffffe06, 0x000001fa, 0x00000904, 0xfffff6fc, 0x00000409, 0xfffffbf7, 0x00000909, @@ -1937,7 +1883,7 @@ const uint32 Indeo3::correction[] = { }; -const uint32 Indeo3::correctionloworder[] = { +const uint32 Indeo3Decoder::correctionloworder[] = { 0x00000000, 0x02020202, 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, 0xfeff0303, 0x0100fcfd, 0x04040404, 0xfbfbfbfc, 0x05050101, 0xfafafeff, 0x01010505, 0xfefefafb, 0x0403fbfc, 0xfbfc0404, 0x0605fdfe, 0xf9fa0202, 0xfdfe0606, 0x0201f9fa, 0x09090404, 0xf6f6fbfc, 0x04040909, 0xfbfbf6f7, 0x09090909, @@ -2709,7 +2655,7 @@ const uint32 Indeo3::correctionloworder[] = { }; -const uint32 Indeo3::correctionhighorder[] = { +const uint32 Indeo3Decoder::correctionhighorder[] = { 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, diff --git a/graphics/video/coktelvideo/indeo3.h b/graphics/video/codecs/indeo3.h index af6ef26449..8d50b74b1e 100644 --- a/graphics/video/coktelvideo/indeo3.h +++ b/graphics/video/codecs/indeo3.h @@ -34,36 +34,28 @@ * written, produced, and directed by Alan Smithee */ -#ifndef GRAPHICS_VIDEO_INDEO3_H -#define GRAPHICS_VIDEO_INDEO3_H +#ifndef GRAPHICS_INDEO3_H +#define GRAPHICS_INDEO3_H -#include "common/stream.h" +#include "graphics/video/codecs/codec.h" namespace Graphics { - class PaletteLUT; - class SierraLight; -} -namespace Graphics { - -class Indeo3 { +class Indeo3Decoder : public Codec { public: - enum DitherAlgorithm { - kDitherNone = 0, - kDitherSierraLight - }; + Indeo3Decoder(uint16 width, uint16 height); + ~Indeo3Decoder(); - Indeo3(int16 width, int16 height, Graphics::PaletteLUT *palLUT); - ~Indeo3(); + Surface *decodeImage(Common::SeekableReadStream *stream); + PixelFormat getPixelFormat() const; static bool isIndeo3(byte *data, uint32 dataLen); - void setDither(DitherAlgorithm dither); +private: + Surface *_surface; - bool decompressFrame(byte *inData, uint32 dataLen, - byte *outData, uint16 width, uint16 height); + PixelFormat _pixelFormat; -private: static const int _corrector_type_0[24]; static const int _corrector_type_2[8]; static const uint32 correction[]; @@ -80,8 +72,6 @@ private: uint16 uv_w, uv_h; }; - int16 _width; - int16 _height; YUVBufs _iv_frame[2]; YUVBufs *_cur_frame; YUVBufs *_ref_frame; @@ -89,38 +79,16 @@ private: byte *_ModPred; uint16 *_corrector_type; - Graphics::PaletteLUT *_palLUT; - - DitherAlgorithm _dither; - Graphics::SierraLight *_ditherSL; - - struct BlitState { - uint32 curX, curY; - uint16 widthY, widthUV; - uint16 heightY, heightUV; - uint16 uwidthUV, uwidthOut; - uint16 uheightUV, uheightOut; - uint16 scaleWYUV, scaleWYOut; - uint16 scaleHYUV, scaleHYOut; - uint16 lineWidthOut, lineHeightOut; - byte *bufY, *bufU, *bufV, *bufOut; - }; - void buildModPred(); void allocFrames(); void decodeChunk(byte *cur, byte *ref, int width, int height, const byte *buf1, uint32 fflags2, const byte *hdr, const byte *buf2, int min_width_160); - - void blitFrame(BlitState &s); - - void blitLine(BlitState &s); - void blitLineDither(BlitState &s); }; } // End of namespace Graphics -#endif // GRAPHICS_VIDEO_INDEO3_H +#endif // GRAPHICS_INDEO3_H #endif // USE_INDEO3 diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp new file mode 100644 index 0000000000..0709288091 --- /dev/null +++ b/graphics/video/coktel_decoder.cpp @@ -0,0 +1,2469 @@ +/* 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/coktel_decoder.h" +#include "graphics/video/codecs/codec.h" +#include "graphics/video/codecs/indeo3.h" + +#ifdef GRAPHICS_VIDEO_COKTELDECODER_H + +#include "sound/audiostream.h" +#include "sound/decoders/raw.h" + +static const uint32 kVideoCodecIndeo3 = MKID_BE('iv32'); + +namespace Graphics { + +CoktelDecoder::State::State() : flags(0), speechId(0) { +} + + +CoktelDecoder::CoktelDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : + _mixer(mixer), _soundType(soundType), _width(0), _height(0), _x(0), _y(0), + _defaultX(0), _defaultY(0), _features(0), _frameCount(0), _paletteDirty(false), + _ownSurface(true), _frameRate(12), _hasSound(false), _soundEnabled(false), + _soundStage(kSoundNone), _audioStream(0) { + + assert(_mixer); + + memset(_palette, 0, 768); +} + +CoktelDecoder::~CoktelDecoder() { +} + +bool CoktelDecoder::evaluateSeekFrame(int32 &frame, int whence) const { + if (!isVideoLoaded()) + // Nothing to do + return false; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _frameCount - frame - 1; + else if (whence == SEEK_SET) + frame--; + else + return false; + + if ((frame < -1) || (frame >= ((int32) _frameCount))) + // Out of range + return false; + + return true; +} + +void CoktelDecoder::setSurfaceMemory(void *mem, uint16 width, uint16 height, uint8 bpp) { + freeSurface(); + + // Sanity checks + assert((width > 0) && (height > 0)); + assert(bpp == getPixelFormat().bytesPerPixel); + + // Create a surface over this memory + _surface.w = width; + _surface.h = height; + _surface.pitch = width * bpp; + _surface.pixels = mem; + _surface.bytesPerPixel = bpp; + + _ownSurface = false; +} + +void CoktelDecoder::setSurfaceMemory() { + freeSurface(); + createSurface(); + + _ownSurface = true; +} + +const Surface *CoktelDecoder::getSurface() const { + if (!isVideoLoaded()) + return 0; + + return &_surface; +} + +bool CoktelDecoder::hasSurface() { + return _surface.pixels != 0; +} + +void CoktelDecoder::createSurface() { + if (hasSurface()) + return; + + if ((_width > 0) && (_height > 0)) + _surface.create(_width, _height, getPixelFormat().bytesPerPixel); + + _ownSurface = true; +} + +void CoktelDecoder::freeSurface() { + if (!_ownSurface) { + _surface.w = 0; + _surface.h = 0; + _surface.pitch = 0; + _surface.pixels = 0; + _surface.bytesPerPixel = 0; + } else + _surface.free(); + + _ownSurface = true; +} + +void CoktelDecoder::setXY(uint16 x, uint16 y) { + _x = x; + _y = y; +} + +void CoktelDecoder::setXY() { + setXY(_defaultX, _defaultY); +} + +void CoktelDecoder::setFrameRate(Common::Rational frameRate) { + _frameRate = frameRate; +} + +uint16 CoktelDecoder::getDefaultX() const { + return _defaultX; +} + +uint16 CoktelDecoder::getDefaultY() const { + return _defaultY; +} + +const Common::List<Common::Rect> &CoktelDecoder::getDirtyRects() const { + return _dirtyRects; +} + +bool CoktelDecoder::hasPalette() const { + return (_features & kFeaturesPalette) != 0; +} + +bool CoktelDecoder::hasSound() const { + return _hasSound; +} + +bool CoktelDecoder::isSoundEnabled() const { + return _soundEnabled; +} + +bool CoktelDecoder::isSoundPlaying() const { + return _audioStream && _mixer->isSoundHandleActive(_audioHandle); +} + +void CoktelDecoder::enableSound() { + if (!hasSound() || isSoundEnabled()) + return; + + // Sanity check + if (_mixer->getOutputRate() == 0) + return; + + // Only possible on the first frame + if (_curFrame > -1) + return; + + _soundEnabled = true; +} + +void CoktelDecoder::disableSound() { + if (_audioStream) { + + if (_soundStage == kSoundPlaying) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + } else + delete _audioStream; + + } + + _soundEnabled = false; + _soundStage = kSoundNone; + + _audioStream = 0; +} + +bool CoktelDecoder::getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height) { + return false; +} + +bool CoktelDecoder::hasEmbeddedFiles() const { + return false; +} + +bool CoktelDecoder::hasEmbeddedFile(const Common::String &fileName) const { + return false; +} + +Common::MemoryReadStream *CoktelDecoder::getEmbeddedFile(const Common::String &fileName) const { + return 0; +} + +int32 CoktelDecoder::getSubtitleIndex() const { + return -1; +} + +void CoktelDecoder::close() { + disableSound(); + freeSurface(); + + _x = 0; + _y = 0; + + _defaultX = 0; + _defaultY = 0; + + _features = 0; + + _frameCount = 0; + + _hasSound = false; +} + +uint16 CoktelDecoder::getWidth() const { + return _width; +} + +uint16 CoktelDecoder::getHeight() const { + return _height; +} + +uint32 CoktelDecoder::getFrameCount() const { + return _frameCount; +} + +byte *CoktelDecoder::getPalette() { + return _palette; +} + +bool CoktelDecoder::hasDirtyPalette() const { + return (_features & kFeaturesPalette) && _paletteDirty; +} + +void CoktelDecoder::deLZ77(byte *dest, byte *src) { + int i; + byte buf[4370]; + uint16 chunkLength; + uint32 frameLength; + uint16 bufPos1; + uint16 bufPos2; + uint16 tmp; + uint8 chunkBitField; + uint8 chunkCount; + bool mode; + + frameLength = READ_LE_UINT32(src); + src += 4; + + if ((READ_LE_UINT16(src) == 0x1234) && (READ_LE_UINT16(src + 2) == 0x5678)) { + src += 4; + bufPos1 = 273; + mode = 1; // 123Ch (cmp al, 12h) + } else { + bufPos1 = 4078; + mode = 0; // 275h (jnz +2) + } + + memset(buf, 32, bufPos1); + chunkCount = 1; + chunkBitField = 0; + + while (frameLength > 0) { + chunkCount--; + if (chunkCount == 0) { + tmp = *src++; + chunkCount = 8; + chunkBitField = tmp; + } + if (chunkBitField % 2) { + chunkBitField >>= 1; + buf[bufPos1] = *src; + *dest++ = *src++; + bufPos1 = (bufPos1 + 1) % 4096; + frameLength--; + continue; + } + chunkBitField >>= 1; + + tmp = READ_LE_UINT16(src); + src += 2; + chunkLength = ((tmp & 0xF00) >> 8) + 3; + + if ((mode && ((chunkLength & 0xFF) == 0x12)) || + (!mode && (chunkLength == 0))) + chunkLength = *src++ + 0x12; + + bufPos2 = (tmp & 0xFF) + ((tmp >> 4) & 0x0F00); + if (((tmp + chunkLength) >= 4096) || + ((chunkLength + bufPos1) >= 4096)) { + + for (i = 0; i < chunkLength; i++, dest++) { + *dest = buf[bufPos2]; + buf[bufPos1] = buf[bufPos2]; + bufPos1 = (bufPos1 + 1) % 4096; + bufPos2 = (bufPos2 + 1) % 4096; + } + + } else if (((tmp + chunkLength) < bufPos1) || + ((chunkLength + bufPos1) < bufPos2)) { + + memcpy(dest, buf + bufPos2, chunkLength); + memmove(buf + bufPos1, buf + bufPos2, chunkLength); + + dest += chunkLength; + bufPos1 += chunkLength; + bufPos2 += chunkLength; + + } else { + + for (i = 0; i < chunkLength; i++, dest++, bufPos1++, bufPos2++) { + *dest = buf[bufPos2]; + buf[bufPos1] = buf[bufPos2]; + } + + } + frameLength -= chunkLength; + + } +} + +void CoktelDecoder::deRLE(byte *&destPtr, const byte *&srcPtr, int16 destLen, int16 srcLen) { + srcPtr++; + + if (srcLen & 1) { + byte data = *srcPtr++; + + if (destLen > 0) { + *destPtr++ = data; + destLen--; + } + } + + srcLen >>= 1; + + while (srcLen > 0) { + uint8 tmp = *srcPtr++; + if (tmp & 0x80) { // Verbatim copy + tmp &= 0x7F; + + int16 copyCount = MAX<int16>(0, MIN<int16>(destLen, tmp * 2)); + + memcpy(destPtr, srcPtr, copyCount); + + srcPtr += tmp * 2; + destPtr += copyCount; + destLen -= copyCount; + } else { // 2 bytes tmp times + for (int i = 0; (i < tmp) && (destLen > 0); i++) { + for (int j = 0; j < 2; j++) { + if (destLen <= 0) + break; + + *destPtr++ = srcPtr[j]; + destLen--; + } + } + srcPtr += 2; + } + srcLen -= tmp; + } +} + +// A whole, completely filled block +void CoktelDecoder::renderBlockWhole(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; + + rect.clip(_surface.w, _surface.h); + + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { + memcpy(dst, src, rect.width()); + + src += srcRect.width(); + dst += _surface.pitch; + } +} + +// A quarter-wide whole, completely filled block +void CoktelDecoder::renderBlockWhole4X(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; + + rect.clip(_surface.w, _surface.h); + + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { + byte *dstRow = dst; + const byte *srcRow = src; + + int16 count = rect.width(); + while (count >= 0) { + memset(dstRow, *srcRow, MIN<int16>(count, 4)); + + count -= 4; + dstRow += 4; + srcRow += 1; + } + + src += srcRect.width() / 4; + dst += _surface.pitch; + } +} + +// A half-high whole, completely filled block +void CoktelDecoder::renderBlockWhole2Y(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; + + rect.clip(_surface.w, _surface.h); + + int16 height = rect.height(); + + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + while (height > 1) { + memcpy(dst , src, rect.width()); + memcpy(dst + _surface.pitch, src, rect.width()); + + height -= 2; + src += srcRect.width(); + dst += 2 * _surface.pitch; + } + + if (height == 1) + memcpy(dst, src, rect.width()); +} + +// A sparse block +void CoktelDecoder::renderBlockSparse(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; + + rect.clip(_surface.w, _surface.h); + + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { + byte *dstRow = dst; + int16 pixWritten = 0; + + while (pixWritten < srcRect.width()) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, srcRect.width() - pixWritten); + copyCount = CLIP<int16>(rect.width() - pixWritten, 0, pixCount); + memcpy(dstRow, src, copyCount); + + pixWritten += pixCount; + dstRow += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + dstRow += pixCount + 1; + } + + } + + dst += _surface.pitch; + } +} + +// A half-high sparse block +void CoktelDecoder::renderBlockSparse2Y(const byte *src, Common::Rect &rect) { + warning("renderBlockSparse2Y"); + + Common::Rect srcRect = rect; + + rect.clip(_surface.w, _surface.h); + + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i += 2) { + byte *dstRow = dst; + int16 pixWritten = 0; + + while (pixWritten < srcRect.width()) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, srcRect.width() - pixWritten); + copyCount = CLIP<int16>(rect.width() - pixWritten, 0, pixCount); + memcpy(dstRow , src, pixCount); + memcpy(dstRow + _surface.pitch, src, pixCount); + + pixWritten += pixCount; + dstRow += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + dstRow += pixCount + 1; + } + + } + + dst += _surface.pitch; + } +} + +void CoktelDecoder::renderBlockRLE(const byte *src, Common::Rect &rect) { + Common::Rect srcRect = rect; + + rect.clip(_surface.w, _surface.h); + + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + for (int i = 0; i < rect.height(); i++) { + byte *dstRow = dst; + int16 pixWritten = 0; + + while (pixWritten < srcRect.width()) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, srcRect.width() - pixWritten); + copyCount = CLIP<int16>(rect.width() - pixWritten, 0, pixCount); + + if (*src != 0xFF) { // Normal copy + + memcpy(dstRow, src, copyCount); + dstRow += copyCount; + src += pixCount; + } else + deRLE(dstRow, src, copyCount, pixCount); + + pixWritten += pixCount; + } else { // "Hole" + int16 copyCount = CLIP<int16>(rect.width() - pixWritten, 0, pixCount + 1); + + dstRow += copyCount; + pixWritten += pixCount + 1; + } + + } + + dst += _surface.pitch; + } +} + +Common::Rational CoktelDecoder::getFrameRate() const { + return _frameRate; +} + +uint32 CoktelDecoder::getTimeToNextFrame() const { + // If there is no audio, just return the static time between + // frames without any elaborate sync calculation. This is + // needed for the gob engine, since it has a lot of control + // between the videos and often plays just few frames out of + // the middle of a long video. + + if (!hasSound()) + return Common::Rational(1000, _frameRate).toInt(); + + // If there /is/ audio, we do need to keep video and audio + // in sync, though. + + return FixedRateVideoDecoder::getTimeToNextFrame(); +} + +inline void CoktelDecoder::unsignedToSigned(byte *buffer, int length) { + while (length-- > 0) *buffer++ ^= 0x80; +} + + +PreIMDDecoder::PreIMDDecoder(uint16 width, uint16 height, + Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + _stream(0), _videoBuffer(0), _videoBufferSize(0) { + + _width = width; + _height = height; +} + +PreIMDDecoder::~PreIMDDecoder() { + close(); +} + +bool PreIMDDecoder::seek(int32 frame, int whence, bool restart) { + if (!evaluateSeekFrame(frame, whence)) + return false; + + if (frame == _curFrame) + // Nothing to do + return true; + + // Run through the frames + _curFrame = -1; + _stream->seek(2); + while (_curFrame != frame) { + uint16 frameSize = _stream->readUint16LE(); + + _stream->skip(frameSize + 2); + + _curFrame++; + } + + return true; +} + +bool PreIMDDecoder::load(Common::SeekableReadStream *stream) { + // Since PreIMDs don't have any width and height values stored, + // we need them to be specified in the constructor + assert((_width > 0) && (_height > 0)); + + close(); + + _stream = stream; + + _stream->seek(0); + + _frameCount = _stream->readUint16LE(); + + _videoBufferSize = _width * _height; + _videoBuffer = new byte[_videoBufferSize]; + + memset(_videoBuffer, 0, _videoBufferSize); + + return true; +} + +void PreIMDDecoder::close() { + reset(); + + CoktelDecoder::close(); + + delete _stream; + + delete[] _videoBuffer; + + _stream = 0; + + _videoBuffer = 0; + _videoBufferSize = 0; +} + +bool PreIMDDecoder::isVideoLoaded() const { + return _stream != 0; +} + +Surface *PreIMDDecoder::decodeNextFrame() { + if (!isVideoLoaded() || endOfVideo()) + return 0; + + createSurface(); + + processFrame(); + renderFrame(); + + if (_curFrame == 0) + _startTime = g_system->getMillis(); + + return &_surface; +} + +void PreIMDDecoder::processFrame() { + uint16 frameSize = _stream->readUint16LE(); + + uint32 nextFramePos = _stream->pos() + frameSize + 2; + + byte cmd; + + cmd = _stream->readByte(); + frameSize--; + + if (cmd == 0) { + // Palette. Ignored by Fascination, though. + + // NOTE: If we ever find another game using this format, + // palettes may need to be evaluated. + + _stream->skip(768); + + frameSize -= 769; + + cmd = _stream->readByte(); + } + + if (cmd != 2) { + // Partial frame data + + uint32 fSize = frameSize; + uint32 vidSize = _videoBufferSize; + + byte *vidBuffer = _videoBuffer; + + while ((fSize > 0) && (vidSize > 0)) { + uint32 n = _stream->readByte(); + fSize--; + + if ((n & 0x80) != 0) { + // Data + + n = MIN<uint32>((n & 0x7F) + 1, MIN(fSize, vidSize)); + + _stream->read(vidBuffer, n); + + vidBuffer += n; + vidSize -= n; + fSize -= n; + + } else { + // Skip + + n = MIN<uint32>(n + 1, vidSize); + + vidBuffer += n; + vidSize -= n; + } + } + + } else { + // Full direct frame + + uint32 vidSize = MIN<uint32>(_videoBufferSize, frameSize); + + _stream->read(_videoBuffer, vidSize); + } + + _stream->seek(nextFramePos); + + _curFrame++; +} + +// Just a simple blit +void PreIMDDecoder::renderFrame() { + _dirtyRects.clear(); + + uint16 w = CLIP<int32>(_surface.w - _x, 0, _width); + uint16 h = CLIP<int32>(_surface.h - _y, 0, _height); + + const byte *src = _videoBuffer; + byte *dst = (byte *)_surface.pixels + (_y * _surface.pitch) + _x; + + uint32 frameDataSize = _videoBufferSize; + + while (h-- > 0) { + uint32 n = MIN<uint32>(w, frameDataSize); + + memcpy(dst, src, n); + + src += _width; + dst += _surface.pitch; + + frameDataSize -= n; + } + + _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height)); +} + +PixelFormat PreIMDDecoder::getPixelFormat() const { + return PixelFormat::createFormatCLUT8(); +} + + +IMDDecoder::IMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + _stream(0), _version(0), _stdX(-1), _stdY(-1), _stdWidth(-1), _stdHeight(-1), + _flags(0), _firstFramePos(0), _framePos(0), _frameCoords(0), + _frameData(0), _frameDataSize(0), _frameDataLen(0), + _videoBuffer(0), _videoBufferSize(0), + _soundFlags(0), _soundFreq(0), _soundSliceSize(0), _soundSlicesCount(0) { + +} + +IMDDecoder::~IMDDecoder() { + close(); +} + +bool IMDDecoder::seek(int32 frame, int whence, bool restart) { + if (!evaluateSeekFrame(frame, whence)) + return false; + + if (frame == _curFrame) + // Nothing to do + return true; + + // Try every possible way to find a file offset to that frame + uint32 framePos = 0; + if (frame == -1) { + // First frame, we know that position + + framePos = _firstFramePos; + + } else if (frame == 0) { + // Second frame, can be calculated from the first frame's position + + framePos = _firstFramePos; + _stream->seek(framePos); + framePos += _stream->readUint16LE() + 4; + + } else if (_framePos) { + // If we have an array of frame positions, use that + + framePos = _framePos[frame + 1]; + + } else if (restart && (_soundStage == kSoundNone)) { + // If we are asked to restart the video if necessary and have no + // audio to worry about, restart the video and run through the frames + + _curFrame = 0; + _stream->seek(_firstFramePos); + + for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) + processFrame(); + + return true; + + } else { + // Not possible + + warning("IMDDecoder::seek(): Frame %d is not directly accessible", frame + 1); + return false; + } + + // Seek + _stream->seek(framePos); + _curFrame = frame; + + return true; +} + +void IMDDecoder::setXY(uint16 x, uint16 y) { + // Adjusting the standard coordinates + if (_stdX != -1) { + if (x != 0xFFFF) + _stdX = _stdX - _x + x; + if (y != 0xFFFF) + _stdY = _stdY - _y + y; + } + + // Going through the coordinate table as well + if (_frameCoords) { + for (uint32 i = 0; i < _frameCount; i++) { + if (_frameCoords[i].left != -1) { + if (x != 0xFFFF) { + _frameCoords[i].left = _frameCoords[i].left - _x + x; + _frameCoords[i].right = _frameCoords[i].right - _x + x; + } + if (y != 0xFFFF) { + _frameCoords[i].top = _frameCoords[i].top - _y + y; + _frameCoords[i].bottom = _frameCoords[i].bottom - _y + y; + } + } + } + } + + if (x != 0xFFFF) + _x = x; + if (y != 0xFFFF) + _y = y; +} + +bool IMDDecoder::load(Common::SeekableReadStream *stream) { + close(); + + _stream = stream; + + uint16 handle; + + handle = _stream->readUint16LE(); + _version = _stream->readByte(); + + // Version checking + if ((handle != 0) || (_version < 2)) { + warning("IMDDecoder::load(): Version incorrect (%d, 0x%X)", handle, _version); + close(); + return false; + } + + // Rest header + _features = _stream->readByte(); + _frameCount = _stream->readUint16LE(); + _defaultX = _stream->readSint16LE(); + _defaultY = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + _flags = _stream->readUint16LE(); + _firstFramePos = _stream->readUint16LE(); + + _x = _defaultX; + _y = _defaultY; + + // IMDs always have video + _features |= kFeaturesVideo; + // IMDs always have palettes + _features |= kFeaturesPalette; + + // Palette + for (int i = 0; i < 768; i++) + _palette[i] = _stream->readByte() << 2; + + _paletteDirty = true; + + if (!loadCoordinates()) { + close(); + return false; + } + + uint32 framePosPos, frameCoordsPos; + if (!loadFrameTableOffsets(framePosPos, frameCoordsPos)) { + close(); + return false; + } + + if (!assessAudioProperties()) { + close(); + return false; + } + + if (!assessVideoProperties()) { + close(); + return false; + } + + if (!loadFrameTables(framePosPos, frameCoordsPos)) { + close(); + return false; + } + + // Seek to the first frame + _stream->seek(_firstFramePos); + + return true; +} + +bool IMDDecoder::loadCoordinates() { + // Standard coordinates + if (_version >= 3) { + uint16 count = _stream->readUint16LE(); + if (count > 1) { + warning("IMDDecoder::loadCoordinates(): More than one standard coordinate quad found (%d)", count); + return false; + } + + if (count != 0) { + _stdX = _stream->readSint16LE(); + _stdY = _stream->readSint16LE(); + _stdWidth = _stream->readSint16LE(); + _stdHeight = _stream->readSint16LE(); + _features |= kFeaturesStdCoords; + } else + _stdX = _stdY = _stdWidth = _stdHeight = -1; + + } else + _stdX = _stdY = _stdWidth = _stdHeight = -1; + + return true; +} + +bool IMDDecoder::loadFrameTableOffsets(uint32 &framePosPos, uint32 &frameCoordsPos) { + framePosPos = 0; + frameCoordsPos = 0; + + // Frame positions + if (_version >= 4) { + framePosPos = _stream->readUint32LE(); + if (framePosPos != 0) { + _framePos = new uint32[_frameCount]; + _features |= kFeaturesFramePos; + } + } + + // Frame coordinates + if (_features & kFeaturesFrameCoords) + frameCoordsPos = _stream->readUint32LE(); + + return true; +} + +bool IMDDecoder::assessVideoProperties() { + // Sizes of the frame data and extra video buffer + if (_features & kFeaturesDataSize) { + _frameDataSize = _stream->readUint16LE(); + if (_frameDataSize == 0) { + _frameDataSize = _stream->readUint32LE(); + _videoBufferSize = _stream->readUint32LE(); + } else + _videoBufferSize = _stream->readUint16LE(); + } else { + _frameDataSize = _width * _height + 500; + if (!(_flags & 0x100) || (_flags & 0x1000)) + _videoBufferSize = _frameDataSize; + } + + // Allocating working memory + _frameData = new byte[_frameDataSize + 500]; + memset(_frameData, 0, _frameDataSize + 500); + + _videoBuffer = new byte[_videoBufferSize + 500]; + memset(_videoBuffer, 0, _videoBufferSize + 500); + + return true; +} + +bool IMDDecoder::assessAudioProperties() { + if (_features & kFeaturesSound) { + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); + _soundSlicesCount = _stream->readSint16LE(); + + if (_soundFreq < 0) + _soundFreq = -_soundFreq; + + if (_soundSlicesCount < 0) + _soundSlicesCount = -_soundSlicesCount - 1; + + if (_soundSlicesCount > 40) { + warning("IMDDecoder::assessAudioProperties(): More than 40 sound slices found (%d)", _soundSlicesCount); + return false; + } + + _frameRate = Common::Rational(_soundFreq, _soundSliceSize); + + _hasSound = true; + _soundEnabled = true; + _soundStage = kSoundLoaded; + + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, false); + } + + return true; +} + +bool IMDDecoder::loadFrameTables(uint32 framePosPos, uint32 frameCoordsPos) { + // Positions table + if (_framePos) { + _stream->seek(framePosPos); + for (uint32 i = 0; i < _frameCount; i++) + _framePos[i] = _stream->readUint32LE(); + } + + // Coordinates table + if (_features & kFeaturesFrameCoords) { + _stream->seek(frameCoordsPos); + _frameCoords = new Coord[_frameCount]; + assert(_frameCoords); + for (uint32 i = 0; i < _frameCount; i++) { + _frameCoords[i].left = _stream->readSint16LE(); + _frameCoords[i].top = _stream->readSint16LE(); + _frameCoords[i].right = _stream->readSint16LE(); + _frameCoords[i].bottom = _stream->readSint16LE(); + } + } + + return true; +} + +void IMDDecoder::close() { + reset(); + + CoktelDecoder::close(); + + delete _stream; + + delete[] _framePos; + delete[] _frameCoords; + + delete[] _frameData; + + delete[] _videoBuffer; + + _stream = 0; + + _version = 0; + + _stdX = -1; + _stdY = -1; + _stdWidth = -1; + _stdHeight = -1; + + _flags = 0; + + _firstFramePos = 0; + _framePos = 0; + _frameCoords = 0; + + _frameData = 0; + _frameDataSize = 0; + _frameDataLen = 0; + + _videoBuffer = 0; + _videoBufferSize = 0; + + _soundFlags = 0; + _soundFreq = 0; + _soundSliceSize = 0; + _soundSlicesCount = 0; + + _hasSound = false; + _soundEnabled = false; + _soundStage = kSoundNone; +} + +bool IMDDecoder::isVideoLoaded() const { + return _stream != 0; +} + +Surface *IMDDecoder::decodeNextFrame() { + if (!isVideoLoaded() || endOfVideo()) + return 0; + + createSurface(); + + processFrame(); + + if (_curFrame == 0) + _startTime = g_system->getMillis(); + + return &_surface; +} + +void IMDDecoder::processFrame() { + _curFrame++; + + _dirtyRects.clear(); + + _paletteDirty = false; + + uint32 cmd = 0; + bool hasNextCmd = false; + bool startSound = false; + + do { + cmd = _stream->readUint16LE(); + + if ((cmd & kCommandBreakMask) == kCommandBreak) { + // Flow control + + if (cmd == kCommandBreak) { + _stream->skip(2); + cmd = _stream->readUint16LE(); + } + + // Break + if (cmd == kCommandBreakSkip0) { + continue; + } else if (cmd == kCommandBreakSkip16) { + cmd = _stream->readUint16LE(); + _stream->skip(cmd); + continue; + } else if (cmd == kCommandBreakSkip32) { + cmd = _stream->readUint32LE(); + _stream->skip(cmd); + continue; + } + } + + // Audio + if (cmd == kCommandNextSound) { + + nextSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); + + } else if (cmd == kCommandStartSound) { + + startSound = initialSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); + + } else + emptySoundSlice(hasNextCmd); + + // Set palette + if (cmd == kCommandPalette) { + _stream->skip(2); + + _paletteDirty = true; + + for (int i = 0; i < 768; i++) + _palette[i] = _stream->readByte() << 2; + + cmd = _stream->readUint16LE(); + } + + hasNextCmd = false; + + if (cmd == kCommandJump) { + // Jump to frame + + int16 frame = _stream->readSint16LE(); + if (_framePos) { + _curFrame = frame - 1; + _stream->seek(_framePos[frame]); + + hasNextCmd = true; + } + + } else if (cmd == kCommandVideoData) { + + _frameDataLen = _stream->readUint32LE() + 2; + _stream->read(_frameData, _frameDataLen); + + Common::Rect rect = calcFrameCoords(_curFrame); + + if (renderFrame(rect)) + _dirtyRects.push_back(rect); + + } else if (cmd != 0) { + + _frameDataLen = cmd + 2; + _stream->read(_frameData, _frameDataLen); + + Common::Rect rect = calcFrameCoords(_curFrame); + + if (renderFrame(rect)) + _dirtyRects.push_back(rect); + + } + + } while (hasNextCmd); + + // Start the audio stream if necessary + if (startSound && _soundEnabled) { + _mixer->playStream(_soundType, &_audioHandle, _audioStream); + _soundStage = kSoundPlaying; + } + + // End the audio stream if necessary + if ((_curFrame >= (int32)(_frameCount - 1)) && (_soundStage == kSoundPlaying)) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + _audioStream = 0; + _soundStage = kSoundNone; + } + +} + +Common::Rect IMDDecoder::calcFrameCoords(uint32 frame) { + Common::Rect rect; + + if (frame == 0) { + // First frame is always a full "keyframe" + + rect.left = _x; + rect.top = _y; + rect.right = _x + _width; + rect.bottom = _y + _height; + } else if (_frameCoords && ((_frameCoords[frame].left != -1))) { + // We have frame coordinates for that frame + + rect.left = _frameCoords[frame].left; + rect.top = _frameCoords[frame].top; + rect.right = _frameCoords[frame].right + 1; + rect.bottom = _frameCoords[frame].bottom + 1; + } else if (_stdX != -1) { + // We have standard coordinates + + rect.left = _stdX; + rect.top = _stdY; + rect.right = _stdX + _stdWidth; + rect.bottom = _stdY + _stdHeight; + } else { + // Otherwise, it must be a full "keyframe" + + rect.left = _x; + rect.top = _y; + rect.right = _x + _width; + rect.bottom = _y + _height; + } + + return rect; +} + +bool IMDDecoder::renderFrame(Common::Rect &rect) { + if (!rect.isValidRect()) + // Invalid rendering area + return false; + + // Clip the rendering area to the video's visible area + rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); + if (!rect.isValidRect() || rect.isEmpty()) + // Result is empty => nothing to do + return false; + + byte *dataPtr = _frameData; + + uint8 type = *dataPtr++; + + if (type & 0x10) { + // Palette data + + // One byte index + int index = *dataPtr++; + + int count = MIN((255 - index) * 3, 48); + for (int i = 0; i < count; i++) + _palette[index * 3 + i] = dataPtr[i] << 2; + + dataPtr += 48; + type ^= 0x10; + + _paletteDirty = true; + } + + if (type & 0x80) { + // Frame data is compressed + + type &= 0x7F; + + if ((type == 2) && (rect.width() == _surface.w) && (_x == 0)) { + // Directly uncompress onto the video surface + deLZ77((byte *)_surface.pixels + (_y * _surface.pitch), dataPtr); + return true; + } + + deLZ77(_videoBuffer, dataPtr); + + dataPtr = _videoBuffer; + } + + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (dataPtr, rect); + else if (type == 0x02) + renderBlockWhole (dataPtr, rect); + else if (type == 0x42) + renderBlockWhole4X (dataPtr, rect); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (dataPtr, rect); + else + renderBlockSparse2Y(dataPtr, rect); + + return true; +} + +void IMDDecoder::nextSoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) { + // Skip sound + + _stream->skip(_soundSliceSize); + return; + } + + // Read, convert, queue + + byte *soundBuf = (byte *)malloc(_soundSliceSize); + + _stream->read(soundBuf, _soundSliceSize); + unsignedToSigned(soundBuf, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); +} + +bool IMDDecoder::initialSoundSlice(bool hasNextCmd) { + int dataLength = _soundSliceSize * _soundSlicesCount; + + if (hasNextCmd || !_soundEnabled) { + // Skip sound + + _stream->skip(dataLength); + return false; + } + + // Read, convert, queue + + byte *soundBuf = (byte *)malloc(dataLength); + + _stream->read(soundBuf, dataLength); + unsignedToSigned(soundBuf, dataLength); + + _audioStream->queueBuffer(soundBuf, dataLength, DisposeAfterUse::YES, 0); + + return _soundStage == kSoundLoaded; +} + +void IMDDecoder::emptySoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) + return; + + // Create an empty sound buffer and queue it + + byte *soundBuf = (byte *)malloc(_soundSliceSize); + + memset(soundBuf, 0, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); +} + +PixelFormat IMDDecoder::getPixelFormat() const { + return PixelFormat::createFormatCLUT8(); +} + + +VMDDecoder::File::File() { + offset = 0; + size = 0; + realSize = 0; +} + + +VMDDecoder::Part::Part() { + type = kPartTypeSeparator; + field_1 = 0; + field_E = 0; + size = 0; + left = 0; + top = 0; + right = 0; + bottom = 0; + id = 0; + flags = 0; +} + + +VMDDecoder::Frame::Frame() { + parts = 0; + offset = 0; +} + +VMDDecoder::Frame::~Frame() { + delete[] parts; +} + + +const uint16 VMDDecoder::_tableDPCM[128] = { + 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, + 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, + 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, + 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, + 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280, + 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0, + 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, + 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, + 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0, + 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480, + 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700, + 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, + 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 +}; + +const int32 VMDDecoder::_tableADPCM[] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767, 0 +}; + +const int32 VMDDecoder::_tableADPCMStep[] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +VMDDecoder::VMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + _stream(0), _version(0), _flags(0), _frameInfoOffset(0), _partsPerFrame(0), _frames(0), + _soundFlags(0), _soundFreq(0), _soundSliceSize(0), _soundSlicesCount(0), + _soundBytesPerSample(0), _soundStereo(0), _soundHeaderSize(0), _soundDataSize(0), + _audioFormat(kAudioFormat8bitRaw), _hasVideo(false), _videoCodec(0), + _blitMode(0), _bytesPerPixel(0), _firstFramePos(0), + _frameData(0), _frameDataSize(0), _frameDataLen(0), + _videoBuffer(0), _videoBufferSize(0), _externalCodec(false), _codec(0), + _subtitle(-1) { + +} + +VMDDecoder::~VMDDecoder() { + close(); +} + +bool VMDDecoder::seek(int32 frame, int whence, bool restart) { + if (!evaluateSeekFrame(frame, whence)) + return false; + + if (frame == _curFrame) + // Nothing to do + return true; + + // Restart sound + if (_hasSound && (frame == -1) && (_soundStage == kSoundNone) && !_audioStream) { + _soundStage = kSoundLoaded; + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); + } + + // Seek + _stream->seek(_frames[frame + 1].offset); + _curFrame = frame; + + _subtitle = -1; + + return true; +} + +void VMDDecoder::setXY(uint16 x, uint16 y) { + for (uint32 i = 0; i < _frameCount; i++) { + for (int j = 0; j < _partsPerFrame; j++) { + + if (_frames[i].parts[j].type == kPartTypeVideo) { + if (x != 0xFFFF) { + _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; + _frames[i].parts[j].right = _frames[i].parts[j].right - _x + x; + } + if (y != 0xFFFF) { + _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; + _frames[i].parts[j].bottom = _frames[i].parts[j].bottom - _y + y; + } + } + + } + } + + if (x != 0xFFFF) + _x = x; + if (y != 0xFFFF) + _y = y; +} + +bool VMDDecoder::load(Common::SeekableReadStream *stream) { + close(); + + _stream = stream; + + _stream->seek(0); + + uint16 headerLength; + uint16 handle; + + headerLength = _stream->readUint16LE(); + handle = _stream->readUint16LE(); + _version = _stream->readUint16LE(); + + // Version checking + if (headerLength == 50) { + // Newer version, used in Addy 5 upwards + warning("VMDDecoder::load(): TODO: Addy 5 videos"); + } else if (headerLength == 814) { + // Old version + _features |= kFeaturesPalette; + } else { + warning("VMDDecoder::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version); + close(); + return false; + } + + _frameCount = _stream->readUint16LE(); + + _defaultX = _stream->readSint16LE(); + _defaultY = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + + _x = _defaultX; + _y = _defaultY; + + if ((_width != 0) && (_height != 0)) { + + _hasVideo = true; + _features |= kFeaturesVideo; + + } else + _hasVideo = false; + + _bytesPerPixel = 1; + if (_version & 4) + _bytesPerPixel = handle + 1; + + if (_bytesPerPixel != 1) { + warning("TODO: _bytesPerPixel = %d", _bytesPerPixel); + close(); + return false; + } + + if (_bytesPerPixel > 3) { + warning("VMDDecoder::load(): Requested %d bytes per pixel (%d, %d, %d)", + _bytesPerPixel, headerLength, handle, _version); + close(); + return false; + } + + _flags = _stream->readUint16LE(); + + _partsPerFrame = _stream->readUint16LE(); + _firstFramePos = _stream->readUint32LE(); + + _videoCodec = _stream->readUint32BE(); + + if (_features & kFeaturesPalette) { + for (int i = 0; i < 768; i++) + _palette[i] = _stream->readByte() << 2; + + _paletteDirty = true; + } + + _frameDataSize = _stream->readUint32LE(); + _videoBufferSize = _stream->readUint32LE(); + + if (_hasVideo) { + if (!assessVideoProperties()) { + close(); + return false; + } + } + + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); + _soundSlicesCount = _stream->readSint16LE(); + _soundFlags = _stream->readUint16LE(); + + _hasSound = (_soundFreq != 0); + + if (_hasSound) { + if (!assessAudioProperties()) { + close(); + return false; + } + } else + _frameRate = 12; + + _frameInfoOffset = _stream->readUint32LE(); + + int numFiles; + if (!readFrameTable(numFiles)) { + close(); + return false; + } + + _stream->seek(_firstFramePos); + + if (numFiles == 0) + return true; + + _files.reserve(numFiles); + if (!readFiles()) { + close(); + return false; + } + + _stream->seek(_firstFramePos); + return true; +} + +bool VMDDecoder::assessVideoProperties() { + if ((_version & 2) && !(_version & 8)) { + _externalCodec = true; + _frameDataSize = _videoBufferSize = 0; + } else + _externalCodec = false; + + if (_externalCodec) { + if (_videoCodec == kVideoCodecIndeo3) { +#ifdef USE_INDEO3 + _codec = new Indeo3Decoder(_width, _height); +#else + warning("VMDDecoder::assessVideoProperties(): Indeo3 decoder not compiled in"); +#endif + } else { + warning("VMDDecoder::assessVideoProperties(): Unknown video codec FourCC \"%s\"", + tag2str(_videoCodec)); + return false; + } + } + + if (_externalCodec) + _blitMode = 0; + else if (_bytesPerPixel == 1) + _blitMode = 0; + else if ((_bytesPerPixel == 2) || (_bytesPerPixel == 3)) { + int n = (_flags & 0x80) ? 2 : 3; + + _blitMode = n - 1; + _bytesPerPixel = n; + } + + if (_hasVideo) { + if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) + _frameDataSize = _width * _height + 1000; + if ((_videoBufferSize == 0) || (_videoBufferSize > 1048576)) + _videoBufferSize = _frameDataSize; + + _frameData = new byte[_frameDataSize]; + memset(_frameData, 0, _frameDataSize); + + _videoBuffer = new byte[_videoBufferSize]; + memset(_videoBuffer, 0, _videoBufferSize); + } + + return true; +} + +bool VMDDecoder::assessAudioProperties() { + bool supportedFormat = true; + + _features |= kFeaturesSound; + + _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0); + + if (_soundSliceSize < 0) { + _soundBytesPerSample = 2; + _soundSliceSize = -_soundSliceSize; + + if (_soundFlags & 0x10) { + _audioFormat = kAudioFormat16bitADPCM; + _soundHeaderSize = 3; + _soundDataSize = _soundSliceSize >> 1; + + if (_soundStereo > 0) + supportedFormat = false; + + } else { + _audioFormat = kAudioFormat16bitDPCM; + _soundHeaderSize = 1; + _soundDataSize = _soundSliceSize; + + if (_soundStereo == 1) { + supportedFormat = false; + } else if (_soundStereo == 2) { + _soundDataSize = 2 * _soundDataSize + 2; + _soundHeaderSize = 4; + } + + } + } else { + _soundBytesPerSample = 1; + _audioFormat = kAudioFormat8bitRaw; + _soundHeaderSize = 0; + _soundDataSize = _soundSliceSize; + + if (_soundStereo > 0) + supportedFormat = false; + } + + if (!supportedFormat) { + warning("VMDDecoder::assessAudioProperties(): Unsupported audio format: %d bits, encoding %d, stereo %d", + _soundBytesPerSample * 8, _audioFormat, _soundStereo); + return false; + } + + _frameRate = Common::Rational(_soundFreq, _soundSliceSize); + + _hasSound = true; + _soundEnabled = true; + _soundStage = kSoundLoaded; + + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); + + return true; +} + +bool VMDDecoder::readFrameTable(int &numFiles) { + numFiles = 0; + + _stream->seek(_frameInfoOffset); + _frames = new Frame[_frameCount]; + for (uint16 i = 0; i < _frameCount; i++) { + _frames[i].parts = new Part[_partsPerFrame]; + _stream->skip(2); // Unknown + _frames[i].offset = _stream->readUint32LE(); + } + + for (uint16 i = 0; i < _frameCount; i++) { + bool separator = false; + + for (uint16 j = 0; j < _partsPerFrame; j++) { + + _frames[i].parts[j].type = (PartType) _stream->readByte(); + _frames[i].parts[j].field_1 = _stream->readByte(); + _frames[i].parts[j].size = _stream->readUint32LE(); + + if (_frames[i].parts[j].type == kPartTypeAudio) { + + _frames[i].parts[j].flags = _stream->readByte(); + _stream->skip(9); // Unknown + + } else if (_frames[i].parts[j].type == kPartTypeVideo) { + + _frames[i].parts[j].left = _stream->readUint16LE(); + _frames[i].parts[j].top = _stream->readUint16LE(); + _frames[i].parts[j].right = _stream->readUint16LE(); + _frames[i].parts[j].bottom = _stream->readUint16LE(); + _frames[i].parts[j].field_E = _stream->readByte(); + _frames[i].parts[j].flags = _stream->readByte(); + + } else if (_frames[i].parts[j].type == kPartTypeSubtitle) { + _frames[i].parts[j].id = _stream->readUint16LE(); + // Speech text file name + _stream->skip(8); + } else if (_frames[i].parts[j].type == kPartTypeFile) { + if (!separator) + numFiles++; + _stream->skip(10); + } else if (_frames[i].parts[j].type == kPartTypeSeparator) { + separator = true; + _stream->skip(10); + } else { + // Unknown type + _stream->skip(10); + } + + } + } + + return true; +} + +bool VMDDecoder::readFiles() { + uint32 ssize = _stream->size(); + for (uint16 i = 0; i < _frameCount; i++) { + _stream->seek(_frames[i].offset); + + for (uint16 j = 0; j < _partsPerFrame; j++) { + if (_frames[i].parts[j].type == kPartTypeSeparator) + break; + + if (_frames[i].parts[j].type == kPartTypeFile) { + File file;; + + file.offset = _stream->pos() + 20; + file.size = _frames[i].parts[j].size; + file.realSize = _stream->readUint32LE(); + + char name[16]; + + _stream->read(name, 16); + name[15] = '\0'; + + file.name = name; + + _stream->skip(_frames[i].parts[j].size - 20); + + if ((((uint32) file.realSize) >= ssize) || (file.name == "")) + continue; + + _files.push_back(file); + + } else + _stream->skip(_frames[i].parts[j].size); + } + } + + return true; +} + +void VMDDecoder::close() { + reset(); + + CoktelDecoder::close(); + + delete _stream; + + delete[] _frames; + + delete[] _frameData; + delete[] _videoBuffer; + + delete _codec; + + _files.clear(); + + + _stream = 0; + + _version = 0; + _flags = 0; + + _frameInfoOffset = 0; + _partsPerFrame = 0; + _frames = 0; + + _soundFlags = 0; + _soundFreq = 0; + _soundSliceSize = 0; + _soundSlicesCount = 0; + _soundBytesPerSample = 0; + _soundStereo = 0; + _soundHeaderSize = 0; + _soundDataSize = 0; + _audioFormat = kAudioFormat8bitRaw; + + _hasVideo = false; + _videoCodec = 0; + _blitMode = 0; + _bytesPerPixel = 0; + + _firstFramePos = 0; + + _frameData = 0; + _frameDataSize = 0; + _frameDataLen = 0; + + _videoBuffer = 0; + _videoBufferSize = 0; + + _externalCodec = false; + _codec = 0; +} + +bool VMDDecoder::isVideoLoaded() const { + return _stream != 0; +} + +Surface *VMDDecoder::decodeNextFrame() { + if (!isVideoLoaded() || endOfVideo()) + return 0; + + createSurface(); + + processFrame(); + + if (_curFrame == 0) + _startTime = g_system->getMillis(); + + return &_surface; +} + +void VMDDecoder::processFrame() { + _curFrame++; + + _dirtyRects.clear(); + + _paletteDirty = false; + _subtitle = -1; + + bool startSound = false; + + for (uint16 i = 0; i < _partsPerFrame; i++) { + uint32 pos = _stream->pos(); + + Part &part = _frames[_curFrame].parts[i]; + + if (part.type == kPartTypeAudio) { + + if (part.flags == 1) { + // Next sound slice data + + if (_soundEnabled) { + filledSoundSlice(part.size); + + if (_soundStage == kSoundLoaded) + startSound = true; + + } else + _stream->skip(part.size); + + } else if (part.flags == 2) { + // Initial sound data (all slices) + + if (_soundEnabled) { + uint32 mask = _stream->readUint32LE(); + filledSoundSlices(part.size - 4, mask); + + if (_soundStage == kSoundLoaded) + startSound = true; + + } else + _stream->skip(part.size); + + } else if (part.flags == 3) { + // Empty sound slice + + if (_soundEnabled) { + emptySoundSlice(_soundDataSize * _soundBytesPerSample); + + if (_soundStage == kSoundLoaded) + startSound = true; + } + + _stream->skip(part.size); + } else if (part.flags == 4) { + warning("VMDDecoder::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size); + disableSound(); + _stream->skip(part.size); + } else { + warning("VMDDecoder::processFrame(): Unknown sound type %d", part.flags); + _stream->skip(part.size); + } + + _stream->seek(pos + part.size); + + } else if ((part.type == kPartTypeVideo) && !_hasVideo) { + + warning("VMDDecoder::processFrame(): Header claims there's no video, but video found (%d)", part.size); + _stream->skip(part.size); + + } else if ((part.type == kPartTypeVideo) && _hasVideo) { + + uint32 size = part.size; + + // New palette + if (part.flags & 2) { + uint8 index = _stream->readByte(); + uint8 count = _stream->readByte(); + + for (int j = 0; j < ((count + 1) * 3); j++) + _palette[index * 3 + j] = _stream->readByte() << 2; + + _stream->skip((255 - count) * 3); + + _paletteDirty = true; + + size -= (768 + 2); + } + + _stream->read(_frameData, size); + _frameDataLen = size; + + Common::Rect rect(part.left, part.top, part.right + 1, part.bottom + 1); + if (renderFrame(rect)) + _dirtyRects.push_back(rect); + + } else if (part.type == kPartTypeSeparator) { + + // Ignore + + } else if (part.type == kPartTypeFile) { + + // Ignore + _stream->skip(part.size); + + } else if (part.type == kPartType4) { + + // Unknown, ignore + _stream->skip(part.size); + + } else if (part.type == kPartTypeSubtitle) { + + _subtitle = part.id; + _stream->skip(part.size); + + } else { + + warning("VMDDecoder::processFrame(): Unknown frame part type %d, size %d (%d of %d)", + part.type, part.size, i + 1, _partsPerFrame); + + } + } + + if (startSound && _soundEnabled) { + if (_hasSound && _audioStream) { + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream); + _soundStage = kSoundPlaying; + } else + _soundStage = kSoundNone; + } + + if (((uint32)_curFrame == (_frameCount - 1)) && (_soundStage == 2)) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + _audioStream = 0; + _soundStage = kSoundNone; + } +} + +bool VMDDecoder::renderFrame(Common::Rect &rect) { + if (!rect.isValidRect()) + // Invalid rendering area + return false; + + // Clip the rendering area to the video's visible area + rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); + if (!rect.isValidRect() || rect.isEmpty()) + // Result is empty => nothing to do + return false; + + if (_externalCodec) { + // TODO + warning("_external codec"); + return false; + } + + if (_blitMode > 0) { + // TODO + warning("_blitMode == %d", _blitMode); + return false; + } + + byte *dataPtr = _frameData; + + uint8 type = *dataPtr++; + + if (type & 0x80) { + // Frame data is compressed + + type &= 0x7F; + + if ((type == 2) && (rect.width() == _surface.w) && (_x == 0)) { + // Directly uncompress onto the video surface + deLZ77((byte *)_surface.pixels + (_y * _surface.pitch), dataPtr); + return true; + } + + deLZ77(_videoBuffer, dataPtr); + + dataPtr = _videoBuffer; + } + + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (dataPtr, rect); + else if (type == 0x02) + renderBlockWhole (dataPtr, rect); + else if (type == 0x03) + renderBlockRLE (dataPtr, rect); + else if (type == 0x42) + renderBlockWhole4X (dataPtr, rect); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (dataPtr, rect); + else + renderBlockSparse2Y(dataPtr, rect); + + return true; +} + +void VMDDecoder::emptySoundSlice(uint32 size) { + byte *sound = soundEmpty(size); + + if (sound) { + uint32 flags = 0; + flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; + flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; + + _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); + } +} + +void VMDDecoder::filledSoundSlice(uint32 size) { + byte *sound = 0; + if (_audioFormat == kAudioFormat8bitRaw) + sound = sound8bitRaw(size); + else if (_audioFormat == kAudioFormat16bitDPCM) + sound = sound16bitDPCM(size); + else if (_audioFormat == kAudioFormat16bitADPCM) + sound = sound16bitADPCM(size); + + if (sound) { + uint32 flags = 0; + flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; + flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; + + _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); + } +} + +void VMDDecoder::filledSoundSlices(uint32 size, uint32 mask) { + bool fillInfo[32]; + + uint8 max; + uint8 n = evaluateMask(mask, fillInfo, max); + + int32 extraSize; + + extraSize = size - n * _soundDataSize; + + if (_soundSlicesCount > 32) + extraSize -= (_soundSlicesCount - 32) * _soundDataSize; + + if (n > 0) + extraSize /= n; + + for (uint8 i = 0; i < max; i++) + if (fillInfo[i]) + filledSoundSlice(_soundDataSize + extraSize); + else + emptySoundSlice(_soundDataSize * _soundBytesPerSample); + + if (_soundSlicesCount > 32) + filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize); +} + +uint8 VMDDecoder::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) { + max = MIN<int>(_soundSlicesCount - 1, 31); + + uint8 n = 0; + for (int i = 0; i < max; i++) { + + if (!(mask & 1)) { + n++; + *fillInfo++ = true; + } else + *fillInfo++ = false; + + mask >>= 1; + } + + return n; +} + +byte *VMDDecoder::soundEmpty(uint32 &size) { + if (!_audioStream) + return 0; + + byte *soundBuf = (byte *)malloc(size); + memset(soundBuf, 0, size); + + return soundBuf; +} + +byte *VMDDecoder::sound8bitRaw(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + byte *soundBuf = (byte *)malloc(size); + _stream->read(soundBuf, size); + unsignedToSigned(soundBuf, size); + + return soundBuf; +} + +byte *VMDDecoder::sound16bitDPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + int32 init[2]; + + init[0] = _stream->readSint16LE(); + size -= 2; + + if (_soundStereo > 0) { + init[1] = _stream->readSint16LE(); + size -= 2; + } + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deDPCM(data, size, init); + + delete[] data; + + return sound; +} + +byte *VMDDecoder::sound16bitADPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + int32 init = _stream->readSint16LE(); + size -= 2; + + int32 index = _stream->readByte(); + size--; + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deADPCM(data, size, init, index); + + delete[] data; + + return sound; +} + +byte *VMDDecoder::deDPCM(const byte *data, uint32 &size, int32 init[2]) { + if (!data || (size == 0)) + return 0; + + int channels = (_soundStereo > 0) ? 2 : 1; + + uint32 inSize = size; + uint32 outSize = size + channels; + + int16 *out = (int16 *)malloc(outSize * 2); + byte *sound = (byte *) out; + + int channel = 0; + + for (int i = 0; i < channels; i++) { + *out++ = TO_BE_16(init[channel]); + + channel = (channel + 1) % channels; + } + + while (inSize-- > 0) { + if (*data & 0x80) + init[channel] -= _tableDPCM[*data++ & 0x7F]; + else + init[channel] += _tableDPCM[*data++]; + + init[channel] = CLIP<int32>(init[channel], -32768, 32767); + *out++ = TO_BE_16(init[channel]); + + channel = (channel + 1) % channels; + } + + size = outSize * 2; + return sound; +} + +// Yet another IMA ADPCM variant +byte *VMDDecoder::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) { + if (!data || (size == 0)) + return 0; + + uint32 outSize = size * 2; + + int16 *out = (int16 *)malloc(outSize * 2); + byte *sound = (byte *) out; + + index = CLIP<int32>(index, 0, 88); + + int32 predictor = _tableADPCM[index]; + + uint32 dataByte = 0; + bool newByte = true; + + size *= 2; + while (size -- > 0) { + byte code = 0; + + if (newByte) { + dataByte = *data++; + code = (dataByte >> 4) & 0xF; + } else + code = dataByte & 0xF; + + newByte = !newByte; + + index += _tableADPCMStep[code]; + index = CLIP<int32>(index, 0, 88); + + int32 value = predictor / 8; + + if (code & 4) + value += predictor; + if (code & 2) + value += predictor / 2; + if (code & 1) + value += predictor / 4; + + if (code & 8) + init -= value; + else + init += value; + + init = CLIP<int32>(init, -32768, 32767); + + predictor = _tableADPCM[index]; + + *out++ = TO_BE_16(init); + } + + size = outSize * 2; + return sound; +} + +PixelFormat VMDDecoder::getPixelFormat() const { + return PixelFormat::createFormatCLUT8(); +} + +bool VMDDecoder::getPartCoords(int16 frame, PartType type, int16 &x, int16 &y, int16 &width, int16 &height) { + if (frame >= ((int32) _frameCount)) + return false; + + Frame &f = _frames[frame]; + + // Look for a part matching the requested type, stopping at a separator + Part *part = 0; + for (int i = 0; i < _partsPerFrame; i++) { + Part &p = f.parts[i]; + + if ((p.type == kPartTypeSeparator) || (p.type == type)) { + part = &p; + break; + } + } + + if (!part) + return false; + + x = part->left; + y = part->top; + width = part->right - part->left + 1; + height = part->bottom - part->top + 1; + + return true; +} + +bool VMDDecoder::getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height) { + return getPartCoords(frame, kPartTypeVideo, x, y, width, height); +} + +bool VMDDecoder::hasEmbeddedFiles() const { + return !_files.empty(); +} + +bool VMDDecoder::hasEmbeddedFile(const Common::String &fileName) const { + for (Common::Array<File>::const_iterator file = _files.begin(); file != _files.end(); ++file) + if (!file->name.compareToIgnoreCase(fileName)) + return true; + + return false; +} + +Common::MemoryReadStream *VMDDecoder::getEmbeddedFile(const Common::String &fileName) const { + const File *file = 0; + + for (Common::Array<File>::const_iterator it = _files.begin(); it != _files.end(); ++it) + if (!it->name.compareToIgnoreCase(fileName)) { + file = &*it; + break; + } + + if (!file) + return 0; + + if ((file->size - 20) != file->realSize) { + warning("VMDDecoder::getEmbeddedFile(): Sizes for \"%s\" differ! (%d, %d)", + fileName.c_str(), (file->size - 20), file->realSize); + return 0; + } + + if (!_stream->seek(file->offset)) { + warning("VMDDecoder::getEmbeddedFile(): Can't seek to offset %d to (file \"%s\")", + file->offset, fileName.c_str()); + return 0; + } + + byte *data = (byte *) malloc(file->realSize); + if (_stream->read(data, file->realSize) != file->realSize) { + free(data); + warning("VMDDecoder::getEmbeddedFile(): Couldn't read %d bytes (file \"%s\")", + file->realSize, fileName.c_str()); + } + + Common::MemoryReadStream *stream = + new Common::MemoryReadStream(data, file->realSize, DisposeAfterUse::YES); + + return stream; +} + +int32 VMDDecoder::getSubtitleIndex() const { + return _subtitle; +} + +} // End of namespace Graphics + +#endif // GRAPHICS_VIDEO_COKTELDECODER_H diff --git a/graphics/video/coktel_decoder.h b/graphics/video/coktel_decoder.h new file mode 100644 index 0000000000..97be9df44b --- /dev/null +++ b/graphics/video/coktel_decoder.h @@ -0,0 +1,505 @@ +/* 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$ + * + */ + +// Currently, only GOB and SCI32 games play IMDs and VMDs, so skip compiling if GOB and SCI32 is disabled. +#if !(defined(ENABLE_GOB) || defined(ENABLE_SCI32) || defined(DYNAMIC_MODULES)) + +// Do not compile the CoktelDecoder code + +#else + +#ifndef GRAPHICS_VIDEO_COKTELDECODER_H +#define GRAPHICS_VIDEO_COKTELDECODER_H + +#include "common/list.h" +#include "common/array.h" +#include "common/rect.h" + +#include "graphics/video/video_decoder.h" + +#include "sound/mixer.h" + +namespace Audio { + class QueuingAudioStream; +} + +namespace Graphics { + +class Codec; + +class CoktelDecoder : public FixedRateVideoDecoder { +public: + struct State { + /** Set accordingly to what was done. */ + uint32 flags; + /** The id of the spoken words. */ + uint16 speechId; + + State(); + }; + + CoktelDecoder(Audio::Mixer *mixer, + Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); + ~CoktelDecoder(); + + virtual bool seek(int32 frame, int whence = SEEK_SET, bool restart = false) = 0; + + /** Draw directly onto the specified video memory. */ + void setSurfaceMemory(void *mem, uint16 width, uint16 height, uint8 bpp); + /** Reset the video memory. */ + void setSurfaceMemory(); + + const Surface *getSurface() const; + + /** Draw the video starting at this position within the video memory. */ + virtual void setXY(uint16 x, uint16 y); + /** Draw the video at the default position. */ + void setXY(); + + /** Override the video's frame rate. */ + void setFrameRate(Common::Rational frameRate); + + /** Get the video's default X position. */ + uint16 getDefaultX() const; + /** Get the video's default Y position. */ + uint16 getDefaultY() const; + + /** Return a list of rectangles that changed in the last frame. */ + const Common::List<Common::Rect> &getDirtyRects() const; + + bool hasPalette() const; + + bool hasSound() const; + bool isSoundEnabled() const; + bool isSoundPlaying() const; + + void enableSound(); + void disableSound(); + + /** Return the coordinates of the specified frame. */ + virtual bool getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height); + + /** Return whether that video has any embedded files. */ + virtual bool hasEmbeddedFiles() const; + + /** Return whether that embedded file exists. */ + virtual bool hasEmbeddedFile(const Common::String &fileName) const; + + /** Return that embedded file. */ + virtual Common::MemoryReadStream *getEmbeddedFile(const Common::String &fileName) const; + + /** Return the current subtitle index. */ + virtual int32 getSubtitleIndex() const; + + + // VideoDecoder interface + + void close(); + + uint16 getWidth() const; + uint16 getHeight() const; + + uint32 getFrameCount() const; + + byte *getPalette(); + bool hasDirtyPalette() const; + + + // FixedRateVideoDecoder interface + + uint32 getTimeToNextFrame() const; + +protected: + enum SoundStage { + kSoundNone = 0, ///< No sound. + kSoundLoaded = 1, ///< Sound loaded. + kSoundPlaying = 2 ///< Sound is playing. + }; + + enum Features { + kFeaturesNone = 0x0000, + kFeaturesPalette = 0x0008, ///< Has an own palette. + kFeaturesDataSize = 0x0020, ///< Suggests a data size. + kFeaturesSound = 0x0040, ///< Has sound. + kFeaturesFrameCoords = 0x0080, ///< Has specific frame coordinates. + kFeaturesStdCoords = 0x0100, ///< Has general standard coordinates. + kFeaturesFramePos = 0x0200, ///< Has a frame positions table. + kFeaturesVideo = 0x0400 ///< Has video. + }; + + Audio::Mixer *_mixer; + Audio::Mixer::SoundType _soundType; + + uint16 _width; + uint16 _height; + + uint16 _x; + uint16 _y; + + uint16 _defaultX; + uint16 _defaultY; + + uint32 _features; + + uint32 _frameCount; + + byte _palette[768]; + bool _paletteDirty; + + bool _ownSurface; + Surface _surface; + + Common::List<Common::Rect> _dirtyRects; + + Common::Rational _frameRate; + + // Current sound state + bool _hasSound; + bool _soundEnabled; + SoundStage _soundStage; + + Audio::QueuingAudioStream *_audioStream; + Audio::SoundHandle _audioHandle; + + bool evaluateSeekFrame(int32 &frame, int whence) const; + + // Surface management + bool hasSurface(); + void createSurface(); + void freeSurface(); + + // Decompression + void deLZ77(byte *dest, byte *src); + void deRLE(byte *&destPtr, const byte *&srcPtr, int16 destLen, int16 srcLen); + + // Block rendering + void renderBlockWhole (const byte *src, Common::Rect &rect); + void renderBlockWhole4X (const byte *src, Common::Rect &rect); + void renderBlockWhole2Y (const byte *src, Common::Rect &rect); + void renderBlockSparse (const byte *src, Common::Rect &rect); + void renderBlockSparse2Y(const byte *src, Common::Rect &rect); + void renderBlockRLE (const byte *src, Common::Rect &rect); + + // Sound helper functions + inline void unsignedToSigned(byte *buffer, int length); + + + // FixedRateVideoDecoder interface + + Common::Rational getFrameRate() const; +}; + +class PreIMDDecoder : public CoktelDecoder { +public: + PreIMDDecoder(uint16 width, uint16 height, Audio::Mixer *mixer, + Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); + ~PreIMDDecoder(); + + bool seek(int32 frame, int whence = SEEK_SET, bool restart = false); + + + // VideoDecoder interface + + bool load(Common::SeekableReadStream *stream); + void close(); + + bool isVideoLoaded() const; + + Surface *decodeNextFrame(); + + PixelFormat getPixelFormat() const; + +private: + Common::SeekableReadStream *_stream; + + // Buffer for processed frame data + byte *_videoBuffer; + uint32 _videoBufferSize; + + // Frame decoding + void processFrame(); + void renderFrame(); +}; + +class IMDDecoder : public CoktelDecoder { +public: + IMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); + ~IMDDecoder(); + + bool seek(int32 frame, int whence = SEEK_SET, bool restart = false); + + void setXY(uint16 x, uint16 y); + + + // VideoDecoder interface + + bool load(Common::SeekableReadStream *stream); + void close(); + + bool isVideoLoaded() const; + + Surface *decodeNextFrame(); + + PixelFormat getPixelFormat() const; + +private: + enum Command { + kCommandNextSound = 0xFF00, + kCommandStartSound = 0xFF01, + + kCommandBreak = 0xFFF0, + kCommandBreakSkip0 = 0xFFF1, + kCommandBreakSkip16 = 0xFFF2, + kCommandBreakSkip32 = 0xFFF3, + kCommandBreakMask = 0xFFF8, + + kCommandPalette = 0xFFF4, + kCommandVideoData = 0xFFFC, + + kCommandJump = 0xFFFD + }; + + struct Coord { + int16 left; + int16 top; + int16 right; + int16 bottom; + }; + + Common::SeekableReadStream *_stream; + + byte _version; + + // Standard coordinates gives by the header + int16 _stdX; + int16 _stdY; + int16 _stdWidth; + int16 _stdHeight; + + uint32 _flags; + + uint32 _firstFramePos; ///< Position of the first frame's data within the stream. + uint32 *_framePos; ///< Positions of all frames. + Coord *_frameCoords; ///< Coordinates of all frames. + + // Buffer for raw frame data + byte *_frameData; + uint32 _frameDataSize; + uint32 _frameDataLen; + + // Buffer for processed frame data + byte *_videoBuffer; + uint32 _videoBufferSize; + + // Sound properties + uint16 _soundFlags; + int16 _soundFreq; + int16 _soundSliceSize; + int16 _soundSlicesCount; + + // Loading helper functions + bool loadCoordinates(); + bool loadFrameTableOffsets(uint32 &framePosPos, uint32 &frameCoordsPos); + bool assessVideoProperties(); + bool assessAudioProperties(); + bool loadFrameTables(uint32 framePosPos, uint32 frameCoordsPos); + + // Frame decoding + void processFrame(); + Common::Rect calcFrameCoords(uint32 frame); + + // Video + bool renderFrame(Common::Rect &rect); + + // Sound + void nextSoundSlice(bool hasNextCmd); + bool initialSoundSlice(bool hasNextCmd); + void emptySoundSlice(bool hasNextCmd); +}; + +class VMDDecoder : public CoktelDecoder { +public: + VMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); + ~VMDDecoder(); + + bool seek(int32 frame, int whence = SEEK_SET, bool restart = false); + + void setXY(uint16 x, uint16 y); + + bool getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height); + + bool hasEmbeddedFiles() const; + bool hasEmbeddedFile(const Common::String &fileName) const; + Common::MemoryReadStream *getEmbeddedFile(const Common::String &fileName) const; + + int32 getSubtitleIndex() const; + + + // VideoDecoder interface + + bool load(Common::SeekableReadStream *stream); + void close(); + + bool isVideoLoaded() const; + + Surface *decodeNextFrame(); + + PixelFormat getPixelFormat() const; + +private: + enum PartType { + kPartTypeSeparator = 0, + kPartTypeAudio = 1, + kPartTypeVideo = 2, + kPartTypeFile = 3, + kPartType4 = 4, + kPartTypeSubtitle = 5 + }; + + enum AudioFormat { + kAudioFormat8bitRaw = 0, + kAudioFormat16bitDPCM = 1, + kAudioFormat16bitADPCM = 2 + }; + + struct File { + Common::String name; + + uint32 offset; + uint32 size; + uint32 realSize; + + File(); + }; + + struct Part { + PartType type; + byte field_1; + byte field_E; + uint32 size; + int16 left; + int16 top; + int16 right; + int16 bottom; + uint16 id; + byte flags; + + Part(); + }; + + struct Frame { + uint32 offset; + Part *parts; + + Frame(); + ~Frame(); + }; + + // Tables for the audio decompressors + static const uint16 _tableDPCM[128]; + static const int32 _tableADPCM[]; + static const int32 _tableADPCMStep[]; + + Common::SeekableReadStream *_stream; + + byte _version; + uint32 _flags; + + uint32 _frameInfoOffset; + uint16 _partsPerFrame; + Frame *_frames; + + Common::Array<File> _files; + + // Sound properties + uint16 _soundFlags; + int16 _soundFreq; + int16 _soundSliceSize; + int16 _soundSlicesCount; + byte _soundBytesPerSample; + byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) + uint32 _soundHeaderSize; + uint32 _soundDataSize; + AudioFormat _audioFormat; + + // Video properties + bool _hasVideo; + uint32 _videoCodec; + byte _blitMode; + byte _bytesPerPixel; + + uint32 _firstFramePos; ///< Position of the first frame's data within the stream. + + // Buffer for raw frame data + byte *_frameData; + uint32 _frameDataSize; + uint32 _frameDataLen; + + // Buffer for processed frame data + byte *_videoBuffer; + uint32 _videoBufferSize; + + bool _externalCodec; + Codec *_codec; + + int32 _subtitle; + + // Loading helper functions + bool assessVideoProperties(); + bool assessAudioProperties(); + bool readFrameTable(int &numFiles); + bool readFiles(); + + // Frame decoding + void processFrame(); + + // Video + bool renderFrame(Common::Rect &rect); + + // Sound + void emptySoundSlice (uint32 size); + void filledSoundSlice (uint32 size); + void filledSoundSlices(uint32 size, uint32 mask); + + uint8 evaluateMask(uint32 mask, bool *fillInfo, uint8 &max); + + // Generating sound slices + byte *soundEmpty (uint32 &size); + byte *sound8bitRaw (uint32 &size); + byte *sound16bitDPCM (uint32 &size); + byte *sound16bitADPCM(uint32 &size); + + // Sound decompression + byte *deDPCM (const byte *data, uint32 &size, int32 init[2]); + byte *deADPCM(const byte *data, uint32 &size, int32 init, int32 index); + + bool getPartCoords(int16 frame, PartType type, int16 &x, int16 &y, int16 &width, int16 &height); +}; + +} // End of namespace Graphics + +#endif // GRAPHICS_VIDEO_COKTELDECODER_H + +#endif // Engine and dynamic plugins guard diff --git a/graphics/video/coktelvideo/coktelvideo.cpp b/graphics/video/coktelvideo/coktelvideo.cpp deleted file mode 100644 index 9ee9fd68d0..0000000000 --- a/graphics/video/coktelvideo/coktelvideo.cpp +++ /dev/null @@ -1,2657 +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/coktelvideo/coktelvideo.h" - -#ifdef GRAPHICS_VIDEO_COKTELVIDEO_H - -#include "common/endian.h" -#include "common/system.h" - -#include "graphics/dither.h" -#include "graphics/video/coktelvideo/indeo3.h" - -#include "sound/audiostream.h" -#include "sound/decoders/raw.h" - -namespace Graphics { - -PreImd::PreImd() { - zeroData(); - - _forcedWidth = 0; - _forcedHeight = 0; -} - -PreImd::PreImd(int16 width, int16 height) { - zeroData(); - - _forcedWidth = width; - _forcedHeight = height; -} - -PreImd::~PreImd() { - deleteData(); - zeroData(); -} - -uint32 PreImd::getFeatures() const { - return _features; -} - -uint16 PreImd::getFlags() const { - return _flags; -} - -int16 PreImd::getX() const { - return _x; -} - -int16 PreImd::getY() const { - return _y; -} - -int16 PreImd::getWidth() const { - return _width; -} - -int16 PreImd::getHeight() const { - return _height; -} - -uint16 PreImd::getFramesCount() const { - return _framesCount; -} - -uint16 PreImd::getCurrentFrame() const { - return _curFrame; -} - -void PreImd::setFrameRate(int16 frameRate) { - if (frameRate == 0) - frameRate = 1; - - _frameRate = frameRate; - _frameLength = 1000 / _frameRate; -} - -int16 PreImd::getFrameRate() const { - return 0; -} - -uint32 PreImd::getSyncLag() const { - return 0; -} - -const byte *PreImd::getPalette() const { - return _palette; -} - -bool PreImd::getFrameCoords(int16 frame, - int16 &x, int16 &y, int16 &width, int16 &height) { - - return false; -} - -bool PreImd::hasExtraData() const { - return false; -} - -bool PreImd::hasExtraData(const char *fileName) const { - return false; -} - -Common::MemoryReadStream *PreImd::getExtraData(const char *fileName) { - return 0; -} - -bool PreImd::load(Common::SeekableReadStream *stream) { - // Since PreIMDs don't have any width and height values stored, - // we need them to be specified in the constructor - assert((_forcedWidth > 0) && (_forcedHeight > 0)); - - unload(); - - _stream = stream; - - _stream->seek(0); - - _framesCount = _stream->readUint16LE(); - - _width = _forcedWidth; - _height = _forcedHeight; - - _vidBufferSize = _width * _height; - _vidBuffer = new byte[_vidBufferSize]; - memset(_vidBuffer, 0, _vidBufferSize); - - return true; -} - -void PreImd::unload() { - clear(); -} - -void PreImd::setXY(int16 x, int16 y) { - _x = x; - _y = y; -} - -void PreImd::setVideoMemory(byte *vidMem, uint16 width, uint16 height) { - deleteVidMem(); - - _hasOwnVidMem = false; - _vidMem = vidMem; - _vidMemWidth = width; - _vidMemHeight = height; -} - -void PreImd::setVideoMemory() { - deleteVidMem(); - - if ((_width > 0) && (_height > 0)) { - setXY(0, 0); - _hasOwnVidMem = true; - _vidMem = new byte[_width * _height]; - _vidMemWidth = _width; - _vidMemHeight = _height; - - memset(_vidMem, 0, _width * _height); - } -} - -void PreImd::setDoubleMode(bool doubleMode) { -} - -void PreImd::enableSound(Audio::Mixer &mixer) { -} - -void PreImd::disableSound() { -} - -bool PreImd::isSoundPlaying() const { - return false; -} - -void PreImd::seekFrame(int32 frame, int16 whence, bool restart) { - if (!_stream) - // Nothing to do - return; - - // Find the frame to which to seek - if (whence == SEEK_CUR) - frame += _curFrame; - else if (whence == SEEK_END) - frame = _framesCount - frame - 1; - else if (whence != SEEK_SET) - return; - - if ((frame < 0) || (frame >= _framesCount) || (frame == _curFrame)) - // Nothing to do - return; - - // Run through the frames - _curFrame = 0; - _stream->seek(2); - while (_curFrame != frame) { - uint16 frameSize = _stream->readUint16LE(); - - _stream->skip(frameSize + 2); - - _curFrame++; - } -} - -CoktelVideo::State PreImd::nextFrame() { - return processFrame(_curFrame); -} - -uint32 PreImd::getFrameWaitTime() { - return _frameLength; -} - -void PreImd::waitEndFrame() { - uint32 waitTime = getFrameWaitTime(); - if (waitTime > 0) - g_system->delayMillis(waitTime); -} - -void PreImd::copyCurrentFrame(byte *dest, - uint16 left, uint16 top, uint16 width, uint16 height, - uint16 x, uint16 y, uint16 pitch, int16 transp) { -} - -void PreImd::zeroVidMem() { - _hasOwnVidMem = false; - _vidMem = 0; - _vidMemWidth = 0; - _vidMemHeight = 0; -} - -void PreImd::deleteVidMem() { - if (_hasOwnVidMem) - delete[] _vidMem; - - zeroVidMem(); -} - -void PreImd::zeroData() { - _stream = 0; - - _features = 0; - _flags = 0; - - _x = 0; - _y = 0; - _width = 0; - _height = 0; - - _framesCount = 0; - _curFrame = 0; - _frameRate = 12; - _frameLength = 1000 / _frameRate; - - memset(_palette, 0, 768); - - zeroVidMem(); - - _vidBufferSize = 0; - _vidBuffer = 0; -} - -void PreImd::deleteData() { - deleteVidMem(); - - delete[] _vidBuffer; -} - -void PreImd::clear() { - deleteData(); - zeroData(); -} - -CoktelVideo::State PreImd::processFrame(uint16 frame) { - assert((_width > 0) && (_height > 0)); - - State state; - - if (!_stream || (frame >= _framesCount)) { - state.flags = kStateBreak; - return state; - } - - if (frame != _curFrame) { - state.flags |= kStateSeeked; - seekFrame(frame); - } - - if (!_vidMem) - setVideoMemory(); - - uint16 frameSize = _stream->readUint16LE(); - - uint32 nextFramePos = _stream->pos() + frameSize + 2; - - byte cmd; - - cmd = _stream->readByte(); - frameSize--; - - bool hasPalette = false; - if (cmd == 0) { - // Palette. Ignored by Fascination, though - - hasPalette = true; - - _stream->read(_palette, 768); - - frameSize -= 769; - - cmd = _stream->readByte(); - } - - if (cmd != 2) { - // Partial frame data - - uint32 fSize = frameSize; - uint32 vidSize = _vidBufferSize; - byte *vidBuffer = _vidBuffer; - - while ((fSize > 0) && (vidSize > 0)) { - uint32 n = _stream->readByte(); - fSize--; - - if ((n & 0x80) != 0) { - // Data - - n = MIN<uint32>((n & 0x7F) + 1, MIN(fSize, vidSize)); - - _stream->read(vidBuffer, n); - - vidBuffer += n; - vidSize -= n; - fSize -= n; - - } else { - // Skip - - n = MIN<uint32>(n + 1, vidSize); - - vidBuffer += n; - vidSize -= n; - } - } - - } else { - // Full direct frame - - uint32 vidSize = MIN<uint32>(_vidBufferSize, frameSize); - - _stream->read(_vidBuffer, vidSize); - } - - renderFrame(); - - _stream->seek(nextFramePos); - - _curFrame++; - - // Complete frame needs to be updated - state.left = _x; - state.top = _y; - state.right = _x + _width - 1; - state.bottom = _y + _height - 1; - - return state; -} - -// Simple blit -void PreImd::renderFrame() { - assert(_vidMem); - - uint16 w = MIN<uint16>(_vidMemWidth , _width); - uint16 h = MIN<uint16>(_vidMemHeight, _height); - - const byte *src = _vidBuffer; - byte *dst = _vidMem + (_y * _vidMemWidth) + _x; - - uint32 frameDataSize = _vidBufferSize; - - while (h-- > 0) { - uint32 n = MIN<uint32>(w, frameDataSize); - - memcpy(dst, src, n); - - src += _width; - dst += _vidMemWidth; - - frameDataSize -= n; - } -} - - -Imd::Imd() { - zeroData(); -} - -Imd::~Imd() { - deleteData(); - zeroData(); -} - -void Imd::setFrameRate(int16 frameRate) { - if (frameRate == 0) - frameRate = 1; - - _frameRate = frameRate; - _frameLength = 1000 / _frameRate; -} - -int16 Imd::getFrameRate() const { - if (!_hasSound) - return _frameRate; - - return 1000 / (_soundSliceLength >> 16); -} - -uint32 Imd::getSyncLag() const { - return _skipFrames; -} - -bool Imd::loadCoordinates() { - // Standard coordinates - if (_version >= 3) { - _stdX = _stream->readUint16LE(); - if (_stdX > 1) { - warning("IMD: More than one standard coordinate quad found (%d)", _stdX); - return false; - } - if (_stdX != 0) { - _stdX = _stream->readSint16LE(); - _stdY = _stream->readSint16LE(); - _stdWidth = _stream->readSint16LE(); - _stdHeight = _stream->readSint16LE(); - _features |= kFeaturesStdCoords; - } else - _stdX = -1; - } else - _stdX = -1; - - return true; -} - -bool Imd::loadFrameTableOffsets(uint32 &framesPosPos, uint32 &framesCoordsPos) { - framesPosPos = 0; - framesCoordsPos = 0; - - // Frame positions - if (_version >= 4) { - framesPosPos = _stream->readUint32LE(); - if (framesPosPos != 0) { - _framesPos = new uint32[_framesCount]; - assert(_framesPos); - _features |= kFeaturesFramesPos; - } - } - - // Frame coordinates - if (_features & kFeaturesFrameCoords) - framesCoordsPos = _stream->readUint32LE(); - - return true; -} - -bool Imd::assessVideoProperties() { - // Sizes of the frame data and extra video buffer - if (_features & kFeaturesDataSize) { - _frameDataSize = _stream->readUint16LE(); - if (_frameDataSize == 0) { - _frameDataSize = _stream->readUint32LE(); - _vidBufferSize = _stream->readUint32LE(); - } else - _vidBufferSize = _stream->readUint16LE(); - } else { - _frameDataSize = _width * _height + 500; - if (!(_flags & 0x100) || (_flags & 0x1000)) - _vidBufferSize = _frameDataSize; - } - - // Allocating working memory - _frameData = new byte[_frameDataSize + 500]; - assert(_frameData); - memset(_frameData, 0, _frameDataSize + 500); - _vidBuffer = new byte[_vidBufferSize + 500]; - assert(_vidBuffer); - memset(_vidBuffer, 0, _vidBufferSize + 500); - - return true; -} - -bool Imd::assessAudioProperties() { - if (_features & kFeaturesSound) { - _soundFreq = _stream->readSint16LE(); - _soundSliceSize = _stream->readSint16LE(); - _soundSlicesCount = _stream->readSint16LE(); - - if (_soundFreq < 0) - _soundFreq = -_soundFreq; - - if (_soundSlicesCount < 0) - _soundSlicesCount = -_soundSlicesCount - 1; - - if (_soundSlicesCount > 40) { - warning("Imd::load(): More than 40 sound slices found (%d)", _soundSlicesCount); - return false; - } - - _soundSliceLength = (uint32) (((double) (1000 << 16)) / - ((double) _soundFreq / (double) _soundSliceSize)); - _frameLength = _soundSliceLength >> 16; - - _soundStage = 1; - _hasSound = true; - - _audioStream = Audio::makeQueuingAudioStream(_soundFreq, false); - } else - _frameLength = 1000 / _frameRate; - - return true; -} - -bool Imd::loadFrameTables(uint32 framesPosPos, uint32 framesCoordsPos) { - // Positions table - if (_framesPos) { - _stream->seek(framesPosPos, SEEK_SET); - for (int i = 0; i < _framesCount; i++) - _framesPos[i] = _stream->readUint32LE(); - } - - // Coordinates table - if (_features & kFeaturesFrameCoords) { - _stream->seek(framesCoordsPos, SEEK_SET); - _frameCoords = new Coord[_framesCount]; - assert(_frameCoords); - for (int i = 0; i < _framesCount; i++) { - _frameCoords[i].left = _stream->readSint16LE(); - _frameCoords[i].top = _stream->readSint16LE(); - _frameCoords[i].right = _stream->readSint16LE(); - _frameCoords[i].bottom = _stream->readSint16LE(); - } - } - - return true; -} - -bool Imd::load(Common::SeekableReadStream *stream) { - unload(); - - _stream = stream; - - uint16 handle; - - handle = _stream->readUint16LE(); - _version = _stream->readByte(); - - // Version checking - if ((handle != 0) || (_version < 2)) { - warning("Imd::load(): Version incorrect (%d,%X)", handle, _version); - unload(); - return false; - } - - // Rest header - _features = _stream->readByte(); - _framesCount = _stream->readUint16LE(); - _x = _stream->readSint16LE(); - _y = _stream->readSint16LE(); - _width = _stream->readSint16LE(); - _height = _stream->readSint16LE(); - _flags = _stream->readUint16LE(); - _firstFramePos = _stream->readUint16LE(); - - // IMDs always have video - _features |= kFeaturesVideo; - // IMDs always have palettes - _features |= kFeaturesPalette; - - // Palette - _stream->read((byte *) _palette, 768); - - if (!loadCoordinates()) { - unload(); - return false; - } - - uint32 framesPosPos, frameCoordsPos; - if (!loadFrameTableOffsets(framesPosPos, frameCoordsPos)) { - unload(); - return false; - } - - if (!assessAudioProperties()) { - unload(); - return false; - } - - if (!assessVideoProperties()) { - unload(); - return false; - } - - if (!loadFrameTables(framesPosPos, frameCoordsPos)) { - unload(); - return false; - } - - // Seek to the first frame - _stream->seek(_firstFramePos, SEEK_SET); - - return true; -} - -void Imd::unload() { - clear(); -} - -void Imd::setXY(int16 x, int16 y) { - // Adjusting the standard coordinates - if (_stdX != -1) { - if (x >= 0) - _stdX = _stdX - _x + x; - if (y >= 0) - _stdY = _stdY - _y + y; - } - - // Going through the coordinate table as well - if (_frameCoords) { - for (int i = 0; i < _framesCount; i++) { - if (_frameCoords[i].left != -1) { - if (x >= 0) { - _frameCoords[i].left = _frameCoords[i].left - _x + x; - _frameCoords[i].right = _frameCoords[i].right - _x + x; - } - if (y >= 0) { - _frameCoords[i].top = _frameCoords[i].top - _y + y; - _frameCoords[i].bottom = _frameCoords[i].bottom - _y + y; - } - } - } - } - - if (x >= 0) - _x = x; - if (y >= 0) - _y = y; -} - -void Imd::enableSound(Audio::Mixer &mixer) { - // Sanity check - if (mixer.getOutputRate() == 0) - return; - - // Only possible on the first frame - if (_curFrame > 0) - return; - - _mixer = &mixer; - _soundEnabled = true; -} - -void Imd::disableSound() { - if (_audioStream) { - - if (_soundStage == 2) { - _audioStream->finish(); - _mixer->stopHandle(_audioHandle); - } else - delete _audioStream; - - _audioStream = 0; - _soundStage = 0; - } - _soundEnabled = false; - _mixer = 0; -} - -bool Imd::isSoundPlaying() const { - if (_audioStream && _mixer && _mixer->isSoundHandleActive(_audioHandle)) - return true; - - return false; -} - -void Imd::seekFrame(int32 frame, int16 whence, bool restart) { - if (!_stream) - // Nothing to do - return; - - // Find the frame to which to seek - if (whence == SEEK_CUR) - frame += _curFrame; - else if (whence == SEEK_END) - frame = _framesCount - frame - 1; - else if (whence != SEEK_SET) - return; - - if ((frame < 0) || (frame >= _framesCount) || (frame == _curFrame)) - // Nothing to do - return; - - // Try every possible way to find a file offset to that frame - uint32 framePos = 0; - if (frame == 0) { - framePos = _firstFramePos; - } else if (frame == 1) { - framePos = _firstFramePos; - _stream->seek(framePos, SEEK_SET); - framePos += _stream->readUint16LE() + 4; - } else if (_framesPos) { - framePos = _framesPos[frame]; - } else if (restart && (_soundStage == 0)) { - for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) - processFrame(i); - return; -//FIXME: This workaround is needed for Bargon Attack intro, which was broken by a fix concerning Ween in r42995. - } else if (_soundStage == 0) { - warning("Imd::seekFrame(): Avoiding \"Frame %d is not directly accessible\"", frame); - _curFrame = frame; -//End of fixme - } else - error("Imd::seekFrame(): Frame %d is not directly accessible", frame); - - // Seek - _stream->seek(framePos); - _curFrame = frame; -} - -CoktelVideo::State Imd::nextFrame() { - return processFrame(_curFrame); -} - -uint32 Imd::getFrameWaitTime() { - if (_soundEnabled && _hasSound) {; - if (_soundStage != 2) - return 0; - - if (_skipFrames == 0) { - int32 waitTime = (int16) (((_curFrame * _soundSliceLength) - - (_mixer->getSoundElapsedTime(_audioHandle) << 16)) >> 16); - - if (waitTime < 0) { - _skipFrames = -waitTime / (_soundSliceLength >> 16); - warning("Video A/V sync broken, skipping %d frame(s)", _skipFrames + 1); - } else if (waitTime > 0) - return waitTime; - - } else - _skipFrames--; - } else - return _frameLength; - - return 0; -} - -void Imd::copyCurrentFrame(byte *dest, - uint16 left, uint16 top, uint16 width, uint16 height, - uint16 x, uint16 y, uint16 pitch, int16 transp) { - - if (!_vidMem) - return; - - if (((left + width) > _width) || ((top + height) > _height)) - return; - - dest += pitch * y; - byte *vidMem = _vidMem + _width * top; - - if (transp < 0) { - // No transparency - if ((x > 0) || (left > 0) || (pitch != _width) || (width != _width)) { - // Copy row-by-row - for (int i = 0; i < height; i++) { - byte *d = dest + x; - byte *s = vidMem + left; - - memcpy(d, s, width); - - dest += pitch; - vidMem += _width; - } - } else - // Dimensions fit, copy everything at once - memcpy(dest, vidMem, width * height); - - return; - } - - for (int i = 0; i < height; i++) { - byte *d = dest + x; - byte *s = vidMem + left; - - for (int j = 0; j < width; j++) { - if (*s != transp) - *d = *s; - - s++; - d++; - } - - dest += pitch; - vidMem += _width; - } - -} - -void Imd::zeroData() { - PreImd::zeroData(); - - _version = 0; - - _stdX = 0; - _stdY = 0; - _stdWidth = 0; - _stdHeight = 0; - - _framesPos = 0; - _firstFramePos = 0; - _frameCoords = 0; - - _frameDataSize = 0; - _frameData = 0; - _frameDataLen = 0; - - _hasSound = false; - _soundEnabled = false; - _soundStage = 0; - _skipFrames = 0; - - _soundFlags = 0; - _soundFreq = 0; - _soundSliceSize = 0; - _soundSlicesCount = 0; - _soundSliceLength = 0; - _audioStream = 0; - - _lastFrameTime = 0; -} - -void Imd::deleteData() { - PreImd::deleteData(); - - delete[] _framesPos; - delete[] _frameCoords; - delete[] _frameData; - - disableSound(); -} - -void Imd::clear() { - deleteData(); - zeroData(); -} - -void Imd::nextSoundSlice(bool hasNextCmd) { - if (hasNextCmd || !_soundEnabled) { - _stream->seek(_soundSliceSize, SEEK_CUR); - return; - } - - byte *soundBuf = (byte *)malloc(_soundSliceSize); - - _stream->read(soundBuf, _soundSliceSize); - unsignedToSigned(soundBuf, _soundSliceSize); - - _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); -} - -bool Imd::initialSoundSlice(bool hasNextCmd) { - int dataLength = _soundSliceSize * _soundSlicesCount; - - if (hasNextCmd || !_soundEnabled) { - _stream->seek(dataLength, SEEK_CUR); - return false; - } - - byte *soundBuf = (byte *)malloc(dataLength); - - _stream->read(soundBuf, dataLength); - unsignedToSigned(soundBuf, dataLength); - - _audioStream->queueBuffer(soundBuf, dataLength, DisposeAfterUse::YES, 0); - - return _soundStage == 1; -} - -void Imd::emptySoundSlice(bool hasNextCmd) { - if (hasNextCmd || !_soundEnabled) - return; - - byte *soundBuf = (byte *)malloc(_soundSliceSize); - - memset(soundBuf, 0, _soundSliceSize); - - _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); -} - -void Imd::videoData(uint32 size, State &state) { - _stream->read(_frameData, size); - _frameDataLen = size; - -/* - if (_vidMemWidth <= state.right) { - state.left = 0; - state.right -= state.left; - } - if (_vidMemWidth <= state.right) - state.right = _vidMemWidth - 1; - if (_vidMemHeight <= state.bottom) { - state.top = 0; - state.bottom -= state.top; - } - if (_vidMemHeight <= state.bottom) - state.bottom = _vidMemHeight -1; -*/ - - state.flags |= renderFrame(state.left, state.top, state.right, state.bottom); - state.flags |= _frameData[0]; -} - -void Imd::calcFrameCoords(uint16 frame, State &state) { - if (_stdX != -1) { - state.left = _stdX; - state.top = _stdY; - state.right = _stdWidth + state.left - 1; - state.bottom = _stdHeight + state.top - 1; - state.flags |= kStateStdCoords; - } - if (_frameCoords && - (_frameCoords[frame].left != -1)) { - state.left = _frameCoords[frame].left; - state.top = _frameCoords[frame].top; - state.right = _frameCoords[frame].right; - state.bottom = _frameCoords[frame].bottom; - state.flags |= kStateFrameCoords; - } -} - -CoktelVideo::State Imd::processFrame(uint16 frame) { - State state; - uint32 cmd = 0; - bool hasNextCmd = false; - bool startSound = false; - - if (!_stream || (frame >= _framesCount)) { - state.flags = kStateBreak; - return state; - } - - if (frame != _curFrame) { - state.flags |= kStateSeeked; - seekFrame(frame); - } - - if (!_vidMem) - setVideoMemory(); - - state.left = _x; - state.top = _y; - state.right = _width + state.left - 1; - state.bottom = _height + state.top - 1; - - do { - if (frame != 0) - calcFrameCoords(frame, state); - - cmd = _stream->readUint16LE(); - - if ((cmd & kCommandBreakMask) == kCommandBreak) { - // Flow control - - if (cmd == kCommandBreak) { - _stream->seek(2, SEEK_CUR); - cmd = _stream->readUint16LE(); - } - - // Break - if (cmd == kCommandBreakSkip0) { - state.flags = kStateBreak; - continue; - } else if (cmd == kCommandBreakSkip16) { - cmd = _stream->readUint16LE(); - _stream->seek(cmd, SEEK_CUR); - state.flags = kStateBreak; - continue; - } else if (cmd == kCommandBreakSkip32) { - cmd = _stream->readUint32LE(); - _stream->seek(cmd, SEEK_CUR); - state.flags = kStateBreak; - continue; - } - } - - // Audio - if (_soundStage != 0) { - if (cmd == kCommandNextSound) { - - nextSoundSlice(hasNextCmd); - cmd = _stream->readUint16LE(); - - } else if (cmd == kCommandStartSound) { - - startSound = initialSoundSlice(hasNextCmd); - cmd = _stream->readUint16LE(); - - } else - emptySoundSlice(hasNextCmd); - } - - // Set palette - if (cmd == kCommandPalette) { - _stream->seek(2, SEEK_CUR); - state.flags |= kStatePalette; - - _stream->read(_palette, 768); - cmd = _stream->readUint16LE(); - } - - hasNextCmd = false; - - if (cmd == kCommandJump) { - // Jump to frame - - frame = _stream->readSint16LE(); - if (_framesPos) { - _curFrame = frame; - _stream->seek(_framesPos[frame], SEEK_SET); - - hasNextCmd = true; - state.flags |= kStateJump; - } - - } else if (cmd == kCommandVideoData) { - uint32 size = _stream->readUint32LE() + 2; - - videoData(size, state); - - state.flags |= 1; - - } else if (cmd != 0) { - uint32 size = cmd + 2; - - videoData(size, state); - - } else - state.flags |= kStateNoVideoData; - - } while (hasNextCmd); - - if (startSound && _soundEnabled) { - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream); - _skipFrames = 0; - _soundStage = 2; - } - - _curFrame++; - if ((_curFrame == _framesCount) && (_soundStage == 2)) { - _audioStream->finish(); - _mixer->stopHandle(_audioHandle); - _audioStream = 0; - _soundStage = 0; - } - - _lastFrameTime = g_system->getMillis(); - return state; -} - -// A whole, completely filled block -void Imd::renderBlockWhole(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch) { - - for (int i = 0; i < height; i++) { - memcpy(dest, src, width); - - src += srcPitch; - dest += destPitch; - } -} - -// A quarter-wide whole, completely filled block -void Imd::renderBlockWhole4X(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch) { - - for (int i = 0; i < height; i++) { - byte *destRow = dest; - const byte *srcRow = src; - - for (int j = 0; j < width; j += 4, destRow += 4, srcRow++) - memset(destRow, *srcRow, 4); - - src += srcPitch; - dest += destPitch; - } -} - -// A half-high whole, completely filled block -void Imd::renderBlockWhole2Y(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch) { - - while (height > 1) { - memcpy(dest , src, width); - memcpy(dest + destPitch, src, width); - - height -= 2; - dest += 2 * destPitch; - src += srcPitch; - } - - if (height == 1) - memcpy(dest, src, width); -} - -// A sparse block -void Imd::renderBlockSparse(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch) { - - for (int i = 0; i < height; i++) { - byte *destRow = dest; - int16 pixWritten = 0; - - while (pixWritten < srcPitch) { - int16 pixCount = *src++; - - if (pixCount & 0x80) { // Data - int16 copyCount; - - pixCount = MIN((pixCount & 0x7F) + 1, srcPitch - pixWritten); - copyCount = MAX<int16>(0, MIN<int16>(pixCount, width - pixWritten)); - memcpy(destRow, src, copyCount); - - pixWritten += pixCount; - destRow += pixCount; - src += pixCount; - } else { // "Hole" - pixWritten += pixCount + 1; - destRow += pixCount + 1; - } - - } - - dest += destPitch; - } -} - -// A half-high sparse block -void Imd::renderBlockSparse2Y(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch) { - - for (int i = 0; i < height; i += 2) { - byte *destRow = dest; - int16 pixWritten = 0; - - while (pixWritten < srcPitch) { - int16 pixCount = *src++; - - if (pixCount & 0x80) { // Data - int16 copyCount; - - pixCount = MIN((pixCount & 0x7F) + 1, srcPitch - pixWritten); - copyCount = MAX<int16>(0, MIN<int16>(pixCount, width - pixWritten)); - memcpy(destRow , src, pixCount); - memcpy(destRow + destPitch, src, pixCount); - - pixWritten += pixCount; - destRow += pixCount; - src += pixCount; - } else { // "Hole" - pixWritten += pixCount + 1; - destRow += pixCount + 1; - } - - } - - dest += destPitch; - } -} - -uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { - if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0)) - return 0; - - uint32 retVal = 0; - - int16 width = right - left + 1; - int16 height = bottom - top + 1; - int16 sW = _vidMemWidth; - int16 sH = _vidMemHeight; - - byte *dataPtr = _frameData; - byte *imdVidMem = _vidMem + sW * top + left; - byte *srcPtr; - - uint8 type = *dataPtr++; - - if (type & 0x10) { // Palette data - // One byte index - int index = *dataPtr++; - // 16 entries with each 3 bytes (RGB) - memcpy(_palette + index * 3, dataPtr, MIN((255 - index) * 3, 48)); - - retVal = kStatePalette; - dataPtr += 48; - type ^= 0x10; - } - - srcPtr = dataPtr; - - if (type & 0x80) { - // Frame data is compressed - - srcPtr = _vidBuffer; - type &= 0x7F; - if ((type == 2) && (width == sW)) { - // Directly uncompress onto the video surface - deLZ77(imdVidMem, dataPtr); - return retVal; - } else - deLZ77(srcPtr, dataPtr); - } - - int16 drawWidth = MAX<int16>(0, MIN<int16>(width , sW - left)); - int16 drawHeight = MAX<int16>(0, MIN<int16>(height, sH - top )); - - // Evaluate the block type - if (type == 0x01) - renderBlockSparse (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); - else if (type == 0x02) - renderBlockWhole (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); - else if (type == 0x42) - renderBlockWhole4X (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); - else if ((type & 0x0F) == 0x02) - renderBlockWhole2Y (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); - else - renderBlockSparse2Y(imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); - - return retVal; -} - -void Imd::deLZ77(byte *dest, byte *src) { - int i; - byte buf[4370]; - uint16 chunkLength; - uint32 frameLength; - uint16 bufPos1; - uint16 bufPos2; - uint16 tmp; - uint8 chunkBitField; - uint8 chunkCount; - bool mode; - - frameLength = READ_LE_UINT32(src); - src += 4; - - if ((READ_LE_UINT16(src) == 0x1234) && (READ_LE_UINT16(src + 2) == 0x5678)) { - src += 4; - bufPos1 = 273; - mode = 1; // 123Ch (cmp al, 12h) - } else { - bufPos1 = 4078; - mode = 0; // 275h (jnz +2) - } - - memset(buf, 32, bufPos1); - chunkCount = 1; - chunkBitField = 0; - - while (frameLength > 0) { - chunkCount--; - if (chunkCount == 0) { - tmp = *src++; - chunkCount = 8; - chunkBitField = tmp; - } - if (chunkBitField % 2) { - chunkBitField >>= 1; - buf[bufPos1] = *src; - *dest++ = *src++; - bufPos1 = (bufPos1 + 1) % 4096; - frameLength--; - continue; - } - chunkBitField >>= 1; - - tmp = READ_LE_UINT16(src); - src += 2; - chunkLength = ((tmp & 0xF00) >> 8) + 3; - - if ((mode && ((chunkLength & 0xFF) == 0x12)) || - (!mode && (chunkLength == 0))) - chunkLength = *src++ + 0x12; - - bufPos2 = (tmp & 0xFF) + ((tmp >> 4) & 0x0F00); - if (((tmp + chunkLength) >= 4096) || - ((chunkLength + bufPos1) >= 4096)) { - - for (i = 0; i < chunkLength; i++, dest++) { - *dest = buf[bufPos2]; - buf[bufPos1] = buf[bufPos2]; - bufPos1 = (bufPos1 + 1) % 4096; - bufPos2 = (bufPos2 + 1) % 4096; - } - - } else if (((tmp + chunkLength) < bufPos1) || - ((chunkLength + bufPos1) < bufPos2)) { - - memcpy(dest, buf + bufPos2, chunkLength); - memmove(buf + bufPos1, buf + bufPos2, chunkLength); - - dest += chunkLength; - bufPos1 += chunkLength; - bufPos2 += chunkLength; - - } else { - - for (i = 0; i < chunkLength; i++, dest++, bufPos1++, bufPos2++) { - *dest = buf[bufPos2]; - buf[bufPos1] = buf[bufPos2]; - } - - } - frameLength -= chunkLength; - - } -} - -inline void Imd::unsignedToSigned(byte *buffer, int length) { - while (length-- > 0) *buffer++ ^= 0x80; -} - - -Vmd::ExtraData::ExtraData() { - memset(name, 0, 16); - - offset = 0; - size = 0; - realSize = 0; -} - - -Vmd::Part::Part() { - type = kPartTypeSeparator; - field_1 = 0; - field_E = 0; - size = 0; - left = 0; - top = 0; - right = 0; - bottom = 0; - id = 0; - flags = 0; -} - - -Vmd::Frame::Frame() { - parts = 0; - offset = 0; -} - -Vmd::Frame::~Frame() { - delete[] parts; -} - - -const uint16 Vmd::_tableDPCM[128] = { - 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, - 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, - 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, - 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, - 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280, - 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0, - 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, - 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, - 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0, - 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480, - 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700, - 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, - 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 -}; - -const int32 Vmd::_tableADPCM[] = { - 7, 8, 9, 10, 11, 12, 13, 14, - 16, 17, 19, 21, 23, 25, 28, 31, - 34, 37, 41, 45, 50, 55, 60, 66, - 73, 80, 88, 97, 107, 118, 130, 143, - 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, - 724, 796, 876, 963, 1060, 1166, 1282, 1411, - 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, - 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, - 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, - 32767, 0 -}; - -const int32 Vmd::_tableADPCMStep[] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 -}; - -Vmd::Vmd(Graphics::PaletteLUT *palLUT) : _palLUT(palLUT) { - zeroData(); -} - -Vmd::~Vmd() { - deleteData(); - zeroData(); -} - -bool Vmd::assessVideoProperties() { - if (_bytesPerPixel > 1) - _features |= kFeaturesFullColor; - else - _features |= kFeaturesPalette; - - if ((_version & 2) && !(_version & 8)) { - _externalCodec = true; - _frameDataSize = _vidBufferSize = 0; - } else - _externalCodec = false; - - if (_externalCodec) { - if (_videoCodec == MKID_BE('iv32')) { -#ifdef USE_INDEO3 - _features &= ~kFeaturesPalette; - _features |= kFeaturesFullColor; - _codecIndeo3 = new Indeo3(_width, _height, _palLUT); -#else - warning("Vmd::assessVideoProperties(): Indeo3 decoder not compiled in"); -#endif - } else { - char *fourcc = (char *) &_videoCodec; - - warning("Vmd::assessVideoProperties(): Unknow video codec FourCC \'%c%c%c%c\'", - fourcc[3], fourcc[2], fourcc[1], fourcc[0]); - return false; - } - } - - _preScaleX = 1; - _postScaleX = 1; - - if (_externalCodec) - _blitMode = 0; - else if (_bytesPerPixel == 1) - _blitMode = 0; - else if ((_bytesPerPixel == 2) || (_bytesPerPixel == 3)) { - int n = (_flags & 0x80) ? 2 : 3; - - _blitMode = n - 1; - - if (_bytesPerPixel == 2) { - _preScaleX = n; - _postScaleX = 1; - } else if (_bytesPerPixel == 3) { - _preScaleX = 1; - _postScaleX = n; - } - - _bytesPerPixel = n; - } - - _scaleExternalX = 1; - if (!_externalCodec && !(_flags & 0x1000)) - _scaleExternalX = _bytesPerPixel; - - if (_hasVideo) { - if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) - _frameDataSize = _width * _height + 1000; - if ((_vidBufferSize == 0) || (_vidBufferSize > 1048576)) - _vidBufferSize = _frameDataSize; - - _frameData = new byte[_frameDataSize]; - assert(_frameData); - memset(_frameData, 0, _frameDataSize); - - _vidBuffer = new byte[_vidBufferSize]; - assert(_vidBuffer); - memset(_vidBuffer, 0, _vidBufferSize); - - if (_blitMode > 0) { - _vidMemBuffer = new byte[_bytesPerPixel * (_width * _height + 1000)]; - memset(_vidMemBuffer, 0, _bytesPerPixel * (_width * _height + 1000)); - } - } - -#ifdef USE_INDEO3 - if (_externalCodec && _codecIndeo3) - _features |= kFeaturesSupportsDouble; -#endif - - return true; -} - -bool Vmd::assessAudioProperties() { - bool supportedFormat = true; - - _features |= kFeaturesSound; - - _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0); - - if (_soundSliceSize < 0) { - _soundBytesPerSample = 2; - _soundSliceSize = -_soundSliceSize; - - if (_soundFlags & 0x10) { - _audioFormat = kAudioFormat16bitADPCM; - _soundHeaderSize = 3; - _soundDataSize = _soundSliceSize >> 1; - - if (_soundStereo > 0) - supportedFormat = false; - - } else { - _audioFormat = kAudioFormat16bitDPCM; - _soundHeaderSize = 1; - _soundDataSize = _soundSliceSize; - - if (_soundStereo == 1) { - supportedFormat = false; - } else if (_soundStereo == 2) { - _soundDataSize = 2 * _soundDataSize + 2; - _soundHeaderSize = 4; - } - - } - } else { - _soundBytesPerSample = 1; - _audioFormat = kAudioFormat8bitDirect; - _soundHeaderSize = 0; - _soundDataSize = _soundSliceSize; - - if (_soundStereo > 0) - supportedFormat = false; - } - - if (!supportedFormat) { - warning("Vmd::assessAudioProperties(): Unsupported audio format: %d bits, encoding %d, stereo %d", - _soundBytesPerSample * 8, _audioFormat, _soundStereo); - return false; - } - - _soundSliceLength = (uint32) (((double) (1000 << 16)) / - ((double) _soundFreq / (double) _soundSliceSize)); - _frameLength = _soundSliceLength >> 16; - - _soundStage = 1; - - _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); - - return true; -} - -void Vmd::readFrameTable(int &numExtraData) { - numExtraData = 0; - - _stream->seek(_frameInfoOffset); - _frames = new Frame[_framesCount]; - for (uint16 i = 0; i < _framesCount; i++) { - _frames[i].parts = new Part[_partsPerFrame]; - _stream->skip(2); // Unknown - _frames[i].offset = _stream->readUint32LE(); - } - - for (uint16 i = 0; i < _framesCount; i++) { - bool separator = false; - - for (uint16 j = 0; j < _partsPerFrame; j++) { - - _frames[i].parts[j].type = (PartType) _stream->readByte(); - _frames[i].parts[j].field_1 = _stream->readByte(); - _frames[i].parts[j].size = _stream->readUint32LE(); - - if (_frames[i].parts[j].type == kPartTypeAudio) { - - _frames[i].parts[j].flags = _stream->readByte(); - _stream->skip(9); // Unknown - - } else if (_frames[i].parts[j].type == kPartTypeVideo) { - - _frames[i].parts[j].left = _stream->readUint16LE(); - _frames[i].parts[j].top = _stream->readUint16LE(); - _frames[i].parts[j].right = _stream->readUint16LE(); - _frames[i].parts[j].bottom = _stream->readUint16LE(); - _frames[i].parts[j].field_E = _stream->readByte(); - _frames[i].parts[j].flags = _stream->readByte(); - - } else if (_frames[i].parts[j].type == kPartTypeSpeech) { - _frames[i].parts[j].id = _stream->readUint16LE(); - // Speech text file name - _stream->skip(8); - } else if (_frames[i].parts[j].type == kPartTypeExtraData) { - if (!separator) - numExtraData++; - _stream->skip(10); - } else if (_frames[i].parts[j].type == kPartTypeSeparator) { - separator = true; - _stream->skip(10); - } else { - // Unknow type - _stream->skip(10); - } - - } - } -} - -void Vmd::readExtraData() { - uint32 ssize = _stream->size(); - for (uint16 i = 0; i < _framesCount; i++) { - _stream->seek(_frames[i].offset); - - for (uint16 j = 0; j < _partsPerFrame; j++) { - if (_frames[i].parts[j].type == kPartTypeSeparator) - break; - - if (_frames[i].parts[j].type == kPartTypeExtraData) { - ExtraData data; - - data.offset = _stream->pos() + 20; - data.size = _frames[i].parts[j].size; - data.realSize = _stream->readUint32LE(); - - _stream->read(data.name, 16); - data.name[15] = '\0'; - - _stream->skip(_frames[i].parts[j].size - 20); - - if ((((uint32) data.realSize) >= ssize) || (data.name[0] == 0)) - continue; - - _extraData.push_back(data); - - } else - _stream->skip(_frames[i].parts[j].size); - } - } -} - -bool Vmd::load(Common::SeekableReadStream *stream) { - unload(); - - _stream = stream; - - uint16 headerLength; - uint16 handle; - - headerLength = _stream->readUint16LE(); - handle = _stream->readUint16LE(); - _version = _stream->readUint16LE(); - - bool readPalette; - - // Version checking - if (headerLength == 50) { - // Newer version, used in Addy 5 upwards - warning("Vmd::load(): TODO: Addy 5 videos"); - readPalette = false; - } else if (headerLength == 814) { - // Old version - readPalette = true; - } else { - warning("Vmd::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version); - unload(); - return false; - } - - _framesCount = _stream->readUint16LE(); - - _x = _stream->readSint16LE(); - _y = _stream->readSint16LE(); - _width = _stream->readSint16LE(); - _height = _stream->readSint16LE(); - - if ((_width != 0) && (_height != 0)) { - - _hasVideo = true; - _features |= kFeaturesVideo; - - } else - _hasVideo = false; - - _bytesPerPixel = 1; - if (_version & 4) - _bytesPerPixel = handle + 1; - - if (_bytesPerPixel > 3) { - warning("Vmd::load(): Requested %d bytes per pixel (%d, %d, %d)", _bytesPerPixel, headerLength, handle, _version); - unload(); - return false; - } - - _flags = _stream->readUint16LE(); - - _partsPerFrame = _stream->readUint16LE(); - _firstFramePos = _stream->readUint32LE(); - - _videoCodec = _stream->readUint32BE(); - - if (readPalette) - _stream->read((byte *) _palette, 768); - - _frameDataSize = _stream->readUint32LE(); - _vidBufferSize = _stream->readUint32LE(); - - _doubleMode = false; - - if (_hasVideo) { - if (!assessVideoProperties()) { - unload(); - return false; - } - } - - _soundFreq = _stream->readSint16LE(); - _soundSliceSize = _stream->readSint16LE(); - _soundSlicesCount = _stream->readSint16LE(); - _soundFlags = _stream->readUint16LE(); - - _hasSound = (_soundFreq != 0); - - if (_hasSound) { - if (!assessAudioProperties()) { - unload(); - return false; - } - } else - _frameLength = 1000 / _frameRate; - - _frameInfoOffset = _stream->readUint32LE(); - - int numExtraData; - readFrameTable(numExtraData); - - _stream->seek(_firstFramePos); - - if (numExtraData == 0) - return true; - - _extraData.reserve(numExtraData); - readExtraData(); - - _stream->seek(_firstFramePos); - return true; -} - -void Vmd::unload() { - clear(); -} - -int16 Vmd::getWidth() const { - return preScaleX(_width); -} - -void Vmd::setXY(int16 x, int16 y) { - - x *= _scaleExternalX; - - for (int i = 0; i < _framesCount; i++) { - for (int j = 0; j < _partsPerFrame; j++) { - - if (_frames[i].parts[j].type == kPartTypeVideo) { - if (x >= 0) { - _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; - _frames[i].parts[j].right = _frames[i].parts[j].right - _x + x; - } - if (y >= 0) { - _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; - _frames[i].parts[j].bottom = _frames[i].parts[j].bottom - _y + y; - } - } - - } - } - - if (x >= 0) - _x = x; - if (y >= 0) - _y = y; -} - -void Vmd::setDoubleMode(bool doubleMode) { - if (_doubleMode == doubleMode) - return; - - if (_vidBuffer) { - delete[] _vidBuffer; - - if (doubleMode) - _vidBufferSize *= 4; - else - _vidBufferSize /= 4; - - _vidBuffer = new byte[_vidBufferSize]; - assert(_vidBuffer); - memset(_vidBuffer, 0, _vidBufferSize); - - } - -#ifdef USE_INDEO3 - if (_codecIndeo3) { - delete _codecIndeo3; - - _codecIndeo3 = new Indeo3(_width * (doubleMode ? 2 : 1), - _height * (doubleMode ? 2 : 1), _palLUT); - } -#endif - - _doubleMode = doubleMode; -} - -void Vmd::seekFrame(int32 frame, int16 whence, bool restart) { - if (!_stream) - // Nothing to do - return; - - // Find the frame to which to seek - if (whence == SEEK_CUR) - frame += _curFrame; - else if (whence == SEEK_END) - frame = _framesCount - frame - 1; - else if (whence != SEEK_SET) - return; - - if ((frame < 0) || (frame >= _framesCount)) - // Nothing to do - return; - - // Restart sound - if (_hasSound && (frame == 0) && (_soundStage == 0) && !_audioStream) { - _soundStage = 1; - _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); - } - - // Seek - _stream->seek(_frames[frame].offset); - _curFrame = frame; -} - -CoktelVideo::State Vmd::nextFrame() { - State state; - - state = processFrame(_curFrame); - _curFrame++; - return state; -} - -void Vmd::zeroData() { - Imd::zeroData(); - - _hasVideo = true; - _videoCodec = 0; - -#ifdef USE_INDEO3 - _codecIndeo3 = 0; -#endif - - _partsPerFrame = 0; - _frames = 0; - - _extraData.clear(); - - _soundBytesPerSample = 1; - _soundStereo = 0; - _soundHeaderSize = 0; - _soundDataSize = 0; - _audioFormat = kAudioFormat8bitDirect; - - _externalCodec = false; - _doubleMode = false; - _blitMode = 0; - _bytesPerPixel = 1; - _preScaleX = 1; - _postScaleX = 1; - _scaleExternalX = 1; - _vidMemBuffer = 0; -} - -void Vmd::deleteData() { - Imd::deleteData(); - -#ifdef USE_INDEO3 - delete _codecIndeo3; -#endif - delete[] _frames; - delete[] _vidMemBuffer; -} - -void Vmd::clear() { - deleteData(); - zeroData(); -} - -CoktelVideo::State Vmd::processFrame(uint16 frame) { - State state; - bool startSound = false; - - seekFrame(frame); - - state.flags |= kStateNoVideoData; - state.left = 0x7FFF; - state.top = 0x7FFF; - state.right = 0; - state.bottom = 0; - - if (!_vidMem) - setVideoMemory(); - - for (uint16 i = 0; (i < _partsPerFrame) && (frame < _framesCount); i++) { - uint32 pos = _stream->pos(); - - Part &part = _frames[frame].parts[i]; - - if (part.type == kPartTypeAudio) { - // Next sound slice data - if (part.flags == 1) { - - if (_soundEnabled) { - filledSoundSlice(part.size); - - if (_soundStage == 1) - startSound = true; - - } else - _stream->skip(part.size); - - // Initial sound data (all slices) - } else if (part.flags == 2) { - - if (_soundEnabled) { - uint32 mask = _stream->readUint32LE(); - filledSoundSlices(part.size - 4, mask); - - if (_soundStage == 1) - startSound = true; - - } else - _stream->skip(part.size); - - // Empty sound slice - } else if (part.flags == 3) { - - if (_soundEnabled) { - emptySoundSlice(_soundDataSize * _soundBytesPerSample); - - if (_soundStage == 1) - startSound = true; - } - - _stream->skip(part.size); - } else if (part.flags == 4) { - warning("Vmd::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size); - disableSound(); - _stream->skip(part.size); - } else { - warning("Vmd::processFrame(): Unknown sound type %d", part.flags); - _stream->skip(part.size); - } - - _stream->seek(pos + part.size); - - } else if ((part.type == kPartTypeVideo) && !_hasVideo) { - warning("Vmd::processFrame(): Header claims there's no video, but video found (%d)", part.size); - _stream->skip(part.size); - } else if ((part.type == kPartTypeVideo) && _hasVideo) { - state.flags &= ~kStateNoVideoData; - - uint32 size = part.size; - - // New palette - if (part.flags & 2) { - uint8 index = _stream->readByte(); - uint8 count = _stream->readByte(); - - _stream->read(_palette + index * 3, (count + 1) * 3); - _stream->skip((255 - count) * 3); - - state.flags |= kStatePalette; - - size -= (768 + 2); - } - - _stream->read(_frameData, size); - _frameDataLen = size; - - int16 l = part.left, t = part.top, r = part.right, b = part.bottom; - if (renderFrame(l, t, r, b)) { - if (!_externalCodec) { - l = preScaleX(l); - r = preScaleX(r); - } - // Rendering succeeded, merging areas - state.left = MIN(state.left, l); - state.top = MIN(state.top, t); - state.right = MAX(state.right, r); - state.bottom = MAX(state.bottom, b); - } - - } else if (part.type == kPartTypeSeparator) { - } else if (part.type == kPartTypeExtraData) { - _stream->skip(part.size); - } else if (part.type == kPartType4) { - // Unknown - _stream->skip(part.size); - } else if (part.type == kPartTypeSpeech) { - state.flags |= kStateSpeech; - state.speechId = part.id; - // Always triggers when speech starts - _stream->skip(part.size); - } else { - warning("Vmd::processFrame(): Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame); - } - } - - if (startSound && _soundEnabled) { - if (_hasSound && _audioStream) { - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream); - _skipFrames = 0; - _soundStage = 2; - } else - _soundStage = 0; - } - - if ((_curFrame == (_framesCount - 1)) && (_soundStage == 2)) { - _audioStream->finish(); - _mixer->stopHandle(_audioHandle); - _audioStream = 0; - _soundStage = 0; - } - - // If these are still 0x7FFF, no video data has been processed - if ((state.left == 0x7FFF) || (state.top == 0x7FFF)) - state.left = state.top = state.right = state.bottom = 0; - - _lastFrameTime = g_system->getMillis(); - return state; -} - -void Vmd::deRLE(byte *&destPtr, const byte *&srcPtr, int16 destLen, int16 srcLen) { - srcPtr++; - - if (srcLen & 1) { - byte data = *srcPtr++; - - if (destLen > 0) { - *destPtr++ = data; - destLen--; - } - } - - srcLen >>= 1; - - while (srcLen > 0) { - uint8 tmp = *srcPtr++; - if (tmp & 0x80) { // Verbatim copy - tmp &= 0x7F; - - int16 copyCount = MAX<int16>(0, MIN<int16>(destLen, tmp * 2)); - - memcpy(destPtr, srcPtr, copyCount); - - srcPtr += tmp * 2; - destPtr += copyCount; - destLen -= copyCount; - } else { // 2 bytes tmp times - for (int i = 0; (i < tmp) && (destLen > 0); i++) { - for (int j = 0; j < 2; j++) { - if (destLen <= 0) - break; - - *destPtr++ = srcPtr[j]; - destLen--; - } - } - srcPtr += 2; - } - srcLen -= tmp; - } -} - -// A run-length-encoded sparse block -void Vmd::renderBlockRLE(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch) { - - for (int i = 0; i < height; i++) { - byte *destRow = dest; - int16 pixWritten = 0; - - while (pixWritten < srcPitch) { - int16 pixCount = *src++; - - if (pixCount & 0x80) { - int16 copyCount; - - pixCount = (pixCount & 0x7F) + 1; - copyCount = MAX<int16>(0, MIN<int16>(pixCount, width - pixWritten)); - - if (*src != 0xFF) { // Normal copy - - memcpy(destRow, src, copyCount); - destRow += copyCount; - src += pixCount; - } else - deRLE(destRow, src, copyCount, pixCount); - - pixWritten += pixCount; - } else { // "Hole" - int16 copyCount = MAX<int16>(0, MIN<int16>(pixCount + 1, width - pixWritten)); - - destRow += copyCount; - pixWritten += pixCount + 1; - } - - } - - dest += destPitch; - } - -} - -uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { - if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0)) - return 0; - - int16 width = right - left + 1; - int16 height = bottom - top + 1; - int16 sW = _vidMemWidth; - int16 sH = _vidMemHeight; - - byte *dataPtr = _frameData; - byte *imdVidMem = _vidMem + sW * top + left; - byte *srcPtr; - - if ((left < 0) || (top < 0) || (right < 0) || (bottom < 0)) - return 1; - if ((width <= 0) || (height <= 0)) - return 1; - - uint8 type; - byte *dest = imdVidMem; - -#ifdef USE_INDEO3 - uint32 dataLen = _frameDataLen; - - if (Indeo3::isIndeo3(dataPtr, dataLen)) { - if (!_codecIndeo3) - return 0; - - if (!_codecIndeo3->decompressFrame(dataPtr, dataLen, _vidBuffer, - width * (_doubleMode ? 2 : 1), height * (_doubleMode ? 2 : 1))) - return 0; - - type = 2; - srcPtr = _vidBuffer; - width = _width * (_doubleMode ? 2 : 1); - height = _height * (_doubleMode ? 2 : 1); - right = left + width - 1; - bottom = top + height - 1; - - } else { - - if (_externalCodec) { - warning("Unknown external codec"); - return 0; - } - -#else - - if (_externalCodec) { - return 0; - } else { - -#endif - - type = *dataPtr++; - srcPtr = dataPtr; - - if (_blitMode > 0) { - dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX((left - _x)); - imdVidMem = _vidMem + _vidMemWidth * top + preScaleX(left); - sW = postScaleX(_width); - sH = _height; - } - - if (type & 0x80) { - // Frame data is compressed - - srcPtr = _vidBuffer; - type &= 0x7F; - if ((type == 2) && (postScaleX(width) == sW)) { - // Directly uncompress onto the video surface - deLZ77(dest, dataPtr); - blit(imdVidMem, dest, width, height); - return 1; - } else - deLZ77(srcPtr, dataPtr); - } - - } - - width = postScaleX(width); - - int16 drawWidth = MAX<int16>(0, MIN<int16>(width , sW - left)); - int16 drawHeight = MAX<int16>(0, MIN<int16>(height, sH - top )); - - // Evaluate the block type - if (type == 0x01) - renderBlockSparse (dest, srcPtr, drawWidth, drawHeight, sW, width); - else if (type == 0x02) - renderBlockWhole (dest, srcPtr, drawWidth, drawHeight, sW, width); - else if (type == 0x03) - renderBlockRLE (dest, srcPtr, drawWidth, drawHeight, sW, width); - else if (type == 0x42) - renderBlockWhole4X (dest, srcPtr, drawWidth, drawHeight, sW, width); - else if ((type & 0x0F) == 0x02) - renderBlockWhole2Y (dest, srcPtr, drawWidth, drawHeight, sW, width); - else - renderBlockSparse2Y(dest, srcPtr, drawWidth, drawHeight, sW, width); - - dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX(left - _x); - blit(imdVidMem, dest, width, height); - - return 1; -} - -inline int32 Vmd::preScaleX(int32 x) const { - return x / _preScaleX; -} - -inline int32 Vmd::postScaleX(int32 x) const { - return x * _postScaleX; -} - -void Vmd::blit(byte *dest, byte *src, int16 width, int16 height) { - if (_blitMode == 0) - return; - - if (_blitMode == 1) - blit16(dest, src, preScaleX(_width), preScaleX(width), height); - else if (_blitMode == 2) - blit24(dest, src, preScaleX(_width), preScaleX(width), height); -} - -void Vmd::blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height) { - assert(_palLUT); - - Graphics::SierraLight *dither = - new Graphics::SierraLight(width, _palLUT); - - for (int i = 0; i < height; i++) { - byte *d = dest; - byte *s = src; - - for (int j = 0; j < width; j++, s += 2) { - uint16 data = READ_LE_UINT16(s); - byte r = ((data & 0x7C00) >> 10); - byte g = ((data & 0x03E0) >> 5); - byte b = ((data & 0x001F) >> 0); - byte dY, dU, dV; - - Graphics::PaletteLUT::RGB2YUV(r << 3, g << 3, b << 3, dY, dU, dV); - - byte p = dither->dither(dY, dU, dV, j); - - if ((dY == 0) || ((r == 0) && (g == 0) && (b == 0))) - *d++ = 0; - else - *d++ = p; - } - - dither->nextLine(); - dest += _vidMemWidth; - src += 2 * srcPitch; - } - - delete dither; -} - -void Vmd::blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height) { - assert(_palLUT); - - Graphics::SierraLight *dither = - new Graphics::SierraLight(width, _palLUT); - - for (int i = 0; i < height; i++) { - byte *d = dest; - byte *s = src; - - for (int j = 0; j < width; j++, s += 3) { - byte r = s[2]; - byte g = s[1]; - byte b = s[0]; - byte dY, dU, dV; - - Graphics::PaletteLUT::RGB2YUV(r, g, b, dY, dU, dV); - - byte p = dither->dither(dY, dU, dV, j); - - if ((dY == 0) || ((r == 0) && (g == 0) && (b == 0))) - *d++ = 0; - else - *d++ = p; - } - - dither->nextLine(); - dest += _vidMemWidth; - src += 3 * srcPitch; - } - - delete dither; -} - -byte *Vmd::deDPCM(const byte *data, uint32 &size, int32 init[2]) { - if (!data || (size == 0)) - return 0; - - int channels = (_soundStereo > 0) ? 2 : 1; - - uint32 inSize = size; - uint32 outSize = size + channels; - - int16 *out = (int16 *)malloc(outSize * 2); - byte *sound = (byte *) out; - - int channel = 0; - - for (int i = 0; i < channels; i++) { - *out++ = TO_BE_16(init[channel]); - - channel = (channel + 1) % channels; - } - - while (inSize-- > 0) { - if (*data & 0x80) - init[channel] -= _tableDPCM[*data++ & 0x7F]; - else - init[channel] += _tableDPCM[*data++]; - - init[channel] = CLIP<int32>(init[channel], -32768, 32767); - *out++ = TO_BE_16(init[channel]); - - channel = (channel + 1) % channels; - } - - size = outSize * 2; - return sound; -} - -// Yet another IMA ADPCM variant -byte *Vmd::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) { - if (!data || (size == 0)) - return 0; - - uint32 outSize = size * 2; - - int16 *out = (int16 *)malloc(outSize * 2); - byte *sound = (byte *) out; - - index = CLIP<int32>(index, 0, 88); - - int32 predictor = _tableADPCM[index]; - - uint32 dataByte = 0; - bool newByte = true; - - size *= 2; - while (size -- > 0) { - byte code = 0; - - if (newByte) { - dataByte = *data++; - code = (dataByte >> 4) & 0xF; - } else - code = dataByte & 0xF; - - newByte = !newByte; - - index += _tableADPCMStep[code]; - index = CLIP<int32>(index, 0, 88); - - int32 value = predictor / 8; - - if (code & 4) - value += predictor; - if (code & 2) - value += predictor / 2; - if (code & 1) - value += predictor / 4; - - if (code & 8) - init -= value; - else - init += value; - - init = CLIP<int32>(init, -32768, 32767); - - predictor = _tableADPCM[index]; - - *out++ = TO_BE_16(init); - } - - size = outSize * 2; - return sound; -} - -byte *Vmd::soundEmpty(uint32 &size) { - if (!_audioStream) - return 0; - - byte *soundBuf = (byte *)malloc(size); - memset(soundBuf, 0, size); - - return soundBuf; -} - -byte *Vmd::sound8bitDirect(uint32 &size) { - if (!_audioStream) { - _stream->skip(size); - return 0; - } - - byte *soundBuf = (byte *)malloc(size); - _stream->read(soundBuf, size); - unsignedToSigned(soundBuf, size); - - return soundBuf; -} - -byte *Vmd::sound16bitDPCM(uint32 &size) { - if (!_audioStream) { - _stream->skip(size); - return 0; - } - - int32 init[2]; - - init[0] = _stream->readSint16LE(); - size -= 2; - - if (_soundStereo > 0) { - init[1] = _stream->readSint16LE(); - size -= 2; - } - - byte *data = new byte[size]; - byte *sound = 0; - - if (_stream->read(data, size) == size) - sound = deDPCM(data, size, init); - - delete[] data; - - return sound; -} - -byte *Vmd::sound16bitADPCM(uint32 &size) { - if (!_audioStream) { - _stream->skip(size); - return 0; - } - - int32 init = _stream->readSint16LE(); - size -= 2; - - int32 index = _stream->readByte(); - size--; - - byte *data = new byte[size]; - byte *sound = 0; - - if (_stream->read(data, size) == size) - sound = deADPCM(data, size, init, index); - - delete[] data; - - return sound; -} - -void Vmd::emptySoundSlice(uint32 size) { - byte *sound = soundEmpty(size); - - if (sound) { - uint32 flags = 0; - flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; - flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; - - _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); - } -} - -void Vmd::filledSoundSlice(uint32 size) { - byte *sound = 0; - if (_audioFormat == kAudioFormat8bitDirect) - sound = sound8bitDirect(size); - else if (_audioFormat == kAudioFormat16bitDPCM) - sound = sound16bitDPCM(size); - else if (_audioFormat == kAudioFormat16bitADPCM) - sound = sound16bitADPCM(size); - - if (sound) { - uint32 flags = 0; - flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; - flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; - - _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); - } -} - -uint8 Vmd::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) { - max = MIN<int>(_soundSlicesCount - 1, 31); - - uint8 n = 0; - for (int i = 0; i < max; i++) { - - if (!(mask & 1)) { - n++; - *fillInfo++ = true; - } else - *fillInfo++ = false; - - mask >>= 1; - } - - return n; -} - -void Vmd::filledSoundSlices(uint32 size, uint32 mask) { - bool fillInfo[32]; - - uint8 max; - uint8 n = evaluateMask(mask, fillInfo, max); - - int32 extraSize; - - extraSize = size - n * _soundDataSize; - - if (_soundSlicesCount > 32) - extraSize -= (_soundSlicesCount - 32) * _soundDataSize; - - if (n > 0) - extraSize /= n; - - for (uint8 i = 0; i < max; i++) - if (fillInfo[i]) - filledSoundSlice(_soundDataSize + extraSize); - else - emptySoundSlice(_soundDataSize * _soundBytesPerSample); - - if (_soundSlicesCount > 32) - filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize); -} - -bool Vmd::getPartCoords(int16 frame, PartType type, - int16 &x, int16 &y, int16 &width, int16 &height) { - - if (frame >= _framesCount) - return false; - - Frame &f = _frames[frame]; - - // Look for a part matching the requested type, stopping at a separator - Part *part = 0; - for (int i = 0; i < _partsPerFrame; i++) { - Part &p = f.parts[i]; - - if ((p.type == kPartTypeSeparator) || (p.type == type)) { - part = &p; - break; - } - } - - if (!part) - return false; - - x = part->left; - y = part->top; - width = part->right - part->left + 1; - height = part->bottom - part->top + 1; - - return true; -} - -bool Vmd::getFrameCoords(int16 frame, - int16 &x, int16 &y, int16 &width, int16 &height) { - - return getPartCoords(frame, kPartTypeVideo, x, y, width, height); -} - -bool Vmd::hasExtraData() const { - return !_extraData.empty(); -} - -bool Vmd::hasExtraData(const char *fileName) const { - for (uint i = 0; i < _extraData.size(); i++) - if (!scumm_stricmp(_extraData[i].name, fileName)) - return true; - - return false; -} - -Common::MemoryReadStream *Vmd::getExtraData(const char *fileName) { - uint i = 0; - - for (i = 0; i < _extraData.size(); i++) - if (!scumm_stricmp(_extraData[i].name, fileName)) - break; - - if (i >= _extraData.size()) - return 0; - - if ((_extraData[i].size - 20) != _extraData[i].realSize) { - warning("Vmd::getExtraData(): Sizes for \"%s\" differ! (%d, %d)", - fileName, (_extraData[i].size - 20), _extraData[i].realSize); - return 0; - } - - if (!_stream->seek(_extraData[i].offset)) { - warning("Vmd::getExtraData(): Can't seek to offset %d to (file \"%s\")", - _extraData[i].offset, fileName); - return 0; - } - - byte *data = (byte *) malloc(_extraData[i].realSize); - if (_stream->read(data, _extraData[i].realSize) != _extraData[i].realSize) { - free(data); - warning("Vmd::getExtraData(): Couldn't read %d bytes (file \"%s\")", - _extraData[i].realSize, fileName); - } - - Common::MemoryReadStream *stream = - new Common::MemoryReadStream(data, _extraData[i].realSize, DisposeAfterUse::YES); - - return stream; -} - -} // End of namespace Graphics - -#endif // GRAPHICS_VIDEO_COKTELVIDEO_H diff --git a/graphics/video/coktelvideo/coktelvideo.h b/graphics/video/coktelvideo/coktelvideo.h deleted file mode 100644 index f8b1965f41..0000000000 --- a/graphics/video/coktelvideo/coktelvideo.h +++ /dev/null @@ -1,594 +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$ - * - */ - -// Currently, only GOB and SCI32 games play IMDs and VMDs -#if defined(ENABLE_GOB) || defined(ENABLE_SCI32) || defined(DYNAMIC_MODULES) - -#ifndef GRAPHICS_VIDEO_COKTELVIDEO_H -#define GRAPHICS_VIDEO_COKTELVIDEO_H - -#include "common/scummsys.h" -#include "common/stream.h" -#include "common/array.h" -#include "graphics/dither.h" -#include "sound/mixer.h" - -namespace Audio { - class QueuingAudioStream; -} - -namespace Graphics { - -#ifdef USE_INDEO3 -class Indeo3; -#endif - -/** Common interface for handling Coktel Vision videos and derivated formats. */ -class CoktelVideo { -public: - enum Features { - kFeaturesNone = 0, - /** Has an own palette. */ - kFeaturesPalette = 8, - /** Suggests a data size. */ - kFeaturesDataSize = 0x20, - /** Has sound. */ - kFeaturesSound = 0x40, - /** Has specific frame coordinates. */ - kFeaturesFrameCoords = 0x80, - /** Has general standard coordinates. */ - kFeaturesStdCoords = 0x100, - /** Has a frame positions table. */ - kFeaturesFramesPos = 0x200, - /** Has video. */ - kFeaturesVideo = 0x400, - /** Is a full color (non-paletted) video. */ - kFeaturesFullColor = 0x4000, - /** Supports automatic doubling. */ - kFeaturesSupportsDouble = 0x40000000 - }; - - enum StateFlags { - kStateNone = 0, - /** Changed the palette. */ - kStatePalette = 0x10, - /** Performed a jump to another frame. */ - kStateJump = 0x200, - /** Updated according to the specific frame coordinates. */ - kStateFrameCoords = 0x400, - /** Got no frame data. */ - kStateNoVideoData = 0x800, - /** Updated according to the general standard coordinates. */ - kStateStdCoords = 0x1000, - /** Had to explicitely seek to the frame. */ - kStateSeeked = 0x2000, - /** Reached a break-point. */ - kStateBreak = 0x8000, - /** Frame marks the beginning of speech. */ - kStateSpeech = 0x4000000 - }; - - struct State { - /** Left-most value of the updated rectangle. */ - int16 left; - /** Top-most value of the updated rectangle. */ - int16 top; - /** Right-most value of the updated rectangle. */ - int16 right; - /** Bottom-most value of the updated rectangle. */ - int16 bottom; - /** Set accordingly to what was done. */ - uint32 flags; - /** The id of the spoken words. */ - uint16 speechId; - - State() : left(0), top(0), right(0), bottom(0), flags(0), speechId(0) { } - }; - - virtual ~CoktelVideo() { } - - /** Returns the features the loaded video possesses. */ - virtual uint32 getFeatures() const = 0; - /** Returns the flags the loaded video possesses. */ - virtual uint16 getFlags() const = 0; - /** Returns the x coordinate of the video. */ - virtual int16 getX() const = 0; - /** Returns the y coordinate of the video. */ - virtual int16 getY() const = 0; - /** Returns the width of the video. */ - virtual int16 getWidth() const = 0; - /** Returns the height of the video. */ - virtual int16 getHeight() const = 0; - - /** Returns the number of frames the loaded video has. */ - virtual uint16 getFramesCount() const = 0; - /** Returns the current frame number. - * - * This is the current frame after the last nextFrame()-call, - * i.e. it's 0 after loading, 1 after the first nextFrame()-call, etc.. - */ - virtual uint16 getCurrentFrame() const = 0; - /** Returns the frame rate. */ - virtual int16 getFrameRate() const = 0; - /** Returns the number of frames the video lags behind the audio. */ - virtual uint32 getSyncLag() const = 0; - /** Returns the current frame's palette. */ - virtual const byte *getPalette() const = 0; - - /** Returns the frame's coordinates */ - virtual bool getFrameCoords(int16 frame, - int16 &x, int16 &y, int16 &width, int16 &height) = 0; - - /** Returns whether any extra data files are embedded in this video. */ - virtual bool hasExtraData() const = 0; - /** Returns whether that extra data file exists */ - virtual bool hasExtraData(const char *fileName) const = 0; - /** Returns an extra data file */ - virtual Common::MemoryReadStream *getExtraData(const char *fileName) = 0; - - /** Load a video out of a stream. */ - virtual bool load(Common::SeekableReadStream *stream) = 0; - /** Unload the currently loaded video. */ - virtual void unload() = 0; - - /** Set the frame rate. */ - virtual void setFrameRate(int16 frameRate) = 0; - - /** Set the coordinations where to draw the video. */ - virtual void setXY(int16 x, int16 y) = 0; - /** Use a specific memory block as video memory. */ - virtual void setVideoMemory(byte *vidMem, uint16 width, uint16 height) = 0; - /** Use an own memory block as video memory. */ - virtual void setVideoMemory() = 0; - - /** Double the video's resolution. */ - virtual void setDoubleMode(bool doubleMode) = 0; - - /** Play sound (if the video has sound). */ - virtual void enableSound(Audio::Mixer &mixer) = 0; - /** Don't play sound or stop currently playing sound. */ - virtual void disableSound() = 0; - - /** Is sound currently playing? */ - virtual bool isSoundPlaying() const = 0; - - /** Seek to a specific frame. - * - * @param frame The frame to which to seek. - * @param whence The offset from whence the frame is given. - * @param restart Restart the video to reach an otherwise inaccessible frame? - */ - virtual void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false) = 0; - - /** Render the next frame. */ - virtual State nextFrame() = 0; - /** Get the time in ms until the next frame can be displayed. Already includes A/V sync measures. */ - virtual uint32 getFrameWaitTime() = 0; - /** Wait for the frame to end. */ - virtual void waitEndFrame() = 0; - - /** Copy the current frame. - * - * @param dest The memory to which to copy the current frame. - * @param left The x position within the frame. - * @param top The y position within the frame. - * @param width The width of the area to copy. - * @param height The height of the area to copy. - * @param x The x position to where to copy. - * @param y The y position to where to copy. - * @param pitch The buffer's width. - * @param transp Which color should be seen as transparent? - */ - virtual void copyCurrentFrame(byte *dest, - uint16 left, uint16 top, uint16 width, uint16 height, - uint16 x, uint16 y, uint16 pitch, int16 transp = -1) = 0; -}; - -/** Coktel Vision's first simple IMD format, used by Fascination. - */ -class PreImd : public CoktelVideo { -public: - PreImd(); - PreImd(int16 width, int16 height); - ~PreImd(); - - uint32 getFeatures() const; - uint16 getFlags() const; - - int16 getX() const; - int16 getY() const; - int16 getWidth() const; - int16 getHeight() const; - - uint16 getFramesCount() const; - uint16 getCurrentFrame() const; - - void setFrameRate(int16 frameRate); - int16 getFrameRate() const; - - uint32 getSyncLag() const; - - const byte *getPalette() const; - - bool getFrameCoords(int16 frame, - int16 &x, int16 &y, int16 &width, int16 &height); - - bool hasExtraData() const; - bool hasExtraData(const char *fileName) const; - Common::MemoryReadStream *getExtraData(const char *fileName); - - bool load(Common::SeekableReadStream *stream); - void unload(); - - void setXY(int16 x, int16 y); - void setVideoMemory(byte *vidMem, uint16 width, uint16 height); - void setVideoMemory(); - - void setDoubleMode(bool doubleMode); - - void enableSound(Audio::Mixer &mixer); - void disableSound(); - - bool isSoundPlaying() const; - - void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false); - - State nextFrame(); - uint32 getFrameWaitTime(); - void waitEndFrame(); - - void copyCurrentFrame(byte *dest, - uint16 left, uint16 top, uint16 width, uint16 height, - uint16 x, uint16 y, uint16 pitch, int16 transp = -1); - -protected: - Common::SeekableReadStream *_stream; - - // Properties - uint32 _features; - uint16 _flags; - - int16 _forcedWidth; - int16 _forcedHeight; - - // Current coordinates - int16 _x; - int16 _y; - int16 _width; - int16 _height; - - uint16 _framesCount; - uint16 _curFrame; - int16 _frameRate; - uint32 _frameLength; - - byte _palette[768]; - - // Video memory - bool _hasOwnVidMem; - byte *_vidMem; - uint16 _vidMemWidth; - uint16 _vidMemHeight; - - byte *_vidBuffer; - uint32 _vidBufferSize; - - void clear(); - void zeroData(); - void deleteData(); - - void zeroVidMem(); - void deleteVidMem(); - - State processFrame(uint16 frame); - void renderFrame(); -}; - -/** Coktel Vision's IMD files. - */ -class Imd : public PreImd { -public: - Imd(); - ~Imd(); - - void setFrameRate(int16 frameRate); - int16 getFrameRate() const; - - uint32 getSyncLag() const; - - bool load(Common::SeekableReadStream *stream); - void unload(); - - void setXY(int16 x, int16 y); - - void enableSound(Audio::Mixer &mixer); - void disableSound(); - - bool isSoundPlaying() const; - - void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false); - - State nextFrame(); - uint32 getFrameWaitTime(); - - void copyCurrentFrame(byte *dest, - uint16 left, uint16 top, uint16 width, uint16 height, - uint16 x, uint16 y, uint16 pitch, int16 transp = -1); - -protected: - enum Command { - kCommandNextSound = 0xFF00, - kCommandStartSound = 0xFF01, - - kCommandBreak = 0xFFF0, - kCommandBreakSkip0 = 0xFFF1, - kCommandBreakSkip16 = 0xFFF2, - kCommandBreakSkip32 = 0xFFF3, - kCommandBreakMask = 0xFFF8, - - kCommandPalette = 0xFFF4, - kCommandVideoData = 0xFFFC, - - kCommandJump = 0xFFFD - }; - - struct Coord { - int16 left; - int16 top; - int16 right; - int16 bottom; - } PACKED_STRUCT; - - // Properties - uint16 _version; - - // Standard coordinates gives by the header - int16 _stdX; - int16 _stdY; - int16 _stdWidth; - int16 _stdHeight; - - uint32 *_framesPos; - uint32 _firstFramePos; - Coord *_frameCoords; - - // Buffer for raw frame data - byte *_frameData; - uint32 _frameDataSize; - uint32 _frameDataLen; - - // Sound properties - uint16 _soundFlags; - int16 _soundFreq; - int16 _soundSliceSize; - int16 _soundSlicesCount; - uint32 _soundSliceLength; - - // Current sound state - bool _hasSound; - bool _soundEnabled; - uint8 _soundStage; // (0: no sound, 1: loaded, 2: playing) - uint32 _skipFrames; - - Audio::QueuingAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - - uint32 _lastFrameTime; - - Audio::Mixer *_mixer; - - void clear(); - void zeroData(); - void deleteData(); - - void unsignedToSigned(byte *buffer, int length); - - bool loadCoordinates(); - bool loadFrameTableOffsets(uint32 &framesPosPos, uint32 &framesCoordsPos); - bool assessVideoProperties(); - bool assessAudioProperties(); - bool loadFrameTables(uint32 framesPosPos, uint32 framesCoordsPos); - - State processFrame(uint16 frame); - uint32 renderFrame(int16 left, int16 top, int16 right, int16 bottom); - void deLZ77(byte *dest, byte *src); - - void renderBlockWhole (byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch); - void renderBlockWhole4X (byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch); - void renderBlockWhole2Y (byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch); - void renderBlockSparse (byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch); - void renderBlockSparse2Y(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch); - - void calcFrameCoords(uint16 frame, State &state); - - void nextSoundSlice(bool hasNextCmd); - bool initialSoundSlice(bool hasNextCmd); - void emptySoundSlice(bool hasNextCmd); - - void videoData(uint32 size, State &state); -}; - -class Vmd : public Imd { -public: - Vmd(Graphics::PaletteLUT *palLUT = 0); - ~Vmd(); - - bool getFrameCoords(int16 frame, - int16 &x, int16 &y, int16 &width, int16 &height); - - bool hasExtraData() const; - bool hasExtraData(const char *fileName) const; - Common::MemoryReadStream *getExtraData(const char *fileName); - - bool load(Common::SeekableReadStream *stream); - void unload(); - - int16 getWidth() const; - - void setXY(int16 x, int16 y); - - void setDoubleMode(bool doubleMode); - - void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false); - - State nextFrame(); - -protected: - enum PartType { - kPartTypeSeparator = 0, - kPartTypeAudio = 1, - kPartTypeVideo = 2, - kPartTypeExtraData = 3, - kPartType4 = 4, - kPartTypeSpeech = 5 - }; - - enum AudioFormat { - kAudioFormat8bitDirect = 0, - kAudioFormat16bitDPCM = 1, - kAudioFormat16bitADPCM = 2 - }; - - struct ExtraData { - char name[16]; - uint32 offset; - uint32 size; - uint32 realSize; - - ExtraData(); - } PACKED_STRUCT; - - struct Part { - PartType type; - byte field_1; - byte field_E; - uint32 size; - int16 left; - int16 top; - int16 right; - int16 bottom; - uint16 id; - byte flags; - - Part(); - } PACKED_STRUCT; - - struct Frame { - uint32 offset; - Part *parts; - - Frame(); - ~Frame(); - } PACKED_STRUCT; - - // Tables for the audio decompressors - static const uint16 _tableDPCM[128]; - static const int32 _tableADPCM[]; - static const int32 _tableADPCMStep[]; - - bool _hasVideo; - uint32 _videoCodec; - - uint32 _frameInfoOffset; - uint16 _partsPerFrame; - Frame *_frames; - - Common::Array<ExtraData> _extraData; - - // Sound properties - byte _soundBytesPerSample; - byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) - uint32 _soundHeaderSize; - uint32 _soundDataSize; - AudioFormat _audioFormat; - - // Video properties - bool _externalCodec; - byte _blitMode; - byte _bytesPerPixel; - byte _preScaleX; - byte _postScaleX; - byte _scaleExternalX; - byte *_vidMemBuffer; - - bool _doubleMode; - - Graphics::PaletteLUT *_palLUT; - -#ifdef USE_INDEO3 - Indeo3 *_codecIndeo3; -#endif - - void clear(); - void zeroData(); - void deleteData(); - - bool getPartCoords(int16 frame, PartType type, - int16 &x, int16 &y, int16 &width, int16 &height); - - bool assessVideoProperties(); - bool assessAudioProperties(); - void readFrameTable(int &numExtraData); - void readExtraData(); - - State processFrame(uint16 frame); - uint32 renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom); - - void renderBlockRLE(byte *dest, const byte *src, int16 width, int16 height, - int16 destPitch, int16 srcPitch); - - void deRLE(byte *&destPtr, const byte *&srcPtr, int16 destLen, int16 srcLen); - - inline int32 preScaleX(int32 x) const; - inline int32 postScaleX(int32 x) const; - - void blit(byte *dest, byte *src, int16 width, int16 height); - void blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); - void blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); - - byte *deDPCM(const byte *data, uint32 &size, int32 init[2]); - byte *deADPCM(const byte *data, uint32 &size, int32 init, int32 v28); - - byte *soundEmpty(uint32 &size); - byte *sound8bitDirect(uint32 &size); - byte *sound16bitDPCM(uint32 &size); - byte *sound16bitADPCM(uint32 &size); - - uint8 evaluateMask(uint32 mask, bool *fillInfo, uint8 &max); - void emptySoundSlice(uint32 size); - void filledSoundSlice(uint32 size); - void filledSoundSlices(uint32 size, uint32 mask); -}; - -} // End of namespace Graphics - -#endif // GRAPHICS_VIDEO_COKTELVIDEO_H - -#endif // Engine and dynamic plugins guard diff --git a/graphics/video/mpeg_player.cpp b/graphics/video/mpeg_player.cpp index f237f76eed..ea1fa4ec1c 100644 --- a/graphics/video/mpeg_player.cpp +++ b/graphics/video/mpeg_player.cpp @@ -23,6 +23,69 @@ * */ +// The YUV to RGB conversion code is derived from SDL's YUV overlay code, which +// in turn appears to be derived from mpeg_play. The following copyright +// notices have been included in accordance with the original license. Please +// note that the term "software" in this context only applies to the +// buildLookup() and plotYUV*() functions below. + +// Copyright (c) 1995 The Regents of the University of California. +// All rights reserved. +// +// Permission to use, copy, modify, and distribute this software and its +// documentation for any purpose, without fee, and without written agreement is +// hereby granted, provided that the above copyright notice and the following +// two paragraphs appear in all copies of this software. +// +// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT +// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF +// CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +// ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO +// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +// Copyright (c) 1995 Erik Corry +// All rights reserved. +// +// Permission to use, copy, modify, and distribute this software and its +// documentation for any purpose, without fee, and without written agreement is +// hereby granted, provided that the above copyright notice and the following +// two paragraphs appear in all copies of this software. +// +// IN NO EVENT SHALL ERIK CORRY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF +// THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIK CORRY HAS BEEN ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ERIK CORRY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" +// BASIS, AND ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, +// UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +// Portions of this software Copyright (c) 1995 Brown University. +// All rights reserved. +// +// Permission to use, copy, modify, and distribute this software and its +// documentation for any purpose, without fee, and without written agreement +// is hereby granted, provided that the above copyright notice and the +// following two paragraphs appear in all copies of this software. +// +// IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR +// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT +// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN +// UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" +// BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, +// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + #include "graphics/video/mpeg_player.h" #include "common/file.h" #include "common/system.h" @@ -284,69 +347,6 @@ void BaseAnimationState::buildLookup(int p, int lines) { #else -// The YUV to RGB conversion code is derived from SDL's YUV overlay code, which -// in turn appears to be derived from mpeg_play. The following copyright -// notices have been included in accordance with the original license. Please -// note that the term "software" in this context only applies to the two -// functions buildLookup() and plotYUV() below. - -// Copyright (c) 1995 The Regents of the University of California. -// All rights reserved. -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without written agreement is -// hereby granted, provided that the above copyright notice and the following -// two paragraphs appear in all copies of this software. -// -// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT -// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF -// CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS -// ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO -// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - -// Copyright (c) 1995 Erik Corry -// All rights reserved. -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without written agreement is -// hereby granted, provided that the above copyright notice and the following -// two paragraphs appear in all copies of this software. -// -// IN NO EVENT SHALL ERIK CORRY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF -// THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIK CORRY HAS BEEN ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. -// -// ERIK CORRY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" -// BASIS, AND ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, -// UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - -// Portions of this software Copyright (c) 1995 Brown University. -// All rights reserved. -// -// Permission to use, copy, modify, and distribute this software and its -// documentation for any purpose, without fee, and without written agreement -// is hereby granted, provided that the above copyright notice and the -// following two paragraphs appear in all copies of this software. -// -// IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR -// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT -// OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN -// UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" -// BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, -// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - void BaseAnimationState::buildLookup() { // Do we already have lookup tables for this bit format? Graphics::PixelFormat format = _sys->getOverlayFormat(); |
