mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-05 00:36:57 +00:00
8c3d2fc741
This is a workaround for how Lost in Time behaves in combination with changes I made to the DataIO code for running Urban Runner on low-memory devices. Urban Runner's intro are far to big to have them copied into memory for these devices, so I made the DataIO code return a SafeSeekableSubReadStream into the opened archive stream instead. Unfortunately, Lost in Time might not close a video file when it closes the data file which it was originally in, especially when loading a saved game. Since the video player needs to be able to gaplessly continue a video and there does not, by itself, close the video if not requested by the scripts, this leads to reading out of an already closed stream in certain cases. So, to worka round this issues, the video player tries to reopen each currently opened video after a data archive was closed, to make sure that that video is still available. If not, the video is closed.
920 lines
24 KiB
C++
920 lines
24 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "gob/videoplayer.h"
|
|
#include "gob/global.h"
|
|
#include "gob/dataio.h"
|
|
#include "gob/video.h"
|
|
#include "gob/game.h"
|
|
#include "gob/palanim.h"
|
|
#include "gob/inter.h"
|
|
#include "gob/map.h"
|
|
#include "gob/sound/sound.h"
|
|
|
|
namespace Gob {
|
|
|
|
VideoPlayer::Properties::Properties() : type(kVideoTypeTry), sprite(Draw::kFrontSurface),
|
|
x(-1), y(-1), width(-1), height(-1), flags(kFlagFrontSurface), switchColorMode(false),
|
|
startFrame(-1), lastFrame(-1), endFrame(-1), forceSeek(false),
|
|
breakKey(kShortKeyEscape), palCmd(8), palStart(0), palEnd(255), palFrame(-1),
|
|
noBlock(false), loop(false), fade(false), waitEndFrame(true),
|
|
hasSound(false), canceled(false) {
|
|
|
|
}
|
|
|
|
|
|
VideoPlayer::Video::Video() : decoder(0), live(false) {
|
|
}
|
|
|
|
bool VideoPlayer::Video::isEmpty() const {
|
|
return decoder == 0;
|
|
}
|
|
|
|
void VideoPlayer::Video::close() {
|
|
delete decoder;
|
|
|
|
decoder = 0;
|
|
fileName.clear();
|
|
surface.reset();
|
|
|
|
live = false;
|
|
}
|
|
|
|
|
|
const char *const VideoPlayer::_extensions[] = { "IMD", "IMD", "VMD", "RMD", "SMD" };
|
|
|
|
VideoPlayer::VideoPlayer(GobEngine *vm) : _vm(vm), _needBlit(false),
|
|
_noCursorSwitch(false), _woodruffCohCottWorkaround(false) {
|
|
}
|
|
|
|
VideoPlayer::~VideoPlayer() {
|
|
for (int i = 0; i < kVideoSlotCount; i++)
|
|
_videoSlots[i].close();
|
|
}
|
|
|
|
void VideoPlayer::evaluateFlags(Properties &properties) {
|
|
if (properties.flags & kFlagFrontSurface) {
|
|
properties.sprite = Draw::kFrontSurface;
|
|
} else if (properties.flags & kFlagOtherSurface) {
|
|
properties.sprite = properties.x;
|
|
properties.x = 0;
|
|
} else if (properties.flags & kFlagScreenSurface) {
|
|
properties.sprite = 0;
|
|
} else if (properties.flags & kFlagNoVideo) {
|
|
properties.sprite = 0;
|
|
} else {
|
|
properties.sprite = Draw::kBackSurface;
|
|
}
|
|
|
|
if (properties.noBlock && (properties.sprite == Draw::kFrontSurface))
|
|
properties.sprite = Draw::kBackSurface;
|
|
}
|
|
|
|
int VideoPlayer::openVideo(bool primary, const Common::String &file, Properties &properties) {
|
|
int slot = 0;
|
|
|
|
Video *video = 0;
|
|
if (!primary) {
|
|
slot = getNextFreeSlot();
|
|
if (slot < 0) {
|
|
warning("VideoPlayer::openVideo(): Can't open video \"%s\": No free slot", file.c_str());
|
|
return -1;
|
|
}
|
|
|
|
video = &_videoSlots[slot];
|
|
} else
|
|
video = &_videoSlots[0];
|
|
|
|
// Different video already in the slot => close that video
|
|
if (!video->isEmpty() && (video->fileName.compareToIgnoreCase(file) != 0))
|
|
video->close();
|
|
|
|
// No video => load the requested file
|
|
if (video->isEmpty()) {
|
|
// Open the video
|
|
if (!(video->decoder = openVideo(file, properties)))
|
|
return -1;
|
|
|
|
if (video->decoder->hasVideo() && !(properties.flags & kFlagNoVideo) &&
|
|
(video->decoder->isPaletted() != !_vm->isTrueColor())) {
|
|
if (!properties.switchColorMode)
|
|
return -1;
|
|
|
|
_vm->setTrueColor(!video->decoder->isPaletted());
|
|
|
|
video->decoder->colorModeChanged();
|
|
}
|
|
|
|
// Set the filename
|
|
video->fileName = file;
|
|
|
|
// WORKAROUND: In some rare cases, the cursor should still be
|
|
// displayed while a video is playing.
|
|
_noCursorSwitch = false;
|
|
if (primary && (_vm->getGameType() == kGameTypeLostInTime)) {
|
|
if (!file.compareToIgnoreCase("PORTA03") ||
|
|
!file.compareToIgnoreCase("PORTA03A") ||
|
|
!file.compareToIgnoreCase("CALE1") ||
|
|
!file.compareToIgnoreCase("AMIL2") ||
|
|
!file.compareToIgnoreCase("AMIL3B") ||
|
|
!file.compareToIgnoreCase("DELB"))
|
|
_noCursorSwitch = true;
|
|
}
|
|
|
|
// WORKAROUND: In Woodruff, Coh Cott vanished in one video on her party.
|
|
// This is a bug in video, so we work around it.
|
|
_woodruffCohCottWorkaround = false;
|
|
if (primary && (_vm->getGameType() == kGameTypeWoodruff)) {
|
|
if (!file.compareToIgnoreCase("SQ32-03"))
|
|
_woodruffCohCottWorkaround = true;
|
|
}
|
|
|
|
if (!(properties.flags & kFlagNoVideo) && (properties.sprite >= 0)) {
|
|
bool ownSurf = (properties.sprite != Draw::kFrontSurface) && (properties.sprite != Draw::kBackSurface);
|
|
bool screenSize = properties.flags & kFlagScreenSurface;
|
|
|
|
if (ownSurf) {
|
|
_vm->_draw->_spritesArray[properties.sprite] =
|
|
_vm->_video->initSurfDesc(screenSize ? _vm->_width : video->decoder->getWidth(),
|
|
screenSize ? _vm->_height : video->decoder->getHeight(), 0);
|
|
}
|
|
|
|
if (!_vm->_draw->_spritesArray[properties.sprite] &&
|
|
(properties.sprite != Draw::kFrontSurface) &&
|
|
(properties.sprite != Draw::kBackSurface)) {
|
|
properties.sprite = -1;
|
|
video->surface.reset();
|
|
video->decoder->setSurfaceMemory();
|
|
properties.x = properties.y = 0;
|
|
} else {
|
|
video->surface = _vm->_draw->_spritesArray[properties.sprite];
|
|
if (properties.sprite == Draw::kFrontSurface)
|
|
video->surface = _vm->_draw->_frontSurface;
|
|
if (properties.sprite == Draw::kBackSurface)
|
|
video->surface = _vm->_draw->_backSurface;
|
|
|
|
video->decoder->setSurfaceMemory(video->surface->getData(),
|
|
video->surface->getWidth(), video->surface->getHeight(), video->surface->getBPP());
|
|
|
|
if (!ownSurf || (ownSurf && screenSize)) {
|
|
if ((properties.x >= 0) || (properties.y >= 0)) {
|
|
properties.x = (properties.x < 0) ? 0xFFFF : properties.x;
|
|
properties.y = (properties.y < 0) ? 0xFFFF : properties.y;
|
|
} else
|
|
properties.x = properties.y = -1;
|
|
} else
|
|
properties.x = properties.y = 0;
|
|
}
|
|
|
|
} else {
|
|
properties.sprite = -1;
|
|
video->surface.reset();
|
|
video->decoder->setSurfaceMemory();
|
|
properties.x = properties.y = 0;
|
|
}
|
|
}
|
|
|
|
video->decoder->setXY(properties.x, properties.y);
|
|
|
|
if (primary)
|
|
_needBlit = (properties.flags & kFlagUseBackSurfaceContent) && (properties.sprite == Draw::kFrontSurface);
|
|
|
|
properties.hasSound = video->decoder->hasSound();
|
|
|
|
if (!video->decoder->hasSound())
|
|
video->decoder->setFrameRate(_vm->_util->getFrameRate());
|
|
|
|
WRITE_VAR(7, video->decoder->getFrameCount());
|
|
|
|
return slot;
|
|
}
|
|
|
|
bool VideoPlayer::closeVideo(int slot) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return false;
|
|
|
|
video->close();
|
|
return true;
|
|
}
|
|
|
|
void VideoPlayer::closeLiveSound() {
|
|
for (int i = 1; i < kVideoSlotCount; i++) {
|
|
Video *video = getVideoBySlot(i);
|
|
if (!video)
|
|
continue;
|
|
|
|
if (video->live)
|
|
closeVideo(i);
|
|
}
|
|
}
|
|
|
|
void VideoPlayer::closeAll() {
|
|
for (int i = 0; i < kVideoSlotCount; i++)
|
|
closeVideo(i);
|
|
}
|
|
|
|
bool VideoPlayer::reopenVideo(int slot) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return true;
|
|
|
|
return reopenVideo(*video);
|
|
}
|
|
|
|
bool VideoPlayer::reopenAll() {
|
|
bool all = true;
|
|
for (int i = 0; i < kVideoSlotCount; i++)
|
|
if (!reopenVideo(i))
|
|
all = false;
|
|
|
|
return all;
|
|
}
|
|
|
|
void VideoPlayer::pauseVideo(int slot, bool pause) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video || !video->decoder)
|
|
return;
|
|
|
|
video->decoder->pauseVideo(pause);
|
|
}
|
|
|
|
void VideoPlayer::pauseAll(bool pause) {
|
|
for (int i = 0; i < kVideoSlotCount; i++)
|
|
pauseVideo(i, pause);
|
|
}
|
|
|
|
void VideoPlayer::finishVideoSound(int slot) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video || !video->decoder)
|
|
return;
|
|
|
|
video->decoder->finishSound();
|
|
}
|
|
|
|
void VideoPlayer::waitSoundEnd(int slot) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video || !video->decoder)
|
|
return;
|
|
|
|
video->decoder->finishSound();
|
|
|
|
while (video->decoder->isSoundPlaying())
|
|
_vm->_util->longDelay(1);
|
|
}
|
|
|
|
bool VideoPlayer::play(int slot, Properties &properties) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return false;
|
|
|
|
bool primary = slot == 0;
|
|
|
|
if (properties.startFrame < 0)
|
|
properties.startFrame = video->decoder->getCurFrame() + 1;
|
|
if (properties.lastFrame < 0)
|
|
properties.lastFrame = video->decoder->getFrameCount() - 1;
|
|
if (properties.endFrame < 0)
|
|
properties.endFrame = properties.lastFrame;
|
|
if (properties.palFrame < 0)
|
|
properties.palFrame = properties.startFrame;
|
|
|
|
properties.startFrame--;
|
|
properties.endFrame--;
|
|
properties.palFrame--;
|
|
|
|
if (primary) {
|
|
_vm->_draw->_showCursor = _noCursorSwitch ? 3 : 0;
|
|
|
|
if (properties.fade)
|
|
_vm->_palAnim->fade(0, -2, 0);
|
|
}
|
|
|
|
bool backwards = properties.startFrame > properties.lastFrame;
|
|
|
|
properties.canceled = false;
|
|
|
|
if (properties.noBlock) {
|
|
properties.waitEndFrame = false;
|
|
|
|
video->live = true;
|
|
video->properties = properties;
|
|
|
|
updateLive(slot, true);
|
|
return true;
|
|
}
|
|
|
|
if ((_vm->getGameType() != kGameTypeUrban) && (_vm->getGameType() != kGameTypeBambou))
|
|
// NOTE: For testing (and comfort?) purposes, we enable aborting of all videos.
|
|
// Except for Urban Runner and Bambou, where it leads to glitches
|
|
properties.breakKey = kShortKeyEscape;
|
|
|
|
while ((properties.startFrame != properties.lastFrame) &&
|
|
(properties.startFrame < (int32)(video->decoder->getFrameCount() - 1))) {
|
|
|
|
playFrame(slot, properties);
|
|
if (properties.canceled)
|
|
break;
|
|
|
|
properties.startFrame += backwards ? -1 : 1;
|
|
|
|
evalBgShading(*video);
|
|
|
|
if (primary && properties.fade) {
|
|
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
|
|
properties.fade = false;
|
|
}
|
|
|
|
if (!_noCursorSwitch && properties.waitEndFrame)
|
|
waitEndFrame(slot);
|
|
}
|
|
|
|
evalBgShading(*video);
|
|
|
|
return true;
|
|
}
|
|
|
|
void VideoPlayer::waitEndFrame(int slot, bool onlySound) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return;
|
|
|
|
if (!onlySound || video->decoder->hasSound()) {
|
|
uint32 waitTime = video->decoder->getTimeToNextFrame();
|
|
if (!video->decoder->hasSound())
|
|
waitTime = video->decoder->getStaticTimeToNextFrame();
|
|
|
|
_vm->_util->delay(waitTime);
|
|
}
|
|
}
|
|
|
|
bool VideoPlayer::isPlayingLive() const {
|
|
const Video *video = getVideoBySlot(0);
|
|
return video && video->live;
|
|
}
|
|
|
|
void VideoPlayer::updateLive(bool force) {
|
|
for (int i = 0; i < kVideoSlotCount; i++)
|
|
updateLive(i, force);
|
|
}
|
|
|
|
void VideoPlayer::updateLive(int slot, bool force) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video || !video->live)
|
|
return;
|
|
|
|
if (video->properties.startFrame >= (int32)(video->decoder->getFrameCount() - 1)) {
|
|
// Video ended
|
|
|
|
if (!video->properties.loop) {
|
|
if (!(video->properties.flags & kFlagNoVideo))
|
|
WRITE_VAR_OFFSET(212, (uint32)-1);
|
|
_vm->_vidPlayer->closeVideo(slot);
|
|
return;
|
|
} else {
|
|
video->decoder->seek(0, SEEK_SET, true);
|
|
video->properties.startFrame = -1;
|
|
}
|
|
}
|
|
|
|
if (video->properties.startFrame == video->properties.lastFrame)
|
|
// Current video sequence ended
|
|
return;
|
|
|
|
if (!force && (video->decoder->getTimeToNextFrame() > 0))
|
|
return;
|
|
|
|
if (!(video->properties.flags & kFlagNoVideo))
|
|
WRITE_VAR_OFFSET(212, video->properties.startFrame + 1);
|
|
|
|
bool backwards = video->properties.startFrame > video->properties.lastFrame;
|
|
playFrame(slot, video->properties);
|
|
|
|
video->properties.startFrame += backwards ? -1 : 1;
|
|
|
|
if (video->properties.fade) {
|
|
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
|
|
video->properties.fade = false;
|
|
}
|
|
}
|
|
|
|
bool VideoPlayer::playFrame(int slot, Properties &properties) {
|
|
Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return false;
|
|
|
|
bool primary = slot == 0;
|
|
|
|
if (video->decoder->getCurFrame() != properties.startFrame) {
|
|
|
|
if (properties.startFrame != -1) {
|
|
// Seek into the middle of the video
|
|
|
|
if (video->decoder->hasSound()) {
|
|
// But there's sound
|
|
|
|
if (properties.forceSeek) {
|
|
// And we force seeking => Seek
|
|
|
|
video->decoder->disableSound();
|
|
video->decoder->seek(properties.startFrame + 1, SEEK_SET, true);
|
|
}
|
|
|
|
} else
|
|
// No sound => We can safely seek
|
|
video->decoder->seek(properties.startFrame + 1, SEEK_SET, true);
|
|
|
|
} else {
|
|
// Seek to the start => We can safely seek
|
|
|
|
video->decoder->disableSound();
|
|
video->decoder->seek(0, SEEK_SET, true);
|
|
video->decoder->enableSound();
|
|
}
|
|
|
|
}
|
|
|
|
if (video->decoder->getCurFrame() > properties.startFrame)
|
|
// If the video is already beyond the wanted frame, skip
|
|
return true;
|
|
|
|
bool modifiedPal = false;
|
|
|
|
if (primary) {
|
|
// Pre-decoding palette and blitting, only for primary videos
|
|
|
|
if ((properties.startFrame == properties.palFrame) ||
|
|
((properties.startFrame == properties.endFrame) && (properties.palCmd == 8))) {
|
|
|
|
modifiedPal = true;
|
|
_vm->_draw->_applyPal = true;
|
|
|
|
if (properties.palCmd >= 4)
|
|
copyPalette(*video, properties.palStart, properties.palEnd);
|
|
}
|
|
|
|
if (modifiedPal && (properties.palCmd == 8) && (video->surface != _vm->_draw->_backSurface))
|
|
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
|
|
|
|
if (_needBlit)
|
|
_vm->_draw->forceBlit();
|
|
}
|
|
|
|
const Graphics::Surface *surface = video->decoder->decodeNextFrame();
|
|
|
|
WRITE_VAR(11, video->decoder->getCurFrame());
|
|
|
|
uint32 ignoreBorder = 0;
|
|
if (_woodruffCohCottWorkaround && (properties.startFrame == 31)) {
|
|
// WORKAROUND: This frame mistakenly masks Coh Cott, making her vanish
|
|
// To prevent that, we'll never draw that part
|
|
ignoreBorder = 50;
|
|
}
|
|
|
|
if (surface && primary) {
|
|
// Post-decoding palette and blitting, only for primary videos
|
|
|
|
if (_needBlit)
|
|
_vm->_draw->forceBlit(true);
|
|
|
|
if (modifiedPal && (properties.palCmd == 16)) {
|
|
if (video->surface == _vm->_draw->_backSurface)
|
|
_vm->_draw->forceBlit();
|
|
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
|
|
_vm->_draw->_noInvalidated = true;
|
|
_vm->_video->dirtyRectsAll();
|
|
}
|
|
|
|
if (video->decoder->hasPalette() && (properties.palCmd > 1)) {
|
|
copyPalette(*video, properties.palStart, properties.palEnd);
|
|
|
|
if (video->surface != _vm->_draw->_backSurface)
|
|
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
|
|
else
|
|
_vm->_draw->_applyPal = true;
|
|
}
|
|
|
|
const Common::List<Common::Rect> &dirtyRects = video->decoder->getDirtyRects();
|
|
|
|
if (modifiedPal && (properties.palCmd == 8) && (video->surface == _vm->_draw->_backSurface))
|
|
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
|
|
|
|
if (video->surface == _vm->_draw->_backSurface) {
|
|
|
|
for (Common::List<Common::Rect>::const_iterator rect = dirtyRects.begin(); rect != dirtyRects.end(); ++rect)
|
|
_vm->_draw->invalidateRect(rect->left + ignoreBorder, rect->top, rect->right - 1, rect->bottom - 1);
|
|
if (!video->live)
|
|
_vm->_draw->blitInvalidated();
|
|
|
|
} else if (video->surface == _vm->_draw->_frontSurface) {
|
|
for (Common::List<Common::Rect>::const_iterator rect = dirtyRects.begin(); rect != dirtyRects.end(); ++rect)
|
|
_vm->_video->dirtyRectsAdd(rect->left + ignoreBorder, rect->top, rect->right - 1, rect->bottom - 1);
|
|
|
|
}
|
|
|
|
if (!video->live && ((video->decoder->getCurFrame() - 1) == properties.startFrame))
|
|
// Only retrace if we're playing the frame we actually want to play
|
|
_vm->_video->retrace();
|
|
|
|
int32 subtitle = video->decoder->getSubtitleIndex();
|
|
if (subtitle != -1)
|
|
_vm->_draw->printTotText(subtitle);
|
|
|
|
if (modifiedPal && ((properties.palCmd == 2) || (properties.palCmd == 4)))
|
|
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
|
|
}
|
|
|
|
if (primary && properties.waitEndFrame)
|
|
checkAbort(*video, properties);
|
|
|
|
if ((video->decoder->getCurFrame() - 1) < properties.startFrame)
|
|
// The video played a frame we actually didn't want, so we have to adjust
|
|
properties.startFrame--;
|
|
|
|
return true;
|
|
}
|
|
|
|
void VideoPlayer::checkAbort(Video &video, Properties &properties) {
|
|
_vm->_util->processInput();
|
|
|
|
if (_vm->shouldQuit()) {
|
|
video.decoder->disableSound();
|
|
|
|
properties.canceled = true;
|
|
return;
|
|
}
|
|
|
|
if (properties.breakKey != 0) {
|
|
_vm->_util->getMouseState(&_vm->_global->_inter_mouseX,
|
|
&_vm->_global->_inter_mouseY, &_vm->_game->_mouseButtons);
|
|
|
|
_vm->_inter->storeKey(_vm->_util->checkKey());
|
|
|
|
// Check for that specific key
|
|
bool pressedBreak = (VAR(0) == (unsigned)properties.breakKey);
|
|
|
|
// Mouse buttons
|
|
if (properties.breakKey < 4)
|
|
if (_vm->_game->_mouseButtons & properties.breakKey)
|
|
pressedBreak = true;
|
|
|
|
// Any key
|
|
if (properties.breakKey == 4)
|
|
if (VAR(0) != 0)
|
|
pressedBreak = true;
|
|
|
|
if (pressedBreak) {
|
|
video.decoder->disableSound();
|
|
|
|
// Seek to the last frame. Some scripts depend on that.
|
|
video.decoder->seek(properties.endFrame + 1, SEEK_SET, true);
|
|
|
|
properties.canceled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool VideoPlayer::slotIsOpen(int slot) const {
|
|
return getVideoBySlot(slot) != 0;
|
|
}
|
|
|
|
Common::String VideoPlayer::getFileName(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return "";
|
|
|
|
return video->fileName;
|
|
}
|
|
|
|
uint32 VideoPlayer::getFrameCount(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return video->decoder->getFrameCount();
|
|
}
|
|
|
|
uint32 VideoPlayer::getCurrentFrame(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return video->decoder->getCurFrame();
|
|
}
|
|
|
|
uint16 VideoPlayer::getWidth(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return video->decoder->getWidth();
|
|
}
|
|
|
|
uint16 VideoPlayer::getHeight(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return video->decoder->getHeight();
|
|
}
|
|
|
|
uint16 VideoPlayer::getDefaultX(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return video->decoder->getDefaultX();
|
|
}
|
|
|
|
uint16 VideoPlayer::getDefaultY(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return video->decoder->getDefaultY();
|
|
}
|
|
|
|
const Common::List<Common::Rect> *VideoPlayer::getDirtyRects(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return &video->decoder->getDirtyRects();
|
|
}
|
|
|
|
bool VideoPlayer::hasEmbeddedFile(const Common::String &fileName, int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return false;
|
|
|
|
return video->decoder->hasEmbeddedFile(fileName);
|
|
}
|
|
|
|
Common::SeekableReadStream *VideoPlayer::getEmbeddedFile(const Common::String &fileName, int slot) {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return 0;
|
|
|
|
return video->decoder->getEmbeddedFile(fileName);
|
|
}
|
|
|
|
int32 VideoPlayer::getSubtitleIndex(int slot) const {
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return -1;
|
|
|
|
return video->decoder->getSubtitleIndex();
|
|
}
|
|
|
|
void VideoPlayer::writeVideoInfo(const Common::String &file, int16 varX, int16 varY,
|
|
int16 varFrames, int16 varWidth, int16 varHeight) {
|
|
|
|
Properties properties;
|
|
|
|
int slot = openVideo(false, file, properties);
|
|
if (slot >= 0) {
|
|
Video &video = _videoSlots[slot];
|
|
|
|
int16 x = -1, y = -1, width = -1, height = -1;
|
|
|
|
x = video.decoder->getDefaultX();
|
|
y = video.decoder->getDefaultY();
|
|
width = video.decoder->getWidth();
|
|
height = video.decoder->getHeight();
|
|
|
|
if (VAR_OFFSET(varX) == 0xFFFFFFFF)
|
|
video.decoder->getFrameCoords(1, x, y, width, height);
|
|
|
|
WRITE_VAR_OFFSET(varX , x);
|
|
WRITE_VAR_OFFSET(varY , y);
|
|
WRITE_VAR_OFFSET(varFrames, video.decoder->getFrameCount());
|
|
WRITE_VAR_OFFSET(varWidth , width);
|
|
WRITE_VAR_OFFSET(varHeight, height);
|
|
|
|
closeVideo(slot);
|
|
|
|
} else {
|
|
WRITE_VAR_OFFSET(varX , (uint32) -1);
|
|
WRITE_VAR_OFFSET(varY , (uint32) -1);
|
|
WRITE_VAR_OFFSET(varFrames, (uint32) -1);
|
|
WRITE_VAR_OFFSET(varWidth , (uint32) -1);
|
|
WRITE_VAR_OFFSET(varHeight, (uint32) -1);
|
|
}
|
|
}
|
|
|
|
bool VideoPlayer::copyFrame(int slot, Surface &dest,
|
|
uint16 left, uint16 top, uint16 width, uint16 height, uint16 x, uint16 y,
|
|
int32 transp) const {
|
|
|
|
const Video *video = getVideoBySlot(slot);
|
|
if (!video)
|
|
return false;
|
|
|
|
const Graphics::Surface *surface = video->decoder->getSurface();
|
|
if (!surface)
|
|
return false;
|
|
|
|
Surface src(surface->w, surface->h, surface->format.bytesPerPixel, (byte *)surface->pixels);
|
|
|
|
dest.blit(src, left, top, left + width - 1, top + height - 1, x, y, transp);
|
|
return true;
|
|
}
|
|
|
|
const VideoPlayer::Video *VideoPlayer::getVideoBySlot(int slot) const {
|
|
if ((slot < 0) || (slot >= kVideoSlotCount))
|
|
return 0;
|
|
|
|
if (_videoSlots[slot].isEmpty())
|
|
return 0;
|
|
|
|
return &_videoSlots[slot];
|
|
}
|
|
|
|
VideoPlayer::Video *VideoPlayer::getVideoBySlot(int slot) {
|
|
if ((slot < 0) || (slot >= kVideoSlotCount))
|
|
return 0;
|
|
|
|
if (_videoSlots[slot].isEmpty())
|
|
return 0;
|
|
|
|
return &_videoSlots[slot];
|
|
}
|
|
|
|
int VideoPlayer::getNextFreeSlot() {
|
|
// Starting with 1, since 0 is reserved for the "primary" video
|
|
for (int i = 1; i < kVideoSlotCount; i++)
|
|
if (_videoSlots[i].isEmpty())
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
void VideoPlayer::evalBgShading(Video &video) {
|
|
if (video.decoder->isSoundPlaying())
|
|
_vm->_sound->bgShade();
|
|
else
|
|
_vm->_sound->bgUnshade();
|
|
}
|
|
|
|
Common::String VideoPlayer::findFile(const Common::String &file, Properties &properties) {
|
|
|
|
bool hasExtension = false;
|
|
|
|
Common::String base = file;
|
|
Common::String fileName = file;
|
|
|
|
const char *posDot = strrchr(base.c_str(), '.');
|
|
if (posDot) {
|
|
hasExtension = true;
|
|
base = Common::String(base.c_str(), posDot);
|
|
posDot++;
|
|
}
|
|
|
|
if (hasExtension) {
|
|
int i;
|
|
for (i = 0; i < ARRAYSIZE(_extensions); i++) {
|
|
if (!scumm_stricmp(posDot, _extensions[i])) {
|
|
if ((properties.type != kVideoTypeTry) && (properties.type == ((Type) i))) {
|
|
warning("Attempted to open video \"%s\", but requested a different type", fileName.c_str());
|
|
return "";
|
|
}
|
|
properties.type = (Type) i;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= ARRAYSIZE(_extensions))
|
|
hasExtension = false;
|
|
}
|
|
|
|
if (!hasExtension) {
|
|
// No or unrecognized extension. Probing.
|
|
|
|
int i;
|
|
for (i = 0; i < ARRAYSIZE(_extensions); i++) {
|
|
if ((properties.type == kVideoTypeTry) || (properties.type == ((Type) i))) {
|
|
fileName = base + "." + _extensions[i];
|
|
|
|
if (_vm->_dataIO->hasFile(fileName)) {
|
|
properties.type = (Type) i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ((i >= ARRAYSIZE(_extensions)) || (properties.type == kVideoTypeTry)) {
|
|
warning("Couldn't open video \"%s\"", file.c_str());
|
|
return "";
|
|
}
|
|
|
|
}
|
|
|
|
return fileName;
|
|
}
|
|
|
|
::Video::CoktelDecoder *VideoPlayer::openVideo(const Common::String &file, Properties &properties) {
|
|
Common::String fileName = findFile(file, properties);
|
|
if (fileName.empty())
|
|
return 0;
|
|
|
|
Common::SeekableReadStream *stream = _vm->_dataIO->getFile(fileName);
|
|
if (!stream)
|
|
return 0;
|
|
|
|
::Video::CoktelDecoder *video = 0;
|
|
if (properties.type == kVideoTypeIMD)
|
|
video = new ::Video::IMDDecoder(_vm->_mixer, Audio::Mixer::kSFXSoundType);
|
|
else if (properties.type == kVideoTypePreIMD)
|
|
video = new ::Video::PreIMDDecoder(properties.width, properties.height, _vm->_mixer, Audio::Mixer::kSFXSoundType);
|
|
else if (properties.type == kVideoTypeVMD)
|
|
video = new ::Video::VMDDecoder(_vm->_mixer, Audio::Mixer::kSFXSoundType);
|
|
else if (properties.type == kVideoTypeRMD)
|
|
video = new ::Video::VMDDecoder(_vm->_mixer, Audio::Mixer::kSFXSoundType);
|
|
else
|
|
warning("Couldn't open video \"%s\": Invalid video Type", fileName.c_str());
|
|
|
|
if (!video) {
|
|
delete stream;
|
|
return 0;
|
|
}
|
|
|
|
if (!video->loadStream(stream)) {
|
|
delete video;
|
|
return 0;
|
|
}
|
|
|
|
properties.width = video->getWidth();
|
|
properties.height = video->getHeight();
|
|
|
|
return video;
|
|
}
|
|
|
|
bool VideoPlayer::reopenVideo(Video &video) {
|
|
if (video.isEmpty())
|
|
return true;
|
|
|
|
if (video.fileName.empty()) {
|
|
video.close();
|
|
return false;
|
|
}
|
|
|
|
Properties properties;
|
|
|
|
properties.type = video.properties.type;
|
|
|
|
Common::String fileName = findFile(video.fileName, properties);
|
|
if (fileName.empty()) {
|
|
video.close();
|
|
return false;
|
|
}
|
|
|
|
Common::SeekableReadStream *stream = _vm->_dataIO->getFile(fileName);
|
|
if (!stream) {
|
|
video.close();
|
|
return false;
|
|
}
|
|
|
|
if (!video.decoder->reloadStream(stream)) {
|
|
delete stream;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VideoPlayer::copyPalette(const Video &video, int16 palStart, int16 palEnd) {
|
|
if (!video.decoder->hasPalette() || !video.decoder->isPaletted())
|
|
return;
|
|
|
|
if (palStart < 0)
|
|
palStart = 0;
|
|
if (palEnd < 0)
|
|
palEnd = 255;
|
|
|
|
palStart = palStart * 3;
|
|
palEnd = (palEnd + 1) * 3;
|
|
|
|
for (int i = palStart; i <= palEnd; i++)
|
|
((char *)(_vm->_global->_pPaletteDesc->vgaPal))[i] = video.decoder->getPalette()[i] >> 2;
|
|
}
|
|
|
|
} // End of namespace Gob
|