//-----------------------------------------------------------------------------
//
//  $Logfile:: /GameSource/q_shared.c                                         $
// $Revision:: 4                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 2/5/02 7:48 PM                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/q_shared.c                            $
//
// 31    1/26/99 5:45p Markd
// Added TransformFromTriangle for 2015
//
// 30    10/05/98 12:28a Jimdose
// Added angledist
//
// 29    9/28/98 5:51p Markd
//
// 28    9/03/98 2:11p Aldie
// Added checksums checks to .def files
//
// 27    8/22/98 8:48p Jimdose
// Added support for alternate gravity axis
//
// 26    8/03/98 7:34p Markd
// Added SURFACE_DamageMultiplier func
//
// 25    8/02/98 9:00p Markd
// Merged code 3.17
//
// 24    7/21/98 9:06p Markd
// Music Mood conversion stuff
//
// 23    7/10/98 6:13a Jimdose
// Nearly lost the file because H: was full! :O
//
// 22    6/15/98 5:35p Markd
// put in UnsignedShort stuff
//
// 21    5/27/98 8:06p Aldie
// Made a SIN_Parse and SIN_GetToken, and it works like the old one.
//
// 20    5/26/98 12:34a Markd
// Fixed RotateBounds function
//
// 19    5/24/98 6:49p Markd
// Got rid of unused variables
//
// 18    5/24/98 6:29p Markd
// Added CalculateRotatedBounds2
//
// 17    5/24/98 4:48p Jimdose
// Made char *'s const
//
// 16    5/20/98 11:12a Markd
// made certain functions take const char *'s instead of char *'s.
//
// 15    5/13/98 6:21p Markd
// Added CalculateRotatedBounds
//
// 14    5/12/98 1:15p Aldie
// Made Com_Parse substitute \n in an embedded string.
//
// 13    4/17/98 6:48p Markd
// Put in /* support
//
// 12    3/30/98 11:07p Aldie
// Updated COM_Parse to get strings within strings.
//
// 11    3/30/98 9:38p Markd
// Made COM_GetToken handle Quoted-Double Quotes correctly
//
// 10    3/27/98 6:59p Markd
// Changed Origin From Triangle and Transform From Triangle
//
// 9     3/24/98 9:58a Markd
// Added TransformFromTriangle
//
// 8     3/18/98 9:03p Jimdose
// Fixed assertion in BoxOnPlaneSide
//
// 7     3/04/98 2:08p Markd
// Fixed bug in COM_GetToken it was returning null when it should have returned
// ""
//
// 6     2/28/98 4:43p Aldie
// Added Matrix4TransformVector
//
// 5     2/25/98 4:38p Markd
// Added COM_GetToken
//
// 4     2/16/98 2:10p Jimdose
// Added the following math utility functions: MatrixToEulerAngles,
// TransposeMatrix, MatrixTransformVector, OrthoNormalize, QuatToMat,
// MatToQuat, SlerpQuaternion, AnglesToMat, RotateAxis, MultQuat, EulerToQuat,
// VectorsToEulerAngles
//
// 3     12/30/97 6:04p Jimdose
// Added header text
//
// DESCRIPTION:
//

#include "q_shared.h"
#include <float.h>

vec3_t vec3_origin = {0, 0, 0};
qboolean bigendien;

#define X 0
#define Y 1
#define Z 2
#define W 3
#define QUAT_EPSILON 0.00001

const gravityaxis_t gravity_axis[GRAVITY_NUM_AXIS] =
{
	{ X, Y, Z, 1 },
	{ Y, Z, X, 1 },
	{ Z, X, Y, 1 },
	{ X, Y, Z, -1 },
	{ Y, Z, X, -1 },
	{ Z, X, Y, -1 }
};

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

#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( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up )
{
	float angle;
#ifdef _MSC_VER /* static to help MS compiler fp bugs */
	static
#endif
	float sr, sp, sy, cr, cp, cy;

	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];
}


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


#if 0
float Q_fabs( float f )
{
	int tmp = *( int* ) &f;
	tmp &= 0x7FFFFFFF;
	return *( float* ) &tmp;
}
#endif

#if defined(_MSC_VER) && defined(_M_IX86) && !defined(C_ONLY)
#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 )
{
#if 0
	if( a >= 0 )
		a -= 360 * ( int )( a / 360 );
	else
		a += 360 * ( 1 + ( int )( -a / 360 ) );
#endif
	a = ( 360.0 / 65536 ) * ( ( int )( a * ( 65536 / 360.0 ) ) & 65535 );
	return a;
}

float angledist( float ang )
{
	float a;

	a = anglemod( ang );
	if( a > 180 )
	{
		a -= 360;
	}

	return a;
}

/*
==================
BOPS_Error

Split out like this for ASM to call.
==================
*/
void BOPS_Error (void)
{
	Sys_Error ("BoxOnPlaneSide:  Bad signbits");
}

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

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

// 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
			Sys_Error ("BoxOnPlaneSide:  Bad signbits");//assert(0);
			break;
	}

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

//	//assert(sides != 0);
//	if (sides == 0)
//		Sys_Error ("BoxOnPlaneSide: sides==0");

	return sides;
}
#endif

#if (id386) && defined(_MSC_VER)
#pragma warning( disable: 4035 )

