]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_cmds.c
DP_QC_GETTIME
[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 extern double host_starttime;
2597 void VM_gettime(void)
2598 {
2599         int timer_index;
2600
2601         VM_SAFEPARMCOUNTRANGE(0,1,VM_gettime);
2602
2603         if(prog->argc == 0)
2604         {
2605                 PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
2606         }
2607         else
2608         {
2609                 timer_index = (int) PRVM_G_FLOAT(OFS_PARM0);
2610         switch(timer_index)
2611         {
2612             case 0: // GETTIME_FRAMESTART
2613                 PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
2614                 break;
2615             case 1: // GETTIME_REALTIME
2616                 PRVM_G_FLOAT(OFS_RETURN) = (float) Sys_DoubleTime();
2617                 break;
2618             case 2: // GETTIME_HIRES
2619                 PRVM_G_FLOAT(OFS_RETURN) = (float) (Sys_DoubleTime() - realtime);
2620                 break;
2621             case 3: // GETTIME_UPTIME
2622                 PRVM_G_FLOAT(OFS_RETURN) = (float) Sys_DoubleTime() - host_starttime;
2623                 break;
2624                         default:
2625                                 VM_Warning("VM_gettime: %s: unsupported timer specified, returning realtime\n", PRVM_NAME);
2626                                 PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
2627                                 break;
2628                 }
2629         }
2630 }
2631
2632 /*
2633 =========
2634 VM_loadfromdata
2635
2636 loadfromdata(string data)
2637 =========
2638 */
2639 void VM_loadfromdata(void)
2640 {
2641         VM_SAFEPARMCOUNT(1,VM_loadentsfromfile);
2642
2643         PRVM_ED_LoadFromFile(PRVM_G_STRING(OFS_PARM0));
2644 }
2645
2646 /*
2647 ========================
2648 VM_parseentitydata
2649
2650 parseentitydata(entity ent, string data)
2651 ========================
2652 */
2653 void VM_parseentitydata(void)
2654 {
2655         prvm_edict_t *ent;
2656         const char *data;
2657
2658         VM_SAFEPARMCOUNT(2, VM_parseentitydata);
2659
2660         // get edict and test it
2661         ent = PRVM_G_EDICT(OFS_PARM0);
2662         if (ent->priv.required->free)
2663                 PRVM_ERROR ("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
2664
2665         data = PRVM_G_STRING(OFS_PARM1);
2666
2667         // parse the opening brace
2668         if (!COM_ParseToken_Simple(&data, false, false) || com_token[0] != '{' )
2669                 PRVM_ERROR ("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", PRVM_NAME, data );
2670
2671         PRVM_ED_ParseEdict (data, ent);
2672 }
2673
2674 /*
2675 =========
2676 VM_loadfromfile
2677
2678 loadfromfile(string file)
2679 =========
2680 */
2681 void VM_loadfromfile(void)
2682 {
2683         const char *filename;
2684         char *data;
2685
2686         VM_SAFEPARMCOUNT(1,VM_loadfromfile);
2687
2688         filename = PRVM_G_STRING(OFS_PARM0);
2689         if (FS_CheckNastyPath(filename, false))
2690         {
2691                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2692                 VM_Warning("VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename);
2693                 return;
2694         }
2695
2696         // not conform with VM_fopen
2697         data = (char *)FS_LoadFile(filename, tempmempool, false, NULL);
2698         if (data == NULL)
2699                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2700
2701         PRVM_ED_LoadFromFile(data);
2702
2703         if(data)
2704                 Mem_Free(data);
2705 }
2706
2707
2708 /*
2709 =========
2710 VM_modulo
2711
2712 float   mod(float val, float m)
2713 =========
2714 */
2715 void VM_modulo(void)
2716 {
2717         int val, m;
2718         VM_SAFEPARMCOUNT(2,VM_module);
2719
2720         val = (int) PRVM_G_FLOAT(OFS_PARM0);
2721         m       = (int) PRVM_G_FLOAT(OFS_PARM1);
2722
2723         PRVM_G_FLOAT(OFS_RETURN) = (float) (val % m);
2724 }
2725
2726 void VM_Search_Init(void)
2727 {
2728         int i;
2729         for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++)
2730                 prog->opensearches[i] = NULL;
2731 }
2732
2733 void VM_Search_Reset(void)
2734 {
2735         int i;
2736         // reset the fssearch list
2737         for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++)
2738         {
2739                 if(prog->opensearches[i])
2740                         FS_FreeSearch(prog->opensearches[i]);
2741                 prog->opensearches[i] = NULL;
2742         }
2743 }
2744
2745 /*
2746 =========
2747 VM_search_begin
2748
2749 float search_begin(string pattern, float caseinsensitive, float quiet)
2750 =========
2751 */
2752 void VM_search_begin(void)
2753 {
2754         int handle;
2755         const char *pattern;
2756         int caseinsens, quiet;
2757
2758         VM_SAFEPARMCOUNT(3, VM_search_begin);
2759
2760         pattern = PRVM_G_STRING(OFS_PARM0);
2761
2762         VM_CheckEmptyString(pattern);
2763
2764         caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1);
2765         quiet = (int)PRVM_G_FLOAT(OFS_PARM2);
2766
2767         for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++)
2768                 if(!prog->opensearches[handle])
2769                         break;
2770
2771         if(handle >= PRVM_MAX_OPENSEARCHES)
2772         {
2773                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2774                 VM_Warning("VM_search_begin: %s ran out of search handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENSEARCHES);
2775                 return;
2776         }
2777
2778         if(!(prog->opensearches[handle] = FS_Search(pattern,caseinsens, quiet)))
2779                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2780         else
2781         {
2782                 prog->opensearches_origin[handle] = PRVM_AllocationOrigin();
2783                 PRVM_G_FLOAT(OFS_RETURN) = handle;
2784         }
2785 }
2786
2787 /*
2788 =========
2789 VM_search_end
2790
2791 void    search_end(float handle)
2792 =========
2793 */
2794 void VM_search_end(void)
2795 {
2796         int handle;
2797         VM_SAFEPARMCOUNT(1, VM_search_end);
2798
2799         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2800
2801         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2802         {
2803                 VM_Warning("VM_search_end: invalid handle %i used in %s\n", handle, PRVM_NAME);
2804                 return;
2805         }
2806         if(prog->opensearches[handle] == NULL)
2807         {
2808                 VM_Warning("VM_search_end: no such handle %i in %s\n", handle, PRVM_NAME);
2809                 return;
2810         }
2811
2812         FS_FreeSearch(prog->opensearches[handle]);
2813         prog->opensearches[handle] = NULL;
2814         if(prog->opensearches_origin[handle])
2815                 PRVM_Free((char *)prog->opensearches_origin[handle]);
2816 }
2817
2818 /*
2819 =========
2820 VM_search_getsize
2821
2822 float   search_getsize(float handle)
2823 =========
2824 */
2825 void VM_search_getsize(void)
2826 {
2827         int handle;
2828         VM_SAFEPARMCOUNT(1, VM_M_search_getsize);
2829
2830         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2831
2832         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2833         {
2834                 VM_Warning("VM_search_getsize: 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_getsize: no such handle %i in %s\n", handle, PRVM_NAME);
2840                 return;
2841         }
2842
2843         PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames;
2844 }
2845
2846 /*
2847 =========
2848 VM_search_getfilename
2849
2850 string  search_getfilename(float handle, float num)
2851 =========
2852 */
2853 void VM_search_getfilename(void)
2854 {
2855         int handle, filenum;
2856         VM_SAFEPARMCOUNT(2, VM_search_getfilename);
2857
2858         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2859         filenum = (int)PRVM_G_FLOAT(OFS_PARM1);
2860
2861         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2862         {
2863                 VM_Warning("VM_search_getfilename: invalid handle %i used in %s\n", handle, PRVM_NAME);
2864                 return;
2865         }
2866         if(prog->opensearches[handle] == NULL)
2867         {
2868                 VM_Warning("VM_search_getfilename: no such handle %i in %s\n", handle, PRVM_NAME);
2869                 return;
2870         }
2871         if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames)
2872         {
2873                 VM_Warning("VM_search_getfilename: invalid filenum %i in %s\n", filenum, PRVM_NAME);
2874                 return;
2875         }
2876
2877         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog->opensearches[handle]->filenames[filenum]);
2878 }
2879
2880 /*
2881 =========
2882 VM_chr
2883
2884 string  chr(float ascii)
2885 =========
2886 */
2887 void VM_chr(void)
2888 {
2889         char tmp[2];
2890         VM_SAFEPARMCOUNT(1, VM_chr);
2891
2892         tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
2893         tmp[1] = 0;
2894
2895         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
2896 }
2897
2898 //=============================================================================
2899 // Draw builtins (client & menu)
2900
2901 /*
2902 =========
2903 VM_iscachedpic
2904
2905 float   iscachedpic(string pic)
2906 =========
2907 */
2908 void VM_iscachedpic(void)
2909 {
2910         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
2911
2912         // drawq hasnt such a function, thus always return true
2913         PRVM_G_FLOAT(OFS_RETURN) = false;
2914 }
2915
2916 /*
2917 =========
2918 VM_precache_pic
2919
2920 string  precache_pic(string pic)
2921 =========
2922 */
2923 void VM_precache_pic(void)
2924 {
2925         const char      *s;
2926
2927         VM_SAFEPARMCOUNT(1, VM_precache_pic);
2928
2929         s = PRVM_G_STRING(OFS_PARM0);
2930         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
2931         VM_CheckEmptyString (s);
2932
2933         // AK Draw_CachePic is supposed to always return a valid pointer
2934         if( Draw_CachePic_Flags(s, CACHEPICFLAG_NOTPERSISTENT)->tex == r_texture_notexture )
2935                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2936 }
2937
2938 /*
2939 =========
2940 VM_freepic
2941
2942 freepic(string s)
2943 =========
2944 */
2945 void VM_freepic(void)
2946 {
2947         const char *s;
2948
2949         VM_SAFEPARMCOUNT(1,VM_freepic);
2950
2951         s = PRVM_G_STRING(OFS_PARM0);
2952         VM_CheckEmptyString (s);
2953
2954         Draw_FreePic(s);
2955 }
2956
2957 dp_font_t *getdrawfont()
2958 {
2959         if(prog->globaloffsets.drawfont >= 0)
2960         {
2961                 int f = (int) PRVM_G_FLOAT(prog->globaloffsets.drawfont);
2962                 if(f < 0 || f >= MAX_FONTS)
2963                         return FONT_DEFAULT;
2964                 return &dp_fonts[f];
2965         }
2966         else
2967                 return FONT_DEFAULT;
2968 }
2969
2970 /*
2971 =========
2972 VM_drawcharacter
2973
2974 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
2975 =========
2976 */
2977 void VM_drawcharacter(void)
2978 {
2979         float *pos,*scale,*rgb;
2980         char   character;
2981         int flag;
2982         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
2983
2984         character = (char) PRVM_G_FLOAT(OFS_PARM1);
2985         if(character == 0)
2986         {
2987                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2988                 VM_Warning("VM_drawcharacter: %s passed null character !\n",PRVM_NAME);
2989                 return;
2990         }
2991
2992         pos = PRVM_G_VECTOR(OFS_PARM0);
2993         scale = PRVM_G_VECTOR(OFS_PARM2);
2994         rgb = PRVM_G_VECTOR(OFS_PARM3);
2995         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2996
2997         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2998         {
2999                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3000                 VM_Warning("VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3001                 return;
3002         }
3003
3004         if(pos[2] || scale[2])
3005                 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")));
3006
3007         if(!scale[0] || !scale[1])
3008         {
3009                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3010                 VM_Warning("VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3011                 return;
3012         }
3013
3014         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());
3015         PRVM_G_FLOAT(OFS_RETURN) = 1;
3016 }
3017
3018 /*
3019 =========
3020 VM_drawstring
3021
3022 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
3023 =========
3024 */
3025 void VM_drawstring(void)
3026 {
3027         float *pos,*scale,*rgb;
3028         const char  *string;
3029         int flag;
3030         VM_SAFEPARMCOUNT(6,VM_drawstring);
3031
3032         string = PRVM_G_STRING(OFS_PARM1);
3033         pos = PRVM_G_VECTOR(OFS_PARM0);
3034         scale = PRVM_G_VECTOR(OFS_PARM2);
3035         rgb = PRVM_G_VECTOR(OFS_PARM3);
3036         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3037
3038         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3039         {
3040                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3041                 VM_Warning("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3042                 return;
3043         }
3044
3045         if(!scale[0] || !scale[1])
3046         {
3047                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3048                 VM_Warning("VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3049                 return;
3050         }
3051
3052         if(pos[2] || scale[2])
3053                 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")));
3054
3055         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());
3056         PRVM_G_FLOAT(OFS_RETURN) = 1;
3057 }
3058
3059 /*
3060 =========
3061 VM_drawcolorcodedstring
3062
3063 float   drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag)
3064 =========
3065 */
3066 void VM_drawcolorcodedstring(void)
3067 {
3068         float *pos,*scale;
3069         const char  *string;
3070         int flag,color;
3071         VM_SAFEPARMCOUNT(5,VM_drawstring);
3072
3073         string = PRVM_G_STRING(OFS_PARM1);
3074         pos = PRVM_G_VECTOR(OFS_PARM0);
3075         scale = PRVM_G_VECTOR(OFS_PARM2);
3076         flag = (int)PRVM_G_FLOAT(OFS_PARM4);
3077
3078         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3079         {
3080                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3081                 VM_Warning("VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3082                 return;
3083         }
3084
3085         if(!scale[0] || !scale[1])
3086         {
3087                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3088                 VM_Warning("VM_drawcolorcodedstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3089                 return;
3090         }
3091
3092         if(pos[2] || scale[2])
3093                 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")));
3094
3095         color = -1;
3096         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());
3097         PRVM_G_FLOAT(OFS_RETURN) = 1;
3098 }
3099 /*
3100 =========
3101 VM_stringwidth
3102
3103 float   stringwidth(string text, float allowColorCodes)
3104 =========
3105 */
3106 void VM_stringwidth(void)
3107 {
3108         const char  *string;
3109         int colors;
3110         VM_SAFEPARMCOUNT(2,VM_drawstring);
3111
3112         string = PRVM_G_STRING(OFS_PARM0);
3113         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
3114
3115         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font(string, 0, !colors, getdrawfont()); // 1x1 characters, don't actually draw
3116 }
3117 /*
3118 =========
3119 VM_drawpic
3120
3121 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
3122 =========
3123 */
3124 void VM_drawpic(void)
3125 {
3126         const char *picname;
3127         float *size, *pos, *rgb;
3128         int flag;
3129
3130         VM_SAFEPARMCOUNT(6,VM_drawpic);
3131
3132         picname = PRVM_G_STRING(OFS_PARM1);
3133         VM_CheckEmptyString (picname);
3134
3135         // is pic cached ? no function yet for that
3136         if(!1)
3137         {
3138                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3139                 VM_Warning("VM_drawpic: %s: %s not cached !\n", PRVM_NAME, picname);
3140                 return;
3141         }
3142
3143         pos = PRVM_G_VECTOR(OFS_PARM0);
3144         size = PRVM_G_VECTOR(OFS_PARM2);
3145         rgb = PRVM_G_VECTOR(OFS_PARM3);
3146         flag = (int) PRVM_G_FLOAT(OFS_PARM5);
3147
3148         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3149         {
3150                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3151                 VM_Warning("VM_drawpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3152                 return;
3153         }
3154
3155         if(pos[2] || size[2])
3156                 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")));
3157
3158         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);
3159         PRVM_G_FLOAT(OFS_RETURN) = 1;
3160 }
3161 /*
3162 =========
3163 VM_drawsubpic
3164
3165 float   drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
3166
3167 =========
3168 */
3169 void VM_drawsubpic(void)
3170 {
3171         const char *picname;
3172         float *size, *pos, *rgb, *srcPos, *srcSize, alpha;
3173         int flag;
3174
3175         VM_SAFEPARMCOUNT(8,VM_drawsubpic);
3176
3177         picname = PRVM_G_STRING(OFS_PARM2);
3178         VM_CheckEmptyString (picname);
3179
3180         // is pic cached ? no function yet for that
3181         if(!1)
3182         {
3183                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3184                 VM_Warning("VM_drawsubpic: %s: %s not cached !\n", PRVM_NAME, picname);
3185                 return;
3186         }
3187
3188         pos = PRVM_G_VECTOR(OFS_PARM0);
3189         size = PRVM_G_VECTOR(OFS_PARM1);
3190         srcPos = PRVM_G_VECTOR(OFS_PARM3);
3191         srcSize = PRVM_G_VECTOR(OFS_PARM4);
3192         rgb = PRVM_G_VECTOR(OFS_PARM5);
3193         alpha = PRVM_G_FLOAT(OFS_PARM6);
3194         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
3195
3196         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3197         {
3198                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3199                 VM_Warning("VM_drawsubpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3200                 return;
3201         }
3202
3203         if(pos[2] || size[2])
3204                 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")));
3205
3206         DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic (picname),
3207                 size[0], size[1],
3208                 srcPos[0],              srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3209                 srcPos[0] + srcSize[0], srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3210                 srcPos[0],              srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3211                 srcPos[0] + srcSize[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3212                 flag);
3213         PRVM_G_FLOAT(OFS_RETURN) = 1;
3214 }
3215
3216 /*
3217 =========
3218 VM_drawfill
3219
3220 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
3221 =========
3222 */
3223 void VM_drawfill(void)
3224 {
3225         float *size, *pos, *rgb;
3226         int flag;
3227
3228         VM_SAFEPARMCOUNT(5,VM_drawfill);
3229
3230
3231         pos = PRVM_G_VECTOR(OFS_PARM0);
3232         size = PRVM_G_VECTOR(OFS_PARM1);
3233         rgb = PRVM_G_VECTOR(OFS_PARM2);
3234         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
3235
3236         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3237         {
3238                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3239                 VM_Warning("VM_drawfill: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3240                 return;
3241         }
3242
3243         if(pos[2] || size[2])
3244                 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")));
3245
3246         DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
3247         PRVM_G_FLOAT(OFS_RETURN) = 1;
3248 }
3249
3250 /*
3251 =========
3252 VM_drawsetcliparea
3253
3254 drawsetcliparea(float x, float y, float width, float height)
3255 =========
3256 */
3257 void VM_drawsetcliparea(void)
3258 {
3259         float x,y,w,h;
3260         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
3261
3262         x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
3263         y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
3264         w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
3265         h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
3266
3267         DrawQ_SetClipArea(x, y, w, h);
3268 }
3269
3270 /*
3271 =========
3272 VM_drawresetcliparea
3273
3274 drawresetcliparea()
3275 =========
3276 */
3277 void VM_drawresetcliparea(void)
3278 {
3279         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
3280
3281         DrawQ_ResetClipArea();
3282 }
3283
3284 /*
3285 =========
3286 VM_getimagesize
3287
3288 vector  getimagesize(string pic)
3289 =========
3290 */
3291 void VM_getimagesize(void)
3292 {
3293         const char *p;
3294         cachepic_t *pic;
3295
3296         VM_SAFEPARMCOUNT(1,VM_getimagesize);
3297
3298         p = PRVM_G_STRING(OFS_PARM0);
3299         VM_CheckEmptyString (p);
3300
3301         pic = Draw_CachePic_Flags (p, CACHEPICFLAG_NOTPERSISTENT);
3302
3303         PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
3304         PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
3305         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3306 }
3307
3308 /*
3309 =========
3310 VM_keynumtostring
3311
3312 string keynumtostring(float keynum)
3313 =========
3314 */
3315 void VM_keynumtostring (void)
3316 {
3317         VM_SAFEPARMCOUNT(1, VM_keynumtostring);
3318
3319         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0)));
3320 }
3321
3322 /*
3323 =========
3324 VM_findkeysforcommand
3325
3326 string  findkeysforcommand(string command)
3327
3328 the returned string is an altstring
3329 =========
3330 */
3331 #define NUMKEYS 5 // TODO: merge the constant in keys.c with this one somewhen
3332
3333 void M_FindKeysForCommand(const char *command, int *keys);
3334 void VM_findkeysforcommand(void)
3335 {
3336         const char *cmd;
3337         char ret[VM_STRINGTEMP_LENGTH];
3338         int keys[NUMKEYS];
3339         int i;
3340
3341         VM_SAFEPARMCOUNT(1, VM_findkeysforcommand);
3342
3343         cmd = PRVM_G_STRING(OFS_PARM0);
3344
3345         VM_CheckEmptyString(cmd);
3346
3347         M_FindKeysForCommand(cmd, keys);
3348
3349         ret[0] = 0;
3350         for(i = 0; i < NUMKEYS; i++)
3351                 strlcat(ret, va(" \'%i\'", keys[i]), sizeof(ret));
3352
3353         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(ret);
3354 }
3355
3356 /*
3357 =========
3358 VM_stringtokeynum
3359
3360 float stringtokeynum(string key)
3361 =========
3362 */
3363 void VM_stringtokeynum (void)
3364 {
3365         VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
3366
3367         PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
3368 }
3369
3370 // CL_Video interface functions
3371
3372 /*
3373 ========================
3374 VM_cin_open
3375
3376 float cin_open(string file, string name)
3377 ========================
3378 */
3379 void VM_cin_open( void )
3380 {
3381         const char *file;
3382         const char *name;
3383
3384         VM_SAFEPARMCOUNT( 2, VM_cin_open );
3385
3386         file = PRVM_G_STRING( OFS_PARM0 );
3387         name = PRVM_G_STRING( OFS_PARM1 );
3388
3389         VM_CheckEmptyString( file );
3390     VM_CheckEmptyString( name );
3391
3392         if( CL_OpenVideo( file, name, MENUOWNER ) )
3393                 PRVM_G_FLOAT( OFS_RETURN ) = 1;
3394         else
3395                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3396 }
3397
3398 /*
3399 ========================
3400 VM_cin_close
3401
3402 void cin_close(string name)
3403 ========================
3404 */
3405 void VM_cin_close( void )
3406 {
3407         const char *name;
3408
3409         VM_SAFEPARMCOUNT( 1, VM_cin_close );
3410
3411         name = PRVM_G_STRING( OFS_PARM0 );
3412         VM_CheckEmptyString( name );
3413
3414         CL_CloseVideo( CL_GetVideoByName( name ) );
3415 }
3416
3417 /*
3418 ========================
3419 VM_cin_setstate
3420 void cin_setstate(string name, float type)
3421 ========================
3422 */
3423 void VM_cin_setstate( void )
3424 {
3425         const char *name;
3426         clvideostate_t  state;
3427         clvideo_t               *video;
3428
3429         VM_SAFEPARMCOUNT( 2, VM_cin_netstate );
3430
3431         name = PRVM_G_STRING( OFS_PARM0 );
3432         VM_CheckEmptyString( name );
3433
3434         state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
3435
3436         video = CL_GetVideoByName( name );
3437         if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
3438                 CL_SetVideoState( video, state );
3439 }
3440
3441 /*
3442 ========================
3443 VM_cin_getstate
3444
3445 float cin_getstate(string name)
3446 ========================
3447 */
3448 void VM_cin_getstate( void )
3449 {
3450         const char *name;
3451         clvideo_t               *video;
3452
3453         VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
3454
3455         name = PRVM_G_STRING( OFS_PARM0 );
3456         VM_CheckEmptyString( name );
3457
3458         video = CL_GetVideoByName( name );
3459         if( video )
3460                 PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
3461         else
3462                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3463 }
3464
3465 /*
3466 ========================
3467 VM_cin_restart
3468
3469 void cin_restart(string name)
3470 ========================
3471 */
3472 void VM_cin_restart( void )
3473 {
3474         const char *name;
3475         clvideo_t               *video;
3476
3477         VM_SAFEPARMCOUNT( 1, VM_cin_restart );
3478
3479         name = PRVM_G_STRING( OFS_PARM0 );
3480         VM_CheckEmptyString( name );
3481
3482         video = CL_GetVideoByName( name );
3483         if( video )
3484                 CL_RestartVideo( video );
3485 }
3486
3487 /*
3488 ========================
3489 VM_Gecko_Init
3490 ========================
3491 */
3492 void VM_Gecko_Init( void ) {
3493         // the prog struct is memset to 0 by Initprog? [12/6/2007 Black]
3494         // FIXME: remove the other _Init functions then, too? [12/6/2007 Black]
3495 }
3496
3497 /*
3498 ========================
3499 VM_Gecko_Destroy
3500 ========================
3501 */
3502 void VM_Gecko_Destroy( void ) {
3503         int i;
3504         for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
3505                 clgecko_t **instance = &prog->opengeckoinstances[ i ];
3506                 if( *instance ) {
3507                         CL_Gecko_DestroyBrowser( *instance );
3508                 }
3509                 *instance = NULL;
3510         }
3511 }
3512
3513 /*
3514 ========================
3515 VM_gecko_create
3516
3517 float[bool] gecko_create( string name )
3518 ========================
3519 */
3520 void VM_gecko_create( void ) {
3521         const char *name;
3522         int i;
3523         clgecko_t *instance;
3524         
3525         VM_SAFEPARMCOUNT( 1, VM_gecko_create );
3526
3527         name = PRVM_G_STRING( OFS_PARM0 );
3528         VM_CheckEmptyString( name );
3529
3530         // find an empty slot for this gecko browser..
3531         for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
3532                 if( prog->opengeckoinstances[ i ] == NULL ) {
3533                         break;
3534                 }
3535         }
3536         if( i == PRVM_MAX_GECKOINSTANCES ) {
3537                         VM_Warning("VM_gecko_create: %s ran out of gecko handles (%i)\n", PRVM_NAME, PRVM_MAX_GECKOINSTANCES);
3538                         PRVM_G_FLOAT( OFS_RETURN ) = 0;
3539                         return;
3540         }
3541
3542         instance = prog->opengeckoinstances[ i ] = CL_Gecko_CreateBrowser( name, PRVM_GetProgNr() );
3543    if( !instance ) {
3544                 // TODO: error handling [12/3/2007 Black]
3545                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3546                 return;
3547         }
3548         PRVM_G_FLOAT( OFS_RETURN ) = 1;
3549 }
3550
3551 /*
3552 ========================
3553 VM_gecko_destroy
3554
3555 void gecko_destroy( string name )
3556 ========================
3557 */
3558 void VM_gecko_destroy( void ) {
3559         const char *name;
3560         clgecko_t *instance;
3561
3562         VM_SAFEPARMCOUNT( 1, VM_gecko_destroy );
3563
3564         name = PRVM_G_STRING( OFS_PARM0 );
3565         VM_CheckEmptyString( name );
3566         instance = CL_Gecko_FindBrowser( name );
3567         if( !instance ) {
3568                 return;
3569         }
3570         CL_Gecko_DestroyBrowser( instance );
3571 }
3572
3573 /*
3574 ========================
3575 VM_gecko_navigate
3576
3577 void gecko_navigate( string name, string URI )
3578 ========================
3579 */
3580 void VM_gecko_navigate( void ) {
3581         const char *name;
3582         const char *URI;
3583         clgecko_t *instance;
3584
3585         VM_SAFEPARMCOUNT( 2, VM_gecko_navigate );
3586
3587         name = PRVM_G_STRING( OFS_PARM0 );
3588         URI = PRVM_G_STRING( OFS_PARM1 );
3589         VM_CheckEmptyString( name );
3590         VM_CheckEmptyString( URI );
3591
3592    instance = CL_Gecko_FindBrowser( name );
3593         if( !instance ) {
3594                 return;
3595         }
3596         CL_Gecko_NavigateToURI( instance, URI );
3597 }
3598
3599 /*
3600 ========================
3601 VM_gecko_keyevent
3602
3603 float[bool] gecko_keyevent( string name, float key, float eventtype ) 
3604 ========================
3605 */
3606 void VM_gecko_keyevent( void ) {
3607         const char *name;
3608         unsigned int key;
3609         clgecko_buttoneventtype_t eventtype;
3610         clgecko_t *instance;
3611
3612         VM_SAFEPARMCOUNT( 3, VM_gecko_keyevent );
3613
3614         name = PRVM_G_STRING( OFS_PARM0 );
3615         VM_CheckEmptyString( name );
3616         key = (unsigned int) PRVM_G_FLOAT( OFS_PARM1 );
3617         switch( (unsigned int) PRVM_G_FLOAT( OFS_PARM2 ) ) {
3618         case 0:
3619                 eventtype = CLG_BET_DOWN;
3620                 break;
3621         case 1:
3622                 eventtype = CLG_BET_UP;
3623                 break;
3624         case 2:
3625                 eventtype = CLG_BET_PRESS;
3626                 break;
3627         case 3:
3628                 eventtype = CLG_BET_DOUBLECLICK;
3629                 break;
3630         default:
3631                 // TODO: console printf? [12/3/2007 Black]
3632                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3633                 return;
3634         }
3635
3636         instance = CL_Gecko_FindBrowser( name );
3637         if( !instance ) {
3638                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3639                 return;
3640         }
3641
3642         PRVM_G_FLOAT( OFS_RETURN ) = (CL_Gecko_Event_Key( instance, (keynum_t) key, eventtype ) == true);
3643 }
3644
3645 /*
3646 ========================
3647 VM_gecko_movemouse
3648
3649 void gecko_mousemove( string name, float x, float y )
3650 ========================
3651 */
3652 void VM_gecko_movemouse( void ) {
3653         const char *name;
3654         float x, y;
3655         clgecko_t *instance;
3656
3657         VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
3658
3659         name = PRVM_G_STRING( OFS_PARM0 );
3660         VM_CheckEmptyString( name );
3661         x = PRVM_G_FLOAT( OFS_PARM1 );
3662         y = PRVM_G_FLOAT( OFS_PARM2 );
3663         
3664         instance = CL_Gecko_FindBrowser( name );
3665         if( !instance ) {
3666                 return;
3667         }
3668         CL_Gecko_Event_CursorMove( instance, x, y );
3669 }
3670
3671
3672 /*
3673 ========================
3674 VM_gecko_resize
3675
3676 void gecko_resize( string name, float w, float h )
3677 ========================
3678 */
3679 void VM_gecko_resize( void ) {
3680         const char *name;
3681         float w, h;
3682         clgecko_t *instance;
3683
3684         VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
3685
3686         name = PRVM_G_STRING( OFS_PARM0 );
3687         VM_CheckEmptyString( name );
3688         w = PRVM_G_FLOAT( OFS_PARM1 );
3689         h = PRVM_G_FLOAT( OFS_PARM2 );
3690         
3691         instance = CL_Gecko_FindBrowser( name );
3692         if( !instance ) {
3693                 return;
3694         }
3695         CL_Gecko_Resize( instance, (int) w, (int) h );
3696 }
3697
3698
3699 /*
3700 ========================
3701 VM_gecko_get_texture_extent
3702
3703 vector gecko_get_texture_extent( string name )
3704 ========================
3705 */
3706 void VM_gecko_get_texture_extent( void ) {
3707         const char *name;
3708         clgecko_t *instance;
3709
3710         VM_SAFEPARMCOUNT( 1, VM_gecko_movemouse );
3711
3712         name = PRVM_G_STRING( OFS_PARM0 );
3713         VM_CheckEmptyString( name );
3714         
3715         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3716         instance = CL_Gecko_FindBrowser( name );
3717         if( !instance ) {
3718                 PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
3719                 PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
3720                 return;
3721         }
3722         CL_Gecko_GetTextureExtent( instance, 
3723                 PRVM_G_VECTOR(OFS_RETURN), PRVM_G_VECTOR(OFS_RETURN)+1 );
3724 }
3725
3726
3727
3728 /*
3729 ==============
3730 VM_makevectors
3731
3732 Writes new values for v_forward, v_up, and v_right based on angles
3733 void makevectors(vector angle)
3734 ==============
3735 */
3736 void VM_makevectors (void)
3737 {
3738         prvm_eval_t *valforward, *valright, *valup;
3739         valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward);
3740         valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right);
3741         valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up);
3742         if (!valforward || !valright || !valup)
3743         {
3744                 VM_Warning("makevectors: could not find v_forward, v_right, or v_up global variables\n");
3745                 return;
3746         }
3747         VM_SAFEPARMCOUNT(1, VM_makevectors);
3748         AngleVectors (PRVM_G_VECTOR(OFS_PARM0), valforward->vector, valright->vector, valup->vector);
3749 }
3750
3751 /*
3752 ==============
3753 VM_vectorvectors
3754
3755 Writes new values for v_forward, v_up, and v_right based on the given forward vector
3756 vectorvectors(vector)
3757 ==============
3758 */
3759 void VM_vectorvectors (void)
3760 {
3761         prvm_eval_t *valforward, *valright, *valup;
3762         valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward);
3763         valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right);
3764         valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up);
3765         if (!valforward || !valright || !valup)
3766         {
3767                 VM_Warning("vectorvectors: could not find v_forward, v_right, or v_up global variables\n");
3768                 return;
3769         }
3770         VM_SAFEPARMCOUNT(1, VM_vectorvectors);
3771         VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), valforward->vector);
3772         VectorVectors(valforward->vector, valright->vector, valup->vector);
3773 }
3774
3775 /*
3776 ========================
3777 VM_drawline
3778
3779 void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
3780 ========================
3781 */
3782 void VM_drawline (void)
3783 {
3784         float   *c1, *c2, *rgb;
3785         float   alpha, width;
3786         unsigned char   flags;
3787
3788         VM_SAFEPARMCOUNT(6, VM_drawline);
3789         width   = PRVM_G_FLOAT(OFS_PARM0);
3790         c1              = PRVM_G_VECTOR(OFS_PARM1);
3791         c2              = PRVM_G_VECTOR(OFS_PARM2);
3792         rgb             = PRVM_G_VECTOR(OFS_PARM3);
3793         alpha   = PRVM_G_FLOAT(OFS_PARM4);
3794         flags   = (int)PRVM_G_FLOAT(OFS_PARM5);
3795         DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
3796 }
3797
3798 // float(float number, float quantity) bitshift (EXT_BITSHIFT)
3799 void VM_bitshift (void)
3800 {
3801         int n1, n2;
3802         VM_SAFEPARMCOUNT(2, VM_bitshift);
3803
3804         n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0));
3805         n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
3806         if(!n1)
3807                 PRVM_G_FLOAT(OFS_RETURN) = n1;
3808         else
3809         if(n2 < 0)
3810                 PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
3811         else
3812                 PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
3813 }
3814
3815 ////////////////////////////////////////
3816 // AltString functions
3817 ////////////////////////////////////////
3818
3819 /*
3820 ========================
3821 VM_altstr_count
3822
3823 float altstr_count(string)
3824 ========================
3825 */
3826 void VM_altstr_count( void )
3827 {
3828         const char *altstr, *pos;
3829         int     count;
3830
3831         VM_SAFEPARMCOUNT( 1, VM_altstr_count );
3832
3833         altstr = PRVM_G_STRING( OFS_PARM0 );
3834         //VM_CheckEmptyString( altstr );
3835
3836         for( count = 0, pos = altstr ; *pos ; pos++ ) {
3837                 if( *pos == '\\' ) {
3838                         if( !*++pos ) {
3839                                 break;
3840                         }
3841                 } else if( *pos == '\'' ) {
3842                         count++;
3843                 }
3844         }
3845
3846         PRVM_G_FLOAT( OFS_RETURN ) = (float) (count / 2);
3847 }
3848
3849 /*
3850 ========================
3851 VM_altstr_prepare
3852
3853 string altstr_prepare(string)
3854 ========================
3855 */
3856 void VM_altstr_prepare( void )
3857 {
3858         char *out;
3859         const char *instr, *in;
3860         int size;
3861         char outstr[VM_STRINGTEMP_LENGTH];
3862
3863         VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
3864
3865         instr = PRVM_G_STRING( OFS_PARM0 );
3866
3867         for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )
3868                 if( *in == '\'' ) {
3869                         *out++ = '\\';
3870                         *out = '\'';
3871                         size--;
3872                 } else
3873                         *out = *in;
3874         *out = 0;
3875
3876         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3877 }
3878
3879 /*
3880 ========================
3881 VM_altstr_get
3882
3883 string altstr_get(string, float)
3884 ========================
3885 */
3886 void VM_altstr_get( void )
3887 {
3888         const char *altstr, *pos;
3889         char *out;
3890         int count, size;
3891         char outstr[VM_STRINGTEMP_LENGTH];
3892
3893         VM_SAFEPARMCOUNT( 2, VM_altstr_get );
3894
3895         altstr = PRVM_G_STRING( OFS_PARM0 );
3896
3897         count = (int)PRVM_G_FLOAT( OFS_PARM1 );
3898         count = count * 2 + 1;
3899
3900         for( pos = altstr ; *pos && count ; pos++ )
3901                 if( *pos == '\\' ) {
3902                         if( !*++pos )
3903                                 break;
3904                 } else if( *pos == '\'' )
3905                         count--;
3906
3907         if( !*pos ) {
3908                 PRVM_G_INT( OFS_RETURN ) = 0;
3909                 return;
3910         }
3911
3912         for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
3913                 if( *pos == '\\' ) {
3914                         if( !*++pos )
3915                                 break;
3916                         *out = *pos;
3917                         size--;
3918                 } else if( *pos == '\'' )
3919                         break;
3920                 else
3921                         *out = *pos;
3922
3923         *out = 0;
3924         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3925 }
3926
3927 /*
3928 ========================
3929 VM_altstr_set
3930
3931 string altstr_set(string altstr, float num, string set)
3932 ========================
3933 */
3934 void VM_altstr_set( void )
3935 {
3936     int num;
3937         const char *altstr, *str;
3938         const char *in;
3939         char *out;
3940         char outstr[VM_STRINGTEMP_LENGTH];
3941
3942         VM_SAFEPARMCOUNT( 3, VM_altstr_set );
3943
3944         altstr = PRVM_G_STRING( OFS_PARM0 );
3945
3946         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3947
3948         str = PRVM_G_STRING( OFS_PARM2 );
3949
3950         out = outstr;
3951         for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
3952                 if( *in == '\\' ) {
3953                         if( !*++in ) {
3954                                 break;
3955                         }
3956                 } else if( *in == '\'' ) {
3957                         num--;
3958                 }
3959
3960         // copy set in
3961         for( ; *str; *out++ = *str++ );
3962         // now jump over the old content
3963         for( ; *in ; in++ )
3964                 if( *in == '\'' || (*in == '\\' && !*++in) )
3965                         break;
3966
3967         strlcpy(out, in, outstr + sizeof(outstr) - out);
3968         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3969 }
3970
3971 /*
3972 ========================
3973 VM_altstr_ins
3974 insert after num
3975 string  altstr_ins(string altstr, float num, string set)
3976 ========================
3977 */
3978 void VM_altstr_ins(void)
3979 {
3980         int num;
3981         const char *setstr;
3982         const char *set;
3983         const char *instr;
3984         const char *in;
3985         char *out;
3986         char outstr[VM_STRINGTEMP_LENGTH];
3987
3988         VM_SAFEPARMCOUNT(3, VM_altstr_ins);
3989
3990         in = instr = PRVM_G_STRING( OFS_PARM0 );
3991         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3992         set = setstr = PRVM_G_STRING( OFS_PARM2 );
3993
3994         out = outstr;
3995         for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
3996                 if( *in == '\\' ) {
3997                         if( !*++in ) {
3998                                 break;
3999                         }
4000                 } else if( *in == '\'' ) {
4001                         num--;
4002                 }
4003
4004         *out++ = '\'';
4005         for( ; *set ; *out++ = *set++ );
4006         *out++ = '\'';
4007
4008         strlcpy(out, in, outstr + sizeof(outstr) - out);
4009         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
4010 }
4011
4012
4013 ////////////////////////////////////////
4014 // BufString functions
4015 ////////////////////////////////////////
4016 //[515]: string buffers support
4017
4018 static size_t stringbuffers_sortlength;
4019
4020 static void BufStr_Expand(prvm_stringbuffer_t *stringbuffer, int strindex)
4021 {
4022         if (stringbuffer->max_strings <= strindex)
4023         {
4024                 char **oldstrings = stringbuffer->strings;
4025                 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
4026                 while (stringbuffer->max_strings <= strindex)
4027                         stringbuffer->max_strings *= 2;
4028                 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
4029                 if (stringbuffer->num_strings > 0)
4030                         memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
4031                 if (oldstrings)
4032                         Mem_Free(oldstrings);
4033         }
4034 }
4035
4036 static void BufStr_Shrink(prvm_stringbuffer_t *stringbuffer)
4037 {
4038         // reduce num_strings if there are empty string slots at the end
4039         while (stringbuffer->num_strings > 0 && stringbuffer->strings[stringbuffer->num_strings - 1] == NULL)
4040                 stringbuffer->num_strings--;
4041
4042         // if empty, free the string pointer array
4043         if (stringbuffer->num_strings == 0)
4044         {
4045                 stringbuffer->max_strings = 0;
4046                 if (stringbuffer->strings)
4047                         Mem_Free(stringbuffer->strings);
4048                 stringbuffer->strings = NULL;
4049         }
4050 }
4051
4052 static int BufStr_SortStringsUP (const void *in1, const void *in2)
4053 {
4054         const char *a, *b;
4055         a = *((const char **) in1);
4056         b = *((const char **) in2);
4057         if(!a[0])       return 1;
4058         if(!b[0])       return -1;
4059         return strncmp(a, b, stringbuffers_sortlength);
4060 }
4061
4062 static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
4063 {
4064         const char *a, *b;
4065         a = *((const char **) in1);
4066         b = *((const char **) in2);
4067         if(!a[0])       return 1;
4068         if(!b[0])       return -1;
4069         return strncmp(b, a, stringbuffers_sortlength);
4070 }
4071
4072 /*
4073 ========================
4074 VM_buf_create
4075 creates new buffer, and returns it's index, returns -1 if failed
4076 float buf_create(void) = #460;
4077 ========================
4078 */
4079 void VM_buf_create (void)
4080 {
4081         prvm_stringbuffer_t *stringbuffer;
4082         int i;
4083         VM_SAFEPARMCOUNT(0, VM_buf_create);
4084         stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
4085         for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
4086         stringbuffer->origin = PRVM_AllocationOrigin();
4087         PRVM_G_FLOAT(OFS_RETURN) = i;
4088 }
4089
4090 /*
4091 ========================
4092 VM_buf_del
4093 deletes buffer and all strings in it
4094 void buf_del(float bufhandle) = #461;
4095 ========================
4096 */
4097 void VM_buf_del (void)
4098 {
4099         prvm_stringbuffer_t *stringbuffer;
4100         VM_SAFEPARMCOUNT(1, VM_buf_del);
4101         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4102         if (stringbuffer)
4103         {
4104                 int i;
4105                 for (i = 0;i < stringbuffer->num_strings;i++)
4106                         if (stringbuffer->strings[i])
4107                                 Mem_Free(stringbuffer->strings[i]);
4108                 if (stringbuffer->strings)
4109                         Mem_Free(stringbuffer->strings);
4110                 if(stringbuffer->origin)
4111                         PRVM_Free((char *)stringbuffer->origin);
4112                 Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
4113         }
4114         else
4115         {
4116                 VM_Warning("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4117                 return;
4118         }
4119 }
4120
4121 /*
4122 ========================
4123 VM_buf_getsize
4124 how many strings are stored in buffer
4125 float buf_getsize(float bufhandle) = #462;
4126 ========================
4127 */
4128 void VM_buf_getsize (void)
4129 {
4130         prvm_stringbuffer_t *stringbuffer;
4131         VM_SAFEPARMCOUNT(1, VM_buf_getsize);
4132
4133         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4134         if(!stringbuffer)
4135         {
4136                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4137                 VM_Warning("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4138                 return;
4139         }
4140         else
4141                 PRVM_G_FLOAT(OFS_RETURN) = stringbuffer->num_strings;
4142 }
4143
4144 /*
4145 ========================
4146 VM_buf_copy
4147 copy all content from one buffer to another, make sure it exists
4148 void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
4149 ========================
4150 */
4151 void VM_buf_copy (void)
4152 {
4153         prvm_stringbuffer_t *srcstringbuffer, *dststringbuffer;
4154         int i;
4155         VM_SAFEPARMCOUNT(2, VM_buf_copy);
4156
4157         srcstringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4158         if(!srcstringbuffer)
4159         {
4160                 VM_Warning("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4161                 return;
4162         }
4163         i = (int)PRVM_G_FLOAT(OFS_PARM1);
4164         if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
4165         {
4166                 VM_Warning("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME);
4167                 return;
4168         }
4169         dststringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4170         if(!dststringbuffer)
4171         {
4172                 VM_Warning("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
4173                 return;
4174         }
4175
4176         for (i = 0;i < dststringbuffer->num_strings;i++)
4177                 if (dststringbuffer->strings[i])
4178                         Mem_Free(dststringbuffer->strings[i]);
4179         if (dststringbuffer->strings)
4180                 Mem_Free(dststringbuffer->strings);
4181         *dststringbuffer = *srcstringbuffer;
4182         if (dststringbuffer->max_strings)
4183                 dststringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(dststringbuffer->strings[0]) * dststringbuffer->max_strings);
4184
4185         for (i = 0;i < dststringbuffer->num_strings;i++)
4186         {
4187                 if (srcstringbuffer->strings[i])
4188                 {
4189                         size_t stringlen;
4190                         stringlen = strlen(srcstringbuffer->strings[i]) + 1;
4191                         dststringbuffer->strings[i] = (char *)Mem_Alloc(prog->progs_mempool, stringlen);
4192                         memcpy(dststringbuffer->strings[i], srcstringbuffer->strings[i], stringlen);
4193                 }
4194         }
4195 }
4196
4197 /*
4198 ========================
4199 VM_buf_sort
4200 sort buffer by beginnings of strings (cmplength defaults it's length)
4201 "backward == TRUE" means that sorting goes upside-down
4202 void buf_sort(float bufhandle, float cmplength, float backward) = #464;
4203 ========================
4204 */
4205 void VM_buf_sort (void)
4206 {
4207         prvm_stringbuffer_t *stringbuffer;
4208         VM_SAFEPARMCOUNT(3, VM_buf_sort);
4209
4210         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4211         if(!stringbuffer)
4212         {
4213                 VM_Warning("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4214                 return;
4215         }
4216         if(stringbuffer->num_strings <= 0)
4217         {
4218                 VM_Warning("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4219                 return;
4220         }
4221         stringbuffers_sortlength = (int)PRVM_G_FLOAT(OFS_PARM1);
4222         if(stringbuffers_sortlength <= 0)
4223                 stringbuffers_sortlength = 0x7FFFFFFF;
4224
4225         if(!PRVM_G_FLOAT(OFS_PARM2))
4226                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsUP);
4227         else
4228                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
4229
4230         BufStr_Shrink(stringbuffer);
4231 }
4232
4233 /*
4234 ========================
4235 VM_buf_implode
4236 concantenates all buffer string into one with "glue" separator and returns it as tempstring
4237 string buf_implode(float bufhandle, string glue) = #465;
4238 ========================
4239 */
4240 void VM_buf_implode (void)
4241 {
4242         prvm_stringbuffer_t *stringbuffer;
4243         char                    k[VM_STRINGTEMP_LENGTH];
4244         const char              *sep;
4245         int                             i;
4246         size_t                  l;
4247         VM_SAFEPARMCOUNT(2, VM_buf_implode);
4248
4249         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4250         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4251         if(!stringbuffer)
4252         {
4253                 VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4254                 return;
4255         }
4256         if(!stringbuffer->num_strings)
4257                 return;
4258         sep = PRVM_G_STRING(OFS_PARM1);
4259         k[0] = 0;
4260         for(l = i = 0;i < stringbuffer->num_strings;i++)
4261         {
4262                 if(stringbuffer->strings[i])
4263                 {
4264                         l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
4265                         if (l >= sizeof(k) - 1)
4266                                 break;
4267                         strlcat(k, sep, sizeof(k));
4268                         strlcat(k, stringbuffer->strings[i], sizeof(k));
4269                 }
4270         }
4271         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(k);
4272 }
4273
4274 /*
4275 ========================
4276 VM_bufstr_get
4277 get a string from buffer, returns tempstring, dont str_unzone it!
4278 string bufstr_get(float bufhandle, float string_index) = #465;
4279 ========================
4280 */
4281 void VM_bufstr_get (void)
4282 {
4283         prvm_stringbuffer_t *stringbuffer;
4284         int                             strindex;
4285         VM_SAFEPARMCOUNT(2, VM_bufstr_get);
4286
4287         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4288         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4289         if(!stringbuffer)
4290         {
4291                 VM_Warning("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4292                 return;
4293         }
4294         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4295         if (strindex < 0)
4296         {
4297                 VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
4298                 return;
4299         }
4300         if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
4301                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(stringbuffer->strings[strindex]);
4302 }
4303
4304 /*
4305 ========================
4306 VM_bufstr_set
4307 copies a string into selected slot of buffer
4308 void bufstr_set(float bufhandle, float string_index, string str) = #466;
4309 ========================
4310 */
4311 void VM_bufstr_set (void)
4312 {
4313         int                             strindex;
4314         prvm_stringbuffer_t *stringbuffer;
4315         const char              *news;
4316
4317         VM_SAFEPARMCOUNT(3, VM_bufstr_set);
4318
4319         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4320         if(!stringbuffer)
4321         {
4322                 VM_Warning("VM_bufstr_set: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4323                 return;
4324         }
4325         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4326         if(strindex < 0 || strindex >= 1000000) // huge number of strings
4327         {
4328                 VM_Warning("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME);
4329                 return;
4330         }
4331
4332         BufStr_Expand(stringbuffer, strindex);
4333         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4334
4335         if(stringbuffer->strings[strindex])
4336                 Mem_Free(stringbuffer->strings[strindex]);
4337         stringbuffer->strings[strindex] = NULL;
4338
4339         news = PRVM_G_STRING(OFS_PARM2);
4340         if (news && news[0])
4341         {
4342                 size_t alloclen = strlen(news) + 1;
4343                 stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4344                 memcpy(stringbuffer->strings[strindex], news, alloclen);
4345         }
4346
4347         BufStr_Shrink(stringbuffer);
4348 }
4349
4350 /*
4351 ========================
4352 VM_bufstr_add
4353 adds string to buffer in first free slot and returns its index
4354 "order == TRUE" means that string will be added after last "full" slot
4355 float bufstr_add(float bufhandle, string str, float order) = #467;
4356 ========================
4357 */
4358 void VM_bufstr_add (void)
4359 {
4360         int                             order, strindex;
4361         prvm_stringbuffer_t *stringbuffer;
4362         const char              *string;
4363         size_t                  alloclen;
4364
4365         VM_SAFEPARMCOUNT(3, VM_bufstr_add);
4366
4367         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4368         PRVM_G_FLOAT(OFS_RETURN) = -1;
4369         if(!stringbuffer)
4370         {
4371                 VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4372                 return;
4373         }
4374         string = PRVM_G_STRING(OFS_PARM1);
4375         if(!string || !string[0])
4376         {
4377                 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);
4378                 return;
4379         }
4380         order = (int)PRVM_G_FLOAT(OFS_PARM2);
4381         if(order)
4382                 strindex = stringbuffer->num_strings;
4383         else
4384                 for (strindex = 0;strindex < stringbuffer->num_strings;strindex++)
4385                         if (stringbuffer->strings[strindex] == NULL)
4386                                 break;
4387
4388         BufStr_Expand(stringbuffer, strindex);
4389
4390         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4391         alloclen = strlen(string) + 1;
4392         stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4393         memcpy(stringbuffer->strings[strindex], string, alloclen);
4394
4395         PRVM_G_FLOAT(OFS_RETURN) = strindex;
4396 }
4397
4398 /*
4399 ========================
4400 VM_bufstr_free
4401 delete string from buffer
4402 void bufstr_free(float bufhandle, float string_index) = #468;
4403 ========================
4404 */
4405 void VM_bufstr_free (void)
4406 {
4407         int                             i;
4408         prvm_stringbuffer_t     *stringbuffer;
4409         VM_SAFEPARMCOUNT(2, VM_bufstr_free);
4410
4411         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4412         if(!stringbuffer)
4413         {
4414                 VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4415                 return;
4416         }
4417         i = (int)PRVM_G_FLOAT(OFS_PARM1);
4418         if(i < 0)
4419         {
4420                 VM_Warning("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME);
4421                 return;
4422         }
4423
4424         if (i < stringbuffer->num_strings)
4425         {
4426                 if(stringbuffer->strings[i])
4427                         Mem_Free(stringbuffer->strings[i]);
4428                 stringbuffer->strings[i] = NULL;
4429         }
4430
4431         BufStr_Shrink(stringbuffer);
4432 }
4433
4434
4435
4436
4437
4438
4439
4440 void VM_buf_cvarlist(void)
4441 {
4442         cvar_t *cvar;
4443         const char *partial, *antipartial;
4444         size_t len, antilen;
4445         size_t alloclen;
4446         qboolean ispattern, antiispattern;
4447         int n;
4448         prvm_stringbuffer_t     *stringbuffer;
4449         VM_SAFEPARMCOUNTRANGE(2, 3, VM_buf_cvarlist);
4450
4451         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4452         if(!stringbuffer)
4453         {
4454                 VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4455                 return;
4456         }
4457
4458         partial = PRVM_G_STRING(OFS_PARM1);
4459         if(!partial)
4460                 len = 0;
4461         else
4462                 len = strlen(partial);
4463
4464         if(prog->argc == 3)
4465                 antipartial = PRVM_G_STRING(OFS_PARM2);
4466         else
4467                 antipartial = NULL;
4468         if(!antipartial)
4469                 antilen = 0;
4470         else
4471                 antilen = strlen(antipartial);
4472         
4473         for (n = 0;n < stringbuffer->num_strings;n++)
4474                 if (stringbuffer->strings[n])
4475                         Mem_Free(stringbuffer->strings[n]);
4476         if (stringbuffer->strings)
4477                 Mem_Free(stringbuffer->strings);
4478         stringbuffer->strings = NULL;
4479
4480         ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
4481         antiispattern = antipartial && (strchr(antipartial, '*') || strchr(antipartial, '?'));
4482
4483         n = 0;
4484         for(cvar = cvar_vars; cvar; cvar = cvar->next)
4485         {
4486                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
4487                         continue;
4488
4489                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
4490                         continue;
4491
4492                 ++n;
4493         }
4494
4495         stringbuffer->max_strings = stringbuffer->num_strings = n;
4496         if (stringbuffer->max_strings)
4497                 stringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(stringbuffer->strings[0]) * stringbuffer->max_strings);
4498         
4499         n = 0;
4500         for(cvar = cvar_vars; cvar; cvar = cvar->next)
4501         {
4502                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
4503                         continue;
4504
4505                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
4506                         continue;
4507
4508                 alloclen = strlen(cvar->name) + 1;
4509                 stringbuffer->strings[n] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4510                 memcpy(stringbuffer->strings[n], cvar->name, alloclen);
4511
4512                 ++n;
4513         }
4514 }
4515
4516
4517
4518
4519 //=============
4520
4521 /*
4522 ==============
4523 VM_changeyaw
4524
4525 This was a major timewaster in progs, so it was converted to C
4526 ==============
4527 */
4528 void VM_changeyaw (void)
4529 {
4530         prvm_edict_t            *ent;
4531         float           ideal, current, move, speed;
4532
4533         // this is called (VERY HACKISHLY) by SV_MoveToGoal, so it can not use any
4534         // parameters because they are the parameters to SV_MoveToGoal, not this
4535         //VM_SAFEPARMCOUNT(0, VM_changeyaw);
4536
4537         ent = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict);
4538         if (ent == prog->edicts)
4539         {
4540                 VM_Warning("changeyaw: can not modify world entity\n");
4541                 return;
4542         }
4543         if (ent->priv.server->free)
4544         {
4545                 VM_Warning("changeyaw: can not modify free entity\n");
4546                 return;
4547         }
4548         if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.ideal_yaw < 0 || prog->fieldoffsets.yaw_speed < 0)
4549         {
4550                 VM_Warning("changeyaw: angles, ideal_yaw, or yaw_speed field(s) not found\n");
4551                 return;
4552         }
4553         current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1]);
4554         ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.ideal_yaw)->_float;
4555         speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.yaw_speed)->_float;
4556
4557         if (current == ideal)
4558                 return;
4559         move = ideal - current;
4560         if (ideal > current)
4561         {
4562                 if (move >= 180)
4563                         move = move - 360;
4564         }
4565         else
4566         {
4567                 if (move <= -180)
4568                         move = move + 360;
4569         }
4570         if (move > 0)
4571         {
4572                 if (move > speed)
4573                         move = speed;
4574         }
4575         else
4576         {
4577                 if (move < -speed)
4578                         move = -speed;
4579         }
4580
4581         PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1] = ANGLEMOD (current + move);
4582 }
4583
4584 /*
4585 ==============
4586 VM_changepitch
4587 ==============
4588 */
4589 void VM_changepitch (void)
4590 {
4591         prvm_edict_t            *ent;
4592         float           ideal, current, move, speed;
4593
4594         VM_SAFEPARMCOUNT(1, VM_changepitch);
4595
4596         ent = PRVM_G_EDICT(OFS_PARM0);
4597         if (ent == prog->edicts)
4598         {
4599                 VM_Warning("changepitch: can not modify world entity\n");
4600                 return;
4601         }
4602         if (ent->priv.server->free)
4603         {
4604                 VM_Warning("changepitch: can not modify free entity\n");
4605                 return;
4606         }
4607         if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.idealpitch < 0 || prog->fieldoffsets.pitch_speed < 0)
4608         {
4609                 VM_Warning("changepitch: angles, idealpitch, or pitch_speed field(s) not found\n");
4610                 return;
4611         }
4612         current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0]);
4613         ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.idealpitch)->_float;
4614         speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pitch_speed)->_float;
4615
4616         if (current == ideal)
4617                 return;
4618         move = ideal - current;
4619         if (ideal > current)
4620         {
4621                 if (move >= 180)
4622                         move = move - 360;
4623         }
4624         else
4625         {
4626                 if (move <= -180)
4627                         move = move + 360;
4628         }
4629         if (move > 0)
4630         {
4631                 if (move > speed)
4632                         move = speed;
4633         }
4634         else
4635         {
4636                 if (move < -speed)
4637                         move = -speed;
4638         }
4639
4640         PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0] = ANGLEMOD (current + move);
4641 }
4642
4643
4644 void VM_uncolorstring (void)
4645 {
4646         char szNewString[VM_STRINGTEMP_LENGTH];
4647         const char *szString;
4648
4649         // Prepare Strings
4650         VM_SAFEPARMCOUNT(1, VM_uncolorstring);
4651         szString = PRVM_G_STRING(OFS_PARM0);
4652         COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), TRUE);
4653         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
4654         
4655 }
4656
4657 // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
4658 //strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.
4659 void VM_strstrofs (void)
4660 {
4661         const char *instr, *match;
4662         int firstofs;
4663         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
4664         instr = PRVM_G_STRING(OFS_PARM0);
4665         match = PRVM_G_STRING(OFS_PARM1);
4666         firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
4667
4668         if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
4669         {
4670                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4671                 return;
4672         }
4673
4674         match = strstr(instr+firstofs, match);
4675         if (!match)
4676                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4677         else
4678                 PRVM_G_FLOAT(OFS_RETURN) = match - instr;
4679 }
4680
4681 //#222 string(string s, float index) str2chr (FTE_STRINGS)
4682 void VM_str2chr (void)
4683 {
4684         const char *s;
4685         VM_SAFEPARMCOUNT(2, VM_str2chr);
4686         s = PRVM_G_STRING(OFS_PARM0);
4687         if((unsigned)PRVM_G_FLOAT(OFS_PARM1) < strlen(s))
4688                 PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(unsigned)PRVM_G_FLOAT(OFS_PARM1)];
4689         else
4690                 PRVM_G_FLOAT(OFS_RETURN) = 0;
4691 }
4692
4693 //#223 string(float c, ...) chr2str (FTE_STRINGS)
4694 void VM_chr2str (void)
4695 {
4696         char    t[9];
4697         int             i;
4698         VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
4699         for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
4700                 t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
4701         t[i] = 0;
4702         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
4703 }
4704
4705 static int chrconv_number(int i, int base, int conv)
4706 {
4707         i -= base;
4708         switch (conv)
4709         {
4710         default:
4711         case 5:
4712         case 6:
4713         case 0:
4714                 break;
4715         case 1:
4716                 base = '0';
4717                 break;
4718         case 2:
4719                 base = '0'+128;
4720                 break;
4721         case 3:
4722                 base = '0'-30;
4723                 break;
4724         case 4:
4725                 base = '0'+128-30;
4726                 break;
4727         }
4728         return i + base;
4729 }
4730 static int chrconv_punct(int i, int base, int conv)
4731 {
4732         i -= base;
4733         switch (conv)
4734         {
4735         default:
4736         case 0:
4737                 break;
4738         case 1:
4739                 base = 0;
4740                 break;
4741         case 2:
4742                 base = 128;
4743                 break;
4744         }
4745         return i + base;
4746 }
4747
4748 static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
4749 {
4750         //convert case and colour seperatly...
4751
4752         i -= baset + basec;
4753         switch (convt)
4754         {
4755         default:
4756         case 0:
4757                 break;
4758         case 1:
4759                 baset = 0;
4760                 break;
4761         case 2:
4762                 baset = 128;
4763                 break;
4764
4765         case 5:
4766         case 6:
4767                 baset = 128*((charnum&1) == (convt-5));
4768                 break;
4769         }
4770
4771         switch (convc)
4772         {
4773         default:
4774         case 0:
4775                 break;
4776         case 1:
4777                 basec = 'a';
4778                 break;
4779         case 2:
4780                 basec = 'A';
4781                 break;
4782         }
4783         return i + basec + baset;
4784 }
4785 // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
4786 //bulk convert a string. change case or colouring.
4787 void VM_strconv (void)
4788 {
4789         int ccase, redalpha, rednum, len, i;
4790         unsigned char resbuf[VM_STRINGTEMP_LENGTH];
4791         unsigned char *result = resbuf;
4792
4793         VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
4794
4795         ccase = (int) PRVM_G_FLOAT(OFS_PARM0);  //0 same, 1 lower, 2 upper
4796         redalpha = (int) PRVM_G_FLOAT(OFS_PARM1);       //0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate
4797         rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
4798         VM_VarString(3, (char *) resbuf, sizeof(resbuf));
4799         len = strlen((char *) resbuf);
4800
4801         for (i = 0; i < len; i++, result++)     //should this be done backwards?
4802         {
4803                 if (*result >= '0' && *result <= '9')   //normal numbers...
4804                         *result = chrconv_number(*result, '0', rednum);
4805                 else if (*result >= '0'+128 && *result <= '9'+128)
4806                         *result = chrconv_number(*result, '0'+128, rednum);
4807                 else if (*result >= '0'+128-30 && *result <= '9'+128-30)
4808                         *result = chrconv_number(*result, '0'+128-30, rednum);
4809                 else if (*result >= '0'-30 && *result <= '9'-30)
4810                         *result = chrconv_number(*result, '0'-30, rednum);
4811
4812                 else if (*result >= 'a' && *result <= 'z')      //normal numbers...
4813                         *result = chrchar_alpha(*result, 'a', 0, ccase, redalpha, i);
4814                 else if (*result >= 'A' && *result <= 'Z')      //normal numbers...
4815                         *result = chrchar_alpha(*result, 'A', 0, ccase, redalpha, i);
4816                 else if (*result >= 'a'+128 && *result <= 'z'+128)      //normal numbers...
4817                         *result = chrchar_alpha(*result, 'a', 128, ccase, redalpha, i);
4818                 else if (*result >= 'A'+128 && *result <= 'Z'+128)      //normal numbers...
4819                         *result = chrchar_alpha(*result, 'A', 128, ccase, redalpha, i);
4820
4821                 else if ((*result & 127) < 16 || !redalpha)     //special chars..
4822                         *result = *result;
4823                 else if (*result < 128)
4824                         *result = chrconv_punct(*result, 0, redalpha);
4825                 else
4826                         *result = chrconv_punct(*result, 128, redalpha);
4827         }
4828         *result = '\0';
4829
4830         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString((char *) resbuf);
4831 }
4832
4833 // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
4834 void VM_strpad (void)
4835 {
4836         char src[VM_STRINGTEMP_LENGTH];
4837         char destbuf[VM_STRINGTEMP_LENGTH];
4838         int pad;
4839         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
4840         pad = (int) PRVM_G_FLOAT(OFS_PARM0);
4841         VM_VarString(1, src, sizeof(src));
4842
4843         // note: < 0 = left padding, > 0 = right padding,
4844         // this is reverse logic of printf!
4845         dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
4846
4847         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(destbuf);
4848 }
4849
4850 // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
4851 //uses qw style \key\value strings
4852 void VM_infoadd (void)
4853 {
4854         const char *info, *key;
4855         char value[VM_STRINGTEMP_LENGTH];
4856         char temp[VM_STRINGTEMP_LENGTH];
4857
4858         VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
4859         info = PRVM_G_STRING(OFS_PARM0);
4860         key = PRVM_G_STRING(OFS_PARM1);
4861         VM_VarString(2, value, sizeof(value));
4862
4863         strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
4864
4865         InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
4866
4867         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(temp);
4868 }
4869
4870 // #227 string(string info, string key) infoget (FTE_STRINGS)
4871 //uses qw style \key\value strings
4872 void VM_infoget (void)
4873 {
4874         const char *info;
4875         const char *key;
4876         char value[VM_STRINGTEMP_LENGTH];
4877
4878         VM_SAFEPARMCOUNT(2, VM_infoget);
4879         info = PRVM_G_STRING(OFS_PARM0);
4880         key = PRVM_G_STRING(OFS_PARM1);
4881
4882         InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
4883
4884         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(value);
4885 }
4886
4887 //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
4888 // also float(string s1, string s2) strcmp (FRIK_FILE)
4889 void VM_strncmp (void)
4890 {
4891         const char *s1, *s2;
4892         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncmp);
4893         s1 = PRVM_G_STRING(OFS_PARM0);
4894         s2 = PRVM_G_STRING(OFS_PARM1);
4895         if (prog->argc > 2)
4896         {
4897                 PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
4898         }
4899         else
4900         {
4901                 PRVM_G_FLOAT(OFS_RETURN) = strcmp(s1, s2);
4902         }
4903 }
4904
4905 // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
4906 // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
4907 void VM_strncasecmp (void)
4908 {
4909         const char *s1, *s2;
4910         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncasecmp);
4911         s1 = PRVM_G_STRING(OFS_PARM0);
4912         s2 = PRVM_G_STRING(OFS_PARM1);
4913         if (prog->argc > 2)
4914         {
4915                 PRVM_G_FLOAT(OFS_RETURN) = strncasecmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
4916         }
4917         else
4918         {
4919                 PRVM_G_FLOAT(OFS_RETURN) = strcasecmp(s1, s2);
4920         }
4921 }
4922
4923 // #494 float(float caseinsensitive, string s, ...) crc16
4924 void VM_crc16(void)
4925 {
4926         float insensitive;
4927         static char s[VM_STRINGTEMP_LENGTH];
4928         VM_SAFEPARMCOUNTRANGE(2, 8, VM_hash);
4929         insensitive = PRVM_G_FLOAT(OFS_PARM0);
4930         VM_VarString(1, s, sizeof(s));
4931         PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
4932 }
4933
4934 void VM_wasfreed (void)
4935 {
4936         VM_SAFEPARMCOUNT(1, VM_wasfreed);
4937         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICT(OFS_PARM0)->priv.required->free;
4938 }
4939
4940 void VM_SetTraceGlobals(const trace_t *trace)
4941 {
4942         prvm_eval_t *val;
4943         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_allsolid)))
4944                 val->_float = trace->allsolid;
4945         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_startsolid)))
4946                 val->_float = trace->startsolid;
4947         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_fraction)))
4948                 val->_float = trace->fraction;
4949         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inwater)))
4950                 val->_float = trace->inwater;
4951         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inopen)))
4952                 val->_float = trace->inopen;
4953         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos)))
4954                 VectorCopy(trace->endpos, val->vector);
4955         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_normal)))
4956                 VectorCopy(trace->plane.normal, val->vector);
4957         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_dist)))
4958                 val->_float = trace->plane.dist;
4959         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_ent)))
4960                 val->edict = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
4961         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
4962                 val->_float = trace->startsupercontents;
4963         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
4964                 val->_float = trace->hitsupercontents;
4965         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
4966                 val->_float = trace->hitq3surfaceflags;
4967         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
4968                 val->string = trace->hittexture ? PRVM_SetTempString(trace->hittexture->name) : 0;
4969 }
4970
4971 //=============
4972
4973 void VM_Cmd_Init(void)
4974 {
4975         // only init the stuff for the current prog
4976         VM_Files_Init();
4977         VM_Search_Init();
4978         VM_Gecko_Init();
4979 //      VM_BufStr_Init();
4980 }
4981
4982 void VM_Cmd_Reset(void)
4983 {
4984         CL_PurgeOwner( MENUOWNER );
4985         VM_Search_Reset();
4986         VM_Files_CloseAll();
4987         VM_Gecko_Destroy();
4988 //      VM_BufStr_ShutDown();
4989 }
4990
4991 // #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
4992 // does URI escaping on a string (replace evil stuff by %AB escapes)
4993 void VM_uri_escape (void)
4994 {
4995         char src[VM_STRINGTEMP_LENGTH];
4996         char dest[VM_STRINGTEMP_LENGTH];
4997         char *p, *q;
4998         static const char *hex = "0123456789ABCDEF";
4999
5000         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_escape);
5001         VM_VarString(0, src, sizeof(src));
5002
5003         for(p = src, q = dest; *p && q < dest + sizeof(dest) - 3; ++p)
5004         {
5005                 if((*p >= 'A' && *p <= 'Z')
5006                         || (*p >= 'a' && *p <= 'z')
5007                         || (*p >= '0' && *p <= '9')
5008                         || (*p == '-')  || (*p == '_') || (*p == '.')
5009                         || (*p == '!')  || (*p == '~') || (*p == '*')
5010                         || (*p == '\'') || (*p == '(') || (*p == ')'))
5011                         *q++ = *p;
5012                 else
5013                 {
5014                         *q++ = '%';
5015                         *q++ = hex[(*(unsigned char *)p >> 4) & 0xF];
5016                         *q++ = hex[ *(unsigned char *)p       & 0xF];
5017                 }
5018         }
5019         *q++ = 0;
5020
5021         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
5022 }
5023
5024 // #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
5025 // does URI unescaping on a string (get back the evil stuff)
5026 void VM_uri_unescape (void)
5027 {
5028         char src[VM_STRINGTEMP_LENGTH];
5029         char dest[VM_STRINGTEMP_LENGTH];
5030         char *p, *q;
5031         int hi, lo;
5032
5033         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_unescape);
5034         VM_VarString(0, src, sizeof(src));
5035
5036         for(p = src, q = dest; *p; ) // no need to check size, because unescape can't expand
5037         {
5038                 if(*p == '%')
5039                 {
5040                         if(p[1] >= '0' && p[1] <= '9')
5041                                 hi = p[1] - '0';
5042                         else if(p[1] >= 'a' && p[1] <= 'f')
5043                                 hi = p[1] - 'a' + 10;
5044                         else if(p[1] >= 'A' && p[1] <= 'F')
5045                                 hi = p[1] - 'A' + 10;
5046                         else
5047                                 goto nohex;
5048                         if(p[2] >= '0' && p[2] <= '9')
5049                                 lo = p[2] - '0';
5050                         else if(p[2] >= 'a' && p[2] <= 'f')
5051                                 lo = p[2] - 'a' + 10;
5052                         else if(p[2] >= 'A' && p[2] <= 'F')
5053                                 lo = p[2] - 'A' + 10;
5054                         else
5055                                 goto nohex;
5056                         if(hi != 0 || lo != 0) // don't unescape NUL bytes
5057                                 *q++ = (char) (hi * 0x10 + lo);
5058                         p += 3;
5059                         continue;
5060                 }
5061
5062 nohex:
5063                 // otherwise:
5064                 *q++ = *p++;
5065         }
5066         *q++ = 0;
5067
5068         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
5069 }
5070
5071 // #502 string(string filename) whichpack (DP_QC_WHICHPACK)
5072 // returns the name of the pack containing a file, or "" if it is not in any pack (but local or non-existant)
5073 void VM_whichpack (void)
5074 {
5075         const char *fn, *pack;
5076
5077         VM_SAFEPARMCOUNT(1, VM_whichpack);
5078         fn = PRVM_G_STRING(OFS_PARM0);
5079         pack = FS_WhichPack(fn);
5080
5081         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(pack ? pack : "");
5082 }
5083
5084 typedef struct
5085 {
5086         int prognr;
5087         double starttime;
5088         float id;
5089         char buffer[MAX_INPUTLINE];
5090 }
5091 uri_to_prog_t;
5092
5093 static void uri_to_string_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
5094 {
5095         uri_to_prog_t *handle = (uri_to_prog_t *) cbdata;
5096
5097         if(!PRVM_ProgLoaded(handle->prognr))
5098         {
5099                 // curl reply came too late... so just drop it
5100                 Z_Free(handle);
5101                 return;
5102         }
5103                 
5104         PRVM_SetProg(handle->prognr);
5105         PRVM_Begin;
5106                 if((prog->starttime == handle->starttime) && (prog->funcoffsets.URI_Get_Callback))
5107                 {
5108                         if(length_received >= sizeof(handle->buffer))
5109                                 length_received = sizeof(handle->buffer) - 1;
5110                         handle->buffer[length_received] = 0;
5111                 
5112                         PRVM_G_FLOAT(OFS_PARM0) = handle->id;
5113                         PRVM_G_FLOAT(OFS_PARM1) = status;
5114                         PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(handle->buffer);
5115                         PRVM_ExecuteProgram(prog->funcoffsets.URI_Get_Callback, "QC function URI_Get_Callback is missing");
5116                 }
5117         PRVM_End;
5118         
5119         Z_Free(handle);
5120 }
5121
5122 // 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
5123 // 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
5124 void VM_uri_get (void)
5125 {
5126         const char *url;
5127         float id;
5128         qboolean ret;
5129         uri_to_prog_t *handle;
5130
5131         if(!prog->funcoffsets.URI_Get_Callback)
5132                 PRVM_ERROR("uri_get called by %s without URI_Get_Callback defined", PRVM_NAME);
5133
5134         VM_SAFEPARMCOUNT(2, VM_uri_get);
5135
5136         url = PRVM_G_STRING(OFS_PARM0);
5137         id = PRVM_G_FLOAT(OFS_PARM1);
5138         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!
5139
5140         handle->prognr = PRVM_GetProgNr();
5141         handle->starttime = prog->starttime;
5142         handle->id = id;
5143         ret = Curl_Begin_ToMemory(url, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
5144         if(ret)
5145         {
5146                 PRVM_G_INT(OFS_RETURN) = 1;
5147         }
5148         else
5149         {
5150                 Z_Free(handle);
5151                 PRVM_G_INT(OFS_RETURN) = 0;
5152         }
5153 }
5154
5155 void VM_netaddress_resolve (void)
5156 {
5157         const char *ip;
5158         char normalized[128];
5159         int port;
5160         lhnetaddress_t addr;
5161
5162         VM_SAFEPARMCOUNTRANGE(1, 2, VM_netaddress_resolve);
5163
5164         ip = PRVM_G_STRING(OFS_PARM0);
5165         port = 0;
5166         if(prog->argc > 1)
5167                 port = (int) PRVM_G_FLOAT(OFS_PARM1);
5168
5169         if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
5170                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(normalized);
5171         else
5172                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
5173 }