+static void BufStr_ClearBuffer (int index)
+{
+ qcstrbuffer_t *b = qcstringbuffers[index];
+ int i;
+
+ if(b)
+ {
+ if(b->num_strings > 0)
+ {
+ for(i=0;i<b->num_strings;i++)
+ if(b->strings[i])
+ Z_Free(b->strings[i]);
+ num_qcstringbuffers--;
+ }
+ Z_Free(qcstringbuffers[index]);
+ qcstringbuffers[index] = NULL;
+ }
+}
+
+static int BufStr_FindFreeString (qcstrbuffer_t *b)
+{
+ int i;
+ for(i=0;i<b->num_strings;i++)
+ if(!b->strings[i] || !b->strings[i][0])
+ return i;
+ if(i == MAX_QCSTR_STRINGS) return -1;
+ else return i;
+}
+
+static int BufStr_SortStringsUP (const void *in1, const void *in2)
+{
+ const char *a, *b;
+ a = *((const char **) in1);
+ b = *((const char **) in2);
+ if(!a[0]) return 1;
+ if(!b[0]) return -1;
+ return strncmp(a, b, buf_sortpower);
+}
+
+static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
+{
+ const char *a, *b;
+ a = *((const char **) in1);
+ b = *((const char **) in2);
+ if(!a[0]) return 1;
+ if(!b[0]) return -1;
+ return strncmp(b, a, buf_sortpower);
+}
+
+/*
+========================
+VM_buf_create
+creates new buffer, and returns it's index, returns -1 if failed
+float buf_create(void) = #460;
+========================
+*/
+void VM_buf_create (void)
+{
+ int i;
+ VM_SAFEPARMCOUNT(0, VM_buf_create);
+ i = BufStr_FindFreeBuffer();
+ if(i >= 0)
+ num_qcstringbuffers++;
+ //else
+ //Con_Printf("VM_buf_create: buffers overflow in %s\n", PRVM_NAME);
+ PRVM_G_FLOAT(OFS_RETURN) = i;
+}
+
+/*
+========================
+VM_buf_del
+deletes buffer and all strings in it
+void buf_del(float bufhandle) = #461;
+========================
+*/
+void VM_buf_del (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_buf_del);
+ if(BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)))
+ BufStr_ClearBuffer((int)PRVM_G_FLOAT(OFS_PARM0));
+ else
+ {
+ VM_Warning("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+}
+
+/*
+========================
+VM_buf_getsize
+how many strings are stored in buffer
+float buf_getsize(float bufhandle) = #462;
+========================
+*/
+void VM_buf_getsize (void)
+{
+ qcstrbuffer_t *b;
+ VM_SAFEPARMCOUNT(1, VM_buf_getsize);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ VM_Warning("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = b->num_strings;
+}
+
+/*
+========================
+VM_buf_copy
+copy all content from one buffer to another, make sure it exists
+void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
+========================
+*/
+void VM_buf_copy (void)
+{
+ qcstrbuffer_t *b1, *b2;
+ int i;
+ VM_SAFEPARMCOUNT(2, VM_buf_copy);
+
+ b1 = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b1)
+ {
+ VM_Warning("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ i = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
+ {
+ VM_Warning("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME);
+ return;
+ }
+ b2 = BUFSTR_BUFFER(i);
+ if(!b2)
+ {
+ VM_Warning("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
+ return;
+ }
+
+ BufStr_ClearBuffer(i);
+ qcstringbuffers[i] = (qcstrbuffer_t *)Z_Malloc(sizeof(qcstrbuffer_t));
+ memset(qcstringbuffers[i], 0, sizeof(qcstrbuffer_t));
+ b2->num_strings = b1->num_strings;
+
+ for(i=0;i<b1->num_strings;i++)
+ if(b1->strings[i] && b1->strings[i][0])
+ {
+ size_t stringlen;
+ stringlen = strlen(b1->strings[i]) + 1;
+ b2->strings[i] = (char *)Z_Malloc(stringlen);
+ if(!b2->strings[i])
+ {
+ VM_Warning("VM_buf_copy: not enough memory for buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
+ break;
+ }
+ memcpy(b2->strings[i], b1->strings[i], stringlen);
+ }
+}
+
+/*
+========================
+VM_buf_sort
+sort buffer by beginnings of strings (sortpower defaults it's lenght)
+"backward == TRUE" means that sorting goes upside-down
+void buf_sort(float bufhandle, float sortpower, float backward) = #464;
+========================
+*/
+void VM_buf_sort (void)
+{
+ qcstrbuffer_t *b;
+ int i;
+ VM_SAFEPARMCOUNT(3, VM_buf_sort);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ VM_Warning("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ if(b->num_strings <= 0)
+ {
+ VM_Warning("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ buf_sortpower = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(buf_sortpower <= 0)
+ buf_sortpower = 99999999;
+
+ if(!PRVM_G_FLOAT(OFS_PARM2))
+ qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsUP);
+ else
+ qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
+
+ for(i=b->num_strings-1;i>=0;i--) //[515]: delete empty lines
+ if(b->strings)
+ {
+ if(b->strings[i][0])
+ break;
+ else
+ {
+ Z_Free(b->strings[i]);
+ --b->num_strings;
+ b->strings[i] = NULL;
+ }
+ }
+ else
+ --b->num_strings;
+}
+
+/*
+========================
+VM_buf_implode
+concantenates all buffer string into one with "glue" separator and returns it as tempstring
+string buf_implode(float bufhandle, string glue) = #465;
+========================
+*/
+void VM_buf_implode (void)
+{
+ qcstrbuffer_t *b;
+ char k[VM_STRINGTEMP_LENGTH];
+ const char *sep;
+ int i;
+ size_t l;
+ VM_SAFEPARMCOUNT(2, VM_buf_implode);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ if(!b)
+ {
+ VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ if(!b->num_strings)
+ return;
+ sep = PRVM_G_STRING(OFS_PARM1);
+ k[0] = 0;
+ for(l=i=0;i<b->num_strings;i++)
+ if(b->strings[i])
+ {
+ l += (i > 0 ? strlen(sep) : 0) + strlen(b->strings[i]);
+ if (l >= sizeof(k) - 1)
+ break;
+ strlcat(k, sep, sizeof(k));
+ strlcat(k, b->strings[i], sizeof(k));
+ }
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(k);
+}
+
+/*
+========================
+VM_bufstr_get
+get a string from buffer, returns tempstring, dont str_unzone it!
+string bufstr_get(float bufhandle, float string_index) = #465;
+========================
+*/
+void VM_bufstr_get (void)
+{
+ qcstrbuffer_t *b;
+ int strindex;
+ VM_SAFEPARMCOUNT(2, VM_bufstr_get);
+
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ VM_Warning("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(strindex < 0 || strindex > MAX_QCSTR_STRINGS)
+ {
+ VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
+ return;
+ }
+ if(b->num_strings <= strindex)
+ return;
+ if(b->strings[strindex])
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(b->strings[strindex]);
+}
+
+/*
+========================
+VM_bufstr_set
+copies a string into selected slot of buffer
+void bufstr_set(float bufhandle, float string_index, string str) = #466;
+========================
+*/
+void VM_bufstr_set (void)
+{
+ int bufindex, strindex;
+ qcstrbuffer_t *b;
+ const char *news;
+ size_t alloclen;
+
+ VM_SAFEPARMCOUNT(3, VM_bufstr_set);
+
+ bufindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+ b = BUFSTR_BUFFER(bufindex);
+ if(!b)
+ {
+ VM_Warning("VM_bufstr_set: invalid buffer %i used in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(strindex < 0 || strindex > MAX_QCSTR_STRINGS)
+ {
+ VM_Warning("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME);
+ return;
+ }
+ news = PRVM_G_STRING(OFS_PARM2);
+ if(b->strings[strindex])
+ Z_Free(b->strings[strindex]);
+ alloclen = strlen(news) + 1;
+ b->strings[strindex] = (char *)Z_Malloc(alloclen);
+ memcpy(b->strings[strindex], news, alloclen);
+}
+
+/*
+========================
+VM_bufstr_add
+adds string to buffer in nearest free slot and returns it
+"order == TRUE" means that string will be added after last "full" slot
+float bufstr_add(float bufhandle, string str, float order) = #467;
+========================
+*/
+void VM_bufstr_add (void)
+{
+ int bufindex, order, strindex;
+ qcstrbuffer_t *b;
+ const char *string;
+ size_t alloclen;
+
+ VM_SAFEPARMCOUNT(3, VM_bufstr_add);
+
+ bufindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+ b = BUFSTR_BUFFER(bufindex);
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ if(!b)
+ {
+ VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ string = PRVM_G_STRING(OFS_PARM1);
+ order = (int)PRVM_G_FLOAT(OFS_PARM2);
+ if(order)
+ strindex = b->num_strings;
+ else
+ {
+ strindex = BufStr_FindFreeString(b);
+ if(strindex < 0)
+ {
+ VM_Warning("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ }
+
+ while(b->num_strings <= strindex)
+ {
+ if(b->num_strings == MAX_QCSTR_STRINGS)
+ {
+ VM_Warning("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME);
+ return;
+ }
+ b->strings[b->num_strings] = NULL;
+ b->num_strings++;
+ }
+ if(b->strings[strindex])
+ Z_Free(b->strings[strindex]);
+ alloclen = strlen(string) + 1;
+ b->strings[strindex] = (char *)Z_Malloc(alloclen);
+ memcpy(b->strings[strindex], string, alloclen);
+ PRVM_G_FLOAT(OFS_RETURN) = strindex;
+}
+
+/*
+========================
+VM_bufstr_free
+delete string from buffer
+void bufstr_free(float bufhandle, float string_index) = #468;
+========================
+*/
+void VM_bufstr_free (void)
+{
+ int i;
+ qcstrbuffer_t *b;
+ VM_SAFEPARMCOUNT(2, VM_bufstr_free);
+
+ b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!b)
+ {
+ VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ i = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(i < 0 || i > MAX_QCSTR_STRINGS)
+ {
+ VM_Warning("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME);
+ return;
+ }
+ if(b->strings[i])
+ Z_Free(b->strings[i]);
+ b->strings[i] = NULL;
+ if(i+1 == b->num_strings)
+ --b->num_strings;
+}
+
+//=============
+
+/*
+==============
+VM_changeyaw
+
+This was a major timewaster in progs, so it was converted to C
+==============
+*/
+void VM_changeyaw (void)
+{
+ prvm_edict_t *ent;
+ float ideal, current, move, speed;
+
+ // this is called (VERY HACKISHLY) by SV_MoveToGoal, so it can not use any
+ // parameters because they are the parameters to SV_MoveToGoal, not this
+ //VM_SAFEPARMCOUNT(0, VM_changeyaw);
+
+ ent = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict);
+ if (ent == prog->edicts)
+ {
+ VM_Warning("changeyaw: can not modify world entity\n");
+ return;
+ }
+ if (ent->priv.server->free)
+ {
+ VM_Warning("changeyaw: can not modify free entity\n");
+ return;
+ }
+ if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.ideal_yaw < 0 || prog->fieldoffsets.yaw_speed < 0)
+ {
+ VM_Warning("changeyaw: angles, ideal_yaw, or yaw_speed field(s) not found\n");
+ return;
+ }
+ current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1]);
+ ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.ideal_yaw)->_float;
+ speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.yaw_speed)->_float;
+
+ if (current == ideal)
+ return;
+ move = ideal - current;
+ if (ideal > current)
+ {
+ if (move >= 180)
+ move = move - 360;
+ }
+ else
+ {
+ if (move <= -180)
+ move = move + 360;
+ }
+ if (move > 0)
+ {
+ if (move > speed)
+ move = speed;
+ }
+ else
+ {
+ if (move < -speed)
+ move = -speed;
+ }
+
+ PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1] = ANGLEMOD (current + move);
+}
+
+/*
+==============
+VM_changepitch
+==============
+*/
+void VM_changepitch (void)
+{
+ prvm_edict_t *ent;
+ float ideal, current, move, speed;
+
+ VM_SAFEPARMCOUNT(1, VM_changepitch);
+
+ ent = PRVM_G_EDICT(OFS_PARM0);
+ if (ent == prog->edicts)
+ {
+ VM_Warning("changepitch: can not modify world entity\n");
+ return;
+ }
+ if (ent->priv.server->free)
+ {
+ VM_Warning("changepitch: can not modify free entity\n");
+ return;
+ }
+ if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.idealpitch < 0 || prog->fieldoffsets.pitch_speed < 0)
+ {
+ VM_Warning("changepitch: angles, idealpitch, or pitch_speed field(s) not found\n");
+ return;
+ }
+ current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0]);
+ ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.idealpitch)->_float;
+ speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pitch_speed)->_float;
+
+ if (current == ideal)
+ return;
+ move = ideal - current;
+ if (ideal > current)
+ {
+ if (move >= 180)
+ move = move - 360;
+ }
+ else
+ {
+ if (move <= -180)
+ move = move + 360;
+ }
+ if (move > 0)
+ {
+ if (move > speed)
+ move = speed;
+ }
+ else
+ {
+ if (move < -speed)
+ move = -speed;
+ }
+
+ PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0] = ANGLEMOD (current + move);
+}
+
+
+static int Is_Text_Color (char c, char t)
+{
+ int a = 0;
+ char c2 = c - (c & 128);
+ char t2 = t - (t & 128);
+
+ if(c != STRING_COLOR_TAG && c2 != STRING_COLOR_TAG) return 0;
+ if(t >= '0' && t <= '9') a = 1;
+ if(t2 >= '0' && t2 <= '9') a = 1;
+/* if(t >= 'A' && t <= 'Z') a = 2;
+ if(t2 >= 'A' && t2 <= 'Z') a = 2;
+
+ if(a == 1 && scr_colortext.integer > 0)
+ return 1;
+ if(a == 2 && scr_multifonts.integer > 0)
+ return 2;
+*/
+ return a;
+}
+
+void VM_uncolorstring (void)
+{
+ const char *in;
+ char out[VM_STRINGTEMP_LENGTH];
+ int k = 0, i = 0;
+
+ VM_SAFEPARMCOUNT(1, VM_uncolorstring);
+ in = PRVM_G_STRING(OFS_PARM0);
+ VM_CheckEmptyString (in);
+
+ while (in[k])
+ {
+ if(in[k+1])
+ if(Is_Text_Color(in[k], in[k+1]) == 1/* || (in[k] == '&' && in[k+1] == 'r')*/)
+ {
+ k += 2;
+ continue;
+ }
+ out[i] = in[k];
+ ++k;
+ ++i;
+ }
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(out);
+}
+
+//#222 string(string s, float index) str2chr (FTE_STRINGS)
+void VM_str2chr (void)
+{
+ const char *s;
+ VM_SAFEPARMCOUNT(2, VM_str2chr);
+ s = PRVM_G_STRING(OFS_PARM0);
+ if((unsigned)PRVM_G_FLOAT(OFS_PARM1) > strlen(s))
+ return;
+ PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(int)PRVM_G_FLOAT(OFS_PARM1)];
+}
+
+//#223 string(float c, ...) chr2str (FTE_STRINGS)
+void VM_chr2str (void)
+{
+ char t[9];
+ int i;
+ VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
+ for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
+ t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
+ t[i] = 0;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
+}
+
+//#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
+void VM_strncmp (void)
+{
+ const char *s1, *s2;
+ VM_SAFEPARMCOUNT(1, VM_strncmp);
+ s1 = PRVM_G_STRING(OFS_PARM0);
+ s2 = PRVM_G_STRING(OFS_PARM1);
+ PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
+}
+
+void VM_wasfreed (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_wasfreed);
+ PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICT(OFS_PARM0)->priv.required->free;
+}
+
+void VM_SetTraceGlobals(const trace_t *trace)
+{
+ prvm_eval_t *val;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_allsolid)))
+ val->_float = trace->allsolid;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_startsolid)))
+ val->_float = trace->startsolid;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_fraction)))
+ val->_float = trace->fraction;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inwater)))
+ val->_float = trace->inwater;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inopen)))
+ val->_float = trace->inopen;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos)))
+ VectorCopy(trace->endpos, val->vector);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_normal)))
+ VectorCopy(trace->plane.normal, val->vector);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_dist)))
+ val->_float = trace->plane.dist;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_ent)))
+ val->edict = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
+ val->_float = trace->startsupercontents;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
+ val->_float = trace->hitsupercontents;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
+ val->_float = trace->hitq3surfaceflags;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
+ val->string = trace->hittexture ? PRVM_SetTempString(trace->hittexture->name) : 0;
+}
+
+//=============
+