__declspec( naked ) int BoxOnPlaneSide( vec3_t emins, vec3_t emaxs, struct cplane_s* 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( 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;
	}
}


int VectorCompare( vec3_t v1, vec3_t v2 )
{
	if( v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] )
		return 0;

	return 1;
}


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;

}

void VectorMA( const vec3_t veca, float scale, const 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( const vec3_t v1, const 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( const vec3_t in, vec3_t out )
{
	out[0] = in[0];
	out[1] = in[1];
	out[2] = in[2];
}

void CrossProduct( const vec3_t v1, const 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];
}

vec_t VectorLength( const 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 VectorInverse( vec3_t v )
{
	v[0] = -v[0];
	v[1] = -v[1];
	v[2] = -v[2];
}

void VectorScale( const vec3_t in, vec_t scale, vec3_t out )
{
	out[0] = in[0] * scale;
	out[1] = in[1] * scale;
	out[2] = in[2] * scale;
}


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

/*
=================
CalculateRotatedBounds
=================
*/
void CalculateRotatedBounds( vec3_t angles, vec3_t mins, vec3_t maxs )
{
	int i;
	vec3_t forward, right, up;
	vec3_t rotmins, rotmaxs;
	float trans[3][3];

	AngleVectors( angles, forward, right, up );
	for( i = 0; i < 3; i++ )
	{
		trans[i][0] =  forward[i];
		trans[i][1] =  -right[i];
		trans[i][2] =  up[i];
	}
	ClearBounds( rotmins, rotmaxs );
	for( i = 0; i < 8; i++ )
	{
		vec3_t tmp, rottemp;

		if( i & 1 )
			tmp[0] = mins[0];
		else
			tmp[0] = maxs[0];

		if( i & 2 )
			tmp[1] = mins[1];
		else
			tmp[1] = maxs[1];

		if( i & 4 )
			tmp[2] = mins[2];
		else
			tmp[2] = maxs[2];

		MatrixTransformVector( tmp, trans, rottemp );
		AddPointToBounds( rottemp, rotmins, rotmaxs );
	}
	VectorCopy( rotmins, mins );
	VectorCopy( rotmaxs, maxs );
}

/*
=================
CalculateRotatedBounds2
=================
*/
void CalculateRotatedBounds2( float trans[3][3], vec3_t mins, vec3_t maxs )
{
	int i;
	vec3_t rotmins, rotmaxs;

	ClearBounds( rotmins, rotmaxs );
	for( i = 0; i < 8; i++ )
	{
		vec3_t tmp, rottemp;

		if( i & 1 )
			tmp[0] = mins[0];
		else
			tmp[0] = maxs[0];

		if( i & 2 )
			tmp[1] = mins[1];
		else
			tmp[1] = maxs[1];

		if( i & 4 )
			tmp[2] = mins[2];
		else
			tmp[2] = maxs[2];

		MatrixTransformVector( tmp, trans, rottemp );
		AddPointToBounds( rottemp, rotmins, rotmaxs );
	}
	VectorCopy( rotmins, mins );
	VectorCopy( rotmaxs, maxs );
}

/*
=================
OriginFromTriangle
=================
*/
//  coordinate system at center
void OriginFromTriangle(/*const*/ float tri[3][3], vec3_t pos)
{
	int i;

	VectorClear( pos );
	for( i = 0; i < 3; i++ )
		pos[i] = ( tri[0][i] + tri[1][i] + tri[2][i] ) / 3;
}

/*
=================
TransformFromTriangle
=================
*/
/*
         0
        /\
       /  \
    0 /    \  2
     /      \
    /        \
   /----------\
  1      1     2

 ((type>>2)&0x3)
 0 -  edge 0 is first edge
 1 -  edge 1 is first edge
 2 -  edge 2 is first edge

 (type>>4)
 0 - forward is perpendicular, up is first edge, right is on the same plane as the triangle
 1 - forward is perpendicular, up is first edge, right is on the same plane as the triangle(inverted)
 2 - forward is first edge, up is on the same plane as the triangle, up is perpendicular
 3 - forward is first edge, up is on the same plane as the triangle(inverted), up is perpendicular
 4 - forward is perpendicular to first edge, up is perpendicular, right is first edge
 5 - forward is perpendicular to first edge, up is perpendicular, right is first edge(inverted)
 6 - forward is perpendicular, up is first edge, right is on the same plane as the triangle
 7 - forward is perpendicular, up is first edge, right is on the same plane as the triangle(inverted)
*/
void TransformFromTriangle(/*const*/ float tri[3][3], float trans[3][3], vec3_t pos)
{
	vec3_t edge[2];
	vec3_t cross;
	vec3_t other;
	vec3_t forward, right, up;

	OriginFromTriangle( tri, pos );

	VectorSubtract( tri[2], tri[0], edge[0] );
	VectorSubtract( tri[1], tri[0], edge[1] );

	VectorNormalize( edge[0] );
	VectorNormalize( edge[1] );
	CrossProduct( edge[1], edge[0], cross );
	CrossProduct( edge[1], cross, other );

	VectorCopy( cross, forward );
	VectorCopy( other, right );
	VectorNegate( edge[1], up );

	VectorCopy( forward, trans[0] );
	VectorCopy( right, trans[1] );
	VectorCopy( up, trans[2] );

	/*
	switch( (type>>2)&0x3 )
	{
	case 0:
		VectorSubtract( tri[2], tri[0], edge[0] );
		VectorSubtract( tri[1], tri[0], edge[1] );
		break;
	case 1:
		VectorSubtract( tri[0], tri[1], edge[0] );
		VectorSubtract( tri[2], tri[1], edge[1] );
		break;
	case 2:
		VectorSubtract( tri[1], tri[2], edge[0] );
		VectorSubtract( tri[0], tri[2], edge[1] );
		break;
	default:
		VectorSubtract( tri[2], tri[0], edge[0] );
		VectorSubtract( tri[1], tri[0], edge[1] );
		break;
	}
	VectorNormalize( edge[0] );
	VectorNormalize( edge[1] );
	CrossProduct( edge[1], edge[0], cross );
	CrossProduct( edge[1], cross, other );
	switch( type>>4 )
	{
	case 0:
		VectorCopy( cross, forward );
		VectorCopy( other, right );
		VectorNegate( edge[1], up );
		break;
	case 1:
		VectorCopy( cross, forward );
		VectorCopy( other, right );
		VectorCopy( edge[1], up );
		break;
	case 2:
		VectorCopy( cross, forward );
		VectorCopy( other, right );
		VectorNegate( edge[0], up );
		break;
	case 3:
		VectorCopy( cross, forward );
		VectorCopy( other, right );
		VectorCopy( edge[0], up );
		break;
	case 4:
		VectorCopy( edge[0], forward );
		VectorNegate( other, up );
		VectorCopy( cross, right );
		break;
	case 5:
		VectorCopy( edge[0], forward );
		VectorCopy( other, up );
		VectorCopy( cross, right );
		break;
	case 6:
		VectorCopy( other, forward );
		VectorNegate( edge[0], right );
		VectorCopy( cross, up );
		break;
	case 7:
		VectorCopy( other, forward );
		VectorCopy( edge[0], right );
		VectorCopy( cross, up );
		break;
	case 8:
		VectorCopy( edge[0], forward );
		VectorNegate( other, right );
		VectorCopy( cross, up );
		break;
	case 9:
		VectorCopy( edge[0], forward );
		VectorCopy( other, right );
		VectorCopy( cross, up );
		break;
	default:
		VectorCopy( edge[0], forward );
		VectorNegate( other, right );
		VectorCopy( cross, up );
		break;
	}
	// fill in the matrix
	VectorCopy( forward, trans[0] );
	VectorCopy( right, trans[1] );
	VectorCopy( up, trans[2] );
	*/
}

// 2015 code
void TransformFromTriangle_2015(/*const*/ float tri[3][3], float trans[3][3], vec3_t pos)
{
	vec3_t edge[2];
	vec3_t cross;
	vec3_t other;
	vec3_t forward, right, up;

	OriginFromTriangle( tri, pos );

	VectorSubtract( tri[2], tri[0], edge[0] );
	VectorSubtract( tri[1], tri[0], edge[1] );

	VectorNormalize( edge[0] );
	VectorNormalize( edge[1] );
	CrossProduct( edge[1], edge[0], cross );
	VectorNormalize( cross ); //
	CrossProduct( edge[1], cross, other );
	VectorNormalize( other ); //

	VectorCopy( cross, forward );
	VectorCopy( other, right );
// VectorNegate( edge[1], up );
	VectorCopy( edge[1], up );

	VectorCopy( forward, trans[0] );
	VectorCopy( right, trans[1] );
	VectorCopy( up, trans[2] );
}

static const char* musicmoods[mood_totalnumber] =
{
	"none",
	"normal",
	"action",
	"suspense",
	"mystery",
	"success",
	"failure",
	"surprise",
	"special",
	"aux1",
	"aux2",
	"aux3",
	"aux4",
	"aux5",
	"aux6",
	"aux7"
};

/*
=================
MusicMood_NameToNum
=================
*/
int MusicMood_NameToNum (const char *name)
{
	int i;

	if (!name)
		return -1;

	for (i = 0; i < mood_totalnumber; i++)
	{
		if( !strcmp(name, musicmoods[i]))
			return i;
	}
	return -1;
}

/*
=================
MusicMood_NumToName
=================
*/
const char *MusicMood_NumToName (int num)
{
	if (num < 0 || num >= mood_totalnumber)
		return "";
	return musicmoods[num];
}

/*
===============
SURFACE_DamageMultiplier
===============
*/
float SURFACE_DamageMultiplier( int flags )
{
	float mult;

	mult = 0;
	switch( flags )
	{
		case SURF_TYPE_FLESH:
		case SURF_TYPE_FABRIC:
		case SURF_TYPE_VEGETATION:
		case SURF_TYPE_WATER:
		case SURF_TYPE_PAPER:
			mult = 0;
			break;
		case SURF_TYPE_DIRT:
			mult = 0.2;
			break;
		case SURF_TYPE_WOOD:
			mult = 0.5;
			break;
		case SURF_TYPE_METAL:
		case SURF_TYPE_DUCT:
		case SURF_TYPE_GRILL:
			mult = 1;
			break;
		case SURF_TYPE_GRAVEL:
		case SURF_TYPE_STONE:
		case SURF_TYPE_CONCRETE:
			mult = 1.5;
			break;
		case SURF_TYPE_GLASS:
		case SURF_TYPE_MONITOR:
			mult = 3;
			break;
		default:
			break;
	}
	return mult;
}

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

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

	last = pathname;
	while( *pathname )
	{
		if( *pathname == '/' )
			last = pathname + 1;
		pathname++;
	}
	return last;
}

/*
============
COM_ParseHex
============
*/
int COM_ParseHex( const char* hex )
{
	const char*    str;
	int num;

	num = 0;
	str = hex;

	while( *str )
	{
		num <<= 4;
		if( *str >= '0' && *str <= '9' )
			num += *str - '0';
		else if( *str >= 'a' && *str <= 'f' )
			num += 10 + *str - 'a';
		else if( *str >= 'A' && *str <= 'F' )
			num += 10 + *str - 'A';
		else
			Com_Printf( "Bad hex number: %s", hex );
		str++;
	}

	return num;
}

/*
============
COM_StripExtension
============
*/
void COM_StripExtension( const char* in, char* out )
{
	while( *in && *in != '.' )
		*out++ = *in++;
	*out = 0;
}

/*
============
COM_FileExtension
============
*/
const char* COM_FileExtension( const char* in )
{
	static char exten[8];
	int i;

	while( *in && *in != '.' )
		in++;
	if( !*in )
		return "";
	in++;
	for( i = 0; i < 7 && *in; i++, in++ )
		exten[i] = *in;
	exten[i] = 0;
	return exten;
}

/*
============
COM_FileBase
============
*/
void COM_FileBase( const char* in, char* out )
{
	const char* s;
	const char* 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 )
{
	const char* s;

	s = in + strlen( in ) - 1;

	while( s != in && *s != '/' )
		s--;

	strncpy( out, in, s - in );
	out[s - in] = 0;
}


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

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

	strcat( path, extension );
}

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

                    BYTE ORDER FUNCTIONS

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

// can't just use function pointers, or dll linkage can
// mess up when qcommon is included in multiple places
static short( *_BigShort )( short l );
static short( *_LittleShort )( short l );
static int ( *_BigLong )( int l );
static int ( *_LittleLong )( int l );
static float ( *_BigFloat )( float l );
static float ( *_LittleFloat )( float l );

static unsigned short( *_BigUnsignedShort )( unsigned short l );
static unsigned short( *_LittleUnsignedShort )( unsigned short l );
static unsigned int ( *_BigUnsignedLong )( unsigned int l );
static unsigned int ( *_LittleUnsignedLong )( unsigned int l );
static double ( *_BigDouble )( double l );
static double ( *_LittleDouble )( double l );

short BigShort( short l )
{
	return _BigShort( l );
}

short LittleShort( short l )
{
	return _LittleShort( l );
}

int BigLong( int l )
{
	return _BigLong( l );
}

int LittleLong( int l )
{
	return _LittleLong( l );
}

float BigFloat( float l )
{
	return _BigFloat( l );
}

float LittleFloat( float l )
{
	return _LittleFloat( l );
}

unsigned short BigUnsignedShort( unsigned short l )
{
	return _BigUnsignedShort( l );
}

unsigned short LittleUnsignedShort( unsigned short l )
{
	return _LittleUnsignedShort( l );
}

