/* 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 "common/endian.h" #include "graphics/fonts/ttf.h" #include "graphics/font.h" #include "graphics/surface.h" #include "image/tga.h" #include "engines/grim/debug.h" #include "engines/grim/grim.h" #include "engines/grim/savegame.h" #include "engines/grim/font.h" #include "engines/grim/resource.h" #include "engines/grim/gfx_base.h" namespace Grim { void Font::save(const Font *font, SaveGame *state) { const FontTTF *ttf = dynamic_cast(font); if (ttf) { state->writeLESint32(-2); state->writeLESint32(ttf->getId()); return; } const BitmapFont *bitmapFont = dynamic_cast(font); if (bitmapFont) { state->writeLESint32(bitmapFont->getId()); return; } state->writeLESint32(-1); } Font *Font::load(SaveGame *state) { int32 fontId = state->readLESint32(); if (fontId == -1) { return nullptr; } if (fontId == -2) { fontId = state->readLESint32(); return FontTTF::getPool().getObject(fontId); } return BitmapFont::getPool().getObject(fontId); } Font *Font::getByFileName(const Common::String& fontName) { for (Font *f : BitmapFont::getPool()) { if (f->getFilename() == fontName) { return f; } } for (Font *f : FontTTF::getPool()) { if (f->getFilename() == fontName) { return f; } } return nullptr; } Font *Font::getFirstFont() { if (BitmapFont::getPool().begin() != BitmapFont::getPool().end()) return *BitmapFont::getPool().begin(); if (FontTTF::getPool().begin() != FontTTF::getPool().end()) return *FontTTF::getPool().begin(); return nullptr; } bool BitmapFont::is8Bit() const { return !_isDBCS && !_isUnicode; } BitmapFont::BitmapFont() : _userData(nullptr), _fontData(nullptr), _charHeaders(nullptr), _numChars(0), _dataSize(0), _kernedHeight(0), _baseOffsetY(0), _firstChar(0), _lastChar(0), _isUnicode(false), _isDBCS(false) { } BitmapFont::~BitmapFont() { _fwdCharIndex.clear(); delete[] _charHeaders; delete[] _fontData; g_driver->destroyFont(this); } void BitmapFont::load(const Common::String &filename, Common::SeekableReadStream *data) { _filename = filename; _numChars = data->readUint32LE(); _dataSize = data->readUint32LE(); _kernedHeight = data->readUint32LE(); _baseOffsetY = data->readUint32LE(); data->seek(24, SEEK_SET); _firstChar = data->readUint32LE(); _lastChar = data->readUint32LE(); _isDBCS = g_grim->getGameLanguage() == Common::ZH_CHN && _numChars > 0xff; if (_isDBCS) { // Read character indexes - are the key/value reversed? _fwdCharIndex.resize(_numChars); for (uint i = 0; i < _numChars; ++i) { uint16 point = data->readUint16LE(); _fwdCharIndex[i] = point ? point : -1; } } else { // Read character indexes - are the key/value reversed? Common::Array revCharIndex; revCharIndex.resize(_numChars); uint16 maxPoint = 0; for (uint i = 0; i < _numChars; ++i) { uint16 point = data->readUint16LE(); revCharIndex[i] = point; if (point > maxPoint) maxPoint = point; } _fwdCharIndex.resize(maxPoint + 1, -1); for (uint i = 0; i < _numChars; ++i) { _fwdCharIndex[revCharIndex[i]] = i; } // In order to ensure the correct character codes for // accented characters it is necessary to check the // requested code against the index of characters for // the font. Previously, signed characters were // causing the problem but it might be possible for // an invalid character to be called for other reasons. // // Example: Without this fix when Manny greets Eva // for the first time and he says "Buenos Días" the // 'í' character will either show up as a different // character or it crashes the game. for (uint i = 0; i < _numChars; ++i) { if (revCharIndex[i] == i) _fwdCharIndex[revCharIndex[i]] = i; } } // Read character headers _charHeaders = new CharHeader[_numChars]; for (uint i = 0; i < _numChars; ++i) { _charHeaders[i].offset = data->readUint32LE(); // Kerned character size _charHeaders[i].kernedWidth = data->readSByte(); _charHeaders[i].startingCol = data->readSByte(); _charHeaders[i].startingLine = data->readSByte(); data->seek(1, SEEK_CUR); // Character bitmap size _charHeaders[i].bitmapPitch = _charHeaders[i].bitmapWidth = data->readUint32LE(); _charHeaders[i].bitmapHeight = data->readUint32LE(); } // Read font data _fontData = new byte[_dataSize]; data->read(_fontData, _dataSize); g_driver->createFont(this); } void BitmapFont::loadTGA(const Common::String &filename, Common::SeekableReadStream *index, Common::SeekableReadStream *image) { Image::TGADecoder dec; bool success = dec.loadStream(*image); if (!success) return; const Graphics::Surface *surf = dec.getSurface(); const int MAX_16BIT_CHARACTER = 0xffff; const int INDEX_ENTRY_SIZE = 10; _filename = filename; _numChars = index->size() / INDEX_ENTRY_SIZE; _dataSize = surf->w * surf->h; _kernedHeight = 16; _baseOffsetY = 0; _firstChar = 0; _lastChar = MAX_16BIT_CHARACTER; _isDBCS = false; _isUnicode = true; // Read character headers _charHeaders = new CharHeader[_numChars]; _fwdCharIndex.resize(MAX_16BIT_CHARACTER + 1, -1); for (uint i = 0; i < _numChars; ++i) { uint16 point = index->readUint16LE(); uint32 x = index->readUint32LE(); uint32 y = index->readUint32LE(); _fwdCharIndex[point] = i; _charHeaders[i].offset = x + y * surf->w; _charHeaders[i].kernedWidth = 16; _charHeaders[i].startingCol = 0; _charHeaders[i].startingLine = 0; _charHeaders[i].bitmapWidth = 16; _charHeaders[i].bitmapHeight = 16; _charHeaders[i].bitmapPitch = surf->w; } // Read font data _fontData = new byte[_dataSize]; for (int y = 0; y < surf->h; y++) for (int x = 0; x < surf->w; x++) _fontData[y * surf->w + x] = surf->getPixel(x, y) ? 0 : 0xff; g_driver->createFont(this); } uint16 BitmapFont::getCharIndex(uint32 c) const { int res = c < _fwdCharIndex.size() ? _fwdCharIndex[c] : -1; if (res >= 0) return res; Debug::warning(Debug::Fonts, "The requested character (code 0x%x) does not correspond to anything in the font data!", c); // If we couldn't find the character then default to // the first character in the font so that something // gets loaded to prevent the game from crashing return 0; } uint32 BitmapFont::getNextChar(const Common::String &text, uint32 &i) const { if (_isUnicode) { uint32 chr = 0; uint num = 1; if ((text[i] & 0xF8) == 0xF0) { num = 4; } else if ((text[i] & 0xF0) == 0xE0) { num = 3; } else if ((text[i] & 0xE0) == 0xC0) { num = 2; } if (text.size() - i < num) { i = text.size(); return '?'; } switch (num) { case 4: chr |= (text[i++] & 0x07) << 18; chr |= (text[i++] & 0x3F) << 12; chr |= (text[i++] & 0x3F) << 6; chr |= (text[i++] & 0x3F); break; case 3: chr |= (text[i++] & 0x0F) << 12; chr |= (text[i++] & 0x3F) << 6; chr |= (text[i++] & 0x3F); break; case 2: chr |= (text[i++] & 0x1F) << 6; chr |= (text[i++] & 0x3F); break; default: chr = (text[i++] & 0x7F); break; } return chr; } uint16 ch = uint8(text[i]); if (_isDBCS && i + 1 < text.size() && (ch & 0x80)) { ch = (ch << 8) | (text[++i] & 0xff); } i++; return ch; } int BitmapFont::getKernedStringLength(const Common::String &text) const { int result = 0; for (uint32 i = 0; i < text.size(); ) { result += getCharKernedWidth(getNextChar(text, i)); } return result; } int BitmapFont::getBitmapStringLength(const Common::String &text) const { int result = 0; const uint size = text.size(); for (uint32 i = 0; i < size; ) { const uint32 ch = getNextChar(text, i); result += getCharStartingCol(ch); result += (i >= size) ? getCharBitmapWidth(ch) : getCharKernedWidth(ch); } return result; } int BitmapFont::getStringHeight(const Common::String &text) const { int result = 0; for (uint32 i = 0; i < text.size(); ) { uint32 ch = getNextChar(text, i); int verticalOffset = getCharStartingLine(ch) + getBaseOffsetY(); int charHeight = verticalOffset + getCharBitmapHeight(ch); if (charHeight > result) result = charHeight; } return result; } void BitmapFont::saveState(SaveGame *state) const { state->writeString(getFilename()); } void BitmapFont::restoreState(SaveGame *state) { Common::String fname = state->readString(); Common::SeekableReadStream *stream; g_driver->destroyFont(this); delete[] _fontData; _fontData = nullptr; _fwdCharIndex.clear(); delete[] _charHeaders; _charHeaders = nullptr; stream = g_resourceloader->openNewStreamFile(fname.c_str(), true); load(fname, stream); delete stream; } void FontTTF::saveState(SaveGame *state) const { state->writeString(getFilename()); state->writeLESint32(_size); } void FontTTF::restoreState(SaveGame *state) { Common::String fname = state->readString(); int size = state->readLESint32(); Common::SeekableReadStream *stream; g_driver->destroyFont(this); delete _font; if (g_grim->getGameType() == GType_GRIM && g_grim->getGameLanguage() == Common::KO_KOR) { Common::String name = fname + ".txt"; stream = g_resourceloader->openNewStreamFile(name, true); if (stream) { Common::String line = stream->readLine(); Common::String font; Common::String fsize; for (uint i = 0; i < line.size(); ++i) { if (line[i] == ' ') { font = Common::String(line.c_str(), i); fsize = Common::String(line.c_str() + i + 1, line.size() - i - 2); } } int s = atoi(fsize.c_str()); delete stream; stream = g_resourceloader->openNewStreamFile(font.c_str(), true); loadTTF(fname, stream, s); } else { error("Cannot load korean ttf font"); } } else { stream = g_resourceloader->openNewStreamFile(fname.c_str(), true); loadTTF(fname, stream, size); } } void BitmapFont::render(Graphics::Surface &buf, const Common::String ¤tLine, const Graphics::PixelFormat &pixelFormat, uint32 blackColor, uint32 color, uint32 colorKey) const { int width = getBitmapStringLength(currentLine) + 1; int height = getStringHeight(currentLine) + 1; int startColumn = 0; buf.create(width, height, pixelFormat); buf.fillRect(Common::Rect(0, 0, width, height), colorKey); for (uint32 d = 0; d < currentLine.size(); ) { uint32 ch = getNextChar(currentLine, d); int32 charBitmapWidth = getCharBitmapWidth(ch); int32 charBitmapPitch = getCharBitmapPitch(ch); int32 charBitmapHeight = getCharBitmapHeight(ch); int8 fontRow = getCharStartingLine(ch) + getBaseOffsetY(); int8 fontCol = getCharStartingCol(ch); for (int line = 0; line < charBitmapHeight; line++) { int lineOffset = (fontRow + line); int columnOffset = startColumn + fontCol; int fontOffset = (charBitmapPitch * line); for (int bitmapCol = 0; bitmapCol < charBitmapWidth; bitmapCol++, columnOffset++, fontOffset++) { byte pixel = getCharData(ch)[fontOffset]; if (pixel == 0x80) { buf.setPixel(columnOffset, lineOffset, blackColor); } else if (pixel == 0xFF) { buf.setPixel(columnOffset, lineOffset, color); } } } startColumn += getCharKernedWidth(ch); } } void FontTTF::loadTTF(const Common::String &filename, Common::SeekableReadStream *data, int size) { _filename = filename; _size = size; #ifdef USE_FREETYPE2 _font = Graphics::loadTTFFont(data, DisposeAfterUse::YES, size); #else _font = nullptr; #endif _isUnicode = false; } void FontTTF::loadTTFFromArchive(const Common::String &filename, int size) { _filename = filename; _size = size; #ifdef USE_FREETYPE2 _font = Graphics::loadTTFFontFromArchive(filename, size, Graphics::kTTFSizeModeCharacter, 0, Graphics::kTTFRenderModeLight); #else _font = nullptr; #endif _isUnicode = true; } int FontTTF::getKernedStringLength(const Common::String &text) const { if (g_grim->getGameLanguage() == Common::KO_KOR) { return _font->getStringWidth(convertToU32String(text.c_str(), Common::kWindows949)); } if (_isUnicode) { return _font->getStringWidth(text.decode(Common::CodePage::kUtf8)); } return _font->getStringWidth(text); } void FontTTF::render(Graphics::Surface &surface, const Common::String ¤tLine, const Graphics::PixelFormat &pixelFormat, uint32 blackColor, uint32 color, uint32 colorKey) const { #ifdef USE_FREETYPE2 if (g_grim->getGameLanguage() == Common::KO_KOR) { Common::U32String u32CurrentLine(currentLine, Common::kWindows949); int width = _font->getStringWidth(u32CurrentLine); int height = _font->getFontHeight(); surface.create(width, height, pixelFormat); surface.fillRect(Common::Rect(0, 0, width, height), colorKey); _font->drawString(&surface, u32CurrentLine, 0, 0, width, 0xFFFFFFFF); } else if (_isUnicode) { Common::Rect bbox = _font->getBoundingBox(currentLine.decode(Common::CodePage::kUtf8)); surface.create(bbox.right, bbox.bottom, pixelFormat); surface.fillRect(Common::Rect(0, 0, bbox.right, bbox.bottom), colorKey); _font->drawString(&surface, currentLine.decode(Common::CodePage::kUtf8), 0, 0, bbox.right, 0xFFFFFFFF); } else { Common::Rect bbox = _font->getBoundingBox(currentLine); surface.create(bbox.right, bbox.bottom, pixelFormat); surface.fillRect(Common::Rect(0, 0, bbox.right, bbox.bottom), colorKey); _font->drawString(&surface, currentLine, 0, 0, bbox.right, 0xFFFFFFFF); } #endif } // Hardcoded default font for FPS, GUI, etc const uint8 BitmapFont::emerFont[][13] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36}, {0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00}, {0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18}, {0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70}, {0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e}, {0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c}, {0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30}, {0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00}, {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03}, {0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c}, {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18}, {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e}, {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e}, {0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c}, {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff}, {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e}, {0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06}, {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60}, {0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e}, {0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18}, {0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, {0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, {0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc}, {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff}, {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff}, {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e}, {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e}, {0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}, {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3}, {0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3}, {0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3}, {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e}, {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, {0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c}, {0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe}, {0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e}, {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff}, {0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3}, {0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3}, {0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff}, {0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c}, {0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60}, {0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18}, {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70}, {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0}, {0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03}, {0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e}, {0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0}, {0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00}, {0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00}, {0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0}, {0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78}, {0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00}, {0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00}, {0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00}, {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, {0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f}, {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}, {0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; } // end of namespace Grim