diff options
author | Einar Johan Trøan Sømåen | 2014-11-05 20:22:34 +0100 |
---|---|---|
committer | Einar Johan Trøan Sømåen | 2014-11-05 20:22:34 +0100 |
commit | 784241c17b57c31af0722d3d98d89314c40743cd (patch) | |
tree | 249e87f5e89acf7cf934b83b23c7e6d4be31e5ad | |
parent | eff78c442474185ecb1bde59bfc54c276178c480 (diff) | |
parent | 9787fc5768fdc7338433fa5bcbc606f7d8ffe1d2 (diff) | |
download | scummvm-rg350-784241c17b57c31af0722d3d98d89314c40743cd.tar.gz scummvm-rg350-784241c17b57c31af0722d3d98d89314c40743cd.tar.bz2 scummvm-rg350-784241c17b57c31af0722d3d98d89314c40743cd.zip |
Merge pull request #437 from tobiatesan/wme_subtitles
WME Subtitles
-rw-r--r-- | engines/wintermute/base/base_game.cpp | 5 | ||||
-rw-r--r-- | engines/wintermute/base/base_game.h | 2 | ||||
-rw-r--r-- | engines/wintermute/base/base_game_settings.cpp | 5 | ||||
-rw-r--r-- | engines/wintermute/base/base_game_settings.h | 1 | ||||
-rw-r--r-- | engines/wintermute/base/base_string_table.cpp | 9 | ||||
-rw-r--r-- | engines/wintermute/base/base_string_table.h | 1 | ||||
-rw-r--r-- | engines/wintermute/module.mk | 2 | ||||
-rw-r--r-- | engines/wintermute/video/subtitle_card.cpp | 56 | ||||
-rw-r--r-- | engines/wintermute/video/subtitle_card.h | 53 | ||||
-rw-r--r-- | engines/wintermute/video/video_subtitler.cpp | 266 | ||||
-rw-r--r-- | engines/wintermute/video/video_subtitler.h | 53 | ||||
-rw-r--r-- | engines/wintermute/video/video_theora_player.cpp | 27 | ||||
-rw-r--r-- | engines/wintermute/video/video_theora_player.h | 6 |
13 files changed, 475 insertions, 11 deletions
diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp index 8df39c825a..668053bb3a 100644 --- a/engines/wintermute/base/base_game.cpp +++ b/engines/wintermute/base/base_game.cpp @@ -3896,6 +3896,11 @@ void BaseGame::expandStringByStringTable(char **str) const { _settings->expandStringByStringTable(str); } +////////////////////////////////////////////////////////////////////////// +void BaseGame::expandStringByStringTable(Common::String &str) const { + _settings->expandStringByStringTable(str); +} + char *BaseGame::getKeyFromStringTable(const char *str) const { return _settings->getKeyFromStringTable(str); } diff --git a/engines/wintermute/base/base_game.h b/engines/wintermute/base/base_game.h index cdbbff6c93..e535cc9618 100644 --- a/engines/wintermute/base/base_game.h +++ b/engines/wintermute/base/base_game.h @@ -123,6 +123,7 @@ public: inline BaseObject *getMainObject() { return _mainObject; } inline BaseFont *getSystemFont() { return _systemFont; } + inline BaseFont *getVideoFont() { return _videoFont; } bool initInput(); bool initLoop(); @@ -140,6 +141,7 @@ public: // String Table void expandStringByStringTable(char **str) const; + void expandStringByStringTable(Common::String &str) const; char *getKeyFromStringTable(const char *str) const; void LOG(bool res, const char *fmt, ...); diff --git a/engines/wintermute/base/base_game_settings.cpp b/engines/wintermute/base/base_game_settings.cpp index 61c5894be3..996bada997 100644 --- a/engines/wintermute/base/base_game_settings.cpp +++ b/engines/wintermute/base/base_game_settings.cpp @@ -215,6 +215,11 @@ void BaseGameSettings::expandStringByStringTable(char **str) const { _stringTable->expand(str); } +////////////////////////////////////////////////////////////////////////// +void BaseGameSettings::expandStringByStringTable(Common::String &str) const { + _stringTable->expand(str); +} + char *BaseGameSettings::getKeyFromStringTable(const char *str) const { return _stringTable->getKey(str); } diff --git a/engines/wintermute/base/base_game_settings.h b/engines/wintermute/base/base_game_settings.h index 2059cb075e..15afb06450 100644 --- a/engines/wintermute/base/base_game_settings.h +++ b/engines/wintermute/base/base_game_settings.h @@ -46,6 +46,7 @@ public: bool loadSettings(const char *filename); bool loadStringTable(const char *filename, bool clearOld); void expandStringByStringTable(char **str) const; + void expandStringByStringTable(Common::String &str) const; char *getKeyFromStringTable(const char *str) const; bool persist(BasePersistenceManager *persistMgr); diff --git a/engines/wintermute/base/base_string_table.cpp b/engines/wintermute/base/base_string_table.cpp index 89407a7b0e..4c750ebc93 100644 --- a/engines/wintermute/base/base_string_table.cpp +++ b/engines/wintermute/base/base_string_table.cpp @@ -147,6 +147,15 @@ void BaseStringTable::expand(char **str) const { } } +////////////////////////////////////////////////////////////////////////// +void BaseStringTable::expand(Common::String &str) const { + char *tmp = new char[str.size()+1]; + strcpy(tmp, str.c_str()); + expand(&tmp); + str = tmp; + delete[] tmp; +} + ////////////////////////////////////////////////////////////////////////// const char *BaseStringTable::expandStatic(const char *string) const { diff --git a/engines/wintermute/base/base_string_table.h b/engines/wintermute/base/base_string_table.h index cdcf11917d..cfa3eeb226 100644 --- a/engines/wintermute/base/base_string_table.h +++ b/engines/wintermute/base/base_string_table.h @@ -41,6 +41,7 @@ class BaseStringTable : public BaseClass { public: bool loadFile(const char *filename, bool deleteAll = true); void expand(char **str) const; + void expand(Common::String &str) const; const char *expandStatic(const char *string) const; bool addString(const char *key, const char *val, bool reportDuplicities = true); BaseStringTable(BaseGame *inGame); diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk index 1b6c52e0b7..4c95314a02 100644 --- a/engines/wintermute/module.mk +++ b/engines/wintermute/module.mk @@ -108,7 +108,9 @@ MODULE_OBJS := \ utils/path_util.o \ utils/string_util.o \ utils/utils.o \ + video/subtitle_card.o \ video/video_player.o \ + video/video_subtitler.o \ video/video_theora_player.o \ debugger.o \ wintermute.o \ diff --git a/engines/wintermute/video/subtitle_card.cpp b/engines/wintermute/video/subtitle_card.cpp new file mode 100644 index 0000000000..5d882502fd --- /dev/null +++ b/engines/wintermute/video/subtitle_card.cpp @@ -0,0 +1,56 @@ +/* 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. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/video/subtitle_card.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +SubtitleCard::SubtitleCard(BaseGame *inGame, + const Common::String &text, + const uint &startFrame, + const uint &endFrame) : _gameRef(inGame), + _startFrame(startFrame), + _endFrame(endFrame) { + _text = text; + _gameRef->expandStringByStringTable(_text); +} + +uint32 SubtitleCard::getStartFrame() const { + return _startFrame; +} + +uint32 SubtitleCard::getEndFrame() const { + return _endFrame; +} + +Common::String SubtitleCard::getText() const { + return _text; +} + +} // End of namespace Wintermute diff --git a/engines/wintermute/video/subtitle_card.h b/engines/wintermute/video/subtitle_card.h new file mode 100644 index 0000000000..629df77287 --- /dev/null +++ b/engines/wintermute/video/subtitle_card.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SUBTITLECARD_H +#define WINTERMUTE_SUBTITLECARD_H + +#include "common/str.h" + +namespace Wintermute { + +class BaseGame; + +class SubtitleCard { +public: + SubtitleCard(BaseGame *inGame, const Common::String &text, const uint &startFrame, const uint &endFrame); + uint32 getEndFrame() const; + uint32 getStartFrame() const; + Common::String getText() const; +private: + BaseGame *_gameRef; + uint32 _endFrame; + uint32 _startFrame; + Common::String _text; +}; + +} // End of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_subtitler.cpp b/engines/wintermute/video/video_subtitler.cpp new file mode 100644 index 0000000000..95d938574b --- /dev/null +++ b/engines/wintermute/video/video_subtitler.cpp @@ -0,0 +1,266 @@ +/* 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. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/video/video_subtitler.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" + +namespace Wintermute { + +VideoSubtitler::VideoSubtitler(BaseGame *inGame): BaseClass(inGame) { + _lastSample = -1; + _currentSubtitle = 0; + _showSubtitle = false; +} + +VideoSubtitler::~VideoSubtitler(void) { + _subtitles.clear(); +} + +bool VideoSubtitler::loadSubtitles(const Common::String &filename, const Common::String &subtitleFile) { + if (filename.size() == 0) { + return false; + } + + _subtitles.clear(); + + _lastSample = -1; + _currentSubtitle = 0; + _showSubtitle = false; + + Common::String newFile; + + /* + * Okay, the expected behaviour is this: either we are + * provided with a subtitle file to use by the script when + * calling PlayTheora(), or we try to autodetect a suitable + * one which, for /some/path/movie/ogg is to be called + * /some/path/movie.sub + */ + if (subtitleFile.size() != 0) { + newFile = subtitleFile; + } else { + Common::String path = PathUtil::getDirectoryName(filename); + Common::String name = PathUtil::getFileNameWithoutExtension(filename); + Common::String ext = ".SUB"; + newFile = PathUtil::combine(path, name + ext); + } + + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(newFile, true, false); + + if (file == nullptr) { + return false; // no subtitles + } + + int fileSize = file->size(); + char *buffer = new char[fileSize]; + + file->read(buffer, fileSize); + + /* This is where we parse .sub files. + * Subtitles cards are in the form + * {StartFrame}{EndFrame} FirstLine | SecondLine \n + */ + int pos = 0; + + while (pos < fileSize) { + char *tokenStart = 0; + int tokenLength = 0; + int tokenPos = -1; + int lineLength = 0; + int start = -1; + int end = -1; + bool inToken = false; + + while (pos + lineLength < fileSize && + buffer[pos + lineLength] != '\n' && + buffer[pos + lineLength] != '\0') { + // Measure the line until we hit EOL, EOS or just hit the boundary + lineLength++; + } + + int realLength; + + if (pos + lineLength >= fileSize) { + realLength = lineLength - 0; + } else { + // If we got here the above loop exited after hitting "\0" "\n" + realLength = lineLength - 1; + } + + Common::String cardText; + char *fileLine = (char *)&buffer[pos]; + + for (int i = 0; i < realLength; i++) { + if (fileLine[i] == '{') { + if (!inToken) { + // We've hit the start of a Start/EndFrame token + inToken = true; + tokenStart = fileLine + i + 1; + tokenLength = 0; + tokenPos++; + } else { + // Actually, we were already inside an (invalid) one. + tokenLength++; + } + } else if (fileLine[i] == '}') { + if (inToken) { + // we were /inside/ a {.*} token, so this is the end of the block + inToken = false; + char *token = new char[tokenLength + 1]; + strncpy(token, tokenStart, tokenLength); + token[tokenLength] = '\0'; + if (tokenPos == 0) { + // Was this StartFrame... + start = atoi(token); + } else if (tokenPos == 1) { + // Or the EndFrame? + end = atoi(token); + } + delete[] token; + } else { + // This char is part of the plain text, just append it + cardText += fileLine[i]; + } + } else { + if (inToken) { + tokenLength++; + } else { + if (fileLine[i] == '|') { + // The pipe character signals a linebreak in the text + cardText += '\n'; + } else { + // This char is part of the plain text, just append it + cardText += fileLine[i]; + } + } + } + } + + if (start != -1 && cardText.size() > 0 && (start != 1 || end != 1)){ + // Add a subtitlecard based on the line we have just parsed + _subtitles.push_back(SubtitleCard(_gameRef, cardText, start, end)); + } + + pos += lineLength + 1; + } + + delete[] buffer; + // Succeeded loading subtitles! + + return true; +} + +void VideoSubtitler::display() { + if (_showSubtitle) { + + BaseFont *font; + + if (_gameRef->getVideoFont() == nullptr) { + font = _gameRef->getSystemFont(); + } else { + font = _gameRef->getVideoFont(); + } + + int textHeight = font->getTextHeight( + (const byte *)_subtitles[_currentSubtitle].getText().c_str(), + _gameRef->_renderer->getWidth()); + + font->drawText( + (const byte *)_subtitles[_currentSubtitle].getText().c_str(), + 0, + (_gameRef->_renderer->getHeight() - textHeight - 5), + (_gameRef->_renderer->getWidth()), + TAL_CENTER); + } +} + +void VideoSubtitler::update(uint32 frame) { + if (_subtitles.size() == 0) { + // Edge case: we have loaded subtitles early on... from a blank file. + return; + } + + if ((int32)frame != _lastSample) { + /* + * If the frame count hasn't advanced the previous state still matches + * the current frame (obviously). + */ + + _lastSample = frame; + // Otherwise, we update _lastSample; see above. + + _showSubtitle = false; + + bool overdue = (frame > _subtitles[_currentSubtitle].getEndFrame()); + bool hasNext = (_currentSubtitle + 1 < _subtitles.size()); + bool nextStarted = false; + if (hasNext) { + nextStarted = (_subtitles[_currentSubtitle + 1].getStartFrame() <= frame); + } + + while (_currentSubtitle < _subtitles.size() && + overdue && hasNext && nextStarted) { + /* + * We advance until we get past all overdue subtitles. + * We should exit the cycle when we either reach the first + * subtitle which is not overdue whose subsequent subtitle + * has not started yet (aka the one we must display now or + * the one which WILL be displayed when its time comes) + * and / or when we reach the last one. + */ + + _currentSubtitle++; + + overdue = (frame > _subtitles[_currentSubtitle].getEndFrame()); + hasNext = (_currentSubtitle + 1 < _subtitles.size()); + if (hasNext) { + nextStarted = (_subtitles[_currentSubtitle + 1].getStartFrame() <= frame); + } else { + nextStarted = false; + } + } + + bool currentValid = (_subtitles[_currentSubtitle].getEndFrame() != 0); + /* + * No idea why we do this check, carried over from Mnemonic's code. + * Possibly a workaround for buggy subtitles or some kind of sentinel? :-\ + */ + + bool currentStarted = frame >= _subtitles[_currentSubtitle].getStartFrame(); + + if (currentStarted && !overdue && currentValid) { + _showSubtitle = true; + } + } +} + +} // End of namespace Wintermute diff --git a/engines/wintermute/video/video_subtitler.h b/engines/wintermute/video/video_subtitler.h new file mode 100644 index 0000000000..94f22909a1 --- /dev/null +++ b/engines/wintermute/video/video_subtitler.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on Wintermute Engine + * http://dead-code.org/redir.php?target=wme + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VIDSUBTITLER_H +#define WINTERMUTE_VIDSUBTITLER_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/video/subtitle_card.h" + +namespace Wintermute { + +class VideoSubtitler : public BaseClass { +public: + VideoSubtitler(BaseGame *inGame); + virtual ~VideoSubtitler(void); + bool loadSubtitles(const Common::String &filename, const Common::String &subtitleFile); + void display(); + void update(uint32 frame); +private: + Common::Array<SubtitleCard> _subtitles; + int32 _lastSample; + bool _showSubtitle; + uint32 _currentSubtitle; +}; + +} // End of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_theora_player.cpp b/engines/wintermute/video/video_theora_player.cpp index e1553580ec..22c235c848 100644 --- a/engines/wintermute/video/video_theora_player.cpp +++ b/engines/wintermute/video/video_theora_player.cpp @@ -85,14 +85,14 @@ void VideoTheoraPlayer::SetDefaults() { _volume = 100; _theoraDecoder = nullptr; - // TODO: Add subtitles-support - //_subtitler = nullptr; + _subtitler = new VideoSubtitler(_gameRef); + _foundSubtitles = false; } ////////////////////////////////////////////////////////////////////////// VideoTheoraPlayer::~VideoTheoraPlayer(void) { cleanup(); -// SAFE_DELETE(_subtitler); + delete _subtitler; } ////////////////////////////////////////////////////////////////////////// @@ -130,6 +130,9 @@ bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common: warning("VideoTheoraPlayer::initialize - Theora support not compiled in, video will be skipped: %s", filename.c_str()); return STATUS_FAILED; #endif + + _foundSubtitles = _subtitler->loadSubtitles(_filename, subtitleFile); + _theoraDecoder->loadStream(_file); if (!_theoraDecoder->isVideoLoaded()) { @@ -214,7 +217,10 @@ bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, _state = THEORA_STATE_PLAYING; _looping = looping; _playbackType = type; - + if (_subtitler && _foundSubtitles && _gameRef->_subtitles) { + _subtitler->update(_theoraDecoder->getFrameCount()); + _subtitler->display(); + } _startTime = startTime; _volume = volume; _posX = x; @@ -256,7 +262,7 @@ bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, #if 0 // Stubbed for now as theora isn't seekable if (StartTime) SeekToTime(StartTime); - Update(); + update(); #endif return STATUS_FAILED; } @@ -289,6 +295,10 @@ bool VideoTheoraPlayer::update() { } if (_theoraDecoder) { + if (_subtitler && _foundSubtitles && _gameRef->_subtitles) { + _subtitler->update(_theoraDecoder->getCurFrame()); + } + if (_theoraDecoder->endOfVideo() && _looping) { warning("Should loop movie %s, hacked for now", _filename.c_str()); _theoraDecoder->rewind(); @@ -412,11 +422,10 @@ bool VideoTheoraPlayer::display(uint32 alpha) { } else { res = STATUS_FAILED; } - // TODO: Add subtitles-support -/* if (m_Subtitler && _gameRef->m_VideoSubtitles) { - m_Subtitler->display(); - }*/ + if (_subtitler && _foundSubtitles && _gameRef->_subtitles) { + _subtitler->display(); + } return res; } diff --git a/engines/wintermute/video/video_theora_player.h b/engines/wintermute/video/video_theora_player.h index 8274a1444f..0b9b3d487a 100644 --- a/engines/wintermute/video/video_theora_player.h +++ b/engines/wintermute/video/video_theora_player.h @@ -31,6 +31,7 @@ #include "engines/wintermute/base/base.h" #include "engines/wintermute/persistent.h" +#include "engines/wintermute/video/video_subtitler.h" #include "video/video_decoder.h" #include "common/stream.h" #include "graphics/surface.h" @@ -59,7 +60,7 @@ public: Common::String _filename; BaseSurface *_texture; - //CVidSubtitler *_subtitler; + VideoSubtitler *_subtitler; // control methods bool initialize(const Common::String &filename, const Common::String &subtitleFile = Common::String()); @@ -137,9 +138,10 @@ private: bool _playbackStarted; + bool _foundSubtitles; + // helpers void SetDefaults(); - }; } // End of namespace Wintermute |