mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-23 20:51:14 +00:00
GRAPHICS: Add support for Windows FON/FNT fonts
As required by Hugo and Mohawk. svn-id: r55120
This commit is contained in:
parent
34ae92033c
commit
bee912ff54
299
graphics/fonts/winfont.cpp
Normal file
299
graphics/fonts/winfont.cpp
Normal file
@ -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
|
96
graphics/fonts/winfont.h
Normal file
96
graphics/fonts/winfont.h
Normal file
@ -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
|
@ -10,6 +10,7 @@ MODULE_OBJS := \
|
||||
fonts/newfont_big.o \
|
||||
fonts/newfont.o \
|
||||
fonts/scummfont.o \
|
||||
fonts/winfont.o \
|
||||
iff.o \
|
||||
imagedec.o \
|
||||
jpeg.o \
|
||||
|
Loading…
x
Reference in New Issue
Block a user