aboutsummaryrefslogtreecommitdiff
path: root/graphics/video
diff options
context:
space:
mode:
authorSven Hesse2010-08-08 00:41:56 +0000
committerSven Hesse2010-08-08 00:41:56 +0000
commitf19be90c37c3aa402ea7efdc0fc02595fe68a122 (patch)
tree4c36204fc25bb3af47ff578b48a5c906b4978c8e /graphics/video
parent31ecaa52759e7f471da049bda0162316655a00b8 (diff)
downloadscummvm-rg350-f19be90c37c3aa402ea7efdc0fc02595fe68a122.tar.gz
scummvm-rg350-f19be90c37c3aa402ea7efdc0fc02595fe68a122.tar.bz2
scummvm-rg350-f19be90c37c3aa402ea7efdc0fc02595fe68a122.zip
VIDEO/GOB: Implement IMD loading
svn-id: r51865
Diffstat (limited to 'graphics/video')
-rw-r--r--graphics/video/coktel_decoder.cpp342
-rw-r--r--graphics/video/coktel_decoder.h106
2 files changed, 435 insertions, 13 deletions
diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp
index 220a290da6..2b4b7182f3 100644
--- a/graphics/video/coktel_decoder.cpp
+++ b/graphics/video/coktel_decoder.cpp
@@ -27,6 +27,8 @@
#ifdef GRAPHICS_VIDEO_COKTELDECODER_H
+#include "sound/audiostream.h"
+
namespace Graphics {
CoktelDecoder::State::State() : flags(0), speechId(0) {
@@ -34,8 +36,10 @@ 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), _frameCount(0),
- _paletteDirty(false), _ownSurface(true), _frameRate(12) {
+ _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) {
memset(_palette, 0, 768);
}
@@ -97,21 +101,85 @@ void CoktelDecoder::setXY(uint16 x, uint16 y) {
_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::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;
+}
+
void CoktelDecoder::close() {
+ disableSound();
freeSurface();
_x = 0;
_y = 0;
+ _defaultX = 0;
+ _defaultY = 0;
+
+ _features = 0;
+
_frameCount = 0;
+
+ _hasSound = false;
}
uint16 CoktelDecoder::getWidth() const {
@@ -131,7 +199,7 @@ byte *CoktelDecoder::getPalette() {
}
bool CoktelDecoder::hasDirtyPalette() const {
- return _paletteDirty;
+ return (_features & kFeaturesPalette) && _paletteDirty;
}
Common::Rational CoktelDecoder::getFrameRate() const {
@@ -237,8 +305,6 @@ Surface *PreIMDDecoder::decodeNextFrame() {
processFrame();
renderFrame();
- _curFrame++;
-
if (_curFrame == 0)
_startTime = g_system->getMillis();
@@ -307,6 +373,8 @@ void PreIMDDecoder::processFrame() {
}
_stream->seek(nextFramePos);
+
+ _curFrame++;
}
void PreIMDDecoder::renderFrame() {
@@ -340,7 +408,12 @@ PixelFormat PreIMDDecoder::getPixelFormat() const {
IMDDecoder::IMDDecoder(Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType),
- _stream(0), _videoBuffer(0), _videoBufferSize(0) {
+ _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() {
@@ -370,7 +443,37 @@ bool IMDDecoder::seek(int32 frame, int whence, bool restart) {
// Nothing to do
return true;
- // TODO
+ // Try every possible way to find a file offset to that frame
+ uint32 framePos = 0;
+ if (frame == -1) {
+
+ framePos = _firstFramePos;
+
+ } else if (frame == 0) {
+
+ framePos = _firstFramePos;
+ _stream->seek(framePos);
+ framePos += _stream->readUint16LE() + 4;
+
+ } else if (_framePos) {
+
+ framePos = _framePos[frame + 1];
+
+ } else if (restart && (_soundStage == kSoundNone)) {
+
+ for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++)
+ processFrame();
+
+ return true;
+
+ } else {
+ warning("Imd::seek(): Frame %d is not directly accessible", frame + 1);
+ return false;
+ }
+
+ // Seek
+ _stream->seek(framePos);
+ _curFrame = frame;
return true;
}
@@ -380,11 +483,192 @@ bool IMDDecoder::load(Common::SeekableReadStream &stream) {
_stream = &stream;
- warning("IMDDecoder::load()");
+ uint16 handle;
- // TODO
+ 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
+ _stream->read((byte *) _palette, 768);
+
+ _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("IMD: 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;
- return false;
+ } 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;
+ _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() {
@@ -394,12 +678,43 @@ void IMDDecoder::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 {
@@ -415,8 +730,6 @@ Surface *IMDDecoder::decodeNextFrame() {
processFrame();
renderFrame();
- _curFrame++;
-
if (_curFrame == 0)
_startTime = g_system->getMillis();
@@ -424,13 +737,18 @@ Surface *IMDDecoder::decodeNextFrame() {
}
void IMDDecoder::processFrame() {
+
// TODO
+
+ _curFrame++;
}
void IMDDecoder::renderFrame() {
_dirtyRects.clear();
// TODO
+
+ _dirtyRects.push_back(Common::Rect(_x, _y, _x + _width, _y + _height));
}
PixelFormat IMDDecoder::getPixelFormat() const {
diff --git a/graphics/video/coktel_decoder.h b/graphics/video/coktel_decoder.h
index 3e8c03e49e..f4da9624bd 100644
--- a/graphics/video/coktel_decoder.h
+++ b/graphics/video/coktel_decoder.h
@@ -40,6 +40,10 @@
#include "sound/mixer.h"
+namespace Audio {
+ class QueuingAudioStream;
+}
+
namespace Graphics {
class CoktelDecoder : public FixedRateVideoDecoder {
@@ -65,14 +69,28 @@ public:
void setSurfaceMemory();
/** Draw the video starting at this position within the video memory. */
- void setXY(uint16 x, uint16 y);
+ 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 hasSound() const;
+ bool isSoundEnabled() const;
+ bool isSoundPlaying() const;
+
+ void enableSound();
+ void disableSound();
+
// VideoDecoder interface
void close();
@@ -86,6 +104,23 @@ public:
bool hasDirtyPalette() 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;
@@ -95,6 +130,11 @@ protected:
uint16 _x;
uint16 _y;
+ uint16 _defaultX;
+ uint16 _defaultY;
+
+ uint32 _features;
+
uint32 _frameCount;
byte _palette[768];
@@ -107,6 +147,14 @@ protected:
Common::Rational _frameRate;
+ // Current sound state
+ bool _hasSound;
+ bool _soundEnabled;
+ SoundStage _soundStage;
+
+ Audio::QueuingAudioStream *_audioStream;
+ Audio::SoundHandle _audioHandle;
+
bool hasSurface();
void createSurface();
void freeSurface();
@@ -137,6 +185,7 @@ public:
private:
Common::SeekableReadStream *_stream;
+ // Buffer for processed frame data
byte *_videoBuffer;
uint32 _videoBufferSize;
@@ -163,11 +212,66 @@ public:
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;
+
+ bool loadCoordinates();
+ bool loadFrameTableOffsets(uint32 &framePosPos, uint32 &frameCoordsPos);
+ bool assessVideoProperties();
+ bool assessAudioProperties();
+ bool loadFrameTables(uint32 framePosPos, uint32 frameCoordsPos);
+
void processFrame();
void renderFrame();
};