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