SCI: Rewrite RobotDecoder to use the AdvancedVideoDecoder API

This commit is contained in:
Matthew Hoops 2012-07-22 23:17:36 -04:00
parent 0f0c6f9354
commit d4231fda1c
5 changed files with 298 additions and 259 deletions

View file

@ -251,25 +251,25 @@ void Console::postEnter() {
if (_videoFile.hasSuffix(".seq")) { if (_videoFile.hasSuffix(".seq")) {
videoDecoder = new SEQDecoder(_videoFrameDelay); videoDecoder = new SEQDecoder(_videoFrameDelay);
((Video::AdvancedVideoDecoder *)videoDecoder)->start(); // TODO: Remove after new API is complete
#ifdef ENABLE_SCI32 #ifdef ENABLE_SCI32
} else if (_videoFile.hasSuffix(".vmd")) { } else if (_videoFile.hasSuffix(".vmd")) {
videoDecoder = new Video::VMDDecoder(g_system->getMixer()); videoDecoder = new Video::VMDDecoder(g_system->getMixer());
} else if (_videoFile.hasSuffix(".rbt")) { } 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")) { } else if (_videoFile.hasSuffix(".duk")) {
duckMode = true; duckMode = true;
videoDecoder = new Video::AVIDecoder(); videoDecoder = new Video::AVIDecoder();
((Video::AdvancedVideoDecoder *)videoDecoder)->start();
#endif #endif
} else if (_videoFile.hasSuffix(".avi")) { } else if (_videoFile.hasSuffix(".avi")) {
videoDecoder = new Video::AVIDecoder(); videoDecoder = new Video::AVIDecoder();
((Video::AdvancedVideoDecoder *)videoDecoder)->start();
} else { } else {
warning("Unrecognized video type"); warning("Unrecognized video type");
} }
if (videoDecoder && videoDecoder->loadFile(_videoFile)) { if (videoDecoder && videoDecoder->loadFile(_videoFile)) {
if (!_videoFile.hasSuffix(".vmd")) // TODO: Remove after new API is complete
((Video::AdvancedVideoDecoder *)videoDecoder)->start();
_engine->_gfxCursor->kernelHide(); _engine->_gfxCursor->kernelHide();
#ifdef ENABLE_SCI32 #ifdef ENABLE_SCI32

View file

@ -254,6 +254,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
int16 y = argv[5].toUint16(); 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); 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->load(id);
g_sci->_robotDecoder->start();
g_sci->_robotDecoder->setPos(x, y); g_sci->_robotDecoder->setPos(x, y);
} }
break; break;
@ -269,13 +270,13 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
warning("kRobot(%d)", subop); warning("kRobot(%d)", subop);
break; break;
case 8: // sync case 8: // sync
//if (false) { // debug: automatically skip all robot videos //if (true) { // debug: automatically skip all robot videos
if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) { if (g_sci->_robotDecoder->endOfVideo()) {
writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
} else {
g_sci->_robotDecoder->close(); g_sci->_robotDecoder->close();
// Signal the engine scripts that the video is done // Signal the engine scripts that the video is done
writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);
} else {
writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
} }
break; break;
default: default:

View file

