scummvm/engines/hopkins/font.cpp
2013-01-24 00:05:36 +01:00

484 lines
14 KiB
C++

/* 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 "common/system.h"
#include "common/file.h"
#include "common/textconsole.h"
#include "hopkins/font.h"
#include "hopkins/files.h"
#include "hopkins/globals.h"
#include "hopkins/graphics.h"
#include "hopkins/hopkins.h"
#include "hopkins/objects.h"
namespace Hopkins {
FontManager::FontManager() {
clearAll();
}
void FontManager::setParent(HopkinsEngine *vm) {
_vm = vm;
}
void FontManager::clearAll() {
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 = g_PTRNUL;
}
/**
* 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) {
byte *v9;
byte *ptre;
Common::String s;
Common::String file;
Common::File f;
int v73 = xp;
if (idx < 0)
error("Bad number for text");
_vm->_globals.police_l = 11;
_vm->_globals._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 += _vm->_globals.police_h + 1;
}
} else {
int height = _text[idx]._height;
int width = _text[idx]._width;
_vm->_graphicsManager.Restore_Mem(
_vm->_graphicsManager._vesaBuffer,
_text[idx]._textBlock,
xp,
yp,
_text[idx]._width,
_text[idx]._height);
_vm->_graphicsManager.addVesaSegment(xp, yp, xp + width, yp + height);
}
} else {
int lineCount = 0;
for (int v62 = 0; v62 <= 19; v62++)
_textSortArray[v62] = 0;
_text[idx]._textLoadedFl = true;
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";
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 v69;
if (filename[0] != 'Z' || filename[1] != 'O') {
if (!f.open(file))
error("Error opening file - %s", _indexName.c_str());
v69 = 2048;
f.seek(_index[messageId]);
_tempText = _vm->_globals.allocMemory(2058);
if (_tempText == g_PTRNUL)
error("Error allocating text");
Common::fill(&_tempText[0], &_tempText[2058], 0);
f.read(_tempText, 2048);
f.close();
} else {
v69 = 100;
v9 = _vm->_globals.allocMemory(110);
Common::fill(&v9[0], &v9[110], 0);
_tempText = v9;
const byte *v10 = _vm->_globals.BUF_ZONE + _index[messageId];
memcpy(v9, v10, 96);
WRITE_LE_UINT16((uint16 *)v9 + 48, (int16)READ_LE_UINT16(v10 + 96));
}
byte *v59 = _tempText;
for (int v63 = 0; v63 < v69; v63++) {
byte v13 = *v59;
if ((byte)(*v59 + 46) > 27) {
if ((byte)(v13 + 80) > 27) {
if ((byte)(v13 - 65) <= 25 || (byte)(v13 - 97) <= 25)
v13 = 32;
} else {
v13 -= 79;
}
} else {
v13 += 111;
}
*v59 = v13;
v59++;
};
int textLength = 0;
if (v69) {
for (;;) {
byte curChar = _tempText[textLength];
if (curChar == '\r' || curChar == '\n') {
_tempText[textLength] = 0;
if (!_text[idx]._length)
break;
}
++textLength;
if (v69 <= textLength)
break;
}
}
if (v69 && v69 > textLength) {
_text[idx]._length = textLength;
_vm->_globals._boxWidth = 0;
for (int v15 = 0; v15 < textLength + 1; v15++) {
byte v16 = _tempText[v15];
if (v16 <= 31)
v16 = ' ';
_vm->_globals._boxWidth += _vm->_objectsManager.getWidth(_vm->_globals.police, v16 - 32);
}
_vm->_globals._boxWidth += 2;
int v17 = _vm->_globals._boxWidth / 2;
if (v17 < 0)
v17 = -v17;
_text[idx]._pos.x = 320 - v17;
v73 = _vm->_eventsManager._startPos.x + 320 - v17;
lineCount = 1;
// CHECKME: textLength should be always positive...
if (textLength + 1 > 0) {
_text[idx]._lines[0] = Common::String((const char *)_tempText, textLength);
}
} else {
if (!_vm->_globals._boxWidth)
_vm->_globals._boxWidth = 240;
int v65 = 0;
int lineSize;
byte curChar;
do {
int v19 = 0;
int ptrb = _vm->_globals._boxWidth - 4;
for (;;) {
lineSize = v19;
do
curChar = _tempText[v65 + v19++];
while (curChar != ' ' && curChar != '%');
if (v19 >= ptrb / _vm->_globals.police_l) {
if (curChar == '%')
curChar = ' ';
break;
}
if (curChar == '%') {
lineSize = v19;
break;
}
}
int v20 = lineCount;
// 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[v65 + actualSize])
++actualSize;
_text[idx]._lines[v20] = Common::String((const char *)_tempText + v65, actualSize);
_textSortArray[lineCount++] = lineSize;
v65 += lineSize;
} while (curChar != '%');
for (int i = 0; i <= 19; i++) {
if (_textSortArray[i] <= 0) {
_textSortArray[i] = 0;
} else {
int ptrc = 0;
for (int v23 = 0; v23 < _textSortArray[i] - 1; v23++) {
Common::String &line = _text[idx]._lines[i];
byte v24 = (v23 >= (int)line.size()) ? '\0' : line.c_str()[v23];
if (v24 <= 32)
v24 = ' ';
ptrc += _vm->_objectsManager.getWidth(_vm->_globals.police, (byte)v24 - 32);
}
_textSortArray[i] = ptrc;
}
}
for (int i = 0; i <= 19; i++) {
for (int v25 = i + 1; v25 != i; v25 = (v25 + 1) % 20) {
if (_textSortArray[i] < _textSortArray[v25])
_textSortArray[i] = 0;
}
};
for (int i = 0; i <= 19; i++) {
if (_textSortArray[i])
_vm->_globals._boxWidth = _textSortArray[i];
}
if ((_text[idx]._textType < 2) || (_text[idx]._textType > 3)) {
int i;
for (i = xp - _vm->_eventsManager._startPos.x; _vm->_globals._boxWidth + i > 638 && i > -2 && _text[idx]._textType; i -= 2)
;
_text[idx]._pos.x = i;
v73 = _vm->_eventsManager._startPos.x + i;
} else {
if (_vm->_globals.nbrligne == (SCREEN_WIDTH - 1)) {
while (_vm->_globals._boxWidth + v73 > 638 && v73 > -2)
v73 -= 2;
}
if (_vm->_globals.nbrligne == (SCREEN_WIDTH * 2)) {
while (_vm->_globals._boxWidth + v73 > 1278 && v73 > -2)
v73 -= 2;
}
_text[idx]._pos.x = v73;
}
}
int posX = v73;
int posY = yp;
int saveWidth = _vm->_globals._boxWidth + 10;
int saveHeight = (_vm->_globals.police_h + 1) * lineCount + 12;
if (_text[idx]._textType == 6) {
int v27 = saveWidth / 2;
if (v27 < 0)
v27 = -v27;
_text[idx]._pos.x = 315 - v27;
int v28 = _vm->_eventsManager._startPos.x + 315 - v27;
v73 = _vm->_eventsManager._startPos.x + 315 - v27;
_text[idx]._pos.y = 50;
posY = 50;
posX = v28;
}
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 == g_PTRNUL)
error("Cutting a block for text box (%d)", size);
_vm->_graphicsManager.Capture_Mem(_vm->_graphicsManager._vesaBuffer, ptrd, posX, posY, saveWidth, saveHeight);
_vm->_graphicsManager.Trans_bloc2(ptrd, _vm->_graphicsManager._colorTable, size);
_vm->_graphicsManager.Restore_Mem(_vm->_graphicsManager._vesaBuffer, ptrd, posX, posY, saveWidth, saveHeight);
_vm->_globals.freeMemory(ptrd);
_vm->_graphicsManager.drawHorizontalLine(_vm->_graphicsManager._vesaBuffer, posX, posY, saveWidth, (byte)-2);
_vm->_graphicsManager.drawHorizontalLine(_vm->_graphicsManager._vesaBuffer, posX, saveHeight + posY, saveWidth, (byte)-2);
_vm->_graphicsManager.drawVerticalLine(_vm->_graphicsManager._vesaBuffer, posX, posY, saveHeight, (byte)-2);
_vm->_graphicsManager.drawVerticalLine(_vm->_graphicsManager._vesaBuffer, saveWidth + posX, posY, saveHeight, (byte)-2);
}
_text[idx]._lineCount = lineCount;
int v71 = posY + 5;
for (int lineNum = 0; lineNum < lineCount; ++lineNum) {
displayText(v73 + 5, v71, _text[idx]._lines[lineNum], _text[idx]._color);
v71 += _vm->_globals.police_h + 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;
ptre = _vm->_globals.allocMemory(blockSize + 20);
if (ptre == g_PTRNUL)
error("Cutting a block for text box (%d)", blockSize);
_text[idx]._textBlock = ptre;
_text[idx]._width = blockWidth;
_text[idx]._height = blockHeight;
_vm->_graphicsManager.Capture_Mem(_vm->_graphicsManager._vesaBuffer, _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) {
char currChar;
int charIndex;
int currentX = xp;
const char *srcP = message.c_str();
for (;;) {
currChar = *srcP++;
if (!currChar)
break;
if (currChar >= 32) {
charIndex = currChar - 32;
_vm->_graphicsManager.displayFont(_vm->_graphicsManager._vesaBuffer, _vm->_globals.police,
currentX, yp, currChar - 32, col);
currentX += _vm->_objectsManager.getWidth(_vm->_globals.police, charIndex);
}
}
_vm->_graphicsManager.addVesaSegment(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) {
char currentChar = message[idx];
if (currentChar > 31) {
int characterIndex = currentChar - 32;
_vm->_graphicsManager.displayFont(_vm->_graphicsManager._vesaBuffer, _vm->_globals.police, xp, yp, characterIndex, col);
xp += _vm->_objectsManager.getWidth(_vm->_globals.police, 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->_graphicsManager.displayFont(_vm->_graphicsManager._vesaBuffer, _vm->_globals.police, charEndPosX, yp, printChar, fontCol);
charEndPosX += _vm->_objectsManager.getWidth(_vm->_globals.police, printChar);
int charWidth = _vm->_objectsManager.getWidth(_vm->_globals.police, printChar);
_vm->_graphicsManager.addVesaSegment(charEndPosX - charWidth, yp, charEndPosX, yp + 12);
if (_vm->_eventsManager._escKeyFl) {
_vm->_globals.iRegul = 1;
_vm->_eventsManager.VBL();
} else {
_vm->_globals.iRegul = 4;
_vm->_eventsManager.VBL();
_vm->_globals.iRegul = 1;
}
}
curChar = *srcP++;
}
}
} // End of namespace Hopkins