+/*
+=========
+VM_getimagesize
+
+vector getimagesize(string pic)
+=========
+*/
+void VM_getimagesize(void)
+{
+ const char *p;
+ cachepic_t *pic;
+
+ VM_SAFEPARMCOUNT(1,VM_getimagesize);
+
+ p = PRVM_G_STRING(OFS_PARM0);
+ VM_CheckEmptyString (p);
+
+ pic = Draw_CachePic_Flags (p, CACHEPICFLAG_NOTPERSISTENT);
+
+ PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
+ PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
+ PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
+}
+
+/*
+=========
+VM_keynumtostring
+
+string keynumtostring(float keynum)
+=========
+*/
+void VM_keynumtostring (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_keynumtostring);
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0)));
+}
+
+/*
+=========
+VM_findkeysforcommand
+
+string findkeysforcommand(string command, float bindmap)
+
+the returned string is an altstring
+=========
+*/
+#define FKFC_NUMKEYS 5
+void M_FindKeysForCommand(const char *command, int *keys);
+void VM_findkeysforcommand(void)
+{
+ const char *cmd;
+ char ret[VM_STRINGTEMP_LENGTH];
+ int keys[FKFC_NUMKEYS];
+ int i;
+ int bindmap;
+
+ VM_SAFEPARMCOUNTRANGE(1, 2, VM_findkeysforcommand);
+
+ cmd = PRVM_G_STRING(OFS_PARM0);
+ if(prog->argc == 2)
+ bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
+ else
+ bindmap = 0; // consistent to "bind"
+
+ VM_CheckEmptyString(cmd);
+
+ Key_FindKeysForCommand(cmd, keys, FKFC_NUMKEYS, bindmap);
+
+ ret[0] = 0;
+ for(i = 0; i < FKFC_NUMKEYS; i++)
+ strlcat(ret, va(" \'%i\'", keys[i]), sizeof(ret));
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(ret);
+}
+
+/*
+=========
+VM_stringtokeynum
+
+float stringtokeynum(string key)
+=========
+*/
+void VM_stringtokeynum (void)
+{
+ VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
+
+ PRVM_G_FLOAT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
+}
+
+/*
+=========
+VM_getkeybind
+
+string getkeybind(float key, float bindmap)
+=========
+*/
+void VM_getkeybind (void)
+{
+ int bindmap;
+ VM_SAFEPARMCOUNTRANGE(1, 2, VM_CL_getkeybind);
+ if(prog->argc == 2)
+ bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
+ else
+ bindmap = 0; // consistent to "bind"
+
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap));
+}
+
+/*
+=========
+VM_setkeybind
+
+float setkeybind(float key, string cmd, float bindmap)
+=========
+*/
+void VM_setkeybind (void)
+{
+ int bindmap;
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_setkeybind);
+ if(prog->argc == 3)
+ bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM2), MAX_BINDMAPS-1);
+ else
+ bindmap = 0; // consistent to "bind"
+
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ if(Key_SetBinding((int)PRVM_G_FLOAT(OFS_PARM0), bindmap, PRVM_G_STRING(OFS_PARM1)))
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+
+/*
+=========
+VM_getbindmap
+
+vector getbindmaps()
+=========
+*/
+void VM_getbindmaps (void)
+{
+ int fg, bg;
+ VM_SAFEPARMCOUNT(0, VM_CL_getbindmap);
+ Key_GetBindMap(&fg, &bg);
+ PRVM_G_VECTOR(OFS_RETURN)[0] = fg;
+ PRVM_G_VECTOR(OFS_RETURN)[1] = bg;
+ PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
+}
+
+/*
+=========
+VM_setbindmap
+
+float setbindmaps(vector bindmap)
+=========
+*/
+void VM_setbindmaps (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_CL_setbindmap);
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ if(PRVM_G_VECTOR(OFS_PARM0)[2] == 0)
+ if(Key_SetBindMap((int)PRVM_G_VECTOR(OFS_PARM0)[0], (int)PRVM_G_VECTOR(OFS_PARM0)[1]))
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+
+// CL_Video interface functions
+
+/*
+========================
+VM_cin_open
+
+float cin_open(string file, string name)
+========================
+*/
+void VM_cin_open( void )
+{
+ const char *file;
+ const char *name;
+
+ VM_SAFEPARMCOUNT( 2, VM_cin_open );
+
+ file = PRVM_G_STRING( OFS_PARM0 );
+ name = PRVM_G_STRING( OFS_PARM1 );
+
+ VM_CheckEmptyString( file );
+ VM_CheckEmptyString( name );
+
+ if( CL_OpenVideo( file, name, MENUOWNER, "" ) )
+ PRVM_G_FLOAT( OFS_RETURN ) = 1;
+ else
+ PRVM_G_FLOAT( OFS_RETURN ) = 0;
+}
+
+/*
+========================
+VM_cin_close
+
+void cin_close(string name)
+========================
+*/
+void VM_cin_close( void )
+{
+ const char *name;
+
+ VM_SAFEPARMCOUNT( 1, VM_cin_close );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+
+ CL_CloseVideo( CL_GetVideoByName( name ) );
+}
+
+/*
+========================
+VM_cin_setstate
+void cin_setstate(string name, float type)
+========================
+*/
+void VM_cin_setstate( void )
+{
+ const char *name;
+ clvideostate_t state;
+ clvideo_t *video;
+
+ VM_SAFEPARMCOUNT( 2, VM_cin_netstate );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+
+ state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
+
+ video = CL_GetVideoByName( name );
+ if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
+ CL_SetVideoState( video, state );
+}
+
+/*
+========================
+VM_cin_getstate
+
+float cin_getstate(string name)
+========================
+*/
+void VM_cin_getstate( void )
+{
+ const char *name;
+ clvideo_t *video;
+
+ VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+
+ video = CL_GetVideoByName( name );
+ if( video )
+ PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
+ else
+ PRVM_G_FLOAT( OFS_RETURN ) = 0;
+}
+
+/*
+========================
+VM_cin_restart
+
+void cin_restart(string name)
+========================
+*/
+void VM_cin_restart( void )
+{
+ const char *name;
+ clvideo_t *video;
+
+ VM_SAFEPARMCOUNT( 1, VM_cin_restart );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+
+ video = CL_GetVideoByName( name );
+ if( video )
+ CL_RestartVideo( video );
+}
+
+/*
+========================
+VM_Gecko_Init
+========================
+*/
+void VM_Gecko_Init( void ) {
+ // the prog struct is memset to 0 by Initprog? [12/6/2007 Black]
+ // FIXME: remove the other _Init functions then, too? [12/6/2007 Black]
+}
+
+/*
+========================
+VM_Gecko_Destroy
+========================
+*/
+void VM_Gecko_Destroy( void ) {
+ int i;
+ for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
+ clgecko_t **instance = &prog->opengeckoinstances[ i ];
+ if( *instance ) {
+ CL_Gecko_DestroyBrowser( *instance );
+ }
+ *instance = NULL;
+ }
+}
+
+/*
+========================
+VM_gecko_create
+
+float[bool] gecko_create( string name )
+========================
+*/
+void VM_gecko_create( void ) {
+ const char *name;
+ int i;
+ clgecko_t *instance;
+
+ VM_SAFEPARMCOUNT( 1, VM_gecko_create );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+
+ // find an empty slot for this gecko browser..
+ for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
+ if( prog->opengeckoinstances[ i ] == NULL ) {
+ break;
+ }
+ }
+ if( i == PRVM_MAX_GECKOINSTANCES ) {
+ VM_Warning("VM_gecko_create: %s ran out of gecko handles (%i)\n", PRVM_NAME, PRVM_MAX_GECKOINSTANCES);
+ PRVM_G_FLOAT( OFS_RETURN ) = 0;
+ return;
+ }
+
+ instance = prog->opengeckoinstances[ i ] = CL_Gecko_CreateBrowser( name, PRVM_GetProgNr() );
+ if( !instance ) {
+ // TODO: error handling [12/3/2007 Black]
+ PRVM_G_FLOAT( OFS_RETURN ) = 0;
+ return;
+ }
+ PRVM_G_FLOAT( OFS_RETURN ) = 1;
+}
+
+/*
+========================
+VM_gecko_destroy
+
+void gecko_destroy( string name )
+========================
+*/
+void VM_gecko_destroy( void ) {
+ const char *name;
+ clgecko_t *instance;
+
+ VM_SAFEPARMCOUNT( 1, VM_gecko_destroy );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+ instance = CL_Gecko_FindBrowser( name );
+ if( !instance ) {
+ return;
+ }
+ CL_Gecko_DestroyBrowser( instance );
+}
+
+/*
+========================
+VM_gecko_navigate
+
+void gecko_navigate( string name, string URI )
+========================
+*/
+void VM_gecko_navigate( void ) {
+ const char *name;
+ const char *URI;
+ clgecko_t *instance;
+
+ VM_SAFEPARMCOUNT( 2, VM_gecko_navigate );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ URI = PRVM_G_STRING( OFS_PARM1 );
+ VM_CheckEmptyString( name );
+ VM_CheckEmptyString( URI );
+
+ instance = CL_Gecko_FindBrowser( name );
+ if( !instance ) {
+ return;
+ }
+ CL_Gecko_NavigateToURI( instance, URI );
+}
+
+/*
+========================
+VM_gecko_keyevent
+
+float[bool] gecko_keyevent( string name, float key, float eventtype )
+========================
+*/
+void VM_gecko_keyevent( void ) {
+ const char *name;
+ unsigned int key;
+ clgecko_buttoneventtype_t eventtype;
+ clgecko_t *instance;
+
+ VM_SAFEPARMCOUNT( 3, VM_gecko_keyevent );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+ key = (unsigned int) PRVM_G_FLOAT( OFS_PARM1 );
+ switch( (unsigned int) PRVM_G_FLOAT( OFS_PARM2 ) ) {
+ case 0:
+ eventtype = CLG_BET_DOWN;
+ break;
+ case 1:
+ eventtype = CLG_BET_UP;
+ break;
+ case 2:
+ eventtype = CLG_BET_PRESS;
+ break;
+ case 3:
+ eventtype = CLG_BET_DOUBLECLICK;
+ break;
+ default:
+ // TODO: console printf? [12/3/2007 Black]
+ PRVM_G_FLOAT( OFS_RETURN ) = 0;
+ return;
+ }
+
+ instance = CL_Gecko_FindBrowser( name );
+ if( !instance ) {
+ PRVM_G_FLOAT( OFS_RETURN ) = 0;
+ return;
+ }
+
+ PRVM_G_FLOAT( OFS_RETURN ) = (CL_Gecko_Event_Key( instance, (keynum_t) key, eventtype ) == true);
+}
+
+/*
+========================
+VM_gecko_movemouse
+
+void gecko_mousemove( string name, float x, float y )
+========================
+*/
+void VM_gecko_movemouse( void ) {
+ const char *name;
+ float x, y;
+ clgecko_t *instance;
+
+ VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+ x = PRVM_G_FLOAT( OFS_PARM1 );
+ y = PRVM_G_FLOAT( OFS_PARM2 );
+
+ instance = CL_Gecko_FindBrowser( name );
+ if( !instance ) {
+ return;
+ }
+ CL_Gecko_Event_CursorMove( instance, x, y );
+}
+
+
+/*
+========================
+VM_gecko_resize
+
+void gecko_resize( string name, float w, float h )
+========================
+*/
+void VM_gecko_resize( void ) {
+ const char *name;
+ float w, h;
+ clgecko_t *instance;
+
+ VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+ w = PRVM_G_FLOAT( OFS_PARM1 );
+ h = PRVM_G_FLOAT( OFS_PARM2 );
+
+ instance = CL_Gecko_FindBrowser( name );
+ if( !instance ) {
+ return;
+ }
+ CL_Gecko_Resize( instance, (int) w, (int) h );
+}
+
+
+/*
+========================
+VM_gecko_get_texture_extent
+
+vector gecko_get_texture_extent( string name )
+========================
+*/
+void VM_gecko_get_texture_extent( void ) {
+ const char *name;
+ clgecko_t *instance;
+
+ VM_SAFEPARMCOUNT( 1, VM_gecko_movemouse );
+
+ name = PRVM_G_STRING( OFS_PARM0 );
+ VM_CheckEmptyString( name );
+
+ PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
+ instance = CL_Gecko_FindBrowser( name );
+ if( !instance ) {
+ PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
+ PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
+ return;
+ }
+ CL_Gecko_GetTextureExtent( instance,
+ PRVM_G_VECTOR(OFS_RETURN), PRVM_G_VECTOR(OFS_RETURN)+1 );
+}
+
+
+
+/*
+==============
+VM_makevectors
+
+Writes new values for v_forward, v_up, and v_right based on angles
+void makevectors(vector angle)
+==============
+*/
+void VM_makevectors (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_makevectors);
+ AngleVectors(PRVM_G_VECTOR(OFS_PARM0), PRVM_gameglobalvector(v_forward), PRVM_gameglobalvector(v_right), PRVM_gameglobalvector(v_up));
+}
+
+/*
+==============
+VM_vectorvectors
+
+Writes new values for v_forward, v_up, and v_right based on the given forward vector
+vectorvectors(vector)
+==============
+*/
+void VM_vectorvectors (void)
+{
+ VM_SAFEPARMCOUNT(1, VM_vectorvectors);
+ VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), PRVM_gameglobalvector(v_forward));
+ VectorVectors(PRVM_gameglobalvector(v_forward), PRVM_gameglobalvector(v_right), PRVM_gameglobalvector(v_up));
+}
+
+/*
+========================
+VM_drawline
+
+void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
+========================
+*/
+void VM_drawline (void)
+{
+ float *c1, *c2, *rgb;
+ float alpha, width;
+ unsigned char flags;
+
+ VM_SAFEPARMCOUNT(6, VM_drawline);
+ width = PRVM_G_FLOAT(OFS_PARM0);
+ c1 = PRVM_G_VECTOR(OFS_PARM1);
+ c2 = PRVM_G_VECTOR(OFS_PARM2);
+ rgb = PRVM_G_VECTOR(OFS_PARM3);
+ alpha = PRVM_G_FLOAT(OFS_PARM4);
+ flags = (int)PRVM_G_FLOAT(OFS_PARM5);
+ DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
+}
+
+// float(float number, float quantity) bitshift (EXT_BITSHIFT)
+void VM_bitshift (void)
+{
+ int n1, n2;
+ VM_SAFEPARMCOUNT(2, VM_bitshift);
+
+ n1 = (int)fabs((float)((int)PRVM_G_FLOAT(OFS_PARM0)));
+ n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
+ if(!n1)
+ PRVM_G_FLOAT(OFS_RETURN) = n1;
+ else
+ if(n2 < 0)
+ PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
+}
+
+////////////////////////////////////////
+// AltString functions
+////////////////////////////////////////
+
+/*
+========================
+VM_altstr_count
+
+float altstr_count(string)
+========================
+*/
+void VM_altstr_count( void )
+{
+ const char *altstr, *pos;
+ int count;
+
+ VM_SAFEPARMCOUNT( 1, VM_altstr_count );
+
+ altstr = PRVM_G_STRING( OFS_PARM0 );
+ //VM_CheckEmptyString( altstr );
+
+ for( count = 0, pos = altstr ; *pos ; pos++ ) {
+ if( *pos == '\\' ) {
+ if( !*++pos ) {
+ break;
+ }
+ } else if( *pos == '\'' ) {
+ count++;
+ }
+ }
+
+ PRVM_G_FLOAT( OFS_RETURN ) = (float) (count / 2);
+}
+
+/*
+========================
+VM_altstr_prepare
+
+string altstr_prepare(string)
+========================
+*/
+void VM_altstr_prepare( void )
+{
+ char *out;
+ const char *instr, *in;
+ int size;
+ char outstr[VM_STRINGTEMP_LENGTH];
+
+ VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
+
+ instr = PRVM_G_STRING( OFS_PARM0 );
+
+ for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )
+ if( *in == '\'' ) {
+ *out++ = '\\';
+ *out = '\'';
+ size--;
+ } else
+ *out = *in;
+ *out = 0;
+
+ PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
+}
+
+/*
+========================
+VM_altstr_get
+
+string altstr_get(string, float)
+========================
+*/
+void VM_altstr_get( void )
+{
+ const char *altstr, *pos;
+ char *out;
+ int count, size;
+ char outstr[VM_STRINGTEMP_LENGTH];
+
+ VM_SAFEPARMCOUNT( 2, VM_altstr_get );
+
+ altstr = PRVM_G_STRING( OFS_PARM0 );
+
+ count = (int)PRVM_G_FLOAT( OFS_PARM1 );
+ count = count * 2 + 1;
+
+ for( pos = altstr ; *pos && count ; pos++ )
+ if( *pos == '\\' ) {
+ if( !*++pos )
+ break;
+ } else if( *pos == '\'' )
+ count--;
+
+ if( !*pos ) {
+ PRVM_G_INT( OFS_RETURN ) = 0;
+ return;
+ }
+
+ for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
+ if( *pos == '\\' ) {
+ if( !*++pos )
+ break;
+ *out = *pos;
+ size--;
+ } else if( *pos == '\'' )
+ break;
+ else
+ *out = *pos;
+
+ *out = 0;
+ PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
+}
+
+/*
+========================
+VM_altstr_set
+
+string altstr_set(string altstr, float num, string set)
+========================
+*/
+void VM_altstr_set( void )
+{
+ int num;
+ const char *altstr, *str;
+ const char *in;
+ char *out;
+ char outstr[VM_STRINGTEMP_LENGTH];
+
+ VM_SAFEPARMCOUNT( 3, VM_altstr_set );
+
+ altstr = PRVM_G_STRING( OFS_PARM0 );
+
+ num = (int)PRVM_G_FLOAT( OFS_PARM1 );
+
+ str = PRVM_G_STRING( OFS_PARM2 );
+
+ out = outstr;
+ for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
+ if( *in == '\\' ) {
+ if( !*++in ) {
+ break;
+ }
+ } else if( *in == '\'' ) {
+ num--;
+ }
+
+ // copy set in
+ for( ; *str; *out++ = *str++ );
+ // now jump over the old content
+ for( ; *in ; in++ )
+ if( *in == '\'' || (*in == '\\' && !*++in) )
+ break;
+
+ strlcpy(out, in, outstr + sizeof(outstr) - out);
+ PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
+}
+
+/*
+========================
+VM_altstr_ins
+insert after num
+string altstr_ins(string altstr, float num, string set)
+========================
+*/
+void VM_altstr_ins(void)
+{
+ int num;
+ const char *set;
+ const char *in;
+ char *out;
+ char outstr[VM_STRINGTEMP_LENGTH];
+
+ VM_SAFEPARMCOUNT(3, VM_altstr_ins);
+
+ in = PRVM_G_STRING( OFS_PARM0 );
+ num = (int)PRVM_G_FLOAT( OFS_PARM1 );
+ set = PRVM_G_STRING( OFS_PARM2 );
+
+ out = outstr;
+ for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
+ if( *in == '\\' ) {
+ if( !*++in ) {
+ break;
+ }
+ } else if( *in == '\'' ) {
+ num--;
+ }
+
+ *out++ = '\'';
+ for( ; *set ; *out++ = *set++ );
+ *out++ = '\'';
+
+ strlcpy(out, in, outstr + sizeof(outstr) - out);
+ PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
+}
+
+
+////////////////////////////////////////
+// BufString functions
+////////////////////////////////////////
+//[515]: string buffers support
+
+static size_t stringbuffers_sortlength;
+
+static void BufStr_Expand(prvm_stringbuffer_t *stringbuffer, int strindex)
+{
+ if (stringbuffer->max_strings <= strindex)
+ {
+ char **oldstrings = stringbuffer->strings;
+ stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
+ while (stringbuffer->max_strings <= strindex)
+ stringbuffer->max_strings *= 2;
+ stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
+ if (stringbuffer->num_strings > 0)
+ memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
+ if (oldstrings)
+ Mem_Free(oldstrings);
+ }
+}
+
+static void BufStr_Shrink(prvm_stringbuffer_t *stringbuffer)
+{
+ // reduce num_strings if there are empty string slots at the end
+ while (stringbuffer->num_strings > 0 && stringbuffer->strings[stringbuffer->num_strings - 1] == NULL)
+ stringbuffer->num_strings--;
+
+ // if empty, free the string pointer array
+ if (stringbuffer->num_strings == 0)
+ {
+ stringbuffer->max_strings = 0;
+ if (stringbuffer->strings)
+ Mem_Free(stringbuffer->strings);
+ stringbuffer->strings = NULL;
+ }
+}
+
+static int BufStr_SortStringsUP (const void *in1, const void *in2)
+{
+ const char *a, *b;
+ a = *((const char **) in1);
+ b = *((const char **) in2);
+ if(!a || !a[0]) return 1;
+ if(!b || !b[0]) return -1;
+ return strncmp(a, b, stringbuffers_sortlength);
+}
+
+static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
+{
+ const char *a, *b;
+ a = *((const char **) in1);
+ b = *((const char **) in2);
+ if(!a || !a[0]) return 1;
+ if(!b || !b[0]) return -1;
+ return strncmp(b, a, stringbuffers_sortlength);
+}
+
+/*
+========================
+VM_buf_create
+creates new buffer, and returns it's index, returns -1 if failed
+float buf_create(void) = #460;
+float newbuf(string format, float flags) = #460;
+========================
+*/
+
+void VM_buf_create (void)
+{
+ prvm_stringbuffer_t *stringbuffer;
+ int i;
+
+ VM_SAFEPARMCOUNTRANGE(0, 2, VM_buf_create);
+
+ // VorteX: optional parm1 (buffer format) is unfinished, to keep intact with future databuffers extension must be set to "string"
+ if(prog->argc >= 1 && strcmp(PRVM_G_STRING(OFS_PARM0), "string"))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ return;
+ }
+ stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
+ for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
+ stringbuffer->origin = PRVM_AllocationOrigin();
+ // optional flags parm
+ if (prog->argc >= 2)
+ stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & 0xFF;
+ 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)
+{
+ prvm_stringbuffer_t *stringbuffer;
+ VM_SAFEPARMCOUNT(1, VM_buf_del);
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if (stringbuffer)
+ {
+ int i;
+ for (i = 0;i < stringbuffer->num_strings;i++)
+ if (stringbuffer->strings[i])
+ Mem_Free(stringbuffer->strings[i]);
+ if (stringbuffer->strings)
+ Mem_Free(stringbuffer->strings);
+ if(stringbuffer->origin)
+ PRVM_Free((char *)stringbuffer->origin);
+ Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
+ }
+ 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)
+{
+ prvm_stringbuffer_t *stringbuffer;
+ VM_SAFEPARMCOUNT(1, VM_buf_getsize);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ 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) = stringbuffer->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)
+{
+ prvm_stringbuffer_t *srcstringbuffer, *dststringbuffer;
+ int i;
+ VM_SAFEPARMCOUNT(2, VM_buf_copy);
+
+ 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)
+{
+ size_t alloclen;
+ 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;
+
+ if(PRVM_G_INT(OFS_PARM2))
+ {
+ // not the NULL string!
+ news = PRVM_G_STRING(OFS_PARM2);
+ 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;
+ }
+ if(!PRVM_G_INT(OFS_PARM1)) // NULL string
+ {
+ 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;
+ }
+ string = PRVM_G_STRING(OFS_PARM1);
+ 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_gameglobaledict(self));
+ 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;
+ }
+ current = PRVM_gameedictvector(ent, angles)[1];
+ current = ANGLEMOD(current);
+ ideal = PRVM_gameedictfloat(ent, ideal_yaw);
+ speed = PRVM_gameedictfloat(ent, yaw_speed);
+
+ 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;
+ }
+
+ current += move;
+ PRVM_gameedictvector(ent, angles)[1] = ANGLEMOD(current);
+}
+
+/*
+==============
+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;
+ }
+ current = PRVM_gameedictvector(ent, angles)[0];
+ current = ANGLEMOD(current);
+ ideal = PRVM_gameedictfloat(ent, idealpitch);
+ speed = PRVM_gameedictfloat(ent, pitch_speed);
+
+ 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;
+ }
+
+ current += move;
+ PRVM_gameedictvector(ent, angles)[0] = ANGLEMOD(current);
+}
+
+
+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;
+ firstofs = u8_bytelen(instr, firstofs);
+
+ 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) = u8_strnlen(instr, match-instr);
+}
+
+//#222 string(string s, float index) str2chr (FTE_STRINGS)
+void VM_str2chr (void)
+{
+ const char *s;
+ Uchar ch;
+ int index;
+ VM_SAFEPARMCOUNT(2, VM_str2chr);
+ s = PRVM_G_STRING(OFS_PARM0);
+ index = u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
+
+ if((unsigned)index < strlen(s))
+ {
+ if (utf8_enable.integer)
+ ch = u8_getchar_noendptr(s + index);
+ else
+ ch = (unsigned char)s[index];
+ PRVM_G_FLOAT(OFS_RETURN) = ch;
+ }
+ 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);
+ */
+ char t[9 * 4 + 1];
+ int i;
+ size_t len = 0;
+ VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
+ for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i)
+ len += u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1);
+ t[len] = 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_gameglobalfloat(trace_allsolid) = trace->allsolid;
+ PRVM_gameglobalfloat(trace_startsolid) = trace->startsolid;
+ PRVM_gameglobalfloat(trace_fraction) = trace->fraction;
+ PRVM_gameglobalfloat(trace_inwater) = trace->inwater;
+ PRVM_gameglobalfloat(trace_inopen) = trace->inopen;
+ VectorCopy(trace->endpos, PRVM_gameglobalvector(trace_endpos));
+ VectorCopy(trace->plane.normal, PRVM_gameglobalvector(trace_plane_normal));
+ PRVM_gameglobalfloat(trace_plane_dist) = trace->plane.dist;
+ PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
+ PRVM_gameglobalfloat(trace_dpstartcontents) = trace->startsupercontents;
+ PRVM_gameglobalfloat(trace_dphitcontents) = trace->hitsupercontents;
+ PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = trace->hitq3surfaceflags;
+ PRVM_gameglobalstring(trace_dphittexturename) = 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_gameglobalfloat(trace_allsolid) = 0;
+ PRVM_gameglobalfloat(trace_startsolid) = 0;
+ PRVM_gameglobalfloat(trace_fraction) = 0;
+ PRVM_gameglobalfloat(trace_inwater) = 0;
+ PRVM_gameglobalfloat(trace_inopen) = 0;
+ VectorClear(PRVM_gameglobalvector(trace_endpos));
+ VectorClear(PRVM_gameglobalvector(trace_plane_normal));
+ PRVM_gameglobalfloat(trace_plane_dist) = 0;
+ PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(prog->edicts);
+ PRVM_gameglobalfloat(trace_dpstartcontents) = 0;
+ PRVM_gameglobalfloat(trace_dphitcontents) = 0;
+ PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = 0;
+ PRVM_gameglobalstring(trace_dphittexturename) = 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 == ')'))
+ *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];
+ unsigned char *postdata; // free when uri_to_prog_t is freed
+ size_t postlen;
+ char *sigdata; // free when uri_to_prog_t is freed
+ size_t siglen;
+}
+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
+ if(handle->postdata)
+ Z_Free(handle->postdata);
+ if(handle->sigdata)
+ Z_Free(handle->sigdata);
+ Z_Free(handle);
+ return;
+ }
+
+ PRVM_SetProg(handle->prognr);
+ PRVM_Begin;
+ if((prog->starttime == handle->starttime) && (PRVM_allfunction(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(PRVM_allfunction(URI_Get_Callback), "QC function URI_Get_Callback is missing");
+ }
+ PRVM_End;
+
+ if(handle->postdata)
+ Z_Free(handle->postdata);
+ if(handle->sigdata)
+ Z_Free(handle->sigdata);
+ 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;
+ const char *posttype = NULL;
+ const char *postseparator = NULL;
+ int poststringbuffer = -1;
+ int postkeyid = -1;
+ const char *query_string = NULL;
+ size_t lq;
+
+ if(!PRVM_allfunction(URI_Get_Callback))
+ PRVM_ERROR("uri_get called by %s without URI_Get_Callback defined", PRVM_NAME);
+
+ VM_SAFEPARMCOUNTRANGE(2, 6, VM_uri_get);
+
+ url = PRVM_G_STRING(OFS_PARM0);
+ id = PRVM_G_FLOAT(OFS_PARM1);
+ if(prog->argc >= 3)
+ posttype = PRVM_G_STRING(OFS_PARM2);
+ if(prog->argc >= 4)
+ postseparator = PRVM_G_STRING(OFS_PARM3);
+ if(prog->argc >= 5)
+ poststringbuffer = PRVM_G_FLOAT(OFS_PARM4);
+ if(prog->argc >= 6)
+ postkeyid = PRVM_G_FLOAT(OFS_PARM5);
+ 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!
+
+ query_string = strchr(url, '?');
+ if(query_string)
+ ++query_string;
+ lq = query_string ? strlen(query_string) : 0;
+
+ handle->prognr = PRVM_GetProgNr();
+ handle->starttime = prog->starttime;
+ handle->id = id;
+ if(postseparator)
+ {
+ size_t l = strlen(postseparator);
+ if(poststringbuffer >= 0)
+ {
+ size_t ltotal;
+ int i;
+ // "implode"
+ prvm_stringbuffer_t *stringbuffer;
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, poststringbuffer);
+ if(!stringbuffer)
+ {
+ VM_Warning("uri_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+ ltotal = 0;
+ for(i = 0;i < stringbuffer->num_strings;i++)
+ {
+ if(i > 0)
+ ltotal += l;
+ if(stringbuffer->strings[i])
+ ltotal += strlen(stringbuffer->strings[i]);
+ }
+ handle->postdata = (unsigned char *)Z_Malloc(ltotal + 1 + lq);
+ handle->postlen = ltotal;
+ ltotal = 0;
+ for(i = 0;i < stringbuffer->num_strings;i++)
+ {
+ if(i > 0)
+ {
+ memcpy(handle->postdata + ltotal, postseparator, l);
+ ltotal += l;
+ }
+ if(stringbuffer->strings[i])
+ {
+ memcpy(handle->postdata + ltotal, stringbuffer->strings[i], strlen(stringbuffer->strings[i]));
+ ltotal += strlen(stringbuffer->strings[i]);
+ }
+ }
+ if(ltotal != handle->postlen)
+ PRVM_ERROR ("%s: string buffer content size mismatch, possible overrun", PRVM_NAME);
+ }
+ else
+ {
+ handle->postdata = (unsigned char *)Z_Malloc(l + 1 + lq);
+ handle->postlen = l;
+ memcpy(handle->postdata, postseparator, l);
+ }
+ handle->postdata[handle->postlen] = 0;
+ if(query_string)
+ memcpy(handle->postdata + handle->postlen + 1, query_string, lq);
+ if(postkeyid >= 0)
+ {
+ // POST: we sign postdata \0 query string
+ size_t ll;
+ handle->sigdata = (char *)Z_Malloc(8192);
+ strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
+ l = strlen(handle->sigdata);
+ handle->siglen = Crypto_SignDataDetached(handle->postdata, handle->postlen + 1 + lq, postkeyid, handle->sigdata + l, 8192 - l);
+ if(!handle->siglen)
+ {
+ Z_Free(handle->sigdata);
+ handle->sigdata = NULL;
+ goto out1;
+ }
+ ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
+ if(!ll)
+ {
+ Z_Free(handle->sigdata);
+ handle->sigdata = NULL;
+ goto out1;
+ }
+ handle->siglen = l + ll;
+ handle->sigdata[handle->siglen] = 0;
+ }
+out1:
+ ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+ }
+ else
+ {
+ if(postkeyid >= 0 && query_string)
+ {
+ // GET: we sign JUST the query string
+ size_t l, ll;
+ handle->sigdata = (char *)Z_Malloc(8192);
+ strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
+ l = strlen(handle->sigdata);
+ handle->siglen = Crypto_SignDataDetached(query_string, lq, postkeyid, handle->sigdata + l, 8192 - l);
+ if(!handle->siglen)
+ {
+ Z_Free(handle->sigdata);
+ handle->sigdata = NULL;
+ goto out2;
+ }
+ ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
+ if(!ll)
+ {
+ Z_Free(handle->sigdata);
+ handle->sigdata = NULL;
+ goto out2;
+ }
+ handle->siglen = l + ll;
+ handle->sigdata[handle->siglen] = 0;
+ }
+out2:
+ handle->postdata = NULL;
+ handle->postlen = 0;
+ ret = Curl_Begin_ToMemory(url, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
+ }
+ if(ret)
+ {
+ PRVM_G_INT(OFS_RETURN) = 1;
+ }
+ else
+ {
+ if(handle->postdata)
+ Z_Free(handle->postdata);
+ if(handle->sigdata)
+ Z_Free(handle->sigdata);
+ 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_SetTempString(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_SetTempString(sv_net_extresponse[first]);
+ }
+}
+
+/*
+=========
+Common functions between menu.dat and clsprogs
+=========
+*/
+
+//#349 float() isdemo
+void VM_CL_isdemo (void)
+{
+ VM_SAFEPARMCOUNT(0, VM_CL_isdemo);
+ PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
+}
+
+//#355 float() videoplaying
+void VM_CL_videoplaying (void)
+{
+ VM_SAFEPARMCOUNT(0, VM_CL_videoplaying);
+ PRVM_G_FLOAT(OFS_RETURN) = cl_videoplaying;
+}
+
+/*
+=========
+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;
+}
+
+/*
+=========
+VM_sprintf
+
+string sprintf(string format, ...)
+=========
+*/
+
+void VM_sprintf(void)
+{
+ const char *s, *s0;
+ char outbuf[MAX_INPUTLINE];
+ char *o = outbuf, *end = outbuf + sizeof(outbuf), *err;
+ int argpos = 1;
+ int width, precision, thisarg, flags;
+ char formatbuf[16];
+ char *f;
+ int isfloat;
+ static int dummyivec[3] = {0, 0, 0};
+ static float dummyvec[3] = {0, 0, 0};
+
+#define PRINTF_ALTERNATE 1
+#define PRINTF_ZEROPAD 2
+#define PRINTF_LEFT 4
+#define PRINTF_SPACEPOSITIVE 8
+#define PRINTF_SIGNPOSITIVE 16
+
+ formatbuf[0] = '%';
+
+ s = PRVM_G_STRING(OFS_PARM0);
+
+#define GETARG_FLOAT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
+#define GETARG_VECTOR(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)
+#define GETARG_INT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0)
+#define GETARG_INTVECTOR(a) (((a)>=1 && (a)<prog->argc) ? ((int*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)
+#define GETARG_STRING(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_STRING(OFS_PARM0 + 3 * (a))) : "")
+
+ for(;;)
+ {
+ s0 = s;
+ switch(*s)
+ {
+ case 0:
+ goto finished;
+ case '%':
+ ++s;
+
+ if(*s == '%')
+ goto verbatim;
+
+ // complete directive format:
+ // %3$*1$.*2$ld
+
+ width = -1;
+ precision = -1;
+ thisarg = -1;
+ flags = 0;
+ isfloat = -1;
+
+ // is number following?
+ if(*s >= '0' && *s <= '9')
+ {
+ width = strtol(s, &err, 10);
+ if(!err)
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ if(*err == '$')
+ {
+ thisarg = width;
+ width = -1;
+ s = err + 1;
+ }
+ else
+ {
+ if(*s == '0')
+ {
+ flags |= PRINTF_ZEROPAD;
+ if(width == 0)
+ width = -1; // it was just a flag
+ }
+ s = err;
+ }
+ }
+
+ if(width < 0)
+ {
+ for(;;)
+ {
+ switch(*s)
+ {
+ case '#': flags |= PRINTF_ALTERNATE; break;
+ case '0': flags |= PRINTF_ZEROPAD; break;
+ case '-': flags |= PRINTF_LEFT; break;
+ case ' ': flags |= PRINTF_SPACEPOSITIVE; break;
+ case '+': flags |= PRINTF_SIGNPOSITIVE; break;
+ default:
+ goto noflags;
+ }
+ ++s;
+ }
+noflags:
+ if(*s == '*')
+ {
+ ++s;
+ if(*s >= '0' && *s <= '9')
+ {
+ width = strtol(s, &err, 10);
+ if(!err || *err != '$')
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err + 1;
+ }
+ else
+ width = argpos++;
+ width = GETARG_FLOAT(width);
+ if(width < 0)
+ {
+ flags |= PRINTF_LEFT;
+ width = -width;
+ }
+ }
+ else if(*s >= '0' && *s <= '9')
+ {
+ width = strtol(s, &err, 10);
+ if(!err)
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err;
+ if(width < 0)
+ {
+ flags |= PRINTF_LEFT;
+ width = -width;
+ }
+ }
+ // otherwise width stays -1
+ }
+
+ if(*s == '.')
+ {
+ ++s;
+ if(*s == '*')
+ {
+ ++s;
+ if(*s >= '0' && *s <= '9')
+ {
+ precision = strtol(s, &err, 10);
+ if(!err || *err != '$')
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err + 1;
+ }
+ else
+ precision = argpos++;
+ precision = GETARG_FLOAT(precision);
+ }
+ else if(*s >= '0' && *s <= '9')
+ {
+ precision = strtol(s, &err, 10);
+ if(!err)
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err;
+ }
+ else
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ }
+
+ for(;;)
+ {
+ switch(*s)
+ {
+ case 'h': isfloat = 1; break;
+ case 'l': isfloat = 0; break;
+ case 'L': isfloat = 0; break;
+ case 'j': break;
+ case 'z': break;
+ case 't': break;
+ default:
+ goto nolength;
+ }
+ ++s;
+ }
+nolength:
+
+ // now s points to the final directive char and is no longer changed
+ if(isfloat < 0)
+ {
+ if(*s == 'i')
+ isfloat = 0;
+ else
+ isfloat = 1;
+ }
+
+ if(thisarg < 0)
+ thisarg = argpos++;
+
+ if(o < end - 1)
+ {
+ f = &formatbuf[1];
+ if(*s != 's' && *s != 'c')
+ if(flags & PRINTF_ALTERNATE) *f++ = '#';
+ if(flags & PRINTF_ZEROPAD) *f++ = '0';
+ if(flags & PRINTF_LEFT) *f++ = '-';
+ if(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';
+ if(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';
+ *f++ = '*';
+ if(precision >= 0)
+ {
+ *f++ = '.';
+ *f++ = '*';
+ }
+ *f++ = *s;
+ *f++ = 0;
+
+ if(width < 0) // not set
+ width = 0;
+
+ switch(*s)
+ {
+ case 'd': case 'i':
+ if(precision < 0) // not set
+ o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));
+ else
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));
+ break;
+ case 'o': case 'u': case 'x': case 'X':
+ if(precision < 0) // not set
+ o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+ else
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+ break;
+ case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
+ if(precision < 0) // not set
+ o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
+ else
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
+ break;
+ case 'v': case 'V':
+ f[-2] += 'g' - 'v';
+ if(precision < 0) // not set
+ o += dpsnprintf(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
+ width, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
+ width, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
+ width, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
+ );
+ else
+ o += dpsnprintf(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
+ width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
+ width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
+ width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
+ );
+ break;
+ case 'c':
+ if(flags & PRINTF_ALTERNATE)
+ {
+ if(precision < 0) // not set
+ o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+ else
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+ }
+ else
+ {
+ unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));
+ const char *buf = u8_encodech(c, NULL);
+ if(!buf)
+ buf = "";
+ if(precision < 0) // not set
+ precision = end - o - 1;
+ o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision);
+ }
+ break;
+ case 's':
+ if(flags & PRINTF_ALTERNATE)
+ {
+ if(precision < 0) // not set
+ o += dpsnprintf(o, end - o, formatbuf, width, GETARG_STRING(thisarg));
+ else
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));
+ }
+ else
+ {
+ if(precision < 0) // not set
+ precision = end - o - 1;
+ o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
+ }
+ break;
+ default:
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ }
+ ++s;
+ break;
+ default:
+verbatim:
+ if(o < end - 1)
+ *o++ = *s++;
+ break;
+ }
+ }
+finished:
+ *o = 0;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(outbuf);
+}
+
+
+// surface querying
+
+static dp_model_t *getmodel(prvm_edict_t *ed)
+{
+ switch(PRVM_GetProgNr())
+ {
+ case PRVM_SERVERPROG:
+ return SV_GetModelFromEdict(ed);
+ case PRVM_CLIENTPROG:
+ return CL_GetModelFromEdict(ed);
+ default:
+ return NULL;
+ }