2013-09-15 19:55:37 +00:00
|
|
|
/* 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"
|
|
|
|
|
2013-10-02 01:08:41 +00:00
|
|
|
#include "zvision/string_manager.h"
|
|
|
|
|
|
|
|
#include "zvision/truetype_font.h"
|
|
|
|
|
2013-09-15 19:55:37 +00:00
|
|
|
#include "common/file.h"
|
|
|
|
#include "common/tokenizer.h"
|
|
|
|
#include "common/debug.h"
|
|
|
|
|
|
|
|
#include "graphics/fontman.h"
|
2013-10-01 22:56:35 +00:00
|
|
|
#include "graphics/colormasks.h"
|
2013-09-15 19:55:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace ZVision {
|
|
|
|
|
|
|
|
StringManager::StringManager(ZVision *engine)
|
|
|
|
: _engine(engine) {
|
|
|
|
}
|
|
|
|
|
|
|
|
StringManager::~StringManager() {
|
2013-10-01 22:27:03 +00:00
|
|
|
for (Common::HashMap<Common::String, TruetypeFont *>::iterator iter = _fonts.begin(); iter != _fonts.end(); ++iter) {
|
2013-10-01 22:15:44 +00:00
|
|
|
delete iter->_value;
|
2013-09-15 19:55:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StringManager::initialize(ZVisionGameId gameId) {
|
2013-09-30 02:41:43 +00:00
|
|
|
if (gameId == GID_NEMESIS) {
|
2013-09-15 19:55:37 +00:00
|
|
|
// TODO: Check this hardcoded filename against all versions of Nemesis
|
|
|
|
parseStrFile("nemesis.str");
|
2013-09-30 02:41:43 +00:00
|
|
|
} else if (gameId == GID_GRANDINQUISITOR) {
|
2013-09-15 19:55:37 +00:00
|
|
|
// 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()) {
|
2013-09-16 05:10:35 +00:00
|
|
|
_lastStyle.align = Graphics::kTextAlignLeft;
|
|
|
|
_lastStyle.color = 0;
|
|
|
|
_lastStyle.font = nullptr;
|
|
|
|
|
|
|
|
Common::String asciiLine = readWideLine(file);
|
|
|
|
if (asciiLine.empty()) {
|
|
|
|
continue;
|
|
|
|
}
|
2013-09-15 19:55:37 +00:00
|
|
|
|
|
|
|
char tagString[150];
|
|
|
|
uint tagStringCursor = 0;
|
|
|
|
char textString[150];
|
|
|
|
uint textStringCursor = 0;
|
|
|
|
bool inTag = false;
|
|
|
|
|
2013-10-01 22:27:03 +00:00
|
|
|
for (uint i = 0; i < asciiLine.size(); ++i) {
|
2013-09-15 19:55:37 +00:00
|
|
|
switch (asciiLine[i]) {
|
|
|
|
case '<':
|
|
|
|
inTag = true;
|
2013-09-16 05:10:35 +00:00
|
|
|
if (!_inGameText[lineNumber].fragments.empty()) {
|
|
|
|
_inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor);
|
|
|
|
textStringCursor = 0;
|
|
|
|
}
|
2013-09-15 19:55:37 +00:00
|
|
|
break;
|
|
|
|
case '>':
|
|
|
|
inTag = false;
|
2013-09-16 05:10:35 +00:00
|
|
|
parseTag(Common::String(tagString, tagStringCursor), lineNumber);
|
2013-09-15 19:55:37 +00:00
|
|
|
tagStringCursor = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (inTag) {
|
2013-09-16 05:10:35 +00:00
|
|
|
tagString[tagStringCursor] = asciiLine[i];
|
2013-09-15 19:55:37 +00:00
|
|
|
tagStringCursor++;
|
|
|
|
} else {
|
|
|
|
textString[textStringCursor] = asciiLine[i];
|
|
|
|
textStringCursor++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-16 05:07:45 +00:00
|
|
|
if (textStringCursor > 0) {
|
|
|
|
_inGameText[lineNumber].fragments.back().text = Common::String(textString, textStringCursor);
|
|
|
|
}
|
2013-09-15 19:55:37 +00:00
|
|
|
|
|
|
|
lineNumber++;
|
2013-09-22 20:52:56 +00:00
|
|
|
assert(lineNumber <= NUM_TEXT_LINES);
|
2013-09-15 19:55:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-16 05:11:52 +00:00
|
|
|
void StringManager::parseTag(const Common::String &tagString, uint lineNumber) {
|
2013-09-15 19:55:37 +00:00
|
|
|
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;
|
2013-09-15 19:55:37 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2013-09-25 07:32:42 +00:00
|
|
|
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";
|
|
|
|
}
|
2013-09-15 19:55:37 +00:00
|
|
|
} 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-15 19:55:37 +00:00
|
|
|
}
|
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];
|
2013-09-15 19:55:37 +00:00
|
|
|
} 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-15 19:55:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-16 05:11:52 +00:00
|
|
|
fragment.style.align = align;
|
2013-10-01 22:56:35 +00:00
|
|
|
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-15 19:55:37 +00:00
|
|
|
|
2013-09-16 05:11:52 +00:00
|
|
|
_lastStyle = fragment.style;
|
2013-09-15 19:55:37 +00:00
|
|
|
}
|
|
|
|
|
2013-09-16 05:07:45 +00:00
|
|
|
Common::String StringManager::readWideLine(Common::SeekableReadStream &stream) {
|
2013-09-17 16:05:49 +00:00
|
|
|
Common::String asciiString;
|
2013-09-15 19:55:37 +00:00
|
|
|
|
|
|
|
// Don't spam the user with warnings about UTF-16 support.
|
|
|
|
// Just do one warning per String
|
|
|
|
bool charOverflowWarning = false;
|
2013-09-16 05:07:45 +00:00
|
|
|
|
|
|
|
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
|
2013-09-15 19:55:37 +00:00
|
|
|
if (value > 255) {
|
|
|
|
charOverflowWarning = true;
|
2013-09-17 16:05:49 +00:00
|
|
|
value = '?';
|
2013-09-15 19:55:37 +00:00
|
|
|
}
|
2013-09-16 05:07:45 +00:00
|
|
|
char charValue = (char)value;
|
|
|
|
|
2013-09-17 16:05:49 +00:00
|
|
|
asciiString += charValue;
|
2013-09-16 05:07:45 +00:00
|
|
|
|
|
|
|
value = stream.readUint16LE();
|
2013-09-15 19:55:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (charOverflowWarning) {
|
2013-10-01 22:57:32 +00:00
|
|
|
warning("UTF-16 is not supported. Characters greater than 255 are replaced with '?'");
|
2013-09-15 19:55:37 +00:00
|
|
|
}
|
|
|
|
|
2013-09-17 16:05:49 +00:00
|
|
|
return asciiString;
|
2013-09-15 19:55:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StringManager::TextStyle StringManager::getTextStyle(uint stringNumber) {
|
|
|
|
return _inGameText[stringNumber].fragments.front().style;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace ZVision
|