diff --git a/src/v_video.c b/src/v_video.c
index 12588f9c2fc2bebda04d6292e137778dff504d63..a7173c51947ef051217c2229edef6f1f8e6b9f1d 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -2515,6 +2515,118 @@ void V_DrawRightAlignedSmallThinString(INT32 x, INT32 y, INT32 option, const cha
 	V_DrawRightAlignedSmallThinStringAtFixed((fixed_t)x, (fixed_t)y, option, string);
 }
 
+//
+// Write a string using a supplied font and scale
+// NOTE: the text is centered for screens larger than the base width
+//
+void V_DrawFontString(INT32 x, INT32 y, INT32 option, fixed_t scale, const char *string, patch_t **font)
+{
+	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
+	const char *ch = string;
+	INT32 charflags = (option & V_CHARCOLORMASK);
+	INT32 spacewidth = 4, charwidth = 0;
+
+	INT32 lowercase = (option & V_ALLOWLOWERCASE);
+	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
+
+	if (option & V_NOSCALESTART)
+	{
+		dupx = vid.dupx;
+		dupy = vid.dupy;
+		scrwidth = vid.width;
+	}
+	else
+	{
+		dupx = dupy = 1;
+		scrwidth = vid.width/vid.dupx;
+		left = (scrwidth - BASEVIDWIDTH)/2;
+		scrwidth -= left;
+	}
+
+	if (option & V_NOSCALEPATCH)
+		scrwidth *= vid.dupx;
+
+	switch (option & V_SPACINGMASK)
+	{
+		case V_MONOSPACE:
+			spacewidth = 8;
+			break;
+		case V_6WIDTHSPACE:
+			spacewidth = 6;
+		default:
+			break;
+	}
+
+	spacewidth = (spacewidth<<FRACBITS * scale<<FRACBITS)>>FRACBITS;
+	for (;;ch++)
+	{
+		if (!*ch)
+			break;
+		if (*ch & 0x80) //color parsing -x 2.16.09
+		{
+			// manually set flags override color codes
+			if (!(option & V_CHARCOLORMASK))
+				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
+			continue;
+		}
+		if (*ch == '\n')
+		{
+			cx = x;
+
+			if (option & V_RETURN8)
+				cy += 4*dupy;
+			else
+				cy += 6*dupy;
+
+			continue;
+		}
+
+		c = *ch;
+		if (!lowercase)
+			c = toupper(c);
+		c -= HU_FONTSTART;
+
+		if (c < 0 || c >= HU_FONTSIZE || !font[c])
+		{
+			cx += spacewidth * dupx;
+			continue;
+		}
+
+		if (charwidth)
+		{
+			w = charwidth * dupx;
+			center = w/2 - font[c]->width*dupx/4;
+		}
+		else
+			w = font[c]->width * dupx / 2;
+
+		if (cx > scrwidth)
+			continue;
+		if (cx+left + w < 0) //left boundary check
+		{
+			cx += w;
+			continue;
+		}
+
+		V_DrawFixedPatch((cx + center)<<FRACBITS, cy<<FRACBITS, scale, option, font[c], V_GetStringColormap(charflags));
+
+		cx += w;
+	}
+}
+
+void V_DrawCenteredFontString(INT32 x, INT32 y, INT32 option, fixed_t scale, const char *string, patch_t **font)
+{
+	x -= V_FontStringWidth(string, option, font)/2;
+	V_DrawFontString(x, y, option, scale, string, font);
+}
+
+
+void V_DrawRightAlignedFontString(INT32 x, INT32 y, INT32 option, fixed_t scale, const char *string, patch_t **font)
+{
+	x -= V_FontStringWidth(string, option, font);
+	V_DrawFontString(x, y, option, scale, string, font);
+}
+
 // Draws a string at a fixed_t location.
 void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 {
@@ -3575,6 +3687,40 @@ boolean *heatshifter = NULL;
 INT32 lastheight = 0;
 INT32 heatindex[2] = { 0, 0 };
 
+//
+// Find string width from supplied font chars, 0.5x scale
+//
+INT32 V_FontStringWidth(const char *string, INT32 option, patch_t **font)
+{
+	INT32 c, w = 0;
+	INT32 spacewidth = 2, charwidth = 0;
+	size_t i;
+
+	switch (option & V_SPACINGMASK)
+	{
+		case V_MONOSPACE:
+			spacewidth = 8;
+			break;
+		case V_6WIDTHSPACE:
+			spacewidth = 6;
+		default:
+			break;
+	}
+
+	for (i = 0; i < strlen(string); i++)
+	{
+		if (string[i] & 0x80)
+			continue;
+		c = toupper(string[i]) - HU_FONTSTART;
+		if (c < 0 || c >= HU_FONTSIZE || !font[c])
+			w += spacewidth;
+		else
+			w += (charwidth ? charwidth : (font[c]->width));
+	}
+
+	return w;
+}
+
 //
 // V_DoPostProcessor
 //
diff --git a/src/v_video.h b/src/v_video.h
index bcb39706ef26f6903a33d488242d4d23dce6f3ee..14e0e3b7d7d26b224747b1e85a6db277da2ee924 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -228,6 +228,11 @@ void V_DrawSmallThinString(INT32 x, INT32 y, INT32 option, const char *string);
 void V_DrawCenteredSmallThinString(INT32 x, INT32 y, INT32 option, const char *string);
 void V_DrawRightAlignedSmallThinString(INT32 x, INT32 y, INT32 option, const char *string);
 
+// draw a string using a supplied font and scale
+void V_DrawFontString(INT32 x, INT32 y, INT32 option, fixed_t scale, const char *string, patch_t **font);
+void V_DrawCenteredFontString(INT32 x, INT32 y, INT32 option, fixed_t scale, const char *string, patch_t **font);
+void V_DrawRightAlignedFontString(INT32 x, INT32 y, INT32 option, fixed_t scale, const char *string, patch_t **font);
+
 // draw a string using the hu_font at fixed_t coordinates
 void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string);
 void V_DrawCenteredStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string);
@@ -274,6 +279,8 @@ INT32 V_SmallStringWidth(const char *string, INT32 option);
 INT32 V_ThinStringWidth(const char *string, INT32 option);
 // Find string width from tny_font chars, 0.5x scale
 INT32 V_SmallThinStringWidth(const char *string, INT32 option);
+// Find string width from supplied font chars
+INT32 V_FontStringWidth(const char *string, INT32 option, patch_t **font);
 
 void V_DoPostProcessor(INT32 view, postimg_t type, INT32 param);