+ srcstringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!srcstringbuffer)
+ {
+ 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;
+ }
+ dststringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!dststringbuffer)
+ {
+ VM_Warning("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
+ return;
+ }
+
+ for (i = 0;i < dststringbuffer->num_strings;i++)
+ if (dststringbuffer->strings[i])
+ Mem_Free(dststringbuffer->strings[i]);
+ if (dststringbuffer->strings)
+ Mem_Free(dststringbuffer->strings);
+ *dststringbuffer = *srcstringbuffer;
+ if (dststringbuffer->max_strings)
+ dststringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(dststringbuffer->strings[0]) * dststringbuffer->max_strings);
+
+ for (i = 0;i < dststringbuffer->num_strings;i++)
+ {
+ if (srcstringbuffer->strings[i])
+ {
+ size_t stringlen;
+ stringlen = strlen(srcstringbuffer->strings[i]) + 1;
+ dststringbuffer->strings[i] = (char *)Mem_Alloc(prog->progs_mempool, stringlen);
+ memcpy(dststringbuffer->strings[i], srcstringbuffer->strings[i], stringlen);
+ }
+ }
+}
+
+/*
+========================
+VM_buf_sort
+sort buffer by beginnings of strings (cmplength defaults it's length)
+"backward == TRUE" means that sorting goes upside-down
+void buf_sort(float bufhandle, float cmplength, float backward) = #464;
+========================
+*/
+void VM_buf_sort (void)
+{
+ prvm_stringbuffer_t *stringbuffer;
+ VM_SAFEPARMCOUNT(3, VM_buf_sort);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ VM_Warning("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ if(stringbuffer->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;
+ }
+ stringbuffers_sortlength = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(stringbuffers_sortlength <= 0)
+ stringbuffers_sortlength = 0x7FFFFFFF;
+
+ if(!PRVM_G_FLOAT(OFS_PARM2))
+ qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsUP);
+ else
+ qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
+
+ BufStr_Shrink(stringbuffer);
+}
+
+/*
+========================
+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)
+{
+ prvm_stringbuffer_t *stringbuffer;
+ char k[VM_STRINGTEMP_LENGTH];
+ const char *sep;
+ int i;
+ size_t l;
+ VM_SAFEPARMCOUNT(2, VM_buf_implode);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ if(!stringbuffer)
+ {
+ VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ if(!stringbuffer->num_strings)
+ return;
+ sep = PRVM_G_STRING(OFS_PARM1);
+ k[0] = 0;
+ for(l = i = 0;i < stringbuffer->num_strings;i++)
+ {
+ if(stringbuffer->strings[i])
+ {
+ l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
+ if (l >= sizeof(k) - 1)
+ break;
+ strlcat(k, sep, sizeof(k));
+ strlcat(k, stringbuffer->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)
+{
+ prvm_stringbuffer_t *stringbuffer;
+ int strindex;
+ VM_SAFEPARMCOUNT(2, VM_bufstr_get);
+
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ 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)
+ {
+ VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
+ return;
+ }
+ if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(stringbuffer->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 strindex;
+ prvm_stringbuffer_t *stringbuffer;
+ const char *news;
+
+ VM_SAFEPARMCOUNT(3, VM_bufstr_set);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ VM_Warning("VM_bufstr_set: 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 >= 1000000) // huge number of strings
+ {
+ VM_Warning("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME);
+ return;
+ }
+
+ BufStr_Expand(stringbuffer, strindex);
+ stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
+
+ if(stringbuffer->strings[strindex])
+ Mem_Free(stringbuffer->strings[strindex]);
+ stringbuffer->strings[strindex] = NULL;
+
+ news = PRVM_G_STRING(OFS_PARM2);
+ if (news && news[0])
+ {
+ size_t alloclen = strlen(news) + 1;
+ stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+ memcpy(stringbuffer->strings[strindex], news, alloclen);
+ }
+
+ BufStr_Shrink(stringbuffer);
+}
+
+/*
+========================
+VM_bufstr_add
+adds string to buffer in first free slot and returns its index
+"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 order, strindex;
+ prvm_stringbuffer_t *stringbuffer;
+ const char *string;
+ size_t alloclen;
+
+ VM_SAFEPARMCOUNT(3, VM_bufstr_add);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ if(!stringbuffer)
+ {
+ VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ string = PRVM_G_STRING(OFS_PARM1);
+ if(!string || !string[0])
+ {
+ VM_Warning("VM_bufstr_add: can not add an empty string to buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ order = (int)PRVM_G_FLOAT(OFS_PARM2);
+ if(order)
+ strindex = stringbuffer->num_strings;
+ else
+ for (strindex = 0;strindex < stringbuffer->num_strings;strindex++)
+ if (stringbuffer->strings[strindex] == NULL)
+ break;
+
+ BufStr_Expand(stringbuffer, strindex);
+
+ stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
+ alloclen = strlen(string) + 1;
+ stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+ memcpy(stringbuffer->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;
+ prvm_stringbuffer_t *stringbuffer;
+ VM_SAFEPARMCOUNT(2, VM_bufstr_free);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ 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)
+ {
+ VM_Warning("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME);
+ return;
+ }
+
+ if (i < stringbuffer->num_strings)
+ {
+ if(stringbuffer->strings[i])
+ Mem_Free(stringbuffer->strings[i]);
+ stringbuffer->strings[i] = NULL;
+ }
+
+ BufStr_Shrink(stringbuffer);
+}
+
+
+
+
+
+
+
+void VM_buf_cvarlist(void)
+{
+ cvar_t *cvar;
+ const char *partial, *antipartial;
+ size_t len, antilen;
+ size_t alloclen;
+ qboolean ispattern, antiispattern;
+ int n;
+ prvm_stringbuffer_t *stringbuffer;
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_buf_cvarlist);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+
+ partial = PRVM_G_STRING(OFS_PARM1);
+ if(!partial)
+ len = 0;
+ else
+ len = strlen(partial);
+
+ if(prog->argc == 3)
+ antipartial = PRVM_G_STRING(OFS_PARM2);
+ else
+ antipartial = NULL;
+ if(!antipartial)
+ antilen = 0;
+ else
+ antilen = strlen(antipartial);
+
+ for (n = 0;n < stringbuffer->num_strings;n++)
+ if (stringbuffer->strings[n])
+ Mem_Free(stringbuffer->strings[n]);
+ if (stringbuffer->strings)
+ Mem_Free(stringbuffer->strings);
+ stringbuffer->strings = NULL;
+
+ ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
+ antiispattern = antipartial && (strchr(antipartial, '*') || strchr(antipartial, '?'));
+
+ n = 0;
+ for(cvar = cvar_vars; cvar; cvar = cvar->next)
+ {
+ if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
+ continue;
+
+ if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
+ continue;
+
+ ++n;
+ }
+
+ stringbuffer->max_strings = stringbuffer->num_strings = n;
+ if (stringbuffer->max_strings)
+ stringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(stringbuffer->strings[0]) * stringbuffer->max_strings);
+
+ n = 0;
+ for(cvar = cvar_vars; cvar; cvar = cvar->next)
+ {
+ if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
+ continue;
+
+ if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
+ continue;
+
+ alloclen = strlen(cvar->name) + 1;
+ stringbuffer->strings[n] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+ memcpy(stringbuffer->strings[n], cvar->name, alloclen);
+
+ ++n;
+ }
+}
+
+
+
+
+//=============
+
+/*
+==============
+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);
+}
+
+
+void VM_uncolorstring (void)
+{
+ char szNewString[VM_STRINGTEMP_LENGTH];
+ const char *szString;
+
+ // Prepare Strings
+ VM_SAFEPARMCOUNT(1, VM_uncolorstring);
+ szString = PRVM_G_STRING(OFS_PARM0);
+ COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), TRUE);
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
+
+}
+
+// #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
+//strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.
+void VM_strstrofs (void)
+{
+ const char *instr, *match;
+ int firstofs;
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
+ instr = PRVM_G_STRING(OFS_PARM0);
+ match = PRVM_G_STRING(OFS_PARM1);
+ firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
+
+ if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ return;
+ }
+
+ match = strstr(instr+firstofs, match);
+ if (!match)
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = match - instr;
+}
+
+//#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))
+ PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(unsigned)PRVM_G_FLOAT(OFS_PARM1)];
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+}
+
+//#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);
+}
+
+static int chrconv_number(int i, int base, int conv)
+{
+ i -= base;
+ switch (conv)
+ {
+ default:
+ case 5:
+ case 6:
+ case 0:
+ break;
+ case 1:
+ base = '0';
+ break;
+ case 2:
+ base = '0'+128;
+ break;
+ case 3:
+ base = '0'-30;
+ break;
+ case 4:
+ base = '0'+128-30;
+ break;
+ }
+ return i + base;
+}
+static int chrconv_punct(int i, int base, int conv)
+{
+ i -= base;
+ switch (conv)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ base = 0;
+ break;
+ case 2:
+ base = 128;
+ break;
+ }
+ return i + base;
+}
+
+static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
+{
+ //convert case and colour seperatly...
+
+ i -= baset + basec;
+ switch (convt)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ baset = 0;
+ break;
+ case 2:
+ baset = 128;
+ break;
+
+ case 5:
+ case 6:
+ baset = 128*((charnum&1) == (convt-5));
+ break;
+ }
+
+ switch (convc)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ basec = 'a';
+ break;
+ case 2:
+ basec = 'A';
+ break;
+ }
+ return i + basec + baset;
+}
+// #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
+//bulk convert a string. change case or colouring.
+void VM_strconv (void)
+{
+ int ccase, redalpha, rednum, len, i;
+ unsigned char resbuf[VM_STRINGTEMP_LENGTH];
+ unsigned char *result = resbuf;
+
+ VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
+
+ ccase = (int) PRVM_G_FLOAT(OFS_PARM0); //0 same, 1 lower, 2 upper
+ redalpha = (int) PRVM_G_FLOAT(OFS_PARM1); //0 same, 1 white, 2 red, 5 alternate, 6 alternate-alternate
+ rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
+ VM_VarString(3, (char *) resbuf, sizeof(resbuf));
+ len = strlen((char *) resbuf);
+
+ for (i = 0; i < len; i++, result++) //should this be done backwards?
+ {
+ if (*result >= '0' && *result <= '9') //normal numbers...
+ *result = chrconv_number(*result, '0', rednum);
+ else if (*result >= '0'+128 && *result <= '9'+128)
+ *result = chrconv_number(*result, '0'+128, rednum);
+ else if (*result >= '0'+128-30 && *result <= '9'+128-30)
+ *result = chrconv_number(*result, '0'+128-30, rednum);
+ else if (*result >= '0'-30 && *result <= '9'-30)
+ *result = chrconv_number(*result, '0'-30, rednum);
+
+ else if (*result >= 'a' && *result <= 'z') //normal numbers...
+ *result = chrchar_alpha(*result, 'a', 0, ccase, redalpha, i);
+ else if (*result >= 'A' && *result <= 'Z') //normal numbers...
+ *result = chrchar_alpha(*result, 'A', 0, ccase, redalpha, i);
+ else if (*result >= 'a'+128 && *result <= 'z'+128) //normal numbers...
+ *result = chrchar_alpha(*result, 'a', 128, ccase, redalpha, i);
+ else if (*result >= 'A'+128 && *result <= 'Z'+128) //normal numbers...
+ *result = chrchar_alpha(*result, 'A', 128, ccase, redalpha, i);
+
+ else if ((*result & 127) < 16 || !redalpha) //special chars..
+ *result = *result;
+ else if (*result < 128)
+ *result = chrconv_punct(*result, 0, redalpha);
+ else
+ *result = chrconv_punct(*result, 128, redalpha);
+ }
+ *result = '\0';
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString((char *) resbuf);
+}
+
+// #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
+void VM_strpad (void)
+{
+ char src[VM_STRINGTEMP_LENGTH];
+ char destbuf[VM_STRINGTEMP_LENGTH];
+ int pad;
+ VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
+ pad = (int) PRVM_G_FLOAT(OFS_PARM0);
+ VM_VarString(1, src, sizeof(src));
+
+ // note: < 0 = left padding, > 0 = right padding,
+ // this is reverse logic of printf!
+ dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(destbuf);
+}
+
+// #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
+//uses qw style \key\value strings
+void VM_infoadd (void)
+{
+ const char *info, *key;
+ char value[VM_STRINGTEMP_LENGTH];
+ char temp[VM_STRINGTEMP_LENGTH];
+
+ VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
+ info = PRVM_G_STRING(OFS_PARM0);
+ key = PRVM_G_STRING(OFS_PARM1);
+ VM_VarString(2, value, sizeof(value));
+
+ strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
+
+ InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(temp);
+}
+
+// #227 string(string info, string key) infoget (FTE_STRINGS)
+//uses qw style \key\value strings
+void VM_infoget (void)
+{
+ const char *info;
+ const char *key;
+ char value[VM_STRINGTEMP_LENGTH];
+
+ VM_SAFEPARMCOUNT(2, VM_infoget);
+ info = PRVM_G_STRING(OFS_PARM0);
+ key = PRVM_G_STRING(OFS_PARM1);
+
+ InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(value);
+}
+
+//#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
+// also float(string s1, string s2) strcmp (FRIK_FILE)
+void VM_strncmp (void)
+{
+ const char *s1, *s2;
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncmp);
+ s1 = PRVM_G_STRING(OFS_PARM0);
+ s2 = PRVM_G_STRING(OFS_PARM1);
+ if (prog->argc > 2)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
+ }
+ else
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = strcmp(s1, s2);
+ }
+}
+
+// #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
+// #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
+void VM_strncasecmp (void)
+{
+ const char *s1, *s2;
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncasecmp);
+ s1 = PRVM_G_STRING(OFS_PARM0);
+ s2 = PRVM_G_STRING(OFS_PARM1);
+ if (prog->argc > 2)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = strncasecmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
+ }
+ else
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = strcasecmp(s1, s2);
+ }
+}
+
+// #494 float(float caseinsensitive, string s, ...) crc16
+void VM_crc16(void)
+{
+ float insensitive;
+ static char s[VM_STRINGTEMP_LENGTH];
+ VM_SAFEPARMCOUNTRANGE(2, 8, VM_hash);
+ insensitive = PRVM_G_FLOAT(OFS_PARM0);
+ VM_VarString(1, s, sizeof(s));
+ PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
+}
+
+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;
+}
+
+void VM_ClearTraceGlobals(void)
+{
+ // clean up all trace globals when leaving the VM (anti-triggerbot safeguard)
+ prvm_eval_t *val;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_allsolid)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_startsolid)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_fraction)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inwater)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inopen)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos)))
+ VectorClear(val->vector);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_normal)))
+ VectorClear(val->vector);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_dist)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_ent)))
+ val->edict = PRVM_EDICT_TO_PROG(prog->edicts);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
+ val->string = 0;
+}
+
+//=============
+
+void VM_Cmd_Init(void)
+{
+ // only init the stuff for the current prog
+ VM_Files_Init();
+ VM_Search_Init();
+ VM_Gecko_Init();
+// VM_BufStr_Init();
+}
+
+void VM_Cmd_Reset(void)
+{
+ CL_PurgeOwner( MENUOWNER );
+ VM_Search_Reset();
+ VM_Files_CloseAll();
+ VM_Gecko_Destroy();
+// VM_BufStr_ShutDown();
+}
+
+// #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
+// does URI escaping on a string (replace evil stuff by %AB escapes)
+void VM_uri_escape (void)
+{
+ char src[VM_STRINGTEMP_LENGTH];
+ char dest[VM_STRINGTEMP_LENGTH];
+ char *p, *q;
+ static const char *hex = "0123456789ABCDEF";
+
+ VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_escape);
+ VM_VarString(0, src, sizeof(src));
+
+ for(p = src, q = dest; *p && q < dest + sizeof(dest) - 3; ++p)
+ {
+ if((*p >= 'A' && *p <= 'Z')
+ || (*p >= 'a' && *p <= 'z')
+ || (*p >= '0' && *p <= '9')
+ || (*p == '-') || (*p == '_') || (*p == '.')
+ || (*p == '!') || (*p == '~') || (*p == '*')
+ || (*p == '\'') || (*p == '(') || (*p == ')'))
+ *q++ = *p;
+ else
+ {
+ *q++ = '%';
+ *q++ = hex[(*(unsigned char *)p >> 4) & 0xF];
+ *q++ = hex[ *(unsigned char *)p & 0xF];
+ }
+ }
+ *q++ = 0;
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
+}
+
+// #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
+// does URI unescaping on a string (get back the evil stuff)
+void VM_uri_unescape (void)
+{
+ char src[VM_STRINGTEMP_LENGTH];
+ char dest[VM_STRINGTEMP_LENGTH];
+ char *p, *q;
+ int hi, lo;
+
+ VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_unescape);
+ VM_VarString(0, src, sizeof(src));
+
+ for(p = src, q = dest; *p; ) // no need to check size, because unescape can't expand
+ {
+ if(*p == '%')
+ {
+ if(p[1] >= '0' && p[1] <= '9')
+ hi = p[1] - '0';
+ else if(p[1] >= 'a' && p[1] <= 'f')
+ hi = p[1] - 'a' + 10;
+ else if(p[1] >= 'A' && p[1] <= 'F')
+ hi = p[1] - 'A' + 10;
+ else
+ goto nohex;
+ if(p[2] >= '0' && p[2] <= '9')
+ lo = p[2] - '0';
+ else if(p[2] >= 'a' && p[2] <= 'f')
+ lo = p[2] - 'a' + 10;
+ else if(p[2] >= 'A' && p[2] <= 'F')
+ lo = p[2] - 'A' + 10;
+ else
+ goto nohex;
+ if(hi != 0 || lo != 0) // don't unescape NUL bytes
+ *q++ = (char) (hi * 0x10 + lo);
+ p += 3;
+ continue;
+ }
+
+nohex:
+ // otherwise:
+ *q++ = *p++;
+ }
+ *q++ = 0;
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
+}
+
+// #502 string(string filename) whichpack (DP_QC_WHICHPACK)
+// returns the name of the pack containing a file, or "" if it is not in any pack (but local or non-existant)
+void VM_whichpack (void)
+{
+ const char *fn, *pack;
+
+ VM_SAFEPARMCOUNT(1, VM_whichpack);
+ fn = PRVM_G_STRING(OFS_PARM0);
+ pack = FS_WhichPack(fn);
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(pack ? pack : "");
+}
+
+typedef struct
+{
+ int prognr;
+ double starttime;
+ float id;
+ char buffer[MAX_INPUTLINE];
+}
+uri_to_prog_t;
+
+static void uri_to_string_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
+{
+ uri_to_prog_t *handle = (uri_to_prog_t *) cbdata;
+
+ if(!PRVM_ProgLoaded(handle->prognr))
+ {
+ // curl reply came too late... so just drop it
+ Z_Free(handle);
+ return;
+ }
+
+ PRVM_SetProg(handle->prognr);
+ PRVM_Begin;
+ if((prog->starttime == handle->starttime) && (prog->funcoffsets.URI_Get_Callback))
+ {
+ if(length_received >= sizeof(handle->buffer))
+ length_received = sizeof(handle->buffer) - 1;
+ handle->buffer[length_received] = 0;
+
+ PRVM_G_FLOAT(OFS_PARM0) = handle->id;
+ PRVM_G_FLOAT(OFS_PARM1) = status;
+ PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(handle->buffer);
+ PRVM_ExecuteProgram(prog->funcoffsets.URI_Get_Callback, "QC function URI_Get_Callback is missing");
+ }
+ PRVM_End;
+
+ Z_Free(handle);
+}
+
+// uri_get() gets content from an URL and calls a callback "uri_get_callback" with it set as string; an unique ID of the transfer is returned
+// returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string
+void VM_uri_get (void)
+{
+ const char *url;
+ float id;
+ qboolean ret;
+ uri_to_prog_t *handle;
+
+ if(!prog->funcoffsets.URI_Get_Callback)
+ PRVM_ERROR("uri_get called by %s without URI_Get_Callback defined", PRVM_NAME);
+
+ VM_SAFEPARMCOUNT(2, VM_uri_get);
+
+ url = PRVM_G_STRING(OFS_PARM0);
+ id = PRVM_G_FLOAT(OFS_PARM1);
+ handle = (uri_to_prog_t *) Z_Malloc(sizeof(*handle)); // this can't be the prog's mem pool, as curl may call the callback later!
+
+ handle->prognr = PRVM_GetProgNr();
+ handle->starttime = prog->starttime;
+ handle->id = id;
+ ret = Curl_Begin_ToMemory(url, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+ if(ret)
+ {
+ PRVM_G_INT(OFS_RETURN) = 1;
+ }
+ else
+ {
+ Z_Free(handle);
+ PRVM_G_INT(OFS_RETURN) = 0;
+ }
+}
+
+void VM_netaddress_resolve (void)
+{
+ const char *ip;
+ char normalized[128];
+ int port;
+ lhnetaddress_t addr;
+
+ VM_SAFEPARMCOUNTRANGE(1, 2, VM_netaddress_resolve);
+
+ ip = PRVM_G_STRING(OFS_PARM0);
+ port = 0;
+ if(prog->argc > 1)
+ port = (int) PRVM_G_FLOAT(OFS_PARM1);
+
+ if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(normalized);
+ else
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
+}
+
+//string(void) getextresponse = #624; // returns the next extResponse packet that was sent to this client
+void VM_CL_getextresponse (void)
+{
+ VM_SAFEPARMCOUNT(0,VM_argv);
+
+ if (cl_net_extresponse_count <= 0)
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ else
+ {
+ int first;
+ --cl_net_extresponse_count;
+ first = (cl_net_extresponse_last + NET_EXTRESPONSE_MAX - cl_net_extresponse_count) % NET_EXTRESPONSE_MAX;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(cl_net_extresponse[first]);
+ }
+}
+
+void VM_SV_getextresponse (void)
+{
+ VM_SAFEPARMCOUNT(0,VM_argv);
+
+ if (sv_net_extresponse_count <= 0)
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ else
+ {
+ int first;
+ --sv_net_extresponse_count;
+ first = (sv_net_extresponse_last + NET_EXTRESPONSE_MAX - sv_net_extresponse_count) % NET_EXTRESPONSE_MAX;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(sv_net_extresponse[first]);
+ }
+}
+
+/*
+=========
+VM_M_callfunction
+
+ callfunction(...,string function_name)
+Extension: pass
+=========
+*/
+mfunction_t *PRVM_ED_FindFunction (const char *name);
+void VM_callfunction(void)
+{
+ mfunction_t *func;
+ const char *s;
+
+ VM_SAFEPARMCOUNTRANGE(1, 8, VM_callfunction);
+
+ s = PRVM_G_STRING(OFS_PARM0+(prog->argc - 1)*3);
+
+ VM_CheckEmptyString(s);
+
+ func = PRVM_ED_FindFunction(s);
+
+ if(!func)
+ PRVM_ERROR("VM_callfunciton: function %s not found !", s);
+ else if (func->first_statement < 0)
+ {
+ // negative statements are built in functions
+ int builtinnumber = -func->first_statement;
+ prog->xfunction->builtinsprofile++;
+ if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
+ prog->builtins[builtinnumber]();
+ else
+ PRVM_ERROR("No such builtin #%i in %s; most likely cause: outdated engine build. Try updating!", builtinnumber, PRVM_NAME);
+ }
+ else if(func - prog->functions > 0)
+ {
+ prog->argc--;
+ PRVM_ExecuteProgram(func - prog->functions,"");
+ prog->argc++;
+ }
+}
+
+/*
+=========
+VM_isfunction
+
+float isfunction(string function_name)
+=========
+*/
+mfunction_t *PRVM_ED_FindFunction (const char *name);
+void VM_isfunction(void)
+{
+ mfunction_t *func;
+ const char *s;
+
+ VM_SAFEPARMCOUNT(1, VM_isfunction);
+
+ s = PRVM_G_STRING(OFS_PARM0);
+
+ VM_CheckEmptyString(s);
+
+ func = PRVM_ED_FindFunction(s);
+
+ if(!func)
+ PRVM_G_FLOAT(OFS_RETURN) = false;
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = true;
+}