213 lines
5.9 KiB
C++
213 lines
5.9 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 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/archive.h"
|
|
#include "common/memstream.h"
|
|
#include "common/stream.h"
|
|
#include "common/substream.h"
|
|
|
|
#include "graphics/surface.h"
|
|
|
|
#include "engines/grim/grim.h"
|
|
#include "engines/grim/localize.h"
|
|
#include "engines/grim/textobject.h"
|
|
#include "engines/grim/textsplit.h"
|
|
#include "engines/grim/movie/bink.h"
|
|
|
|
#include "video/bink_decoder.h"
|
|
|
|
#ifdef USE_BINK
|
|
|
|
namespace Grim {
|
|
|
|
MoviePlayer *CreateBinkPlayer(bool demo) {
|
|
return new BinkPlayer(demo);
|
|
}
|
|
|
|
BinkPlayer::BinkPlayer(bool demo) : MoviePlayer(), _demo(demo) {
|
|
_videoDecoder = new Video::BinkDecoder();
|
|
_subtitleIndex = _subtitles.begin();
|
|
}
|
|
|
|
bool BinkPlayer::bikCheck(Common::SeekableReadStream *stream, uint32 pos) {
|
|
stream->seek(pos);
|
|
uint32 tag = stream->readUint32BE();
|
|
return (tag & 0xFFFFFF00) == MKTAG('B', 'I', 'K', 0);
|
|
}
|
|
|
|
void BinkPlayer::deinit() {
|
|
g_grim->setMovieSubtitle(nullptr);
|
|
MoviePlayer::deinit();
|
|
}
|
|
|
|
void BinkPlayer::handleFrame() {
|
|
MoviePlayer::handleFrame();
|
|
|
|
if (!_showSubtitles || _subtitleIndex == _subtitles.end())
|
|
return;
|
|
|
|
unsigned int startFrame, endFrame, curFrame;
|
|
startFrame = _subtitleIndex->_startFrame;
|
|
endFrame = _subtitleIndex->_endFrame;
|
|
curFrame = _videoDecoder->getCurFrame();
|
|
if (startFrame <= curFrame && curFrame <= endFrame) {
|
|
if (!_subtitleIndex->active) {
|
|
TextObject *textObject = new TextObject();
|
|
textObject->setDefaults(&g_grim->_sayLineDefaults);
|
|
Color c(255, 255, 255);
|
|
textObject->setFGColor(c);
|
|
textObject->setIsSpeech();
|
|
if (g_grim->getMode() == GrimEngine::SmushMode) {
|
|
// TODO: How to center exactly and put the text exactly
|
|
// at the bottom even if there are multiple lines?
|
|
textObject->setX(640 / 2);
|
|
textObject->setY(40);
|
|
}
|
|
textObject->setText(g_localizer->localize(_subtitleIndex->_textId.c_str()), false);
|
|
g_grim->setMovieSubtitle(textObject);
|
|
_subtitleIndex->active = true;
|
|
}
|
|
} else if (endFrame < curFrame) {
|
|
if (_subtitleIndex->active) {
|
|
g_grim->setMovieSubtitle(nullptr);
|
|
_subtitleIndex->active = false;
|
|
_subtitleIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BinkPlayer::loadFile(const Common::String &filename) {
|
|
_fname = filename;
|
|
|
|
if (_demo) {
|
|
Common::String subname = filename + ".sub";
|
|
// The demo uses a .lab suffix
|
|
_fname = filename + ".lab";
|
|
bool ret = MoviePlayer::loadFile(_fname);
|
|
|
|
// Load subtitles from adjacent .sub file, if present
|
|
Common::SeekableReadStream *substream = SearchMan.createReadStreamForMember(Common::Path(subname));
|
|
if (substream) {
|
|
TextSplitter tsSub("", substream);
|
|
while (!tsSub.isEof()) {
|
|
unsigned int start, end;
|
|
char textId[256];
|
|
|
|
// extract single subtitle entry
|
|
tsSub.scanString("%d\t%d\t%s", 3, &start, &end, textId);
|
|
|
|
Subtitle st(start, end, textId);
|
|
_subtitles.push_back(st);
|
|
}
|
|
delete substream;
|
|
_subtitleIndex = _subtitles.begin();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
_fname += ".m4b";
|
|
|
|
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(Common::Path(_fname));
|
|
if (!stream) {
|
|
warning("BinkPlayer::loadFile(): Can't create stream for: %s", _fname.c_str());
|
|
return false;
|
|
}
|
|
|
|
// set the default start of the bink video in case there is no SMUSH header
|
|
uint32 startBinkPos = 0x0;
|
|
|
|
// clear existing subtitles
|
|
_subtitles.clear();
|
|
|
|
char header[6];
|
|
// read the first 5 bytes of the header
|
|
stream->read(header, 5);
|
|
header[5] = 0;
|
|
|
|
if (!strcmp(header, "SMUSH")) {
|
|
// handle SMUSH header
|
|
unsigned char smushHeader[0x2000];
|
|
|
|
// read the first part
|
|
uint32 consumed = 16;
|
|
stream->read(smushHeader, consumed);
|
|
|
|
// decode the first part
|
|
for (unsigned int i = 0; i < consumed; i++) {
|
|
smushHeader[i] ^= 0xd2;
|
|
}
|
|
|
|
Common::MemoryReadStream msStart(smushHeader, consumed);
|
|
TextSplitter tsStart("", &msStart);
|
|
|
|
// extract the length / the start of the following BINK header
|
|
tsStart.scanString("%d", 1, &startBinkPos);
|
|
|
|
assert(startBinkPos < sizeof(smushHeader));
|
|
|
|
// read the rest (5 bytes less because of the string "SMUSH" at the beginning)
|
|
stream->read(smushHeader+consumed, startBinkPos - consumed - 5);
|
|
|
|
// decode the reset
|
|
for (unsigned int i = consumed; i < startBinkPos - 5; i++) {
|
|
smushHeader[i] ^= 0xd2;
|
|
}
|
|
consumed = startBinkPos - 5;
|
|
|
|
Common::MemoryReadStream msSmush(smushHeader, consumed);
|
|
TextSplitter tsSmush("", &msSmush);
|
|
|
|
// skip the first line which contains the length
|
|
tsSmush.nextLine();
|
|
|
|
tsSmush.expectString("BEGINDATA");
|
|
while (!tsSmush.checkString("ENDOFDATA")) {
|
|
unsigned int start, end;
|
|
char textId[256];
|
|
|
|
// extract single subtitle entry
|
|
tsSmush.scanString("%d\t%d\t%s", 3, &start, &end, textId);
|
|
|
|
Subtitle st(start, end, textId);
|
|
_subtitles.push_back(st);
|
|
}
|
|
tsSmush.expectString("ENDOFDATA");
|
|
}
|
|
|
|
// set current subtitle index to the first subtitle
|
|
_subtitleIndex = _subtitles.begin();
|
|
|
|
if (!bikCheck(stream, startBinkPos)) {
|
|
warning("BinkPlayer::loadFile(): Could not find BINK header for: %s", _fname.c_str());
|
|
delete stream;
|
|
return false;
|
|
}
|
|
|
|
Common::SeekableReadStream *bink = nullptr;
|
|
bink = new Common::SeekableSubReadStream(stream, startBinkPos, stream->size(), DisposeAfterUse::YES);
|
|
return _videoDecoder->loadStream(bink);
|
|
}
|
|
|
|
} // end of namespace Grim
|
|
|
|
#endif // USE_BINK
|