scummvm/engines/agos/animation.cpp
D G Turner 783e3fea62 AGOS: Fix engine crash when playing Feeble Files from cabinet datafiles.
Playing with cabinet datafiles, this failed to find the "Charisma.smk"
file in the cabinets when in the Recreation (TV) room on Cygnus Alpha
and this caused an engine abort after the GUI dialog warning of the
"missing" video file. This was due to animation.cpp code using
Common::file::exists() instead of going via the ArchiveMan.

However,a hasFile() method implementation was also required to implement
fallback to decompressed (movie) files if the file requested is not in
the cabinet or the cabinet has been externally decompressed to files.

Thanks to fuzzie for the hasFile() patch.

Also, removed noisy warning which this correction triggers repeatedly in
installshield_cab.cpp hasFile(). This looks like leftover from
debugging, so not critical.
2011-11-13 23:44:38 +00:00

560 lines
13 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.
*
*/
#ifdef ENABLE_AGOS2
#include "common/endian.h"
#include "common/events.h"
#include "common/file.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "graphics/cursorman.h"
#include "graphics/palette.h"
#include "graphics/surface.h"
#include "agos/animation.h"
#include "agos/intern.h"
#include "agos/agos.h"
#include "audio/audiostream.h"
#include "audio/decoders/wave.h"
#include "gui/message.h"
namespace AGOS {
MoviePlayer::MoviePlayer(AGOSEngine_Feeble *vm)
: _vm(vm) {
_mixer = _vm->_mixer;
_leftButtonDown = false;
_rightButtonDown = false;
_skipMovie = false;
memset(baseName, 0, sizeof(baseName));
_ticks = 0;
}
MoviePlayer::~MoviePlayer() {
}
void MoviePlayer::play() {
if (_vm->getBitFlag(40)) {
_vm->setBitFlag(42, false);
startSound();
return;
}
_leftButtonDown = false;
_rightButtonDown = false;
_skipMovie = false;
_vm->_mixer->stopAll();
_ticks = _vm->_system->getMillis();
startSound();
playVideo();
stopVideo();
_vm->o_killAnimate();
if (_vm->getBitFlag(41)) {
_vm->fillBackFromFront();
} else {
uint8 palette[768];
memset(palette, 0, sizeof(palette));
_vm->clearSurfaces();
_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
}
_vm->fillBackGroundFromBack();
_vm->_fastFadeOutFlag = true;
}
void MoviePlayer::handleNextFrame() {
Common::Event event;
Common::EventManager *eventMan = _vm->_system->getEventManager();
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
_leftButtonDown = true;
_rightButtonDown = true;
} else if (event.kbd.keycode == Common::KEYCODE_PAUSE) {
_vm->pause();
}
break;
case Common::EVENT_LBUTTONDOWN:
_leftButtonDown = true;
break;
case Common::EVENT_RBUTTONDOWN:
_rightButtonDown = true;
break;
case Common::EVENT_LBUTTONUP:
_leftButtonDown = false;
break;
case Common::EVENT_RBUTTONUP:
_rightButtonDown = false;
break;
default:
break;
}
}
if (_leftButtonDown && _rightButtonDown && !_vm->getBitFlag(41)) {
_skipMovie = true;
_mixer->stopHandle(_bgSound);
}
}
///////////////////////////////////////////////////////////////////////////////
// Movie player for DXA movies
///////////////////////////////////////////////////////////////////////////////
const char *const MoviePlayerDXA::_sequenceList[90] = {
"agent32",
"Airlock",
"Badluck",
"bentalk1",
"bentalk2",
"bentalk3",
"BigFight",
"BLOWLAB",
"breakdown",
"bridge",
"button2",
"cargo",
"COACH",
"Colatalk",
"cygnus2",
"dream",
"escape2",
"FASALL",
"fbikewurb",
"feebdel",
"Feebohno",
"feebpump",
"feefone1",
"feefone2",
"founder2",
"founder3",
"founder4",
"fxmadsam",
"fxwakeup",
"gate",
"Get Car",
"getaxe",
"getlift",
"icetrench",
"intomb1",
"intomb2",
"Jackpot",
"knockout",
"labocto",
"longfeeb",
"Mainmin",
"maznat",
"meetsquid",
"mflirt",
"mfxHappy",
"Mix_Feeb1",
"Mix_Feeb2",
"Mix_Feeb3",
"Mix_Guardscn",
"Mlights1",
"MLights2",
"MProtest",
"mudman",
"munlock",
"MUS5P2",
"MUSOSP1",
"Omenter",
"Omnicofe",
"OUTMIN~1",
"Readbook",
"Rebelhq",
"RebelHQ2",
"Reedin",
"rescue1",
"rescue2",
"samcar",
"Samdead",
"scanner",
"Sleepy",
"spitbrai",
"statue1",
"statue2",
"sva1",
"sva2",
"Teeter",
"Temple2",
"Temple3",
"Temple4",
"Temple5",
"Temple6",
"Temple7",
"Temple8",
"Tic-tac2",
"torture",
"transmit",
"Typey",
"ventfall",
"ventoff",
"wasting",
"wurbatak"
};
MoviePlayerDXA::MoviePlayerDXA(AGOSEngine_Feeble *vm, const char *name)
: MoviePlayer(vm) {
debug(0, "Creating DXA cutscene player");
memset(baseName, 0, sizeof(baseName));
memcpy(baseName, name, strlen(name));
_sequenceNum = 0;
}
bool MoviePlayerDXA::load() {
if ((_vm->getPlatform() == Common::kPlatformAmiga || _vm->getPlatform() == Common::kPlatformMacintosh) &&
_vm->_language != Common::EN_ANY) {
_sequenceNum = 0;
for (uint i = 0; i < 90; i++) {
if (!scumm_stricmp(baseName, _sequenceList[i]))
_sequenceNum = i;
}
}
Common::String videoName = Common::String::format("%s.dxa", baseName);
Common::SeekableReadStream *videoStream = _vm->_archives.open(videoName);
if (!videoStream)
error("Failed to load video file %s", videoName.c_str());
if (!loadStream(videoStream))
error("Failed to load video stream from file %s", videoName.c_str());
debug(0, "Playing video %s", videoName.c_str());
CursorMan.showMouse(false);
_firstFrameOffset = _fileStream->pos();
return true;
}
void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
uint h = getHeight();
uint w = getWidth();
const Graphics::Surface *surface = decodeNextFrame();
byte *src = (byte *)surface->pixels;
dst += y * pitch + x;
do {
memcpy(dst, src, w);
dst += pitch;
src += w;
} while (--h);
if (hasDirtyPalette())
setSystemPalette();
}
void MoviePlayerDXA::playVideo() {
// Most of the videos included in the Amiga version, reduced the
// resoluton to 384 x 280, so require the screen to be cleared,
// before starting playing those videos.
if (getWidth() == 384 && getHeight() == 280) {
_vm->clearSurfaces();
}
while (!endOfVideo() && !_skipMovie && !_vm->shouldQuit())
handleNextFrame();
}
void MoviePlayerDXA::stopVideo() {
close();
_mixer->stopHandle(_bgSound);
}
void MoviePlayerDXA::startSound() {
uint32 offset, size;
if (getSoundTag() == MKTAG('W','A','V','E')) {
size = _fileStream->readUint32BE();
if (_sequenceNum) {
Common::File in;
_fileStream->seek(size, SEEK_CUR);
in.open((const char *)"audio.wav");
if (!in.isOpen()) {
error("Can't read offset file 'audio.wav'");
}
in.seek(_sequenceNum * 8, SEEK_SET);
offset = in.readUint32LE();
size = in.readUint32LE();
in.seek(offset, SEEK_SET);
_bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES);
in.close();
} else {
_bgSoundStream = Audio::makeWAVStream(_fileStream->readStream(size), DisposeAfterUse::YES);
}
} else {
_bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName);
}
if (_bgSoundStream != NULL) {
_vm->_mixer->stopHandle(_bgSound);
_vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_bgSound, _bgSoundStream);
}
}
void MoviePlayerDXA::nextFrame() {
if (_bgSoundStream && _vm->_mixer->isSoundHandleActive(_bgSound) && needsUpdate()) {
copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
return;
}
if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) {
_fileStream->seek(_firstFrameOffset);
_curFrame = -1;
startSound();
}
if (!endOfVideo()) {
if (_vm->_interactiveVideo == TYPE_OMNITV) {
copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
} else if (_vm->_interactiveVideo == TYPE_LOOPING) {
copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
}
} else if (_vm->_interactiveVideo == TYPE_OMNITV) {
close();
_vm->_interactiveVideo = 0;
_vm->_variableArray[254] = 6747;
}
}
void MoviePlayerDXA::handleNextFrame() {
if (processFrame())
_vm->_system->updateScreen();
MoviePlayer::handleNextFrame();
}
bool MoviePlayerDXA::processFrame() {
Graphics::Surface *screen = _vm->_system->lockScreen();
copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
_vm->_system->unlockScreen();
Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000);
if ((_bgSoundStream == NULL) || ((soundTime * getFrameRate()).toInt() / 1000 < getCurFrame() + 1)) {
if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) {
while (_mixer->isSoundHandleActive(_bgSound) && (soundTime * getFrameRate()).toInt() < getCurFrame()) {
_vm->_system->delayMillis(10);
soundTime = Common::Rational(_mixer->getSoundElapsedTime(_bgSound), 1000);
}
// In case the background sound ends prematurely, update
// _ticks so that we can still fall back on the no-sound
// sync case for the subsequent frames.
_ticks = _vm->_system->getMillis();
} else {
_ticks += getTimeToNextFrame();
while (_vm->_system->getMillis() < _ticks)
_vm->_system->delayMillis(10);
}
return true;
}
warning("dropped frame %i", getCurFrame());
return false;
}
///////////////////////////////////////////////////////////////////////////////
// Movie player for Smacker movies
///////////////////////////////////////////////////////////////////////////////
MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name)
: MoviePlayer(vm), SmackerDecoder(vm->_mixer) {
debug(0, "Creating SMK cutscene player");
memset(baseName, 0, sizeof(baseName));
memcpy(baseName, name, strlen(name));
}
bool MoviePlayerSMK::load() {
Common::String videoName = Common::String::format("%s.smk", baseName);
Common::SeekableReadStream *videoStream = _vm->_archives.open(videoName);
if (!videoStream)
error("Failed to load video file %s", videoName.c_str());
if (!loadStream(videoStream))
error("Failed to load video stream from file %s", videoName.c_str());
debug(0, "Playing video %s", videoName.c_str());
CursorMan.showMouse(false);
_firstFrameOffset = _fileStream->pos();
return true;
}
void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
uint h = getHeight();
uint w = getWidth();
const Graphics::Surface *surface = decodeNextFrame();
byte *src = (byte *)surface->pixels;
dst += y * pitch + x;
do {
memcpy(dst, src, w);
dst += pitch;
src += w;
} while (--h);
if (hasDirtyPalette())
setSystemPalette();
}
void MoviePlayerSMK::playVideo() {
while (!endOfVideo() && !_skipMovie && !_vm->shouldQuit())
handleNextFrame();
}
void MoviePlayerSMK::stopVideo() {
close();
}
void MoviePlayerSMK::startSound() {
}
void MoviePlayerSMK::handleNextFrame() {
processFrame();
MoviePlayer::handleNextFrame();
}
void MoviePlayerSMK::nextFrame() {
if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) {
_fileStream->seek(_firstFrameOffset);
_curFrame = -1;
}
if (!endOfVideo()) {
decodeNextFrame();
if (_vm->_interactiveVideo == TYPE_OMNITV) {
copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
} else if (_vm->_interactiveVideo == TYPE_LOOPING) {
copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
}
} else if (_vm->_interactiveVideo == TYPE_OMNITV) {
close();
_vm->_interactiveVideo = 0;
_vm->_variableArray[254] = 6747;
}
}
bool MoviePlayerSMK::processFrame() {
Graphics::Surface *screen = _vm->_system->lockScreen();
copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch);
_vm->_system->unlockScreen();
uint32 waitTime = getTimeToNextFrame();
if (!waitTime) {
warning("dropped frame %i", getCurFrame());
return false;
}
_vm->_system->updateScreen();
// Wait before showing the next frame
_vm->_system->delayMillis(waitTime);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Factory function for creating the appropriate cutscene player
///////////////////////////////////////////////////////////////////////////////
MoviePlayer *makeMoviePlayer(AGOSEngine_Feeble *vm, const char *name) {
char baseName[40];
char filename[20];
int baseLen = strlen(name) - 4;
memset(baseName, 0, sizeof(baseName));
memcpy(baseName, name, baseLen);
if (vm->getLanguage() == Common::DE_DEU && baseLen >= 8) {
// Check short filename to work around
// bug in a German Windows 2CD version.
char shortName[20];
memset(shortName, 0, sizeof(shortName));
memcpy(shortName, baseName, 6);
sprintf(filename, "%s~1.dxa", shortName);
if (vm->_archives.hasFile(filename)) {
memset(baseName, 0, sizeof(baseName));
memcpy(baseName, filename, 8);
}
sprintf(filename, "%s~1.smk", shortName);
if (vm->_archives.hasFile(filename)) {
memset(baseName, 0, sizeof(baseName));
memcpy(baseName, filename, 8);
}
}
sprintf(filename, "%s.dxa", baseName);
if (vm->_archives.hasFile(filename)) {
return new MoviePlayerDXA(vm, baseName);
}
sprintf(filename, "%s.smk", baseName);
if (vm->_archives.hasFile(filename)) {
return new MoviePlayerSMK(vm, baseName);
}
Common::String buf = Common::String::format(_("Cutscene file '%s' not found!"), baseName);
GUI::MessageDialog dialog(buf, _("OK"));
dialog.runModal();
return NULL;
}
} // End of namespace AGOS
#endif // ENABLE_AGOS2