unsigned int BigUnsignedLong( unsigned int l )
{
	return _BigUnsignedLong( l );
}

unsigned int LittleUnsignedLong( unsigned int l )
{
	return _LittleUnsignedLong( l );
}

double BigDouble( double l )
{
	return _BigDouble( l );
}

double LittleDouble( double l )
{
	return _LittleDouble( l );
}

static short ShortSwap( short l )
{
	byte b1, b2;
	b1 = l & 255;
	b2 = ( l >> 8 ) & 255;
	return ( b1 << 8 ) + b2;
}

static short ShortNoSwap( short l )
{
	return l;
}

static unsigned short UnsignedShortSwap( unsigned short l )
{
	byte b1, b2;

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

	return ( b1 << 8 ) + b2;
}

static unsigned short UnsignedShortNoSwap( unsigned short l )
{
	return l;
}

static 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;
}

static int LongNoSwap( int l )
{
	return l;
}

static unsigned int UnsignedLongSwap( unsigned 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;
}

static double DoubleNoSwap( double l )
{
	return l;
}

static double DoubleSwap( double l )
{
	int i;
	union
	{
		char c[8];
		double d;
	} in, out;

	in.d = l;

	for( i = 0; i < 8; ++i )
		out.c[7 - i] = in.c[i];

	return out.d;
}

static unsigned int UnsignedLongNoSwap( unsigned int l )
{
	return l;
}

static 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;
}

static float FloatNoSwap( float f )
{
	return f;
}

/*
================
Swap_Init
================
 */
void Swap_Init( void )
{
	int	i = 0x12345678;

// set the byte swapping variables in a portable manner
	if ( *(char *)&i == 0x78)	/* LITTLE_ENDIAN */
	{
		bigendien = false;
		_BigShort = ShortSwap;
		_LittleShort = ShortNoSwap;
		_BigLong = LongSwap;
		_LittleLong = LongNoSwap;
		_BigFloat = FloatSwap;
		_LittleFloat = FloatNoSwap;
		_BigUnsignedShort = UnsignedShortSwap;
		_LittleUnsignedShort = UnsignedShortNoSwap;
	}
	else /* if ( *(char *)&i == 0x12) */	/* BIG_ENDIAN */
	{
		bigendien = true;
		_BigShort = ShortNoSwap;
		_LittleShort = ShortSwap;
		_BigLong = LongNoSwap;
		_LittleLong = LongSwap;
		_BigFloat = FloatNoSwap;
		_LittleFloat = FloatSwap;
		_BigUnsignedShort = UnsignedShortNoSwap;
		_LittleUnsignedShort = UnsignedShortSwap;
		_BigUnsignedLong = UnsignedLongNoSwap;
		_LittleUnsignedLong = UnsignedLongSwap;
		_BigDouble = DoubleNoSwap;
		_LittleDouble = DoubleSwap;
	}
}



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

does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
============
*/
#define	VA_NUM_BUFFS	4
#define	VA_BUFFERLEN	1024

static char *get_va_buffer(void)
{
	static char va_buffers[VA_NUM_BUFFS][VA_BUFFERLEN];
	static int buffer_idx = 0;
	buffer_idx = (buffer_idx + 1) & (VA_NUM_BUFFS - 1);
	return va_buffers[buffer_idx];
}

char *va (const char *format, ...)
{
	va_list argptr;
	char *va_buf;

	va_buf = get_va_buffer ();
	va_start (argptr, format);
	Q_vsnprintf (va_buf, VA_BUFFERLEN, format, argptr);
	va_end (argptr);
	va_buf[VA_BUFFERLEN - 1] = 0;

	return va_buf;
}


static char com_token[MAX_STRING_CHARS];

/*
==============
COM_GetToken

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

	data = *data_p;
	len = 0;
	com_token[0] = 0;

	if( !data )
	{
		*data_p = NULL;
		return "";
	}

// skip whitespace
skipwhite:
	while( ( c = *data ) <= ' ' )
	{
		if( c == '\n' && !crossline )
		{
			*data_p = data;
			return "";
		}
		if( c == 0 )
		{
			*data_p = NULL;
			return "";
		}
		data++;
	}

// skip // comments
	if( c == '/' && data[1] == '/' )
	{
		while( *data && *data != '\n' )
			data++;
		goto skipwhite;
	}

// skip /* comments
	if( c == '/' && data[1] == '*' )
	{
		data++;
		while( *data )
		{
			if( ( *( data - 1 ) == '*' ) && ( *data == '/' ) )
				break;
			data++;
		}
		while( *data && *data != '\n' )
			data++;
		goto skipwhite;
	}


// handle quoted strings specially
	if( c == '\"' )
	{
		data++;
		while( 1 )
		{
			c = *data++;
			if( c == '\\' && *data == '\"' )
			{
				if( len < MAX_STRING_CHARS )
				{
					com_token[len] = '\"';
					len++;
				}
				data++;
			}
			else if( c == '\"' || !c )
			{
				com_token[len] = 0;
				*data_p = data;
				return com_token;
			}
			else if( len < MAX_STRING_CHARS )
			{
				if( c == '\\' && *data == 'n' )
				{
					com_token[len] = '\n';
					data++;
				}
				else
				{
					com_token[len] = c;
				}
				len++;
			}
		}
	}

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

	if( len == MAX_STRING_CHARS )
	{
//		Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
		len = 0;
	}
	com_token[len] = 0;

	*data_p = data;
	return com_token;
}

/*
==============
SIN_GetToken

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

	data = *data_p;
	len = 0;
	com_token[0] = 0;

	if( !data )
	{
		*data_p = NULL;
		return "";
	}

// skip whitespace
skipwhite:
	while( ( c = *data ) <= ' ' )
	{
		if( c == '\n' && !crossline )
		{
			*data_p = data;
			return "";
		}
		if( c == 0 )
		{
			*data_p = NULL;
			return "";
		}
		data++;
	}

// skip // comments
	if( c == '/' && data[1] == '/' )
	{
		while( *data && *data != '\n' )
			data++;
		goto skipwhite;
	}

// skip /* comments
	if( c == '/' && data[1] == '*' )
	{
		data++;
		while( *data )
		{
			if( ( *( data - 1 ) == '*' ) && ( *data == '/' ) )
				break;
			data++;
		}
		while( *data && *data != '\n' )
			data++;
		goto skipwhite;
	}


// handle quoted strings specially
	if( c == '\"' )
	{
		data++;
		while( 1 )
		{
			c = *data++;
			if( c == '\\' && *data == '\"' )
			{
				if( len < MAX_STRING_CHARS )
				{
					com_token[len] = '\"';
					len++;
				}
				data++;
			}
			else if( c == '\"' || !c )
			{
				com_token[len] = 0;
				*data_p = data;
				return com_token;
			}
			else if( len < MAX_STRING_CHARS )
			{
				com_token[len] = c;
				len++;
			}
		}
	}

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

	if( len == MAX_STRING_CHARS )
	{
//		Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
		len = 0;
	}
	com_token[len] = 0;

	*data_p = data;
	return com_token;
}


const char* SIN_Parse( const char** data_p )
{
	return SIN_GetToken( data_p, true );
}

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

Parse a token out of a string
==============
*/
const char* COM_Parse( const char** data_p )
{
	return COM_GetToken( data_p, true );
}

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

===============
*/
static 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

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

// FIXME: replace all Q_stricmp with Q_strcasecmp
int Q_stricmp( const char* s1, const char* s2 )
{
#if defined(_WIN32)
	return _stricmp( s1, s2 );
#elif defined(__DJGPP__)
	return stricmp( s1, s2 );
#else
	return strcasecmp( s1, s2 );
#endif
}

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;  // strings not equal
		}
	}
	while( c1 );

	return 0;   // strings are equal
}

int Q_strcasecmp( const char* s1, const char* s2 )
{
	return Q_strncasecmp( s1, s2, 99999 );
}

/* FS: From KMQ2 */
/*
=================
Q_strncpyz

Safe strncpy that ensures a trailing zero
=================
*/
void Q_strncpyz( char* dst, const char* src, int dstSize )
{
	if( !dst )
	{
		//	Com_Error (ERR_FATAL, "Q_strncpyz: NULL dst");
		//	Com_Printf ("Q_strncpyz: NULL dst\n");
		return;
	}
	if( !src )
	{
		//	Com_Error (ERR_FATAL, "Q_strncpyz: NULL src");
		//	Com_Printf ("Q_strncpyz: NULL src\n");
		return;
	}
	if( dstSize < 1 )
	{
		//	Com_Error (ERR_FATAL, "Q_strncpyz: dstSize < 1");
		//	Com_Printf ("Q_strncpyz: dstSize < 1\n");
		return;
	}

	strncpy( dst, src, dstSize - 1 );
	dst[dstSize - 1] = 0;
}

/*
=================
Q_strncatz

Safe strncat that ensures a trailing zero
=================
*/
void Q_strncatz( char* dst, const char* src, int dstSize )
{
	if( !dst )
	{
		//	Com_Error (ERR_FATAL, "Q_strncatz: NULL dst");
		//	Com_Printf ("Q_strncatz: NULL dst\n");
		return;
	}
	if( !src )
	{
		//	Com_Error (ERR_FATAL, "Q_strncatz: NULL src");
		//	Com_Printf ("Q_strncatz: NULL src\n");
		return;
	}
	if( dstSize < 1 )
	{
		//	Com_Error (ERR_FATAL, "Q_strncatz: dstSize < 1");
		//	Com_Printf ("Q_strncatz: dstSize < 1\n");
		return;
	}

	while( --dstSize && *dst )
		dst++;

	if( dstSize > 0 )
	{
		while( --dstSize && *src )
			*dst++ = *src++;

		*dst = 0;
	}
}

char *Q_strlwr (char *string)
{
	char *s = string;
	while (*s) {
		*s = q_tolower(*s);
		s++;
	}
	return string;
}

char *Q_strupr (char *string)
{
	char *s = string;
	while (*s) {
		*s = q_toupper(*s);
		s++;
	}
	return string;
}

#if defined(__DJGPP__) || defined(_WIN32)
char*  /* from OpenBSD */
strtok_r( char* s, const char* delim, char** last )
{
	const char* spanp;
	int c, sc;
	char* tok;

	if( s == NULL && ( s = *last ) == NULL )
		return ( NULL );

	/*
	 * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
	 */
cont:
	c = *s++;
	for( spanp = delim; ( sc = *spanp++ ) != 0; )
	{
		if( c == sc )
			goto cont;
	}

	if( c == 0 )    /* no non-delimiter characters */
	{
		*last = NULL;
		return ( NULL );
	}
	tok = s - 1;

	/*
	 * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
	 * Note that delim must have one NUL; we stop if we see that, too.
	 */
	for( ;; )
	{
		c = *s++;
		spanp = delim;
		do
		{
			if( ( sc = *spanp++ ) == c )
			{
				if( c == 0 )
					s = NULL;
				else
					s[-1] = '\0';
				*last = s;
				return ( tok );
			}
		}
		while( sc != 0 );
	}
	/* NOTREACHED */
}
#endif

void Com_sprintf (char *dest, int size, const char *fmt, ...)
{
	int		len;
	va_list		argptr;

	va_start (argptr,fmt);
	len = Q_vsnprintf (dest, size, fmt, argptr);
	va_end (argptr);
	if (size > 0) dest[size - 1] = 0;
	if (len < 0 || len >= size) {
		Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
	}
}


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

INFO STRINGS

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

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

Searches the string for the given
key and returns the associated value, or an empty string.
===============
*/
const char* Info_ValueForKey( const char* s, const char* key )
{
	char pkey[512];
	static char value[2][512]; // use two buffers so compares
	// work without stomping on each other
	static int valueindex;
	char*   o;

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

		o = value[valueindex];

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

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

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

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

	if( strstr( key, "\\" ) )
	{
//		Com_Printf ("Can't use a key with a \\\n");
		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 ) )
		{
			memmove(start, s, strlen(s) + 1); // 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
==================
*/
qboolean Info_Validate( const char* s )
{
	if( strstr( s, "\"" ) )
		return false;
	if( strstr( s, ";" ) )
		return false;
	return true;
}

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

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

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

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

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

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

	if( strlen( newi ) + strlen( s ) > maxsize )
	{
		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;
}

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


void MatrixTransformVector (vec3_t in, float mat[3][3], vec3_t out)
{
	out[0] = in[0] * mat[0][0] + in[1] * mat[1][0] + in[2] * mat[2][0];
	out[1] = in[0] * mat[0][1] + in[1] * mat[1][1] + in[2] * mat[2][1];
	out[2] = in[0] * mat[0][2] + in[1] * mat[1][2] + in[2] * mat[2][2];
}

void Matrix4TransformVector (vec3_t in, float mat[4][4], vec3_t out)
{
	out[0] = in[0] * mat[0][0] + in[1] * mat[1][0] + in[2] * mat[2][0] + mat[3][0];
	out[1] = in[0] * mat[0][1] + in[1] * mat[1][1] + in[2] * mat[2][1] + mat[3][1];
	out[2] = in[0] * mat[0][2] + in[1] * mat[1][2] + in[2] * mat[2][2] + mat[3][2];
}

void VectorsToEulerAngles (vec3_t forward, vec3_t right, vec3_t up, vec3_t ang)
{
	double theta;
	double cp;
	double sp;

	sp = forward[2];

	// cap off our sin value so that we don't get any NANs
	if( sp > 1.0 )
	{
		sp = 1.0;
	}
	if( sp < -1.0 )
	{
		sp = -1.0;
	}

	theta = -asin( sp );
	cp = cos( theta );

	if( cp > 8192 * FLT_EPSILON )
	{
		ang[0] = theta * 180 / M_PI;
		ang[1] = atan2(forward[1], forward[0]) * 180 / M_PI;
		ang[2] = atan2(-right[2], up[2] ) * 180 / M_PI;
	}
	else
	{
		ang[0] = theta * 180 / M_PI;
		ang[1] = -atan2(right[0], right[1]) * 180 / M_PI;
		ang[2] = 0;
	}
}

void MatrixToEulerAngles (float mat[3][3], vec3_t ang)
{
	double theta;
	double cp;
	double sp;

	sp = mat[0][2];

	// cap off our sin value so that we don't get any NANs
	if( sp > 1.0 )
	{
		sp = 1.0;
	}
	if( sp < -1.0 )
	{
		sp = -1.0;
	}

	theta = -asin( sp );
	cp = cos( theta );

	if( cp > 8192 * FLT_EPSILON )
	{
		ang[0] = theta * 180 / M_PI;
		ang[1] = atan2(mat[0][1], mat[0][0]) * 180 / M_PI;
		ang[2] = atan2(mat[1][2], mat[2][2]) * 180 / M_PI;
	}
	else
	{
		ang[0] = theta * 180 / M_PI;
		ang[1] = -atan2(mat[1][0], mat[1][1]) * 180 / M_PI;
		ang[2] = 0;
	}
}

void TransposeMatrix (float in[3][3], float out[3][3])
{
	out[0][0] = in[0][0];
	out[0][1] = in[1][0];
	out[0][2] = in[2][0];
	out[1][0] = in[0][1];
	out[1][1] = in[1][1];
	out[1][2] = in[2][1];
	out[2][0] = in[0][2];
	out[2][1] = in[1][2];
	out[2][2] = in[2][2];
}

void OrthoNormalize (float mat[3][3])
{
	VectorNormalize(mat[0]);
	CrossProduct(mat[0], mat[1], mat[2]);
	VectorNormalize(mat[2]);
	CrossProduct(mat[2], mat[0], mat[1]);
	VectorNormalize(mat[1]);
}

float NormalizeQuat (float q[4])
{
	float length, ilength;

	length = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
	length = sqrt(length);

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

	return length;
}

void MatToQuat (/*const*/ float srcMatrix[3][3], float destQuat[4])
{
	double trace, s;
	int i, j, k;
	static int next[3] = {Y, Z, X};

	trace = srcMatrix[X][X] + srcMatrix[Y][Y] + srcMatrix[Z][Z];

	if( trace > 0.0 )
	{
		s = sqrt( trace + 1.0 );
		destQuat[W] = s * 0.5;
		s = 0.5 / s;

		destQuat[X] = ( srcMatrix[Z][Y] - srcMatrix[Y][Z] ) * s;
		destQuat[Y] = ( srcMatrix[X][Z] - srcMatrix[Z][X] ) * s;
		destQuat[Z] = ( srcMatrix[Y][X] - srcMatrix[X][Y] ) * s;
	}
	else
	{
		i = X;
		if( srcMatrix[Y][Y] > srcMatrix[X][X] )
			i = Y;
		if( srcMatrix[Z][Z] > srcMatrix[i][i] )
			i = Z;
		j = next[i];
		k = next[j];

		s = sqrt( ( srcMatrix[i][i] - ( srcMatrix[j][j] + srcMatrix[k][k] ) ) + 1.0 );
		destQuat[i] = s * 0.5;

		s = 0.5 / s;

		destQuat[W] = ( srcMatrix[k][j] - srcMatrix[j][k] ) * s;
		destQuat[j] = ( srcMatrix[j][i] + srcMatrix[i][j] ) * s;
		destQuat[k] = ( srcMatrix[k][i] + srcMatrix[i][k] ) * s;
	}
}

void AnglesToMat (/*const*/ float ang[3], float mat[3][3])
{
	AngleVectors(ang, mat[0], mat[1], mat[2]);
	VectorNegate(mat[1], mat[1] );
}

void RotateAxis (float axis[3], float angle, float q[4])
{
	float sin_a;
	float inv_sin_a;
	float cos_a;
	float r;

	r = angle * M_PI / 360;

	sin_a = sin( r );
	if( fabs( sin_a ) > 0.00000001 )
	{
		inv_sin_a = 1 / sin_a;
	}
	else
	{
		inv_sin_a = 0;
	}
	cos_a = cos( r );

	q[X] = axis[0] * inv_sin_a;
	q[Y] = axis[1] * inv_sin_a;
	q[Z] = axis[2] * inv_sin_a;
	q[W] = cos_a;
}

void MultQuat (float q1[4], float q2[4], float out[4])
{
	out[0] = q1[X] * q2[X] - q1[Y] * q2[Y] - q1[Z] * q2[Z] - q1[W] * q2[W];
	out[1] = q1[X] * q2[Y] + q1[Y] * q2[X] + q1[Z] * q2[W] - q1[W] * q2[Z];
	out[2] = q1[X] * q2[Z] - q1[Y] * q2[W] + q1[Z] * q2[X] + q1[W] * q2[Y];
	out[3] = q1[X] * q2[W] + q1[Y] * q2[Z] - q1[Z] * q2[Y] + q1[W] * q2[X];
}

void QuatToMat (float q[4], float m[3][3])
{
	float wx, wy, wz;
	float xx, yy, yz;
	float xy, xz, zz;
	float x2, y2, z2;

	x2 = q[X] + q[X];
	y2 = q[Y] + q[Y];
	z2 = q[Z] + q[Z];

	xx = q[X] * x2;
	xy = q[X] * y2;
	xz = q[X] * z2;

	yy = q[Y] * y2;
	yz = q[Y] * z2;
	zz = q[Z] * z2;

	wx = q[W] * x2;
	wy = q[W] * y2;
	wz = q[W] * z2;

	m[0][0] = 1.0 - ( yy + zz );
	m[0][1] = xy - wz;
	m[0][2] = xz + wy;

	m[1][0] = xy + wz;
	m[1][1] = 1.0 - ( xx + zz );
	m[1][2] = yz - wx;

	m[2][0] = xz - wy;
	m[2][1] = yz + wx;
	m[2][2] = 1.0 - ( xx + yy );
}

#define DELTA 1e-6

void SlerpQuaternion (float from[4], float to[4], float t, float res[4])
{
	float to1[4];
	double omega, cosom, sinom, scale0, scale1;

	cosom = from[X] * to[X] + from[Y] * to[Y] + from[Z] * to[Z] + from[W] * to [W];
	if (cosom < 0.0)
	{
		cosom = -cosom;
		to1[X] = -to[X];
		to1[Y] = -to[Y];
		to1[Z] = -to[Z];
		to1[W] = -to[W];
	}
	else if((from[X] == to[X]) &&
		(from[Y] == to[Y]) &&
		(from[Z] == to[Z]) &&
		(from[W] == to[W]) )
	{
		// equal case, early exit
		res[X] = to[X];
		res[Y] = to[Y];
		res[Z] = to[Z];
		res[W] = to[W];
		return;
	}
	else
	{
		to1[X] = to[X];
		to1[Y] = to[Y];
		to1[Z] = to[Z];
		to1[W] = to[W];
	}

	if((1.0 - cosom) > DELTA)
	{
		omega = acos(cosom);
		sinom = sin(omega);
		scale0 = sin((1.0 - t) * omega) / sinom;
		scale1 = sin(t * omega) / sinom;
	}
	else
	{
		scale0 = 1.0 - t;
		scale1 = t;
	}
	res[X] = scale0 * from[X] + scale1 * to1[X];
	res[Y] = scale0 * from[Y] + scale1 * to1[Y];
	res[Z] = scale0 * from[Z] + scale1 * to1[Z];
	res[W] = scale0 * from[W] + scale1 * to1[W];
}

void EulerToQuat (/*const*/ float ang[3], float q[4])
{
	float mat[3][3];
	AnglesToMat(ang, mat);
	MatToQuat(mat, q);
}

float Sinatof (const char *text)
{
	if (text && text[0])
		return atof( text );
	return 0;
}

long Com_HashFileName (const char *fname, int hashSize, qboolean sized)
{
	int i = 0;
	long hash = 0;
	char letter;

	if (fname[0] == '/' || fname[0] == '\\')
		i++;					/* skip leading slash */
	while (fname[i] != '\0')
	{
		letter = q_tolower(fname[i]);
		//if (letter == '.') break;
		if (letter == '\\') letter = '/';	/* fix filepaths */
		hash += (long) (letter) * (i + 119);
		i++;
	}
	hash = (hash ^ (hash >> 10) ^ (hash >> 20));
	if (sized) {
		hash &= (hashSize - 1);
	}
	return hash;
}
