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
This commit is contained in:
Filippos Karapetis 2011-02-04 17:51:59 +00:00
parent 94b6d23d44
commit 512bcf8b90
9 changed files with 193 additions and 291 deletions

View file

@ -121,7 +121,6 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("draw_cel", WRAP_METHOD(Console, cmdDrawCel)); DCmd_Register("draw_cel", WRAP_METHOD(Console, cmdDrawCel));
#ifdef ENABLE_SCI32 #ifdef ENABLE_SCI32
DCmd_Register("draw_robot", WRAP_METHOD(Console, cmdDrawRobot)); DCmd_Register("draw_robot", WRAP_METHOD(Console, cmdDrawRobot));
DCmd_Register("play_robot_audio", WRAP_METHOD(Console, cmdPlayRobotAudio));
#endif #endif
DCmd_Register("undither", WRAP_METHOD(Console, cmdUndither)); DCmd_Register("undither", WRAP_METHOD(Console, cmdUndither));
DCmd_Register("pic_visualize", WRAP_METHOD(Console, cmdPicVisualize)); DCmd_Register("pic_visualize", WRAP_METHOD(Console, cmdPicVisualize));
@ -1529,23 +1528,6 @@ bool Console::cmdDrawRobot(int argc, const char **argv) {
return true; return true;
} }
bool Console::cmdPlayRobotAudio(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Draws frames from a robot resource\n");
DebugPrintf("Usage: %s <resourceId>\n", argv[0]);
DebugPrintf("where <resourceId> is the id of the robot resource to draw\n");
return true;
}
uint16 resourceId = atoi(argv[1]);
if (_engine->_gfxPaint32) {
_engine->_gfxPaint32->debugPlayRobotAudio(resourceId);
} else {
DebugPrintf("command not available in non-sci32 games");
}
return true;
}
#endif #endif
bool Console::cmdUndither(int argc, const char **argv) { bool Console::cmdUndither(int argc, const char **argv) {

View file

@ -93,7 +93,6 @@ private:
bool cmdDrawCel(int argc, const char **argv); bool cmdDrawCel(int argc, const char **argv);
#ifdef ENABLE_SCI32 #ifdef ENABLE_SCI32
bool cmdDrawRobot(int argc, const char **argv); bool cmdDrawRobot(int argc, const char **argv);
bool cmdPlayRobotAudio(int argc, const char **argv);
#endif #endif
bool cmdUndither(int argc, const char **argv); bool cmdUndither(int argc, const char **argv);
bool cmdPicVisualize(int argc, const char **argv); bool cmdPicVisualize(int argc, const char **argv);

View file

@ -1423,7 +1423,7 @@ 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
robot->drawNextFrame(); robot->processNextFrame();
// Signal the engine scripts that the video is done // Signal the engine scripts that the video is done
if (robot->getCurFrame() == robot->getFrameCount()) if (robot->getCurFrame() == robot->getFrameCount())
writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);

View file

@ -85,17 +85,9 @@ void GfxPaint32::debugDrawRobot(GuiResourceId robotId) {
GfxRobot *test = new GfxRobot(g_sci->getResMan(), _screen, _palette); GfxRobot *test = new GfxRobot(g_sci->getResMan(), _screen, _palette);
test->init(robotId, 0, 0); test->init(robotId, 0, 0);
while (test->getCurFrame() + 1 < test->getFrameCount()) { while (test->getCurFrame() + 1 < test->getFrameCount()) {
test->drawNextFrame(); test->processNextFrame();
} }
delete test; 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 } // End of namespace Sci

View file

@ -49,7 +49,6 @@ public:
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control); void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
void debugDrawRobot(GuiResourceId robotId); void debugDrawRobot(GuiResourceId robotId);
void debugPlayRobotAudio(GuiResourceId robotId);
private: private:
ResourceManager *_resMan; ResourceManager *_resMan;

View file

