aboutsummaryrefslogtreecommitdiff
path: root/engines/hopkins/font.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/hopkins/font.cpp')
-rw-r--r--engines/hopkins/font.cpp496
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