@ -632,7 +632,7 @@ void SciEngine::initGraphics() {
_gfxPaint = _gfxPaint32; _gfxPaint = _gfxPaint32;
_gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen); _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen);
_gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxText32); _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); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32);
} else { } else {
#endif #endif

View file

@ -22,11 +22,13 @@
#include "common/archive.h" #include "common/archive.h"
#include "common/stream.h" #include "common/stream.h"
#include "common/substream.h"
#include "common/system.h" #include "common/system.h"
#include "common/textconsole.h" #include "common/textconsole.h"
#include "common/util.h" #include "common/util.h"
#include "graphics/surface.h" #include "graphics/surface.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h" #include "audio/decoders/raw.h"
#include "sci/resource.h" #include "sci/resource.h"
@ -63,29 +65,49 @@ namespace Sci {
// our graphics engine, it looks just like a part of the room. A RBT can move // our graphics engine, it looks just like a part of the room. A RBT can move
// around the screen and go behind other objects. (...) // around the screen and go behind other objects. (...)
#ifdef ENABLE_SCI32 enum RobotPalTypes {
enum robotPalTypes {
kRobotPalVariable = 0, kRobotPalVariable = 0,
kRobotPalConstant = 1 kRobotPalConstant = 1
}; };
RobotDecoder::RobotDecoder(Audio::Mixer *mixer, bool isBigEndian) { RobotDecoder::RobotDecoder(bool isBigEndian) {
_surface = 0;
_width = 0;
_height = 0;
_fileStream = 0; _fileStream = 0;
_audioStream = 0;
_dirtyPalette = false;
_pos = Common::Point(0, 0); _pos = Common::Point(0, 0);
_mixer = mixer;
_isBigEndian = isBigEndian; _isBigEndian = isBigEndian;
_frameTotalSize = 0;
} }
RobotDecoder::~RobotDecoder() { RobotDecoder::~RobotDecoder() {
close(); close();
} }
bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
_fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES);
readHeaderChunk();
// There are several versions of robot files, ranging from 3 to 6.
// v3: no known examples
// v4: PQ:SWAT demo
// v5: SCI2.1 and SCI3 games
// v6: SCI3 games
if (_header.version < 4 || _header.version > 6)
error("Unknown robot version: %d", _header.version);
RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount);
addTrack(videoTrack);
if (_header.hasSound)
addTrack(new RobotAudioTrack());
videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize);
readFrameSizesChunk();
videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize);
return true;
}
bool RobotDecoder::load(GuiResourceId id) { bool RobotDecoder::load(GuiResourceId id) {
// TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - // 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 // its drawn at odd coordinates. SV can't play it either (along with some
@ -109,33 +131,123 @@ bool RobotDecoder::load(GuiResourceId id) {
return loadStream(stream); return loadStream(stream);
} }
bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { void RobotDecoder::close() {
close(); AdvancedVideoDecoder::close();
_fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES); delete _fileStream;
_surface = new Graphics::Surface(); _fileStream = 0;
readHeaderChunk(); delete[] _frameTotalSize;
_frameTotalSize = 0;
}
// There are several versions of robot files, ranging from 3 to 6. void RobotDecoder::readNextPacket() {
// v3: no known examples // Get our track
// v4: PQ:SWAT demo RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0);
// v5: SCI2.1 and SCI3 games videoTrack->increaseCurFrame();
// v6: SCI3 games Graphics::Surface *surface = videoTrack->getSurface();
if (_header.version < 4 || _header.version > 6)
error("Unknown robot version: %d", _header.version);
if (_header.hasSound) { if (videoTrack->endOfTrack())
_audioStream = Audio::makeQueuingAudioStream(11025, false); return;
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance());
// Read frame image header (24 bytes)
_fileStream->skip(3);
byte frameScale = _fileStream->readByte();
uint16 frameWidth = _fileStream->readUint16();
uint16 frameHeight = _fileStream->readUint16();
_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, 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 > surface->w)
frameX = surface->w - frameWidth;
assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h);
DecompressorLZS lzs;
byte *decompressedFrame = new byte[decompressedSize];
byte *outPtr = decompressedFrame;
if (_header.version == 4) {
// v4 has just the one fragment, it seems, and ignores the fragment count
Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedSize);
lzs.unpack(&fragmentStream, outPtr, compressedSize, decompressedSize);
} else {
for (uint16 i = 0; i < frameFragments; ++i) {
uint32 compressedFragmentSize = _fileStream->readUint32();
uint32 decompressedFragmentSize = _fileStream->readUint32();
uint16 compressionType = _fileStream->readUint16();
if (compressionType == 0) {
Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedFragmentSize);
lzs.unpack(&fragmentStream, outPtr, compressedFragmentSize, decompressedFragmentSize);
} else if (compressionType == 2) { // untested
_fileStream->read(outPtr, compressedFragmentSize);
} else {
error("Unknown frame compression found: %d", compressionType);
}
outPtr += decompressedFragmentSize;
}
} }
readPaletteChunk(_header.paletteDataSize); // Copy over the decompressed frame
readFrameSizesChunk(); byte *inFrame = decompressedFrame;
calculateVideoDimensions(); byte *outFrame = (byte *)surface->pixels;
_surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
return true; // Black out the surface
memset(outFrame, 0, surface->w * surface->h);
// Move to the correct y coordinate
outFrame += surface->w * frameY;
for (uint16 y = 0; y < scaledHeight; y++) {
memcpy(outFrame + frameX, inFrame, frameWidth);
inFrame += frameWidth;
outFrame += surface->w;
}
delete[] decompressedFrame;
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)
#if 0
// Read frame audio header (14 bytes)
_fileStream->skip(2); // buffer position
_fileStream->skip(2); // unknown (usually 1)
_fileStream->skip(2); /*uint16 audioChunkSize = _fileStream->readUint16() + 8;*/
_fileStream->skip(2);
#endif
// Queue the next audio frame
// FIXME: For some reason, there are audio hiccups/gaps
if (_header.hasSound) {
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);
}
} }
void RobotDecoder::readHeaderChunk() { void RobotDecoder::readHeaderChunk() {
@ -160,31 +272,6 @@ void RobotDecoder::readHeaderChunk() {
_fileStream->skip(_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);
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++];
}
_dirtyPalette = true;
delete[] paletteData;
}
void RobotDecoder::readFrameSizesChunk() { void RobotDecoder::readFrameSizesChunk() {
// The robot video file contains 2 tables, with one entry for each frame: // 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 size of the image in each video frame
@ -230,158 +317,90 @@ void RobotDecoder::readFrameSizesChunk() {
_fileStream->seek((curPos & ~0x7ff) + 2048); _fileStream->seek((curPos & ~0x7ff) + 2048);
} }
void RobotDecoder::calculateVideoDimensions() { RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) {
// This is an O(n) operation, as each frame has a different size. _surface = new Graphics::Surface();
// We need to know the actual frame size to have a constant video size. _curFrame = -1;
uint32 pos = _fileStream->pos(); _dirtyPalette = false;
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);
}
_fileStream->seek(pos);
} }
const Graphics::Surface *RobotDecoder::decodeNextFrame() { RobotDecoder::RobotVideoTrack::~RobotVideoTrack() {
// Read frame image header (24 bytes)
_fileStream->skip(3);
byte frameScale = _fileStream->readByte();
uint16 frameWidth = _fileStream->readUint16();
uint16 frameHeight = _fileStream->readUint16();
_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);
// 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);
DecompressorLZS lzs;
byte *decompressedFrame = new byte[decompressedSize];
byte *outPtr = decompressedFrame;
if (_header.version == 4) {
// v4 has just the one fragment, it seems, and ignores the fragment count
Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedSize);
lzs.unpack(&fragmentStream, outPtr, compressedSize, decompressedSize);
} else {
for (uint16 i = 0; i < frameFragments; ++i) {
uint32 compressedFragmentSize = _fileStream->readUint32();
uint32 decompressedFragmentSize = _fileStream->readUint32();
uint16 compressionType = _fileStream->readUint16();
if (compressionType == 0) {
Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedFragmentSize);
lzs.unpack(&fragmentStream, outPtr, compressedFragmentSize, decompressedFragmentSize);
} else if (compressionType == 2) { // untested
_fileStream->read(outPtr, compressedFragmentSize);
} else {
error("Unknown frame compression found: %d", compressionType);
}
outPtr += decompressedFragmentSize;
}
}
// Copy over the decompressed frame
byte *inFrame = decompressedFrame;
byte *outFrame = (byte *)_surface->pixels;
// Black out the surface
memset(outFrame, 0, _width * _height);
// Move to the correct y coordinate
outFrame += _width * frameY;
for (uint16 y = 0; y < scaledHeight; y++) {
memcpy(outFrame + frameX, inFrame, frameWidth);
inFrame += frameWidth;
outFrame += _width;
}
delete[] decompressedFrame;
// +1 because we start with frame number -1
uint32 audioChunkSize = _frameTotalSize[_curFrame + 1] - (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)
#if 0
// Read frame audio header (14 bytes)
_fileStream->skip(2); // buffer position
_fileStream->skip(2); // unknown (usually 1)
_fileStream->skip(2); /*uint16 audioChunkSize = _fileStream->readUint16() + 8;*/
_fileStream->skip(2);
#endif
// 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);
} else {
_fileStream->skip(audioChunkSize);
}
if (_curFrame == -1)
_startTime = g_system->getMillis();
_curFrame++;
return _surface;
}
void RobotDecoder::close() {
if (!_fileStream)
return;
delete _fileStream;
_fileStream = 0;
_surface->free(); _surface->free();
delete _surface; delete _surface;
_surface = 0; }
if (_header.hasSound) { uint16 RobotDecoder::RobotVideoTrack::getWidth() const {
_mixer->stopHandle(_audioHandle); return _surface->w;
//delete _audioStream; _audioStream = 0; }
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() { void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) {
if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) // This is an O(n) operation, as each frame has a different size.
g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); // 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() { RobotDecoder::RobotAudioTrack::RobotAudioTrack() {
if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) _audioStream = Audio::makeQueuingAudioStream(11025, false);
g_system->getMixer()->setChannelBalance(_audioHandle, getBalance());
} }
#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 } // End of namespace Sci

