[Scummvm-git-logs] scummvm master -> afc55543cccecd6eebe3ff6cf7dc2c017dc76de5

AndywinXp noreply at scummvm.org
Wed Jan 28 11:57:55 UTC 2026


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:
afc55543cc SCUMM: HE: Add TTF font support


Commit: afc55543cccecd6eebe3ff6cf7dc2c017dc76de5
    https://github.com/scummvm/scummvm/commit/afc55543cccecd6eebe3ff6cf7dc2c017dc76de5
Author: AndywinXp (andywinxp at gmail.com)
Date: 2026-01-28T12:57:48+01:00

Commit Message:
SCUMM: HE: Add TTF font support

This is a port of the original Font module, which was using
Windows syscalls for TTF font rendering.

I don't know if it's pixel accurate, but since it was originally
OS driven, I'd rather not care too much about it and let our
library do its own thing. ;-)

Fixes #16474 and #16473
"SCUMM: PUTTSFUNSHOP: Loading any pre-filled project fails"

"SCUMM: PUTTSFUNSHOP: Text input is not working"

Changed paths:
  A engines/scumm/he/font_he.cpp
  A engines/scumm/he/font_he.h
    engines/scumm/he/intern_he.h
    engines/scumm/he/script_v100he.cpp
    engines/scumm/he/script_v72he.cpp
    engines/scumm/he/script_v90he.cpp
    engines/scumm/he/wiz_he.cpp
    engines/scumm/module.mk
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/he/font_he.cpp b/engines/scumm/he/font_he.cpp
new file mode 100644
index 00000000000..010d795d855
--- /dev/null
+++ b/engines/scumm/he/font_he.cpp
@@ -0,0 +1,394 @@
+/* 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 "scumm/he/font_he.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/he/wiz_he.h"
+
+#include "common/archive.h"
+#include "common/compression/unzip.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/fs.h"
+
+#include "graphics/surface.h"
+
+#ifdef USE_FREETYPE2
+#include "graphics/fonts/ttf.h"
+#endif
+
+namespace Scumm {
+
+HEFont::HEFont(ScummEngine_v99he *vm) : _vm(vm), _fontsEnumerated(false) {
+	_fontContextList.size();
+}
+
+HEFont::~HEFont() {
+	for (Common::List<HEFontContextElement *>::iterator it = _fontContextList.begin();
+		it != _fontContextList.end(); ++it) {
+		delete *it;
+	}
+
+	_fontContextList.clear();
+	_fontEntries.clear();
+}
+
+HEFontContextElement *HEFont::findFontContext(int imageNum) {
+	for (Common::List<HEFontContextElement *>::iterator it = _fontContextList.begin();
+		it != _fontContextList.end(); ++it) {
+
+		if ((*it)->imageNumber == imageNum) {
+			return *it;
+		}
+	}
+
+	return nullptr;
+}
+
+bool HEFont::startFont(int imageNum) {
+	// Check if a context already exists for this image...
+	HEFontContextElement *existing = findFontContext(imageNum);
+	if (existing) {
+		debug(1, "HEFont::startFont(): Warning - font system exists for image %d. Destroying existing.", imageNum);
+		endFont(imageNum);
+	}
+
+	// Verify the image is suitable...
+	WizSimpleBitmap bitmap;
+	if (!_vm->_wiz->dwSetSimpleBitmapStructFromImage(imageNum, 0, &bitmap)) {
+		debug(1, "HEFont::startFont(): Image %d is not suitable for rendering (must not be compressed)", imageNum);
+		return false;
+	}
+
+	// Create new font context...
+	HEFontContextElement *newContext = new HEFontContextElement();
+	newContext->imageNumber = imageNum;
+
+	_fontContextList.push_back(newContext);
+
+	return true;
+}
+
+bool HEFont::endFont(int imageNum) {
+	for (Common::List<HEFontContextElement *>::iterator it = _fontContextList.begin();
+		it != _fontContextList.end(); ++it) {
+
+		if ((*it)->imageNumber == imageNum) {
+			delete *it;
+			_fontContextList.erase(it);
+			return true;
+		}
+	}
+
+	debug(1, "HEFont::endFont(): Font system not started for image %d", imageNum);
+	return false;
+}
+
+bool HEFont::createFont(int imageNum, const char *fontName, int fgColor, int bgColor, int style, int size) {
+	HEFontContextElement *ctx = findFontContext(imageNum);
+	if (!ctx) {
+		debug(1, "HEFont::createFont(): Font system not created for image %d", imageNum);
+		return false;
+	}
+
+	// Destroy any previously created font...
+	if (ctx->font) {
+		delete ctx->font;
+		ctx->font = nullptr;
+	}
+
+#ifdef USE_FREETYPE2
+	// Find the filename for this font name...
+	Common::String fontFileName;
+	for (uint i = 0; i < _fontEntries.size(); i++) {
+		if (_fontEntries[i].fontName.equalsIgnoreCase(fontName)) {
+			fontFileName = _fontEntries[i].fileName + ".ttf";
+			break;
+		}
+	}
+
+	// Fallback: try using fontName directly as filename...
+	if (fontFileName.empty()) {
+		fontFileName = Common::String(fontName) + ".ttf";
+	}
+
+	// Try loading from fonts.dat...
+	ctx->font = Graphics::loadTTFFontFromArchive(fontFileName,
+												 size,
+												 Graphics::kTTFSizeModeCell,
+												 0, 0,
+												 Graphics::kTTFRenderModeLight,
+												 nullptr);
+
+	// If not in archive, we're probably searching for a game-specific font, so try via SearchMan...
+	if (!ctx->font) {
+		Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(Common::Path(fontFileName));
+
+		if (stream) {
+			ctx->font = Graphics::loadTTFFont(stream,
+											  DisposeAfterUse::YES,
+											  size,
+											  Graphics::kTTFSizeModeCell,
+											  0, 0,
+											  Graphics::kTTFRenderModeLight,
+											  nullptr);
+		}
+	}
+#endif
+
+	if (!ctx->font) {
+		debug(1, "HEFont::createFont(): Could not create font '%s'", fontName);
+		return false;
+	}
+
+	// Store colors
+	ctx->fgColor = fgColor;
+	ctx->bgColor = bgColor;
+	ctx->transparentBg = (bgColor == _vm->VAR(_vm->VAR_WIZ_TRANSPARENT_COLOR));
+
+	// Set alignment
+	ctx->align = Graphics::kTextAlignCenter; // Default
+	if (style & kHEFontStyleLeft) {
+		ctx->align = Graphics::kTextAlignLeft;
+	} else if (style & kHEFontStyleRight) {
+		ctx->align = Graphics::kTextAlignRight;
+	}
+
+	return true;
+}
+
+bool HEFont::renderString(int imageNum, int imageState, int xPos, int yPos, const char *string) {
+	HEFontContextElement *ctx = findFontContext(imageNum);
+	if (!ctx) {
+		debug(1, "HEFont::renderString(): Font system not created for image %d", imageNum);
+		return false;
+	}
+
+	if (!ctx->font) {
+		debug(1, "HEFont::renderString(): No font selected for image %d", imageNum);
+		return false;
+	}
+
+	// Get the bitmap from the current image...
+	WizSimpleBitmap bitmap;
+	if (!_vm->_wiz->dwSetSimpleBitmapStructFromImage(imageNum, imageState, &bitmap)) {
+		debug(1, "HEFont::renderString(): Could not get bitmap from image %d", imageNum);
+		return false;
+	}
+
+	// Wrap it in a Surface, which we can assume is a 8-bit paletted one for our use case...
+	Graphics::Surface surface;
+	surface.init(bitmap.bitmapWidth, bitmap.bitmapHeight,
+				 bitmap.bitmapWidth,
+				 bitmap.bufferPtr(),
+				 Graphics::PixelFormat::createFormatCLUT8());
+
+	int stringWidth = ctx->font->getStringWidth(string);
+	int stringHeight = ctx->font->getFontHeight();
+
+	// If background is not transparent, fill the background rectangle first...
+	if (!ctx->transparentBg) {
+		Common::Rect bgRect(xPos, yPos, xPos + stringWidth, yPos + stringHeight);
+		surface.fillRect(bgRect, ctx->bgColor);
+	}
+
+	// Draw the string!
+	ctx->font->drawString(&surface, string, xPos, yPos, stringWidth, ctx->fgColor, ctx->align);
+
+	return true;
+}
+
+int HEFont::getStringWidth(int imageNum, const char *string) {
+	HEFontContextElement *ctx = findFontContext(imageNum);
+	if (!ctx) {
+		debug(1, "HEFont::getStringWidth(): Font system not created for image %d", imageNum);
+		return 0;
+	}
+
+	if (!ctx->font) {
+		debug(1, "HEFont::getStringWidth(): No font selected for image %d", imageNum);
+		return 0;
+	}
+
+	return ctx->font->getStringWidth(string);
+}
+
+int HEFont::getStringHeight(int imageNum, const char *string) {
+	HEFontContextElement *ctx = findFontContext(imageNum);
+	if (!ctx) {
+		debug(1, "HEFont::getStringHeight(): Font system not created for image %d", imageNum);
+		return 0;
+	}
+
+	if (!ctx->font) {
+		debug(1, "HEFont::getStringHeight(): No font selected for image %d", imageNum);
+		return 0;
+	}
+
+	return ctx->font->getFontHeight();
+}
+
+void HEFont::enumerateFonts() {
+	if (_fontsEnumerated) {
+		return;
+	}
+
+	_fontEntries.clear();
+
+#ifdef USE_FREETYPE2
+	Common::Array<Common::String> candidateFonts;
+
+	// Open fonts.dat...
+	Common::SeekableReadStream *archiveStream = nullptr;
+
+	if (ConfMan.hasKey("extrapath")) {
+		Common::FSDirectory extrapath(ConfMan.getPath("extrapath"));
+		archiveStream = extrapath.createReadStreamForMember("fonts.dat");
+	}
+
+	if (!archiveStream) {
+		archiveStream = SearchMan.createReadStreamForMember("fonts.dat");
+	}
+
+	if (archiveStream) {
+		Common::Archive *archive = Common::makeZipArchive(archiveStream);
+		if (archive) {
+			Common::ArchiveMemberList members;
+			archive->listMembers(members);
+
+			for (Common::ArchiveMemberList::const_iterator it = members.begin();
+				 it != members.end(); ++it) {
+				Common::String name = (*it)->getName();
+				if (name.hasSuffixIgnoreCase(".ttf")) {
+					Common::String fontName = name.substr(0, name.size() - 4);
+					candidateFonts.push_back(fontName);
+				}
+			}
+
+			delete archive;
+		}
+	}
+
+	// If not in archive, we're probably searching for a game-specific font, so try via SearchMan...
+	Common::ArchiveMemberList fontFiles;
+	SearchMan.listMatchingMembers(fontFiles, "*.ttf");
+
+	// Use filenames for fonts.dat fonts...
+	for (uint i = 0; i < candidateFonts.size(); i++) {
+		Common::String fontFileName = candidateFonts[i] + ".ttf";
+
+		Graphics::Font *testFont = Graphics::loadTTFFontFromArchive(
+			fontFileName,
+			12,
+			Graphics::kTTFSizeModeCharacter
+		);
+
+		if (testFont) {
+			HEFontEntry entry;
+			entry.fileName = candidateFonts[i];
+			entry.fontName = candidateFonts[i];
+			_fontEntries.push_back(entry);
+			delete testFont;
+		}
+	}
+
+	// Use actual font name from metadata for game fonts...
+	for (Common::ArchiveMemberList::const_iterator it = fontFiles.begin();
+		 it != fontFiles.end(); ++it) {
+		Common::String name = (*it)->getName();
+		Common::String fileBaseName = name.substr(0, name.size() - 4);
+
+		Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(Common::Path(name));
+		if (stream) {
+			Graphics::Font *testFont = Graphics::loadTTFFont(stream,
+															 DisposeAfterUse::YES,
+															 12,
+															 Graphics::kTTFSizeModeCharacter
+			);
+
+			if (testFont) {
+				HEFontEntry entry;
+				entry.fileName = fileBaseName;
+				entry.fontName = testFont->getFontName();
+
+				// If there's no metadata name, just fallback to the filename...
+				if (entry.fontName.empty()) {
+					entry.fontName = fileBaseName;
+				}
+
+				_fontEntries.push_back(entry);
+				delete testFont;
+			}
+		}
+	}
+#endif
+
+	// Sort alphabetically...
+	Common::sort(_fontEntries.begin(), _fontEntries.end(),
+				 [](const HEFontEntry &a, const HEFontEntry &b) {
+					 return a.fontName.compareToIgnoreCase(b.fontName) < 0;
+				 }
+	);
+
+	_fontsEnumerated = true;
+}
+
+int HEFont::enumInit() {
+	enumerateFonts();
+	return _fontEntries.size();
+}
+
+void HEFont::enumDestroy() {
+	_fontEntries.clear();
+	_fontsEnumerated = false;
+}
+
+const char *HEFont::enumGet(int index) {
+	if (!_fontsEnumerated) {
+		debug(1, "HEFont::enumGet(): Font enumeration not initialized");
+		return nullptr;
+	}
+
+	if (index < 0 || index >= (int)_fontEntries.size()) {
+		debug(1, "HEFont::enumGet(): Index %d out of range (0-%d)", index, (int)_fontEntries.size() - 1);
+		return nullptr;
+	}
+
+	return _fontEntries[index].fontName.c_str();
+}
+
+int HEFont::enumFind(const char *fontName) {
+	if (!_fontsEnumerated) {
+		debug(1, "HEFont::enumFind(): Font enumeration not initialized");
+		return -1;
+	}
+
+	for (uint i = 0; i < _fontEntries.size(); i++) {
+		if (_fontEntries[i].fontName.equalsIgnoreCase(fontName)) {
+			return i;
+		}
+	}
+
+	debug(1, "HEFont::enumFind(): Could not find font '%s'", fontName);
+	return -1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/font_he.h b/engines/scumm/he/font_he.h
new file mode 100644
index 00000000000..c8c4021e215
--- /dev/null
+++ b/engines/scumm/he/font_he.h
@@ -0,0 +1,102 @@
+/* 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/>.
+ *
+ */
+
+#ifndef SCUMM_HE_FONT_HE_H
+#define SCUMM_HE_FONT_HE_H
+
+#include "common/array.h"
+#include "common/list.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+
+#include "graphics/font.h"
+
+namespace Scumm {
+
+class ScummEngine_v99he;
+
+// Font style flags, taken from the original
+enum HEFontStyle {
+	kHEFontStyleItalic = 0x01,
+	kHEFontStyleUnderline = 0x02,
+	kHEFontStyleBold = 0x04,
+	kHEFontStyleCenter = 0x08,
+	kHEFontStyleLeft = 0x10,
+	kHEFontStyleRight = 0x20
+};
+
+struct HEFontContextElement {
+	HEFontContextElement() : imageNumber(0),
+							 font(nullptr),
+							 fgColor(0),
+							 bgColor(0),
+							 transparentBg(false),
+							 align(Graphics::kTextAlignCenter) {}
+
+	~HEFontContextElement() {
+		delete font;
+		font = nullptr;
+	}
+
+	int imageNumber;
+	Graphics::Font *font;
+	int fgColor;
+	int bgColor;
+	bool transparentBg;
+	Graphics::TextAlign align;
+};
+
+struct HEFontEntry {
+	Common::String fileName; // Filename without extension (e.g., "LiberationSans-Bold")
+	Common::String fontName; // Actual font name from metadata (e.g., "Liberation Sans")
+};
+
+class HEFont {
+public:
+	HEFont(ScummEngine_v99he *vm);
+	~HEFont();
+
+	bool startFont(int imageNum);
+	bool endFont(int imageNum);
+	bool createFont(int imageNum, const char *fontName, int fgColor, int bgColor, int style, int size);
+	bool renderString(int imageNum, int imageState, int xPos, int yPos, const char *string);
+	int getStringWidth(int imageNum, const char *string);
+	int getStringHeight(int imageNum, const char *string);
+	int enumInit();
+	void enumDestroy();
+	const char *enumGet(int index);
+	int enumFind(const char *fontName);
+
+private:
+	ScummEngine_v99he *_vm;
+
+	Common::List<HEFontContextElement *> _fontContextList;
+
+	Common::Array<HEFontEntry> _fontEntries;
+	bool _fontsEnumerated;
+
+	HEFontContextElement *findFontContext(int imageNum);
+	void enumerateFonts();
+};
+
+} // End of namespace Scumm
+
+#endif // SCUMM_HE_FONT_HE_H
diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index 94f94b31ae7..3a0cff1c383 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -286,6 +286,7 @@ class Lobby;
 class ScummEngine_v71he : public ScummEngine_v70he {
 	friend class Wiz;
 	friend class Gdi;
+	friend class HEFont;
 
 protected:
 	enum SubOpType {
@@ -826,8 +827,13 @@ protected:
 };
 
 class ScummEngine_v99he : public ScummEngine_v95he {
+friend class ScummEngine_v72he;
+friend class ScummEngine_v90he;
+friend class Wiz;
+
 public:
-	ScummEngine_v99he(OSystem *syst, const DetectorResult &dr) : ScummEngine_v95he(syst, dr) {}
+	ScummEngine_v99he(OSystem *syst, const DetectorResult &dr);
+	~ScummEngine_v99he() override;
 
 	void resetScumm() override;
 
@@ -843,6 +849,8 @@ protected:
 	void setPaletteFromPtr(const byte *ptr, int numcolor = -1) override;
 	void setPalColor(int index, int r, int g, int b) override;
 	void updatePalette() override;
+
+	HEFont *_heFont = nullptr;
 };
 
 class ScummEngine_v100he : public ScummEngine_v99he {
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index b054f313f7d..40adf438b7e 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -27,6 +27,7 @@
 #include "scumm/charset.h"
 #include "scumm/dialogs.h"
 #include "scumm/he/animation_he.h"
+#include "scumm/he/font_he.h"
 #include "scumm/he/intern_he.h"
 #include "scumm/object.h"
 #include "scumm/resource.h"
@@ -2628,6 +2629,7 @@ void ScummEngine_v100he::o100_getSpriteGroupInfo() {
 void ScummEngine_v100he::o100_getWizData() {
 	byte filename[4096];
 	int resId, state, type;
+	int fontImageNum, fontProperty;
 	Common::Rect clipRect;
 	int32 w, h;
 	int32 x, y;
@@ -2687,11 +2689,28 @@ void ScummEngine_v100he::o100_getWizData() {
 		push(y);
 		break;
 	case SO_FONT_START:
-		pop();
+		// I don't think any HE100 games use this, since all FunShop games are HE99, but still...
+		fontProperty = pop();
 		copyScriptString(filename, sizeof(filename));
-		pop();
-		push(0);
-		debug(0, "o100_getWizData() case 111 unhandled");
+		fontImageNum = pop();
+
+		if (fontImageNum) {
+			switch (fontProperty) {
+			case 2: // PFONT_EXTENT_X
+				push(_heFont->getStringWidth(fontImageNum, Common::String((char *)filename).c_str()));
+				break;
+
+			case 3: // PFONT_EXTENT_Y
+				push(_heFont->getStringHeight(fontImageNum, Common::String((char *)filename).c_str()));
+				break;
+			default:
+				// No default case in the original...
+				break;
+			}
+		} else {
+			push(0);
+		}
+
 		break;
 	case SO_HISTOGRAM:
 		clipRect.bottom = pop();
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index b2ea13ef1e8..37b388178db 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -29,6 +29,7 @@
 #include "scumm/charset.h"
 #include "scumm/dialogs.h"
 #include "scumm/file.h"
+#include "scumm/he/font_he.h"
 #include "scumm/he/intern_he.h"
 #include "scumm/he/localizer.h"
 #include "scumm/object.h"
@@ -2167,6 +2168,44 @@ void ScummEngine_v72he::o72_readINI() {
 				push(4);
 			else
 				push(2);
+		} else if (_game.heversion >= 99 && !strcmp((char *)option, "NoFontsInstalled")) {
+			// The three FunShop came with these six exact optional TrueType fonts:
+			// Augie, Dot2Dot, Frosty, Goo Puff, Smilage and Zodiastic.
+			//
+			// These fonts are an optional choice from the installer, and if not installed,
+			// this NoFontsInstalled flag will be set to 1.
+			//
+			// In our case, some users might not have a suitable Windows OS to run the
+			// InstallShield installers, and they might just take the plain data files
+			// from the CD. So let's check for exactly those fonts in our HEFont class
+			// and set this flag accordingly.
+			//
+			// The perceived effect of this is that without these fonts, premade printing
+			// models won't be available, instead of crashing the engine. ;-)
+#ifdef USE_FREETYPE2
+			static const char *funShopFonts[] = {
+				"Augie",
+				"Dot2Dot",
+				"Frosty",
+				"Goo Puff",
+				"Smilage",
+				"Zodiastic"
+			};
+
+			((ScummEngine_v99he *)this)->_heFont->enumInit();
+
+			bool allFontsFound = true;
+			for (int i = 0; i < ARRAYSIZE(funShopFonts); i++) {
+				if (((ScummEngine_v99he *)this)->_heFont->enumFind(funShopFonts[i]) == -1) {
+					allFontsFound = false;
+					break;
+				}
+			}
+
+			push(allFontsFound ? 0 : 1);
+#else
+			push(0);
+#endif
 		} else {
 			push(ConfMan.getInt((char *)option));
 		}
diff --git a/engines/scumm/he/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp
index 5b0b96bd722..a2297730f15 100644
--- a/engines/scumm/he/script_v90he.cpp
+++ b/engines/scumm/he/script_v90he.cpp
@@ -24,6 +24,7 @@
 #include "scumm/actor.h"
 #include "scumm/charset.h"
 #include "scumm/he/animation_he.h"
+#include "scumm/he/font_he.h"
 #include "scumm/he/intern_he.h"
 #include "scumm/he/logic_he.h"
 #include "scumm/object.h"
@@ -1332,8 +1333,6 @@ void ScummEngine_v90he::o90_getWizData() {
 		push(_wiz->dwGetImageGeneralProperty(resId, state, type));
 		break;
 	case SO_FONT_START: // 141
-		// TODO: Implement...
-
 		fontProperty = pop();
 		copyScriptString(filename, sizeof(filename));
 		fontImageNum = pop();
@@ -1341,13 +1340,11 @@ void ScummEngine_v90he::o90_getWizData() {
 		if (fontImageNum) {
 			switch (fontProperty) {
 			case 2: // PFONT_EXTENT_X
-				//push(PFONT_GetStringWidth(iImage, szResultString));
-				push(0);
+				push(((ScummEngine_v99he *)this)->_heFont->getStringWidth(fontImageNum, Common::String((char *)filename).c_str()));
 				break;
 
 			case 3: // PFONT_EXTENT_Y
-				//push(PFONT_GetStringHeight(iImage, szResultString));
-				push(0);
+				push(((ScummEngine_v99he *)this)->_heFont->getStringHeight(fontImageNum, Common::String((char *)filename).c_str()));
 				break;
 			default:
 				// No default case in the original...
@@ -1357,7 +1354,6 @@ void ScummEngine_v90he::o90_getWizData() {
 			push(0);
 		}
 
-		debug(0, "o90_getWizData() case SO_FONT_START unhandled");
 		break;
 	default:
 		error("o90_getWizData: Unknown case %d", subOp);
@@ -2347,28 +2343,38 @@ void ScummEngine_v90he::o90_paletteOps() {
 }
 
 void ScummEngine_v90he::o90_fontEnum() {
-	byte string[80];
+	byte resultString[80];
 
 	byte subOp = fetchScriptByte();
 
 	switch (subOp) {
 	case ScummEngine_v100he::SO_INIT: // HE100
 	case SO_INIT:
-		push(1);
+		push(((ScummEngine_v99he *)this)->_heFont->enumInit());
 		break;
 	case ScummEngine_v100he::SO_PROPERTY:	// HE100
 	case SO_PROPERTY:
 		switch (pop()) {
 		case 1: // FONT_ENUM_GET
-			pop();
-			writeVar(0, 0);
-			defineArray(0, kStringArray, 0, 0, 0, 0);
-			writeArray(0, 0, 0, 0);
-			push(readVar(0));
+		{
+			_scummVars[0] = 0;
+			const char *fontName = ((ScummEngine_v99he *)this)->_heFont->enumGet(pop());
+			if (!fontName) {
+				fontName = "";
+			}
+
+			int len = strlen(fontName);
+			byte *ptr = defineArray(0, kStringArray, 0, 0, 0, len);
+			if (ptr) {
+				memcpy(ptr, fontName, len);
+			}
+
+			push(_scummVars[0]);
 			break;
+		}
 		case 2: // FONT_ENUM_FIND
-			copyScriptString(string, sizeof(string));
-			push(-1);
+			copyScriptString(resultString, sizeof(resultString));
+			push(((ScummEngine_v99he *)this)->_heFont->enumFind((char *)resultString));
 			break;
 		}
 
@@ -2376,8 +2382,6 @@ void ScummEngine_v90he::o90_fontEnum() {
 	default:
 		error("o90_fontEnum: Unknown case %d", subOp);
 	}
-
-	debug(1, "o90_fontEnum stub (%d)", subOp);
 }
 
 void ScummEngine_v90he::o90_getActorAnimProgress() {
diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp
index c416c0884b9..b03bf45d94a 100644
--- a/engines/scumm/he/wiz_he.cpp
+++ b/engines/scumm/he/wiz_he.cpp
@@ -26,6 +26,7 @@
 #include "common/system.h"
 #include "graphics/cursorman.h"
 #include "graphics/primitives.h"
+#include "scumm/he/font_he.h"
 #include "scumm/he/logic_he.h"
 #include "scumm/he/intern_he.h"
 #include "scumm/resource.h"
@@ -2399,23 +2400,40 @@ void Wiz::processWizImageRenderEllipseCmd(const WizImageCommand *params) {
 }
 
 void Wiz::processWizImageFontStartCmd(const WizImageCommand *params) {
-	// Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
-	// TODO: Start Font
+	// Used for TTF text in FreddisFunShop/PuttsFunShop/SamsFunShop
+	if (!(((ScummEngine_v99he *)_vm)->_heFont->startFont(params->image))) {
+		warning("Wiz::processWizImageFontStartCmd(): Couldn't start font");
+	}
 }
 
 void Wiz::processWizImageFontEndCmd(const WizImageCommand *params) {
-	// Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
-	// TODO: End Font
+	// Used for TTF text in FreddisFunShop/PuttsFunShop/SamsFunShop
+	if (!(((ScummEngine_v99he *)_vm)->_heFont->endFont(params->image))) {
+		warning("Wiz::processWizImageFontEndCmd(): Couldn't end font");
+	}
 }
 
 void Wiz::processWizImageFontCreateCmd(const WizImageCommand *params) {
-	// Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
-	// TODO: Create Font
+	// Used for TTF text in FreddisFunShop/PuttsFunShop/SamsFunShop
+	if (!(((ScummEngine_v99he *)_vm)->_heFont->createFont(params->image, 
+														  (char *)params->fontProperties.fontName,
+														  params->fontProperties.fgColor,
+														  params->fontProperties.bgColor,
+														  params->fontProperties.style,
+														  params->fontProperties.size))) {
+		warning("Wiz::processWizImageFontCreateCmd(): Couldn't create font");
+	}
 }
 
 void Wiz::processWizImageFontRenderCmd(const WizImageCommand *params) {
-	// TODO: Render Font String
-	error("Wiz::processWizImageFontRenderCmd(): Render Font String");
+	// Used for TTF text in FreddisFunShop/PuttsFunShop/SamsFunShop
+	if (!(((ScummEngine_v99he *)_vm)->_heFont->renderString(params->image,
+															params->state,
+															params->fontProperties.xPos,
+															params->fontProperties.yPos,
+															(char *)params->fontProperties.string))) {
+		warning("Wiz::processWizImageFontRenderCmd(): Couldn't render font");
+	}
 }
 
 void Wiz::processWizImageRenderFloodFillCmd(const WizImageCommand *params) {
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index a2cc2b66021..de3c730f912 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -169,6 +169,7 @@ MODULE_OBJS += \
 	he/basketball/shooting.o \
 	he/basketball/trajectory.o \
 	he/cup_player_he.o \
+	he/font_he.o \
 	he/gfx_comp/aux_comp.o \
 	he/gfx_comp/mrle_comp.o \
 	he/gfx_comp/trle_comp.o \
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 1da7f083858..d8b20048a11 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -53,6 +53,7 @@
 #include "scumm/players/player_towns.h"
 #include "scumm/insane/insane.h"
 #include "scumm/he/animation_he.h"
+#include "scumm/he/font_he.h"
 #include "scumm/he/intern_he.h"
 #include "scumm/he/logic_he.h"
 #include "scumm/he/sound_he.h"
@@ -843,6 +844,15 @@ ScummEngine_v90he::~ScummEngine_v90he() {
 	}
 }
 
+ScummEngine_v99he::ScummEngine_v99he(OSystem *syst, const DetectorResult &dr) : ScummEngine_v95he(syst, dr) {
+	_heFont = new HEFont(this);
+}
+
+ScummEngine_v99he::~ScummEngine_v99he() {
+	delete _heFont;
+	_heFont = nullptr;
+}
+
 ScummEngine_v100he::ScummEngine_v100he(OSystem *syst, const DetectorResult &dr) : ScummEngine_v99he(syst, dr) {
 	/* Moonbase stuff */
 	if (_game.id == GID_MOONBASE)
@@ -1553,6 +1563,16 @@ Common::Error ScummEngine::init() {
 		_internalGUIControls[i].doubleLinesFlag = false;
 	}
 
+#ifndef USE_FREETYPE2
+	if (_game.id == GID_FUNSHOP) {
+		GUI::MessageDialog dialog(_(
+			"It appears your ScummVM version was not built with TrueType Fonts support.\n\n"
+			"Since the One-Stop Fun Shop series makes extensive use of TTF fonts,\n"
+			"some of the graphics on screen will be missing."));
+		dialog.runModal();
+	}
+#endif
+
 	_setupIsComplete = true;
 
 	syncSoundSettings();




More information about the Scummvm-git-logs mailing list