diff options
Diffstat (limited to 'engines/hopkins/font.cpp')
-rw-r--r-- | engines/hopkins/font.cpp | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/engines/hopkins/font.cpp b/engines/hopkins/font.cpp new file mode 100644 index 0000000000..ac0eee2866 --- /dev/null +++ b/engines/hopkins/font.cpp @@ -0,0 +1,496 @@ +/* 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. + * + */ + +#include "hopkins/font.h" + +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" +#include "hopkins/objects.h" + +#include "common/system.h" +#include "common/file.h" +#include "common/textconsole.h" + +namespace Hopkins { + +FontManager::FontManager(HopkinsEngine *vm) { + _vm = vm; + clearAll(); +} + +FontManager::~FontManager() { + _vm->_globals->freeMemory(_font); + _vm->_globals->freeMemory(_zoneText); +} + +void FontManager::loadZoneText() { + switch (_vm->_globals->_language) { + case LANG_EN: + _zoneText = _vm->_fileIO->loadFile("ZONEAN.TXT"); + break; + case LANG_FR: + _zoneText = _vm->_fileIO->loadFile("ZONE01.TXT"); + break; + case LANG_SP: + _zoneText = _vm->_fileIO->loadFile("ZONEES.TXT"); + break; + } +} + +void FontManager::clearAll() { + _font = NULL; + _fontFixedHeight = 0; + _fontFixedWidth = 0; + + for (int idx = 0; idx < 12; ++idx) { + Common::fill((byte *)&_text[idx], (byte *)&_text[idx] + sizeof(TxtItem), 0); + + _textList[idx]._enabledFl = false; + _textList[idx]._height = 0; + _textList[idx]._width = 0; + _textList[idx]._pos.x = 0; + _textList[idx]._pos.y = 0; + } + + for (int idx = 0; idx < 21; idx++) + _textSortArray[idx] = 0; + + _oldName = Common::String(""); + _indexName = Common::String(""); + + for (int idx = 0; idx < 4048; idx++) + _index[idx] = 0; + + _tempText = NULL; + _zoneText = NULL; + + _boxWidth = 240; +} + +void FontManager::initData() { + _font = _vm->_fileIO->loadFile("FONTE3.SPR"); + _fontFixedWidth = 12; + _fontFixedHeight = 21; + loadZoneText(); +} +/** + * Display Text + */ +void FontManager::showText(int idx) { + if ((idx - 5) > MAX_TEXT) + error("Attempted to display text > MAX_TEXT."); + + TxtItem &txt = _text[idx - 5]; + txt._textOnFl = true; + txt._textLoadedFl = false; + + txt._textBlock = _vm->_globals->freeMemory(txt._textBlock); +} + +/** + * Hide text + */ +void FontManager::hideText(int idx) { + if ((idx - 5) > MAX_TEXT) + error("Attempted to display text > MAX_TEXT."); + + TxtItem &txt = _text[idx - 5]; + txt._textOnFl = false; + txt._textLoadedFl = false; + txt._textBlock = _vm->_globals->freeMemory(txt._textBlock); +} + +/** + * Set Text Color + */ +void FontManager::setTextColor(int idx, byte colByte) { + _text[idx - 5]._color = colByte; +} + +/** + * Set Text Optimal Color + */ +void FontManager::setOptimalColor(int idx1, int idx2, int idx3, int idx4) { + setTextColor(idx1, 255); + setTextColor(idx2, 255); + setTextColor(idx3, 255); + setTextColor(idx4, 253); +} + +/** + * Init text structure + */ +void FontManager::initTextBuffers(int idx, int messageId, const Common::String &filename, int xp, int yp, int textType, int length, int color) { + assert(idx - 5 >= 0 && (idx - 5) <= MAX_TEXT); + + TxtItem &txt = _text[idx - 5]; + txt._textOnFl = false; + txt._filename = filename; + txt._pos.x = xp; + txt._pos.y = yp; + txt._messageId = messageId; + txt._textType = textType; + txt._length = length; + txt._color = color; +} + +// Box +void FontManager::box(int idx, int messageId, const Common::String &filename, int xp, int yp) { + int textPosX = xp; + if (idx < 0) + error("Bad number for text"); + _fontFixedWidth = 11; + + _boxWidth = 11 * _text[idx]._length; + if (_text[idx]._textLoadedFl) { + int textType = _text[idx]._textType; + if (textType != 6 && textType != 1 && textType != 3 && textType != 5) { + int yCurrent = yp + 5; + for (int lineNum = 0; lineNum < _text[idx]._lineCount; ++lineNum) { + displayText(xp + 5, yCurrent, _text[idx]._lines[lineNum], _text[idx]._color); + yCurrent += _fontFixedHeight + 1; + } + } else { + int height = _text[idx]._height; + int width = _text[idx]._width; + _vm->_graphicsMan->restoreSurfaceRect( + _vm->_graphicsMan->_frontBuffer, + _text[idx]._textBlock, + xp, + yp, + _text[idx]._width, + _text[idx]._height); + _vm->_graphicsMan->addDirtyRect(xp, yp, xp + width, yp + height); + } + } else { + int lineCount = 0; + for (int i = 0; i <= 19; i++) + _textSortArray[i] = 0; + + _text[idx]._textLoadedFl = true; + Common::String file = filename; + if (strncmp(file.c_str(), _oldName.c_str(), strlen(file.c_str())) != 0) { + // Starting to access a new file, so read in the index file for the file + _oldName = file; + _indexName = Common::String(file.c_str(), file.size() - 3); + _indexName += "IND"; + + Common::File f; + if (!f.open(_indexName)) + error("Error opening file - %s", _indexName.c_str()); + int filesize = f.size(); + for (int i = 0; i < (filesize / 4); ++i) + _index[i] = f.readUint32LE(); + f.close(); + } + int bufSize; + if (filename[0] != 'Z' || filename[1] != 'O') { + Common::File f; + if (!f.open(file)) + error("Error opening file - %s", _indexName.c_str()); + + bufSize = 2048; + f.seek(_index[messageId]); + + _tempText = _vm->_globals->allocMemory(2058); + if (_tempText == NULL) + error("Error allocating text"); + + Common::fill(&_tempText[0], &_tempText[2058], 0); + f.read(_tempText, 2048); + f.close(); + } else { + bufSize = 100; + _tempText = _vm->_globals->allocMemory(110); + Common::fill(&_tempText[0], &_tempText[110], 0); + memcpy(_tempText, _zoneText + _index[messageId], 96); + WRITE_LE_UINT16((uint16 *)_tempText + 48, READ_LE_INT16(_zoneText + _index[messageId] + 96)); + } + byte *curTempTextPtr = _tempText; + for (int i = 0; i < bufSize; i++) { + byte curChar = *curTempTextPtr; + if ((byte)(*curTempTextPtr + 46) > 27) { + if ((byte)(curChar + 80) > 27) { + if ((byte)(curChar - 65) <= 25 || (byte)(curChar - 97) <= 25) + curChar = 32; + } else { + curChar -= 79; + } + } else { + curChar += 111; + } + *curTempTextPtr = curChar; + curTempTextPtr++; + }; + + int textLength; + for (textLength = 0; textLength < bufSize; textLength++) { + byte curChar = _tempText[textLength]; + if (curChar == '\r' || curChar == '\n') { + _tempText[textLength] = 0; + if (!_text[idx]._length) + break; + } + } + + if (bufSize && bufSize > textLength) { + _text[idx]._length = textLength; + _boxWidth = 0; + + for (int curStrIdx = 0; curStrIdx < textLength + 1; curStrIdx++) { + byte curChar = _tempText[curStrIdx]; + if (curChar <= 31) + curChar = ' '; + _boxWidth += _vm->_objectsMan->getWidth(_font, curChar - 32); + } + + _boxWidth += 2; + _text[idx]._pos.x = 320 - abs(_boxWidth / 2); + textPosX = _vm->_events->_startPos.x + _text[idx]._pos.x; + lineCount = 1; + _text[idx]._lines[0] = Common::String((const char *)_tempText, textLength); + } else { + if (!_boxWidth) + _boxWidth = 240; + int tempTextIdx = 0; + int lineSize; + byte curChar; + do { + int curLineSize = 0; + int ptrb = _boxWidth - 4; + for (;;) { + lineSize = curLineSize; + do + curChar = _tempText[tempTextIdx + curLineSize++]; + while (curChar != ' ' && curChar != '%'); + if (curLineSize >= ptrb / _fontFixedWidth) { + if (curChar == '%') + curChar = ' '; + break; + } + if (curChar == '%') { + lineSize = curLineSize; + break; + } + } + + // WORKAROUND: Perhaps due to the usage of ScummVM strings here, recalculate what the + // actual length of the line to be copied will be. Otherwise, you can see artifacts, + // such as a single character beyond the end of string NULL. + int actualSize = 0; + while (actualSize < lineSize && _tempText[tempTextIdx + actualSize]) + ++actualSize; + + _text[idx]._lines[lineCount] = Common::String((const char *)_tempText + tempTextIdx, actualSize); + _textSortArray[lineCount++] = lineSize; + + tempTextIdx += lineSize; + } while (curChar != '%'); + + for (int i = 0; i <= 19; i++) { + if (_textSortArray[i] <= 0) { + _textSortArray[i] = 0; + } else { + int ptrc = 0; + for (int curIdx = 0; curIdx < _textSortArray[i] - 1; curIdx++) { + Common::String &line = _text[idx]._lines[i]; + byte curChar2 = (curIdx >= (int)line.size()) ? '\0' : line.c_str()[curIdx]; + if (curChar2 <= 31) + curChar2 = ' '; + ptrc += _vm->_objectsMan->getWidth(_font, (byte)curChar2 - 32); + } + _textSortArray[i] = ptrc; + } + } + for (int i = 0; i <= 19; i++) { + for (int j = i + 1; j != i; j = (j + 1) % 20) { + if (_textSortArray[i] < _textSortArray[j]) + _textSortArray[i] = 0; + } + }; + + for (int i = 0; i <= 19; i++) { + if (_textSortArray[i]) + _boxWidth = _textSortArray[i]; + } + + if ((_text[idx]._textType < 2) || (_text[idx]._textType > 3)) { + int i; + for (i = xp - _vm->_events->_startPos.x; _boxWidth + i > 638 && i > -2 && _text[idx]._textType; i -= 2) + ; + _text[idx]._pos.x = i; + textPosX = _vm->_events->_startPos.x + i; + } else { + _text[idx]._pos.x = textPosX; + } + } + int posX = textPosX; + int posY = yp; + int saveWidth = _boxWidth + 10; + int saveHeight = (_fontFixedHeight + 1) * lineCount + 12; + if (_text[idx]._textType == 6) { + _text[idx]._pos.x = 315 - abs(saveWidth / 2); + textPosX = posX = _vm->_events->_startPos.x + _text[idx]._pos.x; + _text[idx]._pos.y = posY = 50; + } + int textType = _text[idx]._textType; + if (textType == 1 || textType == 3 || textType == 5 || textType == 6) { + int size = saveHeight * saveWidth; + byte *ptrd = _vm->_globals->allocMemory(size); + if (ptrd == NULL) + error("Cutting a block for text box (%d)", size); + + _vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, ptrd, posX, posY, saveWidth, saveHeight); + _vm->_graphicsMan->fillSurface(ptrd, _vm->_graphicsMan->_colorTable, size); + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, ptrd, posX, posY, saveWidth, saveHeight); + _vm->_globals->freeMemory(ptrd); + + _vm->_graphicsMan->drawHorizontalLine(_vm->_graphicsMan->_frontBuffer, posX, posY, saveWidth, (byte)-2); + _vm->_graphicsMan->drawHorizontalLine(_vm->_graphicsMan->_frontBuffer, posX, saveHeight + posY, saveWidth, (byte)-2); + _vm->_graphicsMan->drawVerticalLine(_vm->_graphicsMan->_frontBuffer, posX, posY, saveHeight, (byte)-2); + _vm->_graphicsMan->drawVerticalLine(_vm->_graphicsMan->_frontBuffer, saveWidth + posX, posY, saveHeight, (byte)-2); + } + _text[idx]._lineCount = lineCount; + int textPosY = posY + 5; + + for (int lineNum = 0; lineNum < lineCount; ++lineNum) { + displayText(textPosX + 5, textPosY, _text[idx]._lines[lineNum], _text[idx]._color); + textPosY += _fontFixedHeight + 1; + } + + int blockWidth = saveWidth + 1; + int blockHeight = saveHeight + 1; + + _text[idx]._width = blockWidth; + _text[idx]._height = blockHeight; + textType = _text[idx]._textType; + if (textType == 6 || textType == 1 || textType == 3 || textType == 5) { + _text[idx]._textBlock = _vm->_globals->freeMemory(_text[idx]._textBlock); + int blockSize = blockHeight * blockWidth; + byte *ptre = _vm->_globals->allocMemory(blockSize + 20); + if (ptre == NULL) + error("Cutting a block for text box (%d)", blockSize); + + _text[idx]._textBlock = ptre; + _text[idx]._width = blockWidth; + _text[idx]._height = blockHeight; + _vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, _text[idx]._textBlock, posX, posY, _text[idx]._width, blockHeight); + } + _tempText = _vm->_globals->freeMemory(_tempText); + } +} + +/** + * Directly display text (using a VESA segment) + */ +void FontManager::displayTextVesa(int xp, int yp, const Common::String &message, int col) { + int charIndex; + int currentX = xp; + + const char *srcP = message.c_str(); + for (;;) { + byte currChar = *srcP++; + if (!currChar) + break; + if (currChar >= 32) { + charIndex = currChar - 32; + _vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, currentX, yp, currChar - 32, col); + currentX += _vm->_objectsMan->getWidth(_font, charIndex); + } + } + + _vm->_graphicsMan->addDirtyRect(xp, yp, currentX, yp + 12); +} + +/** + * Directly display text + */ +void FontManager::displayText(int xp, int yp, const Common::String &message, int col) { + for (uint idx = 0; idx < message.size(); ++idx) { + byte currentChar = (byte)message[idx]; + + if (currentChar > 31) { + int characterIndex = currentChar - 32; + _vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, xp, yp, characterIndex, col); + _vm->_graphicsMan->addDirtyRect(xp, yp, xp + _vm->_objectsMan->getWidth(_font, characterIndex) + 1, yp + _vm->_objectsMan->getHeight(_font, characterIndex) + 1); + xp += _vm->_objectsMan->getWidth(_font, characterIndex); + } + } +} + +/** + * Compute character width and render text using variable width fonts + */ +void FontManager::renderTextDisplay(int xp, int yp, const Common::String &msg, int col) { + const char *srcP = msg.c_str(); + int charEndPosX = xp; + int fontCol = col; + byte curChar = *srcP++; + while (curChar) { + if (curChar == '&') { + fontCol = 2; + curChar = *srcP++; + } + if (curChar == '$') { + fontCol = 4; + curChar = *srcP++; + } + if (!curChar) + break; + if (curChar >= 32) { + byte printChar = curChar - 32; + _vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, charEndPosX, yp, printChar, fontCol); + + // UGLY HACK: For some obscure reason, the BeOS and OS/2 versions use another font file, which doesn't have variable width. + // All the fonts have a length of 9, which results in completely broken text in the computer. + // This horrible workaround fixes the English versions of the game. So far, no other languages are known for those platforms. + // Just in case, all the accentuated characters are handled properly, which *should* be OK for the other languages too. + int charWidth; + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) { + if ((curChar >= 'A' && curChar <= 'Z') || (curChar >= 'a' && curChar <= 'z' && curChar != 'm' && curChar != 'w') || (curChar >= '0' && curChar <= '9') || curChar == '*' || (curChar >= 128 && curChar <= 168)) + charWidth = _vm->_objectsMan->getWidth(_font, printChar) - 1; + else if (curChar == 'm' || curChar == 'w') + charWidth = _vm->_objectsMan->getWidth(_font, printChar); + else + charWidth = 6; + } else + charWidth = _vm->_objectsMan->getWidth(_font, printChar); + + int charStartPosX = charEndPosX; + charEndPosX += charWidth; + _vm->_graphicsMan->addDirtyRect(charStartPosX, yp, charEndPosX, yp + 12); + if (_vm->_events->_escKeyFl) { + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + _vm->_events->refreshScreenAndEvents(); + } else { + _vm->_globals->_eventMode = EVENTMODE_ALT; + _vm->_events->refreshScreenAndEvents(); + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + } + } + curChar = *srcP++; + } +} + +} // End of namespace Hopkins |