Initial commit
This commit is contained in:
212
engines/grim/movie/bink.cpp
Normal file
212
engines/grim/movie/bink.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user