@ -40,7 +40,8 @@
#include "common/file.h" #include "common/file.h"
#include "common/system.h" #include "common/system.h"
#include "common/memstream.h" #include "common/stream.h"
#include "common/substream.h"
namespace Sci { namespace Sci {
@ -60,9 +61,10 @@ namespace Sci {
// around the screen and go behind other objects. (...) // around the screen and go behind other objects. (...)
#ifdef ENABLE_SCI32 #ifdef ENABLE_SCI32
GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette) GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette)
: _resMan(resMan), _screen(screen), _palette(palette), _resourceData(0), : _resMan(resMan), _screen(screen), _palette(palette), _outputBuffer(0),
_imageStart(0), _audioStart(0), _audioLen(0), _outputBuffer(0), _outputBufferSize(0) { _outputBufferSize(0), _audioStream(0) {
_resourceId = -1; _resourceId = -1;
_x = _y = 0; _x = _y = 0;
} }
@ -72,151 +74,91 @@ GfxRobot::~GfxRobot() {
} }
void GfxRobot::init(GuiResourceId resourceId, uint16 x, uint16 y) { void GfxRobot::init(GuiResourceId resourceId, uint16 x, uint16 y) {
char fileName[10];
uint32 fileSize;
// resourceId = 1305; // debug
_resourceId = resourceId; _resourceId = resourceId;
//_resourceId = 1305; // debug
_x = x; _x = x;
_y = y; _y = y;
_curFrame = 0; _curFrame = 0;
char fileName[10];
sprintf(fileName, "%d.rbt", _resourceId); sprintf(fileName, "%d.rbt", _resourceId);
Common::File robotFile; if (!_robotFile.open(fileName)) {
if (robotFile.open(fileName)) {
_resourceData = new byte[robotFile.size()];
robotFile.read(_resourceData, robotFile.size());
fileSize = robotFile.size();
robotFile.close();
} else {
warning("Unable to open robot file %s", fileName); warning("Unable to open robot file %s", fileName);
return; return;
} }
readHeaderChunk();
// There are several versions of robot files, ranging from 3 to 6. // There are several versions of robot files, ranging from 3 to 6.
// v3: no known examples // v3: no known examples
// v4: PQ:SWAT // v4: PQ:SWAT
// v5: SCI2.1 and SCI3 games // v5: SCI2.1 and SCI3 games
// v6: SCI3 games // v6: SCI3 games
_version = _resourceData[6]; switch (_header.version) {
// 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;
case 4: // used in PQ:SWAT case 4: // used in PQ:SWAT
// Unsupported // Unsupported
// TODO: Add support for this version
warning("TODO: add support for v4 robot videos"); warning("TODO: add support for v4 robot videos");
_curFrame = _frameCount; // jump to the last frame _curFrame = _header.frameCount; // jump to the last frame
return; 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 // Supported
break; break;
case 6: // introduced in SCI3
// Unsupported
// TODO: Add support for this version
warning("TODO: add support for v6 robot videos");
break;
default: default:
// Unsupported, error out so that we find out where this is used // 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(); _frameTotalSize = new uint32[_header.frameCount];
assert(_imageStart[_frameCount] == fileSize); #if 0
if (_header.hasSound) {
setPalette(); _audioStream = Audio::makeQueuingAudioStream(22050, true);
g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream);
debug("Robot %d, %d frames, sound: %s\n", resourceId, _frameCount, _hasSound ? "yes" : "no");
}
void GfxRobot::getFrameOffsets() {
int *audioEnd = new int[_frameCount];
int *videoEnd = new int[_frameCount];
uint32 frameDataOffset;
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;
} }
#endif
readPaletteChunk();
// Pad to nearest 2 kilobytes readFrameSizesChunk();
if (frameDataOffset & 0x7ff)
frameDataOffset = (frameDataOffset & ~0x7ff) + 0x800;
_imageStart = new uint32[_frameCount + 1]; debug("Robot %d, %d frames, sound: %s\n", resourceId, _header.frameCount, _header.hasSound ? "yes" : "no");
_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() { void GfxRobot::readHeaderChunk() {
byte *paletteData = _resourceData + _palOffset; // Header (60 bytes)
uint16 paletteSize = READ_LE_UINT16(_resourceData + 16); _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);
// 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);
}
}
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 resourcePal;
_palette->createFromData(paletteChunk, _header.paletteDataSize, &resourcePal);
delete[] paletteChunk;
byte robotPal[256 * 4]; 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) { for (int i = 0; i < 256; ++i) {
_savedPal[i * 4 + 0] = _palette->_sysPalette.colors[i].r; _savedPal[i * 4 + 0] = _palette->_sysPalette.colors[i].r;
@ -238,23 +180,106 @@ void GfxRobot::setPalette() {
g_system->setPalette(robotPal, 0, 256); g_system->setPalette(robotPal, 0, 256);
} }
void GfxRobot::drawNextFrame() {
uint16 width, height;
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);
}
_robotFile.skip(1024 + 512); // Skip unknown tables 1 and 2
// Pad to nearest 2 kilobytes
uint32 curPos = _robotFile.pos();
if (curPos & 0x7ff) {
curPos = (curPos & ~0x7ff) + 2048;
_robotFile.seek(curPos);
}
}
void GfxRobot::processNextFrame() {
// Make sure that we haven't reached the end of the video already // Make sure that we haven't reached the end of the video already
if (_curFrame == _frameCount) if (_curFrame == _header.frameCount)
return; return;
assembleVideoFrame(_curFrame); // Read frame header (24 bytes)
getFrameDimensions(_curFrame, width, height); _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
uint32 decompressedSize = frameWidth * (frameHeight * frameScale / 100);
// Reallocate the output buffer, if its size has increased
if (decompressedSize > _outputBufferSize) {
delete[] _outputBuffer;
_outputBuffer = new byte[decompressedSize];
}
g_system->copyRectToScreen(_outputBuffer, width, _x, _y, width, height * getFrameScale(_curFrame) / 100); _outputBufferSize = decompressedSize;
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);
}
outPtr += decompressedFragmentSize;
}
uint32 audioChunkSize = _frameTotalSize[_curFrame] - (24 + compressedSize);
// TODO: Audio
#if 0
// Queue the next audio frame
if (_header.hasSound) {
uint16 decodedSize = _robotFile.readUint16LE();
_robotFile.skip(2); // skip audio buffer position
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
// Show frame
g_system->copyRectToScreen(_outputBuffer, frameWidth, _x, _y, frameWidth, frameHeight * frameScale / 100);
g_system->updateScreen(); g_system->updateScreen();
g_system->delayMillis(100); g_system->delayMillis(100);
_curFrame++; _curFrame++;
if (_curFrame == _frameCount) { if (_curFrame == _header.frameCount) {
// End of robot video, restore palette // End of robot video, restore palette
g_system->setPalette(_savedPal, 0, 256); g_system->setPalette(_savedPal, 0, 256);
_resourceId = -1; _resourceId = -1;
@ -262,110 +287,17 @@ void GfxRobot::drawNextFrame() {
} }
} }
void GfxRobot::assembleVideoFrame(uint16 frame) {
byte *videoData = _resourceData + _imageStart[frame];
uint16 frameFragments = READ_LE_UINT16(videoData + 18);
uint32 decompressedSize = 0;
DecompressorLZS lzs;
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);
decompressedSize += fragmentUncompressed;
videoData += 10 + fragmentCompressed;
}
// 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) {
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;
}
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];
width = READ_LE_UINT16(videoData + 4);
height = READ_LE_UINT16(videoData + 6);
}
#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);
return Common::Rect(x, y, x + w, y + h);
}
#endif
byte GfxRobot::getFrameScale(uint16 frame) {
byte *videoData = _resourceData + _imageStart[frame];
return videoData[3];
}
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);
}
}
void GfxRobot::freeData() { void GfxRobot::freeData() {
delete[] _resourceData; _resourceData = 0; #if 0
delete[] _imageStart; _imageStart = 0; if (_header.hasSound) {
delete[] _audioStart; _audioStart = 0; g_system->getMixer()->stopHandle(_audioHandle);
delete[] _audioLen; _audioLen = 0; //delete _audioStream; _audioStream = 0;
}
#endif
delete[] _frameTotalSize; _frameTotalSize = 0;
delete[] _outputBuffer; _outputBuffer = 0; delete[] _outputBuffer; _outputBuffer = 0;
_outputBufferSize = 0;
_robotFile.close();
} }
#endif #endif

