]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_cmds.c
DP_QC_FINDCHAIN_TOFIELD (saves into another field than .chain)
[divverent/darkplaces.git] / prvm_cmds.c
1 // AK
2 // Basically every vm builtin cmd should be in here.
3 // All 3 builtin and extension lists can be found here
4 // cause large (I think they will) parts are from pr_cmds the same copyright like in pr_cmds
5 // also applies here
6
7 #include "quakedef.h"
8
9 #include "prvm_cmds.h"
10 #include "libcurl.h"
11 #include <time.h>
12
13 extern cvar_t prvm_backtraceforwarnings;
14
15 // LordHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value
16 void VM_Warning(const char *fmt, ...)
17 {
18         va_list argptr;
19         char msg[MAX_INPUTLINE];
20         static double recursive = -1;
21
22         va_start(argptr,fmt);
23         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
24         va_end(argptr);
25
26         Con_Print(msg);
27
28         // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black]
29         if(prvm_backtraceforwarnings.integer && recursive != realtime) // NOTE: this compares to the time, just in case if PRVM_PrintState causes a Host_Error and keeps recursive set
30         {
31                 recursive = realtime;
32                 PRVM_PrintState();
33                 recursive = -1;
34         }
35 }
36
37
38 //============================================================================
39 // Common
40
41 // TODO DONE: move vm_files and vm_fssearchlist to prvm_prog_t struct
42 // TODO: move vm_files and vm_fssearchlist back [9/13/2006 Black]
43 // TODO: (move vm_files and vm_fssearchlist to prvm_prog_t struct again) [2007-01-23 LordHavoc]
44 // TODO: will this war ever end? [2007-01-23 LordHavoc]
45
46 void VM_CheckEmptyString (const char *s)
47 {
48         if (ISWHITESPACE(s[0]))
49                 PRVM_ERROR ("%s: Bad string", PRVM_NAME);
50 }
51
52 //============================================================================
53 //BUILT-IN FUNCTIONS
54
55 void VM_VarString(int first, char *out, int outlength)
56 {
57         int i;
58         const char *s;
59         char *outend;
60
61         outend = out + outlength - 1;
62         for (i = first;i < prog->argc && out < outend;i++)
63         {
64                 s = PRVM_G_STRING((OFS_PARM0+i*3));
65                 while (out < outend && *s)
66                         *out++ = *s++;
67         }
68         *out++ = 0;
69 }
70
71 /*
72 =================
73 VM_checkextension
74
75 returns true if the extension is supported by the server
76
77 checkextension(extensionname)
78 =================
79 */
80
81 // kind of helper function
82 static qboolean checkextension(const char *name)
83 {
84         int len;
85         char *e, *start;
86         len = (int)strlen(name);
87
88         for (e = prog->extensionstring;*e;e++)
89         {
90                 while (*e == ' ')
91                         e++;
92                 if (!*e)
93                         break;
94                 start = e;
95                 while (*e && *e != ' ')
96                         e++;
97                 if ((e - start) == len && !strncasecmp(start, name, len))
98                         return true;
99         }
100         return false;
101 }
102
103 void VM_checkextension (void)
104 {
105         VM_SAFEPARMCOUNT(1,VM_checkextension);
106
107         PRVM_G_FLOAT(OFS_RETURN) = checkextension(PRVM_G_STRING(OFS_PARM0));
108 }
109
110 /*
111 =================
112 VM_error
113
114 This is a TERMINAL error, which will kill off the entire prog.
115 Dumps self.
116
117 error(value)
118 =================
119 */
120 void VM_error (void)
121 {
122         prvm_edict_t    *ed;
123         char string[VM_STRINGTEMP_LENGTH];
124
125         VM_VarString(0, string, sizeof(string));
126         Con_Printf("======%s ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
127         if (prog->globaloffsets.self >= 0)
128         {
129                 ed = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict);
130                 PRVM_ED_Print(ed, NULL);
131         }
132
133         PRVM_ERROR ("%s: Program error in function %s:\n%s\nTip: read above for entity information\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
134 }
135
136 /*
137 =================
138 VM_objerror
139
140 Dumps out self, then an error message.  The program is aborted and self is
141 removed, but the level can continue.
142
143 objerror(value)
144 =================
145 */
146 void VM_objerror (void)
147 {
148         prvm_edict_t    *ed;
149         char string[VM_STRINGTEMP_LENGTH];
150
151         VM_VarString(0, string, sizeof(string));
152         Con_Printf("======OBJECT ERROR======\n"); // , PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); // or include them? FIXME
153         if (prog->globaloffsets.self >= 0)
154         {
155                 ed = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict);
156                 PRVM_ED_Print(ed, NULL);
157
158                 PRVM_ED_Free (ed);
159         }
160         else
161                 // objerror has to display the object fields -> else call
162                 PRVM_ERROR ("VM_objecterror: self not defined !");
163         Con_Printf("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
164 }
165
166 /*
167 =================
168 VM_print
169
170 print to console
171
172 print(...[string])
173 =================
174 */
175 void VM_print (void)
176 {
177         char string[VM_STRINGTEMP_LENGTH];
178
179         VM_VarString(0, string, sizeof(string));
180         Con_Print(string);
181 }
182
183 /*
184 =================
185 VM_bprint
186
187 broadcast print to everyone on server
188
189 bprint(...[string])
190 =================
191 */
192 void VM_bprint (void)
193 {
194         char string[VM_STRINGTEMP_LENGTH];
195
196         if(!sv.active)
197         {
198                 VM_Warning("VM_bprint: game is not server(%s) !\n", PRVM_NAME);
199                 return;
200         }
201
202         VM_VarString(0, string, sizeof(string));
203         SV_BroadcastPrint(string);
204 }
205
206 /*
207 =================
208 VM_sprint (menu & client but only if server.active == true)
209
210 single print to a specific client
211
212 sprint(float clientnum,...[string])
213 =================
214 */
215 void VM_sprint (void)
216 {
217         client_t        *client;
218         int                     clientnum;
219         char string[VM_STRINGTEMP_LENGTH];
220
221         VM_SAFEPARMCOUNTRANGE(1, 8, VM_sprint);
222
223         //find client for this entity
224         clientnum = (int)PRVM_G_FLOAT(OFS_PARM0);
225         if (!sv.active  || clientnum < 0 || clientnum >= svs.maxclients || !svs.clients[clientnum].active)
226         {
227                 VM_Warning("VM_sprint: %s: invalid client or server is not active !\n", PRVM_NAME);
228                 return;
229         }
230
231         client = svs.clients + clientnum;
232         if (!client->netconnection)
233                 return;
234
235         VM_VarString(1, string, sizeof(string));
236         MSG_WriteChar(&client->netconnection->message,svc_print);
237         MSG_WriteString(&client->netconnection->message, string);
238 }
239
240 /*
241 =================
242 VM_centerprint
243
244 single print to the screen
245
246 centerprint(value)
247 =================
248 */
249 void VM_centerprint (void)
250 {
251         char string[VM_STRINGTEMP_LENGTH];
252
253         VM_SAFEPARMCOUNTRANGE(1, 8, VM_centerprint);
254         VM_VarString(0, string, sizeof(string));
255         SCR_CenterPrint(string);
256 }
257
258 /*
259 =================
260 VM_normalize
261
262 vector normalize(vector)
263 =================
264 */
265 void VM_normalize (void)
266 {
267         float   *value1;
268         vec3_t  newvalue;
269         double  f;
270
271         VM_SAFEPARMCOUNT(1,VM_normalize);
272
273         value1 = PRVM_G_VECTOR(OFS_PARM0);
274
275         f = VectorLength2(value1);
276         if (f)
277         {
278                 f = 1.0 / sqrt(f);
279                 VectorScale(value1, f, newvalue);
280         }
281         else
282                 VectorClear(newvalue);
283
284         VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN));
285 }
286
287 /*
288 =================
289 VM_vlen
290
291 scalar vlen(vector)
292 =================
293 */
294 void VM_vlen (void)
295 {
296         VM_SAFEPARMCOUNT(1,VM_vlen);
297         PRVM_G_FLOAT(OFS_RETURN) = VectorLength(PRVM_G_VECTOR(OFS_PARM0));
298 }
299
300 /*
301 =================
302 VM_vectoyaw
303
304 float vectoyaw(vector)
305 =================
306 */
307 void VM_vectoyaw (void)
308 {
309         float   *value1;
310         float   yaw;
311
312         VM_SAFEPARMCOUNT(1,VM_vectoyaw);
313
314         value1 = PRVM_G_VECTOR(OFS_PARM0);
315
316         if (value1[1] == 0 && value1[0] == 0)
317                 yaw = 0;
318         else
319         {
320                 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
321                 if (yaw < 0)
322                         yaw += 360;
323         }
324
325         PRVM_G_FLOAT(OFS_RETURN) = yaw;
326 }
327
328
329 /*
330 =================
331 VM_vectoangles
332
333 vector vectoangles(vector[, vector])
334 =================
335 */
336 void VM_vectoangles (void)
337 {
338         VM_SAFEPARMCOUNTRANGE(1, 2,VM_vectoangles);
339
340         AnglesFromVectors(PRVM_G_VECTOR(OFS_RETURN), PRVM_G_VECTOR(OFS_PARM0), prog->argc >= 2 ? PRVM_G_VECTOR(OFS_PARM1) : NULL, true);
341 }
342
343 /*
344 =================
345 VM_random
346
347 Returns a number from 0<= num < 1
348
349 float random()
350 =================
351 */
352 void VM_random (void)
353 {
354         VM_SAFEPARMCOUNT(0,VM_random);
355
356         PRVM_G_FLOAT(OFS_RETURN) = lhrandom(0, 1);
357 }
358
359 /*
360 =========
361 VM_localsound
362
363 localsound(string sample)
364 =========
365 */
366 void VM_localsound(void)
367 {
368         const char *s;
369
370         VM_SAFEPARMCOUNT(1,VM_localsound);
371
372         s = PRVM_G_STRING(OFS_PARM0);
373
374         if(!S_LocalSound (s))
375         {
376                 PRVM_G_FLOAT(OFS_RETURN) = -4;
377                 VM_Warning("VM_localsound: Failed to play %s for %s !\n", s, PRVM_NAME);
378                 return;
379         }
380
381         PRVM_G_FLOAT(OFS_RETURN) = 1;
382 }
383
384 /*
385 =================
386 VM_break
387
388 break()
389 =================
390 */
391 void VM_break (void)
392 {
393         PRVM_ERROR ("%s: break statement", PRVM_NAME);
394 }
395
396 //============================================================================
397
398 /*
399 =================
400 VM_localcmd
401
402 Sends text over to the client's execution buffer
403
404 [localcmd (string, ...) or]
405 cmd (string, ...)
406 =================
407 */
408 void VM_localcmd (void)
409 {
410         char string[VM_STRINGTEMP_LENGTH];
411         VM_SAFEPARMCOUNTRANGE(1, 8, VM_localcmd);
412         VM_VarString(0, string, sizeof(string));
413         Cbuf_AddText(string);
414 }
415
416 /*
417 =================
418 VM_cvar
419
420 float cvar (string)
421 =================
422 */
423 void VM_cvar (void)
424 {
425         char string[VM_STRINGTEMP_LENGTH];
426         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar);
427         VM_VarString(0, string, sizeof(string));
428         VM_CheckEmptyString(string);
429         PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue(string);
430 }
431
432 /*
433 =================
434 VM_cvar
435
436 float cvar_type (string)
437 float CVAR_TYPEFLAG_EXISTS = 1;
438 float CVAR_TYPEFLAG_SAVED = 2;
439 float CVAR_TYPEFLAG_PRIVATE = 4;
440 float CVAR_TYPEFLAG_ENGINE = 8;
441 float CVAR_TYPEFLAG_HASDESCRIPTION = 16;
442 float CVAR_TYPEFLAG_READONLY = 32;
443 =================
444 */
445 void VM_cvar_type (void)
446 {
447         char string[VM_STRINGTEMP_LENGTH];
448         cvar_t *cvar;
449         int ret;
450
451         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar);
452         VM_VarString(0, string, sizeof(string));
453         VM_CheckEmptyString(string);
454         cvar = Cvar_FindVar(string);
455
456
457         if(!cvar)
458         {
459                 PRVM_G_FLOAT(OFS_RETURN) = 0;
460                 return; // CVAR_TYPE_NONE
461         }
462
463         ret = 1; // CVAR_EXISTS
464         if(cvar->flags & CVAR_SAVE)
465                 ret |= 2; // CVAR_TYPE_SAVED
466         if(cvar->flags & CVAR_PRIVATE)
467                 ret |= 4; // CVAR_TYPE_PRIVATE
468         if(!(cvar->flags & CVAR_ALLOCATED))
469                 ret |= 8; // CVAR_TYPE_ENGINE
470         if(cvar->description != cvar_dummy_description)
471                 ret |= 16; // CVAR_TYPE_HASDESCRIPTION
472         if(cvar->flags & CVAR_READONLY)
473                 ret |= 32; // CVAR_TYPE_READONLY
474         
475         PRVM_G_FLOAT(OFS_RETURN) = ret;
476 }
477
478 /*
479 =================
480 VM_cvar_string
481
482 const string    VM_cvar_string (string, ...)
483 =================
484 */
485 void VM_cvar_string(void)
486 {
487         char string[VM_STRINGTEMP_LENGTH];
488         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_string);
489         VM_VarString(0, string, sizeof(string));
490         VM_CheckEmptyString(string);
491         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableString(string));
492 }
493
494
495 /*
496 ========================
497 VM_cvar_defstring
498
499 const string    VM_cvar_defstring (string, ...)
500 ========================
501 */
502 void VM_cvar_defstring (void)
503 {
504         char string[VM_STRINGTEMP_LENGTH];
505         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_defstring);
506         VM_VarString(0, string, sizeof(string));
507         VM_CheckEmptyString(string);
508         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableDefString(string));
509 }
510
511 /*
512 ========================
513 VM_cvar_defstring
514
515 const string    VM_cvar_description (string, ...)
516 ========================
517 */
518 void VM_cvar_description (void)
519 {
520         char string[VM_STRINGTEMP_LENGTH];
521         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_description);
522         VM_VarString(0, string, sizeof(string));
523         VM_CheckEmptyString(string);
524         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableDescription(string));
525 }
526 /*
527 =================
528 VM_cvar_set
529
530 void cvar_set (string,string, ...)
531 =================
532 */
533 void VM_cvar_set (void)
534 {
535         const char *name;
536         char string[VM_STRINGTEMP_LENGTH];
537         VM_SAFEPARMCOUNTRANGE(2,8,VM_cvar_set);
538         VM_VarString(1, string, sizeof(string));
539         name = PRVM_G_STRING(OFS_PARM0);
540         VM_CheckEmptyString(name);
541         Cvar_Set(name, string);
542 }
543
544 /*
545 =========
546 VM_dprint
547
548 dprint(...[string])
549 =========
550 */
551 void VM_dprint (void)
552 {
553         char string[VM_STRINGTEMP_LENGTH];
554         VM_SAFEPARMCOUNTRANGE(1, 8, VM_dprint);
555         if (developer.integer)
556         {
557                 VM_VarString(0, string, sizeof(string));
558 #if 1
559                 Con_Printf("%s", string);
560 #else
561                 Con_Printf("%s: %s", PRVM_NAME, string);
562 #endif
563         }
564 }
565
566 /*
567 =========
568 VM_ftos
569
570 string  ftos(float)
571 =========
572 */
573
574 void VM_ftos (void)
575 {
576         float v;
577         char s[128];
578
579         VM_SAFEPARMCOUNT(1, VM_ftos);
580
581         v = PRVM_G_FLOAT(OFS_PARM0);
582
583         if ((float)((int)v) == v)
584                 dpsnprintf(s, sizeof(s), "%i", (int)v);
585         else
586                 dpsnprintf(s, sizeof(s), "%f", v);
587         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
588 }
589
590 /*
591 =========
592 VM_fabs
593
594 float   fabs(float)
595 =========
596 */
597
598 void VM_fabs (void)
599 {
600         float   v;
601
602         VM_SAFEPARMCOUNT(1,VM_fabs);
603
604         v = PRVM_G_FLOAT(OFS_PARM0);
605         PRVM_G_FLOAT(OFS_RETURN) = fabs(v);
606 }
607
608 /*
609 =========
610 VM_vtos
611
612 string  vtos(vector)
613 =========
614 */
615
616 void VM_vtos (void)
617 {
618         char s[512];
619
620         VM_SAFEPARMCOUNT(1,VM_vtos);
621
622         dpsnprintf (s, sizeof(s), "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
623         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
624 }
625
626 /*
627 =========
628 VM_etos
629
630 string  etos(entity)
631 =========
632 */
633
634 void VM_etos (void)
635 {
636         char s[128];
637
638         VM_SAFEPARMCOUNT(1, VM_etos);
639
640         dpsnprintf (s, sizeof(s), "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
641         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
642 }
643
644 /*
645 =========
646 VM_stof
647
648 float stof(...[string])
649 =========
650 */
651 void VM_stof(void)
652 {
653         char string[VM_STRINGTEMP_LENGTH];
654         VM_SAFEPARMCOUNTRANGE(1, 8, VM_stof);
655         VM_VarString(0, string, sizeof(string));
656         PRVM_G_FLOAT(OFS_RETURN) = atof(string);
657 }
658
659 /*
660 ========================
661 VM_itof
662
663 float itof(intt ent)
664 ========================
665 */
666 void VM_itof(void)
667 {
668         VM_SAFEPARMCOUNT(1, VM_itof);
669         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
670 }
671
672 /*
673 ========================
674 VM_ftoe
675
676 entity ftoe(float num)
677 ========================
678 */
679 void VM_ftoe(void)
680 {
681         int ent;
682         VM_SAFEPARMCOUNT(1, VM_ftoe);
683
684         ent = (int)PRVM_G_FLOAT(OFS_PARM0);
685         if (ent < 0 || ent >= MAX_EDICTS || PRVM_PROG_TO_EDICT(ent)->priv.required->free)
686                 ent = 0; // return world instead of a free or invalid entity
687
688         PRVM_G_INT(OFS_RETURN) = ent;
689 }
690
691 /*
692 ========================
693 VM_etof
694
695 float etof(entity ent)
696 ========================
697 */
698 void VM_etof(void)
699 {
700         VM_SAFEPARMCOUNT(1, VM_etof);
701         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICTNUM(OFS_PARM0);
702 }
703
704 /*
705 =========
706 VM_strftime
707
708 string strftime(float uselocaltime, string[, string ...])
709 =========
710 */
711 void VM_strftime(void)
712 {
713         time_t t;
714 #if _MSC_VER >= 1400
715         struct tm tm;
716         int tmresult;
717 #else
718         struct tm *tm;
719 #endif
720         char fmt[VM_STRINGTEMP_LENGTH];
721         char result[VM_STRINGTEMP_LENGTH];
722         VM_SAFEPARMCOUNTRANGE(2, 8, VM_strftime);
723         VM_VarString(1, fmt, sizeof(fmt));
724         t = time(NULL);
725 #if _MSC_VER >= 1400
726         if (PRVM_G_FLOAT(OFS_PARM0))
727                 tmresult = localtime_s(&tm, &t);
728         else
729                 tmresult = gmtime_s(&tm, &t);
730         if (!tmresult)
731 #else
732         if (PRVM_G_FLOAT(OFS_PARM0))
733                 tm = localtime(&t);
734         else
735                 tm = gmtime(&t);
736         if (!tm)
737 #endif
738         {
739                 PRVM_G_INT(OFS_RETURN) = 0;
740                 return;
741         }
742 #if _MSC_VER >= 1400
743         strftime(result, sizeof(result), fmt, &tm);
744 #else
745         strftime(result, sizeof(result), fmt, tm);
746 #endif
747         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(result);
748 }
749
750 /*
751 =========
752 VM_spawn
753
754 entity spawn()
755 =========
756 */
757
758 void VM_spawn (void)
759 {
760         prvm_edict_t    *ed;
761         VM_SAFEPARMCOUNT(0, VM_spawn);
762         prog->xfunction->builtinsprofile += 20;
763         ed = PRVM_ED_Alloc();
764         VM_RETURN_EDICT(ed);
765 }
766
767 /*
768 =========
769 VM_remove
770
771 remove(entity e)
772 =========
773 */
774
775 void VM_remove (void)
776 {
777         prvm_edict_t    *ed;
778         prog->xfunction->builtinsprofile += 20;
779
780         VM_SAFEPARMCOUNT(1, VM_remove);
781
782         ed = PRVM_G_EDICT(OFS_PARM0);
783         if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
784         {
785                 if (developer.integer >= 1)
786                         VM_Warning( "VM_remove: tried to remove the null entity or a reserved entity!\n" );
787         }
788         else if( ed->priv.required->free )
789         {
790                 if (developer.integer >= 1)
791                         VM_Warning( "VM_remove: tried to remove an already freed entity!\n" );
792         }
793         else
794                 PRVM_ED_Free (ed);
795 }
796
797 /*
798 =========
799 VM_find
800
801 entity  find(entity start, .string field, string match)
802 =========
803 */
804
805 void VM_find (void)
806 {
807         int             e;
808         int             f;
809         const char      *s, *t;
810         prvm_edict_t    *ed;
811
812         VM_SAFEPARMCOUNT(3,VM_find);
813
814         e = PRVM_G_EDICTNUM(OFS_PARM0);
815         f = PRVM_G_INT(OFS_PARM1);
816         s = PRVM_G_STRING(OFS_PARM2);
817
818         // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
819         // expects it to find all the monsters, so we must be careful to support
820         // searching for ""
821
822         for (e++ ; e < prog->num_edicts ; e++)
823         {
824                 prog->xfunction->builtinsprofile++;
825                 ed = PRVM_EDICT_NUM(e);
826                 if (ed->priv.required->free)
827                         continue;
828                 t = PRVM_E_STRING(ed,f);
829                 if (!t)
830                         t = "";
831                 if (!strcmp(t,s))
832                 {
833                         VM_RETURN_EDICT(ed);
834                         return;
835                 }
836         }
837
838         VM_RETURN_EDICT(prog->edicts);
839 }
840
841 /*
842 =========
843 VM_findfloat
844
845   entity        findfloat(entity start, .float field, float match)
846   entity        findentity(entity start, .entity field, entity match)
847 =========
848 */
849 // LordHavoc: added this for searching float, int, and entity reference fields
850 void VM_findfloat (void)
851 {
852         int             e;
853         int             f;
854         float   s;
855         prvm_edict_t    *ed;
856
857         VM_SAFEPARMCOUNT(3,VM_findfloat);
858
859         e = PRVM_G_EDICTNUM(OFS_PARM0);
860         f = PRVM_G_INT(OFS_PARM1);
861         s = PRVM_G_FLOAT(OFS_PARM2);
862
863         for (e++ ; e < prog->num_edicts ; e++)
864         {
865                 prog->xfunction->builtinsprofile++;
866                 ed = PRVM_EDICT_NUM(e);
867                 if (ed->priv.required->free)
868                         continue;
869                 if (PRVM_E_FLOAT(ed,f) == s)
870                 {
871                         VM_RETURN_EDICT(ed);
872                         return;
873                 }
874         }
875
876         VM_RETURN_EDICT(prog->edicts);
877 }
878
879 /*
880 =========
881 VM_findchain
882
883 entity  findchain(.string field, string match)
884 =========
885 */
886 // chained search for strings in entity fields
887 // entity(.string field, string match) findchain = #402;
888 void VM_findchain (void)
889 {
890         int             i;
891         int             f;
892         const char      *s, *t;
893         prvm_edict_t    *ent, *chain;
894         int chainfield;
895
896         VM_SAFEPARMCOUNTRANGE(2,3,VM_findchain);
897
898         if(prog->argc == 3)
899                 chainfield = PRVM_G_INT(OFS_PARM2);
900         else
901                 chainfield = prog->fieldoffsets.chain;
902         if (chainfield < 0)
903                 PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
904
905         chain = prog->edicts;
906
907         f = PRVM_G_INT(OFS_PARM0);
908         s = PRVM_G_STRING(OFS_PARM1);
909
910         // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
911         // expects it to find all the monsters, so we must be careful to support
912         // searching for ""
913
914         ent = PRVM_NEXT_EDICT(prog->edicts);
915         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
916         {
917                 prog->xfunction->builtinsprofile++;
918                 if (ent->priv.required->free)
919                         continue;
920                 t = PRVM_E_STRING(ent,f);
921                 if (!t)
922                         t = "";
923                 if (strcmp(t,s))
924                         continue;
925
926                 PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_NUM_FOR_EDICT(chain);
927                 chain = ent;
928         }
929
930         VM_RETURN_EDICT(chain);
931 }
932
933 /*
934 =========
935 VM_findchainfloat
936
937 entity  findchainfloat(.string field, float match)
938 entity  findchainentity(.string field, entity match)
939 =========
940 */
941 // LordHavoc: chained search for float, int, and entity reference fields
942 // entity(.string field, float match) findchainfloat = #403;
943 void VM_findchainfloat (void)
944 {
945         int             i;
946         int             f;
947         float   s;
948         prvm_edict_t    *ent, *chain;
949         int chainfield;
950
951         VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainfloat);
952
953         if(prog->argc == 3)
954                 chainfield = PRVM_G_INT(OFS_PARM2);
955         else
956                 chainfield = prog->fieldoffsets.chain;
957         if (chainfield < 0)
958                 PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
959
960         chain = (prvm_edict_t *)prog->edicts;
961
962         f = PRVM_G_INT(OFS_PARM0);
963         s = PRVM_G_FLOAT(OFS_PARM1);
964
965         ent = PRVM_NEXT_EDICT(prog->edicts);
966         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
967         {
968                 prog->xfunction->builtinsprofile++;
969                 if (ent->priv.required->free)
970                         continue;
971                 if (PRVM_E_FLOAT(ent,f) != s)
972                         continue;
973
974                 PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_EDICT_TO_PROG(chain);
975                 chain = ent;
976         }
977
978         VM_RETURN_EDICT(chain);
979 }
980
981 /*
982 ========================
983 VM_findflags
984
985 entity  findflags(entity start, .float field, float match)
986 ========================
987 */
988 // LordHavoc: search for flags in float fields
989 void VM_findflags (void)
990 {
991         int             e;
992         int             f;
993         int             s;
994         prvm_edict_t    *ed;
995
996         VM_SAFEPARMCOUNT(3, VM_findflags);
997
998
999         e = PRVM_G_EDICTNUM(OFS_PARM0);
1000         f = PRVM_G_INT(OFS_PARM1);
1001         s = (int)PRVM_G_FLOAT(OFS_PARM2);
1002
1003         for (e++ ; e < prog->num_edicts ; e++)
1004         {
1005                 prog->xfunction->builtinsprofile++;
1006                 ed = PRVM_EDICT_NUM(e);
1007                 if (ed->priv.required->free)
1008                         continue;
1009                 if (!PRVM_E_FLOAT(ed,f))
1010                         continue;
1011                 if ((int)PRVM_E_FLOAT(ed,f) & s)
1012                 {
1013                         VM_RETURN_EDICT(ed);
1014                         return;
1015                 }
1016         }
1017
1018         VM_RETURN_EDICT(prog->edicts);
1019 }
1020
1021 /*
1022 ========================
1023 VM_findchainflags
1024
1025 entity  findchainflags(.float field, float match)
1026 ========================
1027 */
1028 // LordHavoc: chained search for flags in float fields
1029 void VM_findchainflags (void)
1030 {
1031         int             i;
1032         int             f;
1033         int             s;
1034         prvm_edict_t    *ent, *chain;
1035         int chainfield;
1036
1037         VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainflags);
1038
1039         if(prog->argc == 3)
1040                 chainfield = PRVM_G_INT(OFS_PARM2);
1041         else
1042                 chainfield = prog->fieldoffsets.chain;
1043         if (chainfield < 0)
1044                 PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
1045
1046         chain = (prvm_edict_t *)prog->edicts;
1047
1048         f = PRVM_G_INT(OFS_PARM0);
1049         s = (int)PRVM_G_FLOAT(OFS_PARM1);
1050
1051         ent = PRVM_NEXT_EDICT(prog->edicts);
1052         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1053         {
1054                 prog->xfunction->builtinsprofile++;
1055                 if (ent->priv.required->free)
1056                         continue;
1057                 if (!PRVM_E_FLOAT(ent,f))
1058                         continue;
1059                 if (!((int)PRVM_E_FLOAT(ent,f) & s))
1060                         continue;
1061
1062                 PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_EDICT_TO_PROG(chain);
1063                 chain = ent;
1064         }
1065
1066         VM_RETURN_EDICT(chain);
1067 }
1068
1069 /*
1070 =========
1071 VM_precache_sound
1072
1073 string  precache_sound (string sample)
1074 =========
1075 */
1076 void VM_precache_sound (void)
1077 {
1078         const char *s;
1079
1080         VM_SAFEPARMCOUNT(1, VM_precache_sound);
1081
1082         s = PRVM_G_STRING(OFS_PARM0);
1083         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1084         VM_CheckEmptyString(s);
1085
1086         if(snd_initialized.integer && !S_PrecacheSound(s, true, false))
1087         {
1088                 VM_Warning("VM_precache_sound: Failed to load %s for %s\n", s, PRVM_NAME);
1089                 return;
1090         }
1091 }
1092
1093 /*
1094 =================
1095 VM_precache_file
1096
1097 returns the same string as output
1098
1099 does nothing, only used by qcc to build .pak archives
1100 =================
1101 */
1102 void VM_precache_file (void)
1103 {
1104         VM_SAFEPARMCOUNT(1,VM_precache_file);
1105         // precache_file is only used to copy files with qcc, it does nothing
1106         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1107 }
1108
1109 /*
1110 =========
1111 VM_coredump
1112
1113 coredump()
1114 =========
1115 */
1116 void VM_coredump (void)
1117 {
1118         VM_SAFEPARMCOUNT(0,VM_coredump);
1119
1120         Cbuf_AddText("prvm_edicts ");
1121         Cbuf_AddText(PRVM_NAME);
1122         Cbuf_AddText("\n");
1123 }
1124
1125 /*
1126 =========
1127 VM_stackdump
1128
1129 stackdump()
1130 =========
1131 */
1132 void PRVM_StackTrace(void);
1133 void VM_stackdump (void)
1134 {
1135         VM_SAFEPARMCOUNT(0, VM_stackdump);
1136
1137         PRVM_StackTrace();
1138 }
1139
1140 /*
1141 =========
1142 VM_crash
1143
1144 crash()
1145 =========
1146 */
1147
1148 void VM_crash(void)
1149 {
1150         VM_SAFEPARMCOUNT(0, VM_crash);
1151
1152         PRVM_ERROR("Crash called by %s",PRVM_NAME);
1153 }
1154
1155 /*
1156 =========
1157 VM_traceon
1158
1159 traceon()
1160 =========
1161 */
1162 void VM_traceon (void)
1163 {
1164         VM_SAFEPARMCOUNT(0,VM_traceon);
1165
1166         prog->trace = true;
1167 }
1168
1169 /*
1170 =========
1171 VM_traceoff
1172
1173 traceoff()
1174 =========
1175 */
1176 void VM_traceoff (void)
1177 {
1178         VM_SAFEPARMCOUNT(0,VM_traceoff);
1179
1180         prog->trace = false;
1181 }
1182
1183 /*
1184 =========
1185 VM_eprint
1186
1187 eprint(entity e)
1188 =========
1189 */
1190 void VM_eprint (void)
1191 {
1192         VM_SAFEPARMCOUNT(1,VM_eprint);
1193
1194         PRVM_ED_PrintNum (PRVM_G_EDICTNUM(OFS_PARM0), NULL);
1195 }
1196
1197 /*
1198 =========
1199 VM_rint
1200
1201 float   rint(float)
1202 =========
1203 */
1204 void VM_rint (void)
1205 {
1206         float f;
1207         VM_SAFEPARMCOUNT(1,VM_rint);
1208
1209         f = PRVM_G_FLOAT(OFS_PARM0);
1210         if (f > 0)
1211                 PRVM_G_FLOAT(OFS_RETURN) = floor(f + 0.5);
1212         else
1213                 PRVM_G_FLOAT(OFS_RETURN) = ceil(f - 0.5);
1214 }
1215
1216 /*
1217 =========
1218 VM_floor
1219
1220 float   floor(float)
1221 =========
1222 */
1223 void VM_floor (void)
1224 {
1225         VM_SAFEPARMCOUNT(1,VM_floor);
1226
1227         PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
1228 }
1229
1230 /*
1231 =========
1232 VM_ceil
1233
1234 float   ceil(float)
1235 =========
1236 */
1237 void VM_ceil (void)
1238 {
1239         VM_SAFEPARMCOUNT(1,VM_ceil);
1240
1241         PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
1242 }
1243
1244
1245 /*
1246 =============
1247 VM_nextent
1248
1249 entity  nextent(entity)
1250 =============
1251 */
1252 void VM_nextent (void)
1253 {
1254         int             i;
1255         prvm_edict_t    *ent;
1256
1257         VM_SAFEPARMCOUNT(1, VM_nextent);
1258
1259         i = PRVM_G_EDICTNUM(OFS_PARM0);
1260         while (1)
1261         {
1262                 prog->xfunction->builtinsprofile++;
1263                 i++;
1264                 if (i == prog->num_edicts)
1265                 {
1266                         VM_RETURN_EDICT(prog->edicts);
1267                         return;
1268                 }
1269                 ent = PRVM_EDICT_NUM(i);
1270                 if (!ent->priv.required->free)
1271                 {
1272                         VM_RETURN_EDICT(ent);
1273                         return;
1274                 }
1275         }
1276 }
1277
1278 //=============================================================================
1279
1280 /*
1281 ==============
1282 VM_changelevel
1283 server and menu
1284
1285 changelevel(string map)
1286 ==============
1287 */
1288 void VM_changelevel (void)
1289 {
1290         VM_SAFEPARMCOUNT(1, VM_changelevel);
1291
1292         if(!sv.active)
1293         {
1294                 VM_Warning("VM_changelevel: game is not server (%s)\n", PRVM_NAME);
1295                 return;
1296         }
1297
1298 // make sure we don't issue two changelevels
1299         if (svs.changelevel_issued)
1300                 return;
1301         svs.changelevel_issued = true;
1302
1303         Cbuf_AddText (va("changelevel %s\n",PRVM_G_STRING(OFS_PARM0)));
1304 }
1305
1306 /*
1307 =========
1308 VM_sin
1309
1310 float   sin(float)
1311 =========
1312 */
1313 void VM_sin (void)
1314 {
1315         VM_SAFEPARMCOUNT(1,VM_sin);
1316         PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
1317 }
1318
1319 /*
1320 =========
1321 VM_cos
1322 float   cos(float)
1323 =========
1324 */
1325 void VM_cos (void)
1326 {
1327         VM_SAFEPARMCOUNT(1,VM_cos);
1328         PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
1329 }
1330
1331 /*
1332 =========
1333 VM_sqrt
1334
1335 float   sqrt(float)
1336 =========
1337 */
1338 void VM_sqrt (void)
1339 {
1340         VM_SAFEPARMCOUNT(1,VM_sqrt);
1341         PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
1342 }
1343
1344 /*
1345 =========
1346 VM_asin
1347
1348 float   asin(float)
1349 =========
1350 */
1351 void VM_asin (void)
1352 {
1353         VM_SAFEPARMCOUNT(1,VM_asin);
1354         PRVM_G_FLOAT(OFS_RETURN) = asin(PRVM_G_FLOAT(OFS_PARM0));
1355 }
1356
1357 /*
1358 =========
1359 VM_acos
1360 float   acos(float)
1361 =========
1362 */
1363 void VM_acos (void)
1364 {
1365         VM_SAFEPARMCOUNT(1,VM_acos);
1366         PRVM_G_FLOAT(OFS_RETURN) = acos(PRVM_G_FLOAT(OFS_PARM0));
1367 }
1368
1369 /*
1370 =========
1371 VM_atan
1372 float   atan(float)
1373 =========
1374 */
1375 void VM_atan (void)
1376 {
1377         VM_SAFEPARMCOUNT(1,VM_atan);
1378         PRVM_G_FLOAT(OFS_RETURN) = atan(PRVM_G_FLOAT(OFS_PARM0));
1379 }
1380
1381 /*
1382 =========
1383 VM_atan2
1384 float   atan2(float,float)
1385 =========
1386 */
1387 void VM_atan2 (void)
1388 {
1389         VM_SAFEPARMCOUNT(2,VM_atan2);
1390         PRVM_G_FLOAT(OFS_RETURN) = atan2(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1391 }
1392
1393 /*
1394 =========
1395 VM_tan
1396 float   tan(float)
1397 =========
1398 */
1399 void VM_tan (void)
1400 {
1401         VM_SAFEPARMCOUNT(1,VM_tan);
1402         PRVM_G_FLOAT(OFS_RETURN) = tan(PRVM_G_FLOAT(OFS_PARM0));
1403 }
1404
1405 /*
1406 =================
1407 VM_randomvec
1408
1409 Returns a vector of length < 1 and > 0
1410
1411 vector randomvec()
1412 =================
1413 */
1414 void VM_randomvec (void)
1415 {
1416         vec3_t          temp;
1417         //float         length;
1418
1419         VM_SAFEPARMCOUNT(0, VM_randomvec);
1420
1421         //// WTF ??
1422         do
1423         {
1424                 temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1425                 temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1426                 temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1427         }
1428         while (DotProduct(temp, temp) >= 1);
1429         VectorCopy (temp, PRVM_G_VECTOR(OFS_RETURN));
1430
1431         /*
1432         temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1433         temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1434         temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1435         // length returned always > 0
1436         length = (rand()&32766 + 1) * (1.0 / 32767.0) / VectorLength(temp);
1437         VectorScale(temp,length, temp);*/
1438         //VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN));
1439 }
1440
1441 //=============================================================================
1442
1443 /*
1444 =========
1445 VM_registercvar
1446
1447 float   registercvar (string name, string value[, float flags])
1448 =========
1449 */
1450 void VM_registercvar (void)
1451 {
1452         const char *name, *value;
1453         int     flags;
1454
1455         VM_SAFEPARMCOUNTRANGE(2, 3, VM_registercvar);
1456
1457         name = PRVM_G_STRING(OFS_PARM0);
1458         value = PRVM_G_STRING(OFS_PARM1);
1459         flags = prog->argc >= 3 ? (int)PRVM_G_FLOAT(OFS_PARM2) : 0;
1460         PRVM_G_FLOAT(OFS_RETURN) = 0;
1461
1462         if(flags > CVAR_MAXFLAGSVAL)
1463                 return;
1464
1465 // first check to see if it has already been defined
1466         if (Cvar_FindVar (name))
1467                 return;
1468
1469 // check for overlap with a command
1470         if (Cmd_Exists (name))
1471         {
1472                 VM_Warning("VM_registercvar: %s is a command\n", name);
1473                 return;
1474         }
1475
1476         Cvar_Get(name, value, flags, NULL);
1477
1478         PRVM_G_FLOAT(OFS_RETURN) = 1; // success
1479 }
1480
1481
1482 /*
1483 =================
1484 VM_min
1485
1486 returns the minimum of two supplied floats
1487
1488 float min(float a, float b, ...[float])
1489 =================
1490 */
1491 void VM_min (void)
1492 {
1493         VM_SAFEPARMCOUNTRANGE(2, 8, VM_min);
1494         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1495         if (prog->argc >= 3)
1496         {
1497                 int i;
1498                 float f = PRVM_G_FLOAT(OFS_PARM0);
1499                 for (i = 1;i < prog->argc;i++)
1500                         if (f > PRVM_G_FLOAT((OFS_PARM0+i*3)))
1501                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1502                 PRVM_G_FLOAT(OFS_RETURN) = f;
1503         }
1504         else
1505                 PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1506 }
1507
1508 /*
1509 =================
1510 VM_max
1511
1512 returns the maximum of two supplied floats
1513
1514 float   max(float a, float b, ...[float])
1515 =================
1516 */
1517 void VM_max (void)
1518 {
1519         VM_SAFEPARMCOUNTRANGE(2, 8, VM_max);
1520         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1521         if (prog->argc >= 3)
1522         {
1523                 int i;
1524                 float f = PRVM_G_FLOAT(OFS_PARM0);
1525                 for (i = 1;i < prog->argc;i++)
1526                         if (f < PRVM_G_FLOAT((OFS_PARM0+i*3)))
1527                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1528                 PRVM_G_FLOAT(OFS_RETURN) = f;
1529         }
1530         else
1531                 PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1532 }
1533
1534 /*
1535 =================
1536 VM_bound
1537
1538 returns number bounded by supplied range
1539
1540 float   bound(float min, float value, float max)
1541 =================
1542 */
1543 void VM_bound (void)
1544 {
1545         VM_SAFEPARMCOUNT(3,VM_bound);
1546         PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
1547 }
1548
1549 /*
1550 =================
1551 VM_pow
1552
1553 returns a raised to power b
1554
1555 float   pow(float a, float b)
1556 =================
1557 */
1558 void VM_pow (void)
1559 {
1560         VM_SAFEPARMCOUNT(2,VM_pow);
1561         PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1562 }
1563
1564 void VM_Files_Init(void)
1565 {
1566         int i;
1567         for (i = 0;i < PRVM_MAX_OPENFILES;i++)
1568                 prog->openfiles[i] = NULL;
1569 }
1570
1571 void VM_Files_CloseAll(void)
1572 {
1573         int i;
1574         for (i = 0;i < PRVM_MAX_OPENFILES;i++)
1575         {
1576                 if (prog->openfiles[i])
1577                         FS_Close(prog->openfiles[i]);
1578                 prog->openfiles[i] = NULL;
1579         }
1580 }
1581
1582 static qfile_t *VM_GetFileHandle( int index )
1583 {
1584         if (index < 0 || index >= PRVM_MAX_OPENFILES)
1585         {
1586                 Con_Printf("VM_GetFileHandle: invalid file handle %i used in %s\n", index, PRVM_NAME);
1587                 return NULL;
1588         }
1589         if (prog->openfiles[index] == NULL)
1590         {
1591                 Con_Printf("VM_GetFileHandle: no such file handle %i (or file has been closed) in %s\n", index, PRVM_NAME);
1592                 return NULL;
1593         }
1594         return prog->openfiles[index];
1595 }
1596
1597 /*
1598 =========
1599 VM_fopen
1600
1601 float   fopen(string filename, float mode)
1602 =========
1603 */
1604 // float(string filename, float mode) fopen = #110;
1605 // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE),
1606 // returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
1607 void VM_fopen(void)
1608 {
1609         int filenum, mode;
1610         const char *modestring, *filename;
1611
1612         VM_SAFEPARMCOUNT(2,VM_fopen);
1613
1614         for (filenum = 0;filenum < PRVM_MAX_OPENFILES;filenum++)
1615                 if (prog->openfiles[filenum] == NULL)
1616                         break;
1617         if (filenum >= PRVM_MAX_OPENFILES)
1618         {
1619                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1620                 VM_Warning("VM_fopen: %s ran out of file handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENFILES);
1621                 return;
1622         }
1623         filename = PRVM_G_STRING(OFS_PARM0);
1624         mode = (int)PRVM_G_FLOAT(OFS_PARM1);
1625         switch(mode)
1626         {
1627         case 0: // FILE_READ
1628                 modestring = "rb";
1629                 prog->openfiles[filenum] = FS_OpenVirtualFile(va("data/%s", filename), false);
1630                 if (prog->openfiles[filenum] == NULL)
1631                         prog->openfiles[filenum] = FS_OpenVirtualFile(va("%s", filename), false);
1632                 break;
1633         case 1: // FILE_APPEND
1634                 modestring = "a";
1635                 prog->openfiles[filenum] = FS_OpenRealFile(va("data/%s", filename), modestring, false);
1636                 break;
1637         case 2: // FILE_WRITE
1638                 modestring = "w";
1639                 prog->openfiles[filenum] = FS_OpenRealFile(va("data/%s", filename), modestring, false);
1640                 break;
1641         default:
1642                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1643                 VM_Warning("VM_fopen: %s: no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", PRVM_NAME, mode);
1644                 return;
1645         }
1646
1647         if (prog->openfiles[filenum] == NULL)
1648         {
1649                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1650                 if (developer.integer >= 100)
1651                         VM_Warning("VM_fopen: %s: %s mode %s failed\n", PRVM_NAME, filename, modestring);
1652         }
1653         else
1654         {
1655                 PRVM_G_FLOAT(OFS_RETURN) = filenum;
1656                 if (developer.integer >= 100)
1657                         Con_Printf("VM_fopen: %s: %s mode %s opened as #%i\n", PRVM_NAME, filename, modestring, filenum);
1658                 prog->openfiles_origin[filenum] = PRVM_AllocationOrigin();
1659         }
1660 }
1661
1662 /*
1663 =========
1664 VM_fclose
1665
1666 fclose(float fhandle)
1667 =========
1668 */
1669 //void(float fhandle) fclose = #111; // closes a file
1670 void VM_fclose(void)
1671 {
1672         int filenum;
1673
1674         VM_SAFEPARMCOUNT(1,VM_fclose);
1675
1676         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1677         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1678         {
1679                 VM_Warning("VM_fclose: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1680                 return;
1681         }
1682         if (prog->openfiles[filenum] == NULL)
1683         {
1684                 VM_Warning("VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1685                 return;
1686         }
1687         FS_Close(prog->openfiles[filenum]);
1688         prog->openfiles[filenum] = NULL;
1689         if(prog->openfiles_origin[filenum])
1690                 PRVM_Free((char *)prog->openfiles_origin[filenum]);
1691         if (developer.integer >= 100)
1692                 Con_Printf("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum);
1693 }
1694
1695 /*
1696 =========
1697 VM_fgets
1698
1699 string  fgets(float fhandle)
1700 =========
1701 */
1702 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
1703 void VM_fgets(void)
1704 {
1705         int c, end;
1706         char string[VM_STRINGTEMP_LENGTH];
1707         int filenum;
1708
1709         VM_SAFEPARMCOUNT(1,VM_fgets);
1710
1711         // set the return value regardless of any possible errors
1712         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1713
1714         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1715         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1716         {
1717                 VM_Warning("VM_fgets: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1718                 return;
1719         }
1720         if (prog->openfiles[filenum] == NULL)
1721         {
1722                 VM_Warning("VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1723                 return;
1724         }
1725         end = 0;
1726         for (;;)
1727         {
1728                 c = FS_Getc(prog->openfiles[filenum]);
1729                 if (c == '\r' || c == '\n' || c < 0)
1730                         break;
1731                 if (end < VM_STRINGTEMP_LENGTH - 1)
1732                         string[end++] = c;
1733         }
1734         string[end] = 0;
1735         // remove \n following \r
1736         if (c == '\r')
1737         {
1738                 c = FS_Getc(prog->openfiles[filenum]);
1739                 if (c != '\n')
1740                         FS_UnGetc(prog->openfiles[filenum], (unsigned char)c);
1741         }
1742         if (developer.integer >= 100)
1743                 Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
1744         if (c >= 0 || end)
1745                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
1746 }
1747
1748 /*
1749 =========
1750 VM_fputs
1751
1752 fputs(float fhandle, string s)
1753 =========
1754 */
1755 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
1756 void VM_fputs(void)
1757 {
1758         int stringlength;
1759         char string[VM_STRINGTEMP_LENGTH];
1760         int filenum;
1761
1762         VM_SAFEPARMCOUNT(2,VM_fputs);
1763
1764         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1765         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1766         {
1767                 VM_Warning("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1768                 return;
1769         }
1770         if (prog->openfiles[filenum] == NULL)
1771         {
1772                 VM_Warning("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1773                 return;
1774         }
1775         VM_VarString(1, string, sizeof(string));
1776         if ((stringlength = (int)strlen(string)))
1777                 FS_Write(prog->openfiles[filenum], string, stringlength);
1778         if (developer.integer >= 100)
1779                 Con_Printf("fputs: %s: %s\n", PRVM_NAME, string);
1780 }
1781
1782 /*
1783 =========
1784 VM_writetofile
1785
1786         writetofile(float fhandle, entity ent)
1787 =========
1788 */
1789 void VM_writetofile(void)
1790 {
1791         prvm_edict_t * ent;
1792         qfile_t *file;
1793
1794         VM_SAFEPARMCOUNT(2, VM_writetofile);
1795
1796         file = VM_GetFileHandle( (int)PRVM_G_FLOAT(OFS_PARM0) );
1797         if( !file )
1798         {
1799                 VM_Warning("VM_writetofile: invalid or closed file handle\n");
1800                 return;
1801         }
1802
1803         ent = PRVM_G_EDICT(OFS_PARM1);
1804         if(ent->priv.required->free)
1805         {
1806                 VM_Warning("VM_writetofile: %s: entity %i is free !\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
1807                 return;
1808         }
1809
1810         PRVM_ED_Write (file, ent);
1811 }
1812
1813 // KrimZon - DP_QC_ENTITYDATA
1814 /*
1815 =========
1816 VM_numentityfields
1817
1818 float() numentityfields
1819 Return the number of entity fields - NOT offsets
1820 =========
1821 */
1822 void VM_numentityfields(void)
1823 {
1824         PRVM_G_FLOAT(OFS_RETURN) = prog->progs->numfielddefs;
1825 }
1826
1827 // KrimZon - DP_QC_ENTITYDATA
1828 /*
1829 =========
1830 VM_entityfieldname
1831
1832 string(float fieldnum) entityfieldname
1833 Return name of the specified field as a string, or empty if the field is invalid (warning)
1834 =========
1835 */
1836 void VM_entityfieldname(void)
1837 {
1838         ddef_t *d;
1839         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1840         
1841         if (i < 0 || i >= prog->progs->numfielddefs)
1842         {
1843         VM_Warning("VM_entityfieldname: %s: field index out of bounds\n", PRVM_NAME);
1844         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1845                 return;
1846         }
1847         
1848         d = &prog->fielddefs[i];
1849         PRVM_G_INT(OFS_RETURN) = d->s_name; // presuming that s_name points to a string already
1850 }
1851
1852 // KrimZon - DP_QC_ENTITYDATA
1853 /*
1854 =========
1855 VM_entityfieldtype
1856
1857 float(float fieldnum) entityfieldtype
1858 =========
1859 */
1860 void VM_entityfieldtype(void)
1861 {
1862         ddef_t *d;
1863         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1864         
1865         if (i < 0 || i >= prog->progs->numfielddefs)
1866         {
1867                 VM_Warning("VM_entityfieldtype: %s: field index out of bounds\n", PRVM_NAME);
1868                 PRVM_G_FLOAT(OFS_RETURN) = -1.0;
1869                 return;
1870         }
1871         
1872         d = &prog->fielddefs[i];
1873         PRVM_G_FLOAT(OFS_RETURN) = (float)d->type;
1874 }
1875
1876 // KrimZon - DP_QC_ENTITYDATA
1877 /*
1878 =========
1879 VM_getentityfieldstring
1880
1881 string(float fieldnum, entity ent) getentityfieldstring
1882 =========
1883 */
1884 void VM_getentityfieldstring(void)
1885 {
1886         // put the data into a string
1887         ddef_t *d;
1888         int type, j;
1889         int *v;
1890         prvm_edict_t * ent;
1891         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1892         
1893         if (i < 0 || i >= prog->progs->numfielddefs)
1894         {
1895         VM_Warning("VM_entityfielddata: %s: field index out of bounds\n", PRVM_NAME);
1896                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1897                 return;
1898         }
1899         
1900         d = &prog->fielddefs[i];
1901         
1902         // get the entity
1903         ent = PRVM_G_EDICT(OFS_PARM1);
1904         if(ent->priv.required->free)
1905         {
1906                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1907                 VM_Warning("VM_entityfielddata: %s: entity %i is free !\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
1908                 return;
1909         }
1910         v = (int *)((char *)ent->fields.vp + d->ofs*4);
1911         
1912         // if it's 0 or blank, return an empty string
1913         type = d->type & ~DEF_SAVEGLOBAL;
1914         for (j=0 ; j<prvm_type_size[type] ; j++)
1915                 if (v[j])
1916                         break;
1917         if (j == prvm_type_size[type])
1918         {
1919                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1920                 return;
1921         }
1922                 
1923         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
1924 }
1925
1926 // KrimZon - DP_QC_ENTITYDATA
1927 /*
1928 =========
1929 VM_putentityfieldstring
1930
1931 float(float fieldnum, entity ent, string s) putentityfieldstring
1932 =========
1933 */
1934 void VM_putentityfieldstring(void)
1935 {
1936         ddef_t *d;
1937         prvm_edict_t * ent;
1938         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1939
1940         if (i < 0 || i >= prog->progs->numfielddefs)
1941         {
1942         VM_Warning("VM_entityfielddata: %s: field index out of bounds\n", PRVM_NAME);
1943                 PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
1944                 return;
1945         }
1946
1947         d = &prog->fielddefs[i];
1948
1949         // get the entity
1950         ent = PRVM_G_EDICT(OFS_PARM1);
1951         if(ent->priv.required->free)
1952         {
1953                 VM_Warning("VM_entityfielddata: %s: entity %i is free !\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
1954                 PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
1955                 return;
1956         }
1957
1958         // parse the string into the value
1959         PRVM_G_FLOAT(OFS_RETURN) = ( PRVM_ED_ParseEpair(ent, d, PRVM_G_STRING(OFS_PARM2), false) ) ? 1.0f : 0.0f;
1960 }
1961
1962 /*
1963 =========
1964 VM_strlen
1965
1966 float   strlen(string s)
1967 =========
1968 */
1969 //float(string s) strlen = #114; // returns how many characters are in a string
1970 void VM_strlen(void)
1971 {
1972         VM_SAFEPARMCOUNT(1,VM_strlen);
1973
1974         PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
1975 }
1976
1977 // DRESK - Decolorized String
1978 /*
1979 =========
1980 VM_strdecolorize
1981
1982 string  strdecolorize(string s)
1983 =========
1984 */
1985 // string (string s) strdecolorize = #472; // returns the passed in string with color codes stripped
1986 void VM_strdecolorize(void)
1987 {
1988         char szNewString[VM_STRINGTEMP_LENGTH];
1989         const char *szString;
1990
1991         // Prepare Strings
1992         VM_SAFEPARMCOUNT(1,VM_strdecolorize);
1993         szString = PRVM_G_STRING(OFS_PARM0);
1994         COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), TRUE);
1995         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
1996 }
1997
1998 // DRESK - String Length (not counting color codes)
1999 /*
2000 =========
2001 VM_strlennocol
2002
2003 float   strlennocol(string s)
2004 =========
2005 */
2006 // float(string s) strlennocol = #471; // returns how many characters are in a string not including color codes
2007 // For example, ^2Dresk returns a length of 5
2008 void VM_strlennocol(void)
2009 {
2010         const char *szString;
2011         int nCnt;
2012
2013         VM_SAFEPARMCOUNT(1,VM_strlennocol);
2014
2015         szString = PRVM_G_STRING(OFS_PARM0);
2016
2017         nCnt = COM_StringLengthNoColors(szString, 0, NULL);
2018
2019         PRVM_G_FLOAT(OFS_RETURN) = nCnt;
2020 }
2021
2022 // DRESK - String to Uppercase and Lowercase
2023 /*
2024 =========
2025 VM_strtolower
2026
2027 string  strtolower(string s)
2028 =========
2029 */
2030 // string (string s) strtolower = #480; // returns passed in string in lowercase form
2031 void VM_strtolower(void)
2032 {
2033         char szNewString[VM_STRINGTEMP_LENGTH];
2034         const char *szString;
2035
2036         // Prepare Strings
2037         VM_SAFEPARMCOUNT(1,VM_strtolower);
2038         szString = PRVM_G_STRING(OFS_PARM0);
2039
2040         COM_ToLowerString(szString, szNewString, sizeof(szNewString) );
2041
2042         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
2043 }
2044
2045 /*
2046 =========
2047 VM_strtoupper
2048
2049 string  strtoupper(string s)
2050 =========
2051 */
2052 // string (string s) strtoupper = #481; // returns passed in string in uppercase form
2053 void VM_strtoupper(void)
2054 {
2055         char szNewString[VM_STRINGTEMP_LENGTH];
2056         const char *szString;
2057
2058         // Prepare Strings
2059         VM_SAFEPARMCOUNT(1,VM_strtoupper);
2060         szString = PRVM_G_STRING(OFS_PARM0);
2061
2062         COM_ToUpperString(szString, szNewString, sizeof(szNewString) );
2063
2064         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
2065 }
2066
2067 /*
2068 =========
2069 VM_strcat
2070
2071 string strcat(string,string,...[string])
2072 =========
2073 */
2074 //string(string s1, string s2) strcat = #115;
2075 // concatenates two strings (for example "abc", "def" would return "abcdef")
2076 // and returns as a tempstring
2077 void VM_strcat(void)
2078 {
2079         char s[VM_STRINGTEMP_LENGTH];
2080         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strcat);
2081
2082         VM_VarString(0, s, sizeof(s));
2083         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
2084 }
2085
2086 /*
2087 =========
2088 VM_substring
2089
2090 string  substring(string s, float start, float length)
2091 =========
2092 */
2093 // string(string s, float start, float length) substring = #116;
2094 // returns a section of a string as a tempstring
2095 void VM_substring(void)
2096 {
2097         int i, start, length;
2098         const char *s;
2099         char string[VM_STRINGTEMP_LENGTH];
2100
2101         VM_SAFEPARMCOUNT(3,VM_substring);
2102
2103         s = PRVM_G_STRING(OFS_PARM0);
2104         start = (int)PRVM_G_FLOAT(OFS_PARM1);
2105         length = (int)PRVM_G_FLOAT(OFS_PARM2);
2106         for (i = 0;i < start && *s;i++, s++);
2107         for (i = 0;i < (int)sizeof(string) - 1 && *s && i < length;i++, s++)
2108                 string[i] = *s;
2109         string[i] = 0;
2110         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
2111 }
2112
2113 /*
2114 =========
2115 VM_strreplace
2116
2117 string(string search, string replace, string subject) strreplace = #484;
2118 =========
2119 */
2120 // replaces all occurrences of search with replace in the string subject, and returns the result
2121 void VM_strreplace(void)
2122 {
2123         int i, j, si;
2124         const char *search, *replace, *subject;
2125         char string[VM_STRINGTEMP_LENGTH];
2126         int search_len, replace_len, subject_len;
2127
2128         VM_SAFEPARMCOUNT(3,VM_strreplace);
2129
2130         search = PRVM_G_STRING(OFS_PARM0);
2131         replace = PRVM_G_STRING(OFS_PARM1);
2132         subject = PRVM_G_STRING(OFS_PARM2);
2133
2134         search_len = (int)strlen(search);
2135         replace_len = (int)strlen(replace);
2136         subject_len = (int)strlen(subject);
2137
2138         si = 0;
2139         for (i = 0; i < subject_len; i++)
2140         {
2141                 for (j = 0; j < search_len && i+j < subject_len; j++)
2142                         if (subject[i+j] != search[j])
2143                                 break;
2144                 if (j == search_len || i+j == subject_len)
2145                 {
2146                 // found it at offset 'i'
2147                         for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
2148                                 string[si++] = replace[j];
2149                         i += search_len - 1;
2150                 }
2151                 else
2152                 {
2153                 // not found
2154                         if (si < (int)sizeof(string) - 1)
2155                                 string[si++] = subject[i];
2156                 }
2157         }
2158         string[si] = '\0';
2159
2160         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
2161 }
2162
2163 /*
2164 =========
2165 VM_strireplace
2166
2167 string(string search, string replace, string subject) strireplace = #485;
2168 =========
2169 */
2170 // case-insensitive version of strreplace
2171 void VM_strireplace(void)
2172 {
2173         int i, j, si;
2174         const char *search, *replace, *subject;
2175         char string[VM_STRINGTEMP_LENGTH];
2176         int search_len, replace_len, subject_len;
2177
2178         VM_SAFEPARMCOUNT(3,VM_strreplace);
2179
2180         search = PRVM_G_STRING(OFS_PARM0);
2181         replace = PRVM_G_STRING(OFS_PARM1);
2182         subject = PRVM_G_STRING(OFS_PARM2);
2183
2184         search_len = (int)strlen(search);
2185         replace_len = (int)strlen(replace);
2186         subject_len = (int)strlen(subject);
2187
2188         si = 0;
2189         for (i = 0; i < subject_len; i++)
2190         {
2191                 for (j = 0; j < search_len && i+j < subject_len; j++)
2192                         if (tolower(subject[i+j]) != tolower(search[j]))
2193                                 break;
2194                 if (j == search_len || i+j == subject_len)
2195                 {
2196                 // found it at offset 'i'
2197                         for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
2198                                 string[si++] = replace[j];
2199                         i += search_len - 1;
2200                 }
2201                 else
2202                 {
2203                 // not found
2204                         if (si < (int)sizeof(string) - 1)
2205                                 string[si++] = subject[i];
2206                 }
2207         }
2208         string[si] = '\0';
2209
2210         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
2211 }
2212
2213 /*
2214 =========
2215 VM_stov
2216
2217 vector  stov(string s)
2218 =========
2219 */
2220 //vector(string s) stov = #117; // returns vector value from a string
2221 void VM_stov(void)
2222 {
2223         char string[VM_STRINGTEMP_LENGTH];
2224
2225         VM_SAFEPARMCOUNT(1,VM_stov);
2226
2227         VM_VarString(0, string, sizeof(string));
2228         Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
2229 }
2230
2231 /*
2232 =========
2233 VM_strzone
2234
2235 string  strzone(string s)
2236 =========
2237 */
2238 //string(string s, ...) strzone = #118; // makes a copy of a string into the string zone and returns it, this is often used to keep around a tempstring for longer periods of time (tempstrings are replaced often)
2239 void VM_strzone(void)
2240 {
2241         char *out;
2242         char string[VM_STRINGTEMP_LENGTH];
2243         size_t alloclen;
2244
2245         VM_SAFEPARMCOUNT(1,VM_strzone);
2246
2247         VM_VarString(0, string, sizeof(string));
2248         alloclen = strlen(string) + 1;
2249         PRVM_G_INT(OFS_RETURN) = PRVM_AllocString(alloclen, &out);
2250         memcpy(out, string, alloclen);
2251 }
2252
2253 /*
2254 =========
2255 VM_strunzone
2256
2257 strunzone(string s)
2258 =========
2259 */
2260 //void(string s) strunzone = #119; // removes a copy of a string from the string zone (you can not use that string again or it may crash!!!)
2261 void VM_strunzone(void)
2262 {
2263         VM_SAFEPARMCOUNT(1,VM_strunzone);
2264         PRVM_FreeString(PRVM_G_INT(OFS_PARM0));
2265 }
2266
2267 /*
2268 =========
2269 VM_command (used by client and menu)
2270
2271 clientcommand(float client, string s) (for client and menu)
2272 =========
2273 */
2274 //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
2275 //this function originally written by KrimZon, made shorter by LordHavoc
2276 void VM_clcommand (void)
2277 {
2278         client_t *temp_client;
2279         int i;
2280
2281         VM_SAFEPARMCOUNT(2,VM_clcommand);
2282
2283         i = (int)PRVM_G_FLOAT(OFS_PARM0);
2284         if (!sv.active  || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
2285         {
2286                 VM_Warning("VM_clientcommand: %s: invalid client/server is not active !\n", PRVM_NAME);
2287                 return;
2288         }
2289
2290         temp_client = host_client;
2291         host_client = svs.clients + i;
2292         Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1), src_client);
2293         host_client = temp_client;
2294 }
2295
2296
2297 /*
2298 =========
2299 VM_tokenize
2300
2301 float tokenize(string s)
2302 =========
2303 */
2304 //float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many
2305 //this function originally written by KrimZon, made shorter by LordHavoc
2306 //20040203: rewritten by LordHavoc (no longer uses allocations)
2307 static int num_tokens = 0;
2308 static int tokens[VM_STRINGTEMP_LENGTH / 2];
2309 static int tokens_startpos[VM_STRINGTEMP_LENGTH / 2];
2310 static int tokens_endpos[VM_STRINGTEMP_LENGTH / 2];
2311 static char tokenize_string[VM_STRINGTEMP_LENGTH];
2312 void VM_tokenize (void)
2313 {
2314         const char *p;
2315
2316         VM_SAFEPARMCOUNT(1,VM_tokenize);
2317
2318         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2319         p = tokenize_string;
2320
2321         num_tokens = 0;
2322         for(;;)
2323         {
2324                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2325                         break;
2326
2327                 // skip whitespace here to find token start pos
2328                 while(*p && ISWHITESPACE(*p))
2329                         ++p;
2330
2331                 tokens_startpos[num_tokens] = p - tokenize_string;
2332                 if(!COM_ParseToken_VM_Tokenize(&p, false))
2333                         break;
2334                 tokens_endpos[num_tokens] = p - tokenize_string;
2335                 tokens[num_tokens] = PRVM_SetTempString(com_token);
2336                 ++num_tokens;
2337         }
2338
2339         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2340 }
2341
2342 //float(string s) tokenize = #514; // takes apart a string into individal words (access them with argv), returns how many
2343 void VM_tokenize_console (void)
2344 {
2345         const char *p;
2346
2347         VM_SAFEPARMCOUNT(1,VM_tokenize);
2348
2349         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2350         p = tokenize_string;
2351
2352         num_tokens = 0;
2353         for(;;)
2354         {
2355                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2356                         break;
2357
2358                 // skip whitespace here to find token start pos
2359                 while(*p && ISWHITESPACE(*p))
2360                         ++p;
2361
2362                 tokens_startpos[num_tokens] = p - tokenize_string;
2363                 if(!COM_ParseToken_Console(&p))
2364                         break;
2365                 tokens_endpos[num_tokens] = p - tokenize_string;
2366                 tokens[num_tokens] = PRVM_SetTempString(com_token);
2367                 ++num_tokens;
2368         }
2369
2370         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2371 }
2372
2373 /*
2374 =========
2375 VM_tokenizebyseparator
2376
2377 float tokenizebyseparator(string s, string separator1, ...)
2378 =========
2379 */
2380 //float(string s, string separator1, ...) tokenizebyseparator = #479; // takes apart a string into individal words (access them with argv), returns how many
2381 //this function returns the token preceding each instance of a separator (of
2382 //which there can be multiple), and the text following the last separator
2383 //useful for parsing certain kinds of data like IP addresses
2384 //example:
2385 //numnumbers = tokenizebyseparator("10.1.2.3", ".");
2386 //returns 4 and the tokens "10" "1" "2" "3".
2387 void VM_tokenizebyseparator (void)
2388 {
2389         int j, k;
2390         int numseparators;
2391         int separatorlen[7];
2392         const char *separators[7];
2393         const char *p, *p0;
2394         const char *token;
2395         char tokentext[MAX_INPUTLINE];
2396
2397         VM_SAFEPARMCOUNTRANGE(2, 8,VM_tokenizebyseparator);
2398
2399         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2400         p = tokenize_string;
2401
2402         numseparators = 0;
2403         for (j = 1;j < prog->argc;j++)
2404         {
2405                 // skip any blank separator strings
2406                 const char *s = PRVM_G_STRING(OFS_PARM0+j*3);
2407                 if (!s[0])
2408                         continue;
2409                 separators[numseparators] = s;
2410                 separatorlen[numseparators] = strlen(s);
2411                 numseparators++;
2412         }
2413
2414         num_tokens = 0;
2415         j = 0;
2416
2417         while (num_tokens < (int)(sizeof(tokens)/sizeof(tokens[0])))
2418         {
2419                 token = tokentext + j;
2420                 tokens_startpos[num_tokens] = p - tokenize_string;
2421                 p0 = p;
2422                 while (*p)
2423                 {
2424                         for (k = 0;k < numseparators;k++)
2425                         {
2426                                 if (!strncmp(p, separators[k], separatorlen[k]))
2427                                 {
2428                                         p += separatorlen[k];
2429                                         break;
2430                                 }
2431                         }
2432                         if (k < numseparators)
2433                                 break;
2434                         if (j < (int)sizeof(tokentext)-1)
2435                                 tokentext[j++] = *p;
2436                         p++;
2437                         p0 = p;
2438                 }
2439                 tokens_endpos[num_tokens] = p0 - tokenize_string;
2440                 if (j >= (int)sizeof(tokentext))
2441                         break;
2442                 tokentext[j++] = 0;
2443                 tokens[num_tokens++] = PRVM_SetTempString(token);
2444                 if (!*p)
2445                         break;
2446         }
2447
2448         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2449 }
2450
2451 //string(float n) argv = #442; // returns a word from the tokenized string (returns nothing for an invalid index)
2452 //this function originally written by KrimZon, made shorter by LordHavoc
2453 void VM_argv (void)
2454 {
2455         int token_num;
2456
2457         VM_SAFEPARMCOUNT(1,VM_argv);
2458
2459         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2460
2461         if(token_num < 0)
2462                 token_num += num_tokens;
2463
2464         if (token_num >= 0 && token_num < num_tokens)
2465                 PRVM_G_INT(OFS_RETURN) = tokens[token_num];
2466         else
2467                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2468 }
2469
2470 //float(float n) argv_start_index = #515; // returns the start index of a token
2471 void VM_argv_start_index (void)
2472 {
2473         int token_num;
2474
2475         VM_SAFEPARMCOUNT(1,VM_argv);
2476
2477         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2478
2479         if(token_num < 0)
2480                 token_num += num_tokens;
2481
2482         if (token_num >= 0 && token_num < num_tokens)
2483                 PRVM_G_FLOAT(OFS_RETURN) = tokens_startpos[token_num];
2484         else
2485                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2486 }
2487
2488 //float(float n) argv_end_index = #516; // returns the end index of a token
2489 void VM_argv_end_index (void)
2490 {
2491         int token_num;
2492
2493         VM_SAFEPARMCOUNT(1,VM_argv);
2494
2495         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2496
2497         if(token_num < 0)
2498                 token_num += num_tokens;
2499
2500         if (token_num >= 0 && token_num < num_tokens)
2501                 PRVM_G_FLOAT(OFS_RETURN) = tokens_endpos[token_num];
2502         else
2503                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2504 }
2505
2506 /*
2507 =========
2508 VM_isserver
2509
2510 float   isserver()
2511 =========
2512 */
2513 void VM_isserver(void)
2514 {
2515         VM_SAFEPARMCOUNT(0,VM_serverstate);
2516
2517         PRVM_G_FLOAT(OFS_RETURN) = sv.active && (svs.maxclients > 1 || cls.state == ca_dedicated);
2518 }
2519
2520 /*
2521 =========
2522 VM_clientcount
2523
2524 float   clientcount()
2525 =========
2526 */
2527 void VM_clientcount(void)
2528 {
2529         VM_SAFEPARMCOUNT(0,VM_clientcount);
2530
2531         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
2532 }
2533
2534 /*
2535 =========
2536 VM_clientstate
2537
2538 float   clientstate()
2539 =========
2540 */
2541 void VM_clientstate(void)
2542 {
2543         VM_SAFEPARMCOUNT(0,VM_clientstate);
2544
2545
2546         switch( cls.state ) {
2547                 case ca_uninitialized:
2548                 case ca_dedicated:
2549                         PRVM_G_FLOAT(OFS_RETURN) = 0;
2550                         break;
2551                 case ca_disconnected:
2552                         PRVM_G_FLOAT(OFS_RETURN) = 1;
2553                         break;
2554                 case ca_connected:
2555                         PRVM_G_FLOAT(OFS_RETURN) = 2;
2556                         break;
2557                 default:
2558                         // should never be reached!
2559                         break;
2560         }
2561 }
2562
2563 /*
2564 =========
2565 VM_getostype
2566
2567 float   getostype(void)
2568 =========
2569 */ // not used at the moment -> not included in the common list
2570 void VM_getostype(void)
2571 {
2572         VM_SAFEPARMCOUNT(0,VM_getostype);
2573
2574         /*
2575         OS_WINDOWS
2576         OS_LINUX
2577         OS_MAC - not supported
2578         */
2579
2580 #ifdef WIN32
2581         PRVM_G_FLOAT(OFS_RETURN) = 0;
2582 #elif defined(MACOSX)
2583         PRVM_G_FLOAT(OFS_RETURN) = 2;
2584 #else
2585         PRVM_G_FLOAT(OFS_RETURN) = 1;
2586 #endif
2587 }
2588
2589 /*
2590 =========
2591 VM_gettime
2592
2593 float   gettime(void)
2594 =========
2595 */
2596 void VM_gettime(void)
2597 {
2598         VM_SAFEPARMCOUNT(0,VM_gettime);
2599
2600         PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
2601 }
2602
2603 /*
2604 =========
2605 VM_loadfromdata
2606
2607 loadfromdata(string data)
2608 =========
2609 */
2610 void VM_loadfromdata(void)
2611 {
2612         VM_SAFEPARMCOUNT(1,VM_loadentsfromfile);
2613
2614         PRVM_ED_LoadFromFile(PRVM_G_STRING(OFS_PARM0));
2615 }
2616
2617 /*
2618 ========================
2619 VM_parseentitydata
2620
2621 parseentitydata(entity ent, string data)
2622 ========================
2623 */
2624 void VM_parseentitydata(void)
2625 {
2626         prvm_edict_t *ent;
2627         const char *data;
2628
2629         VM_SAFEPARMCOUNT(2, VM_parseentitydata);
2630
2631         // get edict and test it
2632         ent = PRVM_G_EDICT(OFS_PARM0);
2633         if (ent->priv.required->free)
2634                 PRVM_ERROR ("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
2635
2636         data = PRVM_G_STRING(OFS_PARM1);
2637
2638         // parse the opening brace
2639         if (!COM_ParseToken_Simple(&data, false, false) || com_token[0] != '{' )
2640                 PRVM_ERROR ("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", PRVM_NAME, data );
2641
2642         PRVM_ED_ParseEdict (data, ent);
2643 }
2644
2645 /*
2646 =========
2647 VM_loadfromfile
2648
2649 loadfromfile(string file)
2650 =========
2651 */
2652 void VM_loadfromfile(void)
2653 {
2654         const char *filename;
2655         char *data;
2656
2657         VM_SAFEPARMCOUNT(1,VM_loadfromfile);
2658
2659         filename = PRVM_G_STRING(OFS_PARM0);
2660         if (FS_CheckNastyPath(filename, false))
2661         {
2662                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2663                 VM_Warning("VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename);
2664                 return;
2665         }
2666
2667         // not conform with VM_fopen
2668         data = (char *)FS_LoadFile(filename, tempmempool, false, NULL);
2669         if (data == NULL)
2670                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2671
2672         PRVM_ED_LoadFromFile(data);
2673
2674         if(data)
2675                 Mem_Free(data);
2676 }
2677
2678
2679 /*
2680 =========
2681 VM_modulo
2682
2683 float   mod(float val, float m)
2684 =========
2685 */
2686 void VM_modulo(void)
2687 {
2688         int val, m;
2689         VM_SAFEPARMCOUNT(2,VM_module);
2690
2691         val = (int) PRVM_G_FLOAT(OFS_PARM0);
2692         m       = (int) PRVM_G_FLOAT(OFS_PARM1);
2693
2694         PRVM_G_FLOAT(OFS_RETURN) = (float) (val % m);
2695 }
2696
2697 void VM_Search_Init(void)
2698 {
2699         int i;
2700         for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++)
2701                 prog->opensearches[i] = NULL;
2702 }
2703
2704 void VM_Search_Reset(void)
2705 {
2706         int i;
2707         // reset the fssearch list
2708         for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++)
2709         {
2710                 if(prog->opensearches[i])
2711                         FS_FreeSearch(prog->opensearches[i]);
2712                 prog->opensearches[i] = NULL;
2713         }
2714 }
2715
2716 /*
2717 =========
2718 VM_search_begin
2719
2720 float search_begin(string pattern, float caseinsensitive, float quiet)
2721 =========
2722 */
2723 void VM_search_begin(void)
2724 {
2725         int handle;
2726         const char *pattern;
2727         int caseinsens, quiet;
2728
2729         VM_SAFEPARMCOUNT(3, VM_search_begin);
2730
2731         pattern = PRVM_G_STRING(OFS_PARM0);
2732
2733         VM_CheckEmptyString(pattern);
2734
2735         caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1);
2736         quiet = (int)PRVM_G_FLOAT(OFS_PARM2);
2737
2738         for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++)
2739                 if(!prog->opensearches[handle])
2740                         break;
2741
2742         if(handle >= PRVM_MAX_OPENSEARCHES)
2743         {
2744                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2745                 VM_Warning("VM_search_begin: %s ran out of search handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENSEARCHES);
2746                 return;
2747         }
2748
2749         if(!(prog->opensearches[handle] = FS_Search(pattern,caseinsens, quiet)))
2750                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2751         else
2752         {
2753                 prog->opensearches_origin[handle] = PRVM_AllocationOrigin();
2754                 PRVM_G_FLOAT(OFS_RETURN) = handle;
2755         }
2756 }
2757
2758 /*
2759 =========
2760 VM_search_end
2761
2762 void    search_end(float handle)
2763 =========
2764 */
2765 void VM_search_end(void)
2766 {
2767         int handle;
2768         VM_SAFEPARMCOUNT(1, VM_search_end);
2769
2770         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2771
2772         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2773         {
2774                 VM_Warning("VM_search_end: invalid handle %i used in %s\n", handle, PRVM_NAME);
2775                 return;
2776         }
2777         if(prog->opensearches[handle] == NULL)
2778         {
2779                 VM_Warning("VM_search_end: no such handle %i in %s\n", handle, PRVM_NAME);
2780                 return;
2781         }
2782
2783         FS_FreeSearch(prog->opensearches[handle]);
2784         prog->opensearches[handle] = NULL;
2785         if(prog->opensearches_origin[handle])
2786                 PRVM_Free((char *)prog->opensearches_origin[handle]);
2787 }
2788
2789 /*
2790 =========
2791 VM_search_getsize
2792
2793 float   search_getsize(float handle)
2794 =========
2795 */
2796 void VM_search_getsize(void)
2797 {
2798         int handle;
2799         VM_SAFEPARMCOUNT(1, VM_M_search_getsize);
2800
2801         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2802
2803         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2804         {
2805                 VM_Warning("VM_search_getsize: invalid handle %i used in %s\n", handle, PRVM_NAME);
2806                 return;
2807         }
2808         if(prog->opensearches[handle] == NULL)
2809         {
2810                 VM_Warning("VM_search_getsize: no such handle %i in %s\n", handle, PRVM_NAME);
2811                 return;
2812         }
2813
2814         PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames;
2815 }
2816
2817 /*
2818 =========
2819 VM_search_getfilename
2820
2821 string  search_getfilename(float handle, float num)
2822 =========
2823 */
2824 void VM_search_getfilename(void)
2825 {
2826         int handle, filenum;
2827         VM_SAFEPARMCOUNT(2, VM_search_getfilename);
2828
2829         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2830         filenum = (int)PRVM_G_FLOAT(OFS_PARM1);
2831
2832         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2833         {
2834                 VM_Warning("VM_search_getfilename: invalid handle %i used in %s\n", handle, PRVM_NAME);
2835                 return;
2836         }
2837         if(prog->opensearches[handle] == NULL)
2838         {
2839                 VM_Warning("VM_search_getfilename: no such handle %i in %s\n", handle, PRVM_NAME);
2840                 return;
2841         }
2842         if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames)
2843         {
2844                 VM_Warning("VM_search_getfilename: invalid filenum %i in %s\n", filenum, PRVM_NAME);
2845                 return;
2846         }
2847
2848         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog->opensearches[handle]->filenames[filenum]);
2849 }
2850
2851 /*
2852 =========
2853 VM_chr
2854
2855 string  chr(float ascii)
2856 =========
2857 */
2858 void VM_chr(void)
2859 {
2860         char tmp[2];
2861         VM_SAFEPARMCOUNT(1, VM_chr);
2862
2863         tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
2864         tmp[1] = 0;
2865
2866         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
2867 }
2868
2869 //=============================================================================
2870 // Draw builtins (client & menu)
2871
2872 /*
2873 =========
2874 VM_iscachedpic
2875
2876 float   iscachedpic(string pic)
2877 =========
2878 */
2879 void VM_iscachedpic(void)
2880 {
2881         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
2882
2883         // drawq hasnt such a function, thus always return true
2884         PRVM_G_FLOAT(OFS_RETURN) = false;
2885 }
2886
2887 /*
2888 =========
2889 VM_precache_pic
2890
2891 string  precache_pic(string pic)
2892 =========
2893 */
2894 void VM_precache_pic(void)
2895 {
2896         const char      *s;
2897
2898         VM_SAFEPARMCOUNT(1, VM_precache_pic);
2899
2900         s = PRVM_G_STRING(OFS_PARM0);
2901         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
2902         VM_CheckEmptyString (s);
2903
2904         // AK Draw_CachePic is supposed to always return a valid pointer
2905         if( Draw_CachePic_Flags(s, CACHEPICFLAG_NOTPERSISTENT)->tex == r_texture_notexture )
2906                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2907 }
2908
2909 /*
2910 =========
2911 VM_freepic
2912
2913 freepic(string s)
2914 =========
2915 */
2916 void VM_freepic(void)
2917 {
2918         const char *s;
2919
2920         VM_SAFEPARMCOUNT(1,VM_freepic);
2921
2922         s = PRVM_G_STRING(OFS_PARM0);
2923         VM_CheckEmptyString (s);
2924
2925         Draw_FreePic(s);
2926 }
2927
2928 dp_font_t *getdrawfont()
2929 {
2930         if(prog->globaloffsets.drawfont >= 0)
2931         {
2932                 int f = (int) PRVM_G_FLOAT(prog->globaloffsets.drawfont);
2933                 if(f < 0 || f >= MAX_FONTS)
2934                         return FONT_DEFAULT;
2935                 return &dp_fonts[f];
2936         }
2937         else
2938                 return FONT_DEFAULT;
2939 }
2940
2941 /*
2942 =========
2943 VM_drawcharacter
2944
2945 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
2946 =========
2947 */
2948 void VM_drawcharacter(void)
2949 {
2950         float *pos,*scale,*rgb;
2951         char   character;
2952         int flag;
2953         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
2954
2955         character = (char) PRVM_G_FLOAT(OFS_PARM1);
2956         if(character == 0)
2957         {
2958                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2959                 VM_Warning("VM_drawcharacter: %s passed null character !\n",PRVM_NAME);
2960                 return;
2961         }
2962
2963         pos = PRVM_G_VECTOR(OFS_PARM0);
2964         scale = PRVM_G_VECTOR(OFS_PARM2);
2965         rgb = PRVM_G_VECTOR(OFS_PARM3);
2966         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2967
2968         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2969         {
2970                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2971                 VM_Warning("VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2972                 return;
2973         }
2974
2975         if(pos[2] || scale[2])
2976                 Con_Printf("VM_drawcharacter: z value%c from %s discarded\n",(pos[2] && scale[2]) ? 's' : 0,((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
2977
2978         if(!scale[0] || !scale[1])
2979         {
2980                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2981                 VM_Warning("VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2982                 return;
2983         }
2984
2985         DrawQ_String_Font(pos[0], pos[1], &character, 1, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont());
2986         PRVM_G_FLOAT(OFS_RETURN) = 1;
2987 }
2988
2989 /*
2990 =========
2991 VM_drawstring
2992
2993 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
2994 =========
2995 */
2996 void VM_drawstring(void)
2997 {
2998         float *pos,*scale,*rgb;
2999         const char  *string;
3000         int flag;
3001         VM_SAFEPARMCOUNT(6,VM_drawstring);
3002
3003         string = PRVM_G_STRING(OFS_PARM1);
3004         pos = PRVM_G_VECTOR(OFS_PARM0);
3005         scale = PRVM_G_VECTOR(OFS_PARM2);
3006         rgb = PRVM_G_VECTOR(OFS_PARM3);
3007         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3008
3009         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3010         {
3011                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3012                 VM_Warning("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3013                 return;
3014         }
3015
3016         if(!scale[0] || !scale[1])
3017         {
3018                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3019                 VM_Warning("VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3020                 return;
3021         }
3022
3023         if(pos[2] || scale[2])
3024                 Con_Printf("VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
3025
3026         DrawQ_String_Font(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont());
3027         PRVM_G_FLOAT(OFS_RETURN) = 1;
3028 }
3029
3030 /*
3031 =========
3032 VM_drawcolorcodedstring
3033
3034 float   drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag)
3035 =========
3036 */
3037 void VM_drawcolorcodedstring(void)
3038 {
3039         float *pos,*scale;
3040         const char  *string;
3041         int flag,color;
3042         VM_SAFEPARMCOUNT(5,VM_drawstring);
3043
3044         string = PRVM_G_STRING(OFS_PARM1);
3045         pos = PRVM_G_VECTOR(OFS_PARM0);
3046         scale = PRVM_G_VECTOR(OFS_PARM2);
3047         flag = (int)PRVM_G_FLOAT(OFS_PARM4);
3048
3049         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3050         {
3051                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3052                 VM_Warning("VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3053                 return;
3054         }
3055
3056         if(!scale[0] || !scale[1])
3057         {
3058                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3059                 VM_Warning("VM_drawcolorcodedstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3060                 return;
3061         }
3062
3063         if(pos[2] || scale[2])
3064                 Con_Printf("VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
3065
3066         color = -1;
3067         DrawQ_String_Font(pos[0], pos[1], string, 0, scale[0], scale[1], 1, 1, 1, PRVM_G_FLOAT(OFS_PARM3), flag, NULL, false, getdrawfont());
3068         PRVM_G_FLOAT(OFS_RETURN) = 1;
3069 }
3070 /*
3071 =========
3072 VM_stringwidth
3073
3074 float   stringwidth(string text, float allowColorCodes)
3075 =========
3076 */
3077 void VM_stringwidth(void)
3078 {
3079         const char  *string;
3080         int colors;
3081         VM_SAFEPARMCOUNT(2,VM_drawstring);
3082
3083         string = PRVM_G_STRING(OFS_PARM0);
3084         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
3085
3086         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font(string, 0, !colors, getdrawfont()); // 1x1 characters, don't actually draw
3087 }
3088 /*
3089 =========
3090 VM_drawpic
3091
3092 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
3093 =========
3094 */
3095 void VM_drawpic(void)
3096 {
3097         const char *picname;
3098         float *size, *pos, *rgb;
3099         int flag;
3100
3101         VM_SAFEPARMCOUNT(6,VM_drawpic);
3102
3103         picname = PRVM_G_STRING(OFS_PARM1);
3104         VM_CheckEmptyString (picname);
3105
3106         // is pic cached ? no function yet for that
3107         if(!1)
3108         {
3109                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3110                 VM_Warning("VM_drawpic: %s: %s not cached !\n", PRVM_NAME, picname);
3111                 return;
3112         }
3113
3114         pos = PRVM_G_VECTOR(OFS_PARM0);
3115         size = PRVM_G_VECTOR(OFS_PARM2);
3116         rgb = PRVM_G_VECTOR(OFS_PARM3);
3117         flag = (int) PRVM_G_FLOAT(OFS_PARM5);
3118
3119         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3120         {
3121                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3122                 VM_Warning("VM_drawpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3123                 return;
3124         }
3125
3126         if(pos[2] || size[2])
3127                 Con_Printf("VM_drawpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
3128
3129         DrawQ_Pic(pos[0], pos[1], Draw_CachePic (picname), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
3130         PRVM_G_FLOAT(OFS_RETURN) = 1;
3131 }
3132 /*
3133 =========
3134 VM_drawsubpic
3135
3136 float   drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
3137
3138 =========
3139 */
3140 void VM_drawsubpic(void)
3141 {
3142         const char *picname;
3143         float *size, *pos, *rgb, *srcPos, *srcSize, alpha;
3144         int flag;
3145
3146         VM_SAFEPARMCOUNT(8,VM_drawsubpic);
3147
3148         picname = PRVM_G_STRING(OFS_PARM2);
3149         VM_CheckEmptyString (picname);
3150
3151         // is pic cached ? no function yet for that
3152         if(!1)
3153         {
3154                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3155                 VM_Warning("VM_drawsubpic: %s: %s not cached !\n", PRVM_NAME, picname);
3156                 return;
3157         }
3158
3159         pos = PRVM_G_VECTOR(OFS_PARM0);
3160         size = PRVM_G_VECTOR(OFS_PARM1);
3161         srcPos = PRVM_G_VECTOR(OFS_PARM3);
3162         srcSize = PRVM_G_VECTOR(OFS_PARM4);
3163         rgb = PRVM_G_VECTOR(OFS_PARM5);
3164         alpha = PRVM_G_FLOAT(OFS_PARM6);
3165         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
3166
3167         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3168         {
3169                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3170                 VM_Warning("VM_drawsubpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3171                 return;
3172         }
3173
3174         if(pos[2] || size[2])
3175                 Con_Printf("VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
3176
3177         DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic (picname),
3178                 size[0], size[1],
3179                 srcPos[0],              srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3180                 srcPos[0] + srcSize[0], srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3181                 srcPos[0],              srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3182                 srcPos[0] + srcSize[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3183                 flag);
3184         PRVM_G_FLOAT(OFS_RETURN) = 1;
3185 }
3186
3187 /*
3188 =========
3189 VM_drawfill
3190
3191 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
3192 =========
3193 */
3194 void VM_drawfill(void)
3195 {
3196         float *size, *pos, *rgb;
3197         int flag;
3198
3199         VM_SAFEPARMCOUNT(5,VM_drawfill);
3200
3201
3202         pos = PRVM_G_VECTOR(OFS_PARM0);
3203         size = PRVM_G_VECTOR(OFS_PARM1);
3204         rgb = PRVM_G_VECTOR(OFS_PARM2);
3205         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
3206
3207         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3208         {
3209                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3210                 VM_Warning("VM_drawfill: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3211                 return;
3212         }
3213
3214         if(pos[2] || size[2])
3215                 Con_Printf("VM_drawfill: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
3216
3217         DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
3218         PRVM_G_FLOAT(OFS_RETURN) = 1;
3219 }
3220
3221 /*
3222 =========
3223 VM_drawsetcliparea
3224
3225 drawsetcliparea(float x, float y, float width, float height)
3226 =========
3227 */
3228 void VM_drawsetcliparea(void)
3229 {
3230         float x,y,w,h;
3231         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
3232
3233         x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
3234         y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
3235         w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
3236         h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
3237
3238         DrawQ_SetClipArea(x, y, w, h);
3239 }
3240
3241 /*
3242 =========
3243 VM_drawresetcliparea
3244
3245 drawresetcliparea()
3246 =========
3247 */
3248 void VM_drawresetcliparea(void)
3249 {
3250         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
3251
3252         DrawQ_ResetClipArea();
3253 }
3254
3255 /*
3256 =========
3257 VM_getimagesize
3258
3259 vector  getimagesize(string pic)
3260 =========
3261 */
3262 void VM_getimagesize(void)
3263 {
3264         const char *p;
3265         cachepic_t *pic;
3266
3267         VM_SAFEPARMCOUNT(1,VM_getimagesize);
3268
3269         p = PRVM_G_STRING(OFS_PARM0);
3270         VM_CheckEmptyString (p);
3271
3272         pic = Draw_CachePic_Flags (p, CACHEPICFLAG_NOTPERSISTENT);
3273
3274         PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
3275         PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
3276         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3277 }
3278
3279 /*
3280 =========
3281 VM_keynumtostring
3282
3283 string keynumtostring(float keynum)
3284 =========
3285 */
3286 void VM_keynumtostring (void)
3287 {
3288         VM_SAFEPARMCOUNT(1, VM_keynumtostring);
3289
3290         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0)));
3291 }
3292
3293 /*
3294 =========
3295 VM_findkeysforcommand
3296
3297 string  findkeysforcommand(string command)
3298
3299 the returned string is an altstring
3300 =========
3301 */
3302 #define NUMKEYS 5 // TODO: merge the constant in keys.c with this one somewhen
3303
3304 void M_FindKeysForCommand(const char *command, int *keys);
3305 void VM_findkeysforcommand(void)
3306 {
3307         const char *cmd;
3308         char ret[VM_STRINGTEMP_LENGTH];
3309         int keys[NUMKEYS];
3310         int i;
3311
3312         VM_SAFEPARMCOUNT(1, VM_findkeysforcommand);
3313
3314         cmd = PRVM_G_STRING(OFS_PARM0);
3315
3316         VM_CheckEmptyString(cmd);
3317
3318         M_FindKeysForCommand(cmd, keys);
3319
3320         ret[0] = 0;
3321         for(i = 0; i < NUMKEYS; i++)
3322                 strlcat(ret, va(" \'%i\'", keys[i]), sizeof(ret));
3323
3324         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(ret);
3325 }
3326
3327 /*
3328 =========
3329 VM_stringtokeynum
3330
3331 float stringtokeynum(string key)
3332 =========
3333 */
3334 void VM_stringtokeynum (void)
3335 {
3336         VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
3337
3338         PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
3339 }
3340
3341 // CL_Video interface functions
3342
3343 /*
3344 ========================
3345 VM_cin_open
3346
3347 float cin_open(string file, string name)
3348 ========================
3349 */
3350 void VM_cin_open( void )
3351 {
3352         const char *file;
3353         const char *name;
3354
3355         VM_SAFEPARMCOUNT( 2, VM_cin_open );
3356
3357         file = PRVM_G_STRING( OFS_PARM0 );
3358         name = PRVM_G_STRING( OFS_PARM1 );
3359
3360         VM_CheckEmptyString( file );
3361     VM_CheckEmptyString( name );
3362
3363         if( CL_OpenVideo( file, name, MENUOWNER ) )
3364                 PRVM_G_FLOAT( OFS_RETURN ) = 1;
3365         else
3366                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3367 }
3368
3369 /*
3370 ========================
3371 VM_cin_close
3372
3373 void cin_close(string name)
3374 ========================
3375 */
3376 void VM_cin_close( void )
3377 {
3378         const char *name;
3379
3380         VM_SAFEPARMCOUNT( 1, VM_cin_close );
3381
3382         name = PRVM_G_STRING( OFS_PARM0 );
3383         VM_CheckEmptyString( name );
3384
3385         CL_CloseVideo( CL_GetVideoByName( name ) );
3386 }
3387
3388 /*
3389 ========================
3390 VM_cin_setstate
3391 void cin_setstate(string name, float type)
3392 ========================
3393 */
3394 void VM_cin_setstate( void )
3395 {
3396         const char *name;
3397         clvideostate_t  state;
3398         clvideo_t               *video;
3399
3400         VM_SAFEPARMCOUNT( 2, VM_cin_netstate );
3401
3402         name = PRVM_G_STRING( OFS_PARM0 );
3403         VM_CheckEmptyString( name );
3404
3405         state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
3406
3407         video = CL_GetVideoByName( name );
3408         if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
3409                 CL_SetVideoState( video, state );
3410 }
3411
3412 /*
3413 ========================
3414 VM_cin_getstate
3415
3416 float cin_getstate(string name)
3417 ========================
3418 */
3419 void VM_cin_getstate( void )
3420 {
3421         const char *name;
3422         clvideo_t               *video;
3423
3424         VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
3425
3426         name = PRVM_G_STRING( OFS_PARM0 );
3427         VM_CheckEmptyString( name );
3428
3429         video = CL_GetVideoByName( name );
3430         if( video )
3431                 PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
3432         else
3433                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3434 }
3435
3436 /*
3437 ========================
3438 VM_cin_restart
3439
3440 void cin_restart(string name)
3441 ========================
3442 */
3443 void VM_cin_restart( void )
3444 {
3445         const char *name;
3446         clvideo_t               *video;
3447
3448         VM_SAFEPARMCOUNT( 1, VM_cin_restart );
3449
3450         name = PRVM_G_STRING( OFS_PARM0 );
3451         VM_CheckEmptyString( name );
3452
3453         video = CL_GetVideoByName( name );
3454         if( video )
3455                 CL_RestartVideo( video );
3456 }
3457
3458 /*
3459 ========================
3460 VM_Gecko_Init
3461 ========================
3462 */
3463 void VM_Gecko_Init( void ) {
3464         // the prog struct is memset to 0 by Initprog? [12/6/2007 Black]
3465         // FIXME: remove the other _Init functions then, too? [12/6/2007 Black]
3466 }
3467
3468 /*
3469 ========================
3470 VM_Gecko_Destroy
3471 ========================
3472 */
3473 void VM_Gecko_Destroy( void ) {
3474         int i;
3475         for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
3476                 clgecko_t **instance = &prog->opengeckoinstances[ i ];
3477                 if( *instance ) {
3478                         CL_Gecko_DestroyBrowser( *instance );
3479                 }
3480                 *instance = NULL;
3481         }
3482 }
3483
3484 /*
3485 ========================
3486 VM_gecko_create
3487
3488 float[bool] gecko_create( string name )
3489 ========================
3490 */
3491 void VM_gecko_create( void ) {
3492         const char *name;
3493         int i;
3494         clgecko_t *instance;
3495         
3496         VM_SAFEPARMCOUNT( 1, VM_gecko_create );
3497
3498         name = PRVM_G_STRING( OFS_PARM0 );
3499         VM_CheckEmptyString( name );
3500
3501         // find an empty slot for this gecko browser..
3502         for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
3503                 if( prog->opengeckoinstances[ i ] == NULL ) {
3504                         break;
3505                 }
3506         }
3507         if( i == PRVM_MAX_GECKOINSTANCES ) {
3508                         VM_Warning("VM_gecko_create: %s ran out of gecko handles (%i)\n", PRVM_NAME, PRVM_MAX_GECKOINSTANCES);
3509                         PRVM_G_FLOAT( OFS_RETURN ) = 0;
3510                         return;
3511         }
3512
3513         instance = prog->opengeckoinstances[ i ] = CL_Gecko_CreateBrowser( name, PRVM_GetProgNr() );
3514    if( !instance ) {
3515                 // TODO: error handling [12/3/2007 Black]
3516                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3517                 return;
3518         }
3519         PRVM_G_FLOAT( OFS_RETURN ) = 1;
3520 }
3521
3522 /*
3523 ========================
3524 VM_gecko_destroy
3525
3526 void gecko_destroy( string name )
3527 ========================
3528 */
3529 void VM_gecko_destroy( void ) {
3530         const char *name;
3531         clgecko_t *instance;
3532
3533         VM_SAFEPARMCOUNT( 1, VM_gecko_destroy );
3534
3535         name = PRVM_G_STRING( OFS_PARM0 );
3536         VM_CheckEmptyString( name );
3537         instance = CL_Gecko_FindBrowser( name );
3538         if( !instance ) {
3539                 return;
3540         }
3541         CL_Gecko_DestroyBrowser( instance );
3542 }
3543
3544 /*
3545 ========================
3546 VM_gecko_navigate
3547
3548 void gecko_navigate( string name, string URI )
3549 ========================
3550 */
3551 void VM_gecko_navigate( void ) {
3552         const char *name;
3553         const char *URI;
3554         clgecko_t *instance;
3555
3556         VM_SAFEPARMCOUNT( 2, VM_gecko_navigate );
3557
3558         name = PRVM_G_STRING( OFS_PARM0 );
3559         URI = PRVM_G_STRING( OFS_PARM1 );
3560         VM_CheckEmptyString( name );
3561         VM_CheckEmptyString( URI );
3562
3563    instance = CL_Gecko_FindBrowser( name );
3564         if( !instance ) {
3565                 return;
3566         }
3567         CL_Gecko_NavigateToURI( instance, URI );
3568 }
3569
3570 /*
3571 ========================
3572 VM_gecko_keyevent
3573
3574 float[bool] gecko_keyevent( string name, float key, float eventtype ) 
3575 ========================
3576 */
3577 void VM_gecko_keyevent( void ) {
3578         const char *name;
3579         unsigned int key;
3580         clgecko_buttoneventtype_t eventtype;
3581         clgecko_t *instance;
3582
3583         VM_SAFEPARMCOUNT( 3, VM_gecko_keyevent );
3584
3585         name = PRVM_G_STRING( OFS_PARM0 );
3586         VM_CheckEmptyString( name );
3587         key = (unsigned int) PRVM_G_FLOAT( OFS_PARM1 );
3588         switch( (unsigned int) PRVM_G_FLOAT( OFS_PARM2 ) ) {
3589         case 0:
3590                 eventtype = CLG_BET_DOWN;
3591                 break;
3592         case 1:
3593                 eventtype = CLG_BET_UP;
3594                 break;
3595         case 2:
3596                 eventtype = CLG_BET_PRESS;
3597                 break;
3598         case 3:
3599                 eventtype = CLG_BET_DOUBLECLICK;
3600                 break;
3601         default:
3602                 // TODO: console printf? [12/3/2007 Black]
3603                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3604                 return;
3605         }
3606
3607         instance = CL_Gecko_FindBrowser( name );
3608         if( !instance ) {
3609                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3610                 return;
3611         }
3612
3613         PRVM_G_FLOAT( OFS_RETURN ) = (CL_Gecko_Event_Key( instance, (keynum_t) key, eventtype ) == true);
3614 }
3615
3616 /*
3617 ========================
3618 VM_gecko_movemouse
3619
3620 void gecko_mousemove( string name, float x, float y )
3621 ========================
3622 */
3623 void VM_gecko_movemouse( void ) {
3624         const char *name;
3625         float x, y;
3626         clgecko_t *instance;
3627
3628         VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
3629
3630         name = PRVM_G_STRING( OFS_PARM0 );
3631         VM_CheckEmptyString( name );
3632         x = PRVM_G_FLOAT( OFS_PARM1 );
3633         y = PRVM_G_FLOAT( OFS_PARM2 );
3634         
3635         instance = CL_Gecko_FindBrowser( name );
3636         if( !instance ) {
3637                 return;
3638         }
3639         CL_Gecko_Event_CursorMove( instance, x, y );
3640 }
3641
3642
3643 /*
3644 ========================
3645 VM_gecko_resize
3646
3647 void gecko_resize( string name, float w, float h )
3648 ========================
3649 */
3650 void VM_gecko_resize( void ) {
3651         const char *name;
3652         float w, h;
3653         clgecko_t *instance;
3654
3655         VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
3656
3657         name = PRVM_G_STRING( OFS_PARM0 );
3658         VM_CheckEmptyString( name );
3659         w = PRVM_G_FLOAT( OFS_PARM1 );
3660         h = PRVM_G_FLOAT( OFS_PARM2 );
3661         
3662         instance = CL_Gecko_FindBrowser( name );
3663         if( !instance ) {
3664                 return;
3665         }
3666         CL_Gecko_Resize( instance, (int) w, (int) h );
3667 }
3668
3669
3670 /*
3671 ========================
3672 VM_gecko_get_texture_extent
3673
3674 vector gecko_get_texture_extent( string name )
3675 ========================
3676 */
3677 void VM_gecko_get_texture_extent( void ) {
3678         const char *name;
3679         clgecko_t *instance;
3680
3681         VM_SAFEPARMCOUNT( 1, VM_gecko_movemouse );
3682
3683         name = PRVM_G_STRING( OFS_PARM0 );
3684         VM_CheckEmptyString( name );
3685         
3686         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3687         instance = CL_Gecko_FindBrowser( name );
3688         if( !instance ) {
3689                 PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
3690                 PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
3691                 return;
3692         }
3693         CL_Gecko_GetTextureExtent( instance, 
3694                 PRVM_G_VECTOR(OFS_RETURN), PRVM_G_VECTOR(OFS_RETURN)+1 );
3695 }
3696
3697
3698
3699 /*
3700 ==============
3701 VM_makevectors
3702
3703 Writes new values for v_forward, v_up, and v_right based on angles
3704 void makevectors(vector angle)
3705 ==============
3706 */
3707 void VM_makevectors (void)
3708 {
3709         prvm_eval_t *valforward, *valright, *valup;
3710         valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward);
3711         valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right);
3712         valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up);
3713         if (!valforward || !valright || !valup)
3714         {
3715                 VM_Warning("makevectors: could not find v_forward, v_right, or v_up global variables\n");
3716                 return;
3717         }
3718         VM_SAFEPARMCOUNT(1, VM_makevectors);
3719         AngleVectors (PRVM_G_VECTOR(OFS_PARM0), valforward->vector, valright->vector, valup->vector);
3720 }
3721
3722 /*
3723 ==============
3724 VM_vectorvectors
3725
3726 Writes new values for v_forward, v_up, and v_right based on the given forward vector
3727 vectorvectors(vector)
3728 ==============
3729 */
3730 void VM_vectorvectors (void)
3731 {
3732         prvm_eval_t *valforward, *valright, *valup;
3733         valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward);
3734         valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right);
3735         valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up);
3736         if (!valforward || !valright || !valup)
3737         {
3738                 VM_Warning("vectorvectors: could not find v_forward, v_right, or v_up global variables\n");
3739                 return;
3740         }
3741         VM_SAFEPARMCOUNT(1, VM_vectorvectors);
3742         VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), valforward->vector);
3743         VectorVectors(valforward->vector, valright->vector, valup->vector);
3744 }
3745
3746 /*
3747 ========================
3748 VM_drawline
3749
3750 void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
3751 ========================
3752 */
3753 void VM_drawline (void)
3754 {
3755         float   *c1, *c2, *rgb;
3756         float   alpha, width;
3757         unsigned char   flags;
3758
3759         VM_SAFEPARMCOUNT(6, VM_drawline);
3760         width   = PRVM_G_FLOAT(OFS_PARM0);
3761         c1              = PRVM_G_VECTOR(OFS_PARM1);
3762         c2              = PRVM_G_VECTOR(OFS_PARM2);
3763         rgb             = PRVM_G_VECTOR(OFS_PARM3);
3764         alpha   = PRVM_G_FLOAT(OFS_PARM4);
3765         flags   = (int)PRVM_G_FLOAT(OFS_PARM5);
3766         DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
3767 }
3768
3769 // float(float number, float quantity) bitshift (EXT_BITSHIFT)
3770 void VM_bitshift (void)
3771 {
3772         int n1, n2;
3773         VM_SAFEPARMCOUNT(2, VM_bitshift);
3774
3775         n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0));
3776         n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
3777         if(!n1)
3778                 PRVM_G_FLOAT(OFS_RETURN) = n1;
3779         else
3780         if(n2 < 0)
3781                 PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
3782         else
3783                 PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
3784 }
3785
3786 ////////////////////////////////////////
3787 // AltString functions
3788 ////////////////////////////////////////
3789
3790 /*
3791 ========================
3792 VM_altstr_count
3793
3794 float altstr_count(string)
3795 ========================
3796 */
3797 void VM_altstr_count( void )
3798 {
3799         const char *altstr, *pos;
3800         int     count;
3801
3802         VM_SAFEPARMCOUNT( 1, VM_altstr_count );
3803
3804         altstr = PRVM_G_STRING( OFS_PARM0 );
3805         //VM_CheckEmptyString( altstr );
3806
3807         for( count = 0, pos = altstr ; *pos ; pos++ ) {
3808                 if( *pos == '\\' ) {
3809                         if( !*++pos ) {
3810                                 break;
3811                         }
3812                 } else if( *pos == '\'' ) {
3813                         count++;
3814                 }
3815         }
3816
3817         PRVM_G_FLOAT( OFS_RETURN ) = (float) (count / 2);
3818 }
3819
3820 /*
3821 ========================
3822 VM_altstr_prepare
3823
3824 string altstr_prepare(string)
3825 ========================
3826 */
3827 void VM_altstr_prepare( void )
3828 {
3829         char *out;
3830         const char *instr, *in;
3831         int size;
3832         char outstr[VM_STRINGTEMP_LENGTH];
3833
3834         VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
3835
3836         instr = PRVM_G_STRING( OFS_PARM0 );
3837
3838         for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )
3839                 if( *in == '\'' ) {
3840                         *out++ = '\\';
3841                         *out = '\'';
3842                         size--;
3843                 } else
3844                         *out = *in;
3845         *out = 0;
3846
3847         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3848 }
3849
3850 /*
3851 ========================
3852 VM_altstr_get
3853
3854 string altstr_get(string, float)
3855 ========================
3856 */
3857 void VM_altstr_get( void )
3858 {
3859         const char *altstr, *pos;
3860         char *out;
3861         int count, size;
3862         char outstr[VM_STRINGTEMP_LENGTH];
3863
3864         VM_SAFEPARMCOUNT( 2, VM_altstr_get );
3865
3866         altstr = PRVM_G_STRING( OFS_PARM0 );
3867
3868         count = (int)PRVM_G_FLOAT( OFS_PARM1 );
3869         count = count * 2 + 1;
3870
3871         for( pos = altstr ; *pos && count ; pos++ )
3872                 if( *pos == '\\' ) {
3873                         if( !*++pos )
3874                                 break;
3875                 } else if( *pos == '\'' )
3876                         count--;
3877
3878         if( !*pos ) {
3879                 PRVM_G_INT( OFS_RETURN ) = 0;
3880                 return;
3881         }
3882
3883         for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
3884                 if( *pos == '\\' ) {
3885                         if( !*++pos )
3886                                 break;
3887                         *out = *pos;
3888                         size--;
3889                 } else if( *pos == '\'' )
3890                         break;
3891                 else
3892                         *out = *pos;
3893
3894         *out = 0;
3895         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3896 }
3897
3898 /*
3899 ========================
3900 VM_altstr_set
3901
3902 string altstr_set(string altstr, float num, string set)
3903 ========================
3904 */
3905 void VM_altstr_set( void )
3906 {
3907     int num;
3908         const char *altstr, *str;
3909         const char *in;
3910         char *out;
3911         char outstr[VM_STRINGTEMP_LENGTH];
3912
3913         VM_SAFEPARMCOUNT( 3, VM_altstr_set );
3914
3915         altstr = PRVM_G_STRING( OFS_PARM0 );
3916
3917         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3918
3919         str = PRVM_G_STRING( OFS_PARM2 );
3920
3921         out = outstr;
3922         for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
3923                 if( *in == '\\' ) {
3924                         if( !*++in ) {
3925                                 break;
3926                         }
3927                 } else if( *in == '\'' ) {
3928                         num--;
3929                 }
3930
3931         // copy set in
3932         for( ; *str; *out++ = *str++ );
3933         // now jump over the old content
3934         for( ; *in ; in++ )
3935                 if( *in == '\'' || (*in == '\\' && !*++in) )
3936                         break;
3937
3938         strlcpy(out, in, outstr + sizeof(outstr) - out);
3939         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3940 }
3941
3942 /*
3943 ========================
3944 VM_altstr_ins
3945 insert after num
3946 string  altstr_ins(string altstr, float num, string set)
3947 ========================
3948 */
3949 void VM_altstr_ins(void)
3950 {
3951         int num;
3952         const char *setstr;
3953         const char *set;
3954         const char *instr;
3955         const char *in;
3956         char *out;
3957         char outstr[VM_STRINGTEMP_LENGTH];
3958
3959         VM_SAFEPARMCOUNT(3, VM_altstr_ins);
3960
3961         in = instr = PRVM_G_STRING( OFS_PARM0 );
3962         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3963         set = setstr = PRVM_G_STRING( OFS_PARM2 );
3964
3965         out = outstr;
3966         for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
3967                 if( *in == '\\' ) {
3968                         if( !*++in ) {
3969                                 break;
3970                         }
3971                 } else if( *in == '\'' ) {
3972                         num--;
3973                 }
3974
3975         *out++ = '\'';
3976         for( ; *set ; *out++ = *set++ );
3977         *out++ = '\'';
3978
3979         strlcpy(out, in, outstr + sizeof(outstr) - out);
3980         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3981 }
3982
3983
3984 ////////////////////////////////////////
3985 // BufString functions
3986 ////////////////////////////////////////
3987 //[515]: string buffers support
3988
3989 static size_t stringbuffers_sortlength;
3990
3991 static void BufStr_Expand(prvm_stringbuffer_t *stringbuffer, int strindex)
3992 {
3993         if (stringbuffer->max_strings <= strindex)
3994         {
3995                 char **oldstrings = stringbuffer->strings;
3996                 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
3997                 while (stringbuffer->max_strings <= strindex)
3998                         stringbuffer->max_strings *= 2;
3999                 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
4000                 if (stringbuffer->num_strings > 0)
4001                         memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
4002                 if (oldstrings)
4003                         Mem_Free(oldstrings);
4004         }
4005 }
4006
4007 static void BufStr_Shrink(prvm_stringbuffer_t *stringbuffer)
4008 {
4009         // reduce num_strings if there are empty string slots at the end
4010         while (stringbuffer->num_strings > 0 && stringbuffer->strings[stringbuffer->num_strings - 1] == NULL)
4011                 stringbuffer->num_strings--;
4012
4013         // if empty, free the string pointer array
4014         if (stringbuffer->num_strings == 0)
4015         {
4016                 stringbuffer->max_strings = 0;
4017                 if (stringbuffer->strings)
4018                         Mem_Free(stringbuffer->strings);
4019                 stringbuffer->strings = NULL;
4020         }
4021 }
4022
4023 static int BufStr_SortStringsUP (const void *in1, const void *in2)
4024 {
4025         const char *a, *b;
4026         a = *((const char **) in1);
4027         b = *((const char **) in2);
4028         if(!a[0])       return 1;
4029         if(!b[0])       return -1;
4030         return strncmp(a, b, stringbuffers_sortlength);
4031 }
4032
4033 static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
4034 {
4035         const char *a, *b;
4036         a = *((const char **) in1);
4037         b = *((const char **) in2);
4038         if(!a[0])       return 1;
4039         if(!b[0])       return -1;
4040         return strncmp(b, a, stringbuffers_sortlength);
4041 }
4042
4043 /*
4044 ========================
4045 VM_buf_create
4046 creates new buffer, and returns it's index, returns -1 if failed
4047 float buf_create(void) = #460;
4048 ========================
4049 */
4050 void VM_buf_create (void)
4051 {
4052         prvm_stringbuffer_t *stringbuffer;
4053         int i;
4054         VM_SAFEPARMCOUNT(0, VM_buf_create);
4055         stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
4056         for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
4057         stringbuffer->origin = PRVM_AllocationOrigin();
4058         PRVM_G_FLOAT(OFS_RETURN) = i;
4059 }
4060
4061 /*
4062 ========================
4063 VM_buf_del
4064 deletes buffer and all strings in it
4065 void buf_del(float bufhandle) = #461;
4066 ========================
4067 */
4068 void VM_buf_del (void)
4069 {
4070         prvm_stringbuffer_t *stringbuffer;
4071         VM_SAFEPARMCOUNT(1, VM_buf_del);
4072         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4073         if (stringbuffer)
4074         {
4075                 int i;
4076                 for (i = 0;i < stringbuffer->num_strings;i++)
4077                         if (stringbuffer->strings[i])
4078                                 Mem_Free(stringbuffer->strings[i]);
4079                 if (stringbuffer->strings)
4080                         Mem_Free(stringbuffer->strings);
4081                 if(stringbuffer->origin)
4082                         PRVM_Free((char *)stringbuffer->origin);
4083                 Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
4084         }
4085         else
4086         {
4087                 VM_Warning("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4088                 return;
4089         }
4090 }
4091
4092 /*
4093 ========================
4094 VM_buf_getsize
4095 how many strings are stored in buffer
4096 float buf_getsize(float bufhandle) = #462;
4097 ========================
4098 */
4099 void VM_buf_getsize (void)
4100 {
4101         prvm_stringbuffer_t *stringbuffer;
4102         VM_SAFEPARMCOUNT(1, VM_buf_getsize);
4103
4104         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4105         if(!stringbuffer)
4106         {
4107                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4108                 VM_Warning("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4109                 return;
4110         }
4111         else
4112                 PRVM_G_FLOAT(OFS_RETURN) = stringbuffer->num_strings;
4113 }
4114
4115 /*
4116 ========================
4117 VM_buf_copy
4118 copy all content from one buffer to another, make sure it exists
4119 void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
4120 ========================
4121 */
4122 void VM_buf_copy (void)
4123 {
4124         prvm_stringbuffer_t *srcstringbuffer, *dststringbuffer;
4125         int i;
4126         VM_SAFEPARMCOUNT(2, VM_buf_copy);
4127
4128         srcstringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4129         if(!srcstringbuffer)
4130         {
4131                 VM_Warning("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4132                 return;
4133         }
4134         i = (int)PRVM_G_FLOAT(OFS_PARM1);
4135         if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
4136         {
4137                 VM_Warning("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME);
4138                 return;
4139         }
4140         dststringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4141         if(!dststringbuffer)
4142         {
4143                 VM_Warning("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
4144                 return;
4145         }
4146
4147         for (i = 0;i < dststringbuffer->num_strings;i++)
4148                 if (dststringbuffer->strings[i])
4149                         Mem_Free(dststringbuffer->strings[i]);
4150         if (dststringbuffer->strings)
4151                 Mem_Free(dststringbuffer->strings);
4152         *dststringbuffer = *srcstringbuffer;
4153         if (dststringbuffer->max_strings)
4154                 dststringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(dststringbuffer->strings[0]) * dststringbuffer->max_strings);
4155
4156         for (i = 0;i < dststringbuffer->num_strings;i++)
4157         {
4158                 if (srcstringbuffer->strings[i])
4159                 {
4160                         size_t stringlen;
4161                         stringlen = strlen(srcstringbuffer->strings[i]) + 1;
4162                         dststringbuffer->strings[i] = (char *)Mem_Alloc(prog->progs_mempool, stringlen);
4163                         memcpy(dststringbuffer->strings[i], srcstringbuffer->strings[i], stringlen);
4164                 }
4165         }
4166 }
4167
4168 /*
4169 ========================
4170 VM_buf_sort
4171 sort buffer by beginnings of strings (cmplength defaults it's length)
4172 "backward == TRUE" means that sorting goes upside-down
4173 void buf_sort(float bufhandle, float cmplength, float backward) = #464;
4174 ========================
4175 */
4176 void VM_buf_sort (void)
4177 {
4178         prvm_stringbuffer_t *stringbuffer;
4179         VM_SAFEPARMCOUNT(3, VM_buf_sort);
4180
4181         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4182         if(!stringbuffer)
4183         {
4184                 VM_Warning("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4185                 return;
4186         }
4187         if(stringbuffer->num_strings <= 0)
4188         {
4189                 VM_Warning("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4190                 return;
4191         }
4192         stringbuffers_sortlength = (int)PRVM_G_FLOAT(OFS_PARM1);
4193         if(stringbuffers_sortlength <= 0)
4194                 stringbuffers_sortlength = 0x7FFFFFFF;
4195
4196         if(!PRVM_G_FLOAT(OFS_PARM2))
4197                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsUP);
4198         else
4199                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
4200
4201         BufStr_Shrink(stringbuffer);
4202 }
4203
4204 /*
4205 ========================
4206 VM_buf_implode
4207 concantenates all buffer string into one with "glue" separator and returns it as tempstring
4208 string buf_implode(float bufhandle, string glue) = #465;
4209 ========================
4210 */
4211 void VM_buf_implode (void)
4212 {
4213         prvm_stringbuffer_t *stringbuffer;
4214         char                    k[VM_STRINGTEMP_LENGTH];
4215         const char              *sep;
4216         int                             i;
4217         size_t                  l;
4218         VM_SAFEPARMCOUNT(2, VM_buf_implode);
4219
4220         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4221         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4222         if(!stringbuffer)
4223         {
4224                 VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4225                 return;
4226         }
4227         if(!stringbuffer->num_strings)
4228                 return;
4229         sep = PRVM_G_STRING(OFS_PARM1);
4230         k[0] = 0;
4231         for(l = i = 0;i < stringbuffer->num_strings;i++)
4232         {
4233                 if(stringbuffer->strings[i])
4234                 {
4235                         l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
4236                         if (l >= sizeof(k) - 1)
4237                                 break;
4238                         strlcat(k, sep, sizeof(k));
4239                         strlcat(k, stringbuffer->strings[i], sizeof(k));
4240                 }
4241         }
4242         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(k);
4243 }
4244
4245 /*
4246 ========================
4247 VM_bufstr_get
4248 get a string from buffer, returns tempstring, dont str_unzone it!
4249 string bufstr_get(float bufhandle, float string_index) = #465;
4250 ========================
4251 */
4252 void VM_bufstr_get (void)
4253 {
4254         prvm_stringbuffer_t *stringbuffer;
4255         int                             strindex;
4256         VM_SAFEPARMCOUNT(2, VM_bufstr_get);
4257
4258         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4259         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4260         if(!stringbuffer)
4261         {
4262                 VM_Warning("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4263                 return;
4264         }
4265         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4266         if (strindex < 0)
4267         {
4268                 VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
4269                 return;
4270         }
4271         if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
4272                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(stringbuffer->strings[strindex]);
4273 }
4274
4275 /*
4276 ========================
4277 VM_bufstr_set
4278 copies a string into selected slot of buffer
4279 void bufstr_set(float bufhandle, float string_index, string str) = #466;
4280 ========================
4281 */
4282 void VM_bufstr_set (void)
4283 {
4284         int                             strindex;
4285         prvm_stringbuffer_t *stringbuffer;
4286         const char              *news;
4287
4288         VM_SAFEPARMCOUNT(3, VM_bufstr_set);
4289
4290         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4291         if(!stringbuffer)
4292         {
4293                 VM_Warning("VM_bufstr_set: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4294                 return;
4295         }
4296         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4297         if(strindex < 0 || strindex >= 1000000) // huge number of strings
4298         {
4299                 VM_Warning("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME);
4300                 return;
4301         }
4302
4303         BufStr_Expand(stringbuffer, strindex);
4304         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4305
4306         if(stringbuffer->strings[strindex])
4307                 Mem_Free(stringbuffer->strings[strindex]);
4308         stringbuffer->strings[strindex] = NULL;
4309
4310         news = PRVM_G_STRING(OFS_PARM2);
4311         if (news && news[0])
4312         {
4313                 size_t alloclen = strlen(news) + 1;
4314                 stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4315                 memcpy(stringbuffer->strings[strindex], news, alloclen);
4316         }
4317
4318         BufStr_Shrink(stringbuffer);
4319 }
4320
4321 /*
4322 ========================
4323 VM_bufstr_add
4324 adds string to buffer in first free slot and returns its index
4325 "order == TRUE" means that string will be added after last "full" slot
4326 float bufstr_add(float bufhandle, string str, float order) = #467;
4327 ========================
4328 */
4329 void VM_bufstr_add (void)
4330 {
4331         int                             order, strindex;
4332         prvm_stringbuffer_t *stringbuffer;
4333         const char              *string;
4334         size_t                  alloclen;
4335
4336         VM_SAFEPARMCOUNT(3, VM_bufstr_add);
4337
4338         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4339         PRVM_G_FLOAT(OFS_RETURN) = -1;
4340         if(!stringbuffer)
4341         {
4342                 VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4343                 return;
4344         }
4345         string = PRVM_G_STRING(OFS_PARM1);
4346         if(!string || !string[0])
4347         {
4348                 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);
4349                 return;
4350         }
4351         order = (int)PRVM_G_FLOAT(OFS_PARM2);
4352         if(order)
4353                 strindex = stringbuffer->num_strings;
4354         else
4355                 for (strindex = 0;strindex < stringbuffer->num_strings;strindex++)
4356                         if (stringbuffer->strings[strindex] == NULL)
4357                                 break;
4358
4359         BufStr_Expand(stringbuffer, strindex);
4360
4361         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4362         alloclen = strlen(string) + 1;
4363         stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4364         memcpy(stringbuffer->strings[strindex], string, alloclen);
4365
4366         PRVM_G_FLOAT(OFS_RETURN) = strindex;
4367 }
4368
4369 /*
4370 ========================
4371 VM_bufstr_free
4372 delete string from buffer
4373 void bufstr_free(float bufhandle, float string_index) = #468;
4374 ========================
4375 */
4376 void VM_bufstr_free (void)
4377 {
4378         int                             i;
4379         prvm_stringbuffer_t     *stringbuffer;
4380         VM_SAFEPARMCOUNT(2, VM_bufstr_free);
4381
4382         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4383         if(!stringbuffer)
4384         {
4385                 VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4386                 return;
4387         }
4388         i = (int)PRVM_G_FLOAT(OFS_PARM1);
4389         if(i < 0)
4390         {
4391                 VM_Warning("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME);
4392                 return;
4393         }
4394
4395         if (i < stringbuffer->num_strings)
4396         {
4397                 if(stringbuffer->strings[i])
4398                         Mem_Free(stringbuffer->strings[i]);
4399                 stringbuffer->strings[i] = NULL;
4400         }
4401
4402         BufStr_Shrink(stringbuffer);
4403 }
4404
4405
4406
4407
4408
4409
4410
4411 void VM_buf_cvarlist(void)
4412 {
4413         cvar_t *cvar;
4414         const char *partial, *antipartial;
4415         size_t len, antilen;
4416         size_t alloclen;
4417         qboolean ispattern, antiispattern;
4418         int n;
4419         prvm_stringbuffer_t     *stringbuffer;
4420         VM_SAFEPARMCOUNTRANGE(2, 3, VM_buf_cvarlist);
4421
4422         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4423         if(!stringbuffer)
4424         {
4425                 VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4426                 return;
4427         }
4428
4429         partial = PRVM_G_STRING(OFS_PARM1);
4430         if(!partial)
4431                 len = 0;
4432         else
4433                 len = strlen(partial);
4434
4435         if(prog->argc == 3)
4436                 antipartial = PRVM_G_STRING(OFS_PARM2);
4437         else
4438                 antipartial = NULL;
4439         if(!antipartial)
4440                 antilen = 0;
4441         else
4442                 antilen = strlen(antipartial);
4443         
4444         for (n = 0;n < stringbuffer->num_strings;n++)
4445                 if (stringbuffer->strings[n])
4446                         Mem_Free(stringbuffer->strings[n]);
4447         if (stringbuffer->strings)
4448                 Mem_Free(stringbuffer->strings);
4449         stringbuffer->strings = NULL;
4450
4451         ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
4452         antiispattern = antipartial && (strchr(antipartial, '*') || strchr(antipartial, '?'));
4453
4454         n = 0;
4455         for(cvar = cvar_vars; cvar; cvar = cvar->next)
4456         {
4457                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
4458                         continue;
4459
4460                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
4461                         continue;
4462
4463                 ++n;
4464         }
4465
4466         stringbuffer->max_strings = stringbuffer->num_strings = n;
4467         if (stringbuffer->max_strings)
4468                 stringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(stringbuffer->strings[0]) * stringbuffer->max_strings);
4469         
4470         n = 0;
4471         for(cvar = cvar_vars; cvar; cvar = cvar->next)
4472         {
4473                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
4474                         continue;
4475
4476                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
4477                         continue;
4478
4479                 alloclen = strlen(cvar->name) + 1;
4480                 stringbuffer->strings[n] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4481                 memcpy(stringbuffer->strings[n], cvar->name, alloclen);
4482
4483                 ++n;
4484         }
4485 }
4486
4487
4488
4489
4490 //=============
4491
4492 /*
4493 ==============
4494 VM_changeyaw
4495
4496 This was a major timewaster in progs, so it was converted to C
4497 ==============
4498 */
4499 void VM_changeyaw (void)
4500 {
4501         prvm_edict_t            *ent;
4502         float           ideal, current, move, speed;
4503
4504         // this is called (VERY HACKISHLY) by SV_MoveToGoal, so it can not use any
4505         // parameters because they are the parameters to SV_MoveToGoal, not this
4506         //VM_SAFEPARMCOUNT(0, VM_changeyaw);
4507
4508         ent = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict);
4509         if (ent == prog->edicts)
4510         {
4511                 VM_Warning("changeyaw: can not modify world entity\n");
4512                 return;
4513         }
4514         if (ent->priv.server->free)
4515         {
4516                 VM_Warning("changeyaw: can not modify free entity\n");
4517                 return;
4518         }
4519         if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.ideal_yaw < 0 || prog->fieldoffsets.yaw_speed < 0)
4520         {
4521                 VM_Warning("changeyaw: angles, ideal_yaw, or yaw_speed field(s) not found\n");
4522                 return;
4523         }
4524         current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1]);
4525         ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.ideal_yaw)->_float;
4526         speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.yaw_speed)->_float;
4527
4528         if (current == ideal)
4529                 return;
4530         move = ideal - current;
4531         if (ideal > current)
4532         {
4533                 if (move >= 180)
4534                         move = move - 360;
4535         }
4536         else
4537         {
4538                 if (move <= -180)
4539                         move = move + 360;
4540         }
4541         if (move > 0)
4542         {
4543                 if (move > speed)
4544                         move = speed;
4545         }
4546         else
4547         {
4548                 if (move < -speed)
4549                         move = -speed;
4550         }
4551
4552         PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1] = ANGLEMOD (current + move);
4553 }
4554
4555 /*
4556 ==============
4557 VM_changepitch
4558 ==============
4559 */
4560 void VM_changepitch (void)
4561 {
4562         prvm_edict_t            *ent;
4563         float           ideal, current, move, speed;
4564
4565         VM_SAFEPARMCOUNT(1, VM_changepitch);
4566
4567         ent = PRVM_G_EDICT(OFS_PARM0);
4568         if (ent == prog->edicts)
4569         {
4570                 VM_Warning("changepitch: can not modify world entity\n");
4571                 return;
4572         }
4573         if (ent->priv.server->free)
4574         {
4575                 VM_Warning("changepitch: can not modify free entity\n");
4576                 return;
4577         }
4578         if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.idealpitch < 0 || prog->fieldoffsets.pitch_speed < 0)
4579         {
4580                 VM_Warning("changepitch: angles, idealpitch, or pitch_speed field(s) not found\n");
4581                 return;
4582         }
4583         current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0]);
4584         ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.idealpitch)->_float;
4585         speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pitch_speed)->_float;
4586
4587         if (current == ideal)
4588                 return;
4589         move = ideal - current;
4590         if (ideal > current)
4591         {
4592                 if (move >= 180)
4593                         move = move - 360;
4594         }
4595         else
4596         {
4597                 if (move <= -180)
4598                         move = move + 360;
4599         }
4600         if (move > 0)
4601         {
4602                 if (move > speed)
4603                         move = speed;
4604         }
4605         else
4606         {
4607                 if (move < -speed)
4608                         move = -speed;
4609         }
4610
4611         PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0] = ANGLEMOD (current + move);
4612 }
4613
4614
4615 void VM_uncolorstring (void)
4616 {
4617         char szNewString[VM_STRINGTEMP_LENGTH];
4618         const char *szString;
4619
4620         // Prepare Strings
4621         VM_SAFEPARMCOUNT(1, VM_uncolorstring);
4622         szString = PRVM_G_STRING(OFS_PARM0);
4623         COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), TRUE);
4624         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
4625         
4626 }
4627
4628 // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
4629 //strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.
4630 void VM_strstrofs (void)
4631 {
4632         const char *instr, *match;
4633         int firstofs;
4634         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
4635         instr = PRVM_G_STRING(OFS_PARM0);
4636         match = PRVM_G_STRING(OFS_PARM1);
4637         firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
4638
4639         if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
4640         {
4641                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4642                 return;
4643         }
4644
4645         match = strstr(instr+firstofs, match);
4646         if (!match)
4647                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4648         else
4649                 PRVM_G_FLOAT(OFS_RETURN) = match - instr;
4650 }
4651
4652 //#222 string(string s, float index) str2chr (FTE_STRINGS)
4653 void VM_str2chr (void)
4654 {
4655         const char *s;
4656         VM_SAFEPARMCOUNT(2, VM_str2chr);
4657         s = PRVM_G_STRING(OFS_PARM0);
4658         if((unsigned)PRVM_G_FLOAT(OFS_PARM1) < strlen(s))
4659                 PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(unsigned)PRVM_G_FLOAT(OFS_PARM1)];
4660         else
4661                 PRVM_G_FLOAT(OFS_RETURN) = 0;
4662 }
4663
4664 //#223 string(float c, ...) chr2str (FTE_STRINGS)
4665 void VM_chr2str (void)
4666 {
4667         char    t[9];
4668         int             i;
4669         VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
4670         for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
4671                 t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
4672         t[i] = 0;
4673         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
4674 }
4675
4676 static int chrconv_number(int i, int base, int conv)
4677 {
4678         i -= base;
4679         switch (conv)
4680         {
4681         default:
4682         case 5:
4683         case 6:
4684         case 0:
4685                 break;
4686         case 1:
4687                 base = '0';
4688                 break;
4689         case 2:
4690                 base = '0'+128;
4691                 break;
4692         case 3:
4693                 base = '0'-30;
4694                 break;
4695         case 4:
4696                 base = '0'+128-30;
4697                 break;
4698         }
4699         return i + base;
4700 }
4701 static int chrconv_punct(int i, int base, int conv)
4702 {
4703         i -= base;
4704         switch (conv)
4705         {
4706         default:
4707         case 0:
4708                 break;
4709         case 1:
4710                 base = 0;
4711                 break;
4712         case 2:
4713                 base = 128;
4714                 break;
4715         }
4716         return i + base;
4717 }
4718
4719 static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
4720 {
4721         //convert case and colour seperatly...
4722
4723         i -= baset + basec;
4724         switch (convt)
4725         {
4726         default:
4727         case 0:
4728                 break;
4729         case 1:
4730                 baset = 0;
4731                 break;
4732         case 2:
4733                 baset = 128;
4734                 break;
4735
4736         case 5:
4737         case 6:
4738                 baset = 128*((charnum&1) == (convt-5));
4739                 break;
4740         }
4741
4742         switch (convc)
4743         {
4744         default:
4745         case 0:
4746                 break;
4747         case 1:
4748                 basec = 'a';
4749                 break;
4750         case 2:
4751                 basec = 'A';
4752                 break;
4753         }
4754         return i + basec + baset;
4755 }
4756 // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
4757 //bulk convert a string. change case or colouring.
4758 void VM_strconv (void)
4759 {
4760         int ccase, redalpha, rednum, len, i;
4761         unsigned char resbuf[VM_STRINGTEMP_LENGTH];
4762         unsigned char *result = resbuf;
4763
4764         VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
4765
4766         ccase = (int) PRVM_G_FLOAT(OFS_PARM0);  //0 same, 1 lower, 2 upper
4767         redalpha = (int) PRVM_G_FLOAT(OFS_PARM1);       //0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate
4768         rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
4769         VM_VarString(3, (char *) resbuf, sizeof(resbuf));
4770         len = strlen((char *) resbuf);
4771
4772         for (i = 0; i < len; i++, result++)     //should this be done backwards?
4773         {
4774                 if (*result >= '0' && *result <= '9')   //normal numbers...
4775                         *result = chrconv_number(*result, '0', rednum);
4776                 else if (*result >= '0'+128 && *result <= '9'+128)
4777                         *result = chrconv_number(*result, '0'+128, rednum);
4778                 else if (*result >= '0'+128-30 && *result <= '9'+128-30)
4779                         *result = chrconv_number(*result, '0'+128-30, rednum);
4780                 else if (*result >= '0'-30 && *result <= '9'-30)
4781                         *result = chrconv_number(*result, '0'-30, rednum);
4782
4783                 else if (*result >= 'a' && *result <= 'z')      //normal numbers...
4784                         *result = chrchar_alpha(*result, 'a', 0, ccase, redalpha, i);
4785                 else if (*result >= 'A' && *result <= 'Z')      //normal numbers...
4786                         *result = chrchar_alpha(*result, 'A', 0, ccase, redalpha, i);
4787                 else if (*result >= 'a'+128 && *result <= 'z'+128)      //normal numbers...
4788                         *result = chrchar_alpha(*result, 'a', 128, ccase, redalpha, i);
4789                 else if (*result >= 'A'+128 && *result <= 'Z'+128)      //normal numbers...
4790                         *result = chrchar_alpha(*result, 'A', 128, ccase, redalpha, i);
4791
4792                 else if ((*result & 127) < 16 || !redalpha)     //special chars..
4793                         *result = *result;
4794                 else if (*result < 128)
4795                         *result = chrconv_punct(*result, 0, redalpha);
4796                 else
4797                         *result = chrconv_punct(*result, 128, redalpha);
4798         }
4799         *result = '\0';
4800
4801         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString((char *) resbuf);
4802 }
4803
4804 // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
4805 void VM_strpad (void)
4806 {
4807         char src[VM_STRINGTEMP_LENGTH];
4808         char destbuf[VM_STRINGTEMP_LENGTH];
4809         int pad;
4810         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
4811         pad = (int) PRVM_G_FLOAT(OFS_PARM0);
4812         VM_VarString(1, src, sizeof(src));
4813
4814         // note: < 0 = left padding, > 0 = right padding,
4815         // this is reverse logic of printf!
4816         dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
4817
4818         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(destbuf);
4819 }
4820
4821 // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
4822 //uses qw style \key\value strings
4823 void VM_infoadd (void)
4824 {
4825         const char *info, *key;
4826         char value[VM_STRINGTEMP_LENGTH];
4827         char temp[VM_STRINGTEMP_LENGTH];
4828
4829         VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
4830         info = PRVM_G_STRING(OFS_PARM0);
4831         key = PRVM_G_STRING(OFS_PARM1);
4832         VM_VarString(2, value, sizeof(value));
4833
4834         strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
4835
4836         InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
4837
4838         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(temp);
4839 }
4840
4841 // #227 string(string info, string key) infoget (FTE_STRINGS)
4842 //uses qw style \key\value strings
4843 void VM_infoget (void)
4844 {
4845         const char *info;
4846         const char *key;
4847         char value[VM_STRINGTEMP_LENGTH];
4848
4849         VM_SAFEPARMCOUNT(2, VM_infoget);
4850         info = PRVM_G_STRING(OFS_PARM0);
4851         key = PRVM_G_STRING(OFS_PARM1);
4852
4853         InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
4854
4855         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(value);
4856 }
4857
4858 //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
4859 // also float(string s1, string s2) strcmp (FRIK_FILE)
4860 void VM_strncmp (void)
4861 {
4862         const char *s1, *s2;
4863         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncmp);
4864         s1 = PRVM_G_STRING(OFS_PARM0);
4865         s2 = PRVM_G_STRING(OFS_PARM1);
4866         if (prog->argc > 2)
4867         {
4868                 PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
4869         }
4870         else
4871         {
4872                 PRVM_G_FLOAT(OFS_RETURN) = strcmp(s1, s2);
4873         }
4874 }
4875
4876 // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
4877 // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
4878 void VM_strncasecmp (void)
4879 {
4880         const char *s1, *s2;
4881         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncasecmp);
4882         s1 = PRVM_G_STRING(OFS_PARM0);
4883         s2 = PRVM_G_STRING(OFS_PARM1);
4884         if (prog->argc > 2)
4885         {
4886                 PRVM_G_FLOAT(OFS_RETURN) = strncasecmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
4887         }
4888         else
4889         {
4890                 PRVM_G_FLOAT(OFS_RETURN) = strcasecmp(s1, s2);
4891         }
4892 }
4893
4894 // #494 float(float caseinsensitive, string s, ...) crc16
4895 void VM_crc16(void)
4896 {
4897         float insensitive;
4898         static char s[VM_STRINGTEMP_LENGTH];
4899         VM_SAFEPARMCOUNTRANGE(2, 8, VM_hash);
4900         insensitive = PRVM_G_FLOAT(OFS_PARM0);
4901         VM_VarString(1, s, sizeof(s));
4902         PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
4903 }
4904
4905 void VM_wasfreed (void)
4906 {
4907         VM_SAFEPARMCOUNT(1, VM_wasfreed);
4908         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICT(OFS_PARM0)->priv.required->free;
4909 }
4910
4911 void VM_SetTraceGlobals(const trace_t *trace)
4912 {
4913         prvm_eval_t *val;
4914         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_allsolid)))
4915                 val->_float = trace->allsolid;
4916         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_startsolid)))
4917                 val->_float = trace->startsolid;
4918         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_fraction)))
4919                 val->_float = trace->fraction;
4920         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inwater)))
4921                 val->_float = trace->inwater;
4922         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inopen)))
4923                 val->_float = trace->inopen;
4924         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos)))
4925                 VectorCopy(trace->endpos, val->vector);
4926         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_normal)))
4927                 VectorCopy(trace->plane.normal, val->vector);
4928         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_dist)))
4929                 val->_float = trace->plane.dist;
4930         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_ent)))
4931                 val->edict = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
4932         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
4933                 val->_float = trace->startsupercontents;
4934         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
4935                 val->_float = trace->hitsupercontents;
4936         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
4937                 val->_float = trace->hitq3surfaceflags;
4938         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
4939                 val->string = trace->hittexture ? PRVM_SetTempString(trace->hittexture->name) : 0;
4940 }
4941
4942 //=============
4943
4944 void VM_Cmd_Init(void)
4945 {
4946         // only init the stuff for the current prog
4947         VM_Files_Init();
4948         VM_Search_Init();
4949         VM_Gecko_Init();
4950 //      VM_BufStr_Init();
4951 }
4952
4953 void VM_Cmd_Reset(void)
4954 {
4955         CL_PurgeOwner( MENUOWNER );
4956         VM_Search_Reset();
4957         VM_Files_CloseAll();
4958         VM_Gecko_Destroy();
4959 //      VM_BufStr_ShutDown();
4960 }
4961
4962 // #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
4963 // does URI escaping on a string (replace evil stuff by %AB escapes)
4964 void VM_uri_escape (void)
4965 {
4966         char src[VM_STRINGTEMP_LENGTH];
4967         char dest[VM_STRINGTEMP_LENGTH];
4968         char *p, *q;
4969         static const char *hex = "0123456789ABCDEF";
4970
4971         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_escape);
4972         VM_VarString(0, src, sizeof(src));
4973
4974         for(p = src, q = dest; *p && q < dest + sizeof(dest) - 3; ++p)
4975         {
4976                 if((*p >= 'A' && *p <= 'Z')
4977                         || (*p >= 'a' && *p <= 'z')
4978                         || (*p >= '0' && *p <= '9')
4979                         || (*p == '-')  || (*p == '_') || (*p == '.')
4980                         || (*p == '!')  || (*p == '~') || (*p == '*')
4981                         || (*p == '\'') || (*p == '(') || (*p == ')'))
4982                         *q++ = *p;
4983                 else
4984                 {
4985                         *q++ = '%';
4986                         *q++ = hex[(*(unsigned char *)p >> 4) & 0xF];
4987                         *q++ = hex[ *(unsigned char *)p       & 0xF];
4988                 }
4989         }
4990         *q++ = 0;
4991
4992         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
4993 }
4994
4995 // #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
4996 // does URI unescaping on a string (get back the evil stuff)
4997 void VM_uri_unescape (void)
4998 {
4999         char src[VM_STRINGTEMP_LENGTH];
5000         char dest[VM_STRINGTEMP_LENGTH];
5001         char *p, *q;
5002         int hi, lo;
5003
5004         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_unescape);
5005         VM_VarString(0, src, sizeof(src));
5006
5007         for(p = src, q = dest; *p; ) // no need to check size, because unescape can't expand
5008         {
5009                 if(*p == '%')
5010                 {
5011                         if(p[1] >= '0' && p[1] <= '9')
5012                                 hi = p[1] - '0';
5013                         else if(p[1] >= 'a' && p[1] <= 'f')
5014                                 hi = p[1] - 'a' + 10;
5015                         else if(p[1] >= 'A' && p[1] <= 'F')
5016                                 hi = p[1] - 'A' + 10;
5017                         else
5018                                 goto nohex;
5019                         if(p[2] >= '0' && p[2] <= '9')
5020                                 lo = p[2] - '0';
5021                         else if(p[2] >= 'a' && p[2] <= 'f')
5022                                 lo = p[2] - 'a' + 10;
5023                         else if(p[2] >= 'A' && p[2] <= 'F')
5024                                 lo = p[2] - 'A' + 10;
5025                         else
5026                                 goto nohex;
5027                         if(hi != 0 || lo != 0) // don't unescape NUL bytes
5028                                 *q++ = (char) (hi * 0x10 + lo);
5029                         p += 3;
5030                         continue;
5031                 }
5032
5033 nohex:
5034                 // otherwise:
5035                 *q++ = *p++;
5036         }
5037         *q++ = 0;
5038
5039         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
5040 }
5041
5042 // #502 string(string filename) whichpack (DP_QC_WHICHPACK)
5043 // returns the name of the pack containing a file, or "" if it is not in any pack (but local or non-existant)
5044 void VM_whichpack (void)
5045 {
5046         const char *fn, *pack;
5047
5048         VM_SAFEPARMCOUNT(1, VM_whichpack);
5049         fn = PRVM_G_STRING(OFS_PARM0);
5050         pack = FS_WhichPack(fn);
5051
5052         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(pack ? pack : "");
5053 }
5054
5055 typedef struct
5056 {
5057         int prognr;
5058         double starttime;
5059         float id;
5060         char buffer[MAX_INPUTLINE];
5061 }
5062 uri_to_prog_t;
5063
5064 static void uri_to_string_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
5065 {
5066         uri_to_prog_t *handle = (uri_to_prog_t *) cbdata;
5067
5068         if(!PRVM_ProgLoaded(handle->prognr))
5069         {
5070                 // curl reply came too late... so just drop it
5071                 Z_Free(handle);
5072                 return;
5073         }
5074                 
5075         PRVM_SetProg(handle->prognr);
5076         PRVM_Begin;
5077                 if((prog->starttime == handle->starttime) && (prog->funcoffsets.URI_Get_Callback))
5078                 {
5079                         if(length_received >= sizeof(handle->buffer))
5080                                 length_received = sizeof(handle->buffer) - 1;
5081                         handle->buffer[length_received] = 0;
5082                 
5083                         PRVM_G_FLOAT(OFS_PARM0) = handle->id;
5084                         PRVM_G_FLOAT(OFS_PARM1) = status;
5085                         PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(handle->buffer);
5086                         PRVM_ExecuteProgram(prog->funcoffsets.URI_Get_Callback, "QC function URI_Get_Callback is missing");
5087                 }
5088         PRVM_End;
5089         
5090         Z_Free(handle);
5091 }
5092
5093 // 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
5094 // 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
5095 void VM_uri_get (void)
5096 {
5097         const char *url;
5098         float id;
5099         qboolean ret;
5100         uri_to_prog_t *handle;
5101
5102         if(!prog->funcoffsets.URI_Get_Callback)
5103                 PRVM_ERROR("uri_get called by %s without URI_Get_Callback defined", PRVM_NAME);
5104
5105         VM_SAFEPARMCOUNT(2, VM_uri_get);
5106
5107         url = PRVM_G_STRING(OFS_PARM0);
5108         id = PRVM_G_FLOAT(OFS_PARM1);
5109         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!
5110
5111         handle->prognr = PRVM_GetProgNr();
5112         handle->starttime = prog->starttime;
5113         handle->id = id;
5114         ret = Curl_Begin_ToMemory(url, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
5115         if(ret)
5116         {
5117                 PRVM_G_INT(OFS_RETURN) = 1;
5118         }
5119         else
5120         {
5121                 Z_Free(handle);
5122                 PRVM_G_INT(OFS_RETURN) = 0;
5123         }
5124 }
5125
5126 void VM_netaddress_resolve (void)
5127 {
5128         const char *ip;
5129         char normalized[128];
5130         int port;
5131         lhnetaddress_t addr;
5132
5133         VM_SAFEPARMCOUNTRANGE(1, 2, VM_netaddress_resolve);
5134
5135         ip = PRVM_G_STRING(OFS_PARM0);
5136         port = 0;
5137         if(prog->argc > 1)
5138                 port = (int) PRVM_G_FLOAT(OFS_PARM1);
5139
5140         if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
5141                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(normalized);
5142         else
5143                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
5144 }