/* 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. * * $URL$ * $Id$ * */ #include "common/endian.h" #include "common/events.h" #include "common/system.h" #include "graphics/cursorman.h" #include "graphics/surface.h" #include "agos/animation.h" #include "agos/intern.h" #include "agos/agos.h" #include "sound/audiostream.h" #include "sound/wave.h" namespace AGOS { MoviePlayer::MoviePlayer(AGOSEngine *vm, Audio::Mixer *mixer) : DXAPlayer(), _vm(vm), _mixer(mixer) { _omniTV = false; _omniTVFile = 0; _leftButtonDown = false; _rightButtonDown = false; memset(baseName, 0, sizeof(baseName)); _sequenceNum = 0; _ticks = 0; } bool MoviePlayer::load(const char *filename) { char videoName[20]; uint i; int baseLen = strlen(filename) - 4; memset(baseName, 0, sizeof(baseName)); memcpy(baseName, filename, baseLen); // Change file extension to dxa sprintf(videoName, "%s.dxa", baseName); if (!loadFile(videoName)) { // Check short filename to work around // bug in a German Windows 2CD version. if (baseLen >= 8) { char shortName[20]; memset(shortName, 0, sizeof(shortName)); memcpy(shortName, filename, 6); sprintf(shortName, "%s~1.dxa", shortName); if (!loadFile(shortName)) error("Failed to load video file %s or %s", videoName, shortName); memset(baseName, 0, sizeof(baseName)); memcpy(baseName, shortName, 8); debug(0, "Playing video %s", shortName); } else { error("Failed to load video file %s", videoName); } } else { debug(0, "Playing video %s", videoName); } CursorMan.showMouse(false); if ((_vm->getPlatform() == Common::kPlatformAmiga || _vm->getPlatform() == Common::kPlatformMacintosh) && _vm->_language != Common::EN_ANY) { _sequenceNum = 0; for (i = 0; i < 90; i++) { if (!scumm_stricmp(baseName, _sequenceList[i])) _sequenceNum = i; } } return true; } void MoviePlayer::playOmniTV() { // Load OmniTV video if (_fd) { _vm->setBitFlag(42, false); _omniTV = true; startSound(); } else { if (_omniTVFile) { // Restore state _fd = _omniTVFile; _mixer->pauseHandle(_omniTVSound, false); _vm->setBitFlag(42, false); _omniTV = true; } else { _vm->_variableArray[254] = 6747; } } } void MoviePlayer::play() { if (_vm->getBitFlag(40)) { playOmniTV(); return; } if (_omniTVFile) { // Clear any paused OmniTV video _mixer->stopHandle(_omniTVSound); delete _omniTVFile; _omniTVFile = 0; } _leftButtonDown = false; _rightButtonDown = false; _mixer->stopAll(); // Resolution is smaller in Amiga verison so always clear screen if (_width == 384 && _height == 280) { _vm->clearSurfaces(); } _ticks = _vm->_system->getMillis(); startSound(); while (_frameNum < _framesCount) handleNextFrame(); closeFile(); _vm->o_killAnimate(); if (_vm->getBitFlag(41)) { _vm->fillBackFromFront(); } else { uint8 palette[1024]; memset(palette, 0, sizeof(palette)); _vm->clearSurfaces(); _vm->_system->setPalette(palette, 0, 256); } _vm->fillBackGroundFromBack(); _vm->_fastFadeOutFlag = true; } void MoviePlayer::startSound() { byte *buffer; uint32 offset, size, tag; tag = _fd->readUint32BE(); if (tag == MKID_BE('WAVE')) { size = _fd->readUint32BE(); if (_sequenceNum) { Common::File in; _fd->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(); buffer = (byte *)malloc(size); in.seek(offset, SEEK_SET); in.read(buffer, size); in.close(); } else { buffer = (byte *)malloc(size); _fd->read(buffer, size); } Common::MemoryReadStream stream(buffer, size); _bgSoundStream = Audio::makeWAVStream(stream); free(buffer); } else { _bgSoundStream = Audio::AudioStream::openStreamFile(baseName); } if (_bgSoundStream != NULL) { if (_omniTV) { _mixer->stopHandle(_omniTVSound); _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_omniTVSound, _bgSoundStream); } else { _mixer->stopHandle(_bgSound); _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_bgSound, _bgSoundStream); } } } void MoviePlayer::nextFrame() { if (!_omniTV) return; if (_vm->getBitFlag(42)) { // Save state _omniTVFile = _fd; _mixer->pauseHandle(_omniTVSound, true); _fd = 0; _omniTV = false; return; } if (_mixer->isSoundHandleActive(_bgSound) && (_mixer->getSoundElapsedTime(_bgSound) * _framesPerSec) / 1000 < _frameNum) { copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth); return; } if (_frameNum < _framesCount) { decodeNextFrame(); copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth); _frameNum++; } else { _omniTV = false; _omniTVFile = 0; closeFile(); _vm->_variableArray[254] = 6747; } } void MoviePlayer::handleNextFrame() { decodeNextFrame(); if (processFrame()) _vm->_system->updateScreen(); _frameNum++; Common::Event event; Common::EventManager *eventMan = _vm->_system->getEventManager(); while (eventMan->pollEvent(event)) { switch (event.type) { case Common::EVENT_KEYDOWN: if (event.kbd.ascii == 27) { _leftButtonDown = true; _rightButtonDown = true; } 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; case Common::EVENT_QUIT: _vm->_system->quit(); break; default: break; } } if (_leftButtonDown && _rightButtonDown && !_vm->getBitFlag(41)) { _frameNum = _framesCount; _mixer->stopHandle(_bgSound); } } void MoviePlayer::setPalette(byte *pal) { byte palette[1024]; byte *p = palette; for (int i = 0; i < 256; i++) { *p++ = *pal++; *p++ = *pal++; *p++ = *pal++; *p++ = 0; } _vm->_system->setPalette(palette, 0, 256); } bool MoviePlayer::processFrame() { Graphics::Surface *screen = _vm->_system->lockScreen(); copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - _width) / 2, (_vm->_screenHeight - _height) / 2, _vm->_screenWidth); _vm->_system->unlockScreen(); if ((_bgSoundStream == NULL) || ((int)(_mixer->getSoundElapsedTime(_bgSound) * _framesPerSec) / 1000 < _frameNum + 1) || _frameSkipped > _framesPerSec) { if (_frameSkipped > _framesPerSec) { warning("force frame %i redraw", _frameNum); _frameSkipped = 0; } if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) { while (_mixer->isSoundHandleActive(_bgSound) && (_mixer->getSoundElapsedTime(_bgSound) * _framesPerSec) / 1000 < _frameNum) { _vm->_system->delayMillis(10); } // 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 += _frameTicks; while (_vm->_system->getMillis() < _ticks) _vm->_system->delayMillis(10); } return true; } warning("dropped frame %i", _frameNum); _frameSkipped++; return false; } const char * MoviePlayer::_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" }; } // End of namespace AGOS