View file

@ -26,34 +26,46 @@
#ifndef SCI_GRAPHICS_ROBOT_H #ifndef SCI_GRAPHICS_ROBOT_H
#define SCI_GRAPHICS_ROBOT_H #define SCI_GRAPHICS_ROBOT_H
#include "common/file.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "sound/decoders/raw.h"
namespace Sci { namespace Sci {
#define ROBOT_FILE_STARTOFDATA 58
#ifdef ENABLE_SCI32 #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 { class GfxRobot {
public: public:
GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette); GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette);
~GfxRobot(); ~GfxRobot();
void init(GuiResourceId resourceId, uint16 x, uint16 y); void init(GuiResourceId resourceId, uint16 x, uint16 y);
void drawNextFrame(); void processNextFrame();
uint16 getCurFrame() { return _curFrame; } uint16 getCurFrame() { return _curFrame; }
uint16 getFrameCount() { return _frameCount; } uint16 getFrameCount() { return _header.frameCount; }
bool isPlaying() { return _resourceId != -1; } bool isPlaying() { return _resourceId != -1; }
void playAudio(); void playAudio();
private: private:
void initData(GuiResourceId resourceId); void readHeaderChunk();
void getFrameOffsets(); void readPaletteChunk();
void assembleVideoFrame(uint16 frame); void readFrameSizesChunk();
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 freeData(); void freeData();
ResourceManager *_resMan; ResourceManager *_resMan;
@ -61,23 +73,18 @@ private:
GfxPalette *_palette; GfxPalette *_palette;
GuiResourceId _resourceId; GuiResourceId _resourceId;
byte *_resourceData;
byte _savedPal[256 * 4]; byte _savedPal[256 * 4];
byte _version; // robot version Common::File _robotFile;
Audio::QueuingAudioStream *_audioStream;
Audio::SoundHandle _audioHandle;
RobotHeader _header;
uint16 _x; uint16 _x;
uint16 _y; 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; uint16 _curFrame;
uint32 *_frameTotalSize;
byte *_outputBuffer; byte *_outputBuffer;
uint32 _outputBufferSize; uint32 _outputBufferSize;

View file

@ -243,18 +243,9 @@ static byte *readSOLAudio(Common::SeekableReadStream *audioStream, uint32 &size,
return buffer; return buffer;
} }
// FIXME: This doesn't work correctly yet, perhaps there are differences in the byte *AudioPlayer::getDecodedRobotAudioFrame(Common::SeekableReadStream *str, uint32 encodedSize) {
// way the audio in robot files is handled
Audio::RewindableAudioStream *AudioPlayer::getRobotAudioStream(byte *buffer) {
const uint16 rbtHeaderSize = 19; // TODO: is this right?
const uint16 rbtAudioRate = 22050; // Seems to be hardcoded for all Robot videos
byte audioFlags = *(buffer + 6);
byte flags = 0; byte flags = 0;
uint32 audioSize = READ_LE_UINT16(buffer + 15) - rbtHeaderSize; return readSOLAudio(str, encodedSize, 0, flags);
Common::MemoryReadStream dataStream(buffer + rbtHeaderSize - 1, audioSize, DisposeAfterUse::NO);
byte *data = readSOLAudio(&dataStream, audioSize, audioFlags, flags);
return Audio::makeRawStream(data, audioSize, rbtAudioRate, flags);
} }
Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 volume, int *sampleLen) { Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 volume, int *sampleLen) {

View file

@ -69,8 +69,8 @@ public:
void setAudioRate(uint16 rate) { _audioRate = rate; } void setAudioRate(uint16 rate) { _audioRate = rate; }
Audio::SoundHandle *getAudioHandle() { return &_audioHandle; } Audio::SoundHandle *getAudioHandle() { return &_audioHandle; }
Audio::RewindableAudioStream *getRobotAudioStream(byte *buffer);
Audio::RewindableAudioStream *getAudioStream(uint32 number, uint32 volume, int *sampleLen); Audio::RewindableAudioStream *getAudioStream(uint32 number, uint32 volume, int *sampleLen);
byte *getDecodedRobotAudioFrame(Common::SeekableReadStream *str, uint32 encodedSize);
int getAudioPosition(); int getAudioPosition();
int startAudio(uint16 module, uint32 tuple); int startAudio(uint16 module, uint32 tuple);
int wPlayAudio(uint16 module, uint32 tuple); int wPlayAudio(uint16 module, uint32 tuple);