1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 // MAKETEXT - Constructs a single-frame text sprite: returns a handle to a
26 // FLOATING memory block containing the sprite, given a
27 // null-terminated string, max width allowed, pen color and
28 // pointer to required character set.
29 //
30 // NB 1) The routine does not create a standard file header or
31 // an anim header for the text sprite - the data simply begins
32 // with the frame header.
33 //
34 // NB 2) If pen color is zero, it copies the characters into
35 // the sprite without remapping the colors.
36 // ie. It can handle both the standard 2-color font for speech
37 // and any multicolored fonts for control panels, etc.
38 //
39 // Based on textsprt.c as used for Broken Sword 1, but updated
40 // for new system by JEL on 9oct96 and updated again (for font
41 // as a resource) on 5dec96.
42
43
44 #include "common/system.h"
45 #include "common/textconsole.h"
46
47 #include "sword2/sword2.h"
48 #include "sword2/defs.h"
49 #include "sword2/header.h"
50 #include "sword2/logic.h"
51 #include "sword2/maketext.h"
52 #include "sword2/resman.h"
53 #include "sword2/screen.h"
54
55 namespace Sword2 {
56
57 #define MAX_LINES 30 // max character lines in output sprite
58
59 #define BORDER_COL 200 // source color for character border (only
60 // needed for remapping colors)
61
62 #define LETTER_COL 193 // source color for bulk of character ( " )
63 #define LETTER_COL_PSX1 33
64 #define LETTER_COL_PSX2 34
65 #define SPACE ' '
66 #define FIRST_CHAR SPACE // first character in character set
67 #define LAST_CHAR 255 // last character in character set
68 #define DUD 64 // the first "chequered flag" (dud) symbol in
69 // our character set is in the '@' position
70
71 /**
72 * This function creates a new text sprite. The sprite data contains a
73 * FrameHeader, but not a standard file header.
74 *
75 * @param sentence pointer to a null-terminated string
76 * @param maxWidth the maximum allowed text sprite width in pixels
77 * @param pen the text color, or zero to use the source colors
78 * @param fontRes the font resource id
79 * @param border the border color; black by default
80 * @return a handle to a floating memory block containing the text sprite
81 * @note The sentence must contain no leading, trailing or extra spaces.
82 * Out-of-range characters in the string are replaced by a special
83 * error-signal character (chequered flag)
84 */
85
makeTextSprite(const byte * sentence,uint16 maxWidth,uint8 pen,uint32 fontRes,uint8 border)86 byte *FontRenderer::makeTextSprite(const byte *sentence, uint16 maxWidth, uint8 pen, uint32 fontRes, uint8 border) {
87 debug(5, "makeTextSprite(\"%s\", maxWidth=%u)", sentence, maxWidth);
88
89 _borderPen = border;
90
91 // Line- and character spacing are hard-wired, rather than being part
92 // of the resource.
93
94 if (fontRes == _vm->_speechFontId) {
95 if (Sword2Engine::isPsx())
96 _lineSpacing = -4; // Text would be unreadable with psx font if linespacing is higher
97 else
98 _lineSpacing = -6;
99 _charSpacing = -3;
100 } else if (fontRes == CONSOLE_FONT_ID) {
101 _lineSpacing = 0;
102 _charSpacing = 1;
103 } else {
104 _lineSpacing = 0;
105 _charSpacing = 0;
106 }
107
108 // Allocate memory for array of lineInfo structures
109
110 byte *line = (byte *)malloc(MAX_LINES * sizeof(LineInfo));
111
112 // Get details of sentence breakdown into array of LineInfo structures
113 // and get the number of lines involved
114
115 uint16 noOfLines = analyzeSentence(sentence, maxWidth, fontRes, (LineInfo *)line);
116
117 // Construct the sprite based on the info gathered - returns floating
118 // mem block
119
120 byte *textSprite = buildTextSprite(sentence, fontRes, pen, (LineInfo *)line, noOfLines);
121
122 free(line);
123 return textSprite;
124 }
125
analyzeSentence(const byte * sentence,uint16 maxWidth,uint32 fontRes,LineInfo * line)126 uint16 FontRenderer::analyzeSentence(const byte *sentence, uint16 maxWidth, uint32 fontRes, LineInfo *line) {
127 // joinWidth = how much extra space is needed to append a word to a
128 // line. NB. SPACE requires TWICE the '_charSpacing' to join a word
129 // to line
130
131 uint16 joinWidth = charWidth(SPACE, fontRes) + 2 * _charSpacing;
132
133 uint16 lineNo = 0;
134 uint16 pos = 0;
135 bool firstWord = true;
136
137 byte ch;
138
139 do {
140 uint16 wordWidth = 0;
141 uint16 wordLength = 0;
142
143 // Calculate the width of the word.
144
145 ch = sentence[pos++];
146
147 while (ch && ch != SPACE) {
148 wordWidth += charWidth(ch, fontRes) + _charSpacing;
149 wordLength++;
150 ch = sentence[pos++];
151 }
152
153 // Don't include any character spacing at the end of the word.
154 wordWidth -= _charSpacing;
155
156 // 'ch' is now the SPACE or NULL following the word
157 // 'pos' indexes to the position following 'ch'
158
159 if (firstWord) {
160 // This is the first word on the line, so no separating
161 // space is needed.
162
163 line[0].width = wordWidth;
164 line[0].length = wordLength;
165 firstWord = false;
166 } else {
167 // See how much extra space this word will need to
168 // fit on current line (with a separating space
169 // character - also overlapped)
170
171 uint16 spaceNeeded = joinWidth + wordWidth;
172
173 if (line[lineNo].width + spaceNeeded <= maxWidth) {
174 // The word fits on this line.
175 line[lineNo].width += spaceNeeded;
176 line[lineNo].length += (1 + wordLength);
177 } else {
178 // The word spills over to the next line, i.e.
179 // no separating space.
180
181 lineNo++;
182
183 assert(lineNo < MAX_LINES);
184
185 line[lineNo].width = wordWidth;
186 line[lineNo].length = wordLength;
187 }
188 }
189 } while (ch);
190
191 return lineNo + 1;
192 }
193
194 /**
195 * This function creates a new text sprite in a movable memory block. It must
196 * be locked before use, i.e. lock, draw sprite, unlock/free. The sprite data
197 * contains a FrameHeader, but not a standard file header.
198 *
199 * @param sentence pointer to a null-terminated string
200 * @param fontRes the font resource id
201 * @param pen the text color, or zero to use the source colors
202 * @param line array of LineInfo structures, created by analyzeSentence()
203 * @param noOfLines the number of lines, i.e. the number of elements in 'line'
204 * @return a handle to a floating memory block containing the text sprite
205 * @note The sentence must contain no leading, trailing or extra spaces.
206 * Out-of-range characters in the string are replaced by a special
207 * error-signal character (chequered flag)
208 */
209
buildTextSprite(const byte * sentence,uint32 fontRes,uint8 pen,LineInfo * line,uint16 noOfLines)210 byte *FontRenderer::buildTextSprite(const byte *sentence, uint32 fontRes, uint8 pen, LineInfo *line, uint16 noOfLines) {
211 uint16 i;
212
213 // Find the width of the widest line in the output text
214
215 uint16 spriteWidth = 0;
216
217 for (i = 0; i < noOfLines; i++)
218 if (line[i].width > spriteWidth)
219 spriteWidth = line[i].width;
220
221
222 // Check that text sprite has even horizontal resolution in PSX version
223 // (needed to work around a problem in some sprites, which reports an odd
224 // number as horiz resolution, but then have the next even number as true width)
225 if (Sword2Engine::isPsx())
226 spriteWidth = (spriteWidth % 2) ? spriteWidth + 1 : spriteWidth;
227
228 // Find the total height of the text sprite: the total height of the
229 // text lines, plus the total height of the spacing between them.
230
231 uint16 char_height = charHeight(fontRes);
232 uint16 spriteHeight = char_height * noOfLines + _lineSpacing * (noOfLines - 1);
233
234 // Allocate memory for the text sprite
235
236 uint32 sizeOfSprite = spriteWidth * spriteHeight;
237 byte *textSprite = (byte *)malloc(FrameHeader::size() + sizeOfSprite);
238
239 // At this stage, textSprite points to an unmovable memory block. Set
240 // up the frame header.
241
242 FrameHeader frame_head;
243
244 frame_head.compSize = 0;
245 frame_head.width = spriteWidth;
246 frame_head.height = spriteHeight;
247
248 // Normally for PSX frame header we double the height
249 // of the sprite artificially to regain correct aspect
250 // ratio, but this is an "artificially generated" text
251 // sprite, which gets created with correct aspect, so
252 // fix the height.
253 if (Sword2Engine::isPsx())
254 frame_head.height /= 2;
255
256 frame_head.write(textSprite);
257
258 debug(4, "Text sprite size: %ux%u", spriteWidth, spriteHeight);
259
260 // Clear the entire sprite to make it transparent.
261
262 byte *linePtr = textSprite + FrameHeader::size();
263 memset(linePtr, 0, sizeOfSprite);
264
265 byte *charSet = _vm->_resman->openResource(fontRes);
266
267 // Build the sprite, one line at a time
268
269 uint16 pos = 0;
270
271 for (i = 0; i < noOfLines; i++) {
272 // Center each line
273 byte *spritePtr = linePtr + (spriteWidth - line[i].width) / 2;
274
275 // copy the sprite for each character in this line to the
276 // text sprite and inc the sprite ptr by the character's
277 // width minus the 'overlap'
278
279 for (uint j = 0; j < line[i].length; j++) {
280 byte *charPtr = findChar(sentence[pos++], charSet);
281
282 frame_head.read(charPtr);
283
284 assert(frame_head.height == char_height);
285 copyChar(charPtr, spritePtr, spriteWidth, pen);
286
287 // We must remember to free memory for generated character in psx,
288 // as it is extracted differently than pc version (copyed from a
289 // char atlas).
290 if (Sword2Engine::isPsx())
291 free(charPtr);
292
293 spritePtr += frame_head.width + _charSpacing;
294 }
295
296 // Skip space at end of last word in this line
297 pos++;
298
299 if (Sword2Engine::isPsx())
300 linePtr += (char_height / 2 + _lineSpacing) * spriteWidth;
301 else
302 linePtr += (char_height + _lineSpacing) * spriteWidth;
303 }
304
305 _vm->_resman->closeResource(fontRes);
306
307 return textSprite;
308 }
309
310 /**
311 * @param ch the ASCII code of the character
312 * @param fontRes the font resource id
313 * @return the width of the character
314 */
315
charWidth(byte ch,uint32 fontRes)316 uint16 FontRenderer::charWidth(byte ch, uint32 fontRes) {
317 byte *charSet = _vm->_resman->openResource(fontRes);
318 byte *charBuf;
319
320 FrameHeader frame_head;
321
322 charBuf = findChar(ch, charSet);
323
324 frame_head.read(charBuf);
325
326 if (Sword2Engine::isPsx())
327 free(charBuf);
328
329 _vm->_resman->closeResource(fontRes);
330
331 return frame_head.width;
332 }
333
334 /**
335 * @param fontRes the font resource id
336 * @return the height of a character sprite
337 * @note All characters in a font are assumed to have the same height, so
338 * there is no need to specify which one to look at.
339 */
340
341 // Returns the height of a character sprite, given the character's ASCII code
342 // and a pointer to the start of the character set.
343
charHeight(uint32 fontRes)344 uint16 FontRenderer::charHeight(uint32 fontRes) {
345 byte *charSet = _vm->_resman->openResource(fontRes);
346 byte *charbuf;
347
348 FrameHeader frame_head;
349
350 charbuf = findChar(FIRST_CHAR, charSet);
351
352 frame_head.read(charbuf);
353
354 if (Sword2Engine::isPsx())
355 free(charbuf);
356
357 _vm->_resman->closeResource(fontRes);
358
359 return frame_head.height;
360 }
361
362 /**
363 * @param ch the ASCII code of the character to find
364 * @param charSet pointer to the start of the character set
365 * @return pointer to the requested character or, if it's out of range, the
366 * 'dud' character (chequered flag)
367 */
368
findChar(byte ch,byte * charSet)369 byte *FontRenderer::findChar(byte ch, byte *charSet) {
370
371 // PSX version doesn't use an animation table to keep all letters,
372 // instead a big sprite (char atlas) is used, and the single char
373 // must be extracted from that.
374
375 if (Sword2Engine::isPsx()) {
376 byte *buffer;
377 PSXFontEntry header;
378 FrameHeader bogusHeader;
379
380 charSet += ResHeader::size() + 2;
381
382 if (ch < FIRST_CHAR)
383 ch = DUD;
384
385 // Read font entry of the corresponding char.
386 header.read(charSet + PSXFontEntry::size() * (ch - 32));
387
388 // We have no such character, generate an empty one
389 // on the fly, size 6x12.
390 if (header.charWidth == 0) {
391
392 // Prepare a "bogus" FrameHeader to be returned with
393 // "empty" character data.
394 bogusHeader.compSize = 0;
395 bogusHeader.width = 6;
396 bogusHeader.height = 12;
397
398 buffer = (byte *)malloc(24 * 3 + FrameHeader::size());
399 memset(buffer, 0, 24 * 3 + FrameHeader::size());
400 bogusHeader.write(buffer);
401
402 return buffer;
403 }
404
405 buffer = (byte *)malloc(FrameHeader::size() + header.charWidth * header.charHeight * 4);
406 byte *tempchar = (byte *)malloc(header.charWidth * header.charHeight);
407
408 // Prepare the "bogus" header to be returned with character
409 bogusHeader.compSize = 0;
410 bogusHeader.width = header.charWidth * 2;
411 bogusHeader.height = header.charHeight;
412
413 // Go to the beginning of char atlas
414 charSet += 2062;
415
416 memset(buffer, 0, FrameHeader::size() + header.charWidth * header.charHeight * 4);
417
418 bogusHeader.write(buffer);
419
420 // Copy and stretch the char into destination buffer
421 for (int idx = 0; idx < header.charHeight; idx++) {
422 memcpy(tempchar + header.charWidth * idx, charSet + header.offset + 128 * (header.skipLines + idx), header.charWidth);
423 }
424
425 for (int line = 0; line < header.charHeight; line++) {
426 for (int col = 0; col < header.charWidth; col++) {
427 *(buffer + FrameHeader::size() + line * bogusHeader.width + col * 2) = *(tempchar + line * header.charWidth + col);
428 *(buffer + FrameHeader::size() + line * bogusHeader.width + col * 2 + 1) = *(tempchar + line * header.charWidth + col);
429 }
430 }
431
432 free(tempchar);
433
434 return buffer;
435
436 } else {
437 if (ch < FIRST_CHAR)
438 ch = DUD;
439 return _vm->fetchFrameHeader(charSet, ch - FIRST_CHAR);
440 }
441 }
442
443 /**
444 * Copies a character sprite to the sprite buffer.
445 * @param charPtr pointer to the character sprite
446 * @param spritePtr pointer to the sprite buffer
447 * @param spriteWidth the width of the character
448 * @param pen If zero, copy the data directly. Otherwise remap the
449 * sprite's colors from BORDER_COL to _borderPen and from
450 * LETTER_COL to pen.
451 */
452
copyChar(byte * charPtr,byte * spritePtr,uint16 spriteWidth,uint8 pen)453 void FontRenderer::copyChar(byte *charPtr, byte *spritePtr, uint16 spriteWidth, uint8 pen) {
454 FrameHeader frame;
455
456 frame.read(charPtr);
457
458 byte *source = charPtr + FrameHeader::size();
459 byte *rowPtr = spritePtr;
460
461 for (uint i = 0; i < frame.height; i++) {
462 byte *dest = rowPtr;
463
464 if (pen) {
465 // Use the specified colors
466 for (uint j = 0; j < frame.width; j++) {
467 switch (*source++) {
468 case 0:
469 // Do nothing if source pixel is zero,
470 // ie. transparent
471 break;
472 case LETTER_COL_PSX1: // Values for colored zone
473 case LETTER_COL_PSX2:
474 case LETTER_COL:
475 *dest = pen;
476 break;
477 case BORDER_COL:
478 default:
479 // Don't do a border pixel if there's
480 // already a bit of another character
481 // underneath (for overlapping!)
482 if (!*dest)
483 *dest = _borderPen;
484 break;
485 }
486 dest++;
487 }
488 } else {
489 // Pen is zero, so just copy character sprites
490 // directly into text sprite without remapping colors.
491 // Apparently overlapping is never considered here?
492 memcpy(dest, source, frame.width);
493 source += frame.width;
494 }
495 rowPtr += spriteWidth;
496 }
497 }
498
499 // Distance to keep speech text from edges of screen
500 #define TEXT_MARGIN 12
501
502 /**
503 * Creates a text bloc in the list and returns the bloc number. The list of
504 * blocs is read and blitted at render time. Choose alignment type
505 * RDSPR_DISPLAYALIGN or 0
506 */
507
buildNewBloc(byte * ascii,int16 x,int16 y,uint16 width,uint8 pen,uint32 type,uint32 fontRes,uint8 justification)508 uint32 FontRenderer::buildNewBloc(byte *ascii, int16 x, int16 y, uint16 width, uint8 pen, uint32 type, uint32 fontRes, uint8 justification) {
509 uint32 i = 0;
510
511 while (i < MAX_text_blocs && _blocList[i].text_mem)
512 i++;
513
514 assert(i < MAX_text_blocs);
515
516 // Create and position the sprite
517
518 _blocList[i].text_mem = makeTextSprite(ascii, width, pen, fontRes);
519
520 // 'NO_JUSTIFICATION' means print sprite with top-left at (x,y)
521 // without margin checking - used for debug text
522
523 if (justification != NO_JUSTIFICATION) {
524 FrameHeader frame_head;
525
526 frame_head.read(_blocList[i].text_mem);
527
528 switch (justification) {
529 case POSITION_AT_CENTER_OF_BASE:
530 // This one is always used for SPEECH TEXT; possibly
531 // also for pointer text
532 x -= (frame_head.width / 2);
533 y -= frame_head.height;
534 break;
535 case POSITION_AT_CENTER_OF_TOP:
536 x -= (frame_head.width / 2);
537 break;
538 case POSITION_AT_LEFT_OF_TOP:
539 // The given coords are already correct for this!
540 break;
541 case POSITION_AT_RIGHT_OF_TOP:
542 x -= frame_head.width;
543 break;
544 case POSITION_AT_LEFT_OF_BASE:
545 y -= frame_head.height;
546 break;
547 case POSITION_AT_RIGHT_OF_BASE:
548 x -= frame_head.width;
549 y -= frame_head.height;
550 break;
551 case POSITION_AT_LEFT_OF_CENTER:
552 y -= (frame_head.height / 2);
553 break;
554 case POSITION_AT_RIGHT_OF_CENTER:
555 x -= frame_head.width;
556 y -= (frame_head.height) / 2;
557 break;
558 }
559
560 // Ensure text sprite is a few pixels inside the visible screen
561 // remember - it's RDSPR_DISPLAYALIGN
562
563 uint16 text_left_margin = TEXT_MARGIN;
564 uint16 text_right_margin = 640 - TEXT_MARGIN - frame_head.width;
565 uint16 text_top_margin = TEXT_MARGIN;
566 uint16 text_bottom_margin = 400 - TEXT_MARGIN - frame_head.height;
567
568 // Move if too far left or too far right
569
570 if (x < text_left_margin)
571 x = text_left_margin;
572 else if (x > text_right_margin)
573 x = text_right_margin;
574
575 // Move if too high or too low
576
577 if (y < text_top_margin)
578 y = text_top_margin;
579 else if (y > text_bottom_margin)
580 y = text_bottom_margin;
581 }
582
583 // The sprite is always uncompressed
584 _blocList[i].type = type | RDSPR_NOCOMPRESSION;
585
586 _blocList[i].x = x;
587 _blocList[i].y = y;
588
589 return i + 1;
590 }
591
592 /**
593 * Called by buildDisplay()
594 */
595
printTextBlocs()596 void FontRenderer::printTextBlocs() {
597 for (uint i = 0; i < MAX_text_blocs; i++) {
598 if (_blocList[i].text_mem) {
599 FrameHeader frame_head;
600 SpriteInfo spriteInfo;
601
602 frame_head.read(_blocList[i].text_mem);
603
604 spriteInfo.x = _blocList[i].x;
605 spriteInfo.y = _blocList[i].y;
606 spriteInfo.w = frame_head.width;
607 spriteInfo.h = frame_head.height;
608 spriteInfo.scale = 0;
609 spriteInfo.scaledWidth = 0;
610 spriteInfo.scaledHeight = 0;
611 spriteInfo.type = _blocList[i].type;
612 spriteInfo.blend = 0;
613 spriteInfo.data = _blocList[i].text_mem + FrameHeader::size();
614 spriteInfo.colorTable = 0;
615 spriteInfo.isText = true;
616
617 uint32 rv = _vm->_screen->drawSprite(&spriteInfo);
618 if (rv)
619 error("Driver Error %.8x in printTextBlocs", rv);
620 }
621 }
622 }
623
killTextBloc(uint32 bloc_number)624 void FontRenderer::killTextBloc(uint32 bloc_number) {
625 bloc_number--;
626 free(_blocList[bloc_number].text_mem);
627 _blocList[bloc_number].text_mem = NULL;
628 }
629
630 // Resource 3258 contains text from location script for 152 (install, save &
631 // restore text, etc)
632
633 #define TEXT_RES 3258
634
635 // Local line number of "save" (actor no. 1826)
636
637 #define SAVE_LINE_NO 1
638
initializeFontResourceFlags()639 void Sword2Engine::initializeFontResourceFlags() {
640 byte *textFile = _resman->openResource(TEXT_RES);
641
642 // If language is Polish or Finnish it requires alternate fonts.
643 // Otherwise, use regular fonts
644
645 // "tallenna" Finnish for "save"
646 // "zapisz" Polish for "save"
647
648 // Get the text line (& skip the 2 chars containing the wavId)
649 char *textLine = (char *)fetchTextLine(textFile, SAVE_LINE_NO) + 2;
650
651 if (strcmp(textLine, "tallenna") == 0)
652 initializeFontResourceFlags(FINNISH_TEXT);
653 else if (strcmp(textLine, "zapisz") == 0)
654 initializeFontResourceFlags(POLISH_TEXT);
655 else
656 initializeFontResourceFlags(DEFAULT_TEXT);
657
658 // Get the game name for the windows application
659
660 // According to the GetGameName(), which was never called and has
661 // therefore been removed, the name of the game is:
662 //
663 // ENGLISH: "Broken Sword II"
664 // AMERICAN: "Circle of Blood II"
665 // GERMAN: "Baphomet's Fluch II"
666 // default: "Some game or other, part 86"
667 //
668 // But we get it from the text resource instead.
669
670 if (_logic->readVar(DEMO))
671 textLine = (char *)fetchTextLine(textFile, 451) + 2;
672 else
673 textLine = (char *)fetchTextLine(textFile, 54) + 2;
674
675 _system->setWindowCaption(textLine);
676 _resman->closeResource(TEXT_RES);
677 }
678
679 /**
680 * Called from initializeFontResourceFlags(), and also from console.cpp
681 */
682
initializeFontResourceFlags(uint8 language)683 void Sword2Engine::initializeFontResourceFlags(uint8 language) {
684 switch (language) {
685 case FINNISH_TEXT:
686 _speechFontId = FINNISH_SPEECH_FONT_ID;
687 _controlsFontId = FINNISH_CONTROLS_FONT_ID;
688 _redFontId = FINNISH_RED_FONT_ID;
689 break;
690 case POLISH_TEXT:
691 _speechFontId = POLISH_SPEECH_FONT_ID;
692 _controlsFontId = POLISH_CONTROLS_FONT_ID;
693 _redFontId = POLISH_RED_FONT_ID;
694 break;
695 default:
696 _speechFontId = ENGLISH_SPEECH_FONT_ID;
697 _controlsFontId = ENGLISH_CONTROLS_FONT_ID;
698 _redFontId = ENGLISH_RED_FONT_ID;
699 break;
700 }
701 }
702
703 } // End of namespace Sword2
704