aboutsummaryrefslogtreecommitdiff
path: root/engines/bladerunner
diff options
context:
space:
mode:
authorThanasis Antoniou2018-06-18 14:10:00 +0300
committerEugene Sandulenko2018-12-25 12:35:52 +0100
commita86625700fe69ff27e0f704a41307cdd2135a6a8 (patch)
tree3e25f052da3d0cc4bf18d21960cedade06a19911 /engines/bladerunner
parent9ceb2e858658cac3c7e4d592c3fa6f59a5776b9a (diff)
downloadscummvm-rg350-a86625700fe69ff27e0f704a41307cdd2135a6a8.tar.gz
scummvm-rg350-a86625700fe69ff27e0f704a41307cdd2135a6a8.tar.bz2
scummvm-rg350-a86625700fe69ff27e0f704a41307cdd2135a6a8.zip
BLADERUNNER: Added subtitles support and checkbox in KIA
Diffstat (limited to 'engines/bladerunner')
-rw-r--r--engines/bladerunner/actor.cpp17
-rw-r--r--engines/bladerunner/bladerunner.cpp35
-rw-r--r--engines/bladerunner/bladerunner.h18
-rw-r--r--engines/bladerunner/font.cpp87
-rw-r--r--engines/bladerunner/font.h12
-rw-r--r--engines/bladerunner/game_constants.h8
-rw-r--r--engines/bladerunner/module.mk1
-rw-r--r--engines/bladerunner/outtake.cpp18
-rw-r--r--engines/bladerunner/subtitles.cpp778
-rw-r--r--engines/bladerunner/subtitles.h128
-rw-r--r--engines/bladerunner/text_resource.cpp62
-rw-r--r--engines/bladerunner/text_resource.h11
-rw-r--r--engines/bladerunner/ui/elevator.cpp6
-rw-r--r--engines/bladerunner/ui/esper.cpp7
-rw-r--r--engines/bladerunner/ui/kia.cpp7
-rw-r--r--engines/bladerunner/ui/kia_section_settings.cpp50
-rw-r--r--engines/bladerunner/ui/kia_section_settings.h6
-rw-r--r--engines/bladerunner/ui/vk.cpp7
18 files changed, 1250 insertions, 8 deletions
diff --git a/engines/bladerunner/actor.cpp b/engines/bladerunner/actor.cpp
index 45d6d08277..062fc0f37c 100644
--- a/engines/bladerunner/actor.cpp
+++ b/engines/bladerunner/actor.cpp
@@ -41,6 +41,9 @@
#include "bladerunner/slice_animations.h"
#include "bladerunner/slice_renderer.h"
#include "bladerunner/time.h"
+#if SUBTITLES_SUPPORT
+#include "bladerunner/subtitles.h"
+#endif // SUBTITLES_SUPPORT
#include "bladerunner/waypoints.h"
#include "bladerunner/zbuffer.h"
@@ -552,7 +555,11 @@ bool Actor::tick(bool forceDraw, Common::Rect *screenRect) {
needsUpdate = true;
timeLeft = 0;
}
-
+#if SUBTITLES_SUPPORT
+ if(!isSpeeching()) {
+ _vm->_subtitles->hide();
+ }
+#endif // SUBTITLES_SUPPORT
if (needsUpdate) {
int newAnimation = 0, newFrame = 0;
_vm->_aiScripts->updateAnimation(_id, &newAnimation, &newFrame);
@@ -1092,10 +1099,18 @@ void Actor::speechPlay(int sentenceId, bool voiceOver) {
balance = CLIP<int>(balance, -127, 127);
}
+#if SUBTITLES_SUPPORT
+ _vm->_subtitles->getInGameSubsText(_id, sentenceId);
+ _vm->_subtitles->show();
+#endif // SUBTITLES_SUPPORT
+
_vm->_audioSpeech->playSpeech(name, balance);
}
void Actor::speechStop() {
+#if SUBTITLES_SUPPORT
+ _vm->_subtitles->hide();
+#endif // SUBTITLES_SUPPORT
_vm->_audioSpeech->stopSpeech();
}
diff --git a/engines/bladerunner/bladerunner.cpp b/engines/bladerunner/bladerunner.cpp
index 5a00863ceb..52501aa651 100644
--- a/engines/bladerunner/bladerunner.cpp
+++ b/engines/bladerunner/bladerunner.cpp
@@ -60,6 +60,9 @@
#include "bladerunner/shape.h"
#include "bladerunner/slice_animations.h"
#include "bladerunner/slice_renderer.h"
+#if SUBTITLES_SUPPORT
+#include "bladerunner/subtitles.h"
+#endif // SUBTITLES_SUPPORT
#include "bladerunner/suspects_database.h"
#include "bladerunner/text_resource.h"
#include "bladerunner/time.h"
@@ -181,6 +184,9 @@ BladeRunnerEngine::BladeRunnerEngine(OSystem *syst, const ADGameDescription *des
_scores = nullptr;
_elevator = nullptr;
_mainFont = nullptr;
+ #if SUBTITLES_SUPPORT
+ _subtitles = nullptr;
+ #endif // SUBTITLES_SUPPORT
_esper = nullptr;
_vk = nullptr;
_policeMaze = nullptr;
@@ -400,6 +406,20 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
_gameFlags = new GameFlags();
_gameFlags->setFlagCount(_gameInfo->getFlagCount());
+
+ #if BLADERUNNER_RESTORED_CONTENT_GAME
+ // EDS flags
+ _extraGameFlagsForRestoredContent = new GameFlags(); //aux flags - custom
+ _extraGameFlagsForRestoredContent->setFlagCount(RESTORED_CONTENT_EXTRA_FLAGS + 1); // aux flags - custom // +1 since we don't assign something to 0 enum value
+ #if SUBTITLES_SUPPORT
+ #if SUBTITLES_ENABLED_BY_DEFAULT
+ // subtitles enable by default:
+ _extraGameFlagsForRestoredContent->set(kEDSFlagSubtitlesEnable);
+ #else
+ _extraGameFlagsForRestoredContent->reset(kEDSFlagSubtitlesEnable);
+ #endif // SUBTITLES_ENABLED_BY_DEFAULT
+ #endif // SUBTITLES_SUPPORT
+ #endif // BLADERUNNER_RESTORED_CONTENT_GAME
_items = new Items(this);
@@ -495,6 +515,10 @@ bool BladeRunnerEngine::startup(bool hasSavegames) {
_mainFont = new Font(this);
_mainFont->open("KIA6PT.FON", 640, 480, -1, 0, 0x252D);
_mainFont->setSpacing(1, 0);
+
+#if SUBTITLES_SUPPORT
+ _subtitles = new Subtitles(this);
+#endif // SUBTITLES_SUPPORT
for (int i = 0; i != 43; ++i) {
Shape *shape = new Shape(this);
@@ -668,6 +692,13 @@ void BladeRunnerEngine::shutdown() {
_mainFont = nullptr;
}
+#if SUBTITLES_SUPPORT
+ if(_subtitles) {
+ delete _subtitles;
+ _subtitles = nullptr;
+ }
+#endif // SUBTITLES_SUPPORT//
+
delete _items;
_items = nullptr;
@@ -937,7 +968,9 @@ void BladeRunnerEngine::gameTick() {
if (_debugger->_viewObstacles) {
_obstacles->draw();
}
-
+ #if SUBTITLES_SUPPORT
+ _subtitles->tick(_surfaceFront);
+ #endif // SUBTITLES_SUPPORT
blitToScreen(_surfaceFront);
_system->delayMillis(10);
}
diff --git a/engines/bladerunner/bladerunner.h b/engines/bladerunner/bladerunner.h
index f056a669d9..5c3cc88b9d 100644
--- a/engines/bladerunner/bladerunner.h
+++ b/engines/bladerunner/bladerunner.h
@@ -39,6 +39,14 @@
#define BLADERUNNER_DEBUG_CONSOLE 0
#define BLADERUNNER_DEBUG_GAME 0
+#define SUBTITLES_SUPPORT 1
+#if SUBTITLES_SUPPORT
+#define SUBTITLES_EXTERNAL_FONT 1
+#define SUBTITLES_ENABLED_BY_DEFAULT 1
+#endif // SUBTITLES_SUPPORT
+#define BLADERUNNER_RESTORED_CONTENT_GAME 1 // needed for checkbox setting for subtitles enable/disable
+#define RESTORED_CONTENT_EXTRA_FLAGS 1 // needed for checkbox setting for subtitles enable/disable
+
namespace Common {
struct Event;
}
@@ -88,6 +96,9 @@ class Shape;
class SliceAnimations;
class SliceRenderer;
class Spinner;
+#if SUBTITLES_SUPPORT
+class Subtitles;
+#endif
class SuspectsDatabase;
class TextResource;
class Time;
@@ -128,12 +139,19 @@ public:
EndCredits *_endCredits;
ESPER *_esper;
GameFlags *_gameFlags;
+ #if BLADERUNNER_RESTORED_CONTENT_GAME
+ // EDS flags
+ GameFlags *_extraGameFlagsForRestoredContent;
+ #endif // BLADERUNNER_RESTORED_CONTENT_GAME
GameInfo *_gameInfo;
ItemPickup *_itemPickup;
Items *_items;
KIA *_kia;
Lights *_lights;
Font *_mainFont;
+ #if SUBTITLES_SUPPORT
+ Subtitles *_subtitles;
+ #endif // SUBTITLES_SUPPORT
Mouse *_mouse;
Music *_music;
Obstacles *_obstacles;
diff --git a/engines/bladerunner/font.cpp b/engines/bladerunner/font.cpp
index d4b0e16515..e3a30aa0d4 100644
--- a/engines/bladerunner/font.cpp
+++ b/engines/bladerunner/font.cpp
@@ -37,6 +37,48 @@ Font::~Font() {
close();
}
+#if SUBTITLES_SUPPORT
+#if SUBTITLES_EXTERNAL_FONT
+// for external FON font file / subtitles support
+bool Font::openFromStream(Common::ScopedPtr<Common::SeekableReadStream> &stream, int screenWidth, int screenHeight, int spacing1, int spacing2, uint16 color) {
+ reset();
+
+ _screenWidth = screenWidth;
+ _screenHeight = screenHeight;
+ _spacing1 = spacing1;
+ _spacing2 = spacing2;
+ _color = color;
+
+ if (!stream) {
+ return false;
+ }
+ _characterCount = stream->readUint32LE();
+ debug("Font's character count: %d", _characterCount);
+ _maxWidth = stream->readUint32LE();
+ _maxHeight = stream->readUint32LE();
+ _dataSize = stream->readUint32LE();
+ _data = new uint16[_dataSize];
+ if (!_data) {
+ debug("Font::open failed to allocate font buffer");
+ return false;
+ }
+
+ for (int i = 0; i < _characterCount; i++) {
+ _characters[i].x = stream->readUint32LE();
+ _characters[i].y = stream->readUint32LE();
+ _characters[i].width = stream->readUint32LE();
+ _characters[i].height = stream->readUint32LE();
+ _characters[i].dataOffset = stream->readUint32LE();
+ debug("char::%d character x: %d, y: %d, w: %d, h:%d, do: %d", i, _characters[i].x, _characters[i].y, _characters[i].width, _characters[i].height, _characters[i].dataOffset);
+ }
+ for (int i = 0; i < _dataSize; i++) {
+ _data[i] = stream->readUint16LE();
+ }
+ return true;
+}
+#endif // SUBTITLES_EXTERNAL_FONT
+#endif // SUBTITLES_SUPPORT
+
bool Font::open(const Common::String &fileName, int screenWidth, int screenHeight, int spacing1, int spacing2, uint16 color) {
reset();
@@ -68,6 +110,28 @@ bool Font::open(const Common::String &fileName, int screenWidth, int screenHeigh
_characters[i].width = stream->readUint32LE();
_characters[i].height = stream->readUint32LE();
_characters[i].dataOffset = stream->readUint32LE();
+ #if SUBTITLES_SUPPORT
+ #if !SUBTITLES_EXTERNAL_FONT
+ // special explicit alignment fixes for TAHOMA18 (INTERNAL) font
+ if(fileName == "TAHOMA18.FON") {
+ // fix P -> i = 81 (ascii code 80 + 1)
+ if(i == 81 || i == 72 || i == 74 || i == 75 // P, G, I, J
+ || i == 46 // '-'
+ )
+ {
+ _characters[i].x = 0;// from 1
+ }
+ if(i == 81 // P
+ || i == 83 || i == 84 // R, S,
+ || i == 86 // U
+ || i == 87 || i == 88 || i == 89 || i == 90 || i == 91 // V, W, X, Y ,Z
+ ) {
+ _characters[i].y = 7;// from 6 // bring down a pixel
+ }
+ }
+ //debug("char::%d character x: %d, y: %d, w: %d, h:%d, do: %d", i, _characters[i].x, _characters[i].y, _characters[i].width, _characters[i].height, _characters[i].dataOffset);
+ #endif // SUBTITLES_EXTERNAL_FONT
+ #endif // SUBTITLES_SUPPORT
}
for (int i = 0; i < _dataSize; i++) {
_data[i] = stream->readUint16LE();
@@ -176,6 +240,21 @@ void Font::replaceColor(uint16 oldColor, uint16 newColor) {
}
}
+#if SUBTITLES_SUPPORT
+void Font::setBlackColor() {
+ // to create a font that can be used as a shadow effect
+ if (!_data || !_dataSize) {
+ return;
+ }
+ for (int i = 0; i < _dataSize; i++) {
+ //debug("COLOR EXISTING: %d", _data[i]);
+ if(_data[i] != 32768) { // 0x8000 transparent
+ _data[i] = 0x0000; // black
+ }
+ }
+}
+#endif // SUBTITLES_SUPPORT
+
void Font::drawCharacter(const uint8 character, Graphics::Surface &surface, int x, int y) const {
uint8 characterIndex = character + 1;
if (x < 0 || x >= _screenWidth || y < 0 || y >= _screenHeight || !_data || characterIndex >= _characterCount) {
@@ -192,6 +271,14 @@ void Font::drawCharacter(const uint8 character, Graphics::Surface &surface, int
int endY = height + y - 1;
int currentY = y;
+
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ // Temp Bug fix - Return if w h unnaturally big: -- the INTERNAL tahoma18 font is corrupted so it could cause crashes
+ if(width > 100 || height > 100) {
+ return;
+ }
+#endif
+
while (currentY <= endY && currentY < _screenHeight) {
int currentX = x;
int endX = width + x - 1;
diff --git a/engines/bladerunner/font.h b/engines/bladerunner/font.h
index 4af25468c6..4d43aaa875 100644
--- a/engines/bladerunner/font.h
+++ b/engines/bladerunner/font.h
@@ -23,6 +23,10 @@
#ifndef BLADERUNNER_FONT_H
#define BLADERUNNER_FONT_H
+#include "bladerunner/bladerunner.h" // needed for definition of Common::ScopedPtr (subtitles font external font file support) -- and for the subtitles relevant macro defines
+#if SUBTITLES_SUPPORT
+#include "common/util.h"
+#endif
#include "common/str.h"
namespace Graphics {
@@ -61,11 +65,19 @@ public:
Font(BladeRunnerEngine *vm);
~Font();
+ #if SUBTITLES_SUPPORT
+ #if SUBTITLES_EXTERNAL_FONT
+ bool openFromStream(Common::ScopedPtr<Common::SeekableReadStream> &s, int screenWidth, int screenHeight, int spacing1, int spacing2, uint16 color);
+ #endif // SUBTITLES_EXTERNAL_FONT
+ #endif // SUBTITLES_SUPPORT
bool open(const Common::String &fileName, int screenWidth, int screenHeight, int spacing1, int spacing2, uint16 color);
void close();
void setSpacing(int spacing1, int spacing2);
void setColor(uint16 color);
+ #if SUBTITLES_SUPPORT
+ void setBlackColor(); // for subtitles
+ #endif
void draw(const Common::String &text, Graphics::Surface &surface, int x, int y) const;
void drawColor(const Common::String &text, Graphics::Surface &surface, int x, int y, uint16 color);
diff --git a/engines/bladerunner/game_constants.h b/engines/bladerunner/game_constants.h
index 0a9082a60a..280bcc8c92 100644
--- a/engines/bladerunner/game_constants.h
+++ b/engines/bladerunner/game_constants.h
@@ -547,6 +547,14 @@ enum Variables {
kVariableNextTvNews = 52
};
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+// Aux enum from added/ restored content
+// EDS Flags
+enum _extraGameFlagsForRestoredContent {
+ kEDSFlagSubtitlesEnable = 1
+};
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
+
enum Outtakes {
kOuttakeIntro = 0,
kOuttakeMovieA = 1,
diff --git a/engines/bladerunner/module.mk b/engines/bladerunner/module.mk
index 243f426a44..7f800e5354 100644
--- a/engines/bladerunner/module.mk
+++ b/engines/bladerunner/module.mk
@@ -244,6 +244,7 @@ MODULE_OBJS = \
shape.o \
slice_animations.o \
slice_renderer.o \
+ subtitles.o \
suspects_database.o \
text_resource.o \
time.o \
diff --git a/engines/bladerunner/outtake.cpp b/engines/bladerunner/outtake.cpp
index 187f46c0bd..d52646b488 100644
--- a/engines/bladerunner/outtake.cpp
+++ b/engines/bladerunner/outtake.cpp
@@ -23,6 +23,9 @@
#include "bladerunner/outtake.h"
#include "bladerunner/bladerunner.h"
+#if SUBTITLES_SUPPORT
+#include "bladerunner/subtitles.h"
+#endif // SUBTITLES_SUPPORT
#include "bladerunner/vqa_player.h"
#include "common/debug.h"
@@ -43,8 +46,12 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
}
resName = resName + ".VQA";
-
- VQAPlayer vqa_player(_vm, &_vm->_surfaceFront, resName);
+
+#if SUBTITLES_SUPPORT
+ VQAPlayer vqa_player(_vm, &_vm->_surfaceBack); // fix for subtitles rendering properly
+#else
+ VQAPlayer vqa_player(_vm, &_vm->_surfaceFront); // original
+#endif // SUBTITLES_SUPPORT
vqa_player.open();
@@ -56,10 +63,17 @@ void OuttakePlayer::play(const Common::String &name, bool noLocalization, int co
return;
int frame = vqa_player.update();
+ #if SUBTITLES_SUPPORT
+ blit(_vm->_surfaceBack, _vm->_surfaceFront); // new tha hack - helps to make subtitles disappear if the proper video is rendered in surface back and then pushed to the front surface
+ #endif // SUBTITLES_SUPPORT
if (frame == -3)
break;
if (frame >= 0) {
+ #if SUBTITLES_SUPPORT
+ _vm->_subtitles->getOuttakeSubsText(resName + ".TRE" , frame);
+ _vm->_subtitles->tickOuttakes(_vm->_surfaceFront);
+ #endif // SUBTITLES_SUPPORT
_vm->blitToScreen(_vm->_surfaceFront);
}
diff --git a/engines/bladerunner/subtitles.cpp b/engines/bladerunner/subtitles.cpp
new file mode 100644
index 0000000000..b66360f81f
--- /dev/null
+++ b/engines/bladerunner/subtitles.cpp
@@ -0,0 +1,778 @@
+/* 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.
+ *
+ */
+
+#include "bladerunner/subtitles.h"
+
+#if SUBTITLES_SUPPORT
+
+#include "bladerunner/bladerunner.h"
+#include "bladerunner/font.h"
+#include "bladerunner/text_resource.h"
+#include "bladerunner/audio_speech.h"
+//#include "bladerunner/script/scene_script.h" // for Game_Flag_Query declaration (actually script.h, but this seems to be included in other source files instead)
+#include "bladerunner/game_flags.h" // for Game_Flag_Query declaration (actually script.h, but this seems to be included in other source files instead)
+#include "bladerunner/game_constants.h" // for EDS flags - for subtitle checkbox flag state
+#include "common/debug.h"
+#include "common/util.h"
+
+namespace BladeRunner {
+
+/*
+ * Optional support for subtitles
+ * CHECK what happens in VQA where the audio plays separately (are the finales such VQAs ?)
+ *
+ * TODO in python script (FON from png glyphs) check if you can have semi-transparent pixels to better outline the fringe points of the glyphs - check what happens when MSB is set (transparency) and the rest of the color value is not all 0s.
+ * TODO Catch error for bad symbol in a quote (one that causes the font to crash) - this could happen with the corrupted internal font (TAHOMA18) -> font crash or bad font display / garbage character
+ * TODO add a keyboard shortcut key to enable / disable subtitles?
+ * TODO have a debug script to detect/report problematic lines (too long)
+ *
+ * TODO? put external FON and TRE in a new folder "SUBS" - case insensitive (?)
+ * TODO? Use another escape sequence to progressively display text in a line (like in SCUMM games) <-- this could be very useful with very long lines - might also need an extra manual time or ticks parameter to determine when during the display of the first segment we should switch to the second.
+ * TODO? A more advanced subtitles system
+ * TODO: subtitles could be independent from sound playing (but should disappear when switching between UI screens)
+ * TODO?: Support for queuing subtitles when more than one subtitle should play for a spoken dialogue (due to a very long quote)
+ * TODO?: Predefine a minimum time for a subtitle to appear, before it is interrupted by the next one. (might need queuing)
+ * TODO?: If the subtitle is the last one then extend its duration to another predefined delay.
+ * TODO?: A system to auto-split a dialogue after some max characters per both lines to a new dialogue set (delete previous 2 lines, start a new one(s) with the rest of the quote).
+ *
+ * DONE Minor fixes In internal font TAHOMA18 some letters like 'P' and 'o' and not rightly aligned in the font. also not good spacing with '-' and a few other chars
+ * Also seems that this particular font is corrupted!
+ * Create and Support proper external FON for subtitles.
+ * DONE split at new line character (priority over auto-split)
+ * DONE auto-split a long line into two
+ * DONE support the basic 2 line subtitles
+ * DONE support a third line for subtitles (some quotes are too long for 2 lines). Are there quotes that are too long for 3 lines?
+ * DONE handle missing subtitle files! Gracefully don't show subtitles for VQAs or in-game dialogue if the required respective files are missing!
+ * DONE add subtitle files for the rest of VQAs that have spoken dialogue
+ * DONE A system to auto-split a dialogue after some max total width of character glyphs per line.
+ * DONE - OK - CHECK What happens with skipped dialogue (enter / skip dialogue key pressed)
+ * DONE - OK - CHECK what happens in VQA when no corresponding TRE subs file?
+ */
+
+#if SUBTITLES_EXTERNAL_FONT
+const Common::String Subtitles::SUBTITLES_FONT_FILENAME = "SUBTITLES.FON";
+#else
+const Common::String Subtitles::SUBTITLES_FONT_FILENAME = "TAHOMA18.FON";
+#endif
+
+/*
+* All entries need to have the language code appended (after a '_').
+* The outtakes then need a substring ".VQA"
+* And all entries should have the suffix extension ".TRE"
+* If/When adding new TRE resources here --> Update kMaxTextResourceEntries and also update method getIdxForSubsTreName()
+*/
+const Common::String Subtitles::SUBTITLES_FILENAME_PREFIXES[kMaxTextResourceEntries] = {
+ "outQuotes", // 0 // (in-game subtitles, not VQA subtitles)
+ "WSTLGO", // 1
+ "BRLOGO", // 2
+ "INTRO", // 3
+ "MW_A", // 4
+ "MW_B01", // 5
+ "MW_B02", // 6
+ "MW_B03", // 7
+ "MW_B04", // 8
+ "MW_B05", // 9
+ "INTRGT", // 10
+ "MW_C01", // 11
+ "MW_C02", // 12
+ "MW_C03", // 13
+ "MW_D", // 14
+ "END04A", // 15
+ "END04B", // 16
+ "END04C", // 17
+ "END06", // 18
+ "END07", // 19
+ "END01A", // 20
+ "END01B", // 21
+ "END01C", // 22
+ "END01D", // 23
+ "END01E", // 24
+ "END01F", // 25
+ "END03" // 26
+};
+
+/**
+* Subtitles Constructor
+*/
+Subtitles::Subtitles(BladeRunnerEngine *vm) {
+ _vm = vm;
+ // Initializing and reseting Subtitles
+ for (int i = 0; i < kMaxTextResourceEntries; i++) {
+ _gameSubsFdEntries[i] = nullptr;
+ _vqaSubsTextResourceEntries[i] = nullptr;
+ }
+#if SUBTITLES_EXTERNAL_FONT
+ _gameSubsFontsFd = nullptr;
+ _subsFont = nullptr;
+#else
+ _subsFont = nullptr;
+ _subsBgFont = nullptr;
+#endif // SUBTITLES_EXTERNAL_FONT
+ reset();
+ // Done - Subtitles Reset
+ //
+ // Loading text resources
+ for (int i = 0; i < kMaxTextResourceEntries; i++) {
+ _gameSubsFdEntries[i] = new Common::File();
+ _vqaSubsTextResourceEntries[i] = new TextResource(_vm);
+ Common::String tmpConstructedFileName = "";
+ tmpConstructedFileName = SUBTITLES_FILENAME_PREFIXES[i] + "_" + _vm->_languageCode;
+ if (i > 0) {
+ tmpConstructedFileName += ".VQA";
+ }
+ tmpConstructedFileName += ".TRE";
+ if (openGameSubs(tmpConstructedFileName) && loadGameSubsText(i)) {
+ _gameSubsFdEntriesFound[i] = true;
+ }
+ }
+ // Done - Loading text resources
+ //
+ // Initializing/Loading Subtitles' Fonts
+#if SUBTITLES_EXTERNAL_FONT
+ // Open external fonts file (FON file) and load fonts
+ _gameSubsFontsFd = new Common::File();
+ _subsFont = new Font(_vm);
+ if (openSubsFontFile() && loadSubsFont()) {
+ _subsFontsLoaded = true;
+ }
+#else
+ _subsFont = new Font(_vm);
+ // Use TAHOMA18.FON (is corrupted in places)
+ // 10PT or TAHOMA24 or KIA6PT have all caps glyphs (and also are too big or too small) so they are not appropriate.
+ if (_subsFont ->open(SUBTITLES_FONT_FILENAME, 640, 480, -1, 0, 0)) { // Color setting does not seem to affect the TAHOMA fonts or does it affect the black outline since we give 0 here?
+ _subsFont->setSpacing(1, 0);
+ _subsFont->setWhiteColor();
+ _subsFontsLoaded = true;
+ } else {
+ _subsFontsLoaded = false;
+ }
+ _subsBgFont = new Font(_vm);
+ if (_subsFontsLoaded && _subsBgFont ->open(SUBTITLES_FONT_FILENAME, 640, 480, -1, 0, 0)) { // TODO dark color? --- color does not seem to affect the TAHOMA fonts or does it affect the black outline since we give 0 here? ?? - we should give the original color here. What is it for TAHOMA?
+ _subsBgFont ->setSpacing(1, 0);
+ _subsBgFont ->setBlackColor();
+ } else {
+ _subsFontsLoaded = false;
+ }
+#endif // SUBTITLES_EXTERNAL_FONT
+ //Done - Initializing/Loading Subtitles' Fonts
+ //
+ // calculate the Screen Y position of the subtitle lines
+ // getTextHeight("") returns the maxHeight of the font glyphs regardless of the actual text parameter
+ // debug("Max height %d", _subsFont->getTextHeight(""));
+ if (_subsFontsLoaded) {
+ for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
+ _subtitleLineScreenY[i] = 479 - ((kMaxNumOfSubtitlesLines - i) * (_subsFont->getTextHeight("") + 1));
+ }
+ }
+}
+
+/**
+* Subtitles Destructor
+*/
+Subtitles::~Subtitles() {
+ // delete any resource entries in the _vqaSubsTextResourceEntries table
+ // and close any open text resource files
+ for (int i = 0; i != kMaxTextResourceEntries; ++i) {
+ if (_vqaSubsTextResourceEntries[i] != nullptr) {
+ delete _vqaSubsTextResourceEntries[i];
+ _vqaSubsTextResourceEntries[i] = nullptr;
+ }
+ if (_gameSubsFdEntries[i] != nullptr) {
+
+ if (isOpenGameSubs(i)) {
+ closeGameSubs(i);
+ }
+ delete _gameSubsFdEntries[i];
+ _gameSubsFdEntries[i] = nullptr;
+ }
+ }
+#if SUBTITLES_EXTERNAL_FONT
+ if (_subsFont != nullptr) {
+ _subsFont->close();
+ delete _subsFont;
+ _subsFont = nullptr;
+ }
+ if (_gameSubsFontsFd != nullptr) {
+ if (isOpenSubsFontFile()) {
+ closeSubsFontFile();
+ }
+ delete _gameSubsFontsFd;
+ _gameSubsFontsFd = nullptr;
+ }
+#else
+ if (_subsFont != nullptr) {
+ _subsFont->close();
+ delete _subsFont;
+ _subsFont = nullptr;
+ }
+ if (_subsBgFont != nullptr) {
+ _subsBgFont->close();
+ delete _subsBgFont;
+ _subsBgFont = nullptr;
+ }
+#endif // SUBTITLES_EXTERNAL_FONT
+}
+
+/**
+*
+* Returns the index of the specified .TRE filename in the SUBTITLES_FILENAME_PREFIXES table
+*/
+int Subtitles::getIdxForSubsTreName(const Common::String &treName) const {
+ Common::String tmpConstructedFileName = "";
+ for (int i = 0; i < kMaxTextResourceEntries; ++i) {
+ tmpConstructedFileName = SUBTITLES_FILENAME_PREFIXES[i] + "_" + _vm->_languageCode;
+ if (i > 0) {
+ tmpConstructedFileName += ".VQA";
+ }
+ tmpConstructedFileName += ".TRE";
+ if (tmpConstructedFileName == treName) {
+ return i;
+ }
+ }
+ // error case
+ return -1;
+}
+
+
+/**
+* Open an external subtitles File and store its file descriptor
+* @return true if successful, false otherwise
+*/
+bool Subtitles::openGameSubs(const Common::String &filename) {
+ uint32 gameSubsEntryCount = 0;
+ int subTreIdx = getIdxForSubsTreName(filename);
+
+ if (subTreIdx < 0 || _gameSubsFdEntries[subTreIdx] == nullptr) {
+ debug("Subtitles::open(): Could not open %s", filename.c_str());
+ return false;
+ }
+// debug("Now opening subs file: %s", filename.c_str());
+
+ if (!_gameSubsFdEntries[subTreIdx]->open(filename)) {
+ debug("Subtitles::open(): Could not open %s", filename.c_str());
+ return false;
+ }
+ gameSubsEntryCount = _gameSubsFdEntries[subTreIdx]->readUint32LE();
+
+ if (_gameSubsFdEntries[subTreIdx]->err()) {
+ error("Subtitles::open(): Error reading entries in %s", filename.c_str());
+ _gameSubsFdEntries[subTreIdx]->close();
+ return false;
+ }
+ debug("Subtitles::open: Opened in-game external subs file %s with %d entries", filename.c_str(), gameSubsEntryCount);
+ return true;
+}
+
+/**
+* Close an open external subtitles File
+*/
+void Subtitles::closeGameSubs(int subTreIdx) {
+ if (subTreIdx < 0 || _gameSubsFdEntries[subTreIdx] == nullptr) {
+ debug("Subtitles::close(): Could not close file with Idx %d", subTreIdx);
+ return;
+ }
+ return _gameSubsFdEntries[subTreIdx]->close();
+}
+
+/**
+* Check whether an external subtitles File is open
+*/
+bool Subtitles::isOpenGameSubs(int subTreIdx) const {
+ if (subTreIdx < 0 || _gameSubsFdEntries[subTreIdx] == nullptr) {
+ return false;
+ }
+ return _gameSubsFdEntries[subTreIdx]->isOpen();
+}
+
+/**
+* Load the game subs as a TRE resource and store them in a specific entry in _vqaSubsTextResourceEntries table
+*/
+bool Subtitles::loadGameSubsText(int subTreIdx) {
+ bool r = false;
+ Common::SeekableReadStream *stream = createReadStreamForGameSubs(subTreIdx);
+ if (stream != nullptr) {
+ Common::ScopedPtr<Common::SeekableReadStream> s(stream);
+ r = _vqaSubsTextResourceEntries[subTreIdx]->openFromStream(s);
+ if (!r) {
+ error("Failed to load subtitle text");
+ }
+ closeGameSubs(subTreIdx);
+ }
+ return r;
+}
+
+/**
+* Auxiliary method for loadGameSubsText
+* @return nullptr if failure, otherwise return a pointer to a new SafeSeekableSubReadStream
+*/
+Common::SeekableReadStream *Subtitles::createReadStreamForGameSubs(int subTreIdx) {
+ if (subTreIdx < 0 || _gameSubsFdEntries[subTreIdx] == nullptr) {
+ return nullptr;
+ }
+ if (!isOpenGameSubs(subTreIdx)) {
+ return nullptr;
+ }
+ return new Common::SafeSeekableSubReadStream(_gameSubsFdEntries[subTreIdx], 0, _gameSubsFdEntries[subTreIdx]->size(), DisposeAfterUse::YES); // TODO changed to YES from NO is this ok?
+}
+
+#if SUBTITLES_EXTERNAL_FONT
+//
+// EXTERN FONT MANAGEMENT - Font Open/ Create Read Stream / Load / Close methods
+//
+
+/**
+* @return true if successfully opened the external fonts (FON) file, false otherwise
+*/
+bool Subtitles::openSubsFontFile() {
+ uint32 subFontsTableEntryCount = 0;
+// debug("Now opening subs file: %s", SUBTITLES_FONT_FILENAME.c_str());
+
+ if (_gameSubsFontsFd == nullptr || !_gameSubsFontsFd->open(SUBTITLES_FONT_FILENAME)) {
+ debug("Subtitles FONT::open(): Could not open %s", SUBTITLES_FONT_FILENAME.c_str());
+ return false;
+ }
+ subFontsTableEntryCount = _gameSubsFontsFd->readUint32LE(); // only for debug report purposes
+
+ if (_gameSubsFontsFd->err()) {
+ error("Subtitles FONT::open(): Error reading entries in %s", SUBTITLES_FONT_FILENAME.c_str());
+ _gameSubsFontsFd->close();
+ return false;
+ }
+
+ debug("Subtitles FONT::open: Opened in-game external subs FONT file %s with %d entries", SUBTITLES_FONT_FILENAME.c_str(), subFontsTableEntryCount);
+ return true;
+}
+
+/**
+* Close the external Fonts (FON) file
+*/
+void Subtitles::closeSubsFontFile() {
+ if (_gameSubsFontsFd != nullptr) {
+ _gameSubsFontsFd->close();
+ }
+}
+
+/**
+* Checks whether the external fonts (FON) file has been opened
+*/
+bool Subtitles::isOpenSubsFontFile() const {
+ return _gameSubsFontsFd != nullptr && _gameSubsFontsFd->isOpen();
+}
+
+/**
+* Auxiliary function to create a read stream fro the external fonts file
+* @return a pointer to the stream if successful, or nullptr otherwise
+*/
+Common::SeekableReadStream *Subtitles::createReadStreamForSubFonts() {
+ if (_gameSubsFontsFd == nullptr || !isOpenSubsFontFile()) {
+ return nullptr;
+ }
+ return new Common::SafeSeekableSubReadStream(_gameSubsFontsFd, 0, _gameSubsFontsFd->size(), DisposeAfterUse::YES); // TODO changed to YES from NO is this ok?
+}
+
+/**
+* Loads the font from the external font file
+* @return true if successful, or false otherwise
+*/
+bool Subtitles::loadSubsFont() {
+ bool r = false;
+ Common::SeekableReadStream *stream = createReadStreamForSubFonts();
+ if (stream != nullptr) {
+ Common::ScopedPtr<Common::SeekableReadStream> s(stream);
+ r = _subsFont->openFromStream(s, 640, 480, -1, 0, 0);
+
+ if (!r) {
+ error("Failed to load subtitle FONT");
+ } else {
+ _subsFont->setSpacing(-1, 0);
+ }
+ //_subsFont->setSpacing(0, 0);
+ closeSubsFontFile();
+ }
+ return r;
+}
+
+//
+// END OF EXTERNAL FONT MANAGEMENT
+//
+#endif // SUBTITLES_EXTERNAL_FONT
+
+/**
+* Get the active subtitle text by searching with actor ID and speech ID
+* Use this method for in-game dialogue - Not dialogue during a VQA cutscene
+* Returns the dialogue quote, but also sets the private _currentSubtitleTextFull member
+*/
+const char *Subtitles::getInGameSubsText(int actorId, int speech_id) {
+ int32 id = 10000 * actorId + speech_id;
+ if (!_gameSubsFdEntriesFound[0]) {
+ if (_currentSubtitleTextFull != "") {
+ _currentSubtitleTextFull = "";
+ _subtitlesQuoteChanged = true;
+ }
+ return "";
+ }
+ // Search in the first TextResource of the _vqaSubsTextResourceEntries table, which is the TextResource for in-game dialogue (i.e. not VQA dialogue)
+ const Common::String &text = _vqaSubsTextResourceEntries[0]->getText((uint32)id);
+ _currentSubtitleTextFull = Common::String(text);
+ _subtitlesQuoteChanged = true;
+ return _currentSubtitleTextFull.c_str();
+}
+
+/**
+* Use this method for dialogue during VQA cutscenes
+* Returns the dialogue quote, but also sets the private _currentSubtitleTextFull member
+*/
+const char *Subtitles::getOuttakeSubsText(const Common::String &outtakesName, int frame) {
+ int fileIdx = getIdxForSubsTreName(outtakesName);
+ if (fileIdx == -1 || !_gameSubsFdEntriesFound[fileIdx]) {
+ if (_currentSubtitleTextFull != "") {
+ _currentSubtitleTextFull = "";
+ _subtitlesQuoteChanged = true;
+ }
+ return "";
+ }
+ // Search in the requested TextResource at the fileIdx index of the _vqaSubsTextResourceEntries table for a quote that corresponds to the specified video frame
+ // debug("Number of resource quotes to search: %d, requested frame: %u", _vqaSubsTextResourceEntries[fileIdx]->getCount(), (uint32)frame );
+ const Common::String &text = _vqaSubsTextResourceEntries[fileIdx]->getOuttakeTextByFrame((uint32)frame);
+ //if(text != "") {
+ // debug("Text = %s", text.c_str());
+ //}
+ if (_currentSubtitleTextFull != Common::String(text)) {
+ _currentSubtitleTextFull = Common::String(text);
+ _subtitlesQuoteChanged = true;
+ }
+ return _currentSubtitleTextFull.c_str();
+}
+
+/**
+* Explicitly set the active subtitle text to be displayed
+* Used for debug purposes mainly.
+*/
+void Subtitles::setGameSubsText(Common::String dbgQuote) {
+ if (_currentSubtitleTextFull != dbgQuote) {
+ _currentSubtitleTextFull = dbgQuote;
+ _subtitlesQuoteChanged = true;
+ }
+}
+
+/**
+* Sets the _isVisible member var to true if it's not already set
+* @return true if the member was set now, false if the member was already set
+*/
+bool Subtitles::show() {
+
+ if (_isVisible) {
+ return false;
+ }
+ _isVisible = true;
+ return true;
+}
+
+/**
+* Clears the _isVisible member var if not already clear.
+* @return true if the member was cleared, false if it was already clear.
+*/
+bool Subtitles::hide() {
+ if (!_isVisible) {
+ return false;
+ }
+
+ _isVisible = false;
+ return true;
+}
+
+/**
+* Checks whether the subtitles should be visible or not
+* @return the value of the _isVisible member boolean var
+*/
+bool Subtitles::isVisible() const {
+ return _isVisible;
+}
+
+/**
+* Tick method specific for outtakes (VQA videos)
+*/
+void Subtitles::tickOuttakes(Graphics::Surface &s) {
+ if (_currentSubtitleTextFull.empty()) {
+ _vm->_subtitles->hide();
+ } else {
+ _vm->_subtitles->show();
+ }
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ if (!_vm->_extraGameFlagsForRestoredContent->query(kEDSFlagSubtitlesEnable)) {
+ return;
+ }
+#endif
+ if (!_isVisible) { // keep it as a separate if
+ return;
+ }
+ draw(s);
+}
+
+/**
+* Tick method for in-game subtitles -- Not for outtake cutscenes (VQA videos)
+*/
+void Subtitles::tick(Graphics::Surface &s) {
+ if (!_vm->_audioSpeech->isPlaying()) {
+ _vm->_subtitles->hide(); // TODO might need a better system. Don't call it always.
+
+ }
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ if (!_vm->_extraGameFlagsForRestoredContent->query(kEDSFlagSubtitlesEnable)) {
+ return;
+ }
+#endif
+ if (!_isVisible) { // keep it as a separate if
+ return;
+ }
+ draw(s);
+}
+
+/**
+* Draw method for drawing the subtitles on the display surface
+*/
+void Subtitles::draw(Graphics::Surface &s) {
+ if (!_isVisible || _currentSubtitleTextFull.empty() || !_subsFontsLoaded) {
+ return;
+ }
+ if (_subtitlesQuoteChanged) {
+ calculatePosition(); // Don't always call calc position, only when quote has changed
+ _subtitlesQuoteChanged = false;
+ }
+
+#if SUBTITLES_EXTERNAL_FONT
+ for (int i = 0; i < _currentSubtitleLines; ++i) {
+ _subsFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i], _subtitleLineScreenY[i]);
+ }
+#else
+ // INTERNAL FONT. NEEDS HACK (_subsBgFont) FOR SHADOW EFFECT
+ for (int i = 0; i < _currentSubtitleLines; ++i) {
+ _subsBgFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i], _subtitleLineScreenY[i] - 1);
+ _subsBgFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i], _subtitleLineScreenY[i] + 1);
+ _subsBgFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i] + 1, _subtitleLineScreenY[i] + 1);
+ _subsBgFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i] + 1, _subtitleLineScreenY[i] - 1);
+ if (_subtitleLineScreenX[i] > 0) {
+ _subsBgFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i] - 1, _subtitleLineScreenY[i] - 1);
+ _subsBgFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i] - 1, _subtitleLineScreenY[i] + 1);
+ }
+ _subsFont->draw(_subtitleLineQuote[i], s, _subtitleLineScreenX[i], _subtitleLineScreenY[i]);
+ }
+#endif // SUBTITLES_EXTERNAL_FONT
+}
+
+/**
+* Calculate the position (X axis - horizontal) where the current active subtitle text should be displayed/drawn
+* This also determines if more than one lines should be drawn and what text goes into each line; splitting into multiple lines is done here
+*/
+void Subtitles::calculatePosition() {
+
+ // wOrig is in pixels, origQuoteLength is num of chars in string
+ int wOrig = _subsFont->getTextWidth(_currentSubtitleTextFull) + 2; // +2 to account for left/ right shadow pixels (or for good measure)
+ int origQuoteLength = _currentSubtitleTextFull.size();
+ int tmpCharIndex = 0;
+ bool drawSingleLineQuote = false;
+
+ const uint8 *textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();
+ int tmpLineWidth[kMaxNumOfSubtitlesLines];
+
+ _currentSubtitleLines = 1;
+ for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
+ _subtitleLineSplitAtCharIndex[i] = 0;
+ _subtitleLineQuote[i] = "";
+ _subtitleLineScreenX[i] = 0;
+ tmpLineWidth[i] = 0;
+ }
+
+ while (*textCharacters != 0) {
+ // check for new line explicit split
+ if (_currentSubtitleLines < kMaxNumOfSubtitlesLines && *textCharacters == 0x0A && tmpCharIndex != 0 && _subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] == 0) {
+ _subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] = tmpCharIndex;
+ _currentSubtitleLines += 1;
+ }
+ tmpCharIndex += 1;
+ textCharacters += 1;
+ }
+ _subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] = tmpCharIndex;
+ if (_currentSubtitleLines > 1) {
+ // if we can split at new line characters:
+ //
+ int j = 0;
+ textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str(); // reset pointer to the start of subtitle quote
+ for (int i = 0; i < origQuoteLength ; ++i) {
+ if (j < _currentSubtitleLines && i < _subtitleLineSplitAtCharIndex[j]) {
+ _subtitleLineQuote[j] += textCharacters[i];
+ } else { // i is at split point
+ _subtitleLineQuote[j] += '\0';
+ j += 1;
+ }
+ }
+ _subtitleLineQuote[j] += '\0'; // the last line should also be NULL terminated
+ //
+ // Check widths
+ for (int i = 0; i < _currentSubtitleLines; ++i) {
+ tmpLineWidth[i] = _subsFont->getTextWidth(_subtitleLineQuote[i]) + 2;
+ _subtitleLineScreenX[i] = (639 - tmpLineWidth[i]) / 2;
+ _subtitleLineScreenX[i] = CLIP(_subtitleLineScreenX[i], 0, 639 - tmpLineWidth[i]);
+ }
+ } else {
+ // Here we initially have _currentSubtitleLines == 1
+ // Check quote for auto-splitting
+ // Auto splitting requires space characters in the quote string (which should be ok for the typical cases)
+ if (wOrig > kMaxWidthPerLineToAutoSplitThresholdPx) { // kMaxWidthPerLineToAutoSplitThresholdPx is a practical chosen threshold for width for auto-splitting quotes purposes
+ // Start by splitting in two lines. If the new parts are still too lengthy, re-try by splitting in three lines, etc.
+ for (int linesToSplitInto = 2; linesToSplitInto <= kMaxNumOfSubtitlesLines; ++linesToSplitInto) {
+ // find the first blank space after the middle
+ _subtitleLineQuote[0] = "";
+ _currentSubtitleLines = 1;
+
+ textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str(); // reset pointer to the start of subtitle quote
+ textCharacters += (origQuoteLength / linesToSplitInto);
+ _subtitleLineSplitAtCharIndex[0] = (origQuoteLength / linesToSplitInto);
+ while (*textCharacters != 0 && *textCharacters != 0x20) { // seek for a blank space character
+ _subtitleLineSplitAtCharIndex[0] += 1;
+ textCharacters += 1;
+ }
+// debug("space blank at: %d", _subtitleLineSplitAtCharIndex[0]);
+ if (*textCharacters == 0x20) { // if we found a blank space
+ textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str();
+ for (int i = 0; i < _subtitleLineSplitAtCharIndex[0] ; ++i) {
+ _subtitleLineQuote[0] += textCharacters[i];
+ }
+ _subtitleLineQuote[0] += '\0';
+// debug(" Line 0 quote %s", _subtitleLineQuote[0].c_str());
+ tmpLineWidth[0] = _subsFont->getTextWidth(_subtitleLineQuote[0]) + 2; // check the width of the first segment of the quote
+ if (tmpLineWidth[0] > kMaxWidthPerLineToAutoSplitThresholdPx && linesToSplitInto < kMaxNumOfSubtitlesLines) {
+ // reset process by trying to split into more lines
+ continue; // try the for loop with increased linesToSplitInto by 1
+ } else {
+ // keep current split, proceed with splitting the quote for the rest of the subtitle lines (linesToSplitInto)
+ for (int j = 2; j <= linesToSplitInto; ++j) {
+ textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str(); // reset pointer to the start of subtitle quote
+ textCharacters += ((j * origQuoteLength) / linesToSplitInto);
+ _subtitleLineSplitAtCharIndex[_currentSubtitleLines] = ((j * origQuoteLength) / linesToSplitInto);
+ while (*textCharacters != 0 && *textCharacters != 0x20) {
+ _subtitleLineSplitAtCharIndex[_currentSubtitleLines] += 1;
+ textCharacters += 1;
+ }
+ textCharacters = (const uint8 *)_currentSubtitleTextFull.c_str(); // reset pointer to the start of subtitle quote
+ for (int i = _subtitleLineSplitAtCharIndex[_currentSubtitleLines - 1] + 1; i < _subtitleLineSplitAtCharIndex[_currentSubtitleLines]; ++i) {
+ _subtitleLineQuote[_currentSubtitleLines] += textCharacters[i];
+ }
+ _subtitleLineQuote[_currentSubtitleLines] += '\0';
+// debug(" Line %d, space blank at: %d, quote %s", _currentSubtitleLines, _subtitleLineSplitAtCharIndex[_currentSubtitleLines], _subtitleLineQuote[_currentSubtitleLines].c_str());
+ _currentSubtitleLines += 1;
+ }
+ //
+ // Check widths
+ for (int i = 0; i < _currentSubtitleLines; ++i) {
+ tmpLineWidth[i] = _subsFont->getTextWidth(_subtitleLineQuote[i]) + 2;
+ _subtitleLineScreenX[i] = (639 - tmpLineWidth[i]) / 2;
+ _subtitleLineScreenX[i] = CLIP(_subtitleLineScreenX[i], 0, 639 - tmpLineWidth[i]);
+ }
+ break; // from for loop about linesToSplitInto
+ }
+ } else {
+ drawSingleLineQuote = true;
+ break; // from for loop about linesToSplitInto
+ }
+ }
+ } else {
+ drawSingleLineQuote = true;
+ }
+ if (drawSingleLineQuote) {
+ _subtitleLineQuote[0] = _currentSubtitleTextFull;
+ _subtitleLineScreenX[0] = (639 - wOrig) / 2;
+ _subtitleLineScreenX[0] = CLIP(_subtitleLineScreenX[0], 0, 639 - wOrig);
+ }
+ }
+ //debug("calculatePosition: %d %d", w, _screenFirstLineX);
+}
+
+/**
+* Initialize a few basic member vars
+*/
+void Subtitles::clear() {
+ _isVisible = false;
+ _currentSubtitleTextFull = "";
+ for (int i = 0; i < kMaxNumOfSubtitlesLines; ++i) {
+ _subtitleLineQuote[i] = "";
+ _subtitleLineScreenY[i] = 0;
+ _subtitleLineScreenX[i] = 0;
+ _subtitleLineSplitAtCharIndex[i] = 0;
+ }
+ _subtitlesQuoteChanged = true;
+ _currentSubtitleLines = 0;
+}
+
+/**
+* Initialize/ reset member vars, close open file descriptors and garbage collect subtitle fonts and text resource
+*/
+void Subtitles::reset() {
+ clear();
+
+ for (int i = 0; i != kMaxTextResourceEntries; ++i) {
+ if (_vqaSubsTextResourceEntries[i] != nullptr) {
+ delete _vqaSubsTextResourceEntries[i];
+ _vqaSubsTextResourceEntries[i] = nullptr;
+ }
+ _gameSubsFdEntriesFound[i] = false;
+
+ if (_gameSubsFdEntries[i] != nullptr) {
+ if (isOpenGameSubs(i)) {
+ closeGameSubs(i);
+ }
+ delete _gameSubsFdEntries[i];
+ _gameSubsFdEntries[i] = nullptr;
+ }
+ }
+
+#if SUBTITLES_EXTERNAL_FONT
+ if (_subsFont != nullptr) {
+ _subsFont->close();
+ delete _subsFont;
+ _subsFont = nullptr;
+ }
+
+ if (_gameSubsFontsFd != nullptr) {
+ if (isOpenSubsFontFile()) {
+ closeSubsFontFile();
+ }
+ delete _gameSubsFontsFd;
+ _gameSubsFontsFd = nullptr;
+ }
+#else
+ if (_subsFont != nullptr) {
+ _subsFont->close();
+ delete _subsFont;
+ _subsFont = nullptr;
+ }
+ if (_subsBgFont != nullptr) {
+ _subsBgFont->close();
+ delete _subsBgFont;
+ _subsBgFont = nullptr;
+ }
+#endif // SUBTITLES_EXTERNAL_FONT
+ _subsFontsLoaded = false;
+}
+
+} // End of namespace BladeRunner
+#endif
diff --git a/engines/bladerunner/subtitles.h b/engines/bladerunner/subtitles.h
new file mode 100644
index 0000000000..28e970842d
--- /dev/null
+++ b/engines/bladerunner/subtitles.h
@@ -0,0 +1,128 @@
+/* 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.
+ *
+ */
+
+#ifndef BLADERUNNER_SUBTITLES_H
+#define BLADERUNNER_SUBTITLES_H
+
+#include "bladerunner/bladerunner.h"
+#if SUBTITLES_SUPPORT
+
+#include "common/str.h"
+#include "graphics/surface.h"
+
+#include "common/file.h"
+#include "common/substream.h"
+
+
+namespace BladeRunner {
+
+class BladeRunnerEngine;
+//class SaveFileReadStream;
+//class SaveFileWriteStream;
+class TextResource;
+class Font;
+
+class Subtitles {
+ //
+ // Subtitles could be in 6 possible languages are EN_ANY, DE_DEU, FR_FRA, IT_ITA, ES_ESP
+ // with corresponding _vm->_languageCode values: "E", "G", "F", "I", "R", "S"
+ // TODO Maybe support 1 + 6 * 26 entries to support multiple language subtitles? Would that be useful?
+ // TODO Or just support the current _vm->_languageCode ? [current implementation]
+ static const int kMaxNumOfSubtitlesLines = 3;
+ static const int kMaxWidthPerLineToAutoSplitThresholdPx = 610;
+ static const int kMaxTextResourceEntries = 1 + 26; // Support in-game subs (1) and all possible VQAs (26) with spoken dialogue!
+ static const Common::String SUBTITLES_FILENAME_PREFIXES[kMaxTextResourceEntries];
+ static const Common::String SUBTITLES_FONT_FILENAME;
+
+
+ BladeRunnerEngine *_vm;
+
+ TextResource *_vqaSubsTextResourceEntries[kMaxTextResourceEntries];
+ Font *_subsFont;
+#if !SUBTITLES_EXTERNAL_FONT
+ Font *_subsBgFont; // needed for internal font to be used as a shadow effect and make subtitles more legible in certain situations
+#endif // SUBTITLES_EXTERNAL_FONT
+
+ bool _isVisible;
+ Common::String _currentSubtitleTextFull;
+ Common::String _subtitleLineQuote[kMaxNumOfSubtitlesLines];
+ int _subtitleLineScreenY[kMaxNumOfSubtitlesLines];
+ int _subtitleLineScreenX[kMaxNumOfSubtitlesLines];
+ int _subtitleLineSplitAtCharIndex[kMaxNumOfSubtitlesLines];
+ int _currentSubtitleLines;
+ bool _subtitlesQuoteChanged;
+
+ Common::File *_gameSubsFdEntries[kMaxTextResourceEntries]; // an array of pointers to TRE FILEs
+ bool _gameSubsFdEntriesFound[kMaxTextResourceEntries]; // false if a TRE file did not open successfully
+ bool _subsFontsLoaded; // false if external fonts did not load, or internal fonts (fore and background) did not load
+#if SUBTITLES_EXTERNAL_FONT
+ Common::File *_gameSubsFontsFd; // the file for the external FONT for subtitles
+#endif // SUBTITLES_EXTERNAL_FONT
+
+public:
+ Subtitles(BladeRunnerEngine *vm);
+ ~Subtitles();
+
+ const char *getInGameSubsText(int actorId, int speech_id) ; // get the text for actorId, quoteId (in-game subs)
+ const char *getOuttakeSubsText(const Common::String &outtakesName, int frame); // get the text for this frame if any
+
+ void setGameSubsText(Common::String dbgQuote); // for debugging - explicit set subs text
+ bool show();
+ bool hide();
+ bool isVisible() const;
+ void tick(Graphics::Surface &s);
+ void tickOuttakes(Graphics::Surface &s);
+
+private:
+ Common::SeekableReadStream *createReadStreamForGameSubs(int subTreIdx);
+
+ bool openGameSubs(const Common::String &filename);
+ void closeGameSubs(int subTreIdx);
+ bool isOpenGameSubs(int subTreIdx) const;
+
+ bool loadGameSubsText(int subTreIdx); // populate a GAME SUBS TextResource with subtitles
+ //
+ //
+#if SUBTITLES_EXTERNAL_FONT
+ Common::SeekableReadStream *createReadStreamForSubFonts();
+ bool openSubsFontFile();
+ void closeSubsFontFile();
+ bool isOpenSubsFontFile() const; //
+ bool loadSubsFont(); // create a the font object from a FON file (external)
+#endif // SUBTITLES_EXTERNAL_FONT
+
+
+ void draw(Graphics::Surface &s);
+ // bool showAt(int x, int y); // TODO maybe future use (?)
+ void calculatePosition();
+
+ int getIdxForSubsTreName(const Common::String &treName) const;
+
+ void clear();
+ void reset();
+
+};
+
+} // End of namespace BladeRunner
+#endif
+
+#endif
diff --git a/engines/bladerunner/text_resource.cpp b/engines/bladerunner/text_resource.cpp
index 8f54f8a976..b7ae136455 100644
--- a/engines/bladerunner/text_resource.cpp
+++ b/engines/bladerunner/text_resource.cpp
@@ -43,6 +43,50 @@ TextResource::~TextResource() {
delete[] _strings;
}
+#if SUBTITLES_SUPPORT
+// for TRE subtitles support
+bool TextResource::openFromStream(Common::ScopedPtr<Common::SeekableReadStream> &s) {
+
+ if (!s) {
+ return false;
+ }
+
+ _count = s->readUint32LE();
+
+ _ids = new uint32[_count];
+ _offsets = new uint32[_count + 1];
+
+ for (uint32 i = 0; i != _count; ++i) {
+ _ids[i] = s->readUint32LE();
+ }
+
+ for (uint32 i = 0; i != _count + 1; ++i) {
+ _offsets[i] = s->readUint32LE();
+ }
+
+ uint32 stringsStart = s->pos() - 4;
+
+ for (uint32 i = 0; i != _count + 1; ++i) {
+ _offsets[i] -= stringsStart;
+ }
+
+ uint32 remain = s->size() - s->pos();
+ _strings = new char[remain];
+
+ assert(remain >= _offsets[_count]);
+
+ s->read(_strings, remain);
+#if BLADERUNNER_DEBUG_CONSOLE
+// debug("\nRESOURCE:: from Stream\n----------------");
+// for (uint32 i = 0; i != (uint32)_count; ++i) {
+// debug("%3d: %s", _ids[i], getText(_ids[i]));
+// }
+#endif
+
+ return true;
+}
+#endif
+
bool TextResource::open(const Common::String &name) {
assert(name.size() <= 8);
@@ -79,9 +123,9 @@ bool TextResource::open(const Common::String &name) {
s->read(_strings, remain);
#if BLADERUNNER_DEBUG_CONSOLE
- debug("\n%s\n----------------", resName);
+ debug("\n%s\n----------------", resName.c_str());
for (uint32 i = 0; i != (uint32)_count; ++i) {
- debug("%3d: %s", i, getText(i));
+ debug("%3d: %s", _ids[i], getText(_ids[i]));
}
#endif
@@ -98,6 +142,20 @@ const char *TextResource::getText(uint32 id) const {
return "";
}
+#if SUBTITLES_SUPPORT
+const char *TextResource::getOuttakeTextByFrame(uint32 frame) const {
+ for (uint32 i = 0; i != _count; ++i) {
+ //debug("Checking %d - so within: %d , %d", _ids[i], (0x0000FFFF & _ids[i]), ((_ids[i] >> 16) & 0x0000FFFF ) );
+ if ((frame >= (0x0000FFFF & _ids[i]) ) && (frame < ((_ids[i] >> 16) & 0x0000FFFF ) )){
+ // we found an id with lower 16bits smaller or equal to our frame key
+ // and with higher 16 bits higher than the frame key
+ return _strings + _offsets[i];
+ }
+ }
+ return "";
+}
+#endif
+
int TextResource::getCount() const {
return _count;
}
diff --git a/engines/bladerunner/text_resource.h b/engines/bladerunner/text_resource.h
index b513629d9d..be399bf2d8 100644
--- a/engines/bladerunner/text_resource.h
+++ b/engines/bladerunner/text_resource.h
@@ -23,6 +23,10 @@
#ifndef BLADERUNNER_TEXT_RESOURCE_H
#define BLADERUNNER_TEXT_RESOURCE_H
+#include "bladerunner/bladerunner.h" // needed for definition of Common::ScopedPtr (subtitles font external font file support) -- and for the subtitles relevant macro defines
+#if SUBTITLES_SUPPORT
+#include "common/util.h"
+#endif
#include "common/str.h"
namespace BladeRunner {
@@ -43,7 +47,14 @@ public:
~TextResource();
bool open(const Common::String &name);
+ #if SUBTITLES_SUPPORT
+ bool openFromStream(Common::ScopedPtr<Common::SeekableReadStream> &s);
+ #endif
+
const char *getText(uint32 id) const;
+ #if SUBTITLES_SUPPORT
+ const char *getOuttakeTextByFrame(uint32 frame) const;
+ #endif
int getCount() const;
};
diff --git a/engines/bladerunner/ui/elevator.cpp b/engines/bladerunner/ui/elevator.cpp
index 64164f442d..7026f0ea01 100644
--- a/engines/bladerunner/ui/elevator.cpp
+++ b/engines/bladerunner/ui/elevator.cpp
@@ -32,6 +32,9 @@
#include "bladerunner/time.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/vqa_player.h"
+#if SUBTITLES_SUPPORT
+#include "bladerunner/subtitles.h"
+#endif
#include "common/rect.h"
#include "common/str.h"
@@ -226,6 +229,9 @@ void Elevator::tick() {
_imagePicker->draw(_vm->_surfaceFront);
_vm->_mouse->draw(_vm->_surfaceFront, p.x, p.y);
+#if SUBTITLES_SUPPORT
+ _vm->_subtitles->tick(_vm->_surfaceFront);
+#endif
_vm->blitToScreen(_vm->_surfaceFront);
tickDescription();
_vm->_system->delayMillis(10);
diff --git a/engines/bladerunner/ui/esper.cpp b/engines/bladerunner/ui/esper.cpp
index 51c64f6bc9..2a0431f0d8 100644
--- a/engines/bladerunner/ui/esper.cpp
+++ b/engines/bladerunner/ui/esper.cpp
@@ -38,6 +38,9 @@
#include "bladerunner/time.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/vqa_player.h"
+#if SUBTITLES_SUPPORT
+#include "bladerunner/subtitles.h"
+#endif
#include "common/rect.h"
#include "common/str.h"
@@ -230,7 +233,9 @@ void ESPER::tick() {
drawMouse(_vm->_surfaceFront);
tickSound();
-
+#if SUBTITLES_SUPPORT
+ _vm->_subtitles->tick(_vm->_surfaceFront);
+#endif
_vm->blitToScreen(_vm->_surfaceFront);
// TODO: implement 60hz lock for smoother experience
diff --git a/engines/bladerunner/ui/kia.cpp b/engines/bladerunner/ui/kia.cpp
index bd0402847b..183bf615d9 100644
--- a/engines/bladerunner/ui/kia.cpp
+++ b/engines/bladerunner/ui/kia.cpp
@@ -53,6 +53,9 @@
#include "bladerunner/ui/kia_shapes.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/vqa_player.h"
+#if SUBTITLES_SUPPORT
+#include "bladerunner/subtitles.h"
+#endif // SUBTITLES_SUPPORT
#include "common/str.h"
#include "common/keyboard.h"
@@ -371,6 +374,10 @@ void KIA::tick() {
}
_vm->_mouse->draw(_vm->_surfaceFront, mouse.x, mouse.y);
+#if SUBTITLES_SUPPORT
+ _vm->_subtitles->tick(_vm->_surfaceFront);
+#endif
+
_vm->blitToScreen(_vm->_surfaceFront);
_vm->_system->delayMillis(10);
diff --git a/engines/bladerunner/ui/kia_section_settings.cpp b/engines/bladerunner/ui/kia_section_settings.cpp
index b557a04877..8ff3c7e7b5 100644
--- a/engines/bladerunner/ui/kia_section_settings.cpp
+++ b/engines/bladerunner/ui/kia_section_settings.cpp
@@ -55,7 +55,14 @@ KIASectionSettings::KIASectionSettings(BladeRunnerEngine *vm)
_ambientSoundVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 210, 460, 220), 101, 0);
_speechVolume = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 235, 460, 245), 101, 0);
_gammaCorrection = new UISlider(_vm, sliderCallback, this, Common::Rect(180, 260, 460, 270), 101, 0);
- _directorsCut = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(180, 364, 460, 374), 0, false);
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ _directorsCut = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(180, 364, 270, 374), 0, false);
+ #if SUBTITLES_SUPPORT
+ _subtitlesEnable = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(291, 364, 360, 374), 0, false);
+ #endif // SUBTITLES_SUPPORT
+#else
+ _directorsCut = new UICheckBox(_vm, checkBoxCallback, this, Common::Rect(180, 364, 460, 374), 0, false); // original
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
_playerAgendaSelector = new UIImagePicker(_vm, 5);
_uiContainer->add(_musicVolume);
@@ -64,6 +71,11 @@ KIASectionSettings::KIASectionSettings(BladeRunnerEngine *vm)
_uiContainer->add(_speechVolume);
_uiContainer->add(_gammaCorrection);
_uiContainer->add(_directorsCut);
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ _uiContainer->add(_subtitlesEnable);
+ #endif // SUBTITLES_SUPPORT
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
_learyPos = 0;
}
@@ -76,6 +88,11 @@ KIASectionSettings::~KIASectionSettings() {
delete _speechVolume;
delete _gammaCorrection;
delete _directorsCut;
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ delete _subtitlesEnable;
+ #endif // SUBTITLES_SUPPORT
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
delete _playerAgendaSelector;
}
@@ -91,6 +108,11 @@ void KIASectionSettings::open() {
_playerAgendaSelector->activate(mouseInCallback, nullptr, nullptr, mouseUpCallback, this);
_directorsCut->enable();
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ _subtitlesEnable->enable();
+ #endif // SUBTITLES_SUPPORT
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
}
void KIASectionSettings::close() {
@@ -104,6 +126,11 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
_speechVolume->setValue(_vm->_audioSpeech->getVolume());
_gammaCorrection->setValue(100.0f);
_directorsCut->setChecked(_vm->_gameFlags->query(kFlagDirectorsCut));
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ _subtitlesEnable->setChecked(_vm->_extraGameFlagsForRestoredContent->query(kEDSFlagSubtitlesEnable));
+ #endif // SUBTITLES_SUPPORT
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
const char *textConversationChoices = _vm->_textOptions->getText(0);
const char *textMusic = _vm->_textOptions->getText(2);
@@ -116,6 +143,11 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
const char *textDark = _vm->_textOptions->getText(14);
const char *textLight = _vm->_textOptions->getText(15);
const char *textDesignersCut = _vm->_textOptions->getText(18);
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ const char *textSubtitles = "Subtitles";
+ #endif // SUBTITLES_SUPPORT
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
int posConversationChoices = 320 - _vm->_mainFont->getTextWidth(textConversationChoices) / 2;
int posMusic = 320 - _vm->_mainFont->getTextWidth(textMusic) / 2;
@@ -152,6 +184,11 @@ void KIASectionSettings::draw(Graphics::Surface &surface) {
_vm->_mainFont->drawColor(textLight, surface, 462, 261, 0x6EEE);
_vm->_mainFont->drawColor(textDesignersCut, surface, 192, 365, 0x7751);
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ _vm->_mainFont->drawColor(textSubtitles, surface, 303, 365, 0x7751);
+ #endif // SUBTITLES_SUPPORT
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
_playerAgendaSelector->drawTooltip(surface, _mouseX, _mouseY);
}
@@ -227,6 +264,17 @@ void KIASectionSettings::checkBoxCallback(void *callbackData, void *source) {
self->_vm->_gameFlags->reset(kFlagDirectorsCut);
}
}
+ #if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ else if (source == self->_subtitlesEnable) {
+ if (self->_subtitlesEnable->_isChecked) {
+ self->_vm->_extraGameFlagsForRestoredContent->set(kEDSFlagSubtitlesEnable);
+ } else {
+ self->_vm->_extraGameFlagsForRestoredContent->reset(kEDSFlagSubtitlesEnable);
+ }
+ }
+ #endif // SUBTITLES_SUPPORT
+ #endif // BLADERUNNER_RESTORED_CONTENT_GAME
}
void KIASectionSettings::mouseInCallback(int buttonId, void *callbackData) {
diff --git a/engines/bladerunner/ui/kia_section_settings.h b/engines/bladerunner/ui/kia_section_settings.h
index 49356266b7..4c5452b595 100644
--- a/engines/bladerunner/ui/kia_section_settings.h
+++ b/engines/bladerunner/ui/kia_section_settings.h
@@ -23,6 +23,7 @@
#ifndef BLADERUNNER_KIA_SECTION_SETTINGS_H
#define BLADERUNNER_KIA_SECTION_SETTINGS_H
+#include "bladerunner/bladerunner.h" // to get the macro defines
#include "bladerunner/ui/kia_section_base.h"
namespace BladeRunner {
@@ -43,6 +44,11 @@ class KIASectionSettings : public KIASectionBase {
UISlider *_speechVolume;
UISlider *_gammaCorrection;
UICheckBox *_directorsCut;
+#if BLADERUNNER_RESTORED_CONTENT_GAME
+ #if SUBTITLES_SUPPORT
+ UICheckBox *_subtitlesEnable;
+ #endif
+#endif // BLADERUNNER_RESTORED_CONTENT_GAME
UIImagePicker *_playerAgendaSelector;
int _mouseX;
diff --git a/engines/bladerunner/ui/vk.cpp b/engines/bladerunner/ui/vk.cpp
index aa95b6bf9c..8565d39a36 100644
--- a/engines/bladerunner/ui/vk.cpp
+++ b/engines/bladerunner/ui/vk.cpp
@@ -41,6 +41,9 @@
#include "bladerunner/time.h"
#include "bladerunner/ui/ui_image_picker.h"
#include "bladerunner/vqa_player.h"
+#if SUBTITLES_SUPPORT
+#include "bladerunner/subtitles.h"
+#endif
#include "common/str.h"
#include "common/keyboard.h"
@@ -196,6 +199,10 @@ void VK::tick() {
draw();
+#if SUBTITLES_SUPPORT
+ _vm->_subtitles->tick(_vm->_surfaceFront);
+#endif // SUBTITLES_SUPPORT
+
_vm->blitToScreen(_vm->_surfaceFront);
_vm->_system->delayMillis(10);