1 /* ScummVM - Graphic Adventure Engine
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.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "common/endian.h"
28 #include "common/util.h"
30 #include "sword1/text.h"
31 #include "sword1/resman.h"
32 #include "sword1/objectman.h"
33 #include "sword1/swordres.h"
34 #include "sword1/sworddefs.h"
35 #include "sword1/screen.h"
36 #include "sword1/sword1.h"
45 Text::Text(ObjectMan
*pObjMan
, ResMan
*pResMan
, bool czechVersion
) {
49 _fontId
= (czechVersion
) ? CZECH_GAME_FONT
: GAME_FONT
;
50 _font
= (uint8
*)_resMan
->openFetchRes(_fontId
);
52 _joinWidth
= charWidth( SPACE
) - 2 * OVERLAP
;
53 _charHeight
= _resMan
->getUint16(_resMan
->fetchFrame(_font
, 0)->height
); // all chars have the same height
54 for (int i
= 0; i
< MAX_TEXT_OBS
; i
++)
55 _textBlocks
[i
] = NULL
;
59 for (int i
= 0; i
< MAX_TEXT_OBS
; i
++)
61 //_resMan->resClose(_fontId); => wiped automatically by _resMan->flush();
64 uint32
Text::lowTextManager(uint8
*ascii
, int32 width
, uint8 pen
) {
66 if (_textCount
> MAX_TEXT_OBS
)
67 error("Text::lowTextManager: MAX_TEXT_OBS exceeded");
68 uint32 textObjId
= (TEXT_sect
* ITM_PER_SEC
) - 1;
71 } while (_objMan
->fetchObject(textObjId
)->o_status
);
72 // okay, found a free text object
74 _objMan
->fetchObject(textObjId
)->o_status
= STAT_FORE
;
75 makeTextSprite((uint8
)textObjId
, ascii
, (uint16
)width
, pen
);
80 void Text::makeTextSprite(uint8 slot
, uint8
*text
, uint16 maxWidth
, uint8 pen
) {
81 LineInfo lines
[MAX_LINES
];
82 uint16 numLines
= analyzeSentence(text
, maxWidth
, lines
);
86 for (lineCnt
= 0; lineCnt
< numLines
; lineCnt
++)
87 if (lines
[lineCnt
].width
> sprWidth
)
88 sprWidth
= lines
[lineCnt
].width
;
90 uint16 sprHeight
= _charHeight
* numLines
;
91 uint32 sprSize
= sprWidth
* sprHeight
;
92 assert(!_textBlocks
[slot
]); // if this triggers, the speechDriver failed to call Text::releaseText.
93 _textBlocks
[slot
] = (FrameHeader
*)malloc(sprSize
+ sizeof(FrameHeader
));
95 memcpy(_textBlocks
[slot
]->runTimeComp
, "Nu ", 4);
96 _textBlocks
[slot
]->compSize
= 0;
97 _textBlocks
[slot
]->width
= _resMan
->toUint16(sprWidth
);
98 _textBlocks
[slot
]->height
= _resMan
->toUint16(sprHeight
);
99 _textBlocks
[slot
]->offsetX
= 0;
100 _textBlocks
[slot
]->offsetY
= 0;
102 uint8
*linePtr
= ((uint8
*)_textBlocks
[slot
]) + sizeof(FrameHeader
);
103 memset(linePtr
, NO_COL
, sprSize
);
104 for (lineCnt
= 0; lineCnt
< numLines
; lineCnt
++) {
105 uint8
*sprPtr
= linePtr
+ (sprWidth
- lines
[lineCnt
].width
) / 2; // center the text
106 for (uint16 pos
= 0; pos
< lines
[lineCnt
].length
; pos
++)
107 sprPtr
+= copyChar(*text
++, sprPtr
, sprWidth
, pen
) - OVERLAP
;
108 text
++; // skip space at the end of the line
109 if(SwordEngine::isPsx()) //Chars are half height in psx version
110 linePtr
+= (_charHeight
/ 2) * sprWidth
;
112 linePtr
+= _charHeight
* sprWidth
;
116 uint16
Text::charWidth(uint8 ch
) {
119 return _resMan
->getUint16(_resMan
->fetchFrame(_font
, ch
- SPACE
)->width
);
122 uint16
Text::analyzeSentence(uint8
*text
, uint16 maxWidth
, LineInfo
*line
) {
125 bool firstWord
= true;
127 uint16 wordWidth
= 0;
128 uint16 wordLength
= 0;
130 while ((*text
!= SPACE
) && *text
) {
131 wordWidth
+= charWidth(*text
) - OVERLAP
;
138 wordWidth
+= OVERLAP
; // no overlap on final letter of word!
139 if ( firstWord
) { // first word on first line, so no separating SPACE needed
140 line
[0].width
= wordWidth
;
141 line
[0].length
= wordLength
;
144 // see how much extra space this word will need to fit on current line
145 // (with a separating space character - also overlapped)
146 uint16 spaceNeeded
= _joinWidth
+ wordWidth
;
148 if (line
[lineNo
].width
+ spaceNeeded
<= maxWidth
) {
149 line
[lineNo
].width
+= spaceNeeded
;
150 line
[lineNo
].length
+= 1 + wordLength
; // NB. space+word characters
151 } else { // put word (without separating SPACE) at start of next line
153 assert( lineNo
< MAX_LINES
);
154 line
[lineNo
].width
= wordWidth
;
155 line
[lineNo
].length
= wordLength
;
159 return lineNo
+1; // return no of lines
162 uint16
Text::copyChar(uint8 ch
, uint8
*sprPtr
, uint16 sprWidth
, uint8 pen
) {
163 FrameHeader
*chFrame
= _resMan
->fetchFrame(_font
, ch
- SPACE
);
164 uint8
*chData
= ((uint8
*)chFrame
) + sizeof(FrameHeader
);
165 uint8
*dest
= sprPtr
;
166 uint8
*decBuf
= NULL
;
168 uint16 frameHeight
= 0;
170 if(SwordEngine::isPsx()) {
171 frameHeight
= _resMan
->getUint16(chFrame
->height
)/2;
172 if(_fontId
== CZECH_GAME_FONT
) { //Czech game fonts are compressed
173 decBuf
= (uint8
*) malloc((_resMan
->getUint16(chFrame
->width
))*(_resMan
->getUint16(chFrame
->height
)/2));
174 Screen::decompressHIF(chData
, decBuf
);
176 } else //Normal game fonts are not compressed
179 frameHeight
= _resMan
->getUint16(chFrame
->height
);
183 for (uint16 cnty
= 0; cnty
< frameHeight
; cnty
++) {
184 for (uint16 cntx
= 0; cntx
< _resMan
->getUint16(chFrame
->width
); cntx
++) {
185 if (*decChr
== LETTER_COL
)
187 else if (((*decChr
== BORDER_COL
) || (*decChr
== BORDER_COL_PSX
)) && (!dest
[cntx
])) // don't do a border if there's already a color underneath (chars can overlap)
188 dest
[cntx
] = BORDER_COL
;
194 return _resMan
->getUint16(chFrame
->width
);
197 FrameHeader
*Text::giveSpriteData(uint32 textTarget
) {
198 // textTarget is the resource ID of the Compact linking the textdata.
199 // that's 0x950000 for slot 0 and 0x950001 for slot 1. easy, huh? :)
200 textTarget
&= ITM_ID
;
201 assert(textTarget
< MAX_TEXT_OBS
);
203 return _textBlocks
[textTarget
];
206 void Text::releaseText(uint32 id
, bool updateCount
) {
208 assert(id
< MAX_TEXT_OBS
);
209 if (_textBlocks
[id
]) {
210 free(_textBlocks
[id
]);
211 _textBlocks
[id
] = NULL
;
217 } // End of namespace Sword1