/*
Copyright (C) 1997-2001 Id Software, Inc.

This program 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.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include "q_shared.h"

vec3_t vec3_origin = { 0, 0, 0 };


const color_t colorBlack	= {   0,   0,   0, 255 };
const color_t colorRed		= { 255,   0,   0, 255 };
const color_t colorGreen	= {   0, 255,   0, 255 };
const color_t colorYellow	= { 255, 255,   0, 255 };
const color_t colorBlue		= {   0,   0, 255, 255 };
const color_t colorCyan		= {   0, 255, 255, 255 };
const color_t colorMagenta	= { 255,   0, 255, 255 };
const color_t colorWhite	= { 255, 255, 255, 255 };

const color_t colorTable[8] = {
	{   0,   0,   0, 255 },
	{ 255,   0,   0, 255 },
	{   0, 255,   0, 255 },
	{ 255, 255,   0, 255 },
	{   0,   0, 255, 255 },
	{   0, 255, 255, 255 },
	{ 255,   0, 255, 255 },
	{ 255, 255, 255, 255 }
};

vec3_t bytedirs[NUMVERTEXNORMALS] = {
{-0.525731, 0.000000, 0.850651}, 
{-0.442863, 0.238856, 0.864188}, 
{-0.295242, 0.000000, 0.955423}, 
{-0.309017, 0.500000, 0.809017}, 
{-0.162460, 0.262866, 0.951056}, 
{0.000000, 0.000000, 1.000000}, 
{0.000000, 0.850651, 0.525731}, 
{-0.147621, 0.716567, 0.681718}, 
{0.147621, 0.716567, 0.681718}, 
{0.000000, 0.525731, 0.850651}, 
{0.309017, 0.500000, 0.809017}, 
{0.525731, 0.000000, 0.850651}, 
{0.295242, 0.000000, 0.955423}, 
{0.442863, 0.238856, 0.864188}, 
{0.162460, 0.262866, 0.951056}, 
{-0.681718, 0.147621, 0.716567}, 
{-0.809017, 0.309017, 0.500000}, 
{-0.587785, 0.425325, 0.688191}, 
{-0.850651, 0.525731, 0.000000}, 
{-0.864188, 0.442863, 0.238856}, 
{-0.716567, 0.681718, 0.147621}, 
{-0.688191, 0.587785, 0.425325}, 
{-0.500000, 0.809017, 0.309017}, 
{-0.238856, 0.864188, 0.442863}, 
{-0.425325, 0.688191, 0.587785}, 
{-0.716567, 0.681718, -0.147621}, 
{-0.500000, 0.809017, -0.309017}, 
{-0.525731, 0.850651, 0.000000}, 
{0.000000, 0.850651, -0.525731}, 
{-0.238856, 0.864188, -0.442863}, 
{0.000000, 0.955423, -0.295242}, 
{-0.262866, 0.951056, -0.162460}, 
{0.000000, 1.000000, 0.000000}, 
{0.000000, 0.955423, 0.295242}, 
{-0.262866, 0.951056, 0.162460}, 
{0.238856, 0.864188, 0.442863}, 
{0.262866, 0.951056, 0.162460}, 
{0.500000, 0.809017, 0.309017}, 
{0.238856, 0.864188, -0.442863}, 
{0.262866, 0.951056, -0.162460}, 
{0.500000, 0.809017, -0.309017}, 
{0.850651, 0.525731, 0.000000}, 
{0.716567, 0.681718, 0.147621}, 
{0.716567, 0.681718, -0.147621}, 
{0.525731, 0.850651, 0.000000}, 
{0.425325, 0.688191, 0.587785}, 
{0.864188, 0.442863, 0.238856}, 
{0.688191, 0.587785, 0.425325}, 
{0.809017, 0.309017, 0.500000}, 
{0.681718, 0.147621, 0.716567}, 
{0.587785, 0.425325, 0.688191}, 
{0.955423, 0.295242, 0.000000}, 
{1.000000, 0.000000, 0.000000}, 
{0.951056, 0.162460, 0.262866}, 
{0.850651, -0.525731, 0.000000}, 
{0.955423, -0.295242, 0.000000}, 
{0.864188, -0.442863, 0.238856}, 
{0.951056, -0.162460, 0.262866}, 
{0.809017, -0.309017, 0.500000}, 
{0.681718, -0.147621, 0.716567}, 
{0.850651, 0.000000, 0.525731}, 
{0.864188, 0.442863, -0.238856}, 
{0.809017, 0.309017, -0.500000}, 
{0.951056, 0.162460, -0.262866}, 
{0.525731, 0.000000, -0.850651}, 
{0.681718, 0.147621, -0.716567}, 
{0.681718, -0.147621, -0.716567}, 
{0.850651, 0.000000, -0.525731}, 
{0.809017, -0.309017, -0.500000}, 
{0.864188, -0.442863, -0.238856}, 
{0.951056, -0.162460, -0.262866}, 
{0.147621, 0.716567, -0.681718}, 
{0.309017, 0.500000, -0.809017}, 
{0.425325, 0.688191, -0.587785}, 
{0.442863, 0.238856, -0.864188}, 
{0.587785, 0.425325, -0.688191}, 
{0.688191, 0.587785, -0.425325}, 
{-0.147621, 0.716567, -0.681718}, 
{-0.309017, 0.500000, -0.809017}, 
{0.000000, 0.525731, -0.850651}, 
{-0.525731, 0.000000, -0.850651}, 
{-0.442863, 0.238856, -0.864188}, 
{-0.295242, 0.000000, -0.955423}, 
{-0.162460, 0.262866, -0.951056}, 
{0.000000, 0.000000, -1.000000}, 
{0.295242, 0.000000, -0.955423}, 
{0.162460, 0.262866, -0.951056}, 
{-0.442863, -0.238856, -0.864188}, 
{-0.309017, -0.500000, -0.809017}, 
{-0.162460, -0.262866, -0.951056}, 
{0.000000, -0.850651, -0.525731}, 
{-0.147621, -0.716567, -0.681718}, 
{0.147621, -0.716567, -0.681718}, 
{0.000000, -0.525731, -0.850651}, 
{0.309017, -0.500000, -0.809017}, 
{0.442863, -0.238856, -0.864188}, 
{0.162460, -0.262866, -0.951056}, 
{0.238856, -0.864188, -0.442863}, 
{0.500000, -0.809017, -0.309017}, 
{0.425325, -0.688191, -0.587785}, 
{0.716567, -0.681718, -0.147621}, 
{0.688191, -0.587785, -0.425325}, 
{0.587785, -0.425325, -0.688191}, 
{0.000000, -0.955423, -0.295242}, 
{0.000000, -1.000000, 0.000000}, 
{0.262866, -0.951056, -0.162460}, 
{0.000000, -0.850651, 0.525731}, 
{0.000000, -0.955423, 0.295242}, 
{0.238856, -0.864188, 0.442863}, 
{0.262866, -0.951056, 0.162460}, 
{0.500000, -0.809017, 0.309017}, 
{0.716567, -0.681718, 0.147621}, 
{0.525731, -0.850651, 0.000000}, 
{-0.238856, -0.864188, -0.442863}, 
{-0.500000, -0.809017, -0.309017}, 
{-0.262866, -0.951056, -0.162460}, 
{-0.850651, -0.525731, 0.000000}, 
{-0.716567, -0.681718, -0.147621}, 
{-0.716567, -0.681718, 0.147621}, 
{-0.525731, -0.850651, 0.000000}, 
{-0.500000, -0.809017, 0.309017}, 
{-0.238856, -0.864188, 0.442863}, 
{-0.262866, -0.951056, 0.162460}, 
{-0.864188, -0.442863, 0.238856}, 
{-0.809017, -0.309017, 0.500000}, 
{-0.688191, -0.587785, 0.425325}, 
{-0.681718, -0.147621, 0.716567}, 
{-0.442863, -0.238856, 0.864188}, 
{-0.587785, -0.425325, 0.688191}, 
{-0.309017, -0.500000, 0.809017}, 
{-0.147621, -0.716567, 0.681718}, 
{-0.425325, -0.688191, 0.587785}, 
{-0.162460, -0.262866, 0.951056}, 
{0.442863, -0.238856, 0.864188}, 
{0.162460, -0.262866, 0.951056}, 
{0.309017, -0.500000, 0.809017}, 
{0.147621, -0.716567, 0.681718}, 
{0.000000, -0.525731, 0.850651}, 
{0.425325, -0.688191, 0.587785}, 
{0.587785, -0.425325, 0.688191}, 
{0.688191, -0.587785, 0.425325}, 
{-0.955423, 0.295242, 0.000000}, 
{-0.951056, 0.162460, 0.262866}, 
{-1.000000, 0.000000, 0.000000}, 
{-0.850651, 0.000000, 0.525731}, 
{-0.955423, -0.295242, 0.000000}, 
{-0.951056, -0.162460, 0.262866}, 
{-0.864188, 0.442863, -0.238856}, 
{-0.951056, 0.162460, -0.262866}, 
{-0.809017, 0.309017, -0.500000}, 
{-0.864188, -0.442863, -0.238856}, 
{-0.951056, -0.162460, -0.262866}, 
{-0.809017, -0.309017, -0.500000}, 
{-0.681718, 0.147621, -0.716567}, 
{-0.681718, -0.147621, -0.716567}, 
{-0.850651, 0.000000, -0.525731}, 
{-0.688191, 0.587785, -0.425325}, 
{-0.587785, 0.425325, -0.688191}, 
{-0.425325, 0.688191, -0.587785}, 
{-0.425325, -0.688191, -0.587785}, 
{-0.587785, -0.425325, -0.688191}, 
{-0.688191, -0.587785, -0.425325}, 
};

int DirToByte( const vec3_t dir ) {
	int		i, best;
	float	d, bestd;
	
	if( !dir ) {
		return 0;
	}

	bestd = 0;
	best = 0;
	for( i = 0; i < NUMVERTEXNORMALS; i++ ) {
		d = DotProduct( dir, bytedirs[i] );
		if( d > bestd ) {
			bestd = d;
			best = i;
		}
	}
	
	return best;
}

void ByteToDir( int index, vec3_t dir ) {
	if( index < 0 || index >= NUMVERTEXNORMALS ) {
		Com_Error( ERR_FATAL, "ByteToDir: illegal index" );
	}

	VectorCopy( bytedirs[index], dir );
}

//============================================================================

#ifdef __MSC_VER
#pragma optimize( "", off )
#endif

void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees )
{
	float	m[3][3];
	float	im[3][3];
	float	zrot[3][3];
	float	tmpmat[3][3];
	float	rot[3][3];
	int	i;
	vec3_t vr, vup, vf;

	vf[0] = dir[0];
	vf[1] = dir[1];
	vf[2] = dir[2];

	PerpendicularVector( vr, dir );
	CrossProduct( vr, vf, vup );

	m[0][0] = vr[0];
	m[1][0] = vr[1];
	m[2][0] = vr[2];

	m[0][1] = vup[0];
	m[1][1] = vup[1];
	m[2][1] = vup[2];

	m[0][2] = vf[0];
	m[1][2] = vf[1];
	m[2][2] = vf[2];

	memcpy( im, m, sizeof( im ) );

	im[0][1] = m[1][0];
	im[0][2] = m[2][0];
	im[1][0] = m[0][1];
	im[1][2] = m[2][1];
	im[2][0] = m[0][2];
	im[2][1] = m[1][2];

	memset( zrot, 0, sizeof( zrot ) );
	zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;

	zrot[0][0] = cos( DEG2RAD( degrees ) );
	zrot[0][1] = sin( DEG2RAD( degrees ) );
	zrot[1][0] = -sin( DEG2RAD( degrees ) );
	zrot[1][1] = cos( DEG2RAD( degrees ) );

	R_ConcatRotations( m, zrot, tmpmat );
	R_ConcatRotations( tmpmat, im, rot );

	for ( i = 0; i < 3; i++ )
	{
		dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
	}
}

#ifdef __MSC_VER
#pragma optimize( "", on )
#endif



void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
	float		angle;
	float		sr, sp, sy, cr, cp, cy;
	// static to help MS compiler fp bugs

	angle = angles[YAW] * (M_PI*2 / 360);
	sy = sin(angle);
	cy = cos(angle);
	angle = angles[PITCH] * (M_PI*2 / 360);
	sp = sin(angle);
	cp = cos(angle);
	angle = angles[ROLL] * (M_PI*2 / 360);
	sr = sin(angle);
	cr = cos(angle);

	if (forward)
	{
		forward[0] = cp*cy;
		forward[1] = cp*sy;
		forward[2] = -sp;
	}
	if (right)
	{
		right[0] = (-1*sr*sp*cy+-1*cr*-sy);
		right[1] = (-1*sr*sp*sy+-1*cr*cy);
		right[2] = -1*sr*cp;
	}
	if (up)
	{
		up[0] = (cr*sp*cy+-sr*-sy);
		up[1] = (cr*sp*sy+-sr*cy);
		up[2] = cr*cp;
	}
}


void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal )
{
	float d;
	vec3_t n;
	float inv_denom;

	inv_denom = 1.0F / DotProduct( normal, normal );

	d = DotProduct( normal, p ) * inv_denom;

	n[0] = normal[0] * inv_denom;
	n[1] = normal[1] * inv_denom;
	n[2] = normal[2] * inv_denom;

	dst[0] = p[0] - d * n[0];
	dst[1] = p[1] - d * n[1];
	dst[2] = p[2] - d * n[2];
}

/*
** assumes "src" is normalized
*/
void PerpendicularVector( vec3_t dst, const vec3_t src )
{
	int	pos;
	int i;
	float minelem = 1.0F;
	vec3_t tempvec;

	/*
	** find the smallest magnitude axially aligned vector
	*/
	for ( pos = 0, i = 0; i < 3; i++ )
	{
		if ( fabs( src[i] ) < minelem )
		{
			pos = i;
			minelem = fabs( src[i] );
		}
	}
	tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
	tempvec[pos] = 1.0F;

	/*
	** project the point onto the plane defined by src
	*/
	ProjectPointOnPlane( dst, tempvec, src );

	/*
	** normalize the result
	*/
	VectorNormalize( dst );
}



