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
This commit is contained in:
parent
d7fb5239e7
commit
6f9ac84f77
12 changed files with 237 additions and 242 deletions
|
@ -53,6 +53,7 @@
|
|||
#include "sci/video/seq_decoder.h"
|
||||
#ifdef ENABLE_SCI32
|
||||
#include "video/coktel_decoder.h"
|
||||
#include "sci/video/robot_decoder.h"
|
||||
#endif
|
||||
|
||||
#include "common/file.h"
|
||||
|
@ -119,9 +120,6 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
|
|||
DCmd_Register("set_palette", WRAP_METHOD(Console, cmdSetPalette));
|
||||
DCmd_Register("draw_pic", WRAP_METHOD(Console, cmdDrawPic));
|
||||
DCmd_Register("draw_cel", WRAP_METHOD(Console, cmdDrawCel));
|
||||
#ifdef ENABLE_SCI32
|
||||
DCmd_Register("draw_robot", WRAP_METHOD(Console, cmdDrawRobot));
|
||||
#endif
|
||||
DCmd_Register("undither", WRAP_METHOD(Console, cmdUndither));
|
||||
DCmd_Register("pic_visualize", WRAP_METHOD(Console, cmdPicVisualize));
|
||||
DCmd_Register("play_video", WRAP_METHOD(Console, cmdPlayVideo));
|
||||
|
@ -243,16 +241,16 @@ void Console::postEnter() {
|
|||
#ifdef ENABLE_SCI32
|
||||
} else if (_videoFile.hasSuffix(".vmd")) {
|
||||
videoDecoder = new Video::VMDDecoder(g_system->getMixer());
|
||||
#endif
|
||||
} else if (_videoFile.hasSuffix(".rbt")) {
|
||||
videoDecoder = new RobotDecoder(g_system->getMixer(), _engine->getPlatform() == Common::kPlatformMacintosh);
|
||||
} else if (_videoFile.hasSuffix(".duk")) {
|
||||
#ifdef ENABLE_SCI32
|
||||
duckMode = true;
|
||||
videoDecoder = new Video::AviDecoder(g_system->getMixer());
|
||||
#else
|
||||
warning("Duck videos require SCI32 support compiled in");
|
||||
#endif
|
||||
} else if (_videoFile.hasSuffix(".avi")) {
|
||||
videoDecoder = new Video::AviDecoder(g_system->getMixer());
|
||||
} else {
|
||||
warning("Unrecognized video type");
|
||||
}
|
||||
|
||||
if (videoDecoder && videoDecoder->loadFile(_videoFile)) {
|
||||
|
@ -1509,27 +1507,6 @@ bool Console::cmdDrawCel(int argc, const char **argv) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCI32
|
||||
bool Console::cmdDrawRobot(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->debugDrawRobot(resourceId);
|
||||
} else {
|
||||
DebugPrintf("command not available in non-sci32 games");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool Console::cmdUndither(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
DebugPrintf("Enable/disable undithering.\n");
|
||||
|
@ -1569,7 +1546,7 @@ bool Console::cmdPicVisualize(int argc, const char **argv) {
|
|||
|
||||
bool Console::cmdPlayVideo(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
DebugPrintf("Plays a SEQ, AVI, DUK or VMD video.\n");
|
||||
DebugPrintf("Plays a SEQ, AVI, VMD, RBT or DUK video.\n");
|
||||
DebugPrintf("Usage: %s <video file name> <delay>\n", argv[0]);
|
||||
DebugPrintf("The video file name should include the extension\n");
|
||||
DebugPrintf("Delay is only used in SEQ videos and is measured in ticks (default: 10)\n");
|
||||
|
@ -1579,7 +1556,8 @@ bool Console::cmdPlayVideo(int argc, const char **argv) {
|
|||
Common::String filename = argv[1];
|
||||
filename.toLowercase();
|
||||
|
||||
if (filename.hasSuffix(".seq") || filename.hasSuffix(".avi") || filename.hasSuffix(".vmd") || filename.hasSuffix(".duk")) {
|
||||
if (filename.hasSuffix(".seq") || filename.hasSuffix(".avi") || filename.hasSuffix(".vmd") ||
|
||||
filename.hasSuffix(".rbt") || filename.hasSuffix(".duk")) {
|
||||
_videoFile = filename;
|
||||
_videoFrameDelay = (argc == 2) ? 10 : atoi(argv[2]);
|
||||
return Cmd_Exit(0, 0);
|
||||
|
|
|
@ -91,9 +91,6 @@ private:
|
|||
bool cmdSetPalette(int argc, const char **argv);
|
||||
bool cmdDrawPic(int argc, const char **argv);
|
||||
bool cmdDrawCel(int argc, const char **argv);
|
||||
#ifdef ENABLE_SCI32
|
||||
bool cmdDrawRobot(int argc, const char **argv);
|
||||
#endif
|
||||
bool cmdUndither(int argc, const char **argv);
|
||||
bool cmdPicVisualize(int argc, const char **argv);
|
||||
bool cmdPlayVideo(int argc, const char **argv);
|
||||
|
|
|
@ -48,12 +48,12 @@
|
|||
#include "sci/graphics/paint16.h"
|
||||
#include "sci/graphics/picture.h"
|
||||
#include "sci/graphics/ports.h"
|
||||
#include "sci/graphics/robot.h"
|
||||
#include "sci/graphics/screen.h"
|
||||
#include "sci/graphics/text16.h"
|
||||
#include "sci/graphics/view.h"
|
||||
#ifdef ENABLE_SCI32
|
||||
#include "sci/graphics/frameout.h"
|
||||
#include "sci/video/robot_decoder.h"
|
||||
#endif
|
||||
|
||||
namespace Sci {
|
||||
|
@ -1398,7 +1398,6 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
|
|||
reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
|
||||
|
||||
int16 subop = argv[0].toUint16();
|
||||
GfxRobot *robot = g_sci->_gfxRobot;
|
||||
|
||||
switch (subop) {
|
||||
case 0: { // init
|
||||
|
@ -1408,7 +1407,8 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
|
|||
int16 x = argv[4].toUint16();
|
||||
int16 y = argv[5].toUint16();
|
||||
warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y);
|
||||
robot->init(id, x, y);
|
||||
g_sci->_robotDecoder->load(id);
|
||||
g_sci->_robotDecoder->setPos(x, y);
|
||||
}
|
||||
break;
|
||||
case 1: // LSL6 hires (startup)
|
||||
|
@ -1423,10 +1423,13 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
|
|||
warning("kRobot(%d)", subop);
|
||||
break;
|
||||
case 8: // sync
|
||||
robot->processNextFrame();
|
||||
if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) {
|
||||
writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
|
||||
} else {
|
||||
g_sci->_robotDecoder->close();
|
||||
// Signal the engine scripts that the video is done
|
||||
if (robot->getCurFrame() == robot->getFrameCount())
|
||||
writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
warning("kRobot(%d)", subop);
|
||||
|
|
|
@ -51,13 +51,16 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
|
|||
uint16 screenWidth = g_system->getWidth();
|
||||
uint16 screenHeight = g_system->getHeight();
|
||||
bool isVMD = videoState.fileName.hasSuffix(".vmd");
|
||||
bool isRobot = videoState.fileName.hasSuffix(".rbt");
|
||||
|
||||
if (!isRobot) {
|
||||
if (screenWidth == 640 && width <= 320 && height <= 240 && ((videoState.flags & kDoubled) || !isVMD)) {
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
pitch *= 2;
|
||||
scaleBuffer = new byte[width * height * bytesPerPixel];
|
||||
}
|
||||
}
|
||||
|
||||
uint16 x, y;
|
||||
|
||||
|
@ -73,8 +76,13 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
|
|||
y = (screenHeight - height) / 2;
|
||||
}
|
||||
} else {
|
||||
if (!isRobot) {
|
||||
x = (screenWidth - width) / 2;
|
||||
y = (screenHeight - height) / 2;
|
||||
} else {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
bool skipVideo = false;
|
||||
|
||||
|
@ -84,13 +92,18 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
|
|||
while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
|
||||
if (videoDecoder->needsUpdate()) {
|
||||
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
|
||||
|
||||
if (frame) {
|
||||
if (scaleBuffer) {
|
||||
// TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
|
||||
g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel);
|
||||
g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height);
|
||||
} else
|
||||
} else {
|
||||
if (!isRobot)
|
||||
g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
|
||||
else // Frames in robot videos have different dimensions
|
||||
g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
|
||||
}
|
||||
|
||||
if (videoDecoder->hasDirtyPalette())
|
||||
videoDecoder->setSystemPalette();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -79,7 +79,7 @@ ifdef ENABLE_SCI32
|
|||
MODULE_OBJS += \
|
||||
graphics/frameout.o \
|
||||
graphics/paint32.o \
|
||||
graphics/robot.o
|
||||
video/robot_decoder.o
|
||||
endif
|
||||
|
||||
# This module can be built as a plugin
|
||||
|
|
|
@ -66,8 +66,8 @@
|
|||
#include "sci/graphics/transitions.h"
|
||||
|
||||
#ifdef ENABLE_SCI32
|
||||
#include "sci/graphics/robot.h"
|
||||
#include "sci/graphics/frameout.h"
|
||||
#include "sci/video/robot_decoder.h"
|
||||
#endif
|
||||
|
||||
namespace Sci {
|
||||
|
@ -151,7 +151,7 @@ SciEngine::~SciEngine() {
|
|||
DebugMan.clearAllDebugChannels();
|
||||
|
||||
#ifdef ENABLE_SCI32
|
||||
delete _gfxRobot;
|
||||
delete _robotDecoder;
|
||||
delete _gfxFrameout;
|
||||
#endif
|
||||
delete _gfxMenu;
|
||||
|
@ -582,7 +582,7 @@ void SciEngine::initGraphics() {
|
|||
_gfxText16 = 0;
|
||||
_gfxTransitions = 0;
|
||||
#ifdef ENABLE_SCI32
|
||||
_gfxRobot = 0;
|
||||
_robotDecoder = 0;
|
||||
_gfxFrameout = 0;
|
||||
_gfxPaint32 = 0;
|
||||
#endif
|
||||
|
@ -611,7 +611,7 @@ void SciEngine::initGraphics() {
|
|||
_gfxCompare = new GfxCompare(_gamestate->_segMan, _kernel, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
|
||||
_gfxPaint32 = new GfxPaint32(_resMan, _gamestate->_segMan, _kernel, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette);
|
||||
_gfxPaint = _gfxPaint32;
|
||||
_gfxRobot = new GfxRobot(_resMan, _gfxScreen, _gfxPalette);
|
||||
_robotDecoder = new RobotDecoder(g_system->getMixer(), getPlatform() == Common::kPlatformMacintosh);
|
||||
_gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32);
|
||||
} else {
|
||||
#endif
|
||||
|
|
|
@ -77,7 +77,7 @@ class GfxTransitions;
|
|||
|
||||
#ifdef ENABLE_SCI32
|
||||
class SciGui32;
|
||||
class GfxRobot;
|
||||
class RobotDecoder;
|
||||
class GfxFrameout;
|
||||
#endif
|
||||
|
||||
|
@ -313,7 +313,7 @@ public:
|
|||
GfxMacIconBar *_gfxMacIconBar; // Mac Icon Bar manager
|
||||
|
||||
#ifdef ENABLE_SCI32
|
||||
GfxRobot *_gfxRobot;
|
||||
RobotDecoder *_robotDecoder;
|
||||
GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx
|
||||
#endif
|
||||
|
||||
|
|
|
@ -23,25 +23,20 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// 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 "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "graphics/surface.h"
|
||||
#include "sound/decoders/raw.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"
|
||||
#include "sci/resource.h"
|
||||
#include "sci/util.h"
|
||||
#include "sci/sound/audio.h"
|
||||
#include "sci/video/robot_decoder.h"
|
||||
|
||||
namespace Sci {
|
||||
|
||||
|
@ -49,6 +44,7 @@ namespace Sci {
|
|||
// - Positioning
|
||||
// - Proper handling of frame scaling - scaled frames look squashed
|
||||
// (probably because both dimensions should be scaled)
|
||||
// - Transparency support
|
||||
// - 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
|
||||
|
@ -73,31 +69,37 @@ namespace Sci {
|
|||
|
||||
#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;
|
||||
RobotDecoder::RobotDecoder(Audio::Mixer *mixer, bool isBigEndian) {
|
||||
_surface = 0;
|
||||
_fileStream = 0;
|
||||
_audioStream = 0;
|
||||
_dirtyPalette = false;
|
||||
_pos = Common::Point(0, 0);
|
||||
_mixer = mixer;
|
||||
_isBigEndian = isBigEndian;
|
||||
}
|
||||
|
||||
GfxRobot::~GfxRobot() {
|
||||
freeData();
|
||||
RobotDecoder::~RobotDecoder() {
|
||||
close();
|
||||
}
|
||||
|
||||
void GfxRobot::init(GuiResourceId resourceId, uint16 x, uint16 y) {
|
||||
_x = x;
|
||||
_y = y;
|
||||
_curFrame = 0;
|
||||
|
||||
Common::String fileName = Common::String::format("%d.rbt", resourceId);
|
||||
bool RobotDecoder::load(GuiResourceId id) {
|
||||
Common::String fileName = Common::String::format("%d.rbt", id);
|
||||
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName);
|
||||
|
||||
if (!stream) {
|
||||
warning("Unable to open robot file %s", fileName.c_str());
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
_robotFile = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(),
|
||||
g_sci->getPlatform() == Common::kPlatformMacintosh, DisposeAfterUse::YES);
|
||||
return load(stream);
|
||||
}
|
||||
|
||||
bool RobotDecoder::load(Common::SeekableReadStream *stream) {
|
||||
close();
|
||||
|
||||
_fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES);
|
||||
_surface = new Graphics::Surface();
|
||||
|
||||
readHeaderChunk();
|
||||
|
||||
|
@ -109,106 +111,90 @@ void GfxRobot::init(GuiResourceId resourceId, uint16 x, uint16 y) {
|
|||
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);
|
||||
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream);
|
||||
}
|
||||
|
||||
readPaletteChunk();
|
||||
readFrameSizesChunk();
|
||||
|
||||
debug("Robot %d, %d frames, sound: %s\n", resourceId, _header.frameCount, _header.hasSound ? "yes" : "no");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GfxRobot::readHeaderChunk() {
|
||||
void RobotDecoder::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);
|
||||
_fileStream->skip(6);
|
||||
_header.version = _fileStream->readUint16();
|
||||
_header.audioChunkSize = _fileStream->readUint16();
|
||||
_header.audioSilenceSize = _fileStream->readUint16();
|
||||
_fileStream->skip(2);
|
||||
_header.frameCount = _fileStream->readUint16();
|
||||
_header.paletteDataSize = _fileStream->readUint16();
|
||||
_header.unkChunkDataSize = _fileStream->readUint16();
|
||||
_fileStream->skip(5);
|
||||
_header.hasSound = _fileStream->readByte();
|
||||
_fileStream->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);
|
||||
_fileStream->skip(_header.unkChunkDataSize);
|
||||
}
|
||||
|
||||
void GfxRobot::readPaletteChunk() {
|
||||
byte *paletteChunk = new byte[_header.paletteDataSize];
|
||||
_robotFile->read(paletteChunk, _header.paletteDataSize);
|
||||
void RobotDecoder::readPaletteChunk() {
|
||||
byte *paletteData = new byte[_header.paletteDataSize];
|
||||
_fileStream->read(paletteData, _header.paletteDataSize);
|
||||
|
||||
int startIndex = paletteChunk[25];
|
||||
int colorCount = READ_SCI11ENDIAN_UINT16(paletteChunk + 29);
|
||||
// SCI1.1 palette
|
||||
uint16 palColorStart = READ_SCI11ENDIAN_UINT16(paletteData + 25);
|
||||
uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29);
|
||||
|
||||
if (colorCount > 256)
|
||||
error("Invalid color count: %d", colorCount);
|
||||
int palOffset = 37;
|
||||
memset(_palette, 0, 256 * 3);
|
||||
|
||||
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;
|
||||
for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
|
||||
_palette[colorNo * 3 + 0] = paletteData[palOffset++];
|
||||
_palette[colorNo * 3 + 1] = paletteData[palOffset++];
|
||||
_palette[colorNo * 3 + 2] = paletteData[palOffset++];
|
||||
}
|
||||
|
||||
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);
|
||||
_dirtyPalette = true;
|
||||
delete[] paletteData;
|
||||
}
|
||||
|
||||
|
||||
void GfxRobot::readFrameSizesChunk() {
|
||||
void RobotDecoder::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.
|
||||
|
||||
_frameTotalSize = new uint32[_header.frameCount];
|
||||
|
||||
// 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);
|
||||
_fileStream->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);
|
||||
_fileStream->skip(_header.frameCount * 2);
|
||||
for (int i = 0; i < _header.frameCount; ++i)
|
||||
_frameTotalSize[i] = _robotFile->readUint16();
|
||||
_frameTotalSize[i] = _fileStream->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);
|
||||
_fileStream->skip(_header.frameCount * 4);
|
||||
for (int i = 0; i < _header.frameCount; ++i)
|
||||
_frameTotalSize[i] = _robotFile->readUint32();
|
||||
_frameTotalSize[i] = _fileStream->readUint32();
|
||||
break;
|
||||
default:
|
||||
error("Can't yet handle index table for robot version %d", _header.version);
|
||||
|
@ -216,58 +202,49 @@ void GfxRobot::readFrameSizesChunk() {
|
|||
#endif
|
||||
|
||||
// 2 more unknown tables
|
||||
_robotFile->skip(1024 + 512);
|
||||
_fileStream->skip(1024 + 512);
|
||||
|
||||
// Pad to nearest 2 kilobytes
|
||||
uint32 curPos = _robotFile->pos();
|
||||
uint32 curPos = _fileStream->pos();
|
||||
if (curPos & 0x7ff)
|
||||
_robotFile->seek((curPos & ~0x7ff) + 2048);
|
||||
_fileStream->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;
|
||||
|
||||
const Graphics::Surface *RobotDecoder::decodeNextFrame() {
|
||||
// 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
|
||||
_fileStream->skip(3);
|
||||
byte frameScale = _fileStream->readByte();
|
||||
uint16 frameWidth = _fileStream->readUint16();
|
||||
uint16 frameHeight = _fileStream->readUint16();
|
||||
_fileStream->skip(8); // x, y, width and height of the frame
|
||||
uint16 compressedSize = _fileStream->readUint16();
|
||||
uint16 frameFragments = _fileStream->readUint16();
|
||||
_fileStream->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;
|
||||
uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100;
|
||||
_surface->free();
|
||||
_surface->create(frameWidth, frameHeight, 1);
|
||||
_surface->w = frameWidth * frameScale / 100;
|
||||
|
||||
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);
|
||||
Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedSize);
|
||||
lzs.unpack(&fragmentStream, (byte *)_surface->pixels, compressedSize, decompressedSize);
|
||||
} else {
|
||||
byte *outPtr = _outputBuffer;
|
||||
byte *outPtr = (byte *)_surface->pixels;
|
||||
|
||||
for (uint16 i = 0; i < frameFragments; ++i) {
|
||||
uint32 compressedFragmentSize = _robotFile->readUint32();
|
||||
uint32 decompressedFragmentSize = _robotFile->readUint32();
|
||||
uint16 compressionType = _robotFile->readUint16();
|
||||
uint32 compressedFragmentSize = _fileStream->readUint32();
|
||||
uint32 decompressedFragmentSize = _fileStream->readUint32();
|
||||
uint16 compressionType = _fileStream->readUint16();
|
||||
|
||||
if (compressionType == 0) {
|
||||
Common::SeekableSubReadStream fragmentStream(_robotFile, _robotFile->pos(), _robotFile->pos() + compressedFragmentSize);
|
||||
Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedFragmentSize);
|
||||
lzs.unpack(&fragmentStream, outPtr, compressedFragmentSize, decompressedFragmentSize);
|
||||
} else if (compressionType == 2) { // untested
|
||||
_robotFile->read(outPtr, compressedFragmentSize);
|
||||
_fileStream->read(outPtr, compressedFragmentSize);
|
||||
} else {
|
||||
error("Unknown frame compression found: %d", compressionType);
|
||||
}
|
||||
|
@ -276,52 +253,55 @@ void GfxRobot::processNextFrame() {
|
|||
}
|
||||
}
|
||||
|
||||
uint32 audioChunkSize = _frameTotalSize[_curFrame] - (24 + compressedSize);
|
||||
// +1 because we start with frame number -1
|
||||
uint32 audioChunkSize = _frameTotalSize[_curFrame + 1] - (24 + compressedSize);
|
||||
|
||||
// TODO: The audio chunk size below is usually correct, but there are some
|
||||
// exceptions (e.g. robot 4902 in Phantasmagoria, towards its end)
|
||||
#if 0
|
||||
// Read frame audio header (14 bytes)
|
||||
_robotFile->skip(2); // buffer position
|
||||
_robotFile->skip(2); // unknown (usually 1)
|
||||
_robotFile->skip(2); /*uint16 audioChunkSize = _robotFile->readUint16() + 8;*/
|
||||
_robotFile->skip(2);
|
||||
_fileStream->skip(2); // buffer position
|
||||
_fileStream->skip(2); // unknown (usually 1)
|
||||
_fileStream->skip(2); /*uint16 audioChunkSize = _fileStream->readUint16() + 8;*/
|
||||
_fileStream->skip(2);
|
||||
#endif
|
||||
|
||||
// Queue the next audio frame
|
||||
// FIXME: For some reason, there are audio hiccups/gaps
|
||||
if (_header.hasSound) {
|
||||
_robotFile->skip(8); // header
|
||||
_audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_robotFile, audioChunkSize - 8),
|
||||
_fileStream->skip(8); // header
|
||||
_audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize - 8),
|
||||
(audioChunkSize - 8) * 2, DisposeAfterUse::NO,
|
||||
Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
|
||||
} else {
|
||||
_robotFile->skip(audioChunkSize);
|
||||
_fileStream->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
|
||||
if (_curFrame == -1)
|
||||
_startTime = g_system->getMillis();
|
||||
|
||||
_curFrame++;
|
||||
|
||||
if (_curFrame == _header.frameCount) {
|
||||
// End of robot video, restore palette
|
||||
g_system->setPalette(_savedPal, 0, 256);
|
||||
freeData();
|
||||
}
|
||||
return _surface;
|
||||
}
|
||||
|
||||
void GfxRobot::freeData() {
|
||||
void RobotDecoder::close() {
|
||||
if (!_fileStream)
|
||||
return;
|
||||
|
||||
delete _fileStream;
|
||||
_fileStream = 0;
|
||||
|
||||
_surface->free();
|
||||
delete _surface;
|
||||
_surface = 0;
|
||||
|
||||
if (_header.hasSound) {
|
||||
g_system->getMixer()->stopHandle(_audioHandle);
|
||||
_mixer->stopHandle(_audioHandle);
|
||||
//delete _audioStream; _audioStream = 0;
|
||||
}
|
||||
delete[] _frameTotalSize; _frameTotalSize = 0;
|
||||
delete[] _outputBuffer; _outputBuffer = 0;
|
||||
_outputBufferSize = 0;
|
||||
delete _robotFile; _robotFile = 0;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -23,16 +23,16 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef SCI_GRAPHICS_ROBOT_H
|
||||
#define SCI_GRAPHICS_ROBOT_H
|
||||
#ifndef SCI_VIDEO_ROBOT_DECODER_H
|
||||
#define SCI_VIDEO_ROBOT_DECODER_H
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/substream.h"
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/mixer.h"
|
||||
#include "sound/decoders/raw.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableSubReadStreamEndian;
|
||||
}
|
||||
#include "video/video_decoder.h"
|
||||
|
||||
namespace Sci {
|
||||
|
||||
|
@ -52,17 +52,29 @@ struct RobotHeader {
|
|||
// 34 bytes, unknown
|
||||
};
|
||||
|
||||
class GfxRobot {
|
||||
class RobotDecoder : public Video::FixedRateVideoDecoder {
|
||||
public:
|
||||
GfxRobot(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette);
|
||||
~GfxRobot();
|
||||
RobotDecoder(Audio::Mixer *mixer, bool isBigEndian);
|
||||
virtual ~RobotDecoder();
|
||||
|
||||
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();
|
||||
bool load(Common::SeekableReadStream *stream);
|
||||
bool load(GuiResourceId id);
|
||||
void close();
|
||||
|
||||
bool isVideoLoaded() const { return _fileStream != 0; }
|
||||
uint16 getWidth() const { assert(_surface); return _surface->w; }
|
||||
uint16 getHeight() const { assert(_surface); return _surface->h; }
|
||||
uint16 getPitch() const { assert(_surface); return _surface->pitch; }
|
||||
uint32 getFrameCount() const { return _header.frameCount; }
|
||||
const Graphics::Surface *decodeNextFrame();
|
||||
Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
|
||||
const byte *getPalette() { _dirtyPalette = false; return _palette; }
|
||||
bool hasDirtyPalette() const { return _dirtyPalette; }
|
||||
void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); }
|
||||
Common::Point getPos() const { return _pos; }
|
||||
|
||||
protected:
|
||||
Common::Rational getFrameRate() const { return Common::Rational(60, 10); }
|
||||
|
||||
private:
|
||||
void readHeaderChunk();
|
||||
|
@ -71,25 +83,19 @@ private:
|
|||
|
||||
void freeData();
|
||||
|
||||
ResourceManager *_resMan;
|
||||
GfxScreen *_screen;
|
||||
GfxPalette *_palette;
|
||||
RobotHeader _header;
|
||||
Common::Point _pos;
|
||||
bool _isBigEndian;
|
||||
|
||||
byte _savedPal[256 * 4];
|
||||
Common::SeekableSubReadStreamEndian *_fileStream;
|
||||
|
||||
Common::SeekableSubReadStreamEndian *_robotFile;
|
||||
uint32 *_frameTotalSize;
|
||||
byte _palette[256 * 3];
|
||||
bool _dirtyPalette;
|
||||
Graphics::Surface *_surface;
|
||||
Audio::QueuingAudioStream *_audioStream;
|
||||
Audio::SoundHandle _audioHandle;
|
||||
|
||||
RobotHeader _header;
|
||||
|
||||
uint16 _x;
|
||||
uint16 _y;
|
||||
uint16 _curFrame;
|
||||
uint32 *_frameTotalSize;
|
||||
|
||||
byte *_outputBuffer;
|
||||
uint32 _outputBufferSize;
|
||||
Audio::Mixer *_mixer;
|
||||
};
|
||||
#endif
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue