[Scummvm-git-logs] scummvm master -> 8771bc95a2f976c63ed6d473f692ed33741d11bf

neuromancer noreply at scummvm.org
Mon Aug 18 15:41:08 UTC 2025


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

Summary:
24b38ae23c VIDEO: parse tags in subtitles and allow multiples fonts
8771bc95a2 PRIVATE: use new subtitles code


Commit: 24b38ae23c2c5edb1bebe0eb654c5bfae3aecb68
    https://github.com/scummvm/scummvm/commit/24b38ae23c2c5edb1bebe0eb654c5bfae3aecb68
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2025-08-18T17:43:49+02:00

Commit Message:
VIDEO: parse tags in subtitles and allow multiples fonts

Changed paths:
    video/subtitles.cpp
    video/subtitles.h


diff --git a/video/subtitles.cpp b/video/subtitles.cpp
index 555619a41b7..1fb801d57f5 100644
--- a/video/subtitles.cpp
+++ b/video/subtitles.cpp
@@ -49,6 +49,35 @@ void SRTParser::cleanup() {
 	_entries.clear();
 }
 
+Common::String SRTParser::SRTParser::parseTag(Common::String &text) const {
+    // Parse tags in _subtitle
+    // <i> and </i> for italics
+    // <sfx> and </sfx> for sound effects
+
+    Common::String tag;
+    Common::String::size_type pos = text.find("<i>");
+    debug(1, "Subtitles: %s", text.c_str());
+    if (pos != Common::String::npos) {
+        text.erase(pos, 3);
+        pos = text.find("</i>");
+        if (pos != Common::String::npos) {
+            text.erase(pos, 4);
+        }
+        tag = "i";
+    } else {
+        pos = text.find("<sfx>");
+        if (pos != Common::String::npos) {
+            text.erase(pos, 5);
+            pos = text.find("</sfx>");
+            if (pos != Common::String::npos) {
+                text.erase(pos, 6);
+            }
+            tag = "sfx";
+        }
+    }
+    return tag;
+}
+
 bool parseTime(const char **pptr, uint32 *res) {
 	int hours, mins, secs, msecs;
 	const char *ptr = *pptr;
@@ -206,7 +235,8 @@ bool SRTParser::parseFile(const Common::Path &fname) {
 			break;
 		}
 
-		_entries.push_back(new SRTEntry(seq, start, end, text));
+		Common::String tag = parseTag(text);
+		_entries.push_back(new SRTEntry(seq, start, end, text, tag));
 	}
 
 	qsort(_entries.data(), _entries.size(), sizeof(SRTEntry *), &SRTEntryComparator);
@@ -228,9 +258,21 @@ Common::String SRTParser::getSubtitle(uint32 timestamp) const {
 	return (*entry)->text;
 }
 
+Common::String SRTParser::getTag(uint32 timestamp) const {
+	SRTEntry test(0, timestamp, 0, "");
+	SRTEntry *testptr = &test;
+
+	const SRTEntry **entry = (const SRTEntry **)bsearch(&testptr, _entries.data(), _entries.size(), sizeof(SRTEntry *), &SRTEntryComparatorBSearch);
+
+	if (entry == NULL)
+		return "";
+
+	return (*entry)->tag;
+}
+
 #define SHADOW 1
 
-Subtitles::Subtitles() : _loaded(false), _font(nullptr), _hPad(0), _vPad(0), _overlayHasAlpha(true),
+Subtitles::Subtitles() : _loaded(false), _hPad(0), _vPad(0), _overlayHasAlpha(true),
 	_lastOverlayWidth(-1), _lastOverlayHeight(-1) {
 	_surface = new Graphics::Surface();
 	_subtitleDev = ConfMan.getBool("subtitle_dev");
@@ -240,33 +282,36 @@ Subtitles::~Subtitles() {
 	delete _surface;
 }
 
-void Subtitles::setFont(const char *fontname, int height) {
+void Subtitles::setFont(const char *fontname, int height, Common::String type) {
+	Graphics::Font *font = nullptr;
 	_fontHeight = height;
 
 #ifdef USE_FREETYPE2
 	Common::File *file = new Common::File();
 	if (file->open(fontname)) {
-		_font = Graphics::loadTTFFont(file, DisposeAfterUse::YES, _fontHeight, Graphics::kTTFSizeModeCharacter, 96);
-		if (!_font)
+		font = Graphics::loadTTFFont(file, DisposeAfterUse::YES, _fontHeight, Graphics::kTTFSizeModeCharacter, 96);
+		if (!font)
 			delete file;
+		else
+			_fonts[type] = font;
 	} else {
 		delete file;
 	}
 
-	if (!_font) {
-		_font = Graphics::loadTTFFontFromArchive(fontname, _fontHeight, Graphics::kTTFSizeModeCharacter, 96);
+	if (!font) {
+		_fonts[type] = Graphics::loadTTFFontFromArchive(fontname, _fontHeight, Graphics::kTTFSizeModeCharacter, 96);
 	}
 #endif
 
-	if (!_font) {
+	if (!_fonts[type]) {
 		debug(1, "Cannot load font %s directly", fontname);
-		_font = FontMan.getFontByName(fontname);
+		_fonts[type] = FontMan.getFontByName(fontname);
 	}
 
-	if (!_font) {
+	if (!_fonts[type]) {
 		warning("Cannot load font %s", fontname);
 
-		_font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+		_fonts[type] = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
 	}
 
 }
@@ -302,10 +347,12 @@ void Subtitles::setPadding(uint16 horizontal, uint16 vertical) {
 	_vPad = vertical;
 }
 
-bool Subtitles::drawSubtitle(uint32 timestamp, bool force) const {
+bool Subtitles::drawSubtitle(uint32 timestamp, bool force, bool showSFX) {
 	Common::String subtitle;
+	Common::String tag;
 	if (_loaded) {
 		subtitle = _srtParser.getSubtitle(timestamp);
+		tag = _srtParser.getTag(timestamp);
 	} else if (_subtitleDev) {
 		subtitle = _fname.toString('/');
 		uint32 hours, mins, secs, msecs;
@@ -357,10 +404,13 @@ bool Subtitles::drawSubtitle(uint32 timestamp, bool force) const {
 		return false;
 
 	if (force || subtitle != _subtitle) {
-		debug(1, "%d: %s", timestamp, subtitle.c_str());
+		debug(1, "%d: %s with tag %s", timestamp, subtitle.c_str(), tag.c_str());
 
 		_subtitle = subtitle;
-		renderSubtitle();
+		_tag = tag;
+
+		if (_tag != "sfx" || showSFX)
+			renderSubtitle();
 	}
 
 	if (_overlayHasAlpha) {
@@ -382,8 +432,10 @@ void Subtitles::renderSubtitle() const {
 	_surface->fillRect(Common::Rect(0, 0, _surface->w, _surface->h), _transparentColor);
 
 	Common::Array<Common::U32String> lines;
+	Common::String fontType = _tag == "i" ? "italic" : "regular";
+	const Graphics::Font *font = _fonts[fontType] ? _fonts[fontType] : _fonts["regular"];
 
-	_font->wordWrapText(convertUtf8ToUtf32(_subtitle), _realBBox.width(), lines);
+	font->wordWrapText(convertUtf8ToUtf32(_subtitle), _realBBox.width(), lines);
 
 	if (lines.empty()) {
 		_drawRect.left = 0;
@@ -398,7 +450,7 @@ void Subtitles::renderSubtitle() const {
 
 	int width = 0;
 	for (uint i = 0; i < lines.size(); i++)
-		width = MAX(_font->getStringWidth(lines[i]), width);
+		width = MAX(font->getStringWidth(lines[i]), width);
 	width = MIN(width + 2 * _hPad, (int)_realBBox.width());
 
 	int originX = (_realBBox.width() - width) / 2;
@@ -406,14 +458,14 @@ void Subtitles::renderSubtitle() const {
 	for (uint i = 0; i < lines.size(); i++) {
 		Common::U32String line = convertBiDiU32String(lines[i]).visual;
 
-		_font->drawString(_surface, line, originX, height, width, _blackColor, Graphics::kTextAlignCenter);
-		_font->drawString(_surface, line, originX + SHADOW * 2, height, width, _blackColor, Graphics::kTextAlignCenter);
-		_font->drawString(_surface, line, originX, height + SHADOW * 2, width, _blackColor, Graphics::kTextAlignCenter);
-		_font->drawString(_surface, line, originX + SHADOW * 2, height + SHADOW * 2, width, _blackColor, Graphics::kTextAlignCenter);
+		font->drawString(_surface, line, originX, height, width, _blackColor, Graphics::kTextAlignCenter);
+		font->drawString(_surface, line, originX + SHADOW * 2, height, width, _blackColor, Graphics::kTextAlignCenter);
+		font->drawString(_surface, line, originX, height + SHADOW * 2, width, _blackColor, Graphics::kTextAlignCenter);
+		font->drawString(_surface, line, originX + SHADOW * 2, height + SHADOW * 2, width, _blackColor, Graphics::kTextAlignCenter);
 
-		_font->drawString(_surface, line, originX + SHADOW, height + SHADOW, width, _color, Graphics::kTextAlignCenter);
+		font->drawString(_surface, line, originX + SHADOW, height + SHADOW, width, _color, Graphics::kTextAlignCenter);
 
-		height += _font->getFontHeight();
+		height += font->getFontHeight();
 
 		if (height + _vPad > _realBBox.bottom)
 			break;
diff --git a/video/subtitles.h b/video/subtitles.h
index c11e4a4f6d0..1f5aba94a31 100644
--- a/video/subtitles.h
+++ b/video/subtitles.h
@@ -24,6 +24,7 @@
 
 #include "common/str.h"
 #include "common/array.h"
+#include "common/hashmap.h"
 #include "common/rect.h"
 
 namespace Graphics {
@@ -39,9 +40,10 @@ struct SRTEntry {
 	uint32 end;
 
 	Common::String text;
+	Common::String tag;
 
-	SRTEntry(uint seq_, uint32 start_, uint32 end_, Common::String text_) {
-		seq = seq_; start = start_; end = end_; text = text_;
+	SRTEntry(uint seq_, uint32 start_, uint32 end_, Common::String text_, Common::String tag_ = "") {
+		seq = seq_; start = start_; end = end_; text = text_; tag = tag_;
 	}
 };
 
@@ -52,7 +54,9 @@ public:
 
 	void cleanup();
 	bool parseFile(const Common::Path &fname);
+	Common::String parseTag(Common::String &text) const;
 	Common::String getSubtitle(uint32 timestamp) const;
+	Common::String getTag(uint32 timestamp) const;
 
 private:
 	Common::Array<SRTEntry *> _entries;
@@ -65,11 +69,11 @@ public:
 
 	void loadSRTFile(const Common::Path &fname);
 	void close() { _loaded = false; _subtitle.clear(); _fname.clear(); _srtParser.cleanup(); }
-	void setFont(const char *fontname, int height = 18);
+	void setFont(const char *fontname, int height = 18, Common::String type = "regular");
 	void setBBox(const Common::Rect &bbox);
 	void setColor(byte r, byte g, byte b);
 	void setPadding(uint16 horizontal, uint16 vertical);
-	bool drawSubtitle(uint32 timestamp, bool force = false) const;
+	bool drawSubtitle(uint32 timestamp, bool force = false, bool showSFX = false);
 	bool isLoaded() const { return _loaded || _subtitleDev; }
 
 private:
@@ -80,7 +84,7 @@ private:
 	bool _subtitleDev;
 	bool _overlayHasAlpha;
 
-	const Graphics::Font *_font;
+	Common::HashMap<Common::String, const Graphics::Font *> _fonts;
 	int _fontHeight;
 
 	Graphics::Surface *_surface;
@@ -92,6 +96,7 @@ private:
 
 	Common::Path _fname;
 	mutable Common::String _subtitle;
+	mutable Common::String _tag;
 	uint32 _color;
 	uint32 _blackColor;
 	uint32 _transparentColor;


Commit: 8771bc95a2f976c63ed6d473f692ed33741d11bf
    https://github.com/scummvm/scummvm/commit/8771bc95a2f976c63ed6d473f692ed33741d11bf
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2025-08-18T17:43:49+02:00

Commit Message:
PRIVATE: use new subtitles code

Changed paths:
    engines/private/detection.cpp
    engines/private/detection.h
    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 bbff2208f58..8bab26f24ac 100644
--- a/engines/private/detection.cpp
+++ b/engines/private/detection.cpp
@@ -48,7 +48,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye", // Demo from the US release v1.0.0.23
@@ -58,7 +58,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye",  // EU release (UK)
@@ -68,7 +68,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_GRB,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye", // Demo from the EU release
@@ -78,7 +78,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_GRB,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye", // Demo from PCGamer Disc 2.6 JULY 1996 v1.0.0.12
@@ -88,7 +88,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye", // Another demo
@@ -98,7 +98,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_USA,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye", // EU release (ES)
@@ -108,7 +108,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformWindows,
 		ADGF_NO_FLAGS,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye", // Demo from the EU release (ES)
@@ -118,7 +118,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::ES_ESP,
 		Common::kPlatformWindows,
 		ADGF_DEMO,
-		GUIO2(GUIO_NOMIDI, GAMEOPTION_SUBTITLES)
+		GUIO3(GUIO_NOMIDI, GAMEOPTION_SUBTITLES, GAMEOPTION_SFX_SUBTITLES)
 	},
 	{
 		"private-eye", // EU release (FR)
diff --git a/engines/private/detection.h b/engines/private/detection.h
index a57788c4db6..1ddbc8ef4ae 100644
--- a/engines/private/detection.h
+++ b/engines/private/detection.h
@@ -2,5 +2,6 @@
 #define PRIVATE_DETECTION_H
 
 #define GAMEOPTION_SUBTITLES   GUIO_GAMEOPTIONS1
+#define GAMEOPTION_SFX_SUBTITLES GUIO_GAMEOPTIONS2
 
 #endif // PRIVATE_DETECTION_H
\ No newline at end of file
diff --git a/engines/private/metaengine.cpp b/engines/private/metaengine.cpp
index 26d642953e5..42f861e6e5a 100644
--- a/engines/private/metaengine.cpp
+++ b/engines/private/metaengine.cpp
@@ -38,6 +38,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_SFX_SUBTITLES,
+		{
+			_s("Display SFX subtitles"),
+			_s("Use SFX subtitles."),
+			"sfxSubtitles",
+			false,
+			0,
+			0
+		}
+	},
 	AD_EXTRA_GUI_OPTIONS_TERMINATOR
 };
 
diff --git a/engines/private/private.cpp b/engines/private/private.cpp
index 425e1e45aff..89c77bf1b82 100644
--- a/engines/private/private.cpp
+++ b/engines/private/private.cpp
@@ -52,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), _subtitles(nullptr),
+	  _framePalette(nullptr), _maxNumberClicks(0), _sirenWarning(0), _subtitles(nullptr), _sfxSubtitles(false),
 	  _screenW(640), _screenH(480) {
 	_rnd = new Common::RandomSource("private");
 
@@ -203,10 +203,15 @@ 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");
+		warning("Failed to parse bool from subtitles options");
+
+	if (!Common::parseBool(ConfMan.get("sfxSubtitles"), _sfxSubtitles))
+		warning("Failed to parse bool from sfxSubtitles options");
 
 	if (useSubtitles) {
 		g_system->showOverlay(false);
+	} else if (_sfxSubtitles) {
+		warning("SFX subtitles are enabled, but no subtitles will be shown");
 	}
 
 	_language = Common::parseLanguage(ConfMan.get("language"));
@@ -1500,17 +1505,19 @@ void PrivateEngine::loadSubtitles(const Common::Path &path) {
 		_subtitles->loadSRTFile(subPath);
 		g_system->showOverlay(false);
 		int16 h = g_system->getOverlayHeight();
-
+		float scale = h / 2160.f;
 		// If we are in the main menu, we need to adjust the position of the subtitles
-		if (_currentSetting == getMainDesktopSetting()) {
-			_subtitles->setBBox(Common::Rect(20, h - 100, g_system->getOverlayWidth() - 20, h - 20));
+		if (_mode == 0) {
+			_subtitles->setBBox(Common::Rect(20, h - 200 * scale, g_system->getOverlayWidth() - 20, h - 20));
+		} else if (_mode == -1) {
+			_subtitles->setBBox(Common::Rect(20, h - 220 * scale, g_system->getOverlayWidth() - 20, h - 20));
 		} else {
-			_subtitles->setBBox(Common::Rect(20, h - 160, g_system->getOverlayWidth() - 20, h - 20));
+			_subtitles->setBBox(Common::Rect(20, h - 160 * scale, g_system->getOverlayWidth() - 20, h - 20));
 		}
-
+		int fontSize = MAX(8, int(50 * scale));
 		_subtitles->setColor(0xff, 0xff, 0x80);
-		_subtitles->setFont("LiberationSans-Regular.ttf", 50);
-
+		_subtitles->setFont("LiberationSans-Regular.ttf", fontSize, "regular");
+		_subtitles->setFont("NotoSerif-Italic.ttf", fontSize, "italic");
 	} else {
 		delete _subtitles;
 		_subtitles = nullptr;
@@ -1721,7 +1728,7 @@ void PrivateEngine::drawScreen() {
 	}
 
 	if (_subtitles && _videoDecoder && !_videoDecoder->isPaused())
-		_subtitles->drawSubtitle(_videoDecoder->getTime(), false);
+		_subtitles->drawSubtitle(_videoDecoder->getTime(), false, _sfxSubtitles);
 	g_system->updateScreen();
 }
 
diff --git a/engines/private/private.h b/engines/private/private.h
index 599215b4875..7cf35e0fe98 100644
--- a/engines/private/private.h
+++ b/engines/private/private.h
@@ -221,6 +221,7 @@ public:
 
 	void loadSubtitles(const Common::Path &path);
 	Video::Subtitles *_subtitles;
+	bool _sfxSubtitles;
 
 	Graphics::Surface *decodeImage(const Common::String &file, byte **palette);
 	//byte *decodePalette(const Common::String &name);




More information about the Scummvm-git-logs mailing list