From bee912ff54422ded754a36bd47c9d70ae15c765b Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Tue, 4 Jan 2011 20:39:27 +0000 Subject: GRAPHICS: Add support for Windows FON/FNT fonts As required by Hugo and Mohawk. svn-id: r55120 --- graphics/fonts/winfont.cpp | 299 +++++++++++++++++++++++++++++++++++++++++++++ graphics/fonts/winfont.h | 96 +++++++++++++++ 2 files changed, 395 insertions(+) create mode 100644 graphics/fonts/winfont.cpp create mode 100644 graphics/fonts/winfont.h (limited to 'graphics/fonts') diff --git a/graphics/fonts/winfont.cpp b/graphics/fonts/winfont.cpp new file mode 100644 index 0000000000..c66e578040 --- /dev/null +++ b/graphics/fonts/winfont.cpp @@ -0,0 +1,299 @@ +/* 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. + * + * $URL$ + * $Id$ + */ + +#include "common/file.h" +#include "common/ne_exe.h" +#include "common/str.h" +#include "graphics/fonts/winfont.h" + +namespace Graphics { + +WinFont::WinFont() { + _glyphs = 0; + close(); +} + +WinFont::~WinFont() { + close(); +} + +void WinFont::close() { + _pixHeight = 0; + _ascent = 0; + _maxWidth = 0; + _firstChar = 0; + _lastChar = 0; + _defaultChar = 0; + _glyphCount = 0; + delete[] _glyphs; + _glyphs = 0; +} + +// Reads a null-terminated string +static Common::String readString(Common::SeekableReadStream &stream) { + Common::String string; + + char c = stream.readByte(); + while (c && stream.pos() < stream.size()) { + string += c; + c = stream.readByte(); + } + + return string; +} + +static WinFontDirEntry readDirEntry(Common::SeekableReadStream &stream) { + WinFontDirEntry entry; + + stream.skip(68); // Useless + entry.points = stream.readUint16LE(); + stream.skip(43); // Useless (for now, maybe not in the future) + readString(stream); + entry.faceName = readString(stream); + + return entry; +} + +bool WinFont::loadFromFON(const Common::String &fileName, const WinFontDirEntry &dirEntry) { + // TODO: PE libraries (If it's used anywhere by a ScummVM game) + + Common::NEResources exe; + + if (!exe.loadFromEXE(fileName)) + return false; + + // Let's pull out the font directory + Common::SeekableReadStream *fontDirectory = exe.getResource(Common::kNEFontDir, Common::String("FONTDIR")); + if (!fontDirectory) { + warning("No font directory in '%s'", fileName.c_str()); + return false; + } + + uint16 numFonts = fontDirectory->readUint16LE(); + + // Probably not possible, so this is really sanity check + if (numFonts == 0) { + warning("No fonts in '%s'", fileName.c_str()); + return false; + } + + // Scour the directory for our matching name + int fontId = -1; + for (uint16 i = 0; i < numFonts; i++) { + uint16 id = fontDirectory->readUint16LE(); + + if (dirEntry.faceName.empty()) { + // Use the first name when empty + fontId = id; + break; + } + + WinFontDirEntry entry = readDirEntry(*fontDirectory); + + if (dirEntry.faceName.equalsIgnoreCase(entry.faceName) && dirEntry.points == entry.points) { + // Match! + fontId = id; + break; + } + } + + delete fontDirectory; + + // Couldn't match the face name + if (fontId < 0) { + warning("Could not find face '%s' in '%s'", dirEntry.faceName.c_str(), fileName.c_str()); + return false; + } + + // Actually go get our font now... + Common::SeekableReadStream *fontStream = exe.getResource(Common::kNEFont, fontId); + if (!fontStream) { + warning("Could not find font %d in %s", fontId, fileName.c_str()); + return false; + } + + bool ok = loadFromFNT(*fontStream); + delete fontStream; + return ok; +} + +bool WinFont::loadFromFNT(const Common::String &fileName) { + Common::File file; + + return file.open(fileName) && loadFromFNT(file); +} + +char WinFont::indexToCharacter(uint16 index) const { + // Use a space for the sentinel value + if (index == _glyphCount - 1) + return ' '; + + return index + _firstChar; +} + +uint16 WinFont::characterToIndex(byte character) const { + // Go to the default character if we didn't find a mapping + if (character < _firstChar || character > _lastChar) + character = _defaultChar; + + return character - _firstChar; +} + +int WinFont::getCharWidth(byte chr) const { + return _glyphs[characterToIndex(chr)].charWidth; +} + +bool WinFont::loadFromFNT(Common::SeekableReadStream &stream) { + uint16 version = stream.readUint16LE(); + + // We'll accept Win2 and Win3 fonts + if (version != 0x200 && version != 0x300) { + if (version == 0x100) { + // TODO: Hugo1 has a font with this + // Even FreeType won't accept this font! + warning("Windows 1.0 font? Specs please"); + } else + warning("Bad FNT version %04x", version); + + return false; + } + + /* uint32 size = */ stream.readUint32LE(); + stream.skip(60); // Copyright info + uint16 fontType = stream.readUint16LE(); + /* uint16 points = */ stream.readUint16LE(); + /* uint16 vertRes = */ stream.readUint16LE(); + /* uint16 horizRes = */ stream.readUint16LE(); + _ascent = stream.readUint16LE(); + /* uint16 internalLeading = */ stream.readUint16LE(); + /* uint16 externalLeading = */ stream.readUint16LE(); + /* byte italic = */ stream.readByte(); + /* byte underline = */ stream.readByte(); + /* byte strikeOut = */ stream.readByte(); + /* uint16 weight = */ stream.readUint16LE(); + /* byte charSet = */ stream.readByte(); + uint16 pixWidth = stream.readUint16LE(); + _pixHeight = stream.readUint16LE(); + /* byte pitchAndFamily = */ stream.readByte(); + /* uint16 avgWidth = */ stream.readUint16LE(); + _maxWidth = stream.readUint16LE(); + _firstChar = stream.readByte(); + _lastChar = stream.readByte(); + _defaultChar = stream.readByte(); + /* byte breakChar = */ stream.readByte(); + /* uint16 widthBytes = */ stream.readUint16LE(); + /* uint32 device = */ stream.readUint32LE(); + /* uint32 face = */ stream.readUint32LE(); + /* uint32 bitsPointer = */ stream.readUint32LE(); + /* uint32 bitsOffset = */ stream.readUint32LE(); + /* byte reserved = */ stream.readByte(); + + if (version == 0x300) { + // For Windows 3.0, Microsoft added 6 new fields. All of which are + // guaranteed to be 0. Which leads to the question: Why add these at all? + + /* uint32 flags = */ stream.readUint32LE(); + /* uint16 aSpace = */ stream.readUint16LE(); + /* uint16 bSpace = */ stream.readUint16LE(); + /* uint16 cSpace = */ stream.readUint16LE(); + /* uint32 colorPointer = */ stream.readUint32LE(); + stream.skip(16); // Reserved + } + + // Begin loading in the glyphs + _glyphCount = (_lastChar - _firstChar) + 2; + _glyphs = new GlyphEntry[_glyphCount]; + + for (uint16 i = 0; i < _glyphCount; i++) { + _glyphs[i].charWidth = stream.readUint16LE(); + + // Use the default if present + if (pixWidth) + _glyphs[i].charWidth = pixWidth; + + _glyphs[i].offset = (version == 0x300) ? stream.readUint32LE() : stream.readUint16LE(); + } + + // TODO: Currently only raster fonts are supported! + if (fontType & 1) { + warning("Vector FNT files not supported yet"); + return false; + } + + // Read in the bitmaps for the raster images + for (uint16 i = 0; i < _glyphCount - 1; i++) { + stream.seek(_glyphs[i].offset); + + _glyphs[i].bitmap = new byte[_pixHeight * _glyphs[i].charWidth]; + + // Calculate the amount of columns + byte colCount = (_glyphs[i].charWidth + 7) / 8; + + for (uint16 j = 0; j < colCount; j++) { + for (uint16 k = 0; k < _pixHeight; k++) { + byte x = stream.readByte(); + uint offset = j * 8 + k * _glyphs[i].charWidth; + + for (byte l = 0; l < 8 && j * 8 + l < _glyphs[i].charWidth; l++) + _glyphs[i].bitmap[offset + l] = (x & (1 << (7 - l))) ? 1 : 0; + } + } + +#if 0 + // Debug print + debug("Character %02x '%c' at %08x", indexToCharacter(i), indexToCharacter(i), _glyphs[i].offset); + for (uint16 j = 0; j < _pixHeight; j++) { + for (uint16 k = 0; k < _glyphs[i].charWidth; k++) + debugN("%c", _glyphs[i].bitmap[k + j * _glyphs[i].charWidth] ? 'X' : ' '); + + debugN("\n"); + } +#endif + } + + return true; +} + +void WinFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const { + assert(dst); + assert(dst->bytesPerPixel == 1 || dst->bytesPerPixel == 2 || dst->bytesPerPixel == 4); + assert(_glyphs); + + GlyphEntry &glyph = _glyphs[characterToIndex(chr)]; + + for (uint16 i = 0; i < _pixHeight; i++) { + for (uint16 j = 0; j < glyph.charWidth; j++) { + if (glyph.bitmap[j + i * glyph.charWidth]) { + if (dst->bytesPerPixel == 1) + *((byte *)dst->getBasePtr(x + j, y + i)) = color; + else if (dst->bytesPerPixel == 2) + *((uint16 *)dst->getBasePtr(x + j, y + i)) = color; + else if (dst->bytesPerPixel == 4) + *((uint32 *)dst->getBasePtr(x + j, y + i)) = color; + } + } + } +} + +} // End of namespace Graphics diff --git a/graphics/fonts/winfont.h b/graphics/fonts/winfont.h new file mode 100644 index 0000000000..0600527643 --- /dev/null +++ b/graphics/fonts/winfont.h @@ -0,0 +1,96 @@ +/* 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. + * + * $URL$ + * $Id$ + */ + +#ifndef GRAPHICS_WINFONT_H +#define GRAPHICS_WINFONT_H + +#include "graphics/font.h" + +namespace Common { + class SeekableReadStream; + class String; +} + +namespace Graphics { + +struct WinFontDirEntry { + WinFontDirEntry() {} + WinFontDirEntry(const Common::String &name, uint16 p) : faceName(name), points(p) {} + + // This is really just a simple identifier to match a directory entry with + // If need-be, we can add other things to check such as italics and strikethrough, etc. + Common::String faceName; + uint16 points; +}; + +class WinFont : public Font { +public: + WinFont(); + ~WinFont(); + + /** + * Open a font with a name in an FON file. + * + * If dirEntry is not given, the first font in the FONTDIR will be loaded + */ + bool loadFromFON(const Common::String &fileName, const WinFontDirEntry &dirEntry = WinFontDirEntry()); + + /** Open a font from an FNT file */ + bool loadFromFNT(const Common::String &fileName); + + /** Close this font */ + void close(); + + // Font API + int getFontHeight() const { return _ascent; } + int getMaxCharWidth() const { return _maxWidth; } + int getCharWidth(byte chr) const; + void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; + +private: + bool loadFromFNT(Common::SeekableReadStream &stream); + char indexToCharacter(uint16 index) const; + uint16 characterToIndex(byte character) const; + + uint16 _pixHeight; + uint16 _ascent; + uint16 _maxWidth; + byte _firstChar; + byte _lastChar; + byte _defaultChar; + + uint16 _glyphCount; + struct GlyphEntry { + GlyphEntry() { bitmap = 0; } + ~GlyphEntry() { delete[] bitmap; } + + uint16 charWidth; + uint32 offset; + byte *bitmap; + } *_glyphs; +}; + +} // End of namespace Graphics + +#endif -- cgit v1.2.3