aboutsummaryrefslogtreecommitdiff
path: root/graphics/fonts/winfont.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/fonts/winfont.cpp')
-rw-r--r--graphics/fonts/winfont.cpp299
1 files changed, 299 insertions, 0 deletions
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