mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
d16b46e3cf
Overhauled child entity movement in default Lazarus DLL. Added bbox versions of various triggers to default Lazarus DLL. Added level.maptype field to default Lazarus DLL. Added entity class IDs to default Lazarus DLL. Incremented savegame version for default Lazarus DLL.
2341 lines
42 KiB
C
2341 lines
42 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)
|
|
#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 *)∈
|
|
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, in, outSize);
|
|
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, s2+1, outSize);
|
|
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, in, outSize);
|
|
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, extension, pathSize);
|
|
}
|
|
|
|
/*
|
|
============================================================================
|
|
|
|
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_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
|
|
=================
|
|
*/
|
|
void Q_strncpyz (char *dst, const char *src, size_t 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, size_t 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;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
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 < 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;
|
|
}
|
|
|
|
//====================================================================
|
|
|
|
|