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