/* 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 { class Codec; /** 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; // Sound properties byte _soundBytesPerSample; byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) uint32 _soundHeaderSize; uint32 _soundDataSize; AudioFormat _audioFormat; // Video properties byte _blitMode; byte _bytesPerPixel; byte _preScaleX; byte _postScaleX; byte _scaleExternalX; byte *_vidMemBuffer; bool _doubleMode; Graphics::PaletteLUT *_palLUT; bool _externalCodec; Codec *_codec; 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