From 6f9ac84f77f140c8008ffec0e57fcf2ddd17a10e Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Mon, 7 Feb 2011 12:24:09 +0000 Subject: SCI: Converted the robot decoder into a regular video decoder, and decoupled it from the SciEngine class - Robot videos are now shown in frameOut(), like they should, and kRobot(sync) is only used for syncing with the game scripts - Hooked video playing into the "play_video" console command svn-id: r55801 --- engines/sci/graphics/frameout.cpp | 34 +++- engines/sci/graphics/paint32.cpp | 10 -- engines/sci/graphics/paint32.h | 2 - engines/sci/graphics/robot.cpp | 329 -------------------------------------- engines/sci/graphics/robot.h | 98 ------------ 5 files changed, 32 insertions(+), 441 deletions(-) delete mode 100644 engines/sci/graphics/robot.cpp delete mode 100644 engines/sci/graphics/robot.h (limited to 'engines/sci/graphics') diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 8af41ddf57..5b690f289a 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -40,8 +40,8 @@ #include "sci/graphics/paint32.h" #include "sci/graphics/palette.h" #include "sci/graphics/picture.h" -#include "sci/graphics/robot.h" #include "sci/graphics/frameout.h" +#include "sci/video/robot_decoder.h" namespace Sci { @@ -339,8 +339,38 @@ static int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font) { } void GfxFrameout::kernelFrameout() { - if (g_sci->_gfxRobot->isPlaying()) + if (g_sci->_robotDecoder->isVideoLoaded()) { + bool skipVideo = false; + RobotDecoder *videoDecoder = g_sci->_robotDecoder; + uint16 x = videoDecoder->getPos().x; + uint16 y = videoDecoder->getPos().y; + + if (videoDecoder->hasDirtyPalette()) + videoDecoder->setSystemPalette(); + + while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { + if (videoDecoder->needsUpdate()) { + const Graphics::Surface *frame = videoDecoder->decodeNextFrame(); + if (frame) { + g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + + if (videoDecoder->hasDirtyPalette()) + videoDecoder->setSystemPalette(); + + g_system->updateScreen(); + } + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) { + if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) + skipVideo = true; + } + + g_system->delayMillis(10); + } return; + } _palette->palVaryUpdate(); diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp index 2fb4b832e5..aa3bf8dfb3 100644 --- a/engines/sci/graphics/paint32.cpp +++ b/engines/sci/graphics/paint32.cpp @@ -39,7 +39,6 @@ #include "sci/graphics/view.h" #include "sci/graphics/screen.h" #include "sci/graphics/palette.h" -#include "sci/graphics/robot.h" namespace Sci { @@ -81,13 +80,4 @@ void GfxPaint32::kernelGraphDrawLine(Common::Point startPoint, Common::Point end _screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control); } -void GfxPaint32::debugDrawRobot(GuiResourceId robotId) { - GfxRobot *test = new GfxRobot(g_sci->getResMan(), _screen, _palette); - test->init(robotId, 0, 0); - while (test->getCurFrame() + 1 < test->getFrameCount()) { - test->processNextFrame(); - } - delete test; -} - } // End of namespace Sci diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h index a048d7f307..3fa9d11d9b 100644 --- a/engines/sci/graphics/paint32.h +++ b/engines/sci/graphics/paint32.h @@ -48,8 +48,6 @@ public: void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle); void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control); - void debugDrawRobot(GuiResourceId robotId); - private: ResourceManager *_resMan; SegManager *_segMan; diff --git a/engines/sci/graphics/robot.cpp b/engines/sci/graphics/robot.cpp deleted file mode 100644 index 464e32fad1..0000000000 --- a/engines/sci/graphics/robot.cpp +++ /dev/null @@ -1,329 +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$ - * - */ - -// fopen() and friends so we can dump frames and stuff to disk, for debugging -// #define FORBIDDEN_SYMBOL_ALLOW_ALL - -#include "sci/sci.h" -#include "sci/engine/state.h" -#include "sci/graphics/screen.h" -#include "sci/graphics/robot.h" -#include "sci/graphics/palette.h" -#include "sci/sound/audio.h" - -#include "graphics/surface.h" - -#include "sound/audiostream.h" -#include "sound/mixer.h" - -#include "common/archive.h" -#include "common/system.h" -#include "common/stream.h" -#include "common/substream.h" - -namespace Sci { - -// TODO: -// - Positioning -// - Proper handling of frame scaling - scaled frames look squashed -// (probably because both dimensions should be scaled) -// - Timing - the arbitrary 100ms delay between each frame is not quite right -// - Proper handling of sound chunks in some cases, so that the frame size -// table can be ignored (it's only used to determine the correct sound chunk -// size at the moment, cause it can be wrong in some cases) -// - Fix audio "hiccups" - probably data that shouldn't be in the audio frames - - -// Some non technical information on robot files, from an interview with -// Greg Tomko-Pavia of Sierra On-Line -// Taken from http://anthonylarme.tripod.com/phantas/phintgtp.html -// -// (...) What we needed was a way of playing video, but have it blend into -// normal room art instead of occupying its own rectangular area. Room art -// consists of a background pic overlaid with various animating cels -// (traditional lingo: sprites). The cels each have a priority that determines -// who is on top and who is behind in the drawing order. Cels are read from -// *.v56 files (another proprietary format). A Robot is video frames with -// transparent background including priority and x,y information. Thus, it is -// like a cel, except it comes from an RBT - not a v56. Because it blends into -// 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 - -GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette) - : _resMan(resMan), _screen(screen), _palette(palette), _outputBuffer(0), - _outputBufferSize(0), _audioStream(0), _frameTotalSize(0), _robotFile(0) { - _x = _y = 0; -} - -GfxRobot::~GfxRobot() { - freeData(); -} - -void GfxRobot::init(GuiResourceId resourceId, uint16 x, uint16 y) { - _x = x; - _y = y; - _curFrame = 0; - - Common::String fileName = Common::String::format("%d.rbt", resourceId); - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - - if (!stream) { - warning("Unable to open robot file %s", fileName.c_str()); - return; - } - - _robotFile = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), - g_sci->getPlatform() == Common::kPlatformMacintosh, 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); - - _frameTotalSize = new uint32[_header.frameCount]; - - if (_header.hasSound) { - _audioStream = Audio::makeQueuingAudioStream(11025, false); - g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream); - } - - readPaletteChunk(); - readFrameSizesChunk(); - - debug("Robot %d, %d frames, sound: %s\n", resourceId, _header.frameCount, _header.hasSound ? "yes" : "no"); -} - -void GfxRobot::readHeaderChunk() { - // Header (60 bytes) - _robotFile->skip(6); - _header.version = _robotFile->readUint16(); - _header.audioChunkSize = _robotFile->readUint16(); - _header.audioSilenceSize = _robotFile->readUint16(); - _robotFile->skip(2); - _header.frameCount = _robotFile->readUint16(); - _header.paletteDataSize = _robotFile->readUint16(); - _header.unkChunkDataSize = _robotFile->readUint16(); - _robotFile->skip(5); - _header.hasSound = _robotFile->readByte(); - _robotFile->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) - _robotFile->skip(_header.unkChunkDataSize); -} - -void GfxRobot::readPaletteChunk() { - byte *paletteChunk = new byte[_header.paletteDataSize]; - _robotFile->read(paletteChunk, _header.paletteDataSize); - - int startIndex = paletteChunk[25]; - int colorCount = READ_SCI11ENDIAN_UINT16(paletteChunk + 29); - - if (colorCount > 256) - error("Invalid color count: %d", colorCount); - - Palette resourcePal; - _palette->createFromData(paletteChunk, _header.paletteDataSize, &resourcePal); - delete[] paletteChunk; - - byte robotPal[256 * 4]; - - for (int i = 0; i < 256; ++i) { - _savedPal[i * 4 + 0] = _palette->_sysPalette.colors[i].r; - _savedPal[i * 4 + 1] = _palette->_sysPalette.colors[i].g; - _savedPal[i * 4 + 2] = _palette->_sysPalette.colors[i].b; - _savedPal[i * 4 + 3] = 0; - } - - memcpy(robotPal, _savedPal, sizeof(_savedPal)); - - for (int i = 0; i < colorCount; ++i) { - int index = i + startIndex; - robotPal[index * 4 + 0] = resourcePal.colors[index].r; - robotPal[index * 4 + 1] = resourcePal.colors[index].g; - robotPal[index * 4 + 2] = resourcePal.colors[index].b; - robotPal[index * 4 + 3] = 0; - } - - g_system->setPalette(robotPal, 0, 256); -} - - -void GfxRobot::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. - - // 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; - _robotFile->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 - _robotFile->skip(_header.frameCount * 2); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _robotFile->readUint16(); - break; - case 6: // sizes are 32-bit integers - // Skip table with frame image sizes, as we don't need it - _robotFile->skip(_header.frameCount * 4); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _robotFile->readUint32(); - break; - default: - error("Can't yet handle index table for robot version %d", _header.version); - } -#endif - - // 2 more unknown tables - _robotFile->skip(1024 + 512); - - // Pad to nearest 2 kilobytes - uint32 curPos = _robotFile->pos(); - if (curPos & 0x7ff) - _robotFile->seek((curPos & ~0x7ff) + 2048); -} - -void GfxRobot::processNextFrame() { - // Make sure that we haven't reached the end of the video already - if (_curFrame == _header.frameCount) - return; - - // Read frame image header (24 bytes) - _robotFile->skip(3); - byte frameScale = _robotFile->readByte(); - uint16 frameWidth = _robotFile->readUint16(); - uint16 frameHeight = _robotFile->readUint16(); - _robotFile->skip(8); // x, y, width and height of the frame - uint16 compressedSize = _robotFile->readUint16(); - uint16 frameFragments = _robotFile->readUint16(); - _robotFile->skip(4); // unknown - - uint32 decompressedSize = frameWidth * (frameHeight * frameScale / 100); - - // Reallocate the output buffer, if its size has increased - if (decompressedSize > _outputBufferSize) { - delete[] _outputBuffer; - _outputBuffer = new byte[decompressedSize]; - } - - _outputBufferSize = decompressedSize; - - DecompressorLZS lzs; - - if (_header.version == 4) { - // v4 has just the one fragment, it seems, and ignores the fragment count - Common::SeekableSubReadStream fragmentStream(_robotFile, _robotFile->pos(), _robotFile->pos() + compressedSize); - lzs.unpack(&fragmentStream, _outputBuffer, compressedSize, decompressedSize); - } else { - byte *outPtr = _outputBuffer; - - for (uint16 i = 0; i < frameFragments; ++i) { - uint32 compressedFragmentSize = _robotFile->readUint32(); - uint32 decompressedFragmentSize = _robotFile->readUint32(); - uint16 compressionType = _robotFile->readUint16(); - - if (compressionType == 0) { - Common::SeekableSubReadStream fragmentStream(_robotFile, _robotFile->pos(), _robotFile->pos() + compressedFragmentSize); - lzs.unpack(&fragmentStream, outPtr, compressedFragmentSize, decompressedFragmentSize); - } else if (compressionType == 2) { // untested - _robotFile->read(outPtr, compressedFragmentSize); - } else { - error("Unknown frame compression found: %d", compressionType); - } - - outPtr += decompressedFragmentSize; - } - } - - uint32 audioChunkSize = _frameTotalSize[_curFrame] - (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) - _robotFile->skip(2); // buffer position - _robotFile->skip(2); // unknown (usually 1) - _robotFile->skip(2); /*uint16 audioChunkSize = _robotFile->readUint16() + 8;*/ - _robotFile->skip(2); -#endif - - // Queue the next audio frame - // FIXME: For some reason, there are audio hiccups/gaps - if (_header.hasSound) { - _robotFile->skip(8); // header - _audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_robotFile, audioChunkSize - 8), - (audioChunkSize - 8) * 2, DisposeAfterUse::NO, - Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); - } else { - _robotFile->skip(audioChunkSize); - } - - // Show frame - g_system->copyRectToScreen(_outputBuffer, frameWidth, _x, _y, frameWidth, frameHeight * frameScale / 100); - g_system->updateScreen(); - g_system->delayMillis(100); // TODO: This isn't quite right - - _curFrame++; - - if (_curFrame == _header.frameCount) { - // End of robot video, restore palette - g_system->setPalette(_savedPal, 0, 256); - freeData(); - } -} - -void GfxRobot::freeData() { - if (_header.hasSound) { - g_system->getMixer()->stopHandle(_audioHandle); - //delete _audioStream; _audioStream = 0; - } - delete[] _frameTotalSize; _frameTotalSize = 0; - delete[] _outputBuffer; _outputBuffer = 0; - _outputBufferSize = 0; - delete _robotFile; _robotFile = 0; -} - -#endif - -} // End of namespace Sci diff --git a/engines/sci/graphics/robot.h b/engines/sci/graphics/robot.h deleted file mode 100644 index ffb4d0e6e7..0000000000 --- a/engines/sci/graphics/robot.h +++ /dev/null @@ -1,98 +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$ - * - */ - -#ifndef SCI_GRAPHICS_ROBOT_H -#define SCI_GRAPHICS_ROBOT_H - -#include "sound/audiostream.h" -#include "sound/mixer.h" -#include "sound/decoders/raw.h" - -namespace Common { - class SeekableSubReadStreamEndian; -} - -namespace Sci { - -#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 -}; - -class GfxRobot { -public: - GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette); - ~GfxRobot(); - - void init(GuiResourceId resourceId, uint16 x, uint16 y); - void processNextFrame(); - uint16 getCurFrame() { return _curFrame; } - uint16 getFrameCount() { return _header.frameCount; } - bool isPlaying() { return _robotFile != 0; } - void playAudio(); - -private: - void readHeaderChunk(); - void readPaletteChunk(); - void readFrameSizesChunk(); - - void freeData(); - - ResourceManager *_resMan; - GfxScreen *_screen; - GfxPalette *_palette; - - byte _savedPal[256 * 4]; - - Common::SeekableSubReadStreamEndian *_robotFile; - Audio::QueuingAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - - RobotHeader _header; - - uint16 _x; - uint16 _y; - uint16 _curFrame; - uint32 *_frameTotalSize; - - byte *_outputBuffer; - uint32 _outputBufferSize; -}; -#endif - -} // End of namespace Sci - -#endif -- cgit v1.2.3