thirtyflightsofloving/game/q_shared.c
Knightmare66 97a87dbe6e Added color1 userinfo cvar for player railgun effect color to client.
Added red/green/blue component sliders to player setup menu to change player railgun effect color.
Added parsing test code for color1 cvar in client code of default Lazarus DLL.
Will integrate custom color into railgun weapon code for next savegame version increment.
2021-10-21 02:03:19 -04:00

2431 lines
44 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
This file is part of Lazarus Quake 2 Mod source code.
Lazarus Quake 2 Mod source code 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.
Lazarus Quake 2 Mod source code 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 Lazarus Quake 2 Mod source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "q_shared.h"
#include "laz_misc.h"
#ifdef _WIN32
#include "../win32/winquake.h"
#endif
vec2_t vec2_origin = {0,0};
vec3_t vec3_origin = {0,0,0};
vec4_t vec4_origin = {0,0,0,0};
//============================================================================
#ifdef _WIN32
#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 _WIN32
#pragma optimize( "", on )
#endif
void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
float angle;
static float sr, sp, sy, cr, cp, cy;
// static to help MS compiler fp bugs
if (!angles)
return;
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 MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up)
{
float d;
// this rotate and negat guarantees a vector
// not colinear with the original
right[1] = -forward[0];
right[2] = forward[1];
right[0] = forward[2];
d = DotProduct (right, forward);
VectorMA (right, -d, forward, right);
VectorNormalize (right);
CrossProduct (right, forward, up);
}
void VecToAngleRolled (vec3_t value1, float angleyaw, vec3_t angles)
{
float forward, yaw, pitch;
yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
if (pitch < 0)
pitch += 360;
angles[PITCH] = -pitch;
angles[YAW] = yaw;
angles[ROLL] = - angleyaw;
}
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)
{
#if 0
if (f >= 0)
return f;
return -f;
#else
int tmp = * ( int * ) &f;
tmp &= 0x7FFFFFFF;
return * ( float * ) &tmp;
#endif
}
#if defined _M_IX86 && !defined C_ONLY
#pragma warning (disable:4035)
__declspec( naked ) int 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)
//#elif !defined(Q_ftol)
#else
int Q_ftol (float f)
{
return (int)f;
}
#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;
}
// int i;
// vec3_t corners[2];
// 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
Which means (THANKS FOR THE FUCKING COMMENTS CARMACK!):
1 = PSIDE_FRONT (front of plane)
2 = PSIDE_BACK (back of plane)
3 = PSIDE_BOTH (both sides of plane)
==================
*/
//#if !id386 || defined __linux__
//#ifndef id386
//#ifndef _WIN32
#if !defined (_WIN32) || !defined (_M_IX86)
int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct cplane_s *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
assert( 0 );
break;
}
sides = 0;
if (dist1 >= p->dist)
sides = 1;
if (dist2 < p->dist)
sides |= 2;
assert( sides != 0 );
return sides;
}
#else
#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;
}
/*
=================
VectorNormalizeFast
From Q2E
=================
*/
void VectorNormalizeFast (vec3_t v)
{
float ilength = Q_rsqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
v[0] = v[0]*ilength;
v[1] = v[1]*ilength;
v[2] = v[2]*ilength;
}
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 VectorInverse (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;
}
/*
=================
Q_rsqrt
From Q2E
=================
*/
float Q_rsqrt (float in)
{
int i;
float x, y;
x = in * 0.5f;
i = *(int *)&in;
i = 0x5f3759df - (i >> 1);
y = *(float *)&i;
y = y * (1.5f - (x * y * y));
return y;
}
int Q_log2(int val)
{
int answer=0;
while (val>>=1)
answer++;
return answer;
}
/*
=================
VectorRotate
From Q2E
=================
*/
void VectorRotate (const vec3_t v, const vec3_t matrix[3], vec3_t out)
{
out[0] = v[0]*matrix[0][0] + v[1]*matrix[0][1] + v[2]*matrix[0][2];
out[1] = v[0]*matrix[1][0] + v[1]*matrix[1][1] + v[2]*matrix[1][2];
out[2] = v[0]*matrix[2][0] + v[1]*matrix[2][1] + v[2]*matrix[2][2];
}
/*
=================
AnglesToAxis
From Q2E
=================
*/
void AnglesToAxis (const vec3_t angles, vec3_t axis[3])
{
static float sp, sy, sr, cp, cy, cr;
float angle;
angle = DEG2RAD(angles[PITCH]);
sp = sin(angle);
cp = cos(angle);
angle = DEG2RAD(angles[YAW]);
sy = sin(angle);
cy = cos(angle);
angle = DEG2RAD(angles[ROLL]);
sr = sin(angle);
cr = cos(angle);
axis[0][0] = cp*cy;
axis[0][1] = cp*sy;
axis[0][2] = -sp;
axis[1][0] = sr*sp*cy+cr*-sy;
axis[1][1] = sr*sp*sy+cr*cy;
axis[1][2] = sr*cp;
axis[2][0] = cr*sp*cy+-sr*-sy;
axis[2][1] = cr*sp*sy+-sr*cy;
axis[2][2] = cr*cp;
}
/*
=================
AxisClear
From Q2E
=================
*/
void AxisClear (vec3_t axis[3])
{
axis[0][0] = 1;
axis[0][1] = 0;
axis[0][2] = 0;
axis[1][0] = 0;
axis[1][1] = 1;
axis[1][2] = 0;
axis[2][0] = 0;
axis[2][1] = 0;
axis[2][2] = 1;
}
/*
=================
AxisCopy
From Q2E
=================
*/
void AxisCopy (const vec3_t in[3], vec3_t out[3])
{
out[0][0] = in[0][0];
out[0][1] = in[0][1];
out[0][2] = in[0][2];
out[1][0] = in[1][0];
out[1][1] = in[1][1];
out[1][2] = in[1][2];
out[2][0] = in[2][0];
out[2][1] = in[2][1];
out[2][2] = in[2][2];
}
/*
=================
AxisCompare
From Q2E
=================
*/
qboolean AxisCompare (const vec3_t axis1[3], const vec3_t axis2[3])
{
if (axis1[0][0] != axis2[0][0] || axis1[0][1] != axis2[0][1] || axis1[0][2] != axis2[0][2])
return false;
if (axis1[1][0] != axis2[1][0] || axis1[1][1] != axis2[1][1] || axis1[1][2] != axis2[1][2])
return false;
if (axis1[2][0] != axis2[2][0] || axis1[2][1] != axis2[2][1] || axis1[2][2] != axis2[2][2])
return false;
return true;
}
/*
================
MatrixMultiply
From Q3A
================
*/
void MatrixMultiply (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];
}
//====================================================================================
/*
============
COM_SkipPath
Skips the file path
============
*/
char *COM_SkipPath (char *pathname)
{
char *last;
last = pathname;
while (*pathname)
{
if (*pathname == '/' || *pathname == '\\')
last = pathname+1;
pathname++;
}
return last;
}
/*
============
COM_StripExtension
Removes the file extension, if any
============
*/
void COM_StripExtension (char *in, char *out, size_t outSize)
{
/* while (*in && *in != '.')
*out++ = *in++;
*out = 0;
*/
char *s, *last;
s = last = in + strlen(in);
while (*s != '/' && *s != '\\' && s != in)
{
if (*s == '.'){
last = s;
break;
}
s--;
}
Q_strncpyz(out, outSize, in);
if (last-in < outSize)
out[last-in] = 0;
}
/*
============
COM_FileExtension
============
*/
char *COM_FileExtension (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 (char *in, char *out, size_t outSize)
{
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;
Q_strncpyz (out, outSize, s2+1);
if (s-s2 < outSize)
out[s-s2] = 0;
}
}
/*
============
COM_FilePath
Returns the path up to, but not including the last /
============
*/
void COM_FilePath (char *in, char *out, size_t outSize)
{
char *s;
s = in + strlen(in) - 1;
while (s != in && *s != '/')
s--;
// strncpy (out, in, s-in);
// out[s-in] = 0;
Q_strncpyz (out, outSize, in);
if (s-in < outSize)
out[s-in] = 0;
}
/*
==================
COM_DefaultExtension
==================
*/
void COM_DefaultExtension (char *path, size_t pathSize, 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--;
}
// strncat (path, extension);
Q_strncatz(path, pathSize, extension);
}
/*
============================================================================
BYTE ORDER FUNCTIONS
============================================================================
*/
qboolean bigendien;
// can't just use function pointers, or dll linkage can
// mess up when qcommon is included in multiple places
// Knightmare- made these static
static short (*_BigShort) (short l);
static short (*_LittleShort) (short l);
static int (*_BigLong) (int l);
static int (*_LittleLong) (int l);
static qint64 (*_BigLong64) (qint64 l);
static qint64 (*_LittleLong64) (qint64 l);
static float (*_BigFloat) (float l);
static float (*_LittleFloat) (float 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);}
qint64 BigLong64 (qint64 l) {return _BigLong64(l);}
qint64 LittleLong64 (qint64 l) {return _LittleLong64(l);}
float BigFloat (float l) {return _BigFloat(l);}
float LittleFloat (float l) {return _LittleFloat(l);}
short ShortSwap (short l)
{
byte b1,b2;
b1 = l&255;
b2 = (l>>8)&255;
return (b1<<8) + b2;
}
short ShortNoSwap (short l)
{
return l;
}
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;
}
int LongNoSwap (int l)
{
return l;
}
qint64 Long64Swap (qint64 ll)
{
byte b1,b2,b3,b4,b5,b6,b7,b8;
b1 = ll&255;
b2 = (ll>>8)&255;
b3 = (ll>>16)&255;
b4 = (ll>>24)&255;
b5 = (ll>>32)&255;
b6 = (ll>>40)&255;
b7 = (ll>>48)&255;
b8 = (ll>>56)&255;
return ((qint64)b1<<56) + ((qint64)b2<<48) + ((qint64)b3<<40) + ((qint64)b4<<32)
+ ((qint64)b5<<24) + ((qint64)b6<<16) + ((qint64)b7<<8) + (qint64)b8;
}
qint64 Long64NoSwap (qint64 ll)
{
return ll;
}
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;
}
float FloatNoSwap (float f)
{
return f;
}
/*
================
Swap_Init
================
*/
void Swap_Init (void)
{
byte swaptest[2] = {1,0};
// set the byte swapping variables in a portable manner
if ( *(short *)swaptest == 1)
{
bigendien = false;
_BigShort = ShortSwap;
_LittleShort = ShortNoSwap;
_BigLong = LongSwap;
_LittleLong = LongNoSwap;
_BigLong64 = Long64Swap;
_LittleLong64 = Long64NoSwap;
_BigFloat = FloatSwap;
_LittleFloat = FloatNoSwap;
}
else
{
bigendien = true;
_BigShort = ShortNoSwap;
_LittleShort = ShortSwap;
_BigLong = LongNoSwap;
_LittleLong = LongSwap;
_BigLong64 = Long64NoSwap;
_LittleLong64 = Long64Swap;
_BigFloat = FloatNoSwap;
_LittleFloat = FloatSwap;
}
}
/*
============
va
does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
============
*/
char *va (char *format, ...)
{
va_list argptr;
static char string[1024];
va_start (argptr, format);
Q_vsnprintf (string, sizeof(string), format, argptr);
va_end (argptr);
return string;
}
/*
=======================================================================
TEXT PARSING
=======================================================================
*/
char com_token[MAX_TOKEN_CHARS];
//static int com_parseLine;
/*
=================
COM_SkipWhiteSpace
=================
*/
char *COM_SkipWhiteSpace (char *data_p, qboolean *hasNewLines)
{
int c;
while ((c = *data_p) <= ' ')
{
if (!c)
return NULL;
if (c == '\n') {
// com_parseLine++;
*hasNewLines = true;
}
data_p++;
}
return data_p;
}
/*
=================
COM_SkipBracedSection
Skips until a matching close brace is found.
Internal brace depths are properly skipped.
=================
*/
void COM_SkipBracedSection (char **data_p, int depth)
{
char *tok;
do
{
tok = COM_ParseExt (data_p, true);
if (tok[1] == 0)
{
if (tok[0] == '{')
depth++;
else if (tok[0] == '}')
depth--;
}
} while (depth && *data_p);
}
/*
=================
COM_SkipRestOfLine
Skips until a new line is found
=================
*/
void COM_SkipRestOfLine (char **data_p)
{
char *tok;
while (1) {
tok = COM_ParseExt (data_p, false);
if (!tok[0])
break;
}
}
/*
==============
COM_Parse
Parse a token out of a string
==============
*/
char *COM_Parse (char **data_p)
{
int c;
int len;
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 == 0)
{
*data_p = NULL;
return "";
}
data++;
}
// skip // comments
if (c=='/' && data[1] == '/')
{
while (*data && *data != '\n')
data++;
goto skipwhite;
}
// handle quoted strings specially
if (c == '\"')
{
data++;
while (1)
{
c = *data++;
if (c=='\"' || !c)
{
if (len == MAX_TOKEN_CHARS) // Knightmare- discard if > MAX_TOKEN_CHARS-1
len = 0;
com_token[len] = 0;
*data_p = data;
return com_token;
}
if (len < MAX_TOKEN_CHARS)
{
com_token[len] = c;
len++;
}
}
}
// parse a regular word
do
{
if (len < MAX_TOKEN_CHARS)
{
com_token[len] = c;
len++;
}
data++;
c = *data;
} while (c>32);
if (len == MAX_TOKEN_CHARS)
{
// Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
len = 0;
}
com_token[len] = 0;
*data_p = data;
return com_token;
}
/*
=================
Com_ParseExt
Parse a token out of a string
From Quake2Evolved
=================
*/
char *COM_ParseExt (char **data_p, qboolean allowNewLines)
{
int c, len = 0;
char *data;
qboolean hasNewLines = false;
data = *data_p;
com_token[0] = 0;
// Make sure incoming data is valid
if (!data) {
*data_p = NULL;
return com_token;
}
while (1)
{ // Skip whitespace
data = COM_SkipWhiteSpace (data, &hasNewLines);
if (!data) {
*data_p = NULL;
return com_token;
}
if (hasNewLines && !allowNewLines) {
*data_p = data;
return com_token;
}
c = *data;
// Skip // comments
if (c == '/' && data[1] == '/') {
while (*data && *data != '\n')
data++;
}
else if (c == '/' && data[1] == '*') // Skip /* */ comments
{
data += 2;
while (*data && (*data != '*' || data[1] != '/')) {
// if (*data == '\n')
// com_parseLine++;
data++;
}
if (*data)
data += 2;
}
else // An actual token
break;
}
// Handle quoted strings specially
if (c == '\"')
{
data++;
while (1)
{
c = *data++;
// if (c == '\n')
// com_parseLine++;
if (c == '\"' || !c)
{
if (len == MAX_TOKEN_CHARS) // Knightmare- discard if > MAX_TOKEN_CHARS-1
len = 0;
com_token[len] = 0;
*data_p = data;
return com_token;
}
if (len < MAX_TOKEN_CHARS)
com_token[len++] = c;
}
}
// Parse a regular word
do
{
if (len < MAX_TOKEN_CHARS)
com_token[len++] = c;
data++;
c = *data;
} while (c > 32);
if (len == MAX_TOKEN_CHARS)
len = 0;
com_token[len] = 0;
*data_p = data;
return com_token;
}
/*
=================
Com_ParseHexDigit
Parse an int value from a single hex digit
=================
*/
int Com_ParseHexDigit (const char c)
{
if ( (c >= '0') && (c <= '9') )
return (c - '0');
if ( (c >= 'A') && (c <= 'F') )
return 10 + (c - 'A');
if ( (c >= 'a') && (c <= 'f') )
return 10 + (c - 'a');
return -1;
}
/*
=================
Com_ParseColorString
Parse an RGB color from an rrggbb string
=================
*/
qboolean Com_ParseColorString (const char *s, color_t outColor)
{
int i, digits[6];
// catch null string or too short string
if ( !s || (strlen(s) < 6) ) {
outColor[0] = outColor[1] = outColor[2] = 0;
return false;
}
// parse thru all 6 digits
for (i = 0; i < 6; i++) {
digits[i] = Com_ParseHexDigit(s[i]);
if (digits[i] < 0)
return false;
}
outColor[0] = ((digits[0] & 0xF) << 4) | (digits[1] & 0xF);
outColor[1] = ((digits[2] & 0xF) << 4) | (digits[3] & 0xF);
outColor[2] = ((digits[4] & 0xF) << 4) | (digits[5] & 0xF);
return true;
}
/*
===============
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
============================================================================
*/
/*
=================
Q_GlobMatchAfterStar
From Q2E
Like Q_GlobMatch, but match pattern against any final segment of text
=================
*/
static qboolean Q_GlobMatchAfterStar (const char *pattern, const char *text, qboolean caseSensitive){
const char *p = pattern;
const char *t = text;
char c1, c2;
while ((c1 = *p++) == '?' || c1 == '*'){
if (c1 == '?' && *t++ == '\0')
return false;
}
if (c1 == '\0')
return true;
if (c1 == '\\')
c2 = *p;
else
c2 = c1;
while (1){
if (caseSensitive){
if (c1 == '[' || *t == c2){
if (Q_GlobMatch(p - 1, t, caseSensitive))
return true;
}
}
else {
if (c1 == '[' || tolower(*t) == tolower(c2)){
if (Q_GlobMatch(p - 1, t, caseSensitive))
return true;
}
}
if (*t++ == '\0')
return false;
}
}
/*
=================
Q_GlobMatch
From Q2E
Matches the pattern against text.
Returns true if matches, false otherwise.
A match means the entire text is used up in matching.
In the pattern string, '*' matches any sequence of characters, '?'
matches any character, '[SET]' matches any character in the specified
set, '[!SET]' matches any character not in the specified set.
A set is composed of characters or ranges. A range looks like character
hyphen character (as in 0-9 or A-Z).
[0-9a-zA-Z_] is the set of characters allowed in C identifiers.
Any other character in the pattern must be matched exactly.
To suppress the special syntactic significance of any of '[]*?!-\', and
match the character exactly, precede it with a '\'.
=================
*/
qboolean Q_GlobMatch (const char *pattern, const char *text, qboolean caseSensitive){
const char *p = pattern;
const char *t = text;
char c1, c2, start, end;
qboolean invert;
while ((c1 = *p++) != '\0'){
switch (c1){
case '?':
if (*t == '\0')
return false;
else
++t;
break;
case '\\':
if (caseSensitive){
if (*p++ != *t++)
return false;
}
else {
if (tolower(*p++) != tolower(*t++))
return false;
}
break;
case '*':
return Q_GlobMatchAfterStar(p, t, caseSensitive);
case '[':
c2 = *t++;
if (!c2)
return false;
invert = (*p == '!');
if (invert)
p++;
c1 = *p++;
while (1){
start = c1;
end = c1;
if (c1 == '\\'){
start = *p++;
end = start;
}
if (c1 == '\0')
return false;
c1 = *p++;
if (c1 == '-' && *p != ']'){
end = *p++;
if (end == '\\')
end = *p++;
if (end == '\0')
return false;
c1 = *p++;
}
if (caseSensitive){
if (c2 >= start && c2 <= end)
goto match;
}
else {
if (tolower(c2) >= tolower(start) && tolower(c2) <= tolower(end))
goto match;
}
if (c1 == ']')
break;
}
if (!invert)
return false;
break;
match:
while (c1 != ']'){
if (c1 == '\0')
return false;
c1 = *p++;
if (c1 == '\0')
return false;
else if (c1 == '\\')
++p;
}
if (invert)
return false;
break;
default:
if (caseSensitive){
if (c1 != *t++)
return false;
}
else {
if (tolower(c1) != tolower(*t++))
return false;
}
break;
}
}
return (*t == '\0');
}
/*
===============
Q_StrScanToken
A non-ambiguous alternative to strstr.
Useful for parsing the GL extension string.
Similar to code in Fruitz of Dojo Quake2 MacOSX Port.
isCommand parm allows for ';' as separator.
===============
*/
qboolean Q_StrScanToken (const char *string, const char *findToken, qboolean isCommand)
{
int tokenLen;
const char *strPos;
char *tokPos, *terminatorPos;
if ( !string || !findToken )
return false;
if ( (strchr(findToken, ' ') != NULL) || (findToken[0] == 0) )
return false;
strPos = string;
tokenLen = (int)strlen(findToken);
while (1)
{
tokPos = strstr (strPos, findToken);
if ( !tokPos )
break;
terminatorPos = tokPos + tokenLen;
if (isCommand) {
if ( (tokPos == strPos || *(tokPos - 1) == ';' || *(tokPos - 1) == ' ') && (*terminatorPos == ';' || *terminatorPos == ' ' || *terminatorPos == 0) )
return true;
}
else {
if ( (tokPos == strPos || *(tokPos - 1) == ' ') && (*terminatorPos == ' ' || *terminatorPos == 0) )
return true;
}
strPos = terminatorPos;
}
return false;
}
/*
=================
Q_stricmp
=================
*/
// FIXME: replace all Q_stricmp with Q_strcasecmp
int Q_stricmp (char *s1, char *s2)
{
#if defined(WIN32)
return _stricmp (s1, s2);
#else
return strcasecmp (s1, s2);
#endif
}
/*
=================
Q_strncmp
From Q2E
=================
*/
int Q_strncmp (const char *string1, const char *string2, int n)
{
int c1, c2;
if (string1 == NULL)
{
if (string2 == NULL)
return 0;
else
return -1;
}
else if (string2 == NULL)
return 1;
do
{
c1 = *string1++;
c2 = *string2++;
if (!n--)
return 0; // Strings are equal until end point
if (c1 != c2)
return c1 < c2 ? -1 : 1;
} while (c1);
return 0; // Strings are equal
}
/*
=================
Q_strcmp
From Q2E
=================
*/
int Q_strcmp (const char *string1, const char *string2)
{
return Q_strncmp(string1, string2, 99999);
}
/*
=================
Q_SortStrcmp
From Q2E
=================
*/
int Q_SortStrcmp (const char **arg1, const char **arg2)
{
return Q_strcmp(*arg1, *arg2);
}
/*
=================
Q_strncasecmp
=================
*/
int Q_strncasecmp (char *s1, char *s2, size_t 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
}
/*
=================
Q_strcasecmp
=================
*/
int Q_strcasecmp (char *s1, char *s2)
{
return Q_strncasecmp (s1, s2, 99999);
}
/*
=================
Q_strncpyz
Safe strncpy that ensures a trailing zero
=================
*/
size_t Q_strncpyz (char *dst, size_t dstSize, const char *src)
{
char *d = dst;
const char *s = src;
size_t decSize = dstSize;
if (!dst) {
// Com_Error (ERR_FATAL, "Q_strncpyz: NULL dst");
// Com_Printf ("Q_strncpyz: NULL dst\n");
return 0;
}
if (!src) {
// Com_Error (ERR_FATAL, "Q_strncpyz: NULL src");
// Com_Printf ("Q_strncpyz: NULL src\n");
return 0;
}
if (dstSize < 1) {
// Com_Error (ERR_FATAL, "Q_strncpyz: dstSize < 1");
// Com_Printf ("Q_strncpyz: dstSize < 1\n");
return 0;
}
// strncpy(dst, src, dstSize-1);
// dst[dstSize-1] = 0;
while (--decSize && *s)
*d++ = *s++;
*d = 0;
dst[dstSize-1] = 0;
if (decSize == 0) // Insufficent room in dst, return count + length of remaining src
return (s - src - 1 + strlen(s));
else
return (s - src - 1); // returned count excludes NULL terminator
}
/*
=================
Q_strncatz
Safe strncat that ensures a trailing zero
=================
*/
size_t Q_strncatz (char *dst, size_t dstSize, const char *src)
{
char *d = dst;
const char *s = src;
size_t decSize = dstSize;
size_t dLen;
if (!dst) {
// Com_Error (ERR_FATAL, "Q_strncatz: NULL dst");
// Com_Printf ("Q_strncatz: NULL dst\n");
return 0;
}
if (!src) {
// Com_Error (ERR_FATAL, "Q_strncatz: NULL src");
// Com_Printf ("Q_strncatz: NULL src\n");
return 0;
}
if (dstSize < 1) {
// Com_Error (ERR_FATAL, "Q_strncatz: dstSize < 1");
// Com_Printf ("Q_strncatz: dstSize < 1\n");
return 0;
}
/* while (--dstSize && *dst)
dst++;
if (dstSize > 0){
while (--dstSize && *src)
*dst++ = *src++;
*dst = 0;
}*/
while (--decSize && *d)
d++;
dLen = d - dst;
if (decSize == 0)
return (dLen + strlen(s));
if (decSize > 0) {
while (--decSize && *s)
*d++ = *s++;
*d = 0;
}
dst[dstSize-1] = 0;
return (dLen + (s - src)); // returned count excludes NULL terminator
}
/*
=================
Q_snprintfz
Safe snprintf that ensures a trailing zero
=================
*/
void Q_snprintfz (char *dst, size_t dstSize, const char *fmt, ...)
{
va_list argPtr;
if (!dst) {
// Com_Error(ERR_FATAL, "Q_snprintfz: NULL dst");
// Com_Printf("Q_snprintfz: NULL dst\n");
return;
}
if (dstSize < 1) {
// Com_Error(ERR_FATAL, "Q_snprintfz: dstSize < 1");
// Com_Printf("Q_snprintfz: dstSize < 1\n");
return;
}
va_start(argPtr, fmt);
Q_vsnprintf(dst, dstSize, fmt, argPtr);
va_end(argPtr);
dst[dstSize-1] = 0;
}
char *Q_strlwr (char *string)
{
char *s = string;
while (*s) {
*s = tolower(*s);
s++;
}
return string;
}
char *Q_strupr (char *string)
{
char *s = string;
while (*s) {
*s = toupper(*s);
s++;
}
return string;
}
void Com_sprintf (char *dest, size_t size, char *fmt, ...)
{
char bigbuffer[0x10000];
int len;
va_list argptr;
va_start (argptr, fmt);
len = Q_vsnprintf (bigbuffer, sizeof(bigbuffer), fmt, argptr);
va_end (argptr);
// if (len >= size)
// Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
if (len < 0)
Com_Printf ("Com_sprintf: overflow in temp buffer of size %i\n", sizeof(bigbuffer));
else if (len >= size)
Com_Printf ("Com_sprintf: overflow of %i in dest buffer of size %i\n", len, size);
strncpy (dest, bigbuffer, size-1);
dest[size-1] = 0;
}
/*
=============
Com_HashFileName
=============
*/
unsigned int Com_HashFileName (const char *fname, int hashSize, qboolean sized)
{
int i = 0;
unsigned int hash = 0;
char letter;
if (fname[0] == '/' || fname[0] == '\\') i++; // skip leading slash
while (fname[i] != '\0')
{
letter = tolower(fname[i]);
// if (letter == '.') break;
if (letter == '\\') letter = '/'; // fix filepaths
hash += (int)(letter)*(i+119);
i++;
}
hash = (hash ^ (hash >> 10) ^ (hash >> 20));
if (sized) {
hash &= (hashSize-1);
}
return hash;
}
/*
=====================================================================
INFO STRINGS
=====================================================================
*/
/*
===============
Info_ValueForKey
Searches the string for the given
key and returns the associated value, or an empty string.
===============
*/
char *Info_ValueForKey (char *s, 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, 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) )
{
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
==================
*/
qboolean Info_Validate (char *s)
{
if (strstr (s, "\""))
return false;
if (strstr (s, ";"))
return false;
return true;
}
void Info_SetValueForKey (char *s, char *key, 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;
}
//====================================================================