warzone2100/lib/framework/mono.c

506 lines
13 KiB
C

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2007 Warzone Resurrection Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Mono.c
*
* Output to the mono screen
*
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
/* Allow frame header files to be singly included */
#define FRAME_LIB_INCLUDE
#include "mono.h"
#include "types.h"
#define MONO_SCREEN_ADDR 0xB0000
#define SLASH_T 0x09 /* tab */
#define SLASH_N 0x0A /* new line */
static void DBug_DumpString(SDWORD, SDWORD, UBYTE *, UBYTE);
static UBYTE DBug_ExpandString(UBYTE *, UBYTE *, UBYTE, va_list, UBYTE);
static UBYTE DBug_CheckFormatChar(UBYTE);
/*
* NAME
* Debug_MONO_ClearRectangle -- clear a rectangular area
*
* SYNOPSIS
* Debug_MONO_ClearRectangle(leftedge, topedge, width, height);
*
* void Debug_MONO_ClearRectangle(UBYTE, UBYTE, UBYTE, UBYTE);
*
* FUNCTION
* This function clears a rectangular area of the monochrome screen defined by
* <leftedge>, <topedge> and with the dimensions of <width> x <height>.
*
*/
void dbg_MONO_ClearRectangle(SDWORD leftedge,
SDWORD topedge,
SDWORD width,
SDWORD height)
{
UBYTE *pub_rectchar,
ub_newwidth, ub_newheight,
ub_i, ub_j,
ub_leftedge, ub_topedge,
ub_width, ub_height;
ub_leftedge = (UBYTE)leftedge;
ub_topedge = (UBYTE)topedge;
ub_width = (UBYTE)width;
ub_height = (UBYTE)height;
/* do some clipping first */
ub_newwidth = ub_width;
ub_newheight = ub_height;
if ((ub_leftedge + ub_width) > MONO_SCREEN_WIDTH)
{
ub_newwidth = MONO_SCREEN_WIDTH - ub_leftedge;
}
if ((ub_topedge + ub_height) > MONO_SCREEN_HEIGHT)
{
ub_newheight = MONO_SCREEN_HEIGHT - ub_topedge;
}
/* see if it's worth doing */
if ((ub_leftedge < MONO_SCREEN_WIDTH) && (ub_topedge < MONO_SCREEN_HEIGHT) && (ub_newwidth > 2) && (ub_newheight > 2))
{
/* get the screen address of the upper-left corner of the box */
pub_rectchar = (UBYTE *)(MONO_SCREEN_ADDR + ((ub_topedge * (MONO_SCREEN_WIDTH * 2)) + (ub_leftedge * 2)));
/* clear the rectangle */
for (ub_i = 0; ub_i < (ub_newheight - 1); ub_i++)
{
for (ub_j = 0; ub_j < (ub_newwidth * 2); ub_j++)
{
*pub_rectchar++ = '\0';
}
/* go to the start of the next line */
pub_rectchar += ((MONO_SCREEN_WIDTH * 2) - (ub_newwidth * 2));
}
}
}
/*
*
* NAME
* Debug_MONO_SClearScreen -- clear the screen
*
* SYNOPSIS
* Debug_MONO_ClearScreen();
*
* void Debug_MONO_ClearScreen(void);
*
* FUNCTION
* This routine clears the entire monochrome screen.
*
*/
void dbg_MONO_ClearScreen(void)
{
memset((char *)MONO_SCREEN_ADDR, '\0', (MONO_SCREEN_WIDTH * 2) * MONO_SCREEN_HEIGHT);
}
/*
*
* NAME
* Debug_MONO_PrintString -- print a formatted string
*
* SYNOPSIS
* Debug_MONO_PrintString(leftedge, topedge, string);
*
* void Debug_MONO_PrintString(UBYTE, UBYTE, UBYTE *, ...);
*
* FUNCTION
* Debug_MONO_PrintString dumps a printf() style NULL-terminated string to the
* monochrome screen at <leftedge>, <topedge>.
*
* To set the attribute, use %a anywhere in the format string followed by an
* attribute byte (defined in debuglib.h) in the argument list. The default
* attribute is MONO_NORMAL.
*
*/
void dbg_MONO_PrintString(SDWORD ub_leftedge,
SDWORD ub_topedge,
SBYTE *pub_formatstring,
...)
{
va_list val_arglist;
UBYTE *pub_formatstringptr,
aub_percentstring[256],
ub_percentstringindex,
aub_currentcharacter[2],
ub_attribute,
aub_expandedstring[256],
ub_numprinted;
BOOL bool_percent,
bool_printchar;
/* initialise the variables */
pub_formatstringptr = (UBYTE*)pub_formatstring;
aub_percentstring[0] = '\0';
ub_percentstringindex = 0;
aub_currentcharacter[1] = '\0';
ub_attribute = MONO_NORMAL;
aub_expandedstring[0] = '\0';
ub_numprinted = 0;
bool_percent = FALSE;
bool_printchar = FALSE;
/* get the first var arg */
va_start(val_arglist, pub_formatstring);
/* is there a format string? */
if (pub_formatstringptr)
{
/* yes, there is a format string; process it */
while (*pub_formatstringptr)
{
/* get the next character */
aub_currentcharacter[0] = *pub_formatstringptr++;
/* is the character a %? */
if (aub_currentcharacter[0] == '%')
{
/* yes, the character is a %; is the percent flag set? */
if (bool_percent == TRUE)
{
/* yes, the percent flag is set; check for a '%%' case */
if (ub_percentstringindex == 1)
{
aub_percentstring[ub_percentstringindex++] = aub_currentcharacter[0];
aub_percentstring[ub_percentstringindex] = '\0';
/* start a new percent string (sort of) */
ub_percentstringindex = 0;
bool_percent = FALSE;
}
/* expand the percent string */
ub_attribute = DBug_ExpandString(&aub_expandedstring[0], &aub_percentstring[0], ub_attribute, val_arglist, ub_numprinted);
/* print the string */
DBug_DumpString(ub_leftedge, ub_topedge, &aub_expandedstring[0], ub_attribute);
ub_leftedge += (UBYTE)strlen((const char*)aub_expandedstring);
ub_numprinted += (UBYTE)strlen((const char*)aub_expandedstring);
/* start a new percent string; don't do this for a '%%' */
if (ub_percentstringindex != 0)
{
ub_percentstringindex = 0;
aub_percentstring[ub_percentstringindex++] = aub_currentcharacter[0];
aub_percentstring[ub_percentstringindex] = '\0';
}
else
{
/* clear the strings after a '%%' */
aub_expandedstring[0] = '\0';
aub_percentstring[0] = '\0';
}
}
else
{
/* no, the percent flag isn't set; set it */
bool_percent = TRUE;
/* start a new percent string */
ub_percentstringindex = 0;
aub_percentstring[ub_percentstringindex++] = aub_currentcharacter[0];
aub_percentstring[ub_percentstringindex] = '\0';
}
}
else
{
/* no, the character isn't a %; is the percent flag set? */
if (bool_percent == TRUE)
{
/* yes, the percent flag is set; put the character in the percent string */
aub_percentstring[ub_percentstringindex++] = aub_currentcharacter[0];
aub_percentstring[ub_percentstringindex] = '\0';
/* is the character a special format character? */
if (DBug_CheckFormatChar(aub_currentcharacter[0]))
{
/* yes, the character is a special format character */
ub_attribute = DBug_ExpandString(aub_expandedstring, aub_percentstring, ub_attribute, val_arglist, ub_numprinted);
/* print the string */
DBug_DumpString(ub_leftedge, ub_topedge, aub_expandedstring, ub_attribute);
ub_leftedge += (UBYTE)strlen((const char*)aub_expandedstring);
ub_numprinted += (UBYTE)strlen((const char*)aub_expandedstring);
/* clear the strings */
ub_percentstringindex = 0;
aub_expandedstring[0] = '\0';
aub_percentstring[0] = '\0';
/* reset the percent flag */
bool_percent = FALSE;
}
}
else
{
/* no, the percent flag isn't set; is the character a special slash character? */
switch (aub_currentcharacter[0])
{
case SLASH_T:
ub_leftedge += (8 - (ub_leftedge % 8));
ub_numprinted += (8 - (ub_leftedge % 8));
bool_printchar = FALSE;
break;
case SLASH_N:
bool_printchar = FALSE;
break;
default:
bool_printchar = TRUE;
break;
}
/* print the character (maybe) */
if (bool_printchar == TRUE)
{
DBug_DumpString(ub_leftedge++, ub_topedge, &aub_currentcharacter[0], ub_attribute);
ub_numprinted++;
}
/* reset the percent flag */
bool_percent = FALSE;
}
}
}
}
/* do one last check for the percent flag; there may be a string not expanded yet */
if (bool_percent == TRUE)
{
/* there is an unxepanded string; expand it */
ub_attribute = DBug_ExpandString(&aub_expandedstring[0], &aub_percentstring[0], ub_attribute, val_arglist, ub_numprinted);
/* print the string */
DBug_DumpString(ub_leftedge, ub_topedge, &aub_expandedstring[0], ub_attribute);
}
/* clean up the var arg handler */
va_end(val_arglist);
}
static void DBug_DumpString(SDWORD ub_leftedge,
SDWORD ub_topedge,
UBYTE *pub_string,
UBYTE ub_attr)
{
UDWORD ul_stringlength,
ul_i;
UBYTE *pub_stringptr,
*pub_screenptr;
/* init the string pointer */
pub_stringptr = pub_string;
/* is there a string? */
if (pub_stringptr != NULL)
{
/* there is; is it worth printing? */
if ((ub_leftedge < MONO_SCREEN_WIDTH) && (ub_topedge < MONO_SCREEN_HEIGHT))
{
/* it is; init the variables */
pub_screenptr = (UBYTE *)(MONO_SCREEN_ADDR + ((ub_topedge * (MONO_SCREEN_WIDTH * 2)) + (ub_leftedge * 2)));
ul_stringlength = strlen((const char*)pub_stringptr);
/* check for clipping */
if ((ub_leftedge + ul_stringlength) > MONO_SCREEN_WIDTH)
{
ul_stringlength = MONO_SCREEN_WIDTH - ub_leftedge;
}
/* yes there is; print it */
for (ul_i = 0; ul_i < ul_stringlength; ul_i++)
{
*pub_screenptr++ = *pub_stringptr++;
*pub_screenptr++ = ub_attr;
}
}
}
}
static UBYTE DBug_ExpandString(UBYTE *pub_stringbuffer,
UBYTE *pub_percentstring,
UBYTE ub_oldattribute,
va_list val_arglist,
UBYTE ub_numprinted)
{
UBYTE ub_percentchar,
ub_newattribute,
ub_stringlength,
*pub_numberchars;
/* init the variables */
ub_newattribute = ub_oldattribute;
/* find the last character */
ub_stringlength = (UBYTE)strlen((const char*)pub_percentstring) - 1;
ub_percentchar = *(pub_percentstring + ub_stringlength);
/* see what it is */
switch (ub_percentchar)
{
case 'a': /* attribute (char) */
ub_newattribute = va_arg(val_arglist, int);
// changed because gcc 4 says it'll crash here
// was ub_newattribute = va_arg(val_arglist, char);
break;
case 'c': /* character */
sprintf((char*)pub_stringbuffer, (const char*)pub_percentstring, va_arg(val_arglist, int));
// changed because gcc 4 says it'll crash here
// was sprintf((char*)pub_stringbuffer, (const char*)pub_percentstring, va_arg(val_arglist, char));
break;
case 'd': /* signed decimal integer */
case 'i': /* signed decimal integer */
case 'u': /* unsigned decimal integers */
case 'o': /* unsigned octal */
sprintf((char*)pub_stringbuffer, (const char*)pub_percentstring, va_arg(val_arglist, int));
break;
case 'e': /* scientific notation (e) */
case 'E': /* scientific notation (E) */
case 'f': /* decimal floating point */
case 'g': /* uses %e or %f, whichever is shorter */
case 'G': /* uses %E or %F, whichever is shorter */
sprintf((char*)pub_stringbuffer, (const char*)pub_percentstring, va_arg(val_arglist, double));
break;
case 'p': /* displays a pointer */
case 's': /* string */
case 'x': /* unsigned hexadecimal (lower case) */
case 'X': /* unsigned hexadecimal (upper case) */
sprintf((char*)pub_stringbuffer, (const char*)pub_percentstring, va_arg(val_arglist, char *));
break;
case 'n': /* how many characters printed so far */
pub_numberchars = (UBYTE*)va_arg(val_arglist, char *);
*pub_numberchars = ub_numprinted;
break;
case '%': /* percent char (no arg) */
sprintf((char*)pub_stringbuffer, "%%");
break;
default: /* unknown conversion character */
break;
}
/* return a (possibly new) attribute */
return (ub_newattribute);
}
static UBYTE DBug_CheckFormatChar(UBYTE ub_percentchar)
{
UBYTE ub_isformatchar;
/* init the variable */
ub_isformatchar = 0;
/* check to see if it's a format character */
switch (ub_percentchar)
{
case 'a': /* attribute (char) */
case 'c': /* character */
case 'd': /* signed decimal integer */
case 'i': /* signed decimal integer */
case 'e': /* scientific notation (e) */
case 'E': /* scientific notation (E) */
case 'f': /* decimal floating point */
case 'g': /* uses %e or %f, whichever is shorter */
case 'G': /* uses %E or %F, whichever is shorter */
case 'o': /* unsigned octal */
case 'p': /* displays a pointer */
case 's': /* string */
case 'u': /* unsigned decimal integers */
case 'x': /* unsigned hexadecimal (lower case) */
case 'X': /* unsigned hexadecimal (upper case) */
case 'n': /* how many characters printed so far */
case '%': /* percent char (no arg) */
ub_isformatchar = 1;
break;
default: /* unknown conversion character */
ub_isformatchar = 0;
break;
}
return (ub_isformatchar);
}