diff options
Diffstat (limited to 'engines/sci')
-rw-r--r-- | engines/sci/console.cpp | 12 | ||||
-rw-r--r-- | engines/sci/engine/kvideo.cpp | 22 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 5 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 2 | ||||
-rw-r--r-- | engines/sci/video/robot_decoder.cpp | 377 | ||||
-rw-r--r-- | engines/sci/video/robot_decoder.h | 129 | ||||
-rw-r--r-- | engines/sci/video/seq_decoder.cpp | 61 | ||||
-rw-r--r-- | engines/sci/video/seq_decoder.h | 67 |
8 files changed, 358 insertions, 317 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 564bbbbd79..1889d53480 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -250,20 +250,18 @@ void Console::postEnter() { #endif if (_videoFile.hasSuffix(".seq")) { - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(_videoFrameDelay); - videoDecoder = seqDecoder; + videoDecoder = new SEQDecoder(_videoFrameDelay); #ifdef ENABLE_SCI32 } else if (_videoFile.hasSuffix(".vmd")) { - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); } else if (_videoFile.hasSuffix(".rbt")) { - videoDecoder = new RobotDecoder(g_system->getMixer(), _engine->getPlatform() == Common::kPlatformMacintosh); + videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh); } else if (_videoFile.hasSuffix(".duk")) { duckMode = true; - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); #endif } else if (_videoFile.hasSuffix(".avi")) { - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); } else { warning("Unrecognized video type"); } diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index cb2a763da9..6bf9aff2fe 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -50,6 +50,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { if (!videoDecoder) return; + videoDecoder->start(); + byte *scaleBuffer = 0; byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel; uint16 width = videoDecoder->getWidth(); @@ -162,9 +164,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks - videoDecoder = seqDecoder; + // Time is specified as ticks + videoDecoder = new SEQDecoder(argv[1].toUint16()); if (!videoDecoder->loadFile(filename)) { warning("Failed to open movie file %s", filename.c_str()); @@ -190,7 +191,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case 0: { Common::String filename = s->_segMan->getString(argv[1]); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (filename.equalsIgnoreCase("gk2a.avi")) { // HACK: Switch to 16bpp graphics for Indeo3. @@ -252,6 +253,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { int16 y = argv[5].toUint16(); warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); g_sci->_robotDecoder->load(id); + g_sci->_robotDecoder->start(); g_sci->_robotDecoder->setPos(x, y); } break; @@ -267,13 +269,13 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { warning("kRobot(%d)", subop); break; case 8: // sync - //if (false) { // debug: automatically skip all robot videos - if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) { - writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); - } else { + //if (true) { // debug: automatically skip all robot videos + if (g_sci->_robotDecoder->endOfVideo()) { g_sci->_robotDecoder->close(); // Signal the engine scripts that the video is done writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); + } else { + writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); } break; default: @@ -348,7 +350,7 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { break; } case 6: // Play - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); if (s->_videoState.fileName.empty()) { // Happens in Lighthouse @@ -406,7 +408,7 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) { s->_videoState.reset(); s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (!videoDecoder->loadFile(s->_videoState.fileName)) { warning("Could not open Duck %s", s->_videoState.fileName.c_str()); diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 6628247127..968014c032 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -28,6 +28,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/engine.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -488,7 +489,7 @@ void GfxFrameout::showVideo() { uint16 y = videoDecoder->getPos().y; if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { @@ -497,7 +498,7 @@ void GfxFrameout::showVideo() { g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); g_system->updateScreen(); } diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index d43a9d06fc..42ae00b525 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -632,7 +632,7 @@ void SciEngine::initGraphics() { _gfxPaint = _gfxPaint32; _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxText32); - _robotDecoder = new RobotDecoder(g_system->getMixer(), getPlatform() == Common::kPlatformMacintosh); + _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32); } else { #endif diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index ebcfac6054..608c77136f 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -22,11 +22,13 @@ #include "common/archive.h" #include "common/stream.h" +#include "common/substream.h" #include "common/system.h" #include "common/textconsole.h" #include "common/util.h" #include "graphics/surface.h" +#include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "sci/resource.h" @@ -63,57 +65,26 @@ namespace Sci { // our graphics engine, it looks just like a part of the room. A RBT can move // around the screen and go behind other objects. (...) -#ifdef ENABLE_SCI32 - -enum robotPalTypes { +enum RobotPalTypes { kRobotPalVariable = 0, kRobotPalConstant = 1 }; -RobotDecoder::RobotDecoder(Audio::Mixer *mixer, bool isBigEndian) { - _surface = 0; - _width = 0; - _height = 0; +RobotDecoder::RobotDecoder(bool isBigEndian) { _fileStream = 0; - _audioStream = 0; - _dirtyPalette = false; _pos = Common::Point(0, 0); - _mixer = mixer; _isBigEndian = isBigEndian; + _frameTotalSize = 0; } RobotDecoder::~RobotDecoder() { close(); } -bool RobotDecoder::load(GuiResourceId id) { - // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - - // its drawn at odd coordinates. SV can't play it either (along with some - // others), so it must be some new functionality added in RAMA's robot - // videos. Skip it for now. - if (g_sci->getGameId() == GID_RAMA && id == 1003) - return false; - - // TODO: The robot video in the Lighthouse demo gets stuck - if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) - return false; - - Common::String fileName = Common::String::format("%d.rbt", id); - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - - if (!stream) { - warning("Unable to open robot file %s", fileName.c_str()); - return false; - } - - return loadStream(stream); -} - bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES); - _surface = new Graphics::Surface(); readHeaderChunk(); @@ -125,131 +96,60 @@ bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { if (_header.version < 4 || _header.version > 6) error("Unknown robot version: %d", _header.version); - if (_header.hasSound) { - _audioStream = Audio::makeQueuingAudioStream(11025, false); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance()); - } + RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount); + addTrack(videoTrack); - readPaletteChunk(_header.paletteDataSize); - readFrameSizesChunk(); - calculateVideoDimensions(); - _surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8()); + if (_header.hasSound) + addTrack(new RobotAudioTrack()); + videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize); + readFrameSizesChunk(); + videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize); return true; } -void RobotDecoder::readHeaderChunk() { - // Header (60 bytes) - _fileStream->skip(6); - _header.version = _fileStream->readUint16(); - _header.audioChunkSize = _fileStream->readUint16(); - _header.audioSilenceSize = _fileStream->readUint16(); - _fileStream->skip(2); - _header.frameCount = _fileStream->readUint16(); - _header.paletteDataSize = _fileStream->readUint16(); - _header.unkChunkDataSize = _fileStream->readUint16(); - _fileStream->skip(5); - _header.hasSound = _fileStream->readByte(); - _fileStream->skip(34); - - // Some videos (e.g. robot 1305 in Phantasmagoria and - // robot 184 in Lighthouse) have an unknown chunk before - // the palette chunk (probably used for sound preloading). - // Skip it here. - if (_header.unkChunkDataSize) - _fileStream->skip(_header.unkChunkDataSize); -} - -void RobotDecoder::readPaletteChunk(uint16 chunkSize) { - byte *paletteData = new byte[chunkSize]; - _fileStream->read(paletteData, chunkSize); - - // SCI1.1 palette - byte palFormat = paletteData[32]; - uint16 palColorStart = paletteData[25]; - uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); +bool RobotDecoder::load(GuiResourceId id) { + // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - + // its drawn at odd coordinates. SV can't play it either (along with some + // others), so it must be some new functionality added in RAMA's robot + // videos. Skip it for now. + if (g_sci->getGameId() == GID_RAMA && id == 1003) + return false; + + // TODO: The robot video in the Lighthouse demo gets stuck + if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) + return false; - int palOffset = 37; - memset(_palette, 0, 256 * 3); + Common::String fileName = Common::String::format("%d.rbt", id); + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { - if (palFormat == kRobotPalVariable) - palOffset++; - _palette[colorNo * 3 + 0] = paletteData[palOffset++]; - _palette[colorNo * 3 + 1] = paletteData[palOffset++]; - _palette[colorNo * 3 + 2] = paletteData[palOffset++]; + if (!stream) { + warning("Unable to open robot file %s", fileName.c_str()); + return false; } - _dirtyPalette = true; - delete[] paletteData; + return loadStream(stream); } +void RobotDecoder::close() { + VideoDecoder::close(); -void RobotDecoder::readFrameSizesChunk() { - // The robot video file contains 2 tables, with one entry for each frame: - // - A table containing the size of the image in each video frame - // - A table containing the total size of each video frame. - // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, - // they contain 32-bit integers. - - _frameTotalSize = new uint32[_header.frameCount]; - - // TODO: The table reading code can probably be removed once the - // audio chunk size is figured out (check the TODO inside processNextFrame()) -#if 0 - // We don't need any of the two tables to play the video, so we ignore - // both of them. - uint16 wordSize = _header.version == 6 ? 4 : 2; - _fileStream->skip(_header.frameCount * wordSize * 2); -#else - switch (_header.version) { - case 4: - case 5: // sizes are 16-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 2); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint16(); - break; - case 6: // sizes are 32-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 4); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint32(); - break; - default: - error("Can't yet handle index table for robot version %d", _header.version); - } -#endif - - // 2 more unknown tables - _fileStream->skip(1024 + 512); + delete _fileStream; + _fileStream = 0; - // Pad to nearest 2 kilobytes - uint32 curPos = _fileStream->pos(); - if (curPos & 0x7ff) - _fileStream->seek((curPos & ~0x7ff) + 2048); + delete[] _frameTotalSize; + _frameTotalSize = 0; } -void RobotDecoder::calculateVideoDimensions() { - // This is an O(n) operation, as each frame has a different size. - // We need to know the actual frame size to have a constant video size. - uint32 pos = _fileStream->pos(); - - for (uint32 curFrame = 0; curFrame < _header.frameCount; curFrame++) { - _fileStream->skip(4); - uint16 frameWidth = _fileStream->readUint16(); - uint16 frameHeight = _fileStream->readUint16(); - if (frameWidth > _width) - _width = frameWidth; - if (frameHeight > _height) - _height = frameHeight; - _fileStream->skip(_frameTotalSize[curFrame] - 8); - } +void RobotDecoder::readNextPacket() { + // Get our track + RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0); + videoTrack->increaseCurFrame(); + Graphics::Surface *surface = videoTrack->getSurface(); - _fileStream->seek(pos); -} + if (videoTrack->endOfTrack()) + return; -const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Read frame image header (24 bytes) _fileStream->skip(3); byte frameScale = _fileStream->readByte(); @@ -258,23 +158,28 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { _fileStream->skip(4); // unknown, almost always 0 uint16 frameX = _fileStream->readUint16(); uint16 frameY = _fileStream->readUint16(); + // TODO: In v4 robot files, frameX and frameY have a different meaning. // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up // correctly. if (_header.version == 4) frameX = frameY = 0; + uint16 compressedSize = _fileStream->readUint16(); uint16 frameFragments = _fileStream->readUint16(); _fileStream->skip(4); // unknown uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100; + // FIXME: A frame's height + position can go off limits... why? With the // following, we cut the contents to fit the frame - uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, _height - frameY); + uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY); + // FIXME: Same goes for the frame's width + position. In this case, we // modify the position to fit the contents on screen. - if (frameWidth + frameX > _width) - frameX = _width - frameWidth; - assert (frameWidth + frameX <= _width && scaledHeight + frameY <= _height); + if (frameWidth + frameX > surface->w) + frameX = surface->w - frameWidth; + + assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h); DecompressorLZS lzs; byte *decompressedFrame = new byte[decompressedSize]; @@ -305,24 +210,23 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Copy over the decompressed frame byte *inFrame = decompressedFrame; - byte *outFrame = (byte *)_surface->pixels; + byte *outFrame = (byte *)surface->pixels; // Black out the surface - memset(outFrame, 0, _width * _height); + memset(outFrame, 0, surface->w * surface->h); // Move to the correct y coordinate - outFrame += _width * frameY; + outFrame += surface->w * frameY; for (uint16 y = 0; y < scaledHeight; y++) { memcpy(outFrame + frameX, inFrame, frameWidth); inFrame += frameWidth; - outFrame += _width; + outFrame += surface->w; } delete[] decompressedFrame; - // +1 because we start with frame number -1 - uint32 audioChunkSize = _frameTotalSize[_curFrame + 1] - (24 + compressedSize); + uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize); // TODO: The audio chunk size below is usually correct, but there are some // exceptions (e.g. robot 4902 in Phantasmagoria, towards its end) @@ -337,51 +241,166 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Queue the next audio frame // FIXME: For some reason, there are audio hiccups/gaps if (_header.hasSound) { - _fileStream->skip(8); // header - _audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize - 8), - (audioChunkSize - 8) * 2, DisposeAfterUse::NO, - Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); + RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1); + _fileStream->skip(8); // header + audioChunkSize -= 8; + audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2); } else { _fileStream->skip(audioChunkSize); - } - - if (_curFrame == -1) - _startTime = g_system->getMillis(); + } +} - _curFrame++; +void RobotDecoder::readHeaderChunk() { + // Header (60 bytes) + _fileStream->skip(6); + _header.version = _fileStream->readUint16(); + _header.audioChunkSize = _fileStream->readUint16(); + _header.audioSilenceSize = _fileStream->readUint16(); + _fileStream->skip(2); + _header.frameCount = _fileStream->readUint16(); + _header.paletteDataSize = _fileStream->readUint16(); + _header.unkChunkDataSize = _fileStream->readUint16(); + _fileStream->skip(5); + _header.hasSound = _fileStream->readByte(); + _fileStream->skip(34); - return _surface; + // Some videos (e.g. robot 1305 in Phantasmagoria and + // robot 184 in Lighthouse) have an unknown chunk before + // the palette chunk (probably used for sound preloading). + // Skip it here. + if (_header.unkChunkDataSize) + _fileStream->skip(_header.unkChunkDataSize); } -void RobotDecoder::close() { - if (!_fileStream) - return; +void RobotDecoder::readFrameSizesChunk() { + // The robot video file contains 2 tables, with one entry for each frame: + // - A table containing the size of the image in each video frame + // - A table containing the total size of each video frame. + // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, + // they contain 32-bit integers. - delete _fileStream; - _fileStream = 0; + _frameTotalSize = new uint32[_header.frameCount]; + // TODO: The table reading code can probably be removed once the + // audio chunk size is figured out (check the TODO inside processNextFrame()) +#if 0 + // We don't need any of the two tables to play the video, so we ignore + // both of them. + uint16 wordSize = _header.version == 6 ? 4 : 2; + _fileStream->skip(_header.frameCount * wordSize * 2); +#else + switch (_header.version) { + case 4: + case 5: // sizes are 16-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 2); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint16(); + break; + case 6: // sizes are 32-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 4); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint32(); + break; + default: + error("Can't yet handle index table for robot version %d", _header.version); + } +#endif + + // 2 more unknown tables + _fileStream->skip(1024 + 512); + + // Pad to nearest 2 kilobytes + uint32 curPos = _fileStream->pos(); + if (curPos & 0x7ff) + _fileStream->seek((curPos & ~0x7ff) + 2048); +} + +RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) { + _surface = new Graphics::Surface(); + _curFrame = -1; + _dirtyPalette = false; +} + +RobotDecoder::RobotVideoTrack::~RobotVideoTrack() { _surface->free(); delete _surface; - _surface = 0; +} - if (_header.hasSound) { - _mixer->stopHandle(_audioHandle); - //delete _audioStream; _audioStream = 0; +uint16 RobotDecoder::RobotVideoTrack::getWidth() const { + return _surface->w; +} + +uint16 RobotDecoder::RobotVideoTrack::getHeight() const { + return _surface->h; +} + +Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const { + return _surface->format; +} + +void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) { + byte *paletteData = new byte[chunkSize]; + stream->read(paletteData, chunkSize); + + // SCI1.1 palette + byte palFormat = paletteData[32]; + uint16 palColorStart = paletteData[25]; + uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); + + int palOffset = 37; + memset(_palette, 0, 256 * 3); + + for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { + if (palFormat == kRobotPalVariable) + palOffset++; + _palette[colorNo * 3 + 0] = paletteData[palOffset++]; + _palette[colorNo * 3 + 1] = paletteData[palOffset++]; + _palette[colorNo * 3 + 2] = paletteData[palOffset++]; } - reset(); + _dirtyPalette = true; + delete[] paletteData; } -void RobotDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); +void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) { + // This is an O(n) operation, as each frame has a different size. + // We need to know the actual frame size to have a constant video size. + uint32 pos = stream->pos(); + + uint16 width = 0, height = 0; + + for (int curFrame = 0; curFrame < _frameCount; curFrame++) { + stream->skip(4); + uint16 frameWidth = stream->readUint16(); + uint16 frameHeight = stream->readUint16(); + if (frameWidth > width) + width = frameWidth; + if (frameHeight > height) + height = frameHeight; + stream->skip(frameSizes[curFrame] - 8); + } + + stream->seek(pos); + + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); } -void RobotDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); +RobotDecoder::RobotAudioTrack::RobotAudioTrack() { + _audioStream = Audio::makeQueuingAudioStream(11025, false); } -#endif +RobotDecoder::RobotAudioTrack::~RobotAudioTrack() { + delete _audioStream; +} + +void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) { + _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); +} + +Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const { + return _audioStream; +} } // End of namespace Sci diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h index e9cefe7d91..ebc3262939 100644 --- a/engines/sci/video/robot_decoder.h +++ b/engines/sci/video/robot_decoder.h @@ -25,84 +25,103 @@ #include "common/rational.h" #include "common/rect.h" -#include "common/stream.h" -#include "common/substream.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "graphics/pixelformat.h" #include "video/video_decoder.h" -namespace Sci { +namespace Audio { +class QueuingAudioStream; +} -#ifdef ENABLE_SCI32 - -struct RobotHeader { - // 6 bytes, identifier bytes - uint16 version; - uint16 audioChunkSize; - uint16 audioSilenceSize; - // 2 bytes, unknown - uint16 frameCount; - uint16 paletteDataSize; - uint16 unkChunkDataSize; - // 5 bytes, unknown - byte hasSound; - // 34 bytes, unknown -}; +namespace Common { +class SeekableSubReadStreamEndian; +} + +namespace Sci { -class RobotDecoder : public Video::FixedRateVideoDecoder { +class RobotDecoder : public Video::VideoDecoder { public: - RobotDecoder(Audio::Mixer *mixer, bool isBigEndian); + RobotDecoder(bool isBigEndian); virtual ~RobotDecoder(); bool loadStream(Common::SeekableReadStream *stream); bool load(GuiResourceId id); void close(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint32 getFrameCount() const { return _header.frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } + void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); } Common::Point getPos() const { return _pos; } protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - - // FixedRateVideoDecoder API - Common::Rational getFrameRate() const { return Common::Rational(60, 10); } - + void readNextPacket(); + private: + class RobotVideoTrack : public FixedRateVideoTrack { + public: + RobotVideoTrack(int frameCount); + ~RobotVideoTrack(); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame() { return _surface; } + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize); + void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes); + Graphics::Surface *getSurface() { return _surface; } + void increaseCurFrame() { _curFrame++; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, 10); } + + private: + int _frameCount; + int _curFrame; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + }; + + class RobotAudioTrack : public AudioTrack { + public: + RobotAudioTrack(); + ~RobotAudioTrack(); + + Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; } + + void queueBuffer(byte *buffer, int size); + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + Audio::QueuingAudioStream *_audioStream; + }; + + struct RobotHeader { + // 6 bytes, identifier bytes + uint16 version; + uint16 audioChunkSize; + uint16 audioSilenceSize; + // 2 bytes, unknown + uint16 frameCount; + uint16 paletteDataSize; + uint16 unkChunkDataSize; + // 5 bytes, unknown + byte hasSound; + // 34 bytes, unknown + } _header; + void readHeaderChunk(); - void readPaletteChunk(uint16 chunkSize); void readFrameSizesChunk(); - void calculateVideoDimensions(); - - void freeData(); - RobotHeader _header; Common::Point _pos; bool _isBigEndian; + uint32 *_frameTotalSize; Common::SeekableSubReadStreamEndian *_fileStream; - - uint16 _width; - uint16 _height; - uint32 *_frameTotalSize; - byte _palette[256 * 3]; - bool _dirtyPalette; - Graphics::Surface *_surface; - Audio::QueuingAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - Audio::Mixer *_mixer; }; -#endif } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp index abd64911a7..a7b6346eca 100644 --- a/engines/sci/video/seq_decoder.cpp +++ b/engines/sci/video/seq_decoder.cpp @@ -41,33 +41,44 @@ enum seqFrameTypes { kSeqFrameDiff = 1 }; -SeqDecoder::SeqDecoder() { - _fileStream = 0; - _surface = 0; - _dirtyPalette = false; +SEQDecoder::SEQDecoder(uint frameDelay) : _frameDelay(frameDelay) { } -SeqDecoder::~SeqDecoder() { +SEQDecoder::~SEQDecoder() { close(); } -bool SeqDecoder::loadStream(Common::SeekableReadStream *stream) { +bool SEQDecoder::loadStream(Common::SeekableReadStream *stream) { close(); + addTrack(new SEQVideoTrack(stream, _frameDelay)); + + return true; +} + +SEQDecoder::SEQVideoTrack::SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay) { + assert(stream); + assert(frameDelay != 0); _fileStream = stream; + _frameDelay = frameDelay; + _curFrame = -1; + _surface = new Graphics::Surface(); _surface->create(SEQ_SCREEN_WIDTH, SEQ_SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8()); _frameCount = _fileStream->readUint16LE(); - // Set palette - int paletteChunkSize = _fileStream->readUint32LE(); - readPaletteChunk(paletteChunkSize); + // Set initial palette + readPaletteChunk(_fileStream->readUint32LE()); +} - return true; +SEQDecoder::SEQVideoTrack::~SEQVideoTrack() { + delete _fileStream; + _surface->free(); + delete _surface; } -void SeqDecoder::readPaletteChunk(uint16 chunkSize) { +void SEQDecoder::SEQVideoTrack::readPaletteChunk(uint16 chunkSize) { byte *paletteData = new byte[chunkSize]; _fileStream->read(paletteData, chunkSize); @@ -91,23 +102,7 @@ void SeqDecoder::readPaletteChunk(uint16 chunkSize) { delete[] paletteData; } -void SeqDecoder::close() { - if (!_fileStream) - return; - - _frameDelay = 0; - - delete _fileStream; - _fileStream = 0; - - _surface->free(); - delete _surface; - _surface = 0; - - reset(); -} - -const Graphics::Surface *SeqDecoder::decodeNextFrame() { +const Graphics::Surface *SEQDecoder::SEQVideoTrack::decodeNextFrame() { int16 frameWidth = _fileStream->readUint16LE(); int16 frameHeight = _fileStream->readUint16LE(); int16 frameLeft = _fileStream->readUint16LE(); @@ -142,9 +137,6 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { delete[] buf; } - if (_curFrame == -1) - _startTime = g_system->getMillis(); - _curFrame++; return _surface; } @@ -159,7 +151,7 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { } \ memcpy(dest + writeRow * SEQ_SCREEN_WIDTH + writeCol, litData + litPos, n); -bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { +bool SEQDecoder::SEQVideoTrack::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { int writeRow = 0; int writeCol = left; int litPos = 0; @@ -237,4 +229,9 @@ bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litS return true; } +const byte *SEQDecoder::SEQVideoTrack::getPalette() const { + _dirtyPalette = false; + return _palette; +} + } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.h b/engines/sci/video/seq_decoder.h index 800a3c9024..890f349feb 100644 --- a/engines/sci/video/seq_decoder.h +++ b/engines/sci/video/seq_decoder.h @@ -40,44 +40,49 @@ namespace Sci { /** * Implementation of the Sierra SEQ decoder, used in KQ6 DOS floppy/CD and GK1 DOS */ -class SeqDecoder : public Video::FixedRateVideoDecoder { +class SEQDecoder : public Video::VideoDecoder { public: - SeqDecoder(); - virtual ~SeqDecoder(); + SEQDecoder(uint frameDelay); + virtual ~SEQDecoder(); bool loadStream(Common::SeekableReadStream *stream); - void close(); - - void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; } - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } - uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } - uint32 getFrameCount() const { return _frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } - -protected: - Common::Rational getFrameRate() const { assert(_frameDelay); return Common::Rational(60, _frameDelay); } private: - enum { - SEQ_SCREEN_WIDTH = 320, - SEQ_SCREEN_HEIGHT = 200 + class SEQVideoTrack : public FixedRateVideoTrack { + public: + SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay); + ~SEQVideoTrack(); + + uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } + uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const; + bool hasDirtyPalette() const { return _dirtyPalette; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, _frameDelay); } + + private: + enum { + SEQ_SCREEN_WIDTH = 320, + SEQ_SCREEN_HEIGHT = 200 + }; + + void readPaletteChunk(uint16 chunkSize); + bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); + + Common::SeekableReadStream *_fileStream; + int _curFrame, _frameCount; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + uint _frameDelay; }; - void readPaletteChunk(uint16 chunkSize); - bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); - - uint16 _width, _height; - uint16 _frameDelay; - Common::SeekableReadStream *_fileStream; - byte _palette[256 * 3]; - bool _dirtyPalette; - uint32 _frameCount; - Graphics::Surface *_surface; + uint _frameDelay; }; } // End of namespace Sci |