/* 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 . * */ #include "graphics/font.h" #include "graphics/surface.h" #include "common/stream.h" #include "dgds/decompress.h" #include "dgds/font.h" #include "dgds/includes.h" #include "dgds/resource.h" namespace Dgds { DgdsFont *DgdsFont::load(const Common::String &filename, ResourceManager *resourceManager, Decompressor *decompressor) { Common::SeekableReadStream *fontFile = resourceManager->getResource(filename); if (!fontFile) { warning("Font file %s not found", filename.c_str()); return nullptr; } DgdsChunkReader chunk(fontFile); DgdsFont *font = nullptr; while (chunk.readNextHeader(EX_FNT, filename)) { if (chunk.isContainer()) { continue; } chunk.readContent(decompressor); Common::SeekableReadStream *stream = chunk.getContent(); if (chunk.isSection(ID_FNT)) { byte magic = stream->readByte(); stream->seek(-1, SEEK_CUR); debug(10, "Load font %s magic: %u", filename.c_str(), magic); if (magic != 0xFF) font = FFont::load(*stream); else font = PFont::load(*stream, decompressor); } } delete fontFile; return font; } DgdsFont::DgdsFont(byte w, byte h, byte start, byte count, const byte *glyphs) : _w(w), _h(h), _start(start), _count(count), _glyphs(glyphs) { } DgdsFont::~DgdsFont() { } bool DgdsFont::hasChar(byte chr) const { return (chr >= _start && chr <= (_start + _count)); } static inline bool isSet(const byte *data, uint bit) { return data[bit / 8] & (1 << (7 - (bit % 8))); } void DgdsFont::drawDgdsChar(Graphics::Surface* dst, int pos, int x, int y, int w, uint32 color) const { const Common::Rect destRect(Common::Point(x, y), w, _h); Common::Rect clippedDestRect(dst->w, dst->h); clippedDestRect.clip(destRect); const Common::Point croppedBy(clippedDestRect.left - destRect.left, clippedDestRect.top - destRect.top); const int rows = clippedDestRect.height(); const int columns = clippedDestRect.width(); int bitOffset = croppedBy.x; int bytesPerRow = (w + 7) / 8; const byte *src = _glyphs + pos + (croppedBy.y * bytesPerRow); byte *ptr = (byte *)dst->getBasePtr(clippedDestRect.left, clippedDestRect.top); for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { if (isSet(src, bitOffset + j)) ptr[j] = color; } ptr += dst->pitch; src += bytesPerRow; } } FFont::FFont(byte w, byte h, byte start, byte count, byte *data) : DgdsFont(w, h, start, count, data), _rawData(data) { } FFont::~FFont() { delete [] _rawData; } int FFont::charOffset(byte chr) const { return (chr - _start) * _h; } void FFont::drawChar(Graphics::Surface* dst, uint32 chr, int x, int y, uint32 color) const { if (!hasChar(chr)) return; int pos = charOffset(chr); drawDgdsChar(dst, pos, x, y, _w, color); } FFont *FFont::load(Common::SeekableReadStream &input) { byte w = input.readByte(); byte h = input.readByte(); byte start = input.readByte(); byte count = input.readByte(); int size = h * count; assert((4 + size) == input.size()); debug(1, "FFont w: %u, h: %u, start: 0x%x, count: %u", w, h, start, count); byte *data = new byte[size]; input.read(data, size); return new FFont(w, h, start, count, data); } PFont::PFont(byte w, byte h, byte start, byte count, byte *data) : DgdsFont(w, h, start, count, data + 3 * count), _offsets(reinterpret_cast(data)), _widths(data + 2 * count), _rawData(data) { } PFont::~PFont() { delete [] _rawData; } int PFont::charOffset(byte chr) const { return READ_LE_UINT16(&_offsets[chr - _start]); } void PFont::drawChar(Graphics::Surface* dst, uint32 chr, int x, int y, uint32 color) const { if (!hasChar(chr)) return; int pos = charOffset(chr); int w = getCharWidth(chr); drawDgdsChar(dst, pos, x, y, w, color); } int PFont::getCharWidth(uint32 chr) const { if (!hasChar(chr)) return 0; return _widths[(byte)chr - _start]; } PFont *PFont::load(Common::SeekableReadStream &input, Decompressor *decompressor) { byte magic = input.readByte(); byte w = input.readByte(); byte h = input.readByte(); byte unknown = input.readByte(); byte start = input.readByte(); byte count = input.readByte(); int size = input.readUint16LE(); debug(1, "PFont magic: 0x%x, w: %u, h: %u, unk: %u, start: 0x%x, count: %u, size: %u", magic, w, h, unknown, start, count, size); assert(magic == 0xFF); size = input.size() - input.pos(); uint32 uncompressedSize; byte *data = decompressor->decompress(&input, size, uncompressedSize); return new PFont(w, h, start, count, data); } FontManager::~FontManager() { for (auto &entry : _fonts) if (entry._key != kDefaultFont) delete entry._value; } const DgdsFont *FontManager::getFont(FontType type) const { return _fonts.getVal(type); } void FontManager::tryLoadFont(const char *fname, ResourceManager *resMgr, Decompressor *decomp) { FontType ftype = fontTypeByName(fname); DgdsFont *font = DgdsFont::load(fname, resMgr, decomp); if (font) _fonts.setVal(ftype, font); else warning("Failed to load font %s", fname); } FontManager::FontType FontManager::fontTypeByName(const Common::String &filename) const { if (filename == "8X8.FNT") return k8x8Font; if (filename == "6X6.FNT") return k6x6Font; if (filename == "4x5.FNT") return k4x5Font; if (filename == "DRAGON.FNT") return kGameFont; if (filename == "7X8.FNT") return k7x8Font; if (filename == "P6X6.FNT") return kGameDlgFont; if (filename == "HOC.FNT") return kGameFont; if (filename == "CHINA.FNT") return kGameDlgFont; if (filename == "CHINESE.FNT") return kChinaFont; if (filename == "WILLY.FNT") return kGameDlgFont; if (filename == "COMIX_16.FNT") return kVCRFont; if (filename == "WVCR.FNT") return kGameFont; if (filename == "EXIT.FNT") return kVCRFont; if (filename == "SSM1_12.FNT") return kGameFont; if (filename == "SSM1_15.FNT") return kGameDlgFont; if (filename == "SSM1_30.FNT") return k8x8Font; if (filename == "RMN7_19.FNT") return kGameDlgFont; if (filename == "RMN8_11.FNT") return kGameFont; return FontManager::kDefaultFont; } void FontManager::loadFonts(DgdsGameId gameId, ResourceManager *resMgr, Decompressor *decomp) { if (gameId == GID_CASTAWAY) return; // no fonts if (gameId == GID_SQ5DEMO) { tryLoadFont("SSM1_12.FNT", resMgr, decomp); tryLoadFont("SSM1_15.FNT", resMgr, decomp); tryLoadFont("SSM1_30.FNT", resMgr, decomp); tryLoadFont("EXIT.FNT", resMgr, decomp); } else if (gameId == GID_COMINGATTRACTIONS) { tryLoadFont("RMN8_11.FNT", resMgr, decomp); tryLoadFont("RMN7_19.FNT", resMgr, decomp); tryLoadFont("EXIT.FNT", resMgr, decomp); _fonts.setVal(kDefaultFont, _fonts.getVal(kGameDlgFont)); return; } else { tryLoadFont("8X8.FNT", resMgr, decomp); tryLoadFont("6X6.FNT", resMgr, decomp); tryLoadFont("4x5.FNT", resMgr, decomp); if (gameId == GID_DRAGON) { tryLoadFont("DRAGON.FNT", resMgr, decomp); tryLoadFont("7X8.FNT", resMgr, decomp); tryLoadFont("P6X6.FNT", resMgr, decomp); } else if (gameId == GID_HOC) { tryLoadFont("HOC.FNT", resMgr, decomp); tryLoadFont("CHINA.FNT", resMgr, decomp); tryLoadFont("CHINESE.FNT", resMgr, decomp); } else if (gameId == GID_WILLY) { tryLoadFont("WILLY.FNT", resMgr, decomp); tryLoadFont("WVCR.FNT", resMgr, decomp); tryLoadFont("COMIX_16.FNT", resMgr, decomp); } else if (gameId == GID_QUARKY) { tryLoadFont("MRALLY.FNT", resMgr, decomp); tryLoadFont("MVCR.FNT", resMgr, decomp); tryLoadFont("RUDEFONT.FNT", resMgr, decomp); _fonts.setVal(kDefaultFont, _fonts.getVal(k8x8Font)); return; } } _fonts.setVal(kDefaultFont, _fonts.getVal(kGameFont)); } } // End of namespace Dgds