View file

@ -25,84 +25,103 @@
#include "common/rational.h" #include "common/rational.h"
#include "common/rect.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" #include "video/video_decoder.h"
namespace Audio {
class QueuingAudioStream;
}
namespace Common {
class SeekableSubReadStreamEndian;
}
namespace Sci { namespace Sci {
#ifdef ENABLE_SCI32 class RobotDecoder : public Video::AdvancedVideoDecoder {
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
};
class RobotDecoder : public Video::FixedRateVideoDecoder {
public: public:
RobotDecoder(Audio::Mixer *mixer, bool isBigEndian); RobotDecoder(bool isBigEndian);
virtual ~RobotDecoder(); virtual ~RobotDecoder();
bool loadStream(Common::SeekableReadStream *stream); bool loadStream(Common::SeekableReadStream *stream);
bool load(GuiResourceId id); bool load(GuiResourceId id);
void close(); 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); } void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); }
Common::Point getPos() const { return _pos; } Common::Point getPos() const { return _pos; }
protected: protected:
// VideoDecoder API void readNextPacket();
void updateVolume();
void updateBalance();
// FixedRateVideoDecoder API
Common::Rational getFrameRate() const { return Common::Rational(60, 10); }
private: 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 readHeaderChunk();
void readPaletteChunk(uint16 chunkSize);
void readFrameSizesChunk(); void readFrameSizesChunk();
void calculateVideoDimensions();
void freeData();
RobotHeader _header;
Common::Point _pos; Common::Point _pos;
bool _isBigEndian; bool _isBigEndian;
uint32 *_frameTotalSize;
Common::SeekableSubReadStreamEndian *_fileStream; 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 } // End of namespace Sci