1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1998-2000 by DooM Legacy Team.
4 // Copyright (C) 1999-2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file  screen.c
11 /// \brief Handles multiple resolutions, 8bpp/16bpp(highcolor) modes
12 
13 #include "doomdef.h"
14 #include "doomstat.h"
15 #include "screen.h"
16 #include "console.h"
17 #include "am_map.h"
18 #include "i_system.h"
19 #include "i_video.h"
20 #include "r_local.h"
21 #include "r_sky.h"
22 #include "m_argv.h"
23 #include "m_misc.h"
24 #include "v_video.h"
25 #include "st_stuff.h"
26 #include "hu_stuff.h"
27 #include "z_zone.h"
28 #include "d_main.h"
29 #include "d_clisrv.h"
30 #include "f_finale.h"
31 #include "y_inter.h" // usebuffer
32 #include "i_sound.h" // closed captions
33 #include "s_sound.h" // ditto
34 #include "g_game.h" // ditto
35 #include "p_local.h" // P_AutoPause()
36 #ifdef HWRENDER
37 #include "hardware/hw_main.h"
38 #include "hardware/hw_light.h"
39 #include "hardware/hw_model.h"
40 #endif
41 
42 
43 #if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200))
44 #define RUSEASM //MSC.NET can't patch itself
45 #endif
46 
47 // --------------------------------------------
48 // assembly or c drawer routines for 8bpp/16bpp
49 // --------------------------------------------
50 void (*colfunc)(void);
51 void (*colfuncs[COLDRAWFUNC_MAX])(void);
52 
53 void (*spanfunc)(void);
54 void (*spanfuncs[SPANDRAWFUNC_MAX])(void);
55 void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void);
56 
57 // ------------------
58 // global video state
59 // ------------------
60 viddef_t vid;
61 INT32 setmodeneeded; //video mode change needed if > 0 (the mode number to set + 1)
62 UINT8 setrenderneeded = 0;
63 
64 static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"}, {24, "24 bits"}, {32, "32 bits"}, {0, NULL}};
65 
66 //added : 03-02-98: default screen mode, as loaded/saved in config
67 consvar_t cv_scr_width = CVAR_INIT ("scr_width", "1280", CV_SAVE, CV_Unsigned, NULL);
68 consvar_t cv_scr_height = CVAR_INIT ("scr_height", "800", CV_SAVE, CV_Unsigned, NULL);
69 consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL);
70 consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL);
71 
72 CV_PossibleValue_t cv_renderer_t[] = {
73 	{1, "Software"},
74 #ifdef HWRENDER
75 	{2, "OpenGL"},
76 #endif
77 	{0, NULL}
78 };
79 
80 consvar_t cv_renderer = CVAR_INIT ("renderer", "Software", CV_SAVE|CV_NOLUA|CV_CALL, cv_renderer_t, SCR_ChangeRenderer);
81 
82 static void SCR_ChangeFullscreen(void);
83 
84 consvar_t cv_fullscreen = CVAR_INIT ("fullscreen", "Yes", CV_SAVE|CV_CALL, CV_YesNo, SCR_ChangeFullscreen);
85 
86 // =========================================================================
87 //                           SCREEN VARIABLES
88 // =========================================================================
89 
90 INT32 scr_bpp; // current video mode bytes per pixel
91 UINT8 *scr_borderpatch; // flat used to fill the reduced view borders set at ST_Init()
92 
93 // =========================================================================
94 
95 //  Short and Tall sky drawer, for the current color mode
96 void (*walldrawerfunc)(void);
97 
98 boolean R_ASM = true;
99 boolean R_486 = false;
100 boolean R_586 = false;
101 boolean R_MMX = false;
102 boolean R_SSE = false;
103 boolean R_3DNow = false;
104 boolean R_MMXExt = false;
105 boolean R_SSE2 = false;
106 
SCR_SetDrawFuncs(void)107 void SCR_SetDrawFuncs(void)
108 {
109 	//
110 	//  setup the right draw routines for either 8bpp or 16bpp
111 	//
112 	if (true)//vid.bpp == 1) //Always run in 8bpp. todo: remove all 16bpp code?
113 	{
114 		colfuncs[BASEDRAWFUNC] = R_DrawColumn_8;
115 		spanfuncs[BASEDRAWFUNC] = R_DrawSpan_8;
116 
117 		colfunc = colfuncs[BASEDRAWFUNC];
118 		spanfunc = spanfuncs[BASEDRAWFUNC];
119 
120 		colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8;
121 		colfuncs[COLDRAWFUNC_TRANS] = R_DrawTranslatedColumn_8;
122 		colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8;
123 		colfuncs[COLDRAWFUNC_SHADOWED] = R_DrawColumnShadowed_8;
124 		colfuncs[COLDRAWFUNC_TRANSTRANS] = R_DrawTranslatedTranslucentColumn_8;
125 		colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8;
126 		colfuncs[COLDRAWFUNC_TWOSMULTIPATCHTRANS] = R_Draw2sMultiPatchTranslucentColumn_8;
127 		colfuncs[COLDRAWFUNC_FOG] = R_DrawFogColumn_8;
128 
129 		spanfuncs[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_8;
130 		spanfuncs[SPANDRAWFUNC_TILTED] = R_DrawTiltedSpan_8;
131 		spanfuncs[SPANDRAWFUNC_TILTEDTRANS] = R_DrawTiltedTranslucentSpan_8;
132 		spanfuncs[SPANDRAWFUNC_SPLAT] = R_DrawSplat_8;
133 		spanfuncs[SPANDRAWFUNC_TRANSSPLAT] = R_DrawTranslucentSplat_8;
134 		spanfuncs[SPANDRAWFUNC_TILTEDSPLAT] = R_DrawTiltedSplat_8;
135 		spanfuncs[SPANDRAWFUNC_SPRITE] = R_DrawFloorSprite_8;
136 		spanfuncs[SPANDRAWFUNC_TRANSSPRITE] = R_DrawTranslucentFloorSprite_8;
137 		spanfuncs[SPANDRAWFUNC_TILTEDSPRITE] = R_DrawTiltedFloorSprite_8;
138 		spanfuncs[SPANDRAWFUNC_TILTEDTRANSSPRITE] = R_DrawTiltedTranslucentFloorSprite_8;
139 		spanfuncs[SPANDRAWFUNC_WATER] = R_DrawTranslucentWaterSpan_8;
140 		spanfuncs[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedTranslucentWaterSpan_8;
141 		spanfuncs[SPANDRAWFUNC_FOG] = R_DrawFogSpan_8;
142 
143 		// Lactozilla: Non-powers-of-two
144 		spanfuncs_npo2[BASEDRAWFUNC] = R_DrawSpan_NPO2_8;
145 		spanfuncs_npo2[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_NPO2_8;
146 		spanfuncs_npo2[SPANDRAWFUNC_TILTED] = R_DrawTiltedSpan_NPO2_8;
147 		spanfuncs_npo2[SPANDRAWFUNC_TILTEDTRANS] = R_DrawTiltedTranslucentSpan_NPO2_8;
148 		spanfuncs_npo2[SPANDRAWFUNC_SPLAT] = R_DrawSplat_NPO2_8;
149 		spanfuncs_npo2[SPANDRAWFUNC_TRANSSPLAT] = R_DrawTranslucentSplat_NPO2_8;
150 		spanfuncs_npo2[SPANDRAWFUNC_TILTEDSPLAT] = R_DrawTiltedSplat_NPO2_8;
151 		spanfuncs_npo2[SPANDRAWFUNC_SPRITE] = R_DrawFloorSprite_NPO2_8;
152 		spanfuncs_npo2[SPANDRAWFUNC_TRANSSPRITE] = R_DrawTranslucentFloorSprite_NPO2_8;
153 		spanfuncs_npo2[SPANDRAWFUNC_TILTEDSPRITE] = R_DrawTiltedFloorSprite_NPO2_8;
154 		spanfuncs_npo2[SPANDRAWFUNC_TILTEDTRANSSPRITE] = R_DrawTiltedTranslucentFloorSprite_NPO2_8;
155 		spanfuncs_npo2[SPANDRAWFUNC_WATER] = R_DrawTranslucentWaterSpan_NPO2_8;
156 		spanfuncs_npo2[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedTranslucentWaterSpan_NPO2_8;
157 		spanfuncs_npo2[SPANDRAWFUNC_FOG] = NULL; // Not needed
158 
159 #ifdef RUSEASM
160 		if (R_ASM)
161 		{
162 			if (R_MMX)
163 			{
164 				colfuncs[BASEDRAWFUNC] = R_DrawColumn_8_MMX;
165 				//colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8_ASM;
166 				//colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8_ASM;
167 				colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8_MMX;
168 				spanfuncs[BASEDRAWFUNC] = R_DrawSpan_8_MMX;
169 			}
170 			else
171 			{
172 				colfuncs[BASEDRAWFUNC] = R_DrawColumn_8_ASM;
173 				//colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8_ASM;
174 				//colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8_ASM;
175 				colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8_ASM;
176 			}
177 		}
178 #endif
179 	}
180 /*	else if (vid.bpp > 1)
181 	{
182 		I_OutputMsg("using highcolor mode\n");
183 		spanfunc = basespanfunc = R_DrawSpan_16;
184 		transcolfunc = R_DrawTranslatedColumn_16;
185 		transtransfunc = R_DrawTranslucentColumn_16; // No 16bit operation for this function
186 
187 		colfunc = basecolfunc = R_DrawColumn_16;
188 		shadecolfunc = NULL; // detect error if used somewhere..
189 		fuzzcolfunc = R_DrawTranslucentColumn_16;
190 		walldrawerfunc = R_DrawWallColumn_16;
191 	}*/
192 	else
193 		I_Error("unknown bytes per pixel mode %d\n", vid.bpp);
194 /*
195 	if (SCR_IsAspectCorrect(vid.width, vid.height))
196 		CONS_Alert(CONS_WARNING, M_GetText("Resolution is not aspect-correct!\nUse a multiple of %dx%d\n"), BASEVIDWIDTH, BASEVIDHEIGHT);
197 */
198 }
199 
SCR_SetMode(void)200 void SCR_SetMode(void)
201 {
202 	if (dedicated)
203 		return;
204 
205 	if (!(setmodeneeded || setrenderneeded) || WipeInAction)
206 		return; // should never happen and don't change it during a wipe, BAD!
207 
208 	// Lactozilla: Renderer switching
209 	if (setrenderneeded)
210 	{
211 		// stop recording movies (APNG only)
212 		if (setrenderneeded && (moviemode == MM_APNG))
213 			M_StopMovie();
214 
215 		// VID_SetMode will call VID_CheckRenderer itself,
216 		// so no need to do this in here.
217 		if (!setmodeneeded)
218 			VID_CheckRenderer();
219 
220 		vid.recalc = 1;
221 	}
222 
223 	// Set the video mode in the video interface.
224 	if (setmodeneeded)
225 		VID_SetMode(setmodeneeded - 1);
226 
227 	V_SetPalette(0);
228 
229 	SCR_SetDrawFuncs();
230 
231 	// set the apprpriate drawer for the sky (tall or INT16)
232 	setmodeneeded = 0;
233 	setrenderneeded = 0;
234 }
235 
236 // do some initial settings for the game loading screen
237 //
SCR_Startup(void)238 void SCR_Startup(void)
239 {
240 	const CPUInfoFlags *RCpuInfo = I_CPUInfo();
241 	if (!M_CheckParm("-NOCPUID") && RCpuInfo)
242 	{
243 #if defined (__i386__) || defined (_M_IX86) || defined (__WATCOMC__)
244 		R_486 = true;
245 #endif
246 		if (RCpuInfo->RDTSC)
247 			R_586 = true;
248 		if (RCpuInfo->MMX)
249 			R_MMX = true;
250 		if (RCpuInfo->AMD3DNow)
251 			R_3DNow = true;
252 		if (RCpuInfo->MMXExt)
253 			R_MMXExt = true;
254 		if (RCpuInfo->SSE)
255 			R_SSE = true;
256 		if (RCpuInfo->SSE2)
257 			R_SSE2 = true;
258 		CONS_Printf("CPU Info: 486: %i, 586: %i, MMX: %i, 3DNow: %i, MMXExt: %i, SSE2: %i\n", R_486, R_586, R_MMX, R_3DNow, R_MMXExt, R_SSE2);
259 	}
260 
261 	if (M_CheckParm("-noASM"))
262 		R_ASM = false;
263 	if (M_CheckParm("-486"))
264 		R_486 = true;
265 	if (M_CheckParm("-586"))
266 		R_586 = true;
267 	if (M_CheckParm("-MMX"))
268 		R_MMX = true;
269 	if (M_CheckParm("-3DNow"))
270 		R_3DNow = true;
271 	if (M_CheckParm("-MMXExt"))
272 		R_MMXExt = true;
273 
274 	if (M_CheckParm("-SSE"))
275 		R_SSE = true;
276 	if (M_CheckParm("-noSSE"))
277 		R_SSE = false;
278 
279 	if (M_CheckParm("-SSE2"))
280 		R_SSE2 = true;
281 
282 	M_SetupMemcpy();
283 
284 	if (dedicated)
285 	{
286 		V_Init();
287 		V_SetPalette(0);
288 		return;
289 	}
290 
291 	vid.modenum = 0;
292 
293 	V_Init();
294 	V_Recalc();
295 
296 	CV_RegisterVar(&cv_ticrate);
297 	CV_RegisterVar(&cv_constextsize);
298 
299 	V_SetPalette(0);
300 }
301 
302 // Called at new frame, if the video mode has changed
303 //
SCR_Recalc(void)304 void SCR_Recalc(void)
305 {
306 	if (dedicated)
307 		return;
308 
309 	// bytes per pixel quick access
310 	scr_bpp = vid.bpp;
311 
312 	V_Recalc();
313 
314 	// toggle off (then back on) the automap because some screensize-dependent values will
315 	// be calculated next time the automap is activated.
316 	if (automapactive)
317 	{
318 		am_recalc = true;
319 		AM_Start();
320 	}
321 
322 	// set the screen[x] ptrs on the new vidbuffers
323 	V_Init();
324 
325 	// scr_viewsize doesn't change, neither detailLevel, but the pixels
326 	// per screenblock is different now, since we've changed resolution.
327 	R_SetViewSize(); //just set setsizeneeded true now ..
328 
329 	// vid.recalc lasts only for the next refresh...
330 	con_recalc = true;
331 	am_recalc = true;
332 
333 #ifdef HWRENDER
334 	// Shoot! The screen texture was flushed!
335 	if ((rendermode == render_opengl) && (gamestate == GS_INTERMISSION))
336 		usebuffer = false;
337 #endif
338 }
339 
340 // Check for screen cmd-line parms: to force a resolution.
341 //
342 // Set the video mode to set at the 1st display loop (setmodeneeded)
343 //
344 
SCR_CheckDefaultMode(void)345 void SCR_CheckDefaultMode(void)
346 {
347 	INT32 scr_forcex, scr_forcey; // resolution asked from the cmd-line
348 
349 	if (dedicated)
350 		return;
351 
352 	// 0 means not set at the cmd-line
353 	scr_forcex = scr_forcey = 0;
354 
355 	if (M_CheckParm("-width") && M_IsNextParm())
356 		scr_forcex = atoi(M_GetNextParm());
357 
358 	if (M_CheckParm("-height") && M_IsNextParm())
359 		scr_forcey = atoi(M_GetNextParm());
360 
361 	if (scr_forcex && scr_forcey)
362 	{
363 		CONS_Printf(M_GetText("Using resolution: %d x %d\n"), scr_forcex, scr_forcey);
364 		// returns -1 if not found, thus will be 0 (no mode change) if not found
365 		setmodeneeded = VID_GetModeForSize(scr_forcex, scr_forcey) + 1;
366 	}
367 	else
368 	{
369 		CONS_Printf(M_GetText("Default resolution: %d x %d (%d bits)\n"), cv_scr_width.value,
370 			cv_scr_height.value, cv_scr_depth.value);
371 		// see note above
372 		setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1;
373 	}
374 
375 	if (cv_renderer.value != (signed)rendermode)
376 	{
377 		if (chosenrendermode == render_none) // nothing set at command line
378 			SCR_ChangeRenderer();
379 		else
380 		{
381 			// Set cv_renderer to the current render mode
382 			CV_StealthSetValue(&cv_renderer, rendermode);
383 		}
384 	}
385 }
386 
387 // sets the modenum as the new default video mode to be saved in the config file
SCR_SetDefaultMode(void)388 void SCR_SetDefaultMode(void)
389 {
390 	// remember the default screen size
391 	CV_SetValue(&cv_scr_width, vid.width);
392 	CV_SetValue(&cv_scr_height, vid.height);
393 	CV_SetValue(&cv_scr_depth, vid.bpp*8);
394 }
395 
396 // Change fullscreen on/off according to cv_fullscreen
SCR_ChangeFullscreen(void)397 void SCR_ChangeFullscreen(void)
398 {
399 #ifdef DIRECTFULLSCREEN
400 	// allow_fullscreen is set by VID_PrepareModeList
401 	// it is used to prevent switching to fullscreen during startup
402 	if (!allow_fullscreen)
403 		return;
404 
405 	if (graphics_started)
406 	{
407 		VID_PrepareModeList();
408 		setmodeneeded = VID_GetModeForSize(vid.width, vid.height) + 1;
409 	}
410 	return;
411 #endif
412 }
413 
SCR_ChangeRenderer(void)414 void SCR_ChangeRenderer(void)
415 {
416 	if (chosenrendermode != render_none
417 	|| (signed)rendermode == cv_renderer.value)
418 		return;
419 
420 #ifdef HWRENDER
421 	// Check if OpenGL loaded successfully (or wasn't disabled) before switching to it.
422 	if ((vid.glstate == VID_GL_LIBRARY_ERROR)
423 	&& (cv_renderer.value == render_opengl))
424 	{
425 		if (M_CheckParm("-nogl"))
426 			CONS_Alert(CONS_ERROR, "OpenGL rendering was disabled!\n");
427 		else
428 			CONS_Alert(CONS_ERROR, "OpenGL never loaded\n");
429 		return;
430 	}
431 
432 	if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) // Clear these out before switching to software
433 		HWR_ClearAllTextures();
434 
435 #endif
436 
437 	// Set the new render mode
438 	setrenderneeded = cv_renderer.value;
439 }
440 
SCR_IsAspectCorrect(INT32 width,INT32 height)441 boolean SCR_IsAspectCorrect(INT32 width, INT32 height)
442 {
443 	return
444 	 (  width % BASEVIDWIDTH == 0
445 	 && height % BASEVIDHEIGHT == 0
446 	 && width / BASEVIDWIDTH == height / BASEVIDHEIGHT
447 	 );
448 }
449 
450 // XMOD FPS display
451 // moved out of os-specific code for consistency
452 static boolean fpsgraph[TICRATE];
453 static tic_t lasttic;
454 
SCR_DisplayTicRate(void)455 void SCR_DisplayTicRate(void)
456 {
457 	tic_t i;
458 	tic_t ontic = I_GetTime();
459 	tic_t totaltics = 0;
460 	INT32 ticcntcolor = 0;
461 	const INT32 h = vid.height-(8*vid.dupy);
462 
463 	if (gamestate == GS_NULL)
464 		return;
465 
466 	for (i = lasttic + 1; i < TICRATE+lasttic && i < ontic; ++i)
467 		fpsgraph[i % TICRATE] = false;
468 
469 	fpsgraph[ontic % TICRATE] = true;
470 
471 	for (i = 0;i < TICRATE;++i)
472 		if (fpsgraph[i])
473 			++totaltics;
474 
475 	if (totaltics <= TICRATE/2) ticcntcolor = V_REDMAP;
476 	else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP;
477 
478 	if (cv_ticrate.value == 2) // compact counter
479 		V_DrawString(vid.width-(16*vid.dupx), h,
480 			ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%02d", totaltics));
481 	else if (cv_ticrate.value == 1) // full counter
482 	{
483 		V_DrawString(vid.width-(72*vid.dupx), h,
484 			V_YELLOWMAP|V_NOSCALESTART|V_USERHUDTRANS, "FPS:");
485 		V_DrawString(vid.width-(40*vid.dupx), h,
486 			ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%02d/%02u", totaltics, TICRATE));
487 	}
488 
489 	lasttic = ontic;
490 }
491 
SCR_DisplayLocalPing(void)492 void SCR_DisplayLocalPing(void)
493 {
494 	UINT32 ping = playerpingtable[consoleplayer];	// consoleplayer's ping is everyone's ping in a splitnetgame :P
495 	if (cv_showping.value == 1 || (cv_showping.value == 2 && servermaxping && ping > servermaxping))	// only show 2 (warning) if our ping is at a bad level
496 	{
497 		INT32 dispy = cv_ticrate.value ? 180 : 189;
498 		HU_drawPing(307, dispy, ping, true, V_SNAPTORIGHT | V_SNAPTOBOTTOM);
499 	}
500 }
501 
502 
SCR_ClosedCaptions(void)503 void SCR_ClosedCaptions(void)
504 {
505 	UINT8 i;
506 	boolean gamestopped = (paused || P_AutoPause());
507 	INT32 basey = BASEVIDHEIGHT;
508 
509 	if (gamestate != wipegamestate)
510 		return;
511 
512 	if (gamestate == GS_LEVEL)
513 	{
514 		if (promptactive)
515 			basey -= 42;
516 		else if (splitscreen)
517 			basey -= 8;
518 		else if ((modeattacking == ATTACKING_NIGHTS)
519 		|| (!(maptol & TOL_NIGHTS)
520 		&& ((cv_powerupdisplay.value == 2) // "Always"
521 		 || (cv_powerupdisplay.value == 1 && !camera.chase)))) // "First-person only"
522 			basey -= 16;
523 	}
524 
525 	for (i = 0; i < NUMCAPTIONS; i++)
526 	{
527 		INT32 flags, y;
528 		char dot;
529 		boolean music;
530 
531 		if (!closedcaptions[i].s)
532 			continue;
533 
534 		music = (closedcaptions[i].s-S_sfx == sfx_None);
535 
536 		if (music && !gamestopped && (closedcaptions[i].t < flashingtics) && (closedcaptions[i].t & 1))
537 			continue;
538 
539 		flags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_ALLOWLOWERCASE;
540 		y = basey-((i + 2)*10);
541 
542 		if (closedcaptions[i].b)
543 			y -= (closedcaptions[i].b--)*vid.dupy;
544 
545 		if (closedcaptions[i].t < CAPTIONFADETICS)
546 			flags |= (((CAPTIONFADETICS-closedcaptions[i].t)/2)*V_10TRANS);
547 
548 		if (music)
549 			dot = '\x19';
550 		else if (closedcaptions[i].c && closedcaptions[i].c->origin)
551 			dot = '\x1E';
552 		else
553 			dot = ' ';
554 
555 		V_DrawRightAlignedString(BASEVIDWIDTH - 20, y, flags,
556 			va("%c [%s]", dot, (closedcaptions[i].s->caption[0] ? closedcaptions[i].s->caption : closedcaptions[i].s->name)));
557 	}
558 }
559 
SCR_DisplayMarathonInfo(void)560 void SCR_DisplayMarathonInfo(void)
561 {
562 	INT32 flags = V_SNAPTOBOTTOM;
563 	static tic_t entertic, oldentertics = 0, antisplice[2] = {48,0};
564 	const char *str;
565 #if 0 // eh, this probably isn't going to be a problem
566 	if (((signed)marathontime) < 0)
567 	{
568 		flags |= V_REDMAP;
569 		str = "No waiting out the clock to submit a bogus time.";
570 	}
571 	else
572 #endif
573 	{
574 		entertic = I_GetTime();
575 		if (gamecomplete)
576 			flags |= V_YELLOWMAP;
577 		else if (marathonmode & MA_INGAME)
578 			; // see also G_Ticker
579 		else if (marathonmode & MA_INIT)
580 			marathonmode &= ~MA_INIT;
581 		else
582 			marathontime += entertic - oldentertics;
583 
584 		// Create a sequence of primes such that their LCM is nice and big.
585 #define PRIMEV1 13
586 #define PRIMEV2 17 // I can't believe it! I'm on TV!
587 		antisplice[0] += (entertic - oldentertics)*PRIMEV2;
588 		antisplice[0] %= PRIMEV1*((vid.width/vid.dupx)+1);
589 		antisplice[1] += (entertic - oldentertics)*PRIMEV1;
590 		antisplice[1] %= PRIMEV1*((vid.width/vid.dupx)+1);
591 		str = va("%i:%02i:%02i.%02i",
592 			G_TicsToHours(marathontime),
593 			G_TicsToMinutes(marathontime, false),
594 			G_TicsToSeconds(marathontime),
595 			G_TicsToCentiseconds(marathontime));
596 		oldentertics = entertic;
597 	}
598 	V_DrawFill((antisplice[0]/PRIMEV1)-1, BASEVIDHEIGHT-8, 1, 8, V_SNAPTOBOTTOM|V_SNAPTOLEFT);
599 	V_DrawFill((antisplice[0]/PRIMEV1),   BASEVIDHEIGHT-8, 1, 8, V_SNAPTOBOTTOM|V_SNAPTOLEFT|31);
600 	V_DrawFill(BASEVIDWIDTH-((antisplice[1]/PRIMEV1)-1), BASEVIDHEIGHT-8, 1, 8, V_SNAPTOBOTTOM|V_SNAPTORIGHT);
601 	V_DrawFill(BASEVIDWIDTH-((antisplice[1]/PRIMEV1)),   BASEVIDHEIGHT-8, 1, 8, V_SNAPTOBOTTOM|V_SNAPTORIGHT|31);
602 #undef PRIMEV1
603 #undef PRIMEV2
604 	V_DrawPromptBack(-8, cons_backcolor.value);
605 	V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-8, flags, str);
606 }
607