/* 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 3 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, see . * */ #include "engines/util.h" #include "video/avi_decoder.h" #include "video/smk_decoder.h" #include "video/theora_decoder.h" #include "asylum/views/video.h" #include "asylum/system/config.h" #include "asylum/system/cursor.h" #include "asylum/system/graphics.h" #include "asylum/system/savegame.h" #include "asylum/system/screen.h" #include "asylum/system/sound.h" #include "asylum/system/text.h" #include "asylum/asylum.h" #include "asylum/respack.h" #include "asylum/staticres.h" namespace Asylum { VideoPlayer::VideoPlayer(AsylumEngine *engine, Audio::Mixer *mixer) : _vm(engine), _currentMovie(0), _subtitleIndex(0), _subtitleCounter(0), _previousFont(kResourceNone), _done(false) { memset(_subtitlePalette, 0, sizeof(_subtitlePalette)); if (_vm->checkGameVersion("Steam")) { #ifdef USE_THEORADEC _decoder = new Video::TheoraDecoder(); Common::File paletteFile; paletteFile.open("palette"); paletteFile.read(_subtitlePalette, PALETTE_SIZE); paletteFile.close(); #else error("The Steam version of the game uses Theora videos but ScummVM has been compiled without Theora support"); #endif } else if (_vm->isAltDemo()) { _decoder = new Video::AVIDecoder(); } else { _decoder = new Video::SmackerDecoder(); } } VideoPlayer::~VideoPlayer() { delete _decoder; } ////////////////////////////////////////////////////////////////////////// // Event Handler ////////////////////////////////////////////////////////////////////////// bool VideoPlayer::handleEvent(const AsylumEvent &evt) { switch ((int32)evt.type) { default: break; case EVENT_ASYLUM_INIT: if (!_vm->checkGameVersion("Demo")) _previousFont = getText()->loadFont(MAKE_RESOURCE(kResourcePackShared, 57)); _subtitleCounter = 0; _subtitleIndex = -1; break; case EVENT_ASYLUM_DEINIT: getScreen()->clear(); if (!_vm->checkGameVersion("Demo")) getText()->loadFont(_previousFont); break; case EVENT_ASYLUM_SUBTITLE: { int32 newIndex = (evt.param2 == 1) ? evt.param1 : -1; if (_subtitleIndex != newIndex) { _subtitleIndex = newIndex; _subtitleCounter = 2; } if (_subtitleCounter > 0) { getScreen()->fillRect(0, 400, 640, 80, 0); if (_subtitleIndex >= 0) { char *text = getText()->get(_subtitles[_subtitleIndex].resourceId); int16 y = (int16)(10 * (44 - getText()->draw(0, 99, kTextCalculate, Common::Point(10, 400), 20, 620, text))); if (y <= 400) y = 405; getText()->draw(0, 99, kTextCenter, Common::Point(10, y), 20, 620, text); if (_vm->checkGameVersion("Steam")) { Graphics::Surface *st = getScreen()->getSurface()->convertTo(g_system->getScreenFormat(), _subtitlePalette); g_system->copyRectToScreen((const byte *)st->getBasePtr(0, 400), st->pitch, 0, 400, 640, 80); st->free(); delete st; } } --_subtitleCounter; } return true; } case Common::EVENT_LBUTTONDOWN: case Common::EVENT_KEYDOWN: case Common::EVENT_JOYBUTTON_DOWN: case Common::EVENT_CUSTOM_ENGINE_ACTION_START: _done = true; if (!_vm->checkGameVersion("Steam") && !_vm->isAltDemo()) getScreen()->clear(); // Original set a value that does not seems to be used anywhere return true; } return false; } ////////////////////////////////////////////////////////////////////////// // Playing ////////////////////////////////////////////////////////////////////////// void VideoPlayer::play(uint32 videoNumber, EventHandler *handler) { getSaveLoad()->setMovieViewed(videoNumber); _currentMovie = videoNumber; // Prepare getCursor()->hide(); getSharedData()->setFlag(kFlag1, true); getScreen()->paletteFade(0, 25, 10); getSound()->stopAll(); // Play movie _vm->switchEventHandler(this); Common::String filename; if (_vm->checkGameVersion("Steam")) filename = videoNumber == 0 ? "mov000_2_smk.ogv" : Common::String::format("mov%03d_smk.ogv", videoNumber); else if (_vm->isAltDemo()) filename = Common::String::format("mov%03d.avi", videoNumber); else filename = Common::String::format("mov%03d.smk", videoNumber); play(Common::Path(filename), Config.showMovieSubtitles); // Cleanup and switch to previous event handler getCursor()->show(); getSharedData()->setFlag(kFlag1, false); _vm->switchEventHandler(handler); } void VideoPlayer::play(const Common::Path &filename, bool showSubtitles) { if (!_decoder->loadFile(filename)) error("[Video::playVideo] Invalid video index (%d)", _currentMovie); int16 x = (int16)Common::Rational(g_system->getWidth() - _decoder->getWidth(), 2).toInt(); int16 y = (int16)Common::Rational(g_system->getHeight() - _decoder->getHeight(), 2).toInt(); getScreen()->clear(); // TODO check flags and setup volume panning // Load subtitles if (showSubtitles && !_vm->checkGameVersion("Demo")) loadSubtitles(); // Setup playing _done = false; uint32 index = 0; int32 frameStart = 0; int32 frameEnd = 0; int32 currentSubtitle = 0; if (_vm->checkGameVersion("Steam") || _vm->isAltDemo()) { _decoder->setOutputPixelFormats(g_system->getSupportedFormats()); Graphics::PixelFormat decoderFormat = _decoder->getPixelFormat(); initGraphics(640, 480, &decoderFormat); } _decoder->start(); while (!_done && !Engine::shouldQuit() && !_decoder->endOfVideo()) { _vm->handleEvents(); if (_decoder->needsUpdate()) { const Graphics::Surface *frame = _decoder->decodeNextFrame(); if (!frame) continue; if (_vm->checkGameVersion("Steam") || _vm->isAltDemo()) { g_system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, x, y, frame->w, frame->h); } else { if (_decoder->hasDirtyPalette()) setupPalette(); getScreen()->copyToBackBuffer((const byte *)frame->getPixels(), frame->pitch, x, y, frame->w, frame->h); } if (showSubtitles) { int32 currentFrame = _decoder->getCurFrame() + 1; debugC(kDebugLevelVideo, "[Video] {%s} Playing Frame %d", filename.toString(Common::Path::kNativeSeparator).c_str(), currentFrame); // Check for next frame if (currentFrame > frameEnd) { if (index < _subtitles.size()) { frameStart = _subtitles[index].frameStart; frameEnd = _subtitles[index].frameEnd; currentSubtitle = index; ++index; } } if (currentFrame < frameStart || currentFrame > frameEnd) _vm->notify(EVENT_ASYLUM_SUBTITLE, 0, 0); else _vm->notify(EVENT_ASYLUM_SUBTITLE, currentSubtitle, 1); } if (!_vm->checkGameVersion("Steam") && !_vm->isAltDemo()) getScreen()->copyBackBufferToScreen(); g_system->updateScreen(); } if (!_vm->checkGameVersion("Steam") && !_vm->isAltDemo()) g_system->delayMillis(10); } if (_vm->checkGameVersion("Steam") || _vm->isAltDemo()) initGraphics(640, 480); _decoder->close(); _subtitles.clear(); } void VideoPlayer::setupPalette() { getScreen()->setMainPalette(_decoder->getPalette()); getScreen()->setupPalette(nullptr, 0, 0); } void VideoPlayer::loadSubtitles() { char movieToken[10]; snprintf(movieToken, 10, "[MOV%03d]", _currentMovie); Common::File subsFile; subsFile.open("vids.cap"); uint32 fileSize = (uint32)subsFile.size(); char *buffer = new char[fileSize + 1]; subsFile.read(buffer, fileSize); subsFile.close(); buffer[fileSize] = 0; char *start = strstr(buffer, movieToken); char *line = nullptr; if (start) { start += 20; // skip token, newline and "CAPTION = " uint32 count = strcspn(start, "\r\n"); line = new char[count + 1]; strncpy(line, start, count); line[count] = 0; char *tok = strtok(line, " "); while (tok) { VideoSubtitle newSubtitle; newSubtitle.frameStart = atoi(tok); tok = strtok(nullptr, " "); if (!tok) error("[Video::loadSubtitles] Invalid subtitle (frame end missing)!"); newSubtitle.frameEnd = atoi(tok); tok = strtok(nullptr, " "); if (!tok) error("[Video::loadSubtitles] Invalid subtitle (resource id missing)!"); int index = atoi(tok); // Original bug: index starts from 1 instead of 0 if (_currentMovie == 36) index--; newSubtitle.resourceId = (ResourceId)(index + video_subtitle_resourceIds[_currentMovie]); tok = strtok(nullptr, " "); _subtitles.push_back(newSubtitle); } delete [] line; } delete [] buffer; } } // end of namespace Asylum