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:
parent
94b6d23d44
commit
512bcf8b90
9 changed files with 193 additions and 291 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue