[Scummvm-git-logs] scummvm master -> 1efcf323d0580a58f8f3f9aa93ad9781446c8988

neuromancer noreply at scummvm.org
Thu Aug 14 19:43:51 UTC 2025


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
1efcf323d0 PRIVATE: initial support for subtitles in videos and audio


Commit: 1efcf323d0580a58f8f3f9aa93ad9781446c8988
    https://github.com/scummvm/scummvm/commit/1efcf323d0580a58f8f3f9aa93ad9781446c8988
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2025-08-14T21:14:32+02:00

Commit Message:
PRIVATE: initial support for subtitles in videos and audio

Changed paths:
    engines/private/detection.cpp
    engines/private/metaengine.cpp
    engines/private/private.cpp
    engines/private/private.h


diff --git a/engines/private/detection.cpp b/engines/private/detection.cpp
index 850277ee017..2bcb22630f5 100644
--- a/engines/private/detection.cpp
+++ b/engines/private/detection.cpp
@@ -22,7 +22,9 @@
 #include "base/plugins.h"
 #include "common/translation.h"
 #include "engines/advancedDetector.h"
+
 #include "private/private.h"
+#include "private/detection.h"
 
 static const DebugChannelDef debugFlagList[] = {
 	{Private::kPrivateDebugFunction, "functions", "Function execution debug channel"},
@@ -46,7 +48,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye", // Demo from the US release v1.0.0.23
@@ -56,7 +58,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye",  // EU release (UK)
@@ -66,7 +68,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_GRB,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye", // Demo from the EU release
@@ -76,7 +78,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_GRB,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye", // Demo from PCGamer Disc 2.6 JULY 1996 v1.0.0.12
@@ -86,7 +88,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye", // Another demo
@@ -96,7 +98,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye", // EU release (ES)
@@ -196,7 +198,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformMacintosh,
 		ADGF_NO_FLAGS,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye", // MacOS release (US) uninstalled
@@ -224,7 +226,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformMacintosh,
 		ADGF_DEMO,
-		GUIO1(GUIO_NOMIDI)
+		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
 	},
 	{
 		"private-eye", // MacOS demo (US) uninstalled
diff --git a/engines/private/metaengine.cpp b/engines/private/metaengine.cpp
index f7b93438c0f..26d642953e5 100644
--- a/engines/private/metaengine.cpp
+++ b/engines/private/metaengine.cpp
@@ -21,8 +21,25 @@
 
 #include "engines/advancedDetector.h"
 #include "graphics/scaler.h"
+#include "common/translation.h"
 
 #include "private/private.h"
+#include "private/detection.h"
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+	{
+		GAMEOPTION_SUBTITLES,
+		{
+			_s("Display subtitles"),
+			_s("Use subtitles."),
+			"subtitles",
+			true,
+			0,
+			0
+		}
+	},
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
 
 class PrivateMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
 public:
@@ -30,6 +47,10 @@ public:
 		return "private";
 	}
 
+	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+		return optionsList;
+	}
+
 	Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
 	void getSavegameThumbnail(Graphics::Surface &thumb) override;
 };
@@ -40,7 +61,7 @@ Common::Error PrivateMetaEngine::createInstance(OSystem *syst, Engine **engine,
 }
 
 void PrivateMetaEngine::getSavegameThumbnail(Graphics::Surface &thumb) {
-	byte *palette; 
+	byte *palette;
 	Graphics::Surface *vs = Private::g_private->decodeImage(Private::g_private->_nextVS, &palette);
 	::createThumbnail(&thumb, (const uint8 *)vs->getPixels(), vs->w, vs->h, palette);
 	vs->free();
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 4afb7885e2a..ffd4e99c227 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -33,6 +33,7 @@
 #include "common/system.h"
 #include "common/timer.h"
 #include "common/macresman.h"
+#include "common/language.h"
 #include "common/compression/stuffit.h"
 #include "graphics/paletteman.h"
 #include "engines/util.h"
@@ -51,7 +52,7 @@ extern int parse(const char *);
 PrivateEngine::PrivateEngine(OSystem *syst, const ADGameDescription *gd)
 	: Engine(syst), _gameDescription(gd), _image(nullptr), _videoDecoder(nullptr),
 	  _compositeSurface(nullptr), _transparentColor(0), _frameImage(nullptr),
-	  _framePalette(nullptr), _maxNumberClicks(0), _sirenWarning(0),
+	  _framePalette(nullptr), _maxNumberClicks(0), _sirenWarning(0), _subtitles(nullptr),
 	  _screenW(640), _screenH(480) {
 	_rnd = new Common::RandomSource("private");
 
@@ -199,6 +200,15 @@ Common::SeekableReadStream *PrivateEngine::loadAssets() {
 
 Common::Error PrivateEngine::run() {
 
+	// Only enable if subtitles are available
+	bool useSubtitles = false;
+	if (!Common::parseBool(ConfMan.get("subtitles"), useSubtitles))
+		error("Failed to parse bool from cheats options");
+
+	if (useSubtitles) {
+		g_system->showOverlay(false);
+	}
+
 	_language = Common::parseLanguage(ConfMan.get("language"));
 	_platform = Common::parsePlatform(ConfMan.get("platform"));
 
@@ -372,6 +382,8 @@ Common::Error PrivateEngine::run() {
 				_videoDecoder->close();
 				delete _videoDecoder;
 				_videoDecoder = nullptr;
+				delete _subtitles;
+				_subtitles = nullptr;
 				_currentMovie = "";
 			} else if (_videoDecoder->needsUpdate()) {
 				drawScreen();
@@ -395,6 +407,14 @@ Common::Error PrivateEngine::run() {
 
 		g_system->updateScreen();
 		g_system->delayMillis(10);
+		if (_subtitles) {
+			if (_mixer->isSoundHandleActive(_fgSoundHandle)) {
+				_subtitles->drawSubtitle(_mixer->getElapsedTime(_fgSoundHandle).msecs(), false);
+			} else {
+				delete _subtitles;
+				_subtitles = nullptr;
+			}
+		}
 	}
 	return Common::kNoError;
 }
@@ -773,7 +793,7 @@ bool PrivateEngine::selectLocation(const Common::Point &mousePos) {
 	if (_locationMasks.size() == 0) {
 		return false;
 	}
-	
+
 	uint i = 0;
 	uint totalLocations = 0;
 	for (auto &it : maps.locationList) {
@@ -795,7 +815,7 @@ bool PrivateEngine::selectLocation(const Common::Point &mousePos) {
 				if (!diaryPageSet) {
 					return true;
 				}
-	
+
 				_nextSetting = _locationMasks[i].nextSetting;
 
 				return true;
@@ -912,7 +932,7 @@ void PrivateEngine::addMemory(const Common::String &path) {
 	}
 
 	diaryPage.memories.push_back(memory);
-	
+
 	for (int i = _diaryPages.size() - 1; i >= 0; i--) {
 		if (_diaryPages[i].locationID < diaryPage.locationID) {
 			_diaryPages.insert_at(i + 1, diaryPage);
@@ -1448,12 +1468,41 @@ void PrivateEngine::playSound(const Common::String &name, uint loops, bool stopO
 	}
 
 	_mixer->playStream(Audio::Mixer::kSFXSoundType, sh, stream, -1, Audio::Mixer::kMaxChannelVolume);
+	loadSubtitles(path);
 }
 
 bool PrivateEngine::isSoundActive() {
 	return _mixer->isSoundIDActive(-1);
 }
 
+void PrivateEngine::loadSubtitles(const Common::Path &path) {
+	debugC(1, kPrivateDebugFunction, "%s(%s)", __FUNCTION__, path.toString().c_str());
+	Common::String subPathStr = path.toString() + ".srt";
+	subPathStr.toLowercase();
+	subPathStr.replace('/', '_');
+	Common::String language(Common::getLanguageCode(_language));
+	if (language == "us")
+		language = "en";
+
+	Common::Path subPath = "subtitles";
+	subPath = subPath.appendComponent(language);
+	subPath = subPath.appendComponent(subPathStr);
+	debugC(1, kPrivateDebugFunction, "Loading subtitles from %s", subPath.toString().c_str());
+	if (Common::File::exists(subPath)) {
+		_subtitles = new Video::Subtitles();
+		_subtitles->loadSRTFile(subPath);
+		g_system->showOverlay(false);
+		int16 h = g_system->getOverlayHeight();
+
+		_subtitles->setBBox(Common::Rect(20, h - 120, g_system->getOverlayWidth() - 20, h - 20));
+		_subtitles->setColor(0xff, 0xff, 0xff);
+		_subtitles->setFont("LiberationSans-Regular.ttf", 50);
+
+	} else {
+		delete _subtitles;
+		_subtitles = nullptr;
+	}
+}
 void PrivateEngine::playVideo(const Common::String &name) {
 	debugC(1, kPrivateDebugFunction, "%s(%s)", __FUNCTION__, name.c_str());
 	//stopSound(true);
@@ -1465,6 +1514,8 @@ void PrivateEngine::playVideo(const Common::String &name) {
 
 	if (!_videoDecoder->loadStream(file))
 		error("unable to load video %s", path.toString().c_str());
+
+	loadSubtitles(path);
 	_videoDecoder->start();
 }
 
@@ -1472,6 +1523,8 @@ void PrivateEngine::skipVideo() {
 	_videoDecoder->close();
 	delete _videoDecoder;
 	_videoDecoder = nullptr;
+	delete _subtitles;
+	_subtitles = nullptr;
 	_currentMovie = "";
 }
 
@@ -1651,6 +1704,9 @@ void PrivateEngine::drawScreen() {
 		Graphics::Surface sa = _compositeSurface->getSubArea(w);
 		g_system->copyRectToScreen(sa.getPixels(), sa.pitch, _origin.x, _origin.y, sa.w, sa.h);
 	}
+
+	if (_subtitles && _videoDecoder && !_videoDecoder->isPaused())
+		_subtitles->drawSubtitle(_videoDecoder->getTime(), false);
 	g_system->updateScreen();
 }
 
diff --git a/engines/private/private.h b/engines/private/private.h
index 9bf86674e13..599215b4875 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -29,6 +29,7 @@
 #include "graphics/managed_surface.h"
 #include "graphics/wincursor.h"
 #include "video/smk_decoder.h"
+#include "video/subtitles.h"
 
 #include "private/grammar.h"
 
@@ -218,6 +219,9 @@ public:
 	void playVideo(const Common::String &);
 	void skipVideo();
 
+	void loadSubtitles(const Common::Path &path);
+	Video::Subtitles *_subtitles;
+
 	Graphics::Surface *decodeImage(const Common::String &file, byte **palette);
 	//byte *decodePalette(const Common::String &name);
 	void remapImage(uint16 ncolors, const Graphics::Surface *oldImage, const byte *oldPalette, Graphics::Surface *newImage, const byte *currentPalette);




More information about the Scummvm-git-logs mailing list