aboutsummaryrefslogtreecommitdiff
path: root/engines/sword2/maketext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sword2/maketext.cpp')
-rw-r--r--engines/sword2/maketext.cpp579
1 files changed, 579 insertions, 0 deletions
diff --git a/engines/sword2/maketext.cpp b/engines/sword2/maketext.cpp
new file mode 100644
index 0000000000..5edaf5e41e
--- /dev/null
+++ b/engines/sword2/maketext.cpp
@@ -0,0 +1,579 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ */
+
+// MAKETEXT - Constructs a single-frame text sprite: returns a handle to a
+// FLOATING memory block containing the sprite, given a
+// null-terminated string, max width allowed, pen colour and
+// pointer to required character set.
+//
+// NB 1) The routine does not create a standard file header or
+// an anim header for the text sprite - the data simply begins
+// with the frame header.
+//
+// NB 2) If pen colour is zero, it copies the characters into
+// the sprite without remapping the colours.
+// ie. It can handle both the standard 2-colour font for speech
+// and any multicoloured fonts for control panels, etc.
+//
+// Based on textsprt.c as used for Broken Sword 1, but updated
+// for new system by JEL on 9oct96 and updated again (for font
+// as a resource) on 5dec96.
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/maketext.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+#define MAX_LINES 30 // max character lines in output sprite
+
+#define BORDER_COL 200 // source colour for character border (only
+ // needed for remapping colours)
+#define LETTER_COL 193 // source colour for bulk of character ( " )
+#define SPACE ' '
+#define FIRST_CHAR SPACE // first character in character set
+#define LAST_CHAR 255 // last character in character set
+#define DUD 64 // the first "chequered flag" (dud) symbol in
+ // our character set is in the '@' position
+
+/**
+ * This function creates a new text sprite. The sprite data contains a
+ * FrameHeader, but not a standard file header.
+ *
+ * @param sentence pointer to a null-terminated string
+ * @param maxWidth the maximum allowed text sprite width in pixels
+ * @param pen the text colour, or zero to use the source colours
+ * @param fontRes the font resource id
+ * @param border the border colour; black by default
+ * @return a handle to a floating memory block containing the text sprite
+ * @note The sentence must contain no leading, trailing or extra spaces.
+ * Out-of-range characters in the string are replaced by a special
+ * error-signal character (chequered flag)
+ */
+
+byte *FontRenderer::makeTextSprite(byte *sentence, uint16 maxWidth, uint8 pen, uint32 fontRes, uint8 border) {
+ debug(5, "makeTextSprite(\"%s\", maxWidth=%u)", sentence, maxWidth);
+
+ _borderPen = border;
+
+ // Line- and character spacing are hard-wired, rather than being part
+ // of the resource.
+
+ if (fontRes == _vm->_speechFontId) {
+ _lineSpacing = -6;
+ _charSpacing = -3;
+ } else if (fontRes == CONSOLE_FONT_ID) {
+ _lineSpacing = 0;
+ _charSpacing = 1;
+ } else {
+ _lineSpacing = 0;
+ _charSpacing = 0;
+ }
+
+ // Allocate memory for array of lineInfo structures
+
+ byte *line = (byte *)malloc(MAX_LINES * sizeof(LineInfo));
+
+ // Get details of sentence breakdown into array of LineInfo structures
+ // and get the number of lines involved
+
+ uint16 noOfLines = analyseSentence(sentence, maxWidth, fontRes, (LineInfo *)line);
+
+ // Construct the sprite based on the info gathered - returns floating
+ // mem block
+
+ byte *textSprite = buildTextSprite(sentence, fontRes, pen, (LineInfo *)line, noOfLines);
+
+ free(line);
+ return textSprite;
+}
+
+uint16 FontRenderer::analyseSentence(byte *sentence, uint16 maxWidth, uint32 fontRes, LineInfo *line) {
+ // joinWidth = how much extra space is needed to append a word to a
+ // line. NB. SPACE requires TWICE the '_charSpacing' to join a word
+ // to line
+
+ uint16 joinWidth = charWidth(SPACE, fontRes) + 2 * _charSpacing;
+
+ uint16 lineNo = 0;
+ uint16 pos = 0;
+ bool firstWord = true;
+
+ byte ch;
+
+ do {
+ uint16 wordWidth = 0;
+ uint16 wordLength = 0;
+
+ // Calculate the width of the word.
+
+ ch = sentence[pos++];
+
+ while (ch && ch != SPACE) {
+ wordWidth += charWidth(ch, fontRes) + _charSpacing;
+ wordLength++;
+ ch = sentence[pos++];
+ }
+
+ // Don't include any character spacing at the end of the word.
+ wordWidth -= _charSpacing;
+
+ // 'ch' is now the SPACE or NULL following the word
+ // 'pos' indexes to the position following 'ch'
+
+ if (firstWord) {
+ // This is the first word on the line, so no separating
+ // space is needed.
+
+ line[0].width = wordWidth;
+ line[0].length = wordLength;
+ firstWord = false;
+ } else {
+ // See how much extra space this word will need to
+ // fit on current line (with a separating space
+ // character - also overlapped)
+
+ uint16 spaceNeeded = joinWidth + wordWidth;
+
+ if (line[lineNo].width + spaceNeeded <= maxWidth) {
+ // The word fits on this line.
+ line[lineNo].width += spaceNeeded;
+ line[lineNo].length += (1 + wordLength);
+ } else {
+ // The word spills over to the next line, i.e.
+ // no separating space.
+
+ lineNo++;
+
+ assert(lineNo < MAX_LINES);
+
+ line[lineNo].width = wordWidth;
+ line[lineNo].length = wordLength;
+ }
+ }
+ } while (ch);
+
+ return lineNo + 1;
+}
+
+/**
+ * This function creates a new text sprite in a movable memory block. It must
+ * be locked before use, i.e. lock, draw sprite, unlock/free. The sprite data
+ * contains a FrameHeader, but not a standard file header.
+ *
+ * @param sentence pointer to a null-terminated string
+ * @param fontRes the font resource id
+ * @param pen the text colour, or zero to use the source colours
+ * @param line array of LineInfo structures, created by analyseSentence()
+ * @param noOfLines the number of lines, i.e. the number of elements in 'line'
+ * @return a handle to a floating memory block containing the text sprite
+ * @note The sentence must contain no leading, trailing or extra spaces.
+ * Out-of-range characters in the string are replaced by a special
+ * error-signal character (chequered flag)
+ */
+
+byte *FontRenderer::buildTextSprite(byte *sentence, uint32 fontRes, uint8 pen, LineInfo *line, uint16 noOfLines) {
+ uint16 i;
+
+ // Find the width of the widest line in the output text
+
+ uint16 spriteWidth = 0;
+
+ for (i = 0; i < noOfLines; i++)
+ if (line[i].width > spriteWidth)
+ spriteWidth = line[i].width;
+
+ // Find the total height of the text sprite: the total height of the
+ // text lines, plus the total height of the spacing between them.
+
+ uint16 char_height = charHeight(fontRes);
+ uint16 spriteHeight = char_height * noOfLines + _lineSpacing * (noOfLines - 1);
+
+ // Allocate memory for the text sprite
+
+ uint32 sizeOfSprite = spriteWidth * spriteHeight;
+ byte *textSprite = (byte *)malloc(FrameHeader::size() + sizeOfSprite);
+
+ // At this stage, textSprite points to an unmovable memory block. Set
+ // up the frame header.
+
+ FrameHeader frame_head;
+
+ frame_head.compSize = 0;
+ frame_head.width = spriteWidth;
+ frame_head.height = spriteHeight;
+
+ frame_head.write(textSprite);
+
+ debug(4, "Text sprite size: %ux%u", spriteWidth, spriteHeight);
+
+ // Clear the entire sprite to make it transparent.
+
+ byte *linePtr = textSprite + FrameHeader::size();
+ memset(linePtr, 0, sizeOfSprite);
+
+ byte *charSet = _vm->_resman->openResource(fontRes);
+
+ // Build the sprite, one line at a time
+
+ uint16 pos = 0;
+
+ for (i = 0; i < noOfLines; i++) {
+ // Center each line
+ byte *spritePtr = linePtr + (spriteWidth - line[i].width) / 2;
+
+ // copy the sprite for each character in this line to the
+ // text sprite and inc the sprite ptr by the character's
+ // width minus the 'overlap'
+
+ for (uint j = 0; j < line[i].length; j++) {
+ byte *charPtr = findChar(sentence[pos++], charSet);
+
+ frame_head.read(charPtr);
+
+ assert(frame_head.height == char_height);
+ copyChar(charPtr, spritePtr, spriteWidth, pen);
+ spritePtr += frame_head.width + _charSpacing;
+ }
+
+ // Skip space at end of last word in this line
+ pos++;
+
+ linePtr += (char_height + _lineSpacing) * spriteWidth;
+ }
+
+ _vm->_resman->closeResource(fontRes);
+
+ return textSprite;
+}
+
+/**
+ * @param ch the ASCII code of the character
+ * @param fontRes the font resource id
+ * @return the width of the character
+ */
+
+uint16 FontRenderer::charWidth(byte ch, uint32 fontRes) {
+ byte *charSet = _vm->_resman->openResource(fontRes);
+
+ FrameHeader frame_head;
+
+ frame_head.read(findChar(ch, charSet));
+ _vm->_resman->closeResource(fontRes);
+
+ return frame_head.width;
+}
+
+/**
+ * @param fontRes the font resource id
+ * @return the height of a character sprite
+ * @note All characters in a font are assumed to have the same height, so
+ * there is no need to specify which one to look at.
+ */
+
+// Returns the height of a character sprite, given the character's ASCII code
+// and a pointer to the start of the character set.
+
+uint16 FontRenderer::charHeight(uint32 fontRes) {
+ byte *charSet = _vm->_resman->openResource(fontRes);
+
+ FrameHeader frame_head;
+
+ frame_head.read(findChar(FIRST_CHAR, charSet));
+ _vm->_resman->closeResource(fontRes);
+
+ return frame_head.height;
+}
+
+/**
+ * @param ch the ASCII code of the character to find
+ * @param charSet pointer to the start of the character set
+ * @return pointer to the requested character or, if it's out of range, the
+ * 'dud' character (chequered flag)
+ */
+
+byte *FontRenderer::findChar(byte ch, byte *charSet) {
+ if (ch < FIRST_CHAR)
+ ch = DUD;
+ return _vm->fetchFrameHeader(charSet, ch - FIRST_CHAR);
+}
+
+/**
+ * Copies a character sprite to the sprite buffer.
+ * @param charPtr pointer to the character sprite
+ * @param spritePtr pointer to the sprite buffer
+ * @param spriteWidth the width of the character
+ * @param pen If zero, copy the data directly. Otherwise remap the
+ * sprite's colours from BORDER_COL to _borderPen and from
+ * LETTER_COL to pen.
+ */
+
+void FontRenderer::copyChar(byte *charPtr, byte *spritePtr, uint16 spriteWidth, uint8 pen) {
+ FrameHeader frame;
+
+ frame.read(charPtr);
+
+ byte *source = charPtr + FrameHeader::size();
+ byte *rowPtr = spritePtr;
+
+ for (uint i = 0; i < frame.height; i++) {
+ byte *dest = rowPtr;
+
+ if (pen) {
+ // Use the specified colours
+ for (uint j = 0; j < frame.width; j++) {
+ switch (*source++) {
+ case LETTER_COL:
+ *dest = pen;
+ break;
+ case BORDER_COL:
+ // Don't do a border pixel if there's
+ // already a bit of another character
+ // underneath (for overlapping!)
+ if (!*dest)
+ *dest = _borderPen;
+ break;
+ default:
+ // Do nothing if source pixel is zero,
+ // ie. transparent
+ break;
+ }
+ dest++;
+ }
+ } else {
+ // Pen is zero, so just copy character sprites
+ // directly into text sprite without remapping colours.
+ // Apparently overlapping is never considered here?
+ memcpy(dest, source, frame.width);
+ source += frame.width;
+ }
+ rowPtr += spriteWidth;
+ }
+}
+
+// Distance to keep speech text from edges of screen
+#define TEXT_MARGIN 12
+
+/**
+ * Creates a text bloc in the list and returns the bloc number. The list of
+ * blocs is read and blitted at render time. Choose alignment type
+ * RDSPR_DISPLAYALIGN or 0
+ */
+
+uint32 FontRenderer::buildNewBloc(byte *ascii, int16 x, int16 y, uint16 width, uint8 pen, uint32 type, uint32 fontRes, uint8 justification) {
+ uint32 i = 0;
+
+ while (i < MAX_text_blocs && _blocList[i].text_mem)
+ i++;
+
+ assert(i < MAX_text_blocs);
+
+ // Create and position the sprite
+
+ _blocList[i].text_mem = makeTextSprite(ascii, width, pen, fontRes);
+
+ // 'NO_JUSTIFICATION' means print sprite with top-left at (x,y)
+ // without margin checking - used for debug text
+
+ if (justification != NO_JUSTIFICATION) {
+ FrameHeader frame_head;
+
+ frame_head.read(_blocList[i].text_mem);
+
+ switch (justification) {
+ case POSITION_AT_CENTRE_OF_BASE:
+ // This one is always used for SPEECH TEXT; possibly
+ // also for pointer text
+ x -= (frame_head.width / 2);
+ y -= frame_head.height;
+ break;
+ case POSITION_AT_CENTRE_OF_TOP:
+ x -= (frame_head.width / 2);
+ break;
+ case POSITION_AT_LEFT_OF_TOP:
+ // The given coords are already correct for this!
+ break;
+ case POSITION_AT_RIGHT_OF_TOP:
+ x -= frame_head.width;
+ break;
+ case POSITION_AT_LEFT_OF_BASE:
+ y -= frame_head.height;
+ break;
+ case POSITION_AT_RIGHT_OF_BASE:
+ x -= frame_head.width;
+ y -= frame_head.height;
+ break;
+ case POSITION_AT_LEFT_OF_CENTRE:
+ y -= (frame_head.height / 2);
+ break;
+ case POSITION_AT_RIGHT_OF_CENTRE:
+ x -= frame_head.width;
+ y -= (frame_head.height) / 2;
+ break;
+ }
+
+ // Ensure text sprite is a few pixels inside the visible screen
+ // remember - it's RDSPR_DISPLAYALIGN
+
+ uint16 text_left_margin = TEXT_MARGIN;
+ uint16 text_right_margin = 640 - TEXT_MARGIN - frame_head.width;
+ uint16 text_top_margin = TEXT_MARGIN;
+ uint16 text_bottom_margin = 400 - TEXT_MARGIN - frame_head.height;
+
+ // Move if too far left or too far right
+
+ if (x < text_left_margin)
+ x = text_left_margin;
+ else if (x > text_right_margin)
+ x = text_right_margin;
+
+ // Move if too high or too low
+
+ if (y < text_top_margin)
+ y = text_top_margin;
+ else if (y > text_bottom_margin)
+ y = text_bottom_margin;
+ }
+
+ // The sprite is always uncompressed
+ _blocList[i].type = type | RDSPR_NOCOMPRESSION;
+
+ _blocList[i].x = x;
+ _blocList[i].y = y;
+
+ return i + 1;
+}
+
+/**
+ * Called by buildDisplay()
+ */
+
+void FontRenderer::printTextBlocs() {
+ for (uint i = 0; i < MAX_text_blocs; i++) {
+ if (_blocList[i].text_mem) {
+ FrameHeader frame_head;
+ SpriteInfo spriteInfo;
+
+ frame_head.read(_blocList[i].text_mem);
+
+ spriteInfo.x = _blocList[i].x;
+ spriteInfo.y = _blocList[i].y;
+ spriteInfo.w = frame_head.width;
+ spriteInfo.h = frame_head.height;
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.type = _blocList[i].type;
+ spriteInfo.blend = 0;
+ spriteInfo.data = _blocList[i].text_mem + FrameHeader::size();
+ spriteInfo.colourTable = 0;
+
+ uint32 rv = _vm->_screen->drawSprite(&spriteInfo);
+ if (rv)
+ error("Driver Error %.8x in printTextBlocs", rv);
+ }
+ }
+}
+
+void FontRenderer::killTextBloc(uint32 bloc_number) {
+ bloc_number--;
+ free(_blocList[bloc_number].text_mem);
+ _blocList[bloc_number].text_mem = NULL;
+}
+
+// Resource 3258 contains text from location script for 152 (install, save &
+// restore text, etc)
+
+#define TEXT_RES 3258
+
+// Local line number of "save" (actor no. 1826)
+
+#define SAVE_LINE_NO 1
+
+void Sword2Engine::initialiseFontResourceFlags() {
+ byte *textFile = _resman->openResource(TEXT_RES);
+
+ // If language is Polish or Finnish it requires alternate fonts.
+ // Otherwise, use regular fonts
+
+ // "tallenna" Finnish for "save"
+ // "zapisz" Polish for "save"
+
+ // Get the text line (& skip the 2 chars containing the wavId)
+ char *textLine = (char *)fetchTextLine(textFile, SAVE_LINE_NO) + 2;
+
+ if (strcmp(textLine, "tallenna") == 0)
+ initialiseFontResourceFlags(FINNISH_TEXT);
+ else if (strcmp(textLine, "zapisz") == 0)
+ initialiseFontResourceFlags(POLISH_TEXT);
+ else
+ initialiseFontResourceFlags(DEFAULT_TEXT);
+
+ // Get the game name for the windows application
+
+ // According to the GetNameFunction(), which was never called and has
+ // therefore been removed, the name of the game is:
+ //
+ // ENGLISH: "Broken Sword II"
+ // AMERICAN: "Circle of Blood II"
+ // GERMAN: "Baphomet's Fluch II"
+ // default: "Some game or other, part 86"
+ //
+ // But we get it from the text resource instead.
+
+ if (_logic->readVar(DEMO))
+ textLine = (char *)fetchTextLine(textFile, 451) + 2;
+ else
+ textLine = (char *)fetchTextLine(textFile, 54) + 2;
+
+ _system->setWindowCaption(textLine);
+ _resman->closeResource(TEXT_RES);
+}
+
+/**
+ * Called from initialiseFontResourceFlags(), and also from console.cpp
+ */
+
+void Sword2Engine::initialiseFontResourceFlags(uint8 language) {
+ switch (language) {
+ case FINNISH_TEXT:
+ _speechFontId = FINNISH_SPEECH_FONT_ID;
+ _controlsFontId = FINNISH_CONTROLS_FONT_ID;
+ _redFontId = FINNISH_RED_FONT_ID;
+ break;
+ case POLISH_TEXT:
+ _speechFontId = POLISH_SPEECH_FONT_ID;
+ _controlsFontId = POLISH_CONTROLS_FONT_ID;
+ _redFontId = POLISH_RED_FONT_ID;
+ break;
+ default:
+ _speechFontId = ENGLISH_SPEECH_FONT_ID;
+ _controlsFontId = ENGLISH_CONTROLS_FONT_ID;
+ _redFontId = ENGLISH_RED_FONT_ID;
+ break;
+ }
+}
+
+} // End of namespace Sword2