scummvm/engines/zvision/string_manager.cpp

256 lines
6.9 KiB
C++
Raw Normal View History

/* 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/scummsys.h"
#include "zvision/string_manager.h"
#include "zvision/truetype_font.h"
#include "common/file.h"
#include "common/tokenizer.h"
#include "common/debug.h"
#include "graphics/fontman.h"
#include "graphics/colormasks.h"
namespace ZVision {
StringManager::StringManager(ZVision *engine)
: _engine(engine) {
}
StringManager::~StringManager() {
for (Common::HashMap<Common::String, TruetypeFont *>::iterator iter = _fonts.begin(); iter != _fonts.end(); ++iter) {
delete iter->_value;
}
}
void StringManager::initialize(ZVisionGameId gameId) {
if (gameId == GID_NEMESIS) {
// TODO: Check this hardcoded filename against all versions of Nemesis
parseStrFile("nemesis.str");
} else if (gameId == GID_GRANDINQUISITOR) {
// TODO: Check this hardcoded filename against all versions of Grand Inquisitor
parseStrFile("inquis.str");
}
}
void StringManager::parseStrFile(const Common::String &fileName) {
Common::File file;
if (!file.open(fileName)) {
warning("%s does not exist. String parsing failed", fileName.c_str());
return;
}
uint lineNumber = 0;
while (!file.eos()) {
_lastStyle.align = Graphics::kTextAlignLeft;
_lastStyle.color = 0;
_lastStyle.font = nullptr;
Common::String asciiLine = readWideLine(file);
if (asciiLine.empty()) {
continue;
}
char tagString[150];
uint tagStringCursor = 0;
char textString[150];
uint textStringCursor = 0;
bool inTag = false;
for (uint i = 0; i < asciiLine.size(); ++i) {
switch (asciiLine[i]) {
case '<':
inTag = true;
if (!_inGameText[lineNumber].fragments.empty()) {
_inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor);
textStringCursor = 0;
}
break;
case '>':
inTag = false;
parseTag(Common::String(tagString, tagStringCursor), lineNumber);
tagStringCursor = 0;
break;
default:
if (inTag) {
tagString[tagStringCursor] = asciiLine[i];
tagStringCursor++;
} else {
textString[textStringCursor] = asciiLine[i];
textStringCursor++;
}
break;
}
}
if (textStringCursor > 0) {
_inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor);
}
lineNumber++;
assert(lineNumber <= NUM_TEXT_LINES);
}
}
2013-09-16 05:11:52 +00:00
void StringManager::parseTag(const Common::String &tagString, uint lineNumber) {
Common::StringTokenizer tokenizer(tagString);
Common::String token = tokenizer.nextToken();
Common::String fontName;
bool bold = false;
2013-09-16 05:11:52 +00:00
Graphics::TextAlign align = _lastStyle.align;
int point = _lastStyle.font != nullptr ? _lastStyle.font->_fontHeight : 12;
int red = 0;
int green = 0;
int blue = 0;
while (!token.empty()) {
if (token.matchString("font", true)) {
fontName = tokenizer.nextToken();
} else if (token.matchString("bold", true)) {
token = tokenizer.nextToken();
if (token.matchString("on", false)) {
bold = true;
}
} else if (token.matchString("justify", true)) {
token = tokenizer.nextToken();
if (token.matchString("center", false)) {
align = Graphics::kTextAlignCenter;
} else if (token.matchString("right", false)) {
align = Graphics::kTextAlignRight;
}
} else if (token.matchString("point", true)) {
point = atoi(tokenizer.nextToken().c_str());
} else if (token.matchString("red", true)) {
red = atoi(tokenizer.nextToken().c_str());
} else if (token.matchString("green", true)) {
green = atoi(tokenizer.nextToken().c_str());
} else if (token.matchString("blue", true)) {
blue = atoi(tokenizer.nextToken().c_str());
}
token = tokenizer.nextToken();
}
TextFragment fragment;
2013-09-16 05:11:52 +00:00
if (fontName.empty()) {
fragment.style.font = _lastStyle.font;
} else {
Common::String newFontName;
if (fontName.matchString("*times new roman*", true)) {
if (bold) {
newFontName = "timesbd.ttf";
} else {
newFontName = "times.ttf";
}
} else if (fontName.matchString("*courier new*", true)) {
if (bold) {
newFontName = "courbd.ttf";
} else {
newFontName = "cour.ttf";
}
} else if (fontName.matchString("*century schoolbook*", true)) {
if (bold) {
newFontName = "censcbkbd.ttf";
} else {
newFontName = "censcbk.ttf";
}
} else if (fontName.matchString("*garamond*", true)) {
if (bold) {
newFontName = "garabd.ttf";
} else {
newFontName = "gara.ttf";
}
} else {
2013-09-16 05:11:52 +00:00
debug("Could not identify font: %s. Reverting to Arial", fontName.c_str());
if (bold) {
newFontName = "zorknorm.ttf";
} else {
newFontName = "arial.ttf";
}
}
2013-09-16 05:11:52 +00:00
Common::String fontKey = Common::String::format("%s-%d", newFontName.c_str(), point);
if (_fonts.contains(fontKey)) {
fragment.style.font = _fonts[fontKey];
} else {
2013-09-16 05:11:52 +00:00
fragment.style.font = new TruetypeFont(_engine, point);
fragment.style.font->loadFile(newFontName);
_fonts[fontKey] = fragment.style.font;
}
}
2013-09-16 05:11:52 +00:00
fragment.style.align = align;
fragment.style.color = Graphics::ARGBToColor<Graphics::ColorMasks<565>>(0, red, green, blue);
2013-09-16 05:11:52 +00:00
_inGameText[lineNumber].fragments.push_back(fragment);
2013-09-16 05:11:52 +00:00
_lastStyle = fragment.style;
}
Common::String StringManager::readWideLine(Common::SeekableReadStream &stream) {
Common::String asciiString;
// Don't spam the user with warnings about UTF-16 support.
// Just do one warning per String
bool charOverflowWarning = false;
uint16 value = stream.readUint16LE();
while (!stream.eos()) {
// Check for CRLF
if (value == 0x0A0D) {
// Read in the extra NULL char
stream.readByte(); // \0
// End of the line. Break
break;
}
// Crush each octet pair to a single octet with a simple cast
if (value > 255) {
charOverflowWarning = true;
value = '?';
}
char charValue = (char)value;
asciiString += charValue;
value = stream.readUint16LE();
}
if (charOverflowWarning) {
warning("UTF-16 is not supported. Characters greater than 255 are replaced with '?'");
}
return asciiString;
}
StringManager::TextStyle StringManager::getTextStyle(uint stringNumber) {
return _inGameText[stringNumber].fragments.front().style;
}
} // End of namespace ZVision