/* * Copyright(c) 1997-2001 Id Software, Inc. * Copyright(c) 2002 The Quakeforge Project. * Copyright(c) 2006 Quetoo. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or(at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "q_shared.h" #define DEG2RAD(a)(a * M_PI) / 180.0F vec3_t vec3_origin = {0, 0, 0}; 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]; } } 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 angle = angles[YAW] *(M_PI * 2 / 360); sy = sin(angle); cy = cos(angle); angle = angles[PITCH] *(M_PI * 2 / 360); sp = sin(angle); cp = cos(angle); angle = angles[ROLL] *(M_PI * 2 / 360); sr = sin(angle); cr = cos(angle); if(forward){ forward[0] = cp * cy; forward[1] = cp * sy; forward[2] = -sp; } if(right){ right[0] =(-1 * sr * sp * cy + -1 * cr * -sy); right[1] =(-1 * sr * sp * sy + -1 * cr * cy); right[2] = -1 * sr * cp; } if(up){ up[0] =(cr * sp * cy + -sr * -sy); up[1] =(cr * sp * sy + -sr * cy); up[2] = cr * cp; } } void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal){ float d; vec3_t n; float inv_denom; inv_denom = 1.0F / DotProduct(normal, normal); d = DotProduct(normal, p) * inv_denom; n[0] = normal[0] * inv_denom; n[1] = normal[1] * inv_denom; n[2] = normal[2] * inv_denom; dst[0] = p[0] - d * n[0]; dst[1] = p[1] - d * n[1]; dst[2] = p[2] - d * n[2]; } /* ** assumes "src" is normalized */ void PerpendicularVector(vec3_t dst, const vec3_t src){ int pos; int i; float minelem = 1.0F; vec3_t tempvec; /* ** find the smallest magnitude axially aligned vector */ for(pos = 0, i = 0; i < 3; i++){ if(fabs(src[i]) < minelem){ pos = i; minelem = fabs(src[i]); } } tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; tempvec[pos] = 1.0F; /* ** project the point onto the plane defined by src */ ProjectPointOnPlane(dst, tempvec, src); /* ** normalize the result */ VectorNormalize(dst); } /* R_ConcatRotations */ void R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3]){ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; } /* R_ConcatTransforms */ void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]){ out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; } float Q_fabs(float f){ int tmp = *(int *)(char *) & f; tmp &= 0x7FFFFFFF; return *(float *)(char *) &tmp; } /* LerpAngle */ float LerpAngle(float a2, float a1, float frac){ if(a1 - a2 > 180) a1 -= 360; if(a1 - a2 < -180) a1 += 360; return a2 + frac *(a1 - a2); } float anglemod(float a){ a =(360.0 / 65536) *((int)(a *(65536 / 360.0)) & 65535); return a; } /* BoxOnPlaneSide Returns 1, 2, or 1 + 2 */ 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; } 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(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]; } 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]; } 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; } int Q_log2(int val){ int answer = 0; while(val >>= 1) answer++; return answer; } /* Like glob_match, but match pattern against any final segment of text. */ int COM_GlobMatchStar(char *pattern, char *text){ register char *p = pattern, *t = text; register char c, c1; while((c = *p++) == '?' || c == '*') if(c == '?' && *t++ == '\0') return 0; if(c == '\0') return 1; if(c == '\\') c1 = *p; else c1 = c; while(1){ if((c == '[' || *t == c1) && COM_GlobMatch(p - 1, t)) return 1; if(*t++ == '\0') return 0; } } /* Match the pattern against the text; return 1 if it matches, 0 otherwise. A match means the entire string 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 `\'. */ int COM_GlobMatch(char *pattern, char *text){ register char *p = pattern, *t = text; register char c; while((c = *p++) != '\0') switch(c){ case '?': if(*t == '\0') return 0; else ++t; break; case '\\': if(*p++ != *t++) return 0; break; case '*': return COM_GlobMatchStar(p, t); case '[': { register char c1 = *t++; int invert; if(!c1) return(0); invert =((*p == '!') ||(*p == '^')); if(invert) p++; c = *p++; while(1){ register char cstart = c, cend = c; if(c == '\\'){ cstart = *p++; cend = cstart; } if(c == '\0') return 0; c = *p++; if(c == '-' && *p != ']'){ cend = *p++; if(cend == '\\') cend = *p++; if(cend == '\0') return 0; c = *p++; } if(c1 >= cstart && c1 <= cend) goto match; if(c == ']') break; } if(!invert) return 0; break; match: /* Skip the rest of the [...] construct that already matched. */ while(c != ']'){ if(c == '\0') return 0; c = *p++; if(c == '\0') return 0; else if(c == '\\') ++p; } if(invert) return 0; break; } default: if(c != *t++) return 0; } return *t == '\0'; } /* COM_SkipPath */ char *COM_SkipPath(char *pathname){ char *last; last = pathname; while(*pathname){ if(*pathname == '/') last = pathname + 1; pathname++; } return last; } /* COM_StripExtension */ void COM_StripExtension(char *in, char *out){ while(*in && *in != '.') *out++ = *in++; *out = 0; } /* BYTE ORDER FUNCTIONS */ qboolean bigendien; // can't just use function pointers, or dll linkage can // mess up when qcommon is included in multiple places short (*_BigShort)(short l); short (*_LittleShort)(short l); int (*_BigLong)(int l); int (*_LittleLong)(int l); float (*_BigFloat)(float l); 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); } 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; } 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; _BigFloat = FloatSwap; _LittleFloat = FloatNoSwap; } else { bigendien = true; _BigShort = ShortNoSwap; _LittleShort = ShortSwap; _BigLong = LongNoSwap; _LittleLong = LongSwap; _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. FIXME: make this buffer size safe someday */ char *va(char *format, ...){ va_list argptr; static char string[1024]; va_start(argptr, format); vsnprintf(string, 1024, format, argptr); va_end(argptr); return string; } char com_token[MAX_TOKEN_CHARS]; /* 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){ 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){ len = 0; } com_token[len] = 0; *data_p = data; return com_token; } /* LIBRARY REPLACEMENT FUNCTIONS */ int Q_stricmp(char *s1, char *s2){ #ifdef _WIN32 return strnicmp(s1, s2, 9999); #else return strncasecmp(s1, s2, 9999); #endif } void Com_sprintf(char *dest, int size, char *fmt, ...){ int len; va_list argptr; static char bigbuffer[0x10000]; va_start(argptr, fmt); len = vsnprintf(bigbuffer, 0x10000, fmt, argptr); va_end(argptr); strncpy(dest, bigbuffer, size - 1); } /* 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, "\\")){ 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; }