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