scummvm/engines/director/fonts.cpp
2021-12-26 18:48:43 +01:00

440 lines
12 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/substream.h"
#include "graphics/macgui/macfontmanager.h"
#include "graphics/macgui/macwindowmanager.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/util.h"
namespace Director {
void Cast::loadFontMap(Common::SeekableReadStreamEndian &stream) {
if (stream.size() == 0)
return;
debugC(2, kDebugLoading, "****** Loading FontMap VWFM");
uint16 count = stream.readUint16();
uint32 offset = (count * 2) + 2;
uint32 currentRawPosition = offset;
for (uint16 i = 0; i < count; i++) {
uint16 id = stream.readUint16();
uint32 positionInfo = stream.pos();
stream.seek(currentRawPosition);
uint16 size = stream.readByte();
Common::String font;
for (uint16 k = 0; k < size; k++) {
font += stream.readByte();
}
// Map cast font ID to window manager font ID
FontMapEntry *entry = new FontMapEntry;
entry->toFont = _vm->_wm->_fontMan->registerFontName(font, id);
_fontMap[id] = entry;
debugC(3, kDebugLoading, "Cast::loadFontMap: Mapping font %d (%s) to %d", id, font.c_str(), _fontMap[id]->toFont);
currentRawPosition = stream.pos();
stream.seek(positionInfo);
}
}
void Cast::loadFontMapV4(Common::SeekableReadStreamEndian &stream) {
if (stream.size() == 0)
return;
debugC(2, kDebugLoading, "****** Loading FontMap Fmap");
uint32 mapLength = stream.readUint32();
/* uint32 namesLength = */ stream.readUint32();
uint32 bodyStart = stream.pos();
uint32 namesStart = bodyStart + mapLength;
/* uint32 unk1 = */ stream.readUint32();
/* uint32 unk2 = */ stream.readUint32();
uint32 entriesUsed = stream.readUint32();
/* uint32 entriesTotal = */ stream.readUint32();
/* uint32 unk3 = */ stream.readUint32();
/* uint32 unk4 = */ stream.readUint32();
/* uint32 unk5 = */ stream.readUint32();
for (uint32 i = 0; i < entriesUsed; i++) {
uint32 nameOffset = stream.readUint32();
uint32 returnPos = stream.pos();
stream.seek(namesStart + nameOffset);
uint32 nameLength = stream.readUint32();
Common::String name = stream.readString(0, nameLength);
stream.seek(returnPos);
Common::Platform platform = platformFromID(stream.readUint16());
uint16 id = stream.readUint16();
// Map cast font ID to window manager font ID
FontMapEntry *entry = new FontMapEntry;
if (platform == Common::kPlatformWindows && _fontXPlatformMap.contains(name)) {
FontXPlatformInfo *xinfo = _fontXPlatformMap[name];
entry->toFont = _vm->_wm->_fontMan->registerFontName(xinfo->toFont, id);
entry->remapChars = xinfo->remapChars;
entry->sizeMap = xinfo->sizeMap;
} else {
entry->toFont = _vm->_wm->_fontMan->registerFontName(name, id);
}
_fontMap[id] = entry;
debugC(3, kDebugLoading, "Cast::loadFontMapV4: Mapping %s font %d (%s) to %d", getPlatformAbbrev(platform), id, name.c_str(), _fontMap[id]->toFont);
}
}
enum FXmpTokenType {
FXMP_TOKEN_WORD,
FXMP_TOKEN_INT,
FXMP_TOKEN_STRING,
FXMP_TOKEN_COLON,
FXMP_TOKEN_ARROW,
FXMP_TOKEN_NEWLINE,
FXMP_TOKEN_EOF,
FXMP_TOKEN_ERROR
};
const char *FXmpTokenTypeStrings[] = {
"WORD",
"INT",
"STRING",
"COLON",
"ARROW",
"NEWLINE",
"EOF",
"ERROR"
};
struct FXmpToken {
FXmpTokenType type;
Common::String str;
};
FXmpToken readFXmpToken(Common::SeekableReadStreamEndian &stream) {
FXmpToken res;
res.type = FXMP_TOKEN_ERROR;
res.str = "";
char ch = stream.readByte();
// skip non-newline whitespace
while (!stream.eos() && (ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f')) {
ch = stream.readByte();
}
if (stream.eos()) {
res.type = FXMP_TOKEN_EOF;
return res;
}
// skip comment
bool foundComment = false;
if (ch == ';') {
foundComment = true;
} else if (ch == '-') {
ch = stream.readByte();
if (stream.eos()) {
res.type = FXMP_TOKEN_ERROR;
warning("BUILDBOT: readFXmpToken: Expected '-' but got EOF");
return res;
}
if (ch != '-') {
res.type = FXMP_TOKEN_ERROR;
warning("BUILDBOT: readFXmpToken: Expected '-' but got '%c'", ch);
return res;
}
foundComment = true;
}
if (foundComment) {
while (!stream.eos() && ch != '\r') {
ch = stream.readByte();
}
if (stream.eos()) {
res.type = FXMP_TOKEN_EOF;
return res;
}
}
if (Common::isAlpha(ch)) {
res.type = FXMP_TOKEN_WORD;
do {
res.str += ch;
ch = stream.readByte();
} while (!stream.eos() && Common::isAlpha(ch));
if (!stream.eos()) {
stream.seek(-1, SEEK_CUR);
}
} else if (Common::isDigit(ch)) {
res.type = FXMP_TOKEN_INT;
do {
res.str += ch;
ch = stream.readByte();
} while (!stream.eos() && Common::isDigit(ch));
if (!stream.eos()) {
stream.seek(-1, SEEK_CUR);
}
} else if (ch == '"') {
res.type = FXMP_TOKEN_STRING;
ch = stream.readByte();
do {
res.str += ch;
ch = stream.readByte();
} while (!stream.eos() && ch != '"');
if (stream.eos()) {
res.type = FXMP_TOKEN_ERROR;
warning("BUILDBOT: readFXmpToken: Expected '\"' but got EOF");
}
} else if (ch == ':') {
res.type = FXMP_TOKEN_COLON;
res.str += ch;
} else if (ch == '=') {
res.str += ch;
ch = stream.readByte();
if (stream.eos()) {
res.type = FXMP_TOKEN_ERROR;
warning("BUILDBOT: readFXmpToken: Expected '>' but got EOF");
} else {
res.str += ch;
if (ch == '>') {
res.type = FXMP_TOKEN_ARROW;
} else {
res.type = FXMP_TOKEN_ERROR;
warning("BUILDBOT: readFXmpToken: Expected '>' but got '%c'", ch);
}
}
} else if (ch == '\r') {
res.type = FXMP_TOKEN_NEWLINE;
res.str += ch;
ch = stream.readByte();
if (!stream.eos()) {
if (ch == '\n') {
res.str += ch;
} else {
stream.seek(-1, SEEK_CUR);
}
}
} else {
res.type = FXMP_TOKEN_ERROR;
warning("BUILDBOT: readFXmpToken: Unexpected '%c'", ch);
}
return res;
}
void Cast::loadFXmp(Common::SeekableReadStreamEndian &stream) {
debugC(2, kDebugLoading, "****** Loading cross-platform font map FXmp");
while (readFXmpLine(stream)) {}
}
bool Cast::readFXmpLine(Common::SeekableReadStreamEndian &stream) {
// return true to keep reading after this line, false to stop
FXmpToken tok = readFXmpToken(stream);
if (tok.type == FXMP_TOKEN_NEWLINE) {
return true;
}
if (tok.type == FXMP_TOKEN_EOF) {
return false;
}
// from
Common::Platform fromPlatform;
if (tok.type != FXMP_TOKEN_WORD) {
warning("BUILDBOT: Cast::readFXmpLine: Expected WORD, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
if (tok.str.equalsIgnoreCase("Mac")) {
fromPlatform = Common::kPlatformMacintosh;
} else if (tok.str.equalsIgnoreCase("Win")) {
fromPlatform = Common::kPlatformWindows;
} else {
warning("BUILDBOT: Cast::readFXmpLine: Expected 'Mac' or 'Win', got '%s'", tok.str.c_str());
return false;
}
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_COLON) {
warning("BUILDBOT: Cast::readFXmpLine: Expected COLON, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
Common::String fromFont;
tok = readFXmpToken(stream);
if (tok.type == FXMP_TOKEN_WORD || tok.type == FXMP_TOKEN_STRING) {
fromFont = tok.str;
tok = readFXmpToken(stream);
}
// arrow
if (tok.type != FXMP_TOKEN_ARROW) {
warning("BUILDBOT: Cast::readFXmpLine: Expected ARROW, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
// to
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_WORD) {
warning("BUILDBOT: Cast::readFXmpLine: Expected WORD, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
if (fromPlatform == Common::kPlatformMacintosh && !tok.str.equalsIgnoreCase("Win")) {
warning("BUILDBOT: Cast::readFXmpLine: Expected 'Win', got '%s'", tok.str.c_str());
return false;
}
if (fromPlatform == Common::kPlatformWindows && !tok.str.equalsIgnoreCase("Mac")) {
warning("BUILDBOT: Cast::readFXmpLine: Expected 'Mac', got '%s'", tok.str.c_str());
return false;
}
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_COLON) {
warning("BUILDBOT: Cast::readFXmpLine: Expected COLON, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
if (fromFont.empty()) {
// character mappings
tok = readFXmpToken(stream);
while (tok.type != FXMP_TOKEN_NEWLINE && tok.type != FXMP_TOKEN_EOF) {
if (tok.type != FXMP_TOKEN_INT) {
warning("BUILDBOT: Cast::readFXmpLine: Expected INT, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
byte fromChar = atoi(tok.str.c_str());
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_ARROW) {
warning("BUILDBOT: Cast::readFXmpLine: Expected ARROW, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_INT) {
warning("BUILDBOT: Cast::readFXmpLine: Expected INT, got %s", FXmpTokenTypeStrings[tok.type]);
return false;
}
byte toChar = atoi(tok.str.c_str());
if (fromPlatform == Common::kPlatformMacintosh) {
_macCharsToWin[fromChar] = toChar;
debugC(3, kDebugLoading, "Cast::readFXmpLine: Mapping Mac char %d to Win char %d", fromChar, toChar);
} else {
_winCharsToMac[fromChar] = toChar;
debugC(3, kDebugLoading, "Cast::readFXmpLine: Mapping Win char %d to Mac char %d", fromChar, toChar);
}
tok = readFXmpToken(stream);
}
} else {
// font mapping
FontXPlatformInfo *info = new FontXPlatformInfo;
// to font
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_WORD && tok.type != FXMP_TOKEN_STRING) {
warning("BUILDBOT: Cast::readFXmpLine: Expected WORD or STRING, got %s", FXmpTokenTypeStrings[tok.type]);
delete info;
return false;
}
info->toFont = tok.str;
tok = readFXmpToken(stream);
// remap characters?
info->remapChars = true;
if (tok.type == FXMP_TOKEN_WORD) {
if (!tok.str.equalsIgnoreCase("Map")) {
warning("BUILDBOT: Cast::readFXmpLine: Expected 'Map', got '%s'", tok.str.c_str());
delete info;
return false;
}
tok = readFXmpToken(stream);
if (tok.str.equalsIgnoreCase("All")) {
info->remapChars = true;
} else if (tok.str.equalsIgnoreCase("None")) {
info->remapChars = false;
} else {
warning("BUILDBOT: Cast::readFXmpLine: Expected 'All' or 'None', got '%s'", tok.str.c_str());
delete info;
return false;
}
tok = readFXmpToken(stream);
}
// size mappings
while (tok.type != FXMP_TOKEN_NEWLINE && tok.type != FXMP_TOKEN_EOF) {
if (tok.type != FXMP_TOKEN_INT) {
warning("BUILDBOT: Cast::readFXmpLine: Expected INT, got %s", FXmpTokenTypeStrings[tok.type]);
delete info;
return false;
}
uint16 fromSize = atoi(tok.str.c_str());
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_ARROW) {
warning("BUILDBOT: Cast::readFXmpLine: Expected ARROW, got %s", FXmpTokenTypeStrings[tok.type]);
delete info;
return false;
}
tok = readFXmpToken(stream);
if (tok.type != FXMP_TOKEN_INT) {
warning("BUILDBOT: Cast::readFXmpLine: Expected INT, got %s", FXmpTokenTypeStrings[tok.type]);
delete info;
return false;
}
uint16 toSize = atoi(tok.str.c_str());
info->sizeMap[fromSize] = toSize;
tok = readFXmpToken(stream);
}
// TODO: We should fill _fontXPlatformMap with mappings matching the current platform.
// We only have Mac fonts right now, though, so we'll always use the Win => Mac mappings.
if (fromPlatform == Common::kPlatformWindows) {
_fontXPlatformMap[fromFont] = info;
debugC(3, kDebugLoading, "Cast::readFXmpLine: Mapping Win font '%s' to Mac font '%s'", fromFont.c_str(), info->toFont.c_str());
debugC(4, kDebugLoading, " Remap characters: %d", info->remapChars);
for (FontSizeMap::iterator it = info->sizeMap.begin(); it != info->sizeMap.end(); ++it) {
debugC(4, kDebugLoading, " Mapping size %d to %d", it->_key, it->_value);
}
} else {
delete info;
}
}
return true;
}
} // End of namespace Director