1 /*
2 ===========================================================================
3 Copyright (C) 2000 - 2013, Raven Software, Inc.
4 Copyright (C) 2001 - 2013, Activision, Inc.
5 Copyright (C) 2013 - 2015, OpenJK contributors
6 
7 This file is part of the OpenJK source code.
8 
9 OpenJK is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 2 as
11 published by the Free Software Foundation.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 ===========================================================================
21 */
22 
23 // cg_text.c --
24 
25 // this line must stay at top so the whole PCH thing works...
26 #include "cg_headers.h"
27 
28 #include "cg_media.h"
29 
30 
31 //int precacheWav_i;	// Current high index of precacheWav array
32 //precacheWav_t precacheWav[MAX_PRECACHEWAV];
33 
34 
35 //int precacheText_i;	// Current high index of precacheText array
36 //precacheText_t precacheText[MAX_PRECACHETEXT];
37 
38 
39 extern vec4_t textcolor_caption;
40 extern vec4_t textcolor_center;
41 extern vec4_t textcolor_scroll;
42 
43 
44 // display text in a supplied box, start at top left and going down by however many pixels I feel like internally,
45 //	return value is NULL if all fitted, else char * of next char to continue from that didn't fit.
46 //
47 // (coords are in the usual 640x480 virtual space)...
48 //
49 // ( if you get the same char * returned as what you passed in, then none of it fitted at all (box too small) )
50 //
51 		// this is execrable, and should NOT have had to've been done now, but...
52 		//
53 		float gfAdvanceHack = 0.0f;	// MUST default to this
54 		int giLinesOutput;		// hack-city after release, only used by one function
55 //
CG_DisplayBoxedText(int iBoxX,int iBoxY,int iBoxWidth,int iBoxHeight,const char * psText,int iFontHandle,float fScale,const vec4_t v4Color)56 const char *CG_DisplayBoxedText(int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight,
57 								const char *psText, int iFontHandle, float fScale,
58 								const vec4_t v4Color)
59 {
60 	giLinesOutput = 0;
61 	cgi_R_SetColor( v4Color );
62 
63 	// Setup a reasonable vertical spacing (taiwanese & japanese need 1.5 fontheight, so use that for all)...
64 	//
65 	const int iFontHeight		 = cgi_R_Font_HeightPixels(iFontHandle, fScale);
66 	const int iFontHeightAdvance = (int) ( ((gfAdvanceHack == 0.0f) ? 1.5f : gfAdvanceHack) * (float) iFontHeight);
67 	int iYpos = iBoxY;	// start print pos
68 
69 	// this could probably be simplified now, but it was converted from something else I didn't originally write,
70 	//	and it works anyway so wtf...
71 	//
72 	const char *psCurrentTextReadPos = psText;
73 	const char *psReadPosAtLineStart = psCurrentTextReadPos;
74 	const char *psBestLineBreakSrcPos = psCurrentTextReadPos;
75 	const char *psLastGood_s;	// needed if we get a full screen of chars with no punctuation or space (see usage notes)
76 	while( *psCurrentTextReadPos && (iYpos + iFontHeight < (iBoxY + iBoxHeight)) )
77 	{
78 		char sLineForDisplay[2048];	// ott
79 
80 		// construct a line...
81 		//
82 		psCurrentTextReadPos = psReadPosAtLineStart;
83 		sLineForDisplay[0] = '\0';
84 		while ( *psCurrentTextReadPos )
85 		{
86 			psLastGood_s = psCurrentTextReadPos;
87 
88 			// read letter...
89 			//
90 			qboolean bIsTrailingPunctuation;
91 			int iAdvanceCount;
92 			unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psCurrentTextReadPos, &iAdvanceCount, &bIsTrailingPunctuation);
93 			psCurrentTextReadPos += iAdvanceCount;
94 
95 			// concat onto string so far...
96 			//
97 			if (uiLetter == 32 && sLineForDisplay[0] == '\0')
98 			{
99 				psReadPosAtLineStart++;
100 				continue;	// unless it's a space at the start of a line, in which case ignore it.
101 			}
102 
103 			if (uiLetter > 255)
104 			{
105 				Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
106 			}
107 			else
108 			{
109 				Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c",uiLetter & 0xFF));
110 			}
111 
112 			if (uiLetter == '\n')
113 			{
114 				// explicit new line...
115 				//
116 				sLineForDisplay[ strlen(sLineForDisplay)-1 ] = '\0';	// kill the CR
117 				psReadPosAtLineStart = psCurrentTextReadPos;
118 				psBestLineBreakSrcPos = psCurrentTextReadPos;
119 				break;	// print this line
120 			}
121 			else
122 			if ( cgi_R_Font_StrLenPixels(sLineForDisplay, iFontHandle, fScale) >= iBoxWidth )
123 			{
124 				// reached screen edge, so cap off string at bytepos after last good position...
125 				//
126 				if (uiLetter > 255 && bIsTrailingPunctuation && !cgi_Language_UsesSpaces())
127 				{
128 					// Special case, don't consider line breaking if you're on an asian punctuation char of
129 					//	a language that doesn't use spaces...
130 					//
131 				}
132 				else
133 				{
134 					if (psBestLineBreakSrcPos == psReadPosAtLineStart)
135 					{
136 						//  aarrrggh!!!!!   we'll only get here is someone has fed in a (probably) garbage string,
137 						//		since it doesn't have a single space or punctuation mark right the way across one line
138 						//		of the screen.  So far, this has only happened in testing when I hardwired a taiwanese
139 						//		string into this function while the game was running in english (which should NEVER happen
140 						//		normally).  On the other hand I suppose it'psCurrentTextReadPos entirely possible that some taiwanese string
141 						//		might have no punctuation at all, so...
142 						//
143 						psBestLineBreakSrcPos = psLastGood_s;	// force a break after last good letter
144 					}
145 
146 					sLineForDisplay[ psBestLineBreakSrcPos - psReadPosAtLineStart ] = '\0';
147 					psReadPosAtLineStart = psCurrentTextReadPos = psBestLineBreakSrcPos;
148 					break;	// print this line
149 				}
150 			}
151 
152 			// record last-good linebreak pos...  (ie if we've just concat'd a punctuation point (western or asian) or space)
153 			//
154 			if (bIsTrailingPunctuation || uiLetter == ' ' || (uiLetter > 255 && !cgi_Language_UsesSpaces()))
155 			{
156 				psBestLineBreakSrcPos = psCurrentTextReadPos;
157 			}
158 		}
159 
160 		// ... and print it...
161 		//
162 		cgi_R_Font_DrawString(iBoxX, iYpos, sLineForDisplay, v4Color, iFontHandle, -1, fScale);
163 		iYpos += iFontHeightAdvance;
164 		giLinesOutput++;
165 
166 		// and echo to console in dev mode...
167 		//
168 		if ( cg_developer.integer )
169 		{
170 //			Com_Printf( "%psCurrentTextReadPos\n", sLineForDisplay );
171 		}
172 	}
173 	return psReadPosAtLineStart;
174 }
175 
176 
177 
178 /*
179 ===============================================================================
180 
181 CAPTION TEXT
182 
183 ===============================================================================
184 */
CG_CaptionTextStop(void)185 void CG_CaptionTextStop(void)
186 {
187 	cg.captionTextTime = 0;
188 }
189 
190 // try and get the correct StripEd text (with retry) for a given reference...
191 //
192 // returns 0 if failed, else strlen...
193 //
cg_SP_GetStringTextStringWithRetry(const char * psReference,char * psDest,int iSizeofDest)194 static int cg_SP_GetStringTextStringWithRetry( const char *psReference, char *psDest, int iSizeofDest)
195 {
196 	int iReturn;
197 
198 	if (psReference[0] == '#')
199 	{
200 		// then we know the striped package name is already built in, so do NOT try prepending anything else...
201 		//
202 		return cgi_SP_GetStringTextString( va("%s",psReference+1), psDest, iSizeofDest );
203 	}
204 
205 	for (int i=0; i<STRIPED_LEVELNAME_VARIATIONS; i++)
206 	{
207 		if (cgs.stripLevelName[i][0])	// entry present?
208 		{
209 			iReturn = cgi_SP_GetStringTextString( va("%s_%s",cgs.stripLevelName[i],psReference), psDest, iSizeofDest );
210 			if (iReturn)
211 			{
212 				return iReturn;
213 			}
214 		}
215 	}
216 
217 	return 0;
218 }
219 
220 // slightly confusingly, the char arg for this function is an audio filename of the form "path/path/filename",
221 //	the "filename" part of which should be the same as the StripEd reference we're looking for in the current
222 //	level's string package...
223 //
CG_CaptionText(const char * str,int sound)224 void CG_CaptionText( const char *str, int sound)
225 {
226 	const char	*s, *holds;
227 	int i;
228 	int	holdTime;
229 	char text[8192]={0};
230 
231 	const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
232 
233 	holds = strrchr(str,'/');
234 	if (!holds)
235 	{
236 #ifndef FINAL_BUILD
237 		Com_Printf("WARNING: CG_CaptionText given audio filename with no '/':'%s'\n",str);
238 #endif
239 		return;
240 	}
241 	i = cg_SP_GetStringTextStringWithRetry( holds+1, text, sizeof(text) );
242 	//ensure we found a match
243 	if (!i)
244 	{
245 #ifndef FINAL_BUILD
246 		// we only care about some sound dirs...
247 		if (!Q_stricmpn(str,"sound/chars/",12))	// whichever language it is, it'll be pathed as english at this point
248 		{
249 			Com_Printf("WARNING: CG_CaptionText given invalid text key: '%s'\n", str);
250 		}
251 		else
252 		{
253 			// anything else is probably stuff we don't care about. It certainly shouldn't be speech, anyway
254 		}
255 #endif
256 		return;
257 	}
258 
259 	const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale));	// taiwanese & japanese need 1.5 fontheight spacing
260 
261 	cg.captionTextTime = cg.time;
262 	if (in_camera) {
263 		cg.captionTextY = SCREEN_HEIGHT - (client_camera.bar_height_dest/2);	// ths is now a centre'd Y, not a start Y
264 	} else {	//get above the hud
265 		cg.captionTextY = (int) (0.88f*((float)SCREEN_HEIGHT - (float)fontHeight * 1.5f));	// do NOT move this, it has to fit in between the weapon HUD and the datapad update.
266 	}
267 	cg.captionTextCurrentLine = 0;
268 
269 	// count the number of lines for centering
270 	cg.scrollTextLines = 1;
271 
272 	memset (cg.captionText, 0, sizeof(cg.captionText));
273 
274 	// Break into individual lines
275 	i = 0;	// this could be completely removed and replace by "cg.scrollTextLines-1", but wtf?
276 
277 	s=(const char*)&text;
278 	// tai...
279 //	s="�ɨ������դh�w�g�w���F�A�ڤ]��Ҧ��o�{���i���u�ө��v�C�ܤ����a�A��hĵ�����ǥ�è�o�{�F�@�Ǫ��p�A�dzƦb�����e���Ⱦ���E�ǧJ��o�C�L���˦��~��ϸ`�A��L�F�h�h���ơC�{�b�L�����H��A�åB�¯٭n�����f�r�C�ھڳ̷s�����i�A�ǧJ��o�H�ΥL���ҦФw�g�������ڤF�����C�ڨ��R�Ӱl���ǧJ��o�H�αϥX�Ҧ��H��C�o�ä��e���C";
280 	// kor...
281 //	s="Wp:��Ÿ���̴� �ָ�. �׵��� ���Ѵ�� �װ� ������ ����ϰڴ�.��Ÿ���̴� �ָ�. �׵��� ���Ѵ�� �װ� ������ ����ϰڴ�.";
282 	holds = s;
283 
284 	int iPlayingTimeMS	= cgi_S_GetSampleLength(sound);
285 	int iLengthInChars	= strlen(s);//cgi_R_Font_StrLenChars(s);	// strlen is also good for MBCS in this instance, since it's for timing
286 	if (iLengthInChars == 0)
287 	{
288 		iLengthInChars = 1;
289 	}
290 	cg.captionLetterTime = iPlayingTimeMS / iLengthInChars;
291 
292 	const char *psBestLineBreakSrcPos = s;
293 	const char *psLastGood_s;	// needed if we get a full screen of chars with no punctuation or space (see usage notes)
294 	while( *s )
295 	{
296 		psLastGood_s = s;
297 
298 		// read letter...
299 		//
300 		qboolean bIsTrailingPunctuation;
301 		int iAdvanceCount;
302 		unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(s, &iAdvanceCount, &bIsTrailingPunctuation);
303 		s += iAdvanceCount;
304 
305 		// concat onto string so far...
306 		//
307 		if (uiLetter == 32 && cg.captionText[i][0] == '\0')
308 		{
309 			holds++;
310 			continue;	// unless it's a space at the start of a line, in which case ignore it.
311 		}
312 
313 		if (uiLetter > 255)
314 		{
315 			Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
316 		}
317 		else
318 		{
319 			Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c",uiLetter & 0xFF));
320 		}
321 
322 		if (uiLetter == '\n')
323 		{
324 			// explicit new line...
325 			//
326 			cg.captionText[i][ strlen(cg.captionText[i])-1 ] = '\0';	// kill the CR
327 			i++;
328 			holds = s;
329 			psBestLineBreakSrcPos = s;
330 			cg.scrollTextLines++;
331 		}
332 		else
333 		if ( cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale) >= SCREEN_WIDTH)
334 		{
335 			// reached screen edge, so cap off string at bytepos after last good position...
336 			//
337 			if (uiLetter > 255 && bIsTrailingPunctuation && !cgi_Language_UsesSpaces())
338 			{
339 				// Special case, don't consider line breaking if you're on an asian punctuation char of
340 				//	a language that doesn't use spaces...
341 				//
342 			}
343 			else
344 			{
345 				if (psBestLineBreakSrcPos == holds)
346 				{
347 					//  aarrrggh!!!!!   we'll only get here is someone has fed in a (probably) garbage string,
348 					//		since it doesn't have a single space or punctuation mark right the way across one line
349 					//		of the screen.  So far, this has only happened in testing when I hardwired a taiwanese
350 					//		string into this function while the game was running in english (which should NEVER happen
351 					//		normally).  On the other hand I suppose it's entirely possible that some taiwanese string
352 					//		might have no punctuation at all, so...
353 					//
354 					psBestLineBreakSrcPos = psLastGood_s;	// force a break after last good letter
355 				}
356 
357 				cg.captionText[i][ psBestLineBreakSrcPos - holds ] = '\0';
358 				holds = s = psBestLineBreakSrcPos;
359 				i++;
360 				cg.scrollTextLines++;
361 			}
362 		}
363 
364 		// record last-good linebreak pos...  (ie if we've just concat'd a punctuation point (western or asian) or space)
365 		//
366 		if (bIsTrailingPunctuation || uiLetter == ' ' || (uiLetter > 255 && !cgi_Language_UsesSpaces()))
367 		{
368 			psBestLineBreakSrcPos = s;
369 		}
370 	}
371 
372 
373 	// calc the length of time to hold each 2 lines of text on the screen.... presumably this works?
374 	//
375 	holdTime = strlen(cg.captionText[0]);
376 	if (cg.scrollTextLines > 1)
377 	{
378 		holdTime += strlen(cg.captionText[1]);	// strlen is also good for MBCS in this instance, since it's for timing
379 	}
380 	cg.captionNextTextTime = cg.time + (holdTime * cg.captionLetterTime);
381 
382 	cg.scrollTextTime = 0;	// No scrolling during captions
383 
384 	//Echo to console in dev mode
385 	if ( cg_developer.integer )
386 	{
387 		Com_Printf( "%s\n", cg.captionText[0] );	// ste:  was [i], but surely sentence 0 is more useful than last?
388 	}
389 }
390 
391 
CG_DrawCaptionText(void)392 void CG_DrawCaptionText(void)
393 {
394 	int		i;
395 	int		x, y, w;
396 	int	holdTime;
397 
398 	if ( !cg.captionTextTime )
399 	{
400 		return;
401 	}
402 
403 	const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
404 
405 	if (cg_skippingcin.integer != 0)
406 	{
407 		cg.captionTextTime = 0;
408 		return;
409 	}
410 
411 	if ( cg.captionNextTextTime < cg.time )
412 	{
413 		cg.captionTextCurrentLine += 2;
414 
415 		if (cg.captionTextCurrentLine >= cg.scrollTextLines)
416 		{
417 			cg.captionTextTime = 0;
418 			return;
419 		}
420 		else
421 		{
422 			holdTime = strlen(cg.captionText[cg.captionTextCurrentLine]);
423 			if (cg.scrollTextLines >= cg.captionTextCurrentLine)
424 			{
425 				// ( strlen is also good for MBCS in this instance, since it's for timing -ste)
426 				//
427 				holdTime += strlen(cg.captionText[cg.captionTextCurrentLine + 1]);
428 			}
429 
430 			cg.captionNextTextTime = cg.time + (holdTime * cg.captionLetterTime);//50);
431 		}
432 	}
433 
434 	// Give a color if one wasn't given
435 	if((textcolor_caption[0] == 0) && (textcolor_caption[1] == 0) &&
436 		(textcolor_caption[2] == 0) && (textcolor_caption[3] == 0))
437 	{
438 		VectorCopy4( colorTable[CT_WHITE], textcolor_caption );
439 	}
440 
441 	cgi_R_SetColor(textcolor_caption);
442 
443 	// Set Y of the first line (varies if only printing one line of text)
444 	// (this all works, please don't mess with it)
445 	const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale));
446 	const bool bPrinting2Lines = !!(cg.captionText[ cg.captionTextCurrentLine+1 ][0]);
447 	y = cg.captionTextY - ( (float)fontHeight * (bPrinting2Lines ? 1 : 0.5f));	// captionTextY was a centered Y pos, not a top one
448 	y -= cgi_Language_IsAsian() ? 0 : 4;
449 
450 	for (i=	cg.captionTextCurrentLine;i< cg.captionTextCurrentLine + 2;++i)
451 	{
452 		w = cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale);
453 		if (w)
454 		{
455 			x = (SCREEN_WIDTH-w) / 2;
456 			cgi_R_Font_DrawString(x, y, cg.captionText[i], textcolor_caption, cgs.media.qhFontMedium, -1, fFontScale);
457 			y += fontHeight;
458 		}
459 	}
460 
461 	cgi_R_SetColor( NULL );
462 }
463 
464 /*
465 ===============================================================================
466 
467 SCROLL TEXT
468 
469 ===============================================================================
470 
471 CG_ScrollText - split text up into seperate lines
472 
473  'str' arg is StripEd string reference, eg "CREDITS_RAVEN"
474 
475 */
476 int giScrollTextPixelWidth = SCREEN_WIDTH;
CG_ScrollText(const char * str,int iPixelWidth)477 void CG_ScrollText( const char *str, int iPixelWidth )
478 {
479 	const char	*s,*holds;
480 	int i;//, len;//, numChars;
481 
482 	giScrollTextPixelWidth = iPixelWidth;
483 
484 	// first, ask the strlen of the final string...
485 	//
486 	i = cgi_SP_GetStringTextString( str, NULL, 0 );
487 
488 	//ensure we found a match
489 	if (!i)
490 	{
491 #ifndef FINAL_BUILD
492 		Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
493 #endif
494 		return;
495 	}
496 	//
497 	// malloc space to hold it...
498 	//
499 	char *psText = (char *) cgi_Z_Malloc( i+1, TAG_TEMP_WORKSPACE );
500 	//
501 	// now get the string...
502 	//
503 	i = cgi_SP_GetStringTextString( str, psText, i+1 );
504 	//ensure we found a match
505 	if (!i)
506 	{
507 		assert(0);	// should never get here now, but wtf?
508 		cgi_Z_Free(psText);
509 #ifndef FINAL_BUILD
510 		Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
511 #endif
512 		return;
513 	}
514 
515 	cg.scrollTextTime = cg.time;
516 	cg.printTextY = SCREEN_HEIGHT;
517 	cg.scrollTextLines = 1;
518 
519 	s = psText;
520 	i = 0;
521 	holds = s;
522 
523 	const char *psBestLineBreakSrcPos = s;
524 	const char *psLastGood_s;	// needed if we get a full screen of chars with no punctuation or space (see usage notes)
525 	while( *s )
526 	{
527 		psLastGood_s = s;
528 
529 		// read letter...
530 		//
531 		qboolean bIsTrailingPunctuation;
532 		int iAdvanceCount;
533 		unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(s, &iAdvanceCount, &bIsTrailingPunctuation);
534 		s += iAdvanceCount;
535 
536 		// concat onto string so far...
537 		//
538 		if (uiLetter == 32 && cg.printText[i][0] == '\0')
539 		{
540 			holds++;
541 			continue;	// unless it's a space at the start of a line, in which case ignore it.
542 		}
543 
544 		if (uiLetter > 255)
545 		{
546 			Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
547 		}
548 		else
549 		{
550 			Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c",uiLetter & 0xFF));
551 		}
552 
553 		// record last-good linebreak pos...  (ie if we've just concat'd a punctuation point (western or asian) or space)
554 		//
555 		if (bIsTrailingPunctuation || uiLetter == ' ')
556 		{
557 			psBestLineBreakSrcPos = s;
558 		}
559 
560 		if (uiLetter == '\n')
561 		{
562 			// explicit new line...
563 			//
564 			cg.printText[i][ strlen(cg.printText[i])-1 ] = '\0';	// kill the CR
565 			i++;
566 			assert (i < (int)(sizeof(cg.printText)/sizeof(cg.printText[0])) );
567 			if (i >= (int)(sizeof(cg.printText)/sizeof(cg.printText[0])) )
568 			{
569 				break;
570 			}
571 			holds = s;
572 			cg.scrollTextLines++;
573 		}
574 		else
575 		if ( cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f) >= iPixelWidth)
576 		{
577 			// reached screen edge, so cap off string at bytepos after last good position...
578 			//
579 			if (psBestLineBreakSrcPos == holds)
580 			{
581 				//  aarrrggh!!!!!   we'll only get here is someone has fed in a (probably) garbage string,
582 				//		since it doesn't have a single space or punctuation mark right the way across one line
583 				//		of the screen.  So far, this has only happened in testing when I hardwired a taiwanese
584 				//		string into this function while the game was running in english (which should NEVER happen
585 				//		normally).  On the other hand I suppose it's entirely possible that some taiwanese string
586 				//		might have no punctuation at all, so...
587 				//
588 				psBestLineBreakSrcPos = psLastGood_s;	// force a break after last good letter
589 			}
590 
591 			cg.printText[i][ psBestLineBreakSrcPos - holds ] = '\0';
592 			holds = s = psBestLineBreakSrcPos;
593 			i++;
594 			assert (i < (int)(sizeof(cg.printText)/sizeof(cg.printText[0])) );
595 			cg.scrollTextLines++;
596 		}
597 	}
598 
599 	cg.captionTextTime = 0;		// No captions during scrolling
600 	cgi_Z_Free(psText);
601 }
602 
603 
604 // draws using [textcolor_scroll]...
605 //
606 #define SCROLL_LPM (1/50.0) // 1 line per 50 ms
CG_DrawScrollText(void)607 void CG_DrawScrollText(void)
608 {
609 	int		i;
610 	int		x,y;
611 	const int fontHeight = (int) (1.5f * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f));	// taiwanese & japanese need 1.5 fontheight spacing
612 
613 	if ( !cg.scrollTextTime )
614 	{
615 		return;
616 	}
617 
618 	cgi_R_SetColor( textcolor_scroll );
619 
620 	y = cg.printTextY - (cg.time - cg.scrollTextTime) * SCROLL_LPM;
621 
622 //	cgi_R_Font_DrawString(320, 200, va("Scrolltext printing @ %d",y), colorTable[CT_LTGOLD1], cgs.media.qhFontMedium, -1, 1.0f);
623 
624 	// See if text has finished scrolling off screen
625 	if ((y + cg.scrollTextLines * fontHeight) < 1)
626 	{
627 		cg.scrollTextTime = 0;
628 		return;
629 	}
630 
631 	for (i=0;i<cg.scrollTextLines;++i)
632 	{
633 
634 		// Is this line off top of screen?
635 		if ((y + ((i +1) * fontHeight)) < 1)
636 		{
637 			y += fontHeight;
638 			continue;
639 		}
640 		// or past bottom of screen?
641 		else if (y > SCREEN_HEIGHT)
642 		{
643 			break;
644 		}
645 
646 //		w = cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f);
647 //		if (w)
648 		{
649 			x = (SCREEN_WIDTH - giScrollTextPixelWidth) / 2;
650 			cgi_R_Font_DrawString(x,y, cg.printText[i], textcolor_scroll, cgs.media.qhFontMedium, -1, 1.0f);
651 			y += fontHeight;
652 		}
653 	}
654 
655 	cgi_R_SetColor( NULL );
656 }
657 
658 
659 /*
660 ===============================================================================
661 
662 CENTER PRINTING
663 
664 ===============================================================================
665 */
666 
667 
668 /*
669 ==============
670 CG_CenterPrint
671 
672 Called for important messages that should stay in the center of the screen
673 for a few moments
674 ==============
675 */
CG_CenterPrint(const char * str,int y)676 void CG_CenterPrint( const char *str, int y) {
677 	char	*s;
678 
679 	// Find text to match the str given
680 	if (*str == '@')
681 	{
682 		int i;
683 
684 		i = cgi_SP_GetStringTextString( str+1, cg.centerPrint, sizeof(cg.centerPrint) );
685 
686 		if (!i)
687 		{
688 			Com_Printf (S_COLOR_RED"CG_CenterPrint: cannot find reference '%s' in StringPackage!\n",str);
689 			Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
690 		}
691 	}
692 	else
693 	{
694 		Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
695 	}
696 
697 	cg.centerPrintTime = cg.time;
698 	cg.centerPrintY = y;
699 
700 	// count the number of lines for centering
701 	cg.centerPrintLines = 1;
702 	s = cg.centerPrint;
703 	while( *s ) {
704 		if (*s == '\n')
705 			cg.centerPrintLines++;
706 		s++;
707 	}
708 
709 }
710 
711 
712 /*
713 ===================
714 CG_DrawCenterString
715 ===================
716 */
CG_DrawCenterString(void)717 void CG_DrawCenterString( void )
718 {
719 	char	*start;
720 	unsigned int l;
721 	int		x, y, w;
722 	float	*color;
723 
724 	if ( !cg.centerPrintTime ) {
725 		return;
726 	}
727 
728 	color = CG_FadeColor( cg.centerPrintTime, 1000 * 3 );
729 	if ( !color ) {
730 		return;
731 	}
732 
733 	if((textcolor_center[0] == 0) && (textcolor_center[1] == 0) &&
734 		(textcolor_center[2] == 0) && (textcolor_center[3] == 0))
735 	{
736 		VectorCopy4( colorTable[CT_WHITE], textcolor_center );
737 	}
738 
739 	start = cg.centerPrint;
740 
741 	const int fontHeight = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
742 	y = cg.centerPrintY - (cg.centerPrintLines * fontHeight) / 2;
743 
744 	while ( 1 ) {
745 		char linebuffer[1024];
746 
747 		// this is kind of unpleasant when dealing with MBCS, but...
748 		//
749 		const char *psString = start;
750 		int iOutIndex = 0;
751 		for ( l = 0; l < sizeof(linebuffer)-1; l++ ) {
752 			int iAdvanceCount;
753 			unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psString, &iAdvanceCount);
754 			psString += iAdvanceCount;
755 			if (!uiLetter || uiLetter == '\n'){
756 				break;
757 			}
758 			if (uiLetter > 255)
759 			{
760 				linebuffer[iOutIndex++] = uiLetter >> 8;
761 				linebuffer[iOutIndex++] = uiLetter & 0xFF;
762 			}
763 			else
764 			{
765 				linebuffer[iOutIndex++] = uiLetter & 0xFF;
766 			}
767 		}
768 		linebuffer[iOutIndex++] = '\0';
769 
770 		w = cgi_R_Font_StrLenPixels(linebuffer, cgs.media.qhFontMedium, 1.0f);
771 
772 		x = ( SCREEN_WIDTH - w ) / 2;
773 
774 		cgi_R_Font_DrawString(x,y,linebuffer, textcolor_center, cgs.media.qhFontMedium, -1, 1.0f);
775 
776 		y += fontHeight;
777 
778 		while ( *start && ( *start != '\n' ) ) {
779 			start++;
780 		}
781 		if ( !*start ) {
782 			break;
783 		}
784 		start++;
785 	}
786 
787 }
788