]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_cmds.c
tracked down another size check div0 left in
[divverent/darkplaces.git] / prvm_cmds.c
1 // AK
2 // Basically every vm builtin cmd should be in here.
3 // All 3 builtin and extension lists can be found here
4 // cause large (I think they will) parts are from pr_cmds the same copyright like in pr_cmds
5 // also applies here
6
7 #include "quakedef.h"
8
9 #include "prvm_cmds.h"
10 #include <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         }
1623 }
1624
1625 /*
1626 =========
1627 VM_fclose
1628
1629 fclose(float fhandle)
1630 =========
1631 */
1632 //void(float fhandle) fclose = #111; // closes a file
1633 void VM_fclose(void)
1634 {
1635         int filenum;
1636
1637         VM_SAFEPARMCOUNT(1,VM_fclose);
1638
1639         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1640         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1641         {
1642                 VM_Warning("VM_fclose: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1643                 return;
1644         }
1645         if (prog->openfiles[filenum] == NULL)
1646         {
1647                 VM_Warning("VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1648                 return;
1649         }
1650         FS_Close(prog->openfiles[filenum]);
1651         prog->openfiles[filenum] = NULL;
1652         if (developer.integer >= 100)
1653                 Con_Printf("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum);
1654 }
1655
1656 /*
1657 =========
1658 VM_fgets
1659
1660 string  fgets(float fhandle)
1661 =========
1662 */
1663 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
1664 void VM_fgets(void)
1665 {
1666         int c, end;
1667         char string[VM_STRINGTEMP_LENGTH];
1668         int filenum;
1669
1670         VM_SAFEPARMCOUNT(1,VM_fgets);
1671
1672         // set the return value regardless of any possible errors
1673         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1674
1675         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1676         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1677         {
1678                 VM_Warning("VM_fgets: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1679                 return;
1680         }
1681         if (prog->openfiles[filenum] == NULL)
1682         {
1683                 VM_Warning("VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1684                 return;
1685         }
1686         end = 0;
1687         for (;;)
1688         {
1689                 c = FS_Getc(prog->openfiles[filenum]);
1690                 if (c == '\r' || c == '\n' || c < 0)
1691                         break;
1692                 if (end < VM_STRINGTEMP_LENGTH - 1)
1693                         string[end++] = c;
1694         }
1695         string[end] = 0;
1696         // remove \n following \r
1697         if (c == '\r')
1698         {
1699                 c = FS_Getc(prog->openfiles[filenum]);
1700                 if (c != '\n')
1701                         FS_UnGetc(prog->openfiles[filenum], (unsigned char)c);
1702         }
1703         if (developer.integer >= 100)
1704                 Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
1705         if (c >= 0 || end)
1706                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
1707 }
1708
1709 /*
1710 =========
1711 VM_fputs
1712
1713 fputs(float fhandle, string s)
1714 =========
1715 */
1716 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
1717 void VM_fputs(void)
1718 {
1719         int stringlength;
1720         char string[VM_STRINGTEMP_LENGTH];
1721         int filenum;
1722
1723         VM_SAFEPARMCOUNT(2,VM_fputs);
1724
1725         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1726         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1727         {
1728                 VM_Warning("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1729                 return;
1730         }
1731         if (prog->openfiles[filenum] == NULL)
1732         {
1733                 VM_Warning("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1734                 return;
1735         }
1736         VM_VarString(1, string, sizeof(string));
1737         if ((stringlength = (int)strlen(string)))
1738                 FS_Write(prog->openfiles[filenum], string, stringlength);
1739         if (developer.integer >= 100)
1740                 Con_Printf("fputs: %s: %s\n", PRVM_NAME, string);
1741 }
1742
1743 /*
1744 =========
1745 VM_writetofile
1746
1747         writetofile(float fhandle, entity ent)
1748 =========
1749 */
1750 void VM_writetofile(void)
1751 {
1752         prvm_edict_t * ent;
1753         qfile_t *file;
1754
1755         VM_SAFEPARMCOUNT(2, VM_writetofile);
1756
1757         file = VM_GetFileHandle( (int)PRVM_G_FLOAT(OFS_PARM0) );
1758         if( !file )
1759         {
1760                 VM_Warning("VM_writetofile: invalid or closed file handle\n");
1761                 return;
1762         }
1763
1764         ent = PRVM_G_EDICT(OFS_PARM1);
1765         if(ent->priv.required->free)
1766         {
1767                 VM_Warning("VM_writetofile: %s: entity %i is free !\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
1768                 return;
1769         }
1770
1771         PRVM_ED_Write (file, ent);
1772 }
1773
1774 // KrimZon - DP_QC_ENTITYDATA
1775 /*
1776 =========
1777 VM_numentityfields
1778
1779 float() numentityfields
1780 Return the number of entity fields - NOT offsets
1781 =========
1782 */
1783 void VM_numentityfields(void)
1784 {
1785         PRVM_G_FLOAT(OFS_RETURN) = prog->progs->numfielddefs;
1786 }
1787
1788 // KrimZon - DP_QC_ENTITYDATA
1789 /*
1790 =========
1791 VM_entityfieldname
1792
1793 string(float fieldnum) entityfieldname
1794 Return name of the specified field as a string, or empty if the field is invalid (warning)
1795 =========
1796 */
1797 void VM_entityfieldname(void)
1798 {
1799         ddef_t *d;
1800         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1801         
1802         if (i < 0 || i >= prog->progs->numfielddefs)
1803         {
1804         VM_Warning("VM_entityfieldname: %s: field index out of bounds\n", PRVM_NAME);
1805         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1806                 return;
1807         }
1808         
1809         d = &prog->fielddefs[i];
1810         PRVM_G_INT(OFS_RETURN) = d->s_name; // presuming that s_name points to a string already
1811 }
1812
1813 // KrimZon - DP_QC_ENTITYDATA
1814 /*
1815 =========
1816 VM_entityfieldtype
1817
1818 float(float fieldnum) entityfieldtype
1819 =========
1820 */
1821 void VM_entityfieldtype(void)
1822 {
1823         ddef_t *d;
1824         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1825         
1826         if (i < 0 || i >= prog->progs->numfielddefs)
1827         {
1828                 VM_Warning("VM_entityfieldtype: %s: field index out of bounds\n", PRVM_NAME);
1829                 PRVM_G_FLOAT(OFS_RETURN) = -1.0;
1830                 return;
1831         }
1832         
1833         d = &prog->fielddefs[i];
1834         PRVM_G_FLOAT(OFS_RETURN) = (float)d->type;
1835 }
1836
1837 // KrimZon - DP_QC_ENTITYDATA
1838 /*
1839 =========
1840 VM_getentityfieldstring
1841
1842 string(float fieldnum, entity ent) getentityfieldstring
1843 =========
1844 */
1845 void VM_getentityfieldstring(void)
1846 {
1847         // put the data into a string
1848         ddef_t *d;
1849         int type, j;
1850         int *v;
1851         prvm_edict_t * ent;
1852         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1853         
1854         if (i < 0 || i >= prog->progs->numfielddefs)
1855         {
1856         VM_Warning("VM_entityfielddata: %s: field index out of bounds\n", PRVM_NAME);
1857                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1858                 return;
1859         }
1860         
1861         d = &prog->fielddefs[i];
1862         
1863         // get the entity
1864         ent = PRVM_G_EDICT(OFS_PARM1);
1865         if(ent->priv.required->free)
1866         {
1867                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1868                 VM_Warning("VM_entityfielddata: %s: entity %i is free !\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
1869                 return;
1870         }
1871         v = (int *)((char *)ent->fields.vp + d->ofs*4);
1872         
1873         // if it's 0 or blank, return an empty string
1874         type = d->type & ~DEF_SAVEGLOBAL;
1875         for (j=0 ; j<prvm_type_size[type] ; j++)
1876                 if (v[j])
1877                         break;
1878         if (j == prvm_type_size[type])
1879         {
1880                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
1881                 return;
1882         }
1883                 
1884         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
1885 }
1886
1887 // KrimZon - DP_QC_ENTITYDATA
1888 /*
1889 =========
1890 VM_putentityfieldstring
1891
1892 float(float fieldnum, entity ent, string s) putentityfieldstring
1893 =========
1894 */
1895 void VM_putentityfieldstring(void)
1896 {
1897         ddef_t *d;
1898         prvm_edict_t * ent;
1899         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
1900
1901         if (i < 0 || i >= prog->progs->numfielddefs)
1902         {
1903         VM_Warning("VM_entityfielddata: %s: field index out of bounds\n", PRVM_NAME);
1904                 PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
1905                 return;
1906         }
1907
1908         d = &prog->fielddefs[i];
1909
1910         // get the entity
1911         ent = PRVM_G_EDICT(OFS_PARM1);
1912         if(ent->priv.required->free)
1913         {
1914                 VM_Warning("VM_entityfielddata: %s: entity %i is free !\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
1915                 PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
1916                 return;
1917         }
1918
1919         // parse the string into the value
1920         PRVM_G_FLOAT(OFS_RETURN) = ( PRVM_ED_ParseEpair(ent, d, PRVM_G_STRING(OFS_PARM2)) ) ? 1.0f : 0.0f;
1921 }
1922
1923 /*
1924 =========
1925 VM_strlen
1926
1927 float   strlen(string s)
1928 =========
1929 */
1930 //float(string s) strlen = #114; // returns how many characters are in a string
1931 void VM_strlen(void)
1932 {
1933         VM_SAFEPARMCOUNT(1,VM_strlen);
1934
1935         PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
1936 }
1937
1938 // DRESK - Decolorized String
1939 /*
1940 =========
1941 VM_strdecolorize
1942
1943 string  strdecolorize(string s)
1944 =========
1945 */
1946 // string (string s) strdecolorize = #472; // returns the passed in string with color codes stripped
1947 void VM_strdecolorize(void)
1948 {
1949         char szNewString[VM_STRINGTEMP_LENGTH];
1950         const char *szString;
1951
1952         // Prepare Strings
1953         VM_SAFEPARMCOUNT(1,VM_strdecolorize);
1954         szString = PRVM_G_STRING(OFS_PARM0);
1955
1956         COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), TRUE);
1957
1958         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
1959 }
1960
1961 // DRESK - String Length (not counting color codes)
1962 /*
1963 =========
1964 VM_strlennocol
1965
1966 float   strlennocol(string s)
1967 =========
1968 */
1969 // float(string s) strlennocol = #471; // returns how many characters are in a string not including color codes
1970 // For example, ^2Dresk returns a length of 5
1971 void VM_strlennocol(void)
1972 {
1973         const char *szString;
1974         int nCnt;
1975
1976         VM_SAFEPARMCOUNT(1,VM_strlennocol);
1977
1978         szString = PRVM_G_STRING(OFS_PARM0);
1979
1980         nCnt = COM_StringLengthNoColors(szString, 0, NULL);
1981
1982         PRVM_G_FLOAT(OFS_RETURN) = nCnt;
1983 }
1984
1985 // DRESK - String to Uppercase and Lowercase
1986 /*
1987 =========
1988 VM_strtolower
1989
1990 string  strtolower(string s)
1991 =========
1992 */
1993 // string (string s) strtolower = #480; // returns passed in string in lowercase form
1994 void VM_strtolower(void)
1995 {
1996         char szNewString[VM_STRINGTEMP_LENGTH];
1997         const char *szString;
1998
1999         // Prepare Strings
2000         VM_SAFEPARMCOUNT(1,VM_strtolower);
2001         szString = PRVM_G_STRING(OFS_PARM0);
2002
2003         COM_ToLowerString(szString, szNewString, sizeof(szNewString) );
2004
2005         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
2006 }
2007
2008 /*
2009 =========
2010 VM_strtoupper
2011
2012 string  strtoupper(string s)
2013 =========
2014 */
2015 // string (string s) strtoupper = #481; // returns passed in string in uppercase form
2016 void VM_strtoupper(void)
2017 {
2018         char szNewString[VM_STRINGTEMP_LENGTH];
2019         const char *szString;
2020
2021         // Prepare Strings
2022         VM_SAFEPARMCOUNT(1,VM_strtoupper);
2023         szString = PRVM_G_STRING(OFS_PARM0);
2024
2025         COM_ToUpperString(szString, szNewString, sizeof(szNewString) );
2026
2027         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
2028 }
2029
2030 /*
2031 =========
2032 VM_strcat
2033
2034 string strcat(string,string,...[string])
2035 =========
2036 */
2037 //string(string s1, string s2) strcat = #115;
2038 // concatenates two strings (for example "abc", "def" would return "abcdef")
2039 // and returns as a tempstring
2040 void VM_strcat(void)
2041 {
2042         char s[VM_STRINGTEMP_LENGTH];
2043         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strcat);
2044
2045         VM_VarString(0, s, sizeof(s));
2046         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
2047 }
2048
2049 /*
2050 =========
2051 VM_substring
2052
2053 string  substring(string s, float start, float length)
2054 =========
2055 */
2056 // string(string s, float start, float length) substring = #116;
2057 // returns a section of a string as a tempstring
2058 void VM_substring(void)
2059 {
2060         int i, start, length;
2061         const char *s;
2062         char string[VM_STRINGTEMP_LENGTH];
2063
2064         VM_SAFEPARMCOUNT(3,VM_substring);
2065
2066         s = PRVM_G_STRING(OFS_PARM0);
2067         start = (int)PRVM_G_FLOAT(OFS_PARM1);
2068         length = (int)PRVM_G_FLOAT(OFS_PARM2);
2069         for (i = 0;i < start && *s;i++, s++);
2070         for (i = 0;i < (int)sizeof(string) - 1 && *s && i < length;i++, s++)
2071                 string[i] = *s;
2072         string[i] = 0;
2073         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
2074 }
2075
2076 /*
2077 =========
2078 VM_strreplace
2079
2080 string(string search, string replace, string subject) strreplace = #484;
2081 =========
2082 */
2083 // replaces all occurrences of search with replace in the string subject, and returns the result
2084 void VM_strreplace(void)
2085 {
2086         int i, j, si;
2087         const char *search, *replace, *subject;
2088         char string[VM_STRINGTEMP_LENGTH];
2089         int search_len, replace_len, subject_len;
2090
2091         VM_SAFEPARMCOUNT(3,VM_strreplace);
2092
2093         search = PRVM_G_STRING(OFS_PARM0);
2094         replace = PRVM_G_STRING(OFS_PARM1);
2095         subject = PRVM_G_STRING(OFS_PARM2);
2096
2097         search_len = (int)strlen(search);
2098         replace_len = (int)strlen(replace);
2099         subject_len = (int)strlen(subject);
2100
2101         si = 0;
2102         for (i = 0; i < subject_len; i++)
2103         {
2104                 for (j = 0; j < search_len && i+j < subject_len; j++)
2105                         if (subject[i+j] != search[j])
2106                                 break;
2107                 if (j == search_len || i+j == subject_len)
2108                 {
2109                 // found it at offset 'i'
2110                         for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
2111                                 string[si++] = replace[j];
2112                         i += search_len - 1;
2113                 }
2114                 else
2115                 {
2116                 // not found
2117                         if (si < (int)sizeof(string) - 1)
2118                                 string[si++] = subject[i];
2119                 }
2120         }
2121         string[si] = '\0';
2122
2123         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
2124 }
2125
2126 /*
2127 =========
2128 VM_strireplace
2129
2130 string(string search, string replace, string subject) strireplace = #485;
2131 =========
2132 */
2133 // case-insensitive version of strreplace
2134 void VM_strireplace(void)
2135 {
2136         int i, j, si;
2137         const char *search, *replace, *subject;
2138         char string[VM_STRINGTEMP_LENGTH];
2139         int search_len, replace_len, subject_len;
2140
2141         VM_SAFEPARMCOUNT(3,VM_strreplace);
2142
2143         search = PRVM_G_STRING(OFS_PARM0);
2144         replace = PRVM_G_STRING(OFS_PARM1);
2145         subject = PRVM_G_STRING(OFS_PARM2);
2146
2147         search_len = (int)strlen(search);
2148         replace_len = (int)strlen(replace);
2149         subject_len = (int)strlen(subject);
2150
2151         si = 0;
2152         for (i = 0; i < subject_len; i++)
2153         {
2154                 for (j = 0; j < search_len && i+j < subject_len; j++)
2155                         if (tolower(subject[i+j]) != tolower(search[j]))
2156                                 break;
2157                 if (j == search_len || i+j == subject_len)
2158                 {
2159                 // found it at offset 'i'
2160                         for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
2161                                 string[si++] = replace[j];
2162                         i += search_len - 1;
2163                 }
2164                 else
2165                 {
2166                 // not found
2167                         if (si < (int)sizeof(string) - 1)
2168                                 string[si++] = subject[i];
2169                 }
2170         }
2171         string[si] = '\0';
2172
2173         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
2174 }
2175
2176 /*
2177 =========
2178 VM_stov
2179
2180 vector  stov(string s)
2181 =========
2182 */
2183 //vector(string s) stov = #117; // returns vector value from a string
2184 void VM_stov(void)
2185 {
2186         char string[VM_STRINGTEMP_LENGTH];
2187
2188         VM_SAFEPARMCOUNT(1,VM_stov);
2189
2190         VM_VarString(0, string, sizeof(string));
2191         Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
2192 }
2193
2194 /*
2195 =========
2196 VM_strzone
2197
2198 string  strzone(string s)
2199 =========
2200 */
2201 //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)
2202 void VM_strzone(void)
2203 {
2204         char *out;
2205         char string[VM_STRINGTEMP_LENGTH];
2206         size_t alloclen;
2207
2208         VM_SAFEPARMCOUNT(1,VM_strzone);
2209
2210         VM_VarString(0, string, sizeof(string));
2211         alloclen = strlen(string) + 1;
2212         PRVM_G_INT(OFS_RETURN) = PRVM_AllocString(alloclen, &out);
2213         memcpy(out, string, alloclen);
2214 }
2215
2216 /*
2217 =========
2218 VM_strunzone
2219
2220 strunzone(string s)
2221 =========
2222 */
2223 //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!!!)
2224 void VM_strunzone(void)
2225 {
2226         VM_SAFEPARMCOUNT(1,VM_strunzone);
2227         PRVM_FreeString(PRVM_G_INT(OFS_PARM0));
2228 }
2229
2230 /*
2231 =========
2232 VM_command (used by client and menu)
2233
2234 clientcommand(float client, string s) (for client and menu)
2235 =========
2236 */
2237 //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
2238 //this function originally written by KrimZon, made shorter by LordHavoc
2239 void VM_clcommand (void)
2240 {
2241         client_t *temp_client;
2242         int i;
2243
2244         VM_SAFEPARMCOUNT(2,VM_clcommand);
2245
2246         i = (int)PRVM_G_FLOAT(OFS_PARM0);
2247         if (!sv.active  || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
2248         {
2249                 VM_Warning("VM_clientcommand: %s: invalid client/server is not active !\n", PRVM_NAME);
2250                 return;
2251         }
2252
2253         temp_client = host_client;
2254         host_client = svs.clients + i;
2255         Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1), src_client);
2256         host_client = temp_client;
2257 }
2258
2259
2260 /*
2261 =========
2262 VM_tokenize
2263
2264 float tokenize(string s)
2265 =========
2266 */
2267 //float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many
2268 //this function originally written by KrimZon, made shorter by LordHavoc
2269 //20040203: rewritten by LordHavoc (no longer uses allocations)
2270 int num_tokens = 0;
2271 int tokens[256];
2272 void VM_tokenize (void)
2273 {
2274         const char *p;
2275         static char string[VM_STRINGTEMP_LENGTH]; // static, because it's big
2276
2277         VM_SAFEPARMCOUNT(1,VM_tokenize);
2278
2279         strlcpy(string, PRVM_G_STRING(OFS_PARM0), sizeof(string));
2280         p = string;
2281
2282         num_tokens = 0;
2283         while(COM_ParseToken_VM_Tokenize(&p, false))
2284         {
2285                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2286                         break;
2287                 tokens[num_tokens++] = PRVM_SetTempString(com_token);
2288         }
2289
2290         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2291 }
2292
2293 /*
2294 =========
2295 VM_tokenizebyseparator
2296
2297 float tokenizebyseparator(string s, string separator1, ...)
2298 =========
2299 */
2300 //float(string s, string separator1, ...) tokenizebyseparator = #479; // takes apart a string into individal words (access them with argv), returns how many
2301 //this function returns the token preceding each instance of a separator (of
2302 //which there can be multiple), and the text following the last separator
2303 //useful for parsing certain kinds of data like IP addresses
2304 //example:
2305 //numnumbers = tokenizebyseparator("10.1.2.3", ".");
2306 //returns 4 and the tokens "10" "1" "2" "3".
2307 void VM_tokenizebyseparator (void)
2308 {
2309         int j, k;
2310         int numseparators;
2311         int separatorlen[7];
2312         const char *separators[7];
2313         const char *p;
2314         const char *token;
2315         char tokentext[MAX_INPUTLINE];
2316         static char string[VM_STRINGTEMP_LENGTH]; // static, because it's big
2317
2318         VM_SAFEPARMCOUNTRANGE(2, 8,VM_tokenizebyseparator);
2319
2320         strlcpy(string, PRVM_G_STRING(OFS_PARM0), sizeof(string));
2321         p = string;
2322
2323         numseparators = 0;;
2324         for (j = 1;j < prog->argc;j++)
2325         {
2326                 // skip any blank separator strings
2327                 const char *s = PRVM_G_STRING(OFS_PARM0+j*3);
2328                 if (!s[0])
2329                         continue;
2330                 separators[numseparators] = s;
2331                 separatorlen[numseparators] = strlen(s);
2332                 numseparators++;
2333         }
2334
2335         num_tokens = 0;
2336         j = 0;
2337
2338         while (num_tokens < (int)(sizeof(tokens)/sizeof(tokens[0])))
2339         {
2340                 token = tokentext + j;
2341                 while (*p)
2342                 {
2343                         for (k = 0;k < numseparators;k++)
2344                         {
2345                                 if (!strncmp(p, separators[k], separatorlen[k]))
2346                                 {
2347                                         p += separatorlen[k];
2348                                         break;
2349                                 }
2350                         }
2351                         if (k < numseparators)
2352                                 break;
2353                         if (j < (int)sizeof(tokentext)-1)
2354                                 tokentext[j++] = *p;
2355                         p++;
2356                 }
2357                 if (j >= (int)sizeof(tokentext))
2358                         break;
2359                 tokentext[j++] = 0;
2360                 tokens[num_tokens++] = PRVM_SetTempString(token);
2361                 if (!*p)
2362                         break;
2363         }
2364
2365         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2366 }
2367
2368 //string(float n) argv = #442; // returns a word from the tokenized string (returns nothing for an invalid index)
2369 //this function originally written by KrimZon, made shorter by LordHavoc
2370 void VM_argv (void)
2371 {
2372         int token_num;
2373
2374         VM_SAFEPARMCOUNT(1,VM_argv);
2375
2376         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2377
2378         if (token_num >= 0 && token_num < num_tokens)
2379                 PRVM_G_INT(OFS_RETURN) = tokens[token_num];
2380         else
2381                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2382 }
2383
2384 /*
2385 =========
2386 VM_isserver
2387
2388 float   isserver()
2389 =========
2390 */
2391 void VM_isserver(void)
2392 {
2393         VM_SAFEPARMCOUNT(0,VM_serverstate);
2394
2395         PRVM_G_FLOAT(OFS_RETURN) = sv.active && (svs.maxclients > 1 || cls.state == ca_dedicated);
2396 }
2397
2398 /*
2399 =========
2400 VM_clientcount
2401
2402 float   clientcount()
2403 =========
2404 */
2405 void VM_clientcount(void)
2406 {
2407         VM_SAFEPARMCOUNT(0,VM_clientcount);
2408
2409         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
2410 }
2411
2412 /*
2413 =========
2414 VM_clientstate
2415
2416 float   clientstate()
2417 =========
2418 */
2419 void VM_clientstate(void)
2420 {
2421         VM_SAFEPARMCOUNT(0,VM_clientstate);
2422
2423
2424         switch( cls.state ) {
2425                 case ca_uninitialized:
2426                 case ca_dedicated:
2427                         PRVM_G_FLOAT(OFS_RETURN) = 0;
2428                         break;
2429                 case ca_disconnected:
2430                         PRVM_G_FLOAT(OFS_RETURN) = 1;
2431                         break;
2432                 case ca_connected:
2433                         PRVM_G_FLOAT(OFS_RETURN) = 2;
2434                         break;
2435                 default:
2436                         // should never be reached!
2437                         break;
2438         }
2439 }
2440
2441 /*
2442 =========
2443 VM_getostype
2444
2445 float   getostype(void)
2446 =========
2447 */ // not used at the moment -> not included in the common list
2448 void VM_getostype(void)
2449 {
2450         VM_SAFEPARMCOUNT(0,VM_getostype);
2451
2452         /*
2453         OS_WINDOWS
2454         OS_LINUX
2455         OS_MAC - not supported
2456         */
2457
2458 #ifdef WIN32
2459         PRVM_G_FLOAT(OFS_RETURN) = 0;
2460 #elif defined(MACOSX)
2461         PRVM_G_FLOAT(OFS_RETURN) = 2;
2462 #else
2463         PRVM_G_FLOAT(OFS_RETURN) = 1;
2464 #endif
2465 }
2466
2467 /*
2468 =========
2469 VM_gettime
2470
2471 float   gettime(void)
2472 =========
2473 */
2474 void VM_gettime(void)
2475 {
2476         VM_SAFEPARMCOUNT(0,VM_gettime);
2477
2478         PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
2479 }
2480
2481 /*
2482 =========
2483 VM_loadfromdata
2484
2485 loadfromdata(string data)
2486 =========
2487 */
2488 void VM_loadfromdata(void)
2489 {
2490         VM_SAFEPARMCOUNT(1,VM_loadentsfromfile);
2491
2492         PRVM_ED_LoadFromFile(PRVM_G_STRING(OFS_PARM0));
2493 }
2494
2495 /*
2496 ========================
2497 VM_parseentitydata
2498
2499 parseentitydata(entity ent, string data)
2500 ========================
2501 */
2502 void VM_parseentitydata(void)
2503 {
2504         prvm_edict_t *ent;
2505         const char *data;
2506
2507         VM_SAFEPARMCOUNT(2, VM_parseentitydata);
2508
2509         // get edict and test it
2510         ent = PRVM_G_EDICT(OFS_PARM0);
2511         if (ent->priv.required->free)
2512                 PRVM_ERROR ("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
2513
2514         data = PRVM_G_STRING(OFS_PARM1);
2515
2516         // parse the opening brace
2517         if (!COM_ParseToken_Simple(&data, false, false) || com_token[0] != '{' )
2518                 PRVM_ERROR ("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", PRVM_NAME, data );
2519
2520         PRVM_ED_ParseEdict (data, ent);
2521 }
2522
2523 /*
2524 =========
2525 VM_loadfromfile
2526
2527 loadfromfile(string file)
2528 =========
2529 */
2530 void VM_loadfromfile(void)
2531 {
2532         const char *filename;
2533         char *data;
2534
2535         VM_SAFEPARMCOUNT(1,VM_loadfromfile);
2536
2537         filename = PRVM_G_STRING(OFS_PARM0);
2538         if (FS_CheckNastyPath(filename, false))
2539         {
2540                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2541                 VM_Warning("VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename);
2542                 return;
2543         }
2544
2545         // not conform with VM_fopen
2546         data = (char *)FS_LoadFile(filename, tempmempool, false, NULL);
2547         if (data == NULL)
2548                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2549
2550         PRVM_ED_LoadFromFile(data);
2551
2552         if(data)
2553                 Mem_Free(data);
2554 }
2555
2556
2557 /*
2558 =========
2559 VM_modulo
2560
2561 float   mod(float val, float m)
2562 =========
2563 */
2564 void VM_modulo(void)
2565 {
2566         int val, m;
2567         VM_SAFEPARMCOUNT(2,VM_module);
2568
2569         val = (int) PRVM_G_FLOAT(OFS_PARM0);
2570         m       = (int) PRVM_G_FLOAT(OFS_PARM1);
2571
2572         PRVM_G_FLOAT(OFS_RETURN) = (float) (val % m);
2573 }
2574
2575 void VM_Search_Init(void)
2576 {
2577         int i;
2578         for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++)
2579                 prog->opensearches[i] = NULL;
2580 }
2581
2582 void VM_Search_Reset(void)
2583 {
2584         int i;
2585         // reset the fssearch list
2586         for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++)
2587         {
2588                 if(prog->opensearches[i])
2589                         FS_FreeSearch(prog->opensearches[i]);
2590                 prog->opensearches[i] = NULL;
2591         }
2592 }
2593
2594 /*
2595 =========
2596 VM_search_begin
2597
2598 float search_begin(string pattern, float caseinsensitive, float quiet)
2599 =========
2600 */
2601 void VM_search_begin(void)
2602 {
2603         int handle;
2604         const char *pattern;
2605         int caseinsens, quiet;
2606
2607         VM_SAFEPARMCOUNT(3, VM_search_begin);
2608
2609         pattern = PRVM_G_STRING(OFS_PARM0);
2610
2611         VM_CheckEmptyString(pattern);
2612
2613         caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1);
2614         quiet = (int)PRVM_G_FLOAT(OFS_PARM2);
2615
2616         for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++)
2617                 if(!prog->opensearches[handle])
2618                         break;
2619
2620         if(handle >= PRVM_MAX_OPENSEARCHES)
2621         {
2622                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2623                 VM_Warning("VM_search_begin: %s ran out of search handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENSEARCHES);
2624                 return;
2625         }
2626
2627         if(!(prog->opensearches[handle] = FS_Search(pattern,caseinsens, quiet)))
2628                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2629         else
2630                 PRVM_G_FLOAT(OFS_RETURN) = handle;
2631 }
2632
2633 /*
2634 =========
2635 VM_search_end
2636
2637 void    search_end(float handle)
2638 =========
2639 */
2640 void VM_search_end(void)
2641 {
2642         int handle;
2643         VM_SAFEPARMCOUNT(1, VM_search_end);
2644
2645         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2646
2647         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2648         {
2649                 VM_Warning("VM_search_end: invalid handle %i used in %s\n", handle, PRVM_NAME);
2650                 return;
2651         }
2652         if(prog->opensearches[handle] == NULL)
2653         {
2654                 VM_Warning("VM_search_end: no such handle %i in %s\n", handle, PRVM_NAME);
2655                 return;
2656         }
2657
2658         FS_FreeSearch(prog->opensearches[handle]);
2659         prog->opensearches[handle] = NULL;
2660 }
2661
2662 /*
2663 =========
2664 VM_search_getsize
2665
2666 float   search_getsize(float handle)
2667 =========
2668 */
2669 void VM_search_getsize(void)
2670 {
2671         int handle;
2672         VM_SAFEPARMCOUNT(1, VM_M_search_getsize);
2673
2674         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2675
2676         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2677         {
2678                 VM_Warning("VM_search_getsize: invalid handle %i used in %s\n", handle, PRVM_NAME);
2679                 return;
2680         }
2681         if(prog->opensearches[handle] == NULL)
2682         {
2683                 VM_Warning("VM_search_getsize: no such handle %i in %s\n", handle, PRVM_NAME);
2684                 return;
2685         }
2686
2687         PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames;
2688 }
2689
2690 /*
2691 =========
2692 VM_search_getfilename
2693
2694 string  search_getfilename(float handle, float num)
2695 =========
2696 */
2697 void VM_search_getfilename(void)
2698 {
2699         int handle, filenum;
2700         VM_SAFEPARMCOUNT(2, VM_search_getfilename);
2701
2702         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2703         filenum = (int)PRVM_G_FLOAT(OFS_PARM1);
2704
2705         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2706         {
2707                 VM_Warning("VM_search_getfilename: invalid handle %i used in %s\n", handle, PRVM_NAME);
2708                 return;
2709         }
2710         if(prog->opensearches[handle] == NULL)
2711         {
2712                 VM_Warning("VM_search_getfilename: no such handle %i in %s\n", handle, PRVM_NAME);
2713                 return;
2714         }
2715         if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames)
2716         {
2717                 VM_Warning("VM_search_getfilename: invalid filenum %i in %s\n", filenum, PRVM_NAME);
2718                 return;
2719         }
2720
2721         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog->opensearches[handle]->filenames[filenum]);
2722 }
2723
2724 /*
2725 =========
2726 VM_chr
2727
2728 string  chr(float ascii)
2729 =========
2730 */
2731 void VM_chr(void)
2732 {
2733         char tmp[2];
2734         VM_SAFEPARMCOUNT(1, VM_chr);
2735
2736         tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
2737         tmp[1] = 0;
2738
2739         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
2740 }
2741
2742 //=============================================================================
2743 // Draw builtins (client & menu)
2744
2745 /*
2746 =========
2747 VM_iscachedpic
2748
2749 float   iscachedpic(string pic)
2750 =========
2751 */
2752 void VM_iscachedpic(void)
2753 {
2754         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
2755
2756         // drawq hasnt such a function, thus always return true
2757         PRVM_G_FLOAT(OFS_RETURN) = false;
2758 }
2759
2760 /*
2761 =========
2762 VM_precache_pic
2763
2764 string  precache_pic(string pic)
2765 =========
2766 */
2767 void VM_precache_pic(void)
2768 {
2769         const char      *s;
2770
2771         VM_SAFEPARMCOUNT(1, VM_precache_pic);
2772
2773         s = PRVM_G_STRING(OFS_PARM0);
2774         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
2775         VM_CheckEmptyString (s);
2776
2777         // AK Draw_CachePic is supposed to always return a valid pointer
2778         if( Draw_CachePic_Flags(s, CACHEPICFLAG_NOTPERSISTENT)->tex == r_texture_notexture )
2779                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2780 }
2781
2782 /*
2783 =========
2784 VM_freepic
2785
2786 freepic(string s)
2787 =========
2788 */
2789 void VM_freepic(void)
2790 {
2791         const char *s;
2792
2793         VM_SAFEPARMCOUNT(1,VM_freepic);
2794
2795         s = PRVM_G_STRING(OFS_PARM0);
2796         VM_CheckEmptyString (s);
2797
2798         Draw_FreePic(s);
2799 }
2800
2801 dp_font_t *getdrawfont()
2802 {
2803         if(prog->globaloffsets.drawfont >= 0)
2804         {
2805                 int f = PRVM_G_FLOAT(prog->globaloffsets.drawfont);
2806                 if(f < 0 || f >= MAX_FONTS)
2807                         return FONT_DEFAULT;
2808                 return &dp_fonts[f];
2809         }
2810         else
2811                 return FONT_DEFAULT;
2812 }
2813
2814 /*
2815 =========
2816 VM_drawcharacter
2817
2818 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
2819 =========
2820 */
2821 void VM_drawcharacter(void)
2822 {
2823         float *pos,*scale,*rgb;
2824         char   character;
2825         int flag;
2826         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
2827
2828         character = (char) PRVM_G_FLOAT(OFS_PARM1);
2829         if(character == 0)
2830         {
2831                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2832                 VM_Warning("VM_drawcharacter: %s passed null character !\n",PRVM_NAME);
2833                 return;
2834         }
2835
2836         pos = PRVM_G_VECTOR(OFS_PARM0);
2837         scale = PRVM_G_VECTOR(OFS_PARM2);
2838         rgb = PRVM_G_VECTOR(OFS_PARM3);
2839         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2840
2841         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2842         {
2843                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2844                 VM_Warning("VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2845                 return;
2846         }
2847
2848         if(pos[2] || scale[2])
2849                 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")));
2850
2851         if(!scale[0] || !scale[1])
2852         {
2853                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2854                 VM_Warning("VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2855                 return;
2856         }
2857
2858         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());
2859         PRVM_G_FLOAT(OFS_RETURN) = 1;
2860 }
2861
2862 /*
2863 =========
2864 VM_drawstring
2865
2866 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
2867 =========
2868 */
2869 void VM_drawstring(void)
2870 {
2871         float *pos,*scale,*rgb;
2872         const char  *string;
2873         int flag;
2874         VM_SAFEPARMCOUNT(6,VM_drawstring);
2875
2876         string = PRVM_G_STRING(OFS_PARM1);
2877         pos = PRVM_G_VECTOR(OFS_PARM0);
2878         scale = PRVM_G_VECTOR(OFS_PARM2);
2879         rgb = PRVM_G_VECTOR(OFS_PARM3);
2880         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2881
2882         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2883         {
2884                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2885                 VM_Warning("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2886                 return;
2887         }
2888
2889         if(!scale[0] || !scale[1])
2890         {
2891                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2892                 VM_Warning("VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2893                 return;
2894         }
2895
2896         if(pos[2] || scale[2])
2897                 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")));
2898
2899         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());
2900         PRVM_G_FLOAT(OFS_RETURN) = 1;
2901 }
2902
2903 /*
2904 =========
2905 VM_drawcolorcodedstring
2906
2907 float   drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag)
2908 =========
2909 */
2910 void VM_drawcolorcodedstring(void)
2911 {
2912         float *pos,*scale;
2913         const char  *string;
2914         int flag,color;
2915         VM_SAFEPARMCOUNT(5,VM_drawstring);
2916
2917         string = PRVM_G_STRING(OFS_PARM1);
2918         pos = PRVM_G_VECTOR(OFS_PARM0);
2919         scale = PRVM_G_VECTOR(OFS_PARM2);
2920         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2921
2922         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2923         {
2924                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2925                 VM_Warning("VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2926                 return;
2927         }
2928
2929         if(!scale[0] || !scale[1])
2930         {
2931                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2932                 VM_Warning("VM_drawcolorcodedstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2933                 return;
2934         }
2935
2936         if(pos[2] || scale[2])
2937                 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")));
2938
2939         color = -1;
2940         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());
2941         PRVM_G_FLOAT(OFS_RETURN) = 1;
2942 }
2943 /*
2944 =========
2945 VM_stringwidth
2946
2947 float   stringwidth(string text, float allowColorCodes)
2948 =========
2949 */
2950 void VM_stringwidth(void)
2951 {
2952         const char  *string;
2953         int colors;
2954         VM_SAFEPARMCOUNT(2,VM_drawstring);
2955
2956         string = PRVM_G_STRING(OFS_PARM0);
2957         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
2958
2959         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font(string, 0, !colors, getdrawfont()); // 1x1 characters, don't actually draw
2960 }
2961 /*
2962 =========
2963 VM_drawpic
2964
2965 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
2966 =========
2967 */
2968 void VM_drawpic(void)
2969 {
2970         const char *picname;
2971         float *size, *pos, *rgb;
2972         int flag;
2973
2974         VM_SAFEPARMCOUNT(6,VM_drawpic);
2975
2976         picname = PRVM_G_STRING(OFS_PARM1);
2977         VM_CheckEmptyString (picname);
2978
2979         // is pic cached ? no function yet for that
2980         if(!1)
2981         {
2982                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2983                 VM_Warning("VM_drawpic: %s: %s not cached !\n", PRVM_NAME, picname);
2984                 return;
2985         }
2986
2987         pos = PRVM_G_VECTOR(OFS_PARM0);
2988         size = PRVM_G_VECTOR(OFS_PARM2);
2989         rgb = PRVM_G_VECTOR(OFS_PARM3);
2990         flag = (int) PRVM_G_FLOAT(OFS_PARM5);
2991
2992         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2993         {
2994                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2995                 VM_Warning("VM_drawpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2996                 return;
2997         }
2998
2999         if(pos[2] || size[2])
3000                 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")));
3001
3002         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);
3003         PRVM_G_FLOAT(OFS_RETURN) = 1;
3004 }
3005 /*
3006 =========
3007 VM_drawsubpic
3008
3009 float   drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
3010
3011 =========
3012 */
3013 void VM_drawsubpic(void)
3014 {
3015         const char *picname;
3016         float *size, *pos, *rgb, *srcPos, *srcSize, alpha;
3017         int flag;
3018
3019         VM_SAFEPARMCOUNT(8,VM_drawsubpic);
3020
3021         picname = PRVM_G_STRING(OFS_PARM2);
3022         VM_CheckEmptyString (picname);
3023
3024         // is pic cached ? no function yet for that
3025         if(!1)
3026         {
3027                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3028                 VM_Warning("VM_drawsubpic: %s: %s not cached !\n", PRVM_NAME, picname);
3029                 return;
3030         }
3031
3032         pos = PRVM_G_VECTOR(OFS_PARM0);
3033         size = PRVM_G_VECTOR(OFS_PARM1);
3034         srcPos = PRVM_G_VECTOR(OFS_PARM3);
3035         srcSize = PRVM_G_VECTOR(OFS_PARM4);
3036         rgb = PRVM_G_VECTOR(OFS_PARM5);
3037         alpha = PRVM_G_FLOAT(OFS_PARM6);
3038         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
3039
3040         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3041         {
3042                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3043                 VM_Warning("VM_drawsubpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3044                 return;
3045         }
3046
3047         if(pos[2] || size[2])
3048                 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")));
3049
3050         DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic (picname),
3051                 size[0], size[1],
3052                 srcPos[0],              srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3053                 srcPos[0] + srcSize[0], srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3054                 srcPos[0],              srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3055                 srcPos[0] + srcSize[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3056                 flag);
3057         PRVM_G_FLOAT(OFS_RETURN) = 1;
3058 }
3059
3060 /*
3061 =========
3062 VM_drawfill
3063
3064 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
3065 =========
3066 */
3067 void VM_drawfill(void)
3068 {
3069         float *size, *pos, *rgb;
3070         int flag;
3071
3072         VM_SAFEPARMCOUNT(5,VM_drawfill);
3073
3074
3075         pos = PRVM_G_VECTOR(OFS_PARM0);
3076         size = PRVM_G_VECTOR(OFS_PARM1);
3077         rgb = PRVM_G_VECTOR(OFS_PARM2);
3078         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
3079
3080         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3081         {
3082                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3083                 VM_Warning("VM_drawfill: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
3084                 return;
3085         }
3086
3087         if(pos[2] || size[2])
3088                 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")));
3089
3090         DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
3091         PRVM_G_FLOAT(OFS_RETURN) = 1;
3092 }
3093
3094 /*
3095 =========
3096 VM_drawsetcliparea
3097
3098 drawsetcliparea(float x, float y, float width, float height)
3099 =========
3100 */
3101 void VM_drawsetcliparea(void)
3102 {
3103         float x,y,w,h;
3104         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
3105
3106         x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
3107         y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
3108         w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
3109         h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
3110
3111         DrawQ_SetClipArea(x, y, w, h);
3112 }
3113
3114 /*
3115 =========
3116 VM_drawresetcliparea
3117
3118 drawresetcliparea()
3119 =========
3120 */
3121 void VM_drawresetcliparea(void)
3122 {
3123         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
3124
3125         DrawQ_ResetClipArea();
3126 }
3127
3128 /*
3129 =========
3130 VM_getimagesize
3131
3132 vector  getimagesize(string pic)
3133 =========
3134 */
3135 void VM_getimagesize(void)
3136 {
3137         const char *p;
3138         cachepic_t *pic;
3139
3140         VM_SAFEPARMCOUNT(1,VM_getimagesize);
3141
3142         p = PRVM_G_STRING(OFS_PARM0);
3143         VM_CheckEmptyString (p);
3144
3145         pic = Draw_CachePic_Flags (p, CACHEPICFLAG_NOTPERSISTENT);
3146
3147         PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
3148         PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
3149         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3150 }
3151
3152 /*
3153 =========
3154 VM_keynumtostring
3155
3156 string keynumtostring(float keynum)
3157 =========
3158 */
3159 void VM_keynumtostring (void)
3160 {
3161         VM_SAFEPARMCOUNT(1, VM_keynumtostring);
3162
3163         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0)));
3164 }
3165
3166 /*
3167 =========
3168 VM_stringtokeynum
3169
3170 float stringtokeynum(string key)
3171 =========
3172 */
3173 void VM_stringtokeynum (void)
3174 {
3175         VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
3176
3177         PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
3178 }
3179
3180 // CL_Video interface functions
3181
3182 /*
3183 ========================
3184 VM_cin_open
3185
3186 float cin_open(string file, string name)
3187 ========================
3188 */
3189 void VM_cin_open( void )
3190 {
3191         const char *file;
3192         const char *name;
3193
3194         VM_SAFEPARMCOUNT( 2, VM_cin_open );
3195
3196         file = PRVM_G_STRING( OFS_PARM0 );
3197         name = PRVM_G_STRING( OFS_PARM1 );
3198
3199         VM_CheckEmptyString( file );
3200     VM_CheckEmptyString( name );
3201
3202         if( CL_OpenVideo( file, name, MENUOWNER ) )
3203                 PRVM_G_FLOAT( OFS_RETURN ) = 1;
3204         else
3205                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3206 }
3207
3208 /*
3209 ========================
3210 VM_cin_close
3211
3212 void cin_close(string name)
3213 ========================
3214 */
3215 void VM_cin_close( void )
3216 {
3217         const char *name;
3218
3219         VM_SAFEPARMCOUNT( 1, VM_cin_close );
3220
3221         name = PRVM_G_STRING( OFS_PARM0 );
3222         VM_CheckEmptyString( name );
3223
3224         CL_CloseVideo( CL_GetVideoByName( name ) );
3225 }
3226
3227 /*
3228 ========================
3229 VM_cin_setstate
3230 void cin_setstate(string name, float type)
3231 ========================
3232 */
3233 void VM_cin_setstate( void )
3234 {
3235         const char *name;
3236         clvideostate_t  state;
3237         clvideo_t               *video;
3238
3239         VM_SAFEPARMCOUNT( 2, VM_cin_netstate );
3240
3241         name = PRVM_G_STRING( OFS_PARM0 );
3242         VM_CheckEmptyString( name );
3243
3244         state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
3245
3246         video = CL_GetVideoByName( name );
3247         if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
3248                 CL_SetVideoState( video, state );
3249 }
3250
3251 /*
3252 ========================
3253 VM_cin_getstate
3254
3255 float cin_getstate(string name)
3256 ========================
3257 */
3258 void VM_cin_getstate( void )
3259 {
3260         const char *name;
3261         clvideo_t               *video;
3262
3263         VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
3264
3265         name = PRVM_G_STRING( OFS_PARM0 );
3266         VM_CheckEmptyString( name );
3267
3268         video = CL_GetVideoByName( name );
3269         if( video )
3270                 PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
3271         else
3272                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3273 }
3274
3275 /*
3276 ========================
3277 VM_cin_restart
3278
3279 void cin_restart(string name)
3280 ========================
3281 */
3282 void VM_cin_restart( void )
3283 {
3284         const char *name;
3285         clvideo_t               *video;
3286
3287         VM_SAFEPARMCOUNT( 1, VM_cin_restart );
3288
3289         name = PRVM_G_STRING( OFS_PARM0 );
3290         VM_CheckEmptyString( name );
3291
3292         video = CL_GetVideoByName( name );
3293         if( video )
3294                 CL_RestartVideo( video );
3295 }
3296
3297 /*
3298 ========================
3299 VM_Gecko_Init
3300 ========================
3301 */
3302 void VM_Gecko_Init( void ) {
3303         // the prog struct is memset to 0 by Initprog? [12/6/2007 Black]
3304         // FIXME: remove the other _Init functions then, too? [12/6/2007 Black]
3305 }
3306
3307 /*
3308 ========================
3309 VM_Gecko_Destroy
3310 ========================
3311 */
3312 void VM_Gecko_Destroy( void ) {
3313         int i;
3314         for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
3315                 clgecko_t **instance = &prog->opengeckoinstances[ i ];
3316                 if( *instance ) {
3317                         CL_Gecko_DestroyBrowser( *instance );
3318                 }
3319                 *instance = NULL;
3320         }
3321 }
3322
3323 /*
3324 ========================
3325 VM_gecko_create
3326
3327 float[bool] gecko_create( string name )
3328 ========================
3329 */
3330 void VM_gecko_create( void ) {
3331         const char *name;
3332         int i;
3333         clgecko_t *instance;
3334         
3335         VM_SAFEPARMCOUNT( 1, VM_gecko_create );
3336
3337         name = PRVM_G_STRING( OFS_PARM0 );
3338         VM_CheckEmptyString( name );
3339
3340         // find an empty slot for this gecko browser..
3341         for( i = 0 ; i < PRVM_MAX_GECKOINSTANCES ; i++ ) {
3342                 if( prog->opengeckoinstances[ i ] == NULL ) {
3343                         break;
3344                 }
3345         }
3346         if( i == PRVM_MAX_GECKOINSTANCES ) {
3347                         VM_Warning("VM_gecko_create: %s ran out of gecko handles (%i)\n", PRVM_NAME, PRVM_MAX_GECKOINSTANCES);
3348                         PRVM_G_FLOAT( OFS_RETURN ) = 0;
3349                         return;
3350         }
3351
3352         instance = prog->opengeckoinstances[ i ] = CL_Gecko_CreateBrowser( name, PRVM_GetProgNr() );
3353    if( !instance ) {
3354                 // TODO: error handling [12/3/2007 Black]
3355                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3356                 return;
3357         }
3358         PRVM_G_FLOAT( OFS_RETURN ) = 1;
3359 }
3360
3361 /*
3362 ========================
3363 VM_gecko_destroy
3364
3365 void gecko_destroy( string name )
3366 ========================
3367 */
3368 void VM_gecko_destroy( void ) {
3369         const char *name;
3370         clgecko_t *instance;
3371
3372         VM_SAFEPARMCOUNT( 1, VM_gecko_destroy );
3373
3374         name = PRVM_G_STRING( OFS_PARM0 );
3375         VM_CheckEmptyString( name );
3376         instance = CL_Gecko_FindBrowser( name );
3377         if( !instance ) {
3378                 return;
3379         }
3380         CL_Gecko_DestroyBrowser( instance );
3381 }
3382
3383 /*
3384 ========================
3385 VM_gecko_navigate
3386
3387 void gecko_navigate( string name, string URI )
3388 ========================
3389 */
3390 void VM_gecko_navigate( void ) {
3391         const char *name;
3392         const char *URI;
3393         clgecko_t *instance;
3394
3395         VM_SAFEPARMCOUNT( 2, VM_gecko_navigate );
3396
3397         name = PRVM_G_STRING( OFS_PARM0 );
3398         URI = PRVM_G_STRING( OFS_PARM1 );
3399         VM_CheckEmptyString( name );
3400         VM_CheckEmptyString( URI );
3401
3402    instance = CL_Gecko_FindBrowser( name );
3403         if( !instance ) {
3404                 return;
3405         }
3406         CL_Gecko_NavigateToURI( instance, URI );
3407 }
3408
3409 /*
3410 ========================
3411 VM_gecko_keyevent
3412
3413 float[bool] gecko_keyevent( string name, float key, float eventtype ) 
3414 ========================
3415 */
3416 void VM_gecko_keyevent( void ) {
3417         const char *name;
3418         unsigned int key;
3419         clgecko_buttoneventtype_t eventtype;
3420         clgecko_t *instance;
3421
3422         VM_SAFEPARMCOUNT( 3, VM_gecko_keyevent );
3423
3424         name = PRVM_G_STRING( OFS_PARM0 );
3425         VM_CheckEmptyString( name );
3426         key = (unsigned int) PRVM_G_FLOAT( OFS_PARM1 );
3427         switch( (unsigned int) PRVM_G_FLOAT( OFS_PARM2 ) ) {
3428         case 0:
3429                 eventtype = CLG_BET_DOWN;
3430                 break;
3431         case 1:
3432                 eventtype = CLG_BET_UP;
3433                 break;
3434         case 2:
3435                 eventtype = CLG_BET_PRESS;
3436                 break;
3437         case 3:
3438                 eventtype = CLG_BET_DOUBLECLICK;
3439                 break;
3440         default:
3441                 // TODO: console printf? [12/3/2007 Black]
3442                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3443                 return;
3444         }
3445
3446         instance = CL_Gecko_FindBrowser( name );
3447         if( !instance ) {
3448                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
3449                 return;
3450         }
3451
3452         PRVM_G_FLOAT( OFS_RETURN ) = (CL_Gecko_Event_Key( instance, key, eventtype ) == true);
3453 }
3454
3455 /*
3456 ========================
3457 VM_gecko_movemouse
3458
3459 void gecko_mousemove( string name, float x, float y )
3460 ========================
3461 */
3462 void VM_gecko_movemouse( void ) {
3463         const char *name;
3464         float x, y;
3465         clgecko_t *instance;
3466
3467         VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
3468
3469         name = PRVM_G_STRING( OFS_PARM0 );
3470         VM_CheckEmptyString( name );
3471         x = PRVM_G_FLOAT( OFS_PARM1 );
3472         y = PRVM_G_FLOAT( OFS_PARM2 );
3473         
3474         instance = CL_Gecko_FindBrowser( name );
3475         if( !instance ) {
3476                 return;
3477         }
3478         CL_Gecko_Event_CursorMove( instance, x, y );
3479 }
3480
3481
3482 /*
3483 ========================
3484 VM_gecko_resize
3485
3486 void gecko_resize( string name, float w, float h )
3487 ========================
3488 */
3489 void VM_gecko_resize( void ) {
3490         const char *name;
3491         float w, h;
3492         clgecko_t *instance;
3493
3494         VM_SAFEPARMCOUNT( 3, VM_gecko_movemouse );
3495
3496         name = PRVM_G_STRING( OFS_PARM0 );
3497         VM_CheckEmptyString( name );
3498         w = PRVM_G_FLOAT( OFS_PARM1 );
3499         h = PRVM_G_FLOAT( OFS_PARM2 );
3500         
3501         instance = CL_Gecko_FindBrowser( name );
3502         if( !instance ) {
3503                 return;
3504         }
3505         CL_Gecko_Resize( instance, w, h );
3506 }
3507
3508
3509 /*
3510 ========================
3511 VM_gecko_get_texture_extent
3512
3513 vector gecko_get_texture_extent( string name )
3514 ========================
3515 */
3516 void VM_gecko_get_texture_extent( void ) {
3517         const char *name;
3518         clgecko_t *instance;
3519
3520         VM_SAFEPARMCOUNT( 1, VM_gecko_movemouse );
3521
3522         name = PRVM_G_STRING( OFS_PARM0 );
3523         VM_CheckEmptyString( name );
3524         
3525         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3526         instance = CL_Gecko_FindBrowser( name );
3527         if( !instance ) {
3528                 PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
3529                 PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
3530                 return;
3531         }
3532         CL_Gecko_GetTextureExtent( instance, 
3533                 PRVM_G_VECTOR(OFS_RETURN), PRVM_G_VECTOR(OFS_RETURN)+1 );
3534 }
3535
3536
3537
3538 /*
3539 ==============
3540 VM_makevectors
3541
3542 Writes new values for v_forward, v_up, and v_right based on angles
3543 void makevectors(vector angle)
3544 ==============
3545 */
3546 void VM_makevectors (void)
3547 {
3548         prvm_eval_t *valforward, *valright, *valup;
3549         valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward);
3550         valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right);
3551         valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up);
3552         if (!valforward || !valright || !valup)
3553         {
3554                 VM_Warning("makevectors: could not find v_forward, v_right, or v_up global variables\n");
3555                 return;
3556         }
3557         VM_SAFEPARMCOUNT(1, VM_makevectors);
3558         AngleVectors (PRVM_G_VECTOR(OFS_PARM0), valforward->vector, valright->vector, valup->vector);
3559 }
3560
3561 /*
3562 ==============
3563 VM_vectorvectors
3564
3565 Writes new values for v_forward, v_up, and v_right based on the given forward vector
3566 vectorvectors(vector)
3567 ==============
3568 */
3569 void VM_vectorvectors (void)
3570 {
3571         prvm_eval_t *valforward, *valright, *valup;
3572         valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward);
3573         valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right);
3574         valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up);
3575         if (!valforward || !valright || !valup)
3576         {
3577                 VM_Warning("vectorvectors: could not find v_forward, v_right, or v_up global variables\n");
3578                 return;
3579         }
3580         VM_SAFEPARMCOUNT(1, VM_vectorvectors);
3581         VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), valforward->vector);
3582         VectorVectors(valforward->vector, valright->vector, valup->vector);
3583 }
3584
3585 /*
3586 ========================
3587 VM_drawline
3588
3589 void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
3590 ========================
3591 */
3592 void VM_drawline (void)
3593 {
3594         float   *c1, *c2, *rgb;
3595         float   alpha, width;
3596         unsigned char   flags;
3597
3598         VM_SAFEPARMCOUNT(6, VM_drawline);
3599         width   = PRVM_G_FLOAT(OFS_PARM0);
3600         c1              = PRVM_G_VECTOR(OFS_PARM1);
3601         c2              = PRVM_G_VECTOR(OFS_PARM2);
3602         rgb             = PRVM_G_VECTOR(OFS_PARM3);
3603         alpha   = PRVM_G_FLOAT(OFS_PARM4);
3604         flags   = (int)PRVM_G_FLOAT(OFS_PARM5);
3605         DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
3606 }
3607
3608 // float(float number, float quantity) bitshift (EXT_BITSHIFT)
3609 void VM_bitshift (void)
3610 {
3611         int n1, n2;
3612         VM_SAFEPARMCOUNT(2, VM_bitshift);
3613
3614         n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0));
3615         n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
3616         if(!n1)
3617                 PRVM_G_FLOAT(OFS_RETURN) = n1;
3618         else
3619         if(n2 < 0)
3620                 PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
3621         else
3622                 PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
3623 }
3624
3625 ////////////////////////////////////////
3626 // AltString functions
3627 ////////////////////////////////////////
3628
3629 /*
3630 ========================
3631 VM_altstr_count
3632
3633 float altstr_count(string)
3634 ========================
3635 */
3636 void VM_altstr_count( void )
3637 {
3638         const char *altstr, *pos;
3639         int     count;
3640
3641         VM_SAFEPARMCOUNT( 1, VM_altstr_count );
3642
3643         altstr = PRVM_G_STRING( OFS_PARM0 );
3644         //VM_CheckEmptyString( altstr );
3645
3646         for( count = 0, pos = altstr ; *pos ; pos++ ) {
3647                 if( *pos == '\\' ) {
3648                         if( !*++pos ) {
3649                                 break;
3650                         }
3651                 } else if( *pos == '\'' ) {
3652                         count++;
3653                 }
3654         }
3655
3656         PRVM_G_FLOAT( OFS_RETURN ) = (float) (count / 2);
3657 }
3658
3659 /*
3660 ========================
3661 VM_altstr_prepare
3662
3663 string altstr_prepare(string)
3664 ========================
3665 */
3666 void VM_altstr_prepare( void )
3667 {
3668         char *out;
3669         const char *instr, *in;
3670         int size;
3671         char outstr[VM_STRINGTEMP_LENGTH];
3672
3673         VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
3674
3675         instr = PRVM_G_STRING( OFS_PARM0 );
3676
3677         for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )
3678                 if( *in == '\'' ) {
3679                         *out++ = '\\';
3680                         *out = '\'';
3681                         size--;
3682                 } else
3683                         *out = *in;
3684         *out = 0;
3685
3686         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3687 }
3688
3689 /*
3690 ========================
3691 VM_altstr_get
3692
3693 string altstr_get(string, float)
3694 ========================
3695 */
3696 void VM_altstr_get( void )
3697 {
3698         const char *altstr, *pos;
3699         char *out;
3700         int count, size;
3701         char outstr[VM_STRINGTEMP_LENGTH];
3702
3703         VM_SAFEPARMCOUNT( 2, VM_altstr_get );
3704
3705         altstr = PRVM_G_STRING( OFS_PARM0 );
3706
3707         count = (int)PRVM_G_FLOAT( OFS_PARM1 );
3708         count = count * 2 + 1;
3709
3710         for( pos = altstr ; *pos && count ; pos++ )
3711                 if( *pos == '\\' ) {
3712                         if( !*++pos )
3713                                 break;
3714                 } else if( *pos == '\'' )
3715                         count--;
3716
3717         if( !*pos ) {
3718                 PRVM_G_INT( OFS_RETURN ) = 0;
3719                 return;
3720         }
3721
3722         for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
3723                 if( *pos == '\\' ) {
3724                         if( !*++pos )
3725                                 break;
3726                         *out = *pos;
3727                         size--;
3728                 } else if( *pos == '\'' )
3729                         break;
3730                 else
3731                         *out = *pos;
3732
3733         *out = 0;
3734         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3735 }
3736
3737 /*
3738 ========================
3739 VM_altstr_set
3740
3741 string altstr_set(string altstr, float num, string set)
3742 ========================
3743 */
3744 void VM_altstr_set( void )
3745 {
3746     int num;
3747         const char *altstr, *str;
3748         const char *in;
3749         char *out;
3750         char outstr[VM_STRINGTEMP_LENGTH];
3751
3752         VM_SAFEPARMCOUNT( 3, VM_altstr_set );
3753
3754         altstr = PRVM_G_STRING( OFS_PARM0 );
3755
3756         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3757
3758         str = PRVM_G_STRING( OFS_PARM2 );
3759
3760         out = outstr;
3761         for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
3762                 if( *in == '\\' ) {
3763                         if( !*++in ) {
3764                                 break;
3765                         }
3766                 } else if( *in == '\'' ) {
3767                         num--;
3768                 }
3769
3770         // copy set in
3771         for( ; *str; *out++ = *str++ );
3772         // now jump over the old content
3773         for( ; *in ; in++ )
3774                 if( *in == '\'' || (*in == '\\' && !*++in) )
3775                         break;
3776
3777         strlcpy(out, in, outstr + sizeof(outstr) - out);
3778         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3779 }
3780
3781 /*
3782 ========================
3783 VM_altstr_ins
3784 insert after num
3785 string  altstr_ins(string altstr, float num, string set)
3786 ========================
3787 */
3788 void VM_altstr_ins(void)
3789 {
3790         int num;
3791         const char *setstr;
3792         const char *set;
3793         const char *instr;
3794         const char *in;
3795         char *out;
3796         char outstr[VM_STRINGTEMP_LENGTH];
3797
3798         VM_SAFEPARMCOUNT(3, VM_altstr_ins);
3799
3800         in = instr = PRVM_G_STRING( OFS_PARM0 );
3801         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3802         set = setstr = PRVM_G_STRING( OFS_PARM2 );
3803
3804         out = outstr;
3805         for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
3806                 if( *in == '\\' ) {
3807                         if( !*++in ) {
3808                                 break;
3809                         }
3810                 } else if( *in == '\'' ) {
3811                         num--;
3812                 }
3813
3814         *out++ = '\'';
3815         for( ; *set ; *out++ = *set++ );
3816         *out++ = '\'';
3817
3818         strlcpy(out, in, outstr + sizeof(outstr) - out);
3819         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3820 }
3821
3822
3823 ////////////////////////////////////////
3824 // BufString functions
3825 ////////////////////////////////////////
3826 //[515]: string buffers support
3827
3828 static size_t stringbuffers_sortlength;
3829
3830 static void BufStr_Expand(prvm_stringbuffer_t *stringbuffer, int strindex)
3831 {
3832         if (stringbuffer->max_strings <= strindex)
3833         {
3834                 char **oldstrings = stringbuffer->strings;
3835                 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
3836                 while (stringbuffer->max_strings <= strindex)
3837                         stringbuffer->max_strings *= 2;
3838                 stringbuffer->strings = Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
3839                 if (stringbuffer->num_strings > 0)
3840                         memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
3841                 if (oldstrings)
3842                         Mem_Free(oldstrings);
3843         }
3844 }
3845
3846 static void BufStr_Shrink(prvm_stringbuffer_t *stringbuffer)
3847 {
3848         // reduce num_strings if there are empty string slots at the end
3849         while (stringbuffer->num_strings > 0 && stringbuffer->strings[stringbuffer->num_strings - 1] == NULL)
3850                 stringbuffer->num_strings--;
3851
3852         // if empty, free the string pointer array
3853         if (stringbuffer->num_strings == 0)
3854         {
3855                 stringbuffer->max_strings = 0;
3856                 if (stringbuffer->strings)
3857                         Mem_Free(stringbuffer->strings);
3858                 stringbuffer->strings = NULL;
3859         }
3860 }
3861
3862 static int BufStr_SortStringsUP (const void *in1, const void *in2)
3863 {
3864         const char *a, *b;
3865         a = *((const char **) in1);
3866         b = *((const char **) in2);
3867         if(!a[0])       return 1;
3868         if(!b[0])       return -1;
3869         return strncmp(a, b, stringbuffers_sortlength);
3870 }
3871
3872 static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
3873 {
3874         const char *a, *b;
3875         a = *((const char **) in1);
3876         b = *((const char **) in2);
3877         if(!a[0])       return 1;
3878         if(!b[0])       return -1;
3879         return strncmp(b, a, stringbuffers_sortlength);
3880 }
3881
3882 /*
3883 ========================
3884 VM_buf_create
3885 creates new buffer, and returns it's index, returns -1 if failed
3886 float buf_create(void) = #460;
3887 ========================
3888 */
3889 void VM_buf_create (void)
3890 {
3891         prvm_stringbuffer_t *stringbuffer;
3892         int i;
3893         VM_SAFEPARMCOUNT(0, VM_buf_create);
3894         stringbuffer = Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
3895         for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
3896         PRVM_G_FLOAT(OFS_RETURN) = i;
3897 }
3898
3899 /*
3900 ========================
3901 VM_buf_del
3902 deletes buffer and all strings in it
3903 void buf_del(float bufhandle) = #461;
3904 ========================
3905 */
3906 void VM_buf_del (void)
3907 {
3908         prvm_stringbuffer_t *stringbuffer;
3909         VM_SAFEPARMCOUNT(1, VM_buf_del);
3910         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
3911         if (stringbuffer)
3912         {
3913                 int i;
3914                 for (i = 0;i < stringbuffer->num_strings;i++)
3915                         if (stringbuffer->strings[i])
3916                                 Mem_Free(stringbuffer->strings[i]);
3917                 if (stringbuffer->strings)
3918                         Mem_Free(stringbuffer->strings);
3919                 Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
3920         }
3921         else
3922         {
3923                 VM_Warning("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3924                 return;
3925         }
3926 }
3927
3928 /*
3929 ========================
3930 VM_buf_getsize
3931 how many strings are stored in buffer
3932 float buf_getsize(float bufhandle) = #462;
3933 ========================
3934 */
3935 void VM_buf_getsize (void)
3936 {
3937         prvm_stringbuffer_t *stringbuffer;
3938         VM_SAFEPARMCOUNT(1, VM_buf_getsize);
3939
3940         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
3941         if(!stringbuffer)
3942         {
3943                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3944                 VM_Warning("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3945                 return;
3946         }
3947         else
3948                 PRVM_G_FLOAT(OFS_RETURN) = stringbuffer->num_strings;
3949 }
3950
3951 /*
3952 ========================
3953 VM_buf_copy
3954 copy all content from one buffer to another, make sure it exists
3955 void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
3956 ========================
3957 */
3958 void VM_buf_copy (void)
3959 {
3960         prvm_stringbuffer_t *srcstringbuffer, *dststringbuffer;
3961         int i;
3962         VM_SAFEPARMCOUNT(2, VM_buf_copy);
3963
3964         srcstringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
3965         if(!srcstringbuffer)
3966         {
3967                 VM_Warning("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3968                 return;
3969         }
3970         i = (int)PRVM_G_FLOAT(OFS_PARM1);
3971         if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
3972         {
3973                 VM_Warning("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME);
3974                 return;
3975         }
3976         dststringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
3977         if(!dststringbuffer)
3978         {
3979                 VM_Warning("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
3980                 return;
3981         }
3982
3983         for (i = 0;i < dststringbuffer->num_strings;i++)
3984                 if (dststringbuffer->strings[i])
3985                         Mem_Free(dststringbuffer->strings[i]);
3986         if (dststringbuffer->strings)
3987                 Mem_Free(dststringbuffer->strings);
3988         *dststringbuffer = *srcstringbuffer;
3989         if (dststringbuffer->max_strings)
3990                 dststringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(dststringbuffer->strings[0]) * dststringbuffer->max_strings);
3991
3992         for (i = 0;i < dststringbuffer->num_strings;i++)
3993         {
3994                 if (srcstringbuffer->strings[i])
3995                 {
3996                         size_t stringlen;
3997                         stringlen = strlen(srcstringbuffer->strings[i]) + 1;
3998                         dststringbuffer->strings[i] = (char *)Mem_Alloc(prog->progs_mempool, stringlen);
3999                         memcpy(dststringbuffer->strings[i], srcstringbuffer->strings[i], stringlen);
4000                 }
4001         }
4002 }
4003
4004 /*
4005 ========================
4006 VM_buf_sort
4007 sort buffer by beginnings of strings (cmplength defaults it's length)
4008 "backward == TRUE" means that sorting goes upside-down
4009 void buf_sort(float bufhandle, float cmplength, float backward) = #464;
4010 ========================
4011 */
4012 void VM_buf_sort (void)
4013 {
4014         prvm_stringbuffer_t *stringbuffer;
4015         VM_SAFEPARMCOUNT(3, VM_buf_sort);
4016
4017         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4018         if(!stringbuffer)
4019         {
4020                 VM_Warning("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4021                 return;
4022         }
4023         if(stringbuffer->num_strings <= 0)
4024         {
4025                 VM_Warning("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4026                 return;
4027         }
4028         stringbuffers_sortlength = (int)PRVM_G_FLOAT(OFS_PARM1);
4029         if(stringbuffers_sortlength <= 0)
4030                 stringbuffers_sortlength = 0x7FFFFFFF;
4031
4032         if(!PRVM_G_FLOAT(OFS_PARM2))
4033                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsUP);
4034         else
4035                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
4036
4037         BufStr_Shrink(stringbuffer);
4038 }
4039
4040 /*
4041 ========================
4042 VM_buf_implode
4043 concantenates all buffer string into one with "glue" separator and returns it as tempstring
4044 string buf_implode(float bufhandle, string glue) = #465;
4045 ========================
4046 */
4047 void VM_buf_implode (void)
4048 {
4049         prvm_stringbuffer_t *stringbuffer;
4050         char                    k[VM_STRINGTEMP_LENGTH];
4051         const char              *sep;
4052         int                             i;
4053         size_t                  l;
4054         VM_SAFEPARMCOUNT(2, VM_buf_implode);
4055
4056         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4057         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4058         if(!stringbuffer)
4059         {
4060                 VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4061                 return;
4062         }
4063         if(!stringbuffer->num_strings)
4064                 return;
4065         sep = PRVM_G_STRING(OFS_PARM1);
4066         k[0] = 0;
4067         for(l = i = 0;i < stringbuffer->num_strings;i++)
4068         {
4069                 if(stringbuffer->strings[i])
4070                 {
4071                         l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
4072                         if (l >= sizeof(k) - 1)
4073                                 break;
4074                         strlcat(k, sep, sizeof(k));
4075                         strlcat(k, stringbuffer->strings[i], sizeof(k));
4076                 }
4077         }
4078         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(k);
4079 }
4080
4081 /*
4082 ========================
4083 VM_bufstr_get
4084 get a string from buffer, returns tempstring, dont str_unzone it!
4085 string bufstr_get(float bufhandle, float string_index) = #465;
4086 ========================
4087 */
4088 void VM_bufstr_get (void)
4089 {
4090         prvm_stringbuffer_t *stringbuffer;
4091         int                             strindex;
4092         VM_SAFEPARMCOUNT(2, VM_bufstr_get);
4093
4094         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4095         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4096         if(!stringbuffer)
4097         {
4098                 VM_Warning("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4099                 return;
4100         }
4101         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4102         if (strindex < 0)
4103         {
4104                 VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
4105                 return;
4106         }
4107         if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
4108                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(stringbuffer->strings[strindex]);
4109 }
4110
4111 /*
4112 ========================
4113 VM_bufstr_set
4114 copies a string into selected slot of buffer
4115 void bufstr_set(float bufhandle, float string_index, string str) = #466;
4116 ========================
4117 */
4118 void VM_bufstr_set (void)
4119 {
4120         int                             strindex;
4121         prvm_stringbuffer_t *stringbuffer;
4122         const char              *news;
4123
4124         VM_SAFEPARMCOUNT(3, VM_bufstr_set);
4125
4126         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4127         if(!stringbuffer)
4128         {
4129                 VM_Warning("VM_bufstr_set: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4130                 return;
4131         }
4132         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4133         if(strindex < 0 || strindex >= 1000000) // huge number of strings
4134         {
4135                 VM_Warning("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME);
4136                 return;
4137         }
4138
4139         BufStr_Expand(stringbuffer, strindex);
4140         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4141
4142         if(stringbuffer->strings[strindex])
4143                 Mem_Free(stringbuffer->strings[strindex]);
4144         stringbuffer->strings[strindex] = NULL;
4145
4146         news = PRVM_G_STRING(OFS_PARM2);
4147         if (news && news[0])
4148         {
4149                 size_t alloclen = strlen(news) + 1;
4150                 stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4151                 memcpy(stringbuffer->strings[strindex], news, alloclen);
4152         }
4153
4154         BufStr_Shrink(stringbuffer);
4155 }
4156
4157 /*
4158 ========================
4159 VM_bufstr_add
4160 adds string to buffer in first free slot and returns its index
4161 "order == TRUE" means that string will be added after last "full" slot
4162 float bufstr_add(float bufhandle, string str, float order) = #467;
4163 ========================
4164 */
4165 void VM_bufstr_add (void)
4166 {
4167         int                             order, strindex;
4168         prvm_stringbuffer_t *stringbuffer;
4169         const char              *string;
4170         size_t                  alloclen;
4171
4172         VM_SAFEPARMCOUNT(3, VM_bufstr_add);
4173
4174         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4175         PRVM_G_FLOAT(OFS_RETURN) = -1;
4176         if(!stringbuffer)
4177         {
4178                 VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4179                 return;
4180         }
4181         string = PRVM_G_STRING(OFS_PARM1);
4182         if(!string || !string[0])
4183         {
4184                 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);
4185                 return;
4186         }
4187         order = (int)PRVM_G_FLOAT(OFS_PARM2);
4188         if(order)
4189                 strindex = stringbuffer->num_strings;
4190         else
4191                 for (strindex = 0;strindex < stringbuffer->num_strings;strindex++)
4192                         if (stringbuffer->strings[strindex] == NULL)
4193                                 break;
4194
4195         BufStr_Expand(stringbuffer, strindex);
4196
4197         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4198         alloclen = strlen(string) + 1;
4199         stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4200         memcpy(stringbuffer->strings[strindex], string, alloclen);
4201
4202         PRVM_G_FLOAT(OFS_RETURN) = strindex;
4203 }
4204
4205 /*
4206 ========================
4207 VM_bufstr_free
4208 delete string from buffer
4209 void bufstr_free(float bufhandle, float string_index) = #468;
4210 ========================
4211 */
4212 void VM_bufstr_free (void)
4213 {
4214         int                             i;
4215         prvm_stringbuffer_t     *stringbuffer;
4216         VM_SAFEPARMCOUNT(2, VM_bufstr_free);
4217
4218         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4219         if(!stringbuffer)
4220         {
4221                 VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
4222                 return;
4223         }
4224         i = (int)PRVM_G_FLOAT(OFS_PARM1);
4225         if(i < 0)
4226         {
4227                 VM_Warning("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME);
4228                 return;
4229         }
4230
4231         if (i < stringbuffer->num_strings)
4232         {
4233                 if(stringbuffer->strings[i])
4234                         Mem_Free(stringbuffer->strings[i]);
4235                 stringbuffer->strings[i] = NULL;
4236         }
4237
4238         BufStr_Shrink(stringbuffer);
4239 }
4240
4241 //=============
4242
4243 /*
4244 ==============
4245 VM_changeyaw
4246
4247 This was a major timewaster in progs, so it was converted to C
4248 ==============
4249 */
4250 void VM_changeyaw (void)
4251 {
4252         prvm_edict_t            *ent;
4253         float           ideal, current, move, speed;
4254
4255         // this is called (VERY HACKISHLY) by SV_MoveToGoal, so it can not use any
4256         // parameters because they are the parameters to SV_MoveToGoal, not this
4257         //VM_SAFEPARMCOUNT(0, VM_changeyaw);
4258
4259         ent = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict);
4260         if (ent == prog->edicts)
4261         {
4262                 VM_Warning("changeyaw: can not modify world entity\n");
4263                 return;
4264         }
4265         if (ent->priv.server->free)
4266         {
4267                 VM_Warning("changeyaw: can not modify free entity\n");
4268                 return;
4269         }
4270         if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.ideal_yaw < 0 || prog->fieldoffsets.yaw_speed < 0)
4271         {
4272                 VM_Warning("changeyaw: angles, ideal_yaw, or yaw_speed field(s) not found\n");
4273                 return;
4274         }
4275         current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1]);
4276         ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.ideal_yaw)->_float;
4277         speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.yaw_speed)->_float;
4278
4279         if (current == ideal)
4280                 return;
4281         move = ideal - current;
4282         if (ideal > current)
4283         {
4284                 if (move >= 180)
4285                         move = move - 360;
4286         }
4287         else
4288         {
4289                 if (move <= -180)
4290                         move = move + 360;
4291         }
4292         if (move > 0)
4293         {
4294                 if (move > speed)
4295                         move = speed;
4296         }
4297         else
4298         {
4299                 if (move < -speed)
4300                         move = -speed;
4301         }
4302
4303         PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1] = ANGLEMOD (current + move);
4304 }
4305
4306 /*
4307 ==============
4308 VM_changepitch
4309 ==============
4310 */
4311 void VM_changepitch (void)
4312 {
4313         prvm_edict_t            *ent;
4314         float           ideal, current, move, speed;
4315
4316         VM_SAFEPARMCOUNT(1, VM_changepitch);
4317
4318         ent = PRVM_G_EDICT(OFS_PARM0);
4319         if (ent == prog->edicts)
4320         {
4321                 VM_Warning("changepitch: can not modify world entity\n");
4322                 return;
4323         }
4324         if (ent->priv.server->free)
4325         {
4326                 VM_Warning("changepitch: can not modify free entity\n");
4327                 return;
4328         }
4329         if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.idealpitch < 0 || prog->fieldoffsets.pitch_speed < 0)
4330         {
4331                 VM_Warning("changepitch: angles, idealpitch, or pitch_speed field(s) not found\n");
4332                 return;
4333         }
4334         current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0]);
4335         ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.idealpitch)->_float;
4336         speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pitch_speed)->_float;
4337
4338         if (current == ideal)
4339                 return;
4340         move = ideal - current;
4341         if (ideal > current)
4342         {
4343                 if (move >= 180)
4344                         move = move - 360;
4345         }
4346         else
4347         {
4348                 if (move <= -180)
4349                         move = move + 360;
4350         }
4351         if (move > 0)
4352         {
4353                 if (move > speed)
4354                         move = speed;
4355         }
4356         else
4357         {
4358                 if (move < -speed)
4359                         move = -speed;
4360         }
4361
4362         PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0] = ANGLEMOD (current + move);
4363 }
4364
4365 // TODO: adapt all static function names to use a single naming convention... [12/3/2007 Black]
4366 static int Is_Text_Color (char c, char t)
4367 {
4368         int a = 0;
4369         char c2 = c - (c & 128);
4370         char t2 = t - (t & 128);
4371
4372         if(c != STRING_COLOR_TAG && c2 != STRING_COLOR_TAG)             return 0;
4373         if(t >= '0' && t <= '9')                a = 1;
4374         if(t2 >= '0' && t2 <= '9')              a = 1;
4375 /*      if(t >= 'A' && t <= 'Z')                a = 2;
4376         if(t2 >= 'A' && t2 <= 'Z')              a = 2;
4377
4378         if(a == 1 && scr_colortext.integer > 0)
4379                 return 1;
4380         if(a == 2 && scr_multifonts.integer > 0)
4381                 return 2;
4382 */
4383         return a;
4384 }
4385
4386 void VM_uncolorstring (void)
4387 {
4388         const char      *in;
4389         char            out[VM_STRINGTEMP_LENGTH];
4390         int                     k = 0, i = 0;
4391
4392         VM_SAFEPARMCOUNT(1, VM_uncolorstring);
4393         in = PRVM_G_STRING(OFS_PARM0);
4394         VM_CheckEmptyString (in);
4395
4396         while (in[k])
4397         {
4398                 if(in[k+1])
4399                 if(Is_Text_Color(in[k], in[k+1]) == 1/* || (in[k] == '&' && in[k+1] == 'r')*/)
4400                 {
4401                         k += 2;
4402                         continue;
4403                 }
4404                 out[i] = in[k];
4405                 ++k;
4406                 ++i;
4407         }
4408         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(out);
4409 }
4410
4411 // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
4412 //strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.
4413 void VM_strstrofs (void)
4414 {
4415         const char *instr, *match;
4416         int firstofs;
4417         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
4418         instr = PRVM_G_STRING(OFS_PARM0);
4419         match = PRVM_G_STRING(OFS_PARM1);
4420         firstofs = (prog->argc > 2)?PRVM_G_FLOAT(OFS_PARM2):0;
4421
4422         if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
4423         {
4424                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4425                 return;
4426         }
4427
4428         match = strstr(instr+firstofs, match);
4429         if (!match)
4430                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4431         else
4432                 PRVM_G_FLOAT(OFS_RETURN) = match - instr;
4433 }
4434
4435 //#222 string(string s, float index) str2chr (FTE_STRINGS)
4436 void VM_str2chr (void)
4437 {
4438         const char *s;
4439         VM_SAFEPARMCOUNT(2, VM_str2chr);
4440         s = PRVM_G_STRING(OFS_PARM0);
4441         if((unsigned)PRVM_G_FLOAT(OFS_PARM1) < strlen(s))
4442                 PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(unsigned)PRVM_G_FLOAT(OFS_PARM1)];
4443         else
4444                 PRVM_G_FLOAT(OFS_RETURN) = 0;
4445 }
4446
4447 //#223 string(float c, ...) chr2str (FTE_STRINGS)
4448 void VM_chr2str (void)
4449 {
4450         char    t[9];
4451         int             i;
4452         VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
4453         for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
4454                 t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
4455         t[i] = 0;
4456         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
4457 }
4458
4459 static int chrconv_number(int i, int base, int conv)
4460 {
4461         i -= base;
4462         switch (conv)
4463         {
4464         default:
4465         case 5:
4466         case 6:
4467         case 0:
4468                 break;
4469         case 1:
4470                 base = '0';
4471                 break;
4472         case 2:
4473                 base = '0'+128;
4474                 break;
4475         case 3:
4476                 base = '0'-30;
4477                 break;
4478         case 4:
4479                 base = '0'+128-30;
4480                 break;
4481         }
4482         return i + base;
4483 }
4484 static int chrconv_punct(int i, int base, int conv)
4485 {
4486         i -= base;
4487         switch (conv)
4488         {
4489         default:
4490         case 0:
4491                 break;
4492         case 1:
4493                 base = 0;
4494                 break;
4495         case 2:
4496                 base = 128;
4497                 break;
4498         }
4499         return i + base;
4500 }
4501
4502 static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
4503 {
4504         //convert case and colour seperatly...
4505
4506         i -= baset + basec;
4507         switch (convt)
4508         {
4509         default:
4510         case 0:
4511                 break;
4512         case 1:
4513                 baset = 0;
4514                 break;
4515         case 2:
4516                 baset = 128;
4517                 break;
4518
4519         case 5:
4520         case 6:
4521                 baset = 128*((charnum&1) == (convt-5));
4522                 break;
4523         }
4524
4525         switch (convc)
4526         {
4527         default:
4528         case 0:
4529                 break;
4530         case 1:
4531                 basec = 'a';
4532                 break;
4533         case 2:
4534                 basec = 'A';
4535                 break;
4536         }
4537         return i + basec + baset;
4538 }
4539 // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
4540 //bulk convert a string. change case or colouring.
4541 void VM_strconv (void)
4542 {
4543         int ccase, redalpha, rednum, len, i;
4544         unsigned char resbuf[VM_STRINGTEMP_LENGTH];
4545         unsigned char *result = resbuf;
4546
4547         VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
4548
4549         ccase = PRVM_G_FLOAT(OFS_PARM0);        //0 same, 1 lower, 2 upper
4550         redalpha = PRVM_G_FLOAT(OFS_PARM1);     //0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate
4551         rednum = PRVM_G_FLOAT(OFS_PARM2);       //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
4552         VM_VarString(3, (char *) resbuf, sizeof(resbuf));
4553         len = strlen((char *) resbuf);
4554
4555         for (i = 0; i < len; i++, result++)     //should this be done backwards?
4556         {
4557                 if (*result >= '0' && *result <= '9')   //normal numbers...
4558                         *result = chrconv_number(*result, '0', rednum);
4559                 else if (*result >= '0'+128 && *result <= '9'+128)
4560                         *result = chrconv_number(*result, '0'+128, rednum);
4561                 else if (*result >= '0'+128-30 && *result <= '9'+128-30)
4562                         *result = chrconv_number(*result, '0'+128-30, rednum);
4563                 else if (*result >= '0'-30 && *result <= '9'-30)
4564                         *result = chrconv_number(*result, '0'-30, rednum);
4565
4566                 else if (*result >= 'a' && *result <= 'z')      //normal numbers...
4567                         *result = chrchar_alpha(*result, 'a', 0, ccase, redalpha, i);
4568                 else if (*result >= 'A' && *result <= 'Z')      //normal numbers...
4569                         *result = chrchar_alpha(*result, 'A', 0, ccase, redalpha, i);
4570                 else if (*result >= 'a'+128 && *result <= 'z'+128)      //normal numbers...
4571                         *result = chrchar_alpha(*result, 'a', 128, ccase, redalpha, i);
4572                 else if (*result >= 'A'+128 && *result <= 'Z'+128)      //normal numbers...
4573                         *result = chrchar_alpha(*result, 'A', 128, ccase, redalpha, i);
4574
4575                 else if ((*result & 127) < 16 || !redalpha)     //special chars..
4576                         *result = *result;
4577                 else if (*result < 128)
4578                         *result = chrconv_punct(*result, 0, redalpha);
4579                 else
4580                         *result = chrconv_punct(*result, 128, redalpha);
4581         }
4582         *result = '\0';
4583
4584         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString((char *) resbuf);
4585 }
4586
4587 // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
4588 void VM_strpad (void)
4589 {
4590         char src[VM_STRINGTEMP_LENGTH];
4591         char destbuf[VM_STRINGTEMP_LENGTH];
4592         int pad;
4593         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
4594         pad = PRVM_G_FLOAT(OFS_PARM0);
4595         VM_VarString(1, src, sizeof(src));
4596
4597         // note: < 0 = left padding, > 0 = right padding,
4598         // this is reverse logic of printf!
4599         dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
4600
4601         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(destbuf);
4602 }
4603
4604 // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
4605 //uses qw style \key\value strings
4606 void VM_infoadd (void)
4607 {
4608         const char *info, *key;
4609         char value[VM_STRINGTEMP_LENGTH];
4610         char temp[VM_STRINGTEMP_LENGTH];
4611
4612         VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
4613         info = PRVM_G_STRING(OFS_PARM0);
4614         key = PRVM_G_STRING(OFS_PARM1);
4615         VM_VarString(2, value, sizeof(value));
4616
4617         strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
4618
4619         InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
4620
4621         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(temp);
4622 }
4623
4624 // #227 string(string info, string key) infoget (FTE_STRINGS)
4625 //uses qw style \key\value strings
4626 void VM_infoget (void)
4627 {
4628         const char *info;
4629         const char *key;
4630         char value[VM_STRINGTEMP_LENGTH];
4631
4632         VM_SAFEPARMCOUNT(2, VM_infoget);
4633         info = PRVM_G_STRING(OFS_PARM0);
4634         key = PRVM_G_STRING(OFS_PARM1);
4635
4636         InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
4637
4638         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(value);
4639 }
4640
4641 //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
4642 // also float(string s1, string s2) strcmp (FRIK_FILE)
4643 void VM_strncmp (void)
4644 {
4645         const char *s1, *s2;
4646         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncmp);
4647         s1 = PRVM_G_STRING(OFS_PARM0);
4648         s2 = PRVM_G_STRING(OFS_PARM1);
4649         if (prog->argc > 2)
4650         {
4651                 PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
4652         }
4653         else
4654         {
4655                 PRVM_G_FLOAT(OFS_RETURN) = strcmp(s1, s2);
4656         }
4657 }
4658
4659 // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
4660 // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
4661 void VM_strncasecmp (void)
4662 {
4663         const char *s1, *s2;
4664         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncasecmp);
4665         s1 = PRVM_G_STRING(OFS_PARM0);
4666         s2 = PRVM_G_STRING(OFS_PARM1);
4667         if (prog->argc > 2)
4668         {
4669                 PRVM_G_FLOAT(OFS_RETURN) = strncasecmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
4670         }
4671         else
4672         {
4673                 PRVM_G_FLOAT(OFS_RETURN) = strcasecmp(s1, s2);
4674         }
4675 }
4676
4677 // #494 float(float caseinsensitive, string s, ...) crc16
4678 void VM_crc16(void)
4679 {
4680         float insensitive;
4681         static char s[VM_STRINGTEMP_LENGTH];
4682         VM_SAFEPARMCOUNTRANGE(2, 8, VM_hash);
4683         insensitive = PRVM_G_FLOAT(OFS_PARM0);
4684         VM_VarString(1, s, sizeof(s));
4685         PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
4686 }
4687
4688 void VM_wasfreed (void)
4689 {
4690         VM_SAFEPARMCOUNT(1, VM_wasfreed);
4691         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICT(OFS_PARM0)->priv.required->free;
4692 }
4693
4694 void VM_SetTraceGlobals(const trace_t *trace)
4695 {
4696         prvm_eval_t *val;
4697         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_allsolid)))
4698                 val->_float = trace->allsolid;
4699         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_startsolid)))
4700                 val->_float = trace->startsolid;
4701         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_fraction)))
4702                 val->_float = trace->fraction;
4703         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inwater)))
4704                 val->_float = trace->inwater;
4705         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inopen)))
4706                 val->_float = trace->inopen;
4707         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos)))
4708                 VectorCopy(trace->endpos, val->vector);
4709         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_normal)))
4710                 VectorCopy(trace->plane.normal, val->vector);
4711         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_dist)))
4712                 val->_float = trace->plane.dist;
4713         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_ent)))
4714                 val->edict = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
4715         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
4716                 val->_float = trace->startsupercontents;
4717         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
4718                 val->_float = trace->hitsupercontents;
4719         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
4720                 val->_float = trace->hitq3surfaceflags;
4721         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
4722                 val->string = trace->hittexture ? PRVM_SetTempString(trace->hittexture->name) : 0;
4723 }
4724
4725 //=============
4726
4727 void VM_Cmd_Init(void)
4728 {
4729         // only init the stuff for the current prog
4730         VM_Files_Init();
4731         VM_Search_Init();
4732         VM_Gecko_Init();
4733 //      VM_BufStr_Init();
4734 }
4735
4736 void VM_Cmd_Reset(void)
4737 {
4738         CL_PurgeOwner( MENUOWNER );
4739         VM_Search_Reset();
4740         VM_Files_CloseAll();
4741         VM_Gecko_Destroy();
4742 //      VM_BufStr_ShutDown();
4743 }
4744
4745 // #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
4746 // does URI escaping on a string (replace evil stuff by %AB escapes)
4747 void VM_uri_escape (void)
4748 {
4749         char src[VM_STRINGTEMP_LENGTH];
4750         char dest[VM_STRINGTEMP_LENGTH];
4751         char *p, *q;
4752         static const char *hex = "0123456789ABCDEF";
4753
4754         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_escape);
4755         VM_VarString(0, src, sizeof(src));
4756
4757         for(p = src, q = dest; *p && q < dest + sizeof(dest) - 3; ++p)
4758         {
4759                 if((*p >= 'A' && *p <= 'Z')
4760                         || (*p >= 'a' && *p <= 'z')
4761                         || (*p >= '0' && *p <= '9')
4762                         || (*p == '-')  || (*p == '_') || (*p == '.')
4763                         || (*p == '!')  || (*p == '~') || (*p == '*')
4764                         || (*p == '\'') || (*p == '(') || (*p == ')'))
4765                         *q++ = *p;
4766                 else
4767                 {
4768                         *q++ = '%';
4769                         *q++ = hex[(*(unsigned char *)p >> 4) & 0xF];
4770                         *q++ = hex[ *(unsigned char *)p       & 0xF];
4771                 }
4772         }
4773         *q++ = 0;
4774
4775         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
4776 }
4777
4778 // #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
4779 // does URI unescaping on a string (get back the evil stuff)
4780 void VM_uri_unescape (void)
4781 {
4782         char src[VM_STRINGTEMP_LENGTH];
4783         char dest[VM_STRINGTEMP_LENGTH];
4784         char *p, *q;
4785         int hi, lo;
4786
4787         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_unescape);
4788         VM_VarString(0, src, sizeof(src));
4789
4790         for(p = src, q = dest; *p; ) // no need to check size, because unescape can't expand
4791         {
4792                 if(*p == '%')
4793                 {
4794                         if(p[1] >= '0' && p[1] <= '9')
4795                                 hi = p[1] - '0';
4796                         else if(p[1] >= 'a' && p[1] <= 'f')
4797                                 hi = p[1] - 'a' + 10;
4798                         else if(p[1] >= 'A' && p[1] <= 'F')
4799                                 hi = p[1] - 'A' + 10;
4800                         else
4801                                 goto nohex;
4802                         if(p[2] >= '0' && p[2] <= '9')
4803                                 lo = p[2] - '0';
4804                         else if(p[2] >= 'a' && p[2] <= 'f')
4805                                 lo = p[2] - 'a' + 10;
4806                         else if(p[2] >= 'A' && p[2] <= 'F')
4807                                 lo = p[2] - 'A' + 10;
4808                         else
4809                                 goto nohex;
4810                         if(hi != 0 || lo != 0) // don't unescape NUL bytes
4811                                 *q++ = (char) (hi * 0x10 + lo);
4812                         p += 3;
4813                         continue;
4814                 }
4815
4816 nohex:
4817                 // otherwise:
4818                 *q++ = *p++;
4819         }
4820         *q++ = 0;
4821
4822         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(dest);
4823 }
4824