/*
================
R_ConcatRotations
================
*/
void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3])
{
	out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
				in1[0][2] * in2[2][0];
	out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
				in1[0][2] * in2[2][1];
	out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
				in1[0][2] * in2[2][2];
	out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
				in1[1][2] * in2[2][0];
	out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
				in1[1][2] * in2[2][1];
	out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
				in1[1][2] * in2[2][2];
	out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
				in1[2][2] * in2[2][0];
	out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
				in1[2][2] * in2[2][1];
	out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
				in1[2][2] * in2[2][2];
}


/*
================
R_ConcatTransforms
================
*/
void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
{
	out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
				in1[0][2] * in2[2][0];
	out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
				in1[0][2] * in2[2][1];
	out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
				in1[0][2] * in2[2][2];
	out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
				in1[0][2] * in2[2][3] + in1[0][3];
	out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
				in1[1][2] * in2[2][0];
	out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
				in1[1][2] * in2[2][1];
	out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
				in1[1][2] * in2[2][2];
	out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
				in1[1][2] * in2[2][3] + in1[1][3];
	out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
				in1[2][2] * in2[2][0];
	out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
				in1[2][2] * in2[2][1];
	out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
				in1[2][2] * in2[2][2];
	out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
				in1[2][2] * in2[2][3] + in1[2][3];
}


//============================================================================


float Q_fabs( float f ) {
	uint32 tmp = *( uint32 * )&f;
	tmp &= 0x7FFFFFFF;
	return *( float * )&tmp;
}

#if id386 && ( defined _MSC_VER )
#pragma warning( disable:4035 )
__declspec( naked ) long Q_ftol( float f )
{
	static int tmp;
	__asm fld dword ptr [esp+4]
	__asm fistp tmp
	__asm mov eax, tmp
	__asm ret
}
#pragma warning( default:4035 )
#endif

/*
===============
LerpAngle

===============
*/
float LerpAngle (float a2, float a1, float frac)
{
	if (a1 - a2 > 180)
		a1 -= 360;
	if (a1 - a2 < -180)
		a1 += 360;
	return a2 + frac * (a1 - a2);
}


float	anglemod(float a)
{
	a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);
	return a;
}

// this is the slow, general version
int BoxOnPlaneSide2 (vec3_t emins, vec3_t emaxs, struct cplane_s *p)
{
	int		i;
	float	dist1, dist2;
	int		sides;
	vec3_t	corners[2];

	for (i=0 ; i<3 ; i++)
	{
		if (p->normal[i] < 0)
		{
			corners[0][i] = emins[i];
			corners[1][i] = emaxs[i];
		}
		else
		{
			corners[1][i] = emins[i];
			corners[0][i] = emaxs[i];
		}
	}
	dist1 = DotProduct (p->normal, corners[0]) - p->dist;
	dist2 = DotProduct (p->normal, corners[1]) - p->dist;
	sides = 0;
	if (dist1 >= 0)
		sides = 1;
	if (dist2 < 0)
		sides |= 2;

	return sides;
}

/*
==================
BoxOnPlaneSide

Returns 1, 2, or 1 + 2
==================
*/
#if !id386 || !defined _MSC_VER
int BoxOnPlaneSide( vec3_t emins, vec3_t emaxs, cplane_t *p )
{
	float	dist1, dist2;
	int		sides;

// fast axial cases
	if (p->type < 3)
	{
		if (p->dist <= emins[p->type])
			return 1;
		if (p->dist >= emaxs[p->type])
			return 2;
		return 3;
	}
	
// general case
	switch (p->signbits)
	{
	case 0:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
		break;
	case 1:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
		break;
	case 2:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
		break;
	case 3:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
		break;
	case 4:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
		break;
	case 5:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
		break;
	case 6:
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
		break;
	case 7:
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
		break;
	default:
		dist1 = dist2 = 0;		// shut up compiler
		break;
	}

	sides = 0;
	if (dist1 >= p->dist)
		sides = 1;
	if (dist2 < p->dist)
		sides |= 2;

	return sides;
}
#else
#pragma warning( disable: 4035 )

__declspec( naked ) int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, cplane_t *p)
{
	static int bops_initialized;
	static int Ljmptab[8];

	__asm {

		push ebx
			
		cmp bops_initialized, 1
		je  initialized
		mov bops_initialized, 1
		
		mov Ljmptab[0*4], offset Lcase0
		mov Ljmptab[1*4], offset Lcase1
		mov Ljmptab[2*4], offset Lcase2
		mov Ljmptab[3*4], offset Lcase3
		mov Ljmptab[4*4], offset Lcase4
		mov Ljmptab[5*4], offset Lcase5
		mov Ljmptab[6*4], offset Lcase6
		mov Ljmptab[7*4], offset Lcase7
			
initialized:

		mov edx,ds:dword ptr[4+12+esp]
		mov ecx,ds:dword ptr[4+4+esp]
		xor eax,eax
		mov ebx,ds:dword ptr[4+8+esp]
		mov al,ds:byte ptr[17+edx]
		cmp al,8
		jge Lerror
		fld ds:dword ptr[0+edx]
		fld st(0)
		jmp dword ptr[Ljmptab+eax*4]
Lcase0:
		fmul ds:dword ptr[ebx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ebx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
		jmp LSetSides
Lcase1:
		fmul ds:dword ptr[ecx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ebx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
		jmp LSetSides
Lcase2:
		fmul ds:dword ptr[ebx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ecx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
		jmp LSetSides
Lcase3:
		fmul ds:dword ptr[ecx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ecx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
		jmp LSetSides
Lcase4:
		fmul ds:dword ptr[ebx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ebx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
		jmp LSetSides
Lcase5:
		fmul ds:dword ptr[ecx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ebx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
		jmp LSetSides
Lcase6:
		fmul ds:dword ptr[ebx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ecx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ecx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
		jmp LSetSides
Lcase7:
		fmul ds:dword ptr[ecx]
		fld ds:dword ptr[0+4+edx]
		fxch st(2)
		fmul ds:dword ptr[ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[4+ecx]
		fld ds:dword ptr[0+8+edx]
		fxch st(2)
		fmul ds:dword ptr[4+ebx]
		fxch st(2)
		fld st(0)
		fmul ds:dword ptr[8+ecx]
		fxch st(5)
		faddp st(3),st(0)
		fmul ds:dword ptr[8+ebx]
		fxch st(1)
		faddp st(3),st(0)
		fxch st(3)
		faddp st(2),st(0)
LSetSides:
		faddp st(2),st(0)
		fcomp ds:dword ptr[12+edx]
		xor ecx,ecx
		fnstsw ax
		fcomp ds:dword ptr[12+edx]
		and ah,1
		xor ah,1
		add cl,ah
		fnstsw ax
		and ah,1
		add ah,ah
		add cl,ah
		pop ebx
		mov eax,ecx
		ret
Lerror:
		int 3
	}
}
#pragma warning( default: 4035 )
#endif

void ClearBounds (vec3_t mins, vec3_t maxs)
{
	mins[0] = mins[1] = mins[2] = 99999;
	maxs[0] = maxs[1] = maxs[2] = -99999;
}

void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs)
{
	int		i;
	vec_t	val;

	for (i=0 ; i<3 ; i++)
	{
		val = v[i];
		if (val < mins[i])
			mins[i] = val;
		if (val > maxs[i])
			maxs[i] = val;
	}
}

void UnionBounds( vec3_t a[2], vec3_t b[2], vec3_t c[2] ) {
	c[0][0] = b[0][0] < a[0][0] ? b[0][0] : a[0][0];
	c[0][1] = b[0][1] < a[0][1] ? b[0][1] : a[0][1];
	c[0][2] = b[0][2] < a[0][2] ? b[0][2] : a[0][2];

	c[1][0] = b[1][0] > a[1][0] ? b[1][0] : a[1][0];
	c[1][1] = b[1][1] > a[1][1] ? b[1][1] : a[1][1];
	c[1][2] = b[1][2] > a[1][2] ? b[1][2] : a[1][2];
}

vec_t VectorNormalize (vec3_t v)
{
	float	length, ilength;

	length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
	length = sqrt (length);		// FIXME

	if (length)
	{
		ilength = 1/length;
		v[0] *= ilength;
		v[1] *= ilength;
		v[2] *= ilength;
	}
		
	return length;

}

vec_t VectorNormalize2 (vec3_t v, vec3_t out)
{
	float	length, ilength;

	length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
	length = sqrt (length);		// FIXME

	if (length)
	{
		ilength = 1/length;
		out[0] = v[0]*ilength;
		out[1] = v[1]*ilength;
		out[2] = v[2]*ilength;
	}
		
	return length;

}

/*
=================
RadiusFromBounds
=================
*/
vec_t RadiusFromBounds (const vec3_t mins, const vec3_t maxs)
{
	int		i;
	vec3_t	corner;
	vec_t a, b;

	for (i=0 ; i<3 ; i++)
	{
		a = fabs(mins[i]);
		b = fabs(maxs[i]);
		corner[i] = a > b ? a : b;
	}

	return VectorLength (corner);
}

#if 0
void _VectorMA( vec3_t veca, float scale, vec3_t vecb, vec3_t vecc )
{
	vecc[0] = veca[0] + scale*vecb[0];
	vecc[1] = veca[1] + scale*vecb[1];
	vecc[2] = veca[2] + scale*vecb[2];
}


vec_t _DotProduct (vec3_t v1, vec3_t v2)
{
	return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}

void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out)
{
	out[0] = veca[0]-vecb[0];
	out[1] = veca[1]-vecb[1];
	out[2] = veca[2]-vecb[2];
}

void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out)
{
	out[0] = veca[0]+vecb[0];
	out[1] = veca[1]+vecb[1];
	out[2] = veca[2]+vecb[2];
}

void _VectorCopy (vec3_t in, vec3_t out)
{
	out[0] = in[0];
	out[1] = in[1];
	out[2] = in[2];
}

void _CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross)
{
	cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
	cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
	cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
}

double sqrt(double x);

vec_t VectorLength(vec3_t v)
{
	int		i;
	float	length;
	
	length = 0;
	for (i=0 ; i< 3 ; i++)
		length += v[i]*v[i];
	length = sqrt (length);		// FIXME

	return length;
}

void _VectorNegate (vec3_t v)
{
	v[0] = -v[0];
	v[1] = -v[1];
	v[2] = -v[2];
}

void _VectorScale (vec3_t in, vec_t scale, vec3_t out)
{
	out[0] = in[0]*scale;
	out[1] = in[1]*scale;
	out[2] = in[2]*scale;
}
#endif

int Q_log2(int val)
{
	int answer=0;
	while (val>>=1)
		answer++;
	return answer;
}

/*
===============
Q_CeilPowerOfTwo
===============
*/
int Q_CeilPowerOfTwo( int value ) {
	int i;

	for( i = 1; i < value; i <<= 1 )
		;

	return i;
}


/*
====================
Com_CalcFov
====================
*/
float Com_CalcFov( float fov_x, float width, float height ) {
	float	a;
	float	x;

	if( fov_x < 1 || fov_x > 179 )
		Com_Error( ERR_DROP, "Com_CalcFov: bad fov: %f", fov_x );

	x = width / tan( fov_x / 360 * M_PI );

	a = atan( height / x );
	a = a * 360/ M_PI;

	return a;
}

void SetPlaneType( cplane_t *plane ) {
	vec_t *normal = plane->normal;
	
	if( normal[0] == 1 ) {
		plane->type = PLANE_X;
		return;
	}
	if( normal[1] == 1 ) {
		plane->type = PLANE_Y;
		return;
	}
	if( normal[2] == 1 ) {
		plane->type = PLANE_Z;
		return;
	}

	plane->type = PLANE_NON_AXIAL;
}

void SetPlaneSignbits( cplane_t *plane ) {
    int bits = 0;
    
    if( plane->normal[0] < 0 ) {
        bits |= 1;
    }
    if( plane->normal[1] < 0 ) {
        bits |= 2;
    }
    if( plane->normal[2] < 0 ) {
        bits |= 4;
    }
    plane->signbits = bits;
}



//====================================================================================

/*
============
COM_SkipPath
============
*/
char *COM_SkipPath( const char *pathname ) {
	char	*last;

	if( !pathname ) {
		Com_Error( ERR_FATAL, "COM_SkipPath: NULL" );
	}
	
	last = (char *)pathname;
	while( *pathname ) {
		if( *pathname == '/' )
			last = (char *)pathname + 1;
		pathname++;
	}
	return last;
}

/*
============
COM_StripExtension
============
*/
void COM_StripExtension( const char *in, char *out, int outSize ) {
	char *s;

	Q_strncpyz( out, in, outSize );

	s = out + strlen( out );
	
	while( s != out ) {
		if( *s == '/' ) {
			break;
		}
		if( *s == '.' ) {
			*s = 0;
			break;
		}
		s--;
	}
}

/*
============
COM_FileExtension
============
*/
char *COM_FileExtension( const char *in ) {
	const char *s;
	const char *last;

	if( !in ) {
		Com_Error( ERR_FATAL, "COM_FileExtension: NULL" );
	}

	s = in + strlen( in );
	last = s;
	
	while( s != in ) {
		if( *s == '/' ) {
			break;
		}
		if( *s == '.' ) {
			return (char *)s;
		}
		s--;
	}

	return (char *)last;
}

/*
============
COM_FileBase
============
*/
void COM_FileBase (char *in, char *out)
{
	char *s, *s2;
	
	s = in + strlen(in) - 1;
	
	while (s != in && *s != '.')
		s--;
	
	for (s2 = s ; s2 != in && *s2 != '/' ; s2--)
	;
	
	if (s-s2 < 2)
		out[0] = 0;
	else
	{
		s--;
		strncpy (out,s2+1, s-s2);
		out[s-s2] = 0;
	}
}

/*
============
COM_FilePath

Returns the path up to, but not including the last /
============
*/
void COM_FilePath( const char *in, char *out, int outSize ) {
	char *s;

	Q_strncpyz( out, in, outSize );
	
	s = out + strlen( out );
	
	while( s != out && *s != '/' )
		s--;

	*s = 0;
}


/*
==================
COM_DefaultExtension
==================
*/
void COM_DefaultExtension( char *path, const char *extension, int pathSize ) {
	char    *src;
//
// if path doesn't have a .EXT, append extension
// (extension should include the .)
//
	if( *path ) {
		src = path + strlen( path ) - 1;

		while( *src != '/' && src != path ) {
			if( *src == '.' )
				return;                 // it has an extension
			src--;
		}
	}

	Q_strcat( path, pathSize, extension );
}

/*
==================
COM_IsNumeric

Returns true if the given string is valid representation
of floating point or integer number.
==================
*/
qboolean COM_IsNumeric( const char *string ) {
	int c;

	if( !string ) {
		return qfalse;
	}

	if( !*string ) {
		return qfalse;
	}

	do {
		c = *string++;
		if( Q_isdigit( c ) ) {
			continue;
		}
		if( c != '-' && c != '.' && c != ' ' ) {
			return qfalse;
		}
	} while( *string );

	return qtrue;

}

/* Parses hexadecimal number until it encounters
 * illegible symbol or end of string.
 * Does not check for overflow.
 */
uint32 COM_ParseHex( const char *string ) {
	int ch;
	uint32 result, digit;

	result = 0;
	while( *string ) {
		ch = *string++;
		if( ch >= '0' && ch <= '9' ) {
			digit = ch - '0';
		} else if( ch >= 'a' && ch <= 'f' ) {
			digit = ch - 'a' + 10;
		} else if( ch >= 'A' && ch <= 'F' ) {
			digit = ch - 'A' + 10;
		} else {
			break;
		}

		result = digit | ( result << 4 );
	}

	return result;

}

/*
=================
SortStrcmp
=================
*/
int SortStrcmp( const void *p1, const void *p2 ) {
	const char *s1 = *(const char **)p1;
	const char *s2 = *(const char **)p2;

	return strcmp( s1, s2 );
}

/*
=================
Com_WildCmp

Wildcard compare.
Returns non-zero if matches, zero otherwise.
=================
*/
int Com_WildCmp( const char *filter, const char *string, qboolean ignoreCase ) {
	switch( *filter ) {
	case '\0':
		return !*string;

	case '*':
		return Com_WildCmp( filter + 1, string, ignoreCase ) || (*string && Com_WildCmp( filter, string + 1, ignoreCase ));

	case '?':
		return *string && Com_WildCmp( filter + 1, string + 1, ignoreCase );

	default:
		return ((*filter == *string) || (ignoreCase && (Q_toupper( *filter ) == Q_toupper( *string )))) && Com_WildCmp( filter + 1, string + 1, ignoreCase );
	}
}

/*
================
Com_HashString
================
*/
uint32 Com_HashString( const char *string, int hashSize ) {
	uint32 hash;
	uint32 c;

	hash = 0;
	while( *string ) {
		c = *string++;
		hash = 127 * hash + c;
	}

	hash = ( hash >> 20 ) ^ ( hash >> 10 ) ^ hash;
	return hash & ( hashSize - 1 );

}

/*
================
Com_HashPath
================
*/
uint32 Com_HashPath( const char *string, int hashSize ) {
	uint32 hash;
	uint32 c;

	hash = 0;
	while( *string ) {
		c = *string++;
		if( c == '\\' ) {
			c = '/';
		}
		hash = 127 * hash + c;
	}

	hash = ( hash >> 20 ) ^ ( hash >> 10 ) ^ hash;
	return hash & ( hashSize - 1 );

}

/*
================
Com_ReplaceSeparators
================
*/
char *Com_ReplaceSeparators( char *s, int separator ) {
	char *p;

	p = s;
	while( *p ) {
		if( *p == '/' || *p == '\\' ) {
			*p = separator;
		}
		p++;
	}

	return s;
}

/*
================
Q_DrawStrlen
================
*/
int Q_DrawStrlen( const char *string ) {
	int length;

	length = 0;
	while( *string ) {
		if( Q_IsColorString( string ) ) {
			string++;
		} else {
			length++;
		}
		string++;
	}

	return length;
}

/*
================
Q_DrawStrlenTo
================
*/
int Q_DrawStrlenTo( const char *string, int maxChars ) {
	int length;

	if( maxChars < 1 ) {
		maxChars = MAX_STRING_CHARS;
	}

	length = 0;
	while( *string && maxChars-- ) {
		if( Q_IsColorString( string ) ) {
			string++;
		} else {
			length++;
		}
		string++;
	}

	return length;
}

/*
================
Q_CleanColorStr

Removes color escape codes, high-bit and unprintable characters.
Return number of characters written, not including the NULL character.
================
*/
int Q_CleanColorStr( const char *string, char *buffer, int bufferSize ) {
	byte *last, *src, *dst;

	if( bufferSize < 1 ) {
		Com_Error( ERR_FATAL, "Q_CleanStr: bufferSize < 1" );
	}

	src = ( byte * )string;
	dst = ( byte * )buffer;
	last = ( byte * )buffer + bufferSize - 1;
	while( *src && dst != last ) {
		if( Q_IsColorString( src ) ) {
			src += 2;
			continue;
		}
		if( *src < 32 || *src > 127 ) {
			src++;
			continue;
		}
		*dst++ = *src++;
	}

	*dst = 0;

	return ( int )( dst - ( byte * )buffer );
}

/*
================
Q_CleanStr

Removes high-bit and unprintable characters.
Return number of characters written, not including the NULL character.
================
*/
int Q_CleanStr( const char *string, char *buffer, int bufferSize ) {
	byte *last, *src, *dst;

	if( bufferSize < 1 ) {
		Com_Error( ERR_FATAL, "Q_CleanStr: bufferSize < 1" );
	}

	src = ( byte * )string;
	dst = ( byte * )buffer;
	last = ( byte * )buffer + bufferSize - 1;
	while( *src && dst != last ) {
		if( *src < 32 || *src > 127 ) {
			src++;
			continue;
		}
		*dst++ = *src++;
	}

	*dst = 0;

	return ( int )( dst - ( byte * )buffer );
}

/*
================
Q_IsWhiteSpace
================
*/
qboolean Q_IsWhiteSpace( const char *string ) {
	while( *string ) {
		if( ( *string & 127 ) > 32 ) {
			return qfalse;
		}
		string++;
	}

	return qtrue;
}

/*
================
Q_FormatString

replaces some common escape codes and unprintable characters
================
*/
char *Q_FormatString( const char *string ) {
	static const char *hexchars = "0123456789ABCDEF";
	static char buffer[MAX_STRING_CHARS];
	char	*dst;
	int		c;

	dst = buffer;
	while( *string ) {
		c = *string++;

		switch( c ) {
		case '\t':
			*dst++ = '\\';
			*dst++ = 't';
			break;
		case '\b':
			*dst++ = '\\';
			*dst++ = 'b';
			break;
		case '\r':
			*dst++ = '\\';
			*dst++ = 'r';
			break;
		case '\n':
			*dst++ = '\\';
			*dst++ = 'n';
			break;
		case '\\':
			*dst++ = '\\';
			*dst++ = '\\';
			break;
		case '\"':
			*dst++ = '\\';
			*dst++ = '\"';
			break;
		default:
			if( c < 32 || c > 127 ) {
				*dst++ = '\\';
				*dst++ = 'x';
				*dst++ = hexchars[c << 4];
				*dst++ = hexchars[c & 15];
			} else {
				*dst++ = c;
			}
			break;
		}

		if( dst - buffer >= MAX_STRING_CHARS - 4 ) {
			break;
		}
	}

	*dst = 0;

	return buffer;
}

/*
================
Q_UnescapeString
================
*/
char *Q_UnescapeString( const char *string ) {
	static char buffer[MAX_STRING_CHARS];
	char	*dst, *last;
	int		c;

	dst = buffer;
	last = buffer + MAX_STRING_CHARS - 1;
	while( *string && dst != last ) {
		c = *string++;

		if( c != '\\' ) {
			*dst++ = c;
			continue;
		}

		c = *string++;
		if( c == 0 ) {
			break;
		}
		switch( c ) {
		case 't':
			c = '\t';
			break;
		case 'b':
			c = '\b';
			break;
		case 'r':
			c = '\r';
			break;
		case 'n':
			c = '\n';
			break;
		case '\\':
			c = '\\';
			break;
		default:
			break;
		}

		*dst++ = c;
	}

	*dst = 0;

	return buffer;

}

/*
================
Com_LocalTime
================
*/
void Com_LocalTime( qtime_t *qtime ) {
#ifndef _WIN32_WCE
	time_t clock;
	struct tm	*localTime;

	time( &clock );
	localTime = localtime( &clock );

	*qtime = *localTime;
#else
	memset( qtime, 0, sizeof( *qtime ) );
#endif
}

/*
============================================================================

					BYTE ORDER FUNCTIONS

============================================================================
*/

short ShortSwap( short l ) {
	byte    b1, b2;

	b1 = l & 255;
	b2 = ( l >> 8 ) & 255;

	return ( b1 << 8 ) + b2;
}

int LongSwap( int l ) {
	byte    b1, b2, b3, b4;

	b1 = l & 255;
	b2 = ( l >>  8 ) & 255;
	b3 = ( l >> 16 ) & 255;
	b4 = ( l >> 24 ) & 255;

	return ( ( int )b1 << 24 ) + ( ( int )b2 << 16 ) + ( ( int ) b3 << 8 ) + b4;
}

float FloatSwap( float f ) {
	union {
		float	f;
		byte	b[4];
	} dat1, dat2;
	
	
	dat1.f = f;
	dat2.b[0] = dat1.b[3];
	dat2.b[1] = dat1.b[2];
	dat2.b[2] = dat1.b[1];
	dat2.b[3] = dat1.b[0];
	return dat2.f;
}



/*
============
va

does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
FIXME: make this buffer size safe someday
============
*/
char *va( const char *format, ... ) {
	va_list		argptr;
	static char		buffers[2][0x2800];
	static int		index;

	index ^= 1;
	
	va_start( argptr, format );
	Q_vsnprintf( buffers[index], sizeof( buffers[0] ), format, argptr );
	va_end( argptr );

	return buffers[index];	
}


static char		com_token[MAX_TOKEN_CHARS];

/*
==============
COM_Parse

Parse a token out of a string
==============
*/
char *COM_Parse( const char **data_p ) {
	int		c;
	int		len;
	const char	*data;

	data = *data_p;
	len = 0;
	com_token[0] = 0;
	
	if( !data ) {
		*data_p = NULL;
		return com_token;
	}
		
// skip whitespace
skipwhite:
	while( ( c = *data ) <= ' ' ) {
		if( c == 0 ) {
			*data_p = NULL;
			return com_token;
		}
		data++;
	}
	
// skip // comments
	if( c == '/' && data[1] == '/' ) {
		while( *data && *data != '\n' )
			data++;
		goto skipwhite;
	}

// skip /* */ comments
	if( c == '/' && data[1] == '*' ) {
		data += 2;
		while( *data ) {
			if( data[0] == '*' && data[1] == '/' ) {
				data += 2;
				break;
			}
			data++;
		}
		goto skipwhite;
	}

// handle quoted strings specially
	if( c == '\"' ) {
		data++;
		while( 1 ) {
			c = *data++;
			if( c == '\"' || !c ) {
				goto finish;
			}

			if( len < MAX_TOKEN_CHARS - 1 ) {
				com_token[len] = c;
				len++;
			}
		}
	}

// parse a regular word
	do {
		if( len < MAX_TOKEN_CHARS - 1 ) {
			com_token[len] = c;
			len++;
		}
		data++;
		c = *data;
	} while( c > 32 );

finish:
	com_token[len] = 0;

	*data_p = data;
	return com_token;
}


/*
===============
Com_PageInMemory

===============
*/
int	paged_total;

void Com_PageInMemory (byte *buffer, int size)
{
	int		i;

	for (i=size-1 ; i>0 ; i-=4096)
		paged_total += buffer[i];
}



/*
============================================================================

					LIBRARY REPLACEMENT FUNCTIONS

============================================================================
*/

/* quake2 deals only with C locale and uses replacements 
 * for libc locale-dependent string functions */
int Q_tolower( int c ) {
	if( Q_isupper( c ) ) {
		c += ( 'a' - 'A' );
	}

	return c;
}

int Q_toupper( int c ) {
	if( Q_islower( c ) ) {
		c -= ( 'a' - 'A' );
	}

	return c;
}

char *Q_strlwr( char *s ) {
	char *p = s;

	while( *p ) {
		*p = Q_tolower( *p );
		p++;
	}

	return s;
}

char *Q_strupr( char *s ) {
	char *p = s;

	while( *p ) {
		*p = Q_toupper( *p );
		p++;
	}

	return s;
}

int Q_strncasecmp( const char *s1, const char *s2, int n ) {
	int		c1, c2;
	
	do {
		c1 = *s1++;
		c2 = *s2++;

		if( !n-- )
			return 0;		/* strings are equal until end point */
		
		if( c1 != c2 ) {
			if( c1 >= 'a' && c1 <= 'z' )
				c1 -= ( 'a' - 'A' );
			if( c2 >= 'a' && c2 <= 'z' )
				c2 -= ( 'a' - 'A' );
			if( c1 < c2 )
				return -1;
			if( c1 > c2 )
				return 1;		/* strings not equal */
		}
	} while( c1 );
	
	return 0;		/* strings are equal */
}

int Q_strcasecmp( const char *s1, const char *s2 ) {
	int		c1, c2;
	
	do {
		c1 = *s1++;
		c2 = *s2++;
		
		if( c1 != c2 ) {
			if( c1 >= 'a' && c1 <= 'z' )
				c1 -= ( 'a' - 'A' );
			if( c2 >= 'a' && c2 <= 'z' )
				c2 -= ( 'a' - 'A' );
			if( c1 < c2 )
				return -1;
			if( c1 > c2 )
				return 1;		/* strings not equal */
		}
	} while( c1 );
	
	return 0;		/* strings are equal */
}

/*
===============
Q_strncpyz
===============
*/
void Q_strncpyz( char *dest, const char *src, int destsize ) {
#ifdef _DEBUG
	if( destsize < 1 ) {
		Com_Error( ERR_FATAL, "Q_strncpyz: destsize < 1" );
	}
	if( !dest ) {
		Com_Error( ERR_FATAL, "Q_strncpyz: NULL dest" );
	}
	if( !src ) {
		Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
	}
#endif

	strncpy( dest, src, destsize - 1 );
	dest[destsize - 1] = 0;
}

/*
===============
Q_strcat
===============
*/
void Q_strcat( char *dest, int destsize, const char *src ) {
	int len;

#ifdef _DEBUG
	if( !dest ) {
		Com_Error( ERR_FATAL, "Q_strcat: NULL dest" );
	}
#endif

	len = strlen( dest );
	if( len >= destsize ) {
		Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
	}

	Q_strncpyz( dest + len, src, destsize - len );
}

/*
===============
Q_vsnprintf

safe multiplatfrom implementation of vsnprintf
handles overflows correctly

returns number of chars written, not including NULL-terminator
===============
*/
int Q_vsnprintf( char *dest, int destsize, const char *fmt, va_list argptr ) {
	int ret;

#ifdef _WIN32
	ret = _vsnprintf( dest, destsize - 1, fmt, argptr );
	if( ret < 0 || ret > destsize - 1 ) {
		/* truncated, non-terminated */
		dest[destsize - 1] = 0;
		Com_WPrintf( "Q_vsnprintf: overflow of %i in %i\n", ret, destsize );
		ret = destsize - 1;
	} else if( ret == destsize - 1 ) {
		/* correct, non-terminated */
		dest[destsize - 1] = 0;
	}
#else
	/* !!!TODO: investigate libc vsnprintf semantics */
	ret = vsnprintf( dest, destsize, fmt, argptr );
#endif

	return ret;
}

/*
===============
Com_sprintf
===============
*/
int Com_sprintf( char *dest, int destsize, const char *fmt, ... ) {
	va_list		argptr;
	int			ret;

	va_start( argptr, fmt );
	ret = Q_vsnprintf( dest, destsize, fmt, argptr );
	va_end( argptr );

	return ret;
}

/*
=====================================================================

  INFO STRINGS

=====================================================================
*/

/*
===============
Info_ValueForKey

Searches the string for the given
key and returns the associated value, or an empty string.
===============
*/
char *Info_ValueForKey( const char *s, const char *key ) {
	char	pkey[MAX_INFO_STRING];
	static	char value[4][MAX_INFO_STRING];	// use 4 buffers so compares
								// work without stomping on each other
	static	int	valueindex;
	char	*o;
	
	valueindex++;
	if( *s == '\\' )
		s++;
	while( 1 ) {
		o = pkey;
		while( *s != '\\' ) {
			if( !*s )
				return "";
			*o++ = *s++;
		}
		*o = 0;
		s++;

		o = value[valueindex & 3];
		while( *s != '\\' && *s ) {
			*o++ = *s++;
		}
		*o = 0;

		if( !strcmp( key, pkey ) )
			return value[valueindex & 3];

		if( !*s )
			return "";
		s++;
	}

	return "";
}

/*
==================
Info_RemoveKey
==================
*/
void Info_RemoveKey( char *s, const char *key ) {
	char	*start;
	char	pkey[MAX_INFO_STRING];
	char	value[MAX_INFO_STRING];
	char	*o;

	if( strchr( key, '\\' ) ) {
		return;
	}

	while( 1 ) {
		start = s;
		if( *s == '\\' )
			s++;
		o = pkey;
		while( *s != '\\' ) {
			if( !*s )
				return;
			*o++ = *s++;
		}
		*o = 0;
		s++;

		o = value;
		while( *s != '\\' && *s ) {
			if( !*s )
				return;
			*o++ = *s++;
		}
		*o = 0;

		if( !strcmp( key, pkey ) ) {
			strcpy( start, s );	// remove this part
			return;
		}

		if( !*s )
			return;
	}

}


/*
==================
Info_Validate

Some characters are illegal in info strings because they
can mess up the server's parsing.
Also checks the length of keys/values and the whole string.
==================
*/
qboolean Info_Validate( const char *s ) {
	const char *start;
	int		len;
	
	start = s;
	while( 1 ) {
		//
		// validate key
		//
		if( *s == '\\' ) {
			s++;
		}
		if( !*s ) {
			return qfalse;	// missing key
		}
		len = 0;
		while( *s != '\\' ) {
			if( *s == '\"' || *s == ';' ) {
				return qfalse;	// illegal characters
			}
			if( len == MAX_INFO_KEY - 1 ) {
				return qfalse;	// oversize key
			}
			s++; len++;
			if( !*s ) {
				return qfalse;	// missing value
			}
		}

		//
		// validate value
		//
		s++;
		if( !*s ) {
			return qfalse;	// missing value
		}
		len = 0;
		while( *s != '\\' ) {
			if( *s == '\"' || *s == ';' ) {
				return qfalse;	// illegal characters
			}
			if( len == MAX_INFO_VALUE - 1 ) {
				return qfalse;	// oversize value
			}
			s++; len++;
			if( !*s ) {
				if( s - start > MAX_INFO_STRING ) {
					return qfalse;
				}
				return qtrue;	// end of string
			}
		}
	}

	return qfalse; // quiet compiler warning
}

/*
============
Info_ValidateSubstring
============
*/
qboolean Info_ValidateSubstring( const char *s ) {
	const char *start;

	start = s;
	while( *s ) {
		if( *s == '\\' || *s == '\"' || *s == ';' ) {
			return qfalse;
		}
		s++;
	}
	if( s - start > MAX_QPATH ) {
		return qfalse;
	}
	return qtrue;
}

/*
==================
Info_SetValueForKey
==================
*/
void Info_SetValueForKey( char *s, const char *key, const char *value ) {
	char	newi[MAX_INFO_STRING], *v;
	int		c;

	if( strchr( key, '\\' ) || strchr( value, '\\' ) ) {
		Com_Printf( "Can't use keys or values with a \\\n" );
		return;
	}

	if( strchr( key, ';' ) ) {
		Com_Printf( "Can't use keys or values with a semicolon\n" );
		return;
	}

	if( strchr( key, '\"' ) || strchr( value, '\"' ) ) {
		Com_Printf ("Can't use keys or values with a \"\n");
		return;
	}

	if( strlen( key ) > MAX_INFO_KEY - 1 || strlen( value ) > MAX_INFO_VALUE - 1 ) {
		Com_Printf( "Keys and values must be < %i characters.\n", MAX_INFO_KEY );
		return;
	}
	Info_RemoveKey( s, key );
	if( !value[0] ) {
		return;
	}

	Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );

	if( strlen( newi ) + strlen( s ) > MAX_INFO_STRING - 1 ) {
		Com_Printf( "Info string length exceeded\n" );
		return;
	}

	// only copy ascii values
	s += strlen( s );
	v = newi;
	while( *v ) {
		c = *v++;
		c &= 127;		// strip high bits
		if( c >= 32 && c < 127 )
			*s++ = c;
	}
	*s = 0;
}

/*
==================
Info_AttemptSetValueForKey
==================
*/
qboolean Info_AttemptSetValueForKey( char *s, const char *key, const char *value ) {
	char	newi[MAX_INFO_STRING], *v;
	int		c;

	if( !Info_ValidateSubstring( key ) ) {
		return qfalse;
	}
	if( !Info_ValidateSubstring( value ) ) {
		return qfalse;
	}

	Info_RemoveKey( s, key );
	if( !value[0] ) {
		return qtrue;
	}

	Com_sprintf( newi, sizeof( newi ), "\\%s\\%s", key, value );

	if( strlen( newi ) + strlen( s ) > MAX_INFO_STRING - 1 ) {
		return qfalse;
	}

	// only copy ascii values
	s += strlen( s );
	v = newi;
	while( *v ) {
		c = *v++;
		c &= 127;		// strip high bits
		if( c >= 32 && c < 127 )
			*s++ = c;
	}
	*s = 0;

	return qtrue;
}

/*
==================
Info_NextPair
==================
*/
void Info_NextPair( const char **string, char *key, char *value ) {
	char	*o;
	const char	*s;

	*value = *key = 0;

	s = *string;
	if( !s ) {
		return;
	}

	if( *s == '\\' )
		s++;

	if( !*s ) {
		*string = NULL;
		return;
	}
	
	o = key;
	while( *s && *s != '\\' ) {
		*o++ = *s++;
	}
	
	*o = 0;

	if( !*s ) {
		*string = NULL;
		return;
	}

	o = value;
	s++;
	while( *s && *s != '\\' ) {
		*o++ = *s++;
	}
	*o = 0;

	if( *s ) {
		s++;
	}

	*string = s;
	
}

/*
==================
Info_Print
==================
*/
void Info_Print( const char *infostring ) {
	char	key[MAX_INFO_STRING];
	char	value[MAX_INFO_STRING];

	while( infostring ) {
		Info_NextPair( &infostring, key, value );
		
		if( !key[0] ) {
			break;
		}

		if( value[0] ) {
			Com_Printf( "%-20s %s\n", key, value );
		} else {
			Com_Printf( "%-20s <MISSING VALUE>\n", key );
		}
	}
}




