aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics
diff options
context:
space:
mode:
authorFilippos Karapetis2011-02-04 17:51:59 +0000
committerFilippos Karapetis2011-02-04 17:51:59 +0000
commit512bcf8b907edd69ed7bed2c988f6aa0b8cdfe69 (patch)
treee16114ba871f957eff4cd0082c84de77c324fabb /engines/sci/graphics
parent94b6d23d44ee9d48b4e4631f7d38d16c012bf56b (diff)
downloadscummvm-rg350-512bcf8b907edd69ed7bed2c988f6aa0b8cdfe69.tar.gz
scummvm-rg350-512bcf8b907edd69ed7bed2c988f6aa0b8cdfe69.tar.bz2
scummvm-rg350-512bcf8b907edd69ed7bed2c988f6aa0b8cdfe69.zip
SCI: Rewrote the robot playing code in a way similar to other video decoders
- The code now streams videos instead of loading them in memory, without utilizing seeking - Removed the sound-related robot code from the graphics classes - Started implementing the code for the sound in robot videos (still not finished) svn-id: r55772
Diffstat (limited to 'engines/sci/graphics')
-rw-r--r--engines/sci/graphics/paint32.cpp10
-rw-r--r--engines/sci/graphics/paint32.h1
-rw-r--r--engines/sci/graphics/robot.cpp356
-rw-r--r--engines/sci/graphics/robot.h59
4 files changed, 178 insertions, 248 deletions
diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp
index 974efd371a..2fb4b832e5 100644
--- a/engines/sci/graphics/paint32.cpp
+++ b/engines/sci/graphics/paint32.cpp
@@ -85,17 +85,9 @@ 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->drawNextFrame();
+ test->processNextFrame();
}
delete test;
}
-void GfxPaint32::debugPlayRobotAudio(GuiResourceId robotId) {
- GfxRobot *test = new GfxRobot(g_sci->getResMan(), _screen, _palette);
- test->init(robotId, 0, 0);
- test->playAudio();
- delete test;
-}
-
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h
index 7fbaf4594e..a048d7f307 100644
--- a/engines/sci/graphics/paint32.h
+++ b/engines/sci/graphics/paint32.h
@@ -49,7 +49,6 @@ public:
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
void debugDrawRobot(GuiResourceId robotId);
- void debugPlayRobotAudio(GuiResourceId robotId);
private:
ResourceManager *_resMan;
diff --git a/engines/sci/graphics/robot.cpp b/engines/sci/graphics/robot.cpp
index f45d3cb552..0d2142ebde 100644
--- a/engines/sci/graphics/robot.cpp
+++ b/engines/sci/graphics/robot.cpp
@@ -40,7 +40,8 @@
#include "common/file.h"
#include "common/system.h"
-#include "common/memstream.h"
+#include "common/stream.h"
+#include "common/substream.h"
namespace Sci {
@@ -60,9 +61,10 @@ namespace Sci {
// 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), _resourceData(0),
- _imageStart(0), _audioStart(0), _audioLen(0), _outputBuffer(0), _outputBufferSize(0) {
+ : _resMan(resMan), _screen(screen), _palette(palette), _outputBuffer(0),
+ _outputBufferSize(0), _audioStream(0) {
_resourceId = -1;
_x = _y = 0;
}
@@ -72,151 +74,91 @@ GfxRobot::~GfxRobot() {
}
void GfxRobot::init(GuiResourceId resourceId, uint16 x, uint16 y) {
- char fileName[10];
- uint32 fileSize;
-
- // resourceId = 1305; // debug
-
_resourceId = resourceId;
+ //_resourceId = 1305; // debug
_x = x;
_y = y;
_curFrame = 0;
+
+ char fileName[10];
sprintf(fileName, "%d.rbt", _resourceId);
- Common::File robotFile;
- if (robotFile.open(fileName)) {
- _resourceData = new byte[robotFile.size()];
- robotFile.read(_resourceData, robotFile.size());
- fileSize = robotFile.size();
- robotFile.close();
- } else {
+ if (!_robotFile.open(fileName)) {
warning("Unable to open robot file %s", fileName);
return;
}
+ readHeaderChunk();
+
// There are several versions of robot files, ranging from 3 to 6.
// v3: no known examples
// v4: PQ:SWAT
// v5: SCI2.1 and SCI3 games
// v6: SCI3 games
- _version = _resourceData[6];
-
- // Currently, we only support robot version 5. Robot version
- _frameCount = READ_LE_UINT16(_resourceData + 14);
-
- // There is another value in the header, at offset 0x12, which
- // equals this value plus 14 (audio header size) in the cases
- // I've seen so far. Dunno which one to use, really.
- _audioSize = READ_LE_UINT16(_resourceData + 60);
-
- //_frameSize = READ_LE_UINT32(_resourceData + 34);
- _hasSound = (_resourceData[25] != 0);
-
- _palOffset = 60;
-
- // Some robot files have sound, which doesn't start from frame 0
- // (e.g. Phantasmagoria, robot 1305)
- // Yes, strangely, the word at offset 10 is non-zero if it DOESN'T have a preload
- // in that case, the word is equal to that value which would otherwise be stored
- // in the audio header (i.e. _audioSize above)
- if (_hasSound && READ_LE_UINT16(_resourceData + 10) == 0)
- _palOffset += READ_LE_UINT32(_resourceData + 60) + 14;
-
- switch (_version) {
- case 3:
- // Unsupported, and there doesn't seem to be any game that actually
- // uses this, thus error out so that we find out where this is used
- error("Unknown robot version: %d", _version);
- break;
+ switch (_header.version) {
case 4: // used in PQ:SWAT
// Unsupported
- // TODO: Add support for this version
warning("TODO: add support for v4 robot videos");
- _curFrame = _frameCount; // jump to the last frame
+ _curFrame = _header.frameCount; // jump to the last frame
return;
- case 5: // used in most SCI2.1 games
+ case 5: // used in most SCI2.1 games and in some SCI3 robots
+ case 6: // used in SCI3 games
// Supported
break;
- case 6: // introduced in SCI3
- // Unsupported
- // TODO: Add support for this version
- warning("TODO: add support for v6 robot videos");
- break;
default:
// Unsupported, error out so that we find out where this is used
- error("Unknown robot version: %d", _version);
+ error("Unknown robot version: %d", _header.version);
}
- getFrameOffsets();
- assert(_imageStart[_frameCount] == fileSize);
+ _frameTotalSize = new uint32[_header.frameCount];
+#if 0
+ if (_header.hasSound) {
+ _audioStream = Audio::makeQueuingAudioStream(22050, true);
+ g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream);
+ }
+#endif
- setPalette();
+ readPaletteChunk();
+ readFrameSizesChunk();
- debug("Robot %d, %d frames, sound: %s\n", resourceId, _frameCount, _hasSound ? "yes" : "no");
+ debug("Robot %d, %d frames, sound: %s\n", resourceId, _header.frameCount, _header.hasSound ? "yes" : "no");
}
-void GfxRobot::getFrameOffsets() {
- int *audioEnd = new int[_frameCount];
- int *videoEnd = new int[_frameCount];
- uint32 frameDataOffset;
+void GfxRobot::readHeaderChunk() {
+ // Header (60 bytes)
+ _robotFile.skip(6);
+ _header.version = _robotFile.readUint16LE();
+ _robotFile.skip(2);
+ _header.audioSilenceSize = _robotFile.readUint16LE();
+ _robotFile.skip(2);
+ _header.frameCount = _robotFile.readUint16LE();
+ _header.paletteDataSize = _robotFile.readUint16LE();
+ _robotFile.skip(7);
+ _header.hasSound = _robotFile.readByte();
+ _robotFile.skip(34);
- switch (_version) {
- case 5:
- for (int i = 0; i < _frameCount; ++i) {
- videoEnd[i] = READ_LE_UINT16(_resourceData + _palOffset + 1200 + i * 2);
- audioEnd[i] = READ_LE_UINT16(_resourceData + _palOffset + 1200 + _frameCount * 2 + i * 2);
- }
- frameDataOffset = _palOffset + 0x4b0 + 0x400 + 0x200 + _frameCount * 4;
- break;
- case 6:
- for (int i = 0; i < _frameCount; ++i) {
- videoEnd[i] = READ_LE_UINT32(_resourceData + _palOffset + 1200 + i * 4);
- audioEnd[i] = READ_LE_UINT32(_resourceData + _palOffset + 1200 + _frameCount * 4 + i * 4);
- }
- frameDataOffset = _palOffset + 0x4b0 + 0x400 + 0x200 + _frameCount * 8;
- break;
- default:
- error("Can't yet handle index table for robot version %d", _version);
- return;
+ // Some robot files have sound, which doesn't start from frame 0
+ // (e.g. Phantasmagoria, robot 1305). In this case, there won't
+ // be audio silence in the header, but there will be an extra audio
+ // preload chunk before the palette chunk. Skip past it and its
+ // 14-byte header.
+ if (_header.hasSound && !_header.audioSilenceSize) {
+ // The header is 14 bytes: the chunk size + 10 more
+ uint32 preloadChunkSize = _robotFile.readUint32LE();
+ _robotFile.skip(preloadChunkSize + 10);
}
-
-
- // Pad to nearest 2 kilobytes
- if (frameDataOffset & 0x7ff)
- frameDataOffset = (frameDataOffset & ~0x7ff) + 0x800;
-
- _imageStart = new uint32[_frameCount + 1];
- _audioStart = new uint32[_frameCount];
- _audioLen = new uint32[_frameCount];
-
- _imageStart[0] = frameDataOffset;
-
- // Plus one so we can assert on this in the calling routine
- // The last one should point to end-of-file, unless I'm misunderstanding something
- for (int i = 1; i < _frameCount + 1; ++i)
- _imageStart[i] = _imageStart[i - 1] + audioEnd[i - 1];
- for (int i = 0; i < _frameCount; ++i)
- _audioStart[i] = _imageStart[i] + videoEnd[i];
- for (int i = 0; i < _frameCount; ++i)
- _audioLen[i] = _imageStart[i + 1] - _audioStart[i];
-
- delete[] audioEnd;
- delete[] videoEnd;
}
-void GfxRobot::setPalette() {
- byte *paletteData = _resourceData + _palOffset;
- uint16 paletteSize = READ_LE_UINT16(_resourceData + 16);
-
+void GfxRobot::readPaletteChunk() {
+ byte *paletteChunk = new byte[_header.paletteDataSize];
+ _robotFile.read(paletteChunk, _header.paletteDataSize);
+ int startIndex = READ_LE_UINT16(paletteChunk + 25);
+ int colorCount = READ_LE_UINT16(paletteChunk + 29);
Palette resourcePal;
+ _palette->createFromData(paletteChunk, _header.paletteDataSize, &resourcePal);
+ delete[] paletteChunk;
byte robotPal[256 * 4];
- int startIndex = READ_LE_UINT16(paletteData + 25);
- int colorCount = READ_LE_UINT16(paletteData + 29);
-
- warning("%d palette entries starting at %d", colorCount, startIndex);
-
- _palette->createFromData(paletteData, paletteSize, &resourcePal);
for (int i = 0; i < 256; ++i) {
_savedPal[i * 4 + 0] = _palette->_sysPalette.colors[i].r;
@@ -238,134 +180,124 @@ void GfxRobot::setPalette() {
g_system->setPalette(robotPal, 0, 256);
}
-void GfxRobot::drawNextFrame() {
- uint16 width, height;
- // Make sure that we haven't reached the end of the video already
- if (_curFrame == _frameCount)
- return;
-
- assembleVideoFrame(_curFrame);
- getFrameDimensions(_curFrame, width, height);
-
- g_system->copyRectToScreen(_outputBuffer, width, _x, _y, width, height * getFrameScale(_curFrame) / 100);
- g_system->updateScreen();
- g_system->delayMillis(100);
+void GfxRobot::readFrameSizesChunk() {
+ switch (_header.version) {
+ 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.readUint16LE();
+ 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.readUint32LE();
+ break;
+ default:
+ error("Can't yet handle index table for robot version %d", _header.version);
+ }
- _curFrame++;
+ _robotFile.skip(1024 + 512); // Skip unknown tables 1 and 2
- if (_curFrame == _frameCount) {
- // End of robot video, restore palette
- g_system->setPalette(_savedPal, 0, 256);
- _resourceId = -1;
- freeData();
+ // Pad to nearest 2 kilobytes
+ uint32 curPos = _robotFile.pos();
+ if (curPos & 0x7ff) {
+ curPos = (curPos & ~0x7ff) + 2048;
+ _robotFile.seek(curPos);
}
}
-void GfxRobot::assembleVideoFrame(uint16 frame) {
- byte *videoData = _resourceData + _imageStart[frame];
- uint16 frameFragments = READ_LE_UINT16(videoData + 18);
-
- uint32 decompressedSize = 0;
-
- DecompressorLZS lzs;
+void GfxRobot::processNextFrame() {
+ // Make sure that we haven't reached the end of the video already
+ if (_curFrame == _header.frameCount)
+ return;
- videoData = _resourceData + _imageStart[frame] + 24;
-
- for (int i = 0; i < frameFragments; ++i) {
- uint32 fragmentCompressed = READ_LE_UINT32(videoData);
- uint32 fragmentUncompressed = READ_LE_UINT32(videoData + 4);
+ // Read frame header (24 bytes)
+ _robotFile.skip(3);
+ byte frameScale = _robotFile.readByte();
+ uint16 frameWidth = _robotFile.readUint16LE();
+ uint16 frameHeight = _robotFile.readUint16LE();
+ _robotFile.skip(8); // x, y, width and height of the frame
+ uint16 compressedSize = _robotFile.readUint16LE();
+ uint16 frameFragments = _robotFile.readUint16LE();
+ _robotFile.skip(4); // unknown
- decompressedSize += fragmentUncompressed;
- videoData += 10 + fragmentCompressed;
- }
+ uint32 decompressedSize = frameWidth * (frameHeight * frameScale / 100);
- // Causes assertions in various places, such as Lighthouse/Demo 693.RBT
- // uint16 frameWidth = READ_LE_UINT16(_resourceData + _imageStart[frame] + 4);
- // uint16 frameHeight = READ_LE_UINT16(_resourceData + _imageStart[frame] + 6);
- // assert(decompressedSize == (uint32)(frameWidth * frameHeight) * getFrameScale(frame) / 100);
-
- // Reallocate the output buffer, if its size has changed
- if (decompressedSize != _outputBufferSize) {
+ // Reallocate the output buffer, if its size has increased
+ if (decompressedSize > _outputBufferSize) {
delete[] _outputBuffer;
_outputBuffer = new byte[decompressedSize];
}
-
+
_outputBufferSize = decompressedSize;
- uint32 assemblePtr = 0;
-
- videoData = _resourceData + _imageStart[frame] + 24;
-
- for (int i = 0; i < frameFragments; ++i) {
- uint32 fragmentCompressed = READ_LE_UINT32(videoData);
- uint32 fragmentUncompressed = READ_LE_UINT32(videoData + 4);
- uint16 compressionType = READ_LE_UINT16(videoData + 8);
-
- switch (compressionType) {
- case 0: {
- Common::MemoryReadStream compressedFrame(videoData + 10, fragmentCompressed, DisposeAfterUse::NO);
-
- lzs.unpack(&compressedFrame, _outputBuffer + assemblePtr, fragmentCompressed, fragmentUncompressed);
- assemblePtr += fragmentUncompressed;
- break;
- }
- case 2: // Untested
- memcpy(_outputBuffer + assemblePtr, videoData + 10, fragmentCompressed);
- assemblePtr += fragmentUncompressed;
- break;
+ DecompressorLZS lzs;
+ byte *outPtr = _outputBuffer;
+
+ for (uint16 i = 0; i < frameFragments; ++i) {
+ uint32 compressedFragmentSize = _robotFile.readUint32LE();
+ uint32 decompressedFragmentSize = _robotFile.readUint32LE();
+ uint16 compressionType = _robotFile.readUint16LE();
+
+ 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);
}
-
- videoData += 10 + fragmentCompressed;
- }
- assert(assemblePtr == decompressedSize);
-
- // FILE *f = fopen("/tmp/flap", "w");
- // fwrite(_outputBuffer, 1, decompressedSize, f);
- // fclose(f);
- // exit(1);
-}
-
-void GfxRobot::getFrameDimensions(uint16 frame, uint16 &width, uint16 &height) {
- byte *videoData = _resourceData + _imageStart[frame];
+ outPtr += decompressedFragmentSize;
+ }
- width = READ_LE_UINT16(videoData + 4);
- height = READ_LE_UINT16(videoData + 6);
-}
+ uint32 audioChunkSize = _frameTotalSize[_curFrame] - (24 + compressedSize);
+ // TODO: Audio
#if 0
-Common::Rect GfxRobot::getFrameRect(uint16 frame) {
- byte *videoData = _resourceData + _imageStart[frame];
-
- uint16 x = READ_LE_UINT16(videoData + 8);
- uint16 y = READ_LE_UINT16(videoData + 10);
- uint16 w = READ_LE_UINT16(videoData + 12);
- uint16 h = READ_LE_UINT16(videoData + 14);
+ // Queue the next audio frame
+ if (_header.hasSound) {
+ uint16 decodedSize = _robotFile.readUint16LE();
+ _robotFile.skip(2); // skip audio buffer position
- return Common::Rect(x, y, x + w, y + h);
-}
+ byte *audioFrame = g_sci->_audio->getDecodedRobotAudioFrame(&_robotFile, audioChunkSize - 4);
+ _audioStream->queueBuffer(audioFrame, decodedSize, DisposeAfterUse::NO, Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO);
+ } else {
+ _robotFile.skip(audioChunkSize);
+ }
+#else
+ _robotFile.skip(audioChunkSize);
#endif
-byte GfxRobot::getFrameScale(uint16 frame) {
- byte *videoData = _resourceData + _imageStart[frame];
- return videoData[3];
-}
+ // Show frame
+ g_system->copyRectToScreen(_outputBuffer, frameWidth, _x, _y, frameWidth, frameHeight * frameScale / 100);
+ g_system->updateScreen();
+ g_system->delayMillis(100);
+
+ _curFrame++;
-void GfxRobot::playAudio() {
- if (_hasSound) {
- Audio::SoundHandle _audioHandle;
- Audio::AudioStream *audioStream = g_sci->_audio->getRobotAudioStream(_resourceData);
- g_system->getMixer()->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream);
+ if (_curFrame == _header.frameCount) {
+ // End of robot video, restore palette
+ g_system->setPalette(_savedPal, 0, 256);
+ _resourceId = -1;
+ freeData();
}
}
void GfxRobot::freeData() {
- delete[] _resourceData; _resourceData = 0;
- delete[] _imageStart; _imageStart = 0;
- delete[] _audioStart; _audioStart = 0;
- delete[] _audioLen; _audioLen = 0;
+#if 0
+ if (_header.hasSound) {
+ g_system->getMixer()->stopHandle(_audioHandle);
+ //delete _audioStream; _audioStream = 0;
+ }
+#endif
+ delete[] _frameTotalSize; _frameTotalSize = 0;
delete[] _outputBuffer; _outputBuffer = 0;
+ _outputBufferSize = 0;
+ _robotFile.close();
}
#endif
diff --git a/engines/sci/graphics/robot.h b/engines/sci/graphics/robot.h
index f1b9324b6a..044f84eefe 100644
--- a/engines/sci/graphics/robot.h
+++ b/engines/sci/graphics/robot.h
@@ -26,34 +26,46 @@
#ifndef SCI_GRAPHICS_ROBOT_H
#define SCI_GRAPHICS_ROBOT_H
-namespace Sci {
+#include "common/file.h"
+
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+#include "sound/decoders/raw.h"
-#define ROBOT_FILE_STARTOFDATA 58
+namespace Sci {
#ifdef ENABLE_SCI32
+
+struct RobotHeader {
+ // 6 bytes, identifier bytes
+ uint16 version;
+ // 2 bytes, unknown
+ uint16 audioSilenceSize;
+ // 2 bytes, unknown
+ uint16 frameCount;
+ uint16 paletteDataSize;
+ // 6 bytes, unknown
+ byte hasSound;
+ // 35 bytes, unknown
+};
+
class GfxRobot {
public:
GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette);
~GfxRobot();
void init(GuiResourceId resourceId, uint16 x, uint16 y);
- void drawNextFrame();
+ void processNextFrame();
uint16 getCurFrame() { return _curFrame; }
- uint16 getFrameCount() { return _frameCount; }
+ uint16 getFrameCount() { return _header.frameCount; }
bool isPlaying() { return _resourceId != -1; }
void playAudio();
private:
- void initData(GuiResourceId resourceId);
- void getFrameOffsets();
- void assembleVideoFrame(uint16 frame);
- void getFrameDimensions(uint16 frame, uint16 &width, uint16 &height);
-#if 0
- // Unused
- Common::Rect getFrameRect(uint16 frame);
-#endif
- byte getFrameScale(uint16 frame); // Scale factor (multiplied by 100). More like custom height, but why use a percentage for it?
- void setPalette();
+ void readHeaderChunk();
+ void readPaletteChunk();
+ void readFrameSizesChunk();
+
void freeData();
ResourceManager *_resMan;
@@ -61,23 +73,18 @@ private:
GfxPalette *_palette;
GuiResourceId _resourceId;
- byte *_resourceData;
byte _savedPal[256 * 4];
- byte _version; // robot version
+ Common::File _robotFile;
+ Audio::QueuingAudioStream *_audioStream;
+ Audio::SoundHandle _audioHandle;
+
+ RobotHeader _header;
+
uint16 _x;
uint16 _y;
- //uint16 _width;
- //uint16 _height;
- uint16 _frameCount;
- uint32 _frameSize; // is width * height (pixelCount)
- uint16 _audioSize;
- bool _hasSound;
- uint32 _palOffset;
- uint32 *_imageStart;
- uint32 *_audioStart;
- uint32 *_audioLen;
uint16 _curFrame;
+ uint32 *_frameTotalSize;
byte *_outputBuffer;
uint32 _outputBufferSize;