Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

maketext.cpp

/* 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.
 *
 * Additional copyright for this file:
 * Copyright (C) 1994-1998 Revolution Software Ltd.
 *
 * 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/sword2/maketext.cpp $
 * $Id: maketext.cpp 30944 2008-02-23 22:50:18Z sev $
 */

// 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/system.h"

#include "sword2/sword2.h"
#include "sword2/defs.h"
#include "sword2/header.h"
#include "sword2/logic.h"
#include "sword2/maketext.h"
#include "sword2/resman.h"
#include "sword2/screen.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

Generated by  Doxygen 1.6.0   Back to index