]> icculus.org git repositories - divverent/darkplaces.git/blob - prvm_cmds.c
some initial changes to support terrain texture blending someday
[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 "prvm_cmds.h"
8 #include <time.h>
9
10 // LordHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value
11 void VM_Warning(const char *fmt, ...)
12 {
13         va_list argptr;
14         char msg[MAX_INPUTLINE];
15
16         va_start(argptr,fmt);
17         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
18         va_end(argptr);
19
20         Con_Print(msg);
21         // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black]
22         //PRVM_PrintState();
23 }
24
25
26 //============================================================================
27 // Common
28
29 // TODO DONE: move vm_files and vm_fssearchlist to prvm_prog_t struct
30 // TODO: move vm_files and vm_fssearchlist back [9/13/2006 Black]
31 // TODO: (move vm_files and vm_fssearchlist to prvm_prog_t struct again) [2007-01-23 LordHavoc]
32 // TODO: will this war ever end? [2007-01-23 LordHavoc]
33
34 void VM_CheckEmptyString (const char *s)
35 {
36         if (s[0] <= ' ')
37                 PRVM_ERROR ("%s: Bad string", PRVM_NAME);
38 }
39
40 //============================================================================
41 //BUILT-IN FUNCTIONS
42
43 void VM_VarString(int first, char *out, int outlength)
44 {
45         int i;
46         const char *s;
47         char *outend;
48
49         outend = out + outlength - 1;
50         for (i = first;i < prog->argc && out < outend;i++)
51         {
52                 s = PRVM_G_STRING((OFS_PARM0+i*3));
53                 while (out < outend && *s)
54                         *out++ = *s++;
55         }
56         *out++ = 0;
57 }
58
59 /*
60 =================
61 VM_checkextension
62
63 returns true if the extension is supported by the server
64
65 checkextension(extensionname)
66 =================
67 */
68
69 // kind of helper function
70 static qboolean checkextension(const char *name)
71 {
72         int len;
73         char *e, *start;
74         len = (int)strlen(name);
75
76         for (e = prog->extensionstring;*e;e++)
77         {
78                 while (*e == ' ')
79                         e++;
80                 if (!*e)
81                         break;
82                 start = e;
83                 while (*e && *e != ' ')
84                         e++;
85                 if ((e - start) == len && !strncasecmp(start, name, len))
86                         return true;
87         }
88         return false;
89 }
90
91 void VM_checkextension (void)
92 {
93         VM_SAFEPARMCOUNT(1,VM_checkextension);
94
95         PRVM_G_FLOAT(OFS_RETURN) = checkextension(PRVM_G_STRING(OFS_PARM0));
96 }
97
98 /*
99 =================
100 VM_error
101
102 This is a TERMINAL error, which will kill off the entire prog.
103 Dumps self.
104
105 error(value)
106 =================
107 */
108 void VM_error (void)
109 {
110         prvm_edict_t    *ed;
111         char string[VM_STRINGTEMP_LENGTH];
112
113         VM_VarString(0, string, sizeof(string));
114         Con_Printf("======%s ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string);
115         if(prog->self)
116         {
117                 ed = PRVM_G_EDICT(prog->self->ofs);
118                 PRVM_ED_Print(ed);
119         }
120
121         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);
122 }
123
124 /*
125 =================
126 VM_objerror
127
128 Dumps out self, then an error message.  The program is aborted and self is
129 removed, but the level can continue.
130
131 objerror(value)
132 =================
133 */
134 void VM_objerror (void)
135 {
136         prvm_edict_t    *ed;
137         char string[VM_STRINGTEMP_LENGTH];
138
139         VM_VarString(0, string, sizeof(string));
140         Con_Printf("======OBJECT ERROR======\n"); // , PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); // or include them? FIXME
141         if(prog->self)
142         {
143                 ed = PRVM_G_EDICT (prog->self->ofs);
144                 PRVM_ED_Print(ed);
145
146                 PRVM_ED_Free (ed);
147         }
148         else
149                 // objerror has to display the object fields -> else call
150                 PRVM_ERROR ("VM_objecterror: self not defined !");
151         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);
152 }
153
154 /*
155 =================
156 VM_print
157
158 print to console
159
160 print(...[string])
161 =================
162 */
163 void VM_print (void)
164 {
165         char string[VM_STRINGTEMP_LENGTH];
166
167         VM_VarString(0, string, sizeof(string));
168         Con_Print(string);
169 }
170
171 /*
172 =================
173 VM_bprint
174
175 broadcast print to everyone on server
176
177 bprint(...[string])
178 =================
179 */
180 void VM_bprint (void)
181 {
182         char string[VM_STRINGTEMP_LENGTH];
183
184         if(!sv.active)
185         {
186                 VM_Warning("VM_bprint: game is not server(%s) !\n", PRVM_NAME);
187                 return;
188         }
189
190         VM_VarString(0, string, sizeof(string));
191         SV_BroadcastPrint(string);
192 }
193
194 /*
195 =================
196 VM_sprint (menu & client but only if server.active == true)
197
198 single print to a specific client
199
200 sprint(float clientnum,...[string])
201 =================
202 */
203 void VM_sprint (void)
204 {
205         client_t        *client;
206         int                     clientnum;
207         char string[VM_STRINGTEMP_LENGTH];
208
209         //find client for this entity
210         clientnum = (int)PRVM_G_FLOAT(OFS_PARM0);
211         if (!sv.active  || clientnum < 0 || clientnum >= svs.maxclients || !svs.clients[clientnum].active)
212         {
213                 VM_Warning("VM_sprint: %s: invalid client or server is not active !\n", PRVM_NAME);
214                 return;
215         }
216
217         client = svs.clients + clientnum;
218         if (!client->netconnection)
219                 return;
220
221         VM_VarString(1, string, sizeof(string));
222         MSG_WriteChar(&client->netconnection->message,svc_print);
223         MSG_WriteString(&client->netconnection->message, string);
224 }
225
226 /*
227 =================
228 VM_centerprint
229
230 single print to the screen
231
232 centerprint(clientent, value)
233 =================
234 */
235 void VM_centerprint (void)
236 {
237         char string[VM_STRINGTEMP_LENGTH];
238
239         VM_VarString(0, string, sizeof(string));
240         SCR_CenterPrint(string);
241 }
242
243 /*
244 =================
245 VM_normalize
246
247 vector normalize(vector)
248 =================
249 */
250 void VM_normalize (void)
251 {
252         float   *value1;
253         vec3_t  newvalue;
254         double  f;
255
256         VM_SAFEPARMCOUNT(1,VM_normalize);
257
258         value1 = PRVM_G_VECTOR(OFS_PARM0);
259
260         f = VectorLength2(value1);
261         if (f)
262         {
263                 f = 1.0 / sqrt(f);
264                 VectorScale(value1, f, newvalue);
265         }
266         else
267                 VectorClear(newvalue);
268
269         VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN));
270 }
271
272 /*
273 =================
274 VM_vlen
275
276 scalar vlen(vector)
277 =================
278 */
279 void VM_vlen (void)
280 {
281         VM_SAFEPARMCOUNT(1,VM_vlen);
282         PRVM_G_FLOAT(OFS_RETURN) = VectorLength(PRVM_G_VECTOR(OFS_PARM0));
283 }
284
285 /*
286 =================
287 VM_vectoyaw
288
289 float vectoyaw(vector)
290 =================
291 */
292 void VM_vectoyaw (void)
293 {
294         float   *value1;
295         float   yaw;
296
297         VM_SAFEPARMCOUNT(1,VM_vectoyaw);
298
299         value1 = PRVM_G_VECTOR(OFS_PARM0);
300
301         if (value1[1] == 0 && value1[0] == 0)
302                 yaw = 0;
303         else
304         {
305                 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
306                 if (yaw < 0)
307                         yaw += 360;
308         }
309
310         PRVM_G_FLOAT(OFS_RETURN) = yaw;
311 }
312
313
314 /*
315 =================
316 VM_vectoangles
317
318 vector vectoangles(vector)
319 =================
320 */
321 void VM_vectoangles (void)
322 {
323         float   *value1;
324         float   forward;
325         float   yaw, pitch;
326
327         VM_SAFEPARMCOUNT(1,VM_vectoangles);
328
329         value1 = PRVM_G_VECTOR(OFS_PARM0);
330
331         if (value1[1] == 0 && value1[0] == 0)
332         {
333                 yaw = 0;
334                 if (value1[2] > 0)
335                         pitch = 90;
336                 else
337                         pitch = 270;
338         }
339         else
340         {
341                 // LordHavoc: optimized a bit
342                 if (value1[0])
343                 {
344                         yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
345                         if (yaw < 0)
346                                 yaw += 360;
347                 }
348                 else if (value1[1] > 0)
349                         yaw = 90;
350                 else
351                         yaw = 270;
352
353                 forward = sqrt(value1[0]*value1[0] + value1[1]*value1[1]);
354                 pitch = (atan2(value1[2], forward) * 180 / M_PI);
355                 if (pitch < 0)
356                         pitch += 360;
357         }
358
359         PRVM_G_FLOAT(OFS_RETURN+0) = pitch;
360         PRVM_G_FLOAT(OFS_RETURN+1) = yaw;
361         PRVM_G_FLOAT(OFS_RETURN+2) = 0;
362 }
363
364 /*
365 =================
366 VM_random
367
368 Returns a number from 0<= num < 1
369
370 float random()
371 =================
372 */
373 void VM_random (void)
374 {
375         VM_SAFEPARMCOUNT(0,VM_random);
376
377         PRVM_G_FLOAT(OFS_RETURN) = lhrandom(0, 1);
378 }
379
380 /*
381 =================
382 PF_sound
383
384 Each entity can have eight independant sound sources, like voice,
385 weapon, feet, etc.
386
387 Channel 0 is an auto-allocate channel, the others override anything
388 already running on that entity/channel pair.
389
390 An attenuation of 0 will play full volume everywhere in the level.
391 Larger attenuations will drop off.
392
393 =================
394 */
395 /*
396 void PF_sound (void)
397 {
398         char            *sample;
399         int                     channel;
400         prvm_edict_t            *entity;
401         int             volume;
402         float attenuation;
403
404         entity = PRVM_G_EDICT(OFS_PARM0);
405         channel = PRVM_G_FLOAT(OFS_PARM1);
406         sample = PRVM_G_STRING(OFS_PARM2);
407         volume = PRVM_G_FLOAT(OFS_PARM3) * 255;
408         attenuation = PRVM_G_FLOAT(OFS_PARM4);
409
410         if (volume < 0 || volume > 255)
411                 Host_Error ("SV_StartSound: volume = %i", volume);
412
413         if (attenuation < 0 || attenuation > 4)
414                 Host_Error ("SV_StartSound: attenuation = %f", attenuation);
415
416         if (channel < 0 || channel > 7)
417                 Host_Error ("SV_StartSound: channel = %i", channel);
418
419         SV_StartSound (entity, channel, sample, volume, attenuation);
420 }
421 */
422
423 /*
424 =========
425 VM_localsound
426
427 localsound(string sample)
428 =========
429 */
430 void VM_localsound(void)
431 {
432         const char *s;
433
434         VM_SAFEPARMCOUNT(1,VM_localsound);
435
436         s = PRVM_G_STRING(OFS_PARM0);
437
438         if(!S_LocalSound (s))
439         {
440                 PRVM_G_FLOAT(OFS_RETURN) = -4;
441                 VM_Warning("VM_localsound: Failed to play %s for %s !\n", s, PRVM_NAME);
442                 return;
443         }
444
445         PRVM_G_FLOAT(OFS_RETURN) = 1;
446 }
447
448 /*
449 =================
450 VM_break
451
452 break()
453 =================
454 */
455 void VM_break (void)
456 {
457         PRVM_ERROR ("%s: break statement", PRVM_NAME);
458 }
459
460 //============================================================================
461
462 /*
463 =================
464 VM_localcmd
465
466 Sends text over to the client's execution buffer
467
468 [localcmd (string, ...) or]
469 cmd (string, ...)
470 =================
471 */
472 void VM_localcmd (void)
473 {
474         char string[VM_STRINGTEMP_LENGTH];
475         VM_VarString(0, string, sizeof(string));
476         Cbuf_AddText(string);
477 }
478
479 /*
480 =================
481 VM_cvar
482
483 float cvar (string)
484 =================
485 */
486 void VM_cvar (void)
487 {
488         VM_SAFEPARMCOUNT(1,VM_cvar);
489
490         PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue(PRVM_G_STRING(OFS_PARM0));
491 }
492
493 /*
494 =================
495 VM_cvar_string
496
497 const string    VM_cvar_string (string)
498 =================
499 */
500 void VM_cvar_string(void)
501 {
502         const char *name;
503         VM_SAFEPARMCOUNT(1,VM_cvar_string);
504
505         name = PRVM_G_STRING(OFS_PARM0);
506
507         VM_CheckEmptyString(name);
508
509         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableString(name));
510 }
511
512
513 /*
514 ========================
515 VM_cvar_defstring
516
517 const string    VM_cvar_defstring (string)
518 ========================
519 */
520 void VM_cvar_defstring (void)
521 {
522         const char *name;
523         VM_SAFEPARMCOUNT(1,VM_cvar_string);
524
525         name = PRVM_G_STRING(OFS_PARM0);
526
527         VM_CheckEmptyString(name);
528
529         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableDefString(name));
530 }
531 /*
532 =================
533 VM_cvar_set
534
535 void cvar_set (string,string)
536 =================
537 */
538 void VM_cvar_set (void)
539 {
540         VM_SAFEPARMCOUNT(2,VM_cvar_set);
541
542         Cvar_Set(PRVM_G_STRING(OFS_PARM0), PRVM_G_STRING(OFS_PARM1));
543 }
544
545 /*
546 =========
547 VM_dprint
548
549 dprint(...[string])
550 =========
551 */
552 void VM_dprint (void)
553 {
554         char string[VM_STRINGTEMP_LENGTH];
555         if (developer.integer)
556         {
557                 VM_VarString(0, string, sizeof(string));
558 #if 1
559                 Con_Printf("%s", string);
560 #else
561                 Con_Printf("%s: %s", PRVM_NAME, string);
562 #endif
563         }
564 }
565
566 /*
567 =========
568 VM_ftos
569
570 string  ftos(float)
571 =========
572 */
573
574 void VM_ftos (void)
575 {
576         float v;
577         char s[128];
578
579         VM_SAFEPARMCOUNT(1, VM_ftos);
580
581         v = PRVM_G_FLOAT(OFS_PARM0);
582
583         if ((float)((int)v) == v)
584                 sprintf(s, "%i", (int)v);
585         else
586                 sprintf(s, "%f", v);
587         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
588 }
589
590 /*
591 =========
592 VM_fabs
593
594 float   fabs(float)
595 =========
596 */
597
598 void VM_fabs (void)
599 {
600         float   v;
601
602         VM_SAFEPARMCOUNT(1,VM_fabs);
603
604         v = PRVM_G_FLOAT(OFS_PARM0);
605         PRVM_G_FLOAT(OFS_RETURN) = fabs(v);
606 }
607
608 /*
609 =========
610 VM_vtos
611
612 string  vtos(vector)
613 =========
614 */
615
616 void VM_vtos (void)
617 {
618         char s[512];
619
620         VM_SAFEPARMCOUNT(1,VM_vtos);
621
622         sprintf (s, "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
623         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
624 }
625
626 /*
627 =========
628 VM_etos
629
630 string  etos(entity)
631 =========
632 */
633
634 void VM_etos (void)
635 {
636         char s[128];
637
638         VM_SAFEPARMCOUNT(1, VM_etos);
639
640         sprintf (s, "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
641         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
642 }
643
644 /*
645 =========
646 VM_stof
647
648 float stof(...[string])
649 =========
650 */
651 void VM_stof(void)
652 {
653         char string[VM_STRINGTEMP_LENGTH];
654         VM_VarString(0, string, sizeof(string));
655         PRVM_G_FLOAT(OFS_RETURN) = atof(string);
656 }
657
658 /*
659 ========================
660 VM_itof
661
662 float itof(intt ent)
663 ========================
664 */
665 void VM_itof(void)
666 {
667         VM_SAFEPARMCOUNT(1, VM_itof);
668         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
669 }
670
671 /*
672 ========================
673 VM_ftoe
674
675 entity ftoe(float num)
676 ========================
677 */
678 void VM_ftoe(void)
679 {
680         int ent;
681         VM_SAFEPARMCOUNT(1, VM_ftoe);
682
683         ent = (int)PRVM_G_FLOAT(OFS_PARM0);
684         if (ent < 0 || ent >= MAX_EDICTS || PRVM_PROG_TO_EDICT(ent)->priv.required->free)
685                 ent = 0; // return world instead of a free or invalid entity
686
687         PRVM_G_INT(OFS_RETURN) = ent;
688 }
689
690 /*
691 =========
692 VM_strftime
693
694 string strftime(float uselocaltime, string[, string ...])
695 =========
696 */
697 void VM_strftime(void)
698 {
699         time_t t;
700         struct tm *tm;
701         char fmt[VM_STRINGTEMP_LENGTH];
702         char result[VM_STRINGTEMP_LENGTH];
703         VM_VarString(0, fmt, sizeof(fmt));
704         t = time(NULL);
705         if (PRVM_G_FLOAT(OFS_PARM0))
706                 tm = localtime(&t);
707         else
708                 tm = gmtime(&t);
709         if (!tm)
710         {
711                 PRVM_G_FLOAT(OFS_RETURN) = 0;
712                 return;
713         }
714         strftime(result, sizeof(result), fmt, tm);
715         PRVM_G_FLOAT(OFS_RETURN) = PRVM_SetTempString(result);
716 }
717
718 /*
719 =========
720 VM_spawn
721
722 entity spawn()
723 =========
724 */
725
726 void VM_spawn (void)
727 {
728         prvm_edict_t    *ed;
729         prog->xfunction->builtinsprofile += 20;
730         ed = PRVM_ED_Alloc();
731         VM_RETURN_EDICT(ed);
732 }
733
734 /*
735 =========
736 VM_remove
737
738 remove(entity e)
739 =========
740 */
741
742 void VM_remove (void)
743 {
744         prvm_edict_t    *ed;
745         prog->xfunction->builtinsprofile += 20;
746
747         VM_SAFEPARMCOUNT(1, VM_remove);
748
749         ed = PRVM_G_EDICT(OFS_PARM0);
750         if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
751         {
752                 if (developer.integer >= 1)
753                         VM_Warning( "VM_remove: tried to remove the null entity or a reserved entity!\n" );
754         }
755         else if( ed->priv.required->free )
756         {
757                 if (developer.integer >= 1)
758                         VM_Warning( "VM_remove: tried to remove an already freed entity!\n" );
759         }
760         else
761                 PRVM_ED_Free (ed);
762 //      if (ed == prog->edicts)
763 //              PRVM_ERROR ("remove: tried to remove world");
764 //      if (PRVM_NUM_FOR_EDICT(ed) <= sv.maxclients)
765 //              Host_Error("remove: tried to remove a client");
766 }
767
768 /*
769 =========
770 VM_find
771
772 entity  find(entity start, .string field, string match)
773 =========
774 */
775
776 void VM_find (void)
777 {
778         int             e;
779         int             f;
780         const char      *s, *t;
781         prvm_edict_t    *ed;
782
783         VM_SAFEPARMCOUNT(3,VM_find);
784
785         e = PRVM_G_EDICTNUM(OFS_PARM0);
786         f = PRVM_G_INT(OFS_PARM1);
787         s = PRVM_G_STRING(OFS_PARM2);
788
789         // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
790         // expects it to find all the monsters, so we must be careful to support
791         // searching for ""
792
793         for (e++ ; e < prog->num_edicts ; e++)
794         {
795                 prog->xfunction->builtinsprofile++;
796                 ed = PRVM_EDICT_NUM(e);
797                 if (ed->priv.required->free)
798                         continue;
799                 t = PRVM_E_STRING(ed,f);
800                 if (!t)
801                         t = "";
802                 if (!strcmp(t,s))
803                 {
804                         VM_RETURN_EDICT(ed);
805                         return;
806                 }
807         }
808
809         VM_RETURN_EDICT(prog->edicts);
810 }
811
812 /*
813 =========
814 VM_findfloat
815
816   entity        findfloat(entity start, .float field, float match)
817   entity        findentity(entity start, .entity field, entity match)
818 =========
819 */
820 // LordHavoc: added this for searching float, int, and entity reference fields
821 void VM_findfloat (void)
822 {
823         int             e;
824         int             f;
825         float   s;
826         prvm_edict_t    *ed;
827
828         VM_SAFEPARMCOUNT(3,VM_findfloat);
829
830         e = PRVM_G_EDICTNUM(OFS_PARM0);
831         f = PRVM_G_INT(OFS_PARM1);
832         s = PRVM_G_FLOAT(OFS_PARM2);
833
834         for (e++ ; e < prog->num_edicts ; e++)
835         {
836                 prog->xfunction->builtinsprofile++;
837                 ed = PRVM_EDICT_NUM(e);
838                 if (ed->priv.required->free)
839                         continue;
840                 if (PRVM_E_FLOAT(ed,f) == s)
841                 {
842                         VM_RETURN_EDICT(ed);
843                         return;
844                 }
845         }
846
847         VM_RETURN_EDICT(prog->edicts);
848 }
849
850 /*
851 =========
852 VM_findchain
853
854 entity  findchain(.string field, string match)
855 =========
856 */
857 // chained search for strings in entity fields
858 // entity(.string field, string match) findchain = #402;
859 void VM_findchain (void)
860 {
861         int             i;
862         int             f;
863         int             chain_of;
864         const char      *s, *t;
865         prvm_edict_t    *ent, *chain;
866
867         VM_SAFEPARMCOUNT(2,VM_findchain);
868
869         // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another
870         if(!prog->flag & PRVM_FE_CHAIN)
871                 PRVM_ERROR("VM_findchain: %s doesnt have a chain field !", PRVM_NAME);
872
873         chain_of = PRVM_ED_FindField("chain")->ofs;
874
875         chain = prog->edicts;
876
877         f = PRVM_G_INT(OFS_PARM0);
878         s = PRVM_G_STRING(OFS_PARM1);
879
880         // LordHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
881         // expects it to find all the monsters, so we must be careful to support
882         // searching for ""
883
884         ent = PRVM_NEXT_EDICT(prog->edicts);
885         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
886         {
887                 prog->xfunction->builtinsprofile++;
888                 if (ent->priv.required->free)
889                         continue;
890                 t = PRVM_E_STRING(ent,f);
891                 if (!t)
892                         t = "";
893                 if (strcmp(t,s))
894                         continue;
895
896                 PRVM_E_INT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain);
897                 chain = ent;
898         }
899
900         VM_RETURN_EDICT(chain);
901 }
902
903 /*
904 =========
905 VM_findchainfloat
906
907 entity  findchainfloat(.string field, float match)
908 entity  findchainentity(.string field, entity match)
909 =========
910 */
911 // LordHavoc: chained search for float, int, and entity reference fields
912 // entity(.string field, float match) findchainfloat = #403;
913 void VM_findchainfloat (void)
914 {
915         int             i;
916         int             f;
917         int             chain_of;
918         float   s;
919         prvm_edict_t    *ent, *chain;
920
921         VM_SAFEPARMCOUNT(2, VM_findchainfloat);
922
923         if(!prog->flag & PRVM_FE_CHAIN)
924                 PRVM_ERROR("VM_findchainfloat: %s doesnt have a chain field !", PRVM_NAME);
925
926         chain_of = PRVM_ED_FindField("chain")->ofs;
927
928         chain = (prvm_edict_t *)prog->edicts;
929
930         f = PRVM_G_INT(OFS_PARM0);
931         s = PRVM_G_FLOAT(OFS_PARM1);
932
933         ent = PRVM_NEXT_EDICT(prog->edicts);
934         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
935         {
936                 prog->xfunction->builtinsprofile++;
937                 if (ent->priv.required->free)
938                         continue;
939                 if (PRVM_E_FLOAT(ent,f) != s)
940                         continue;
941
942                 PRVM_E_INT(ent,chain_of) = PRVM_EDICT_TO_PROG(chain);
943                 chain = ent;
944         }
945
946         VM_RETURN_EDICT(chain);
947 }
948
949 /*
950 ========================
951 VM_findflags
952
953 entity  findflags(entity start, .float field, float match)
954 ========================
955 */
956 // LordHavoc: search for flags in float fields
957 void VM_findflags (void)
958 {
959         int             e;
960         int             f;
961         int             s;
962         prvm_edict_t    *ed;
963
964         VM_SAFEPARMCOUNT(3, VM_findflags);
965
966
967         e = PRVM_G_EDICTNUM(OFS_PARM0);
968         f = PRVM_G_INT(OFS_PARM1);
969         s = (int)PRVM_G_FLOAT(OFS_PARM2);
970
971         for (e++ ; e < prog->num_edicts ; e++)
972         {
973                 prog->xfunction->builtinsprofile++;
974                 ed = PRVM_EDICT_NUM(e);
975                 if (ed->priv.required->free)
976                         continue;
977                 if (!PRVM_E_FLOAT(ed,f))
978                         continue;
979                 if ((int)PRVM_E_FLOAT(ed,f) & s)
980                 {
981                         VM_RETURN_EDICT(ed);
982                         return;
983                 }
984         }
985
986         VM_RETURN_EDICT(prog->edicts);
987 }
988
989 /*
990 ========================
991 VM_findchainflags
992
993 entity  findchainflags(.float field, float match)
994 ========================
995 */
996 // LordHavoc: chained search for flags in float fields
997 void VM_findchainflags (void)
998 {
999         int             i;
1000         int             f;
1001         int             s;
1002         int             chain_of;
1003         prvm_edict_t    *ent, *chain;
1004
1005         VM_SAFEPARMCOUNT(2, VM_findchainflags);
1006
1007         if(!prog->flag & PRVM_FE_CHAIN)
1008                 PRVM_ERROR("VM_findchainflags: %s doesnt have a chain field !", PRVM_NAME);
1009
1010         chain_of = PRVM_ED_FindField("chain")->ofs;
1011
1012         chain = (prvm_edict_t *)prog->edicts;
1013
1014         f = PRVM_G_INT(OFS_PARM0);
1015         s = (int)PRVM_G_FLOAT(OFS_PARM1);
1016
1017         ent = PRVM_NEXT_EDICT(prog->edicts);
1018         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1019         {
1020                 prog->xfunction->builtinsprofile++;
1021                 if (ent->priv.required->free)
1022                         continue;
1023                 if (!PRVM_E_FLOAT(ent,f))
1024                         continue;
1025                 if (!((int)PRVM_E_FLOAT(ent,f) & s))
1026                         continue;
1027
1028                 PRVM_E_INT(ent,chain_of) = PRVM_EDICT_TO_PROG(chain);
1029                 chain = ent;
1030         }
1031
1032         VM_RETURN_EDICT(chain);
1033 }
1034
1035 /*
1036 =========
1037 VM_coredump
1038
1039 coredump()
1040 =========
1041 */
1042 void VM_coredump (void)
1043 {
1044         VM_SAFEPARMCOUNT(0,VM_coredump);
1045
1046         Cbuf_AddText("prvm_edicts ");
1047         Cbuf_AddText(PRVM_NAME);
1048         Cbuf_AddText("\n");
1049 }
1050
1051 /*
1052 =========
1053 VM_stackdump
1054
1055 stackdump()
1056 =========
1057 */
1058 void PRVM_StackTrace(void);
1059 void VM_stackdump (void)
1060 {
1061         VM_SAFEPARMCOUNT(0, VM_stackdump);
1062
1063         PRVM_StackTrace();
1064 }
1065
1066 /*
1067 =========
1068 VM_crash
1069
1070 crash()
1071 =========
1072 */
1073
1074 void VM_crash(void)
1075 {
1076         VM_SAFEPARMCOUNT(0, VM_crash);
1077
1078         PRVM_ERROR("Crash called by %s",PRVM_NAME);
1079 }
1080
1081 /*
1082 =========
1083 VM_traceon
1084
1085 traceon()
1086 =========
1087 */
1088 void VM_traceon (void)
1089 {
1090         VM_SAFEPARMCOUNT(0,VM_traceon);
1091
1092         prog->trace = true;
1093 }
1094
1095 /*
1096 =========
1097 VM_traceoff
1098
1099 traceoff()
1100 =========
1101 */
1102 void VM_traceoff (void)
1103 {
1104         VM_SAFEPARMCOUNT(0,VM_traceoff);
1105
1106         prog->trace = false;
1107 }
1108
1109 /*
1110 =========
1111 VM_eprint
1112
1113 eprint(entity e)
1114 =========
1115 */
1116 void VM_eprint (void)
1117 {
1118         VM_SAFEPARMCOUNT(1,VM_eprint);
1119
1120         PRVM_ED_PrintNum (PRVM_G_EDICTNUM(OFS_PARM0));
1121 }
1122
1123 /*
1124 =========
1125 VM_rint
1126
1127 float   rint(float)
1128 =========
1129 */
1130 void VM_rint (void)
1131 {
1132         float f;
1133         VM_SAFEPARMCOUNT(1,VM_rint);
1134
1135         f = PRVM_G_FLOAT(OFS_PARM0);
1136         if (f > 0)
1137                 PRVM_G_FLOAT(OFS_RETURN) = floor(f + 0.5);
1138         else
1139                 PRVM_G_FLOAT(OFS_RETURN) = ceil(f - 0.5);
1140 }
1141
1142 /*
1143 =========
1144 VM_floor
1145
1146 float   floor(float)
1147 =========
1148 */
1149 void VM_floor (void)
1150 {
1151         VM_SAFEPARMCOUNT(1,VM_floor);
1152
1153         PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
1154 }
1155
1156 /*
1157 =========
1158 VM_ceil
1159
1160 float   ceil(float)
1161 =========
1162 */
1163 void VM_ceil (void)
1164 {
1165         VM_SAFEPARMCOUNT(1,VM_ceil);
1166
1167         PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
1168 }
1169
1170
1171 /*
1172 =============
1173 VM_nextent
1174
1175 entity  nextent(entity)
1176 =============
1177 */
1178 void VM_nextent (void)
1179 {
1180         int             i;
1181         prvm_edict_t    *ent;
1182
1183         i = PRVM_G_EDICTNUM(OFS_PARM0);
1184         while (1)
1185         {
1186                 prog->xfunction->builtinsprofile++;
1187                 i++;
1188                 if (i == prog->num_edicts)
1189                 {
1190                         VM_RETURN_EDICT(prog->edicts);
1191                         return;
1192                 }
1193                 ent = PRVM_EDICT_NUM(i);
1194                 if (!ent->priv.required->free)
1195                 {
1196                         VM_RETURN_EDICT(ent);
1197                         return;
1198                 }
1199         }
1200 }
1201
1202 //=============================================================================
1203
1204 /*
1205 ==============
1206 VM_changelevel
1207 server and menu
1208
1209 changelevel(string map)
1210 ==============
1211 */
1212 void VM_changelevel (void)
1213 {
1214         VM_SAFEPARMCOUNT(1, VM_changelevel);
1215
1216         if(!sv.active)
1217         {
1218                 VM_Warning("VM_changelevel: game is not server (%s)\n", PRVM_NAME);
1219                 return;
1220         }
1221
1222 // make sure we don't issue two changelevels
1223         if (svs.changelevel_issued)
1224                 return;
1225         svs.changelevel_issued = true;
1226
1227         Cbuf_AddText (va("changelevel %s\n",PRVM_G_STRING(OFS_PARM0)));
1228 }
1229
1230 /*
1231 =========
1232 VM_sin
1233
1234 float   sin(float)
1235 =========
1236 */
1237 void VM_sin (void)
1238 {
1239         VM_SAFEPARMCOUNT(1,VM_sin);
1240         PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
1241 }
1242
1243 /*
1244 =========
1245 VM_cos
1246 float   cos(float)
1247 =========
1248 */
1249 void VM_cos (void)
1250 {
1251         VM_SAFEPARMCOUNT(1,VM_cos);
1252         PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
1253 }
1254
1255 /*
1256 =========
1257 VM_sqrt
1258
1259 float   sqrt(float)
1260 =========
1261 */
1262 void VM_sqrt (void)
1263 {
1264         VM_SAFEPARMCOUNT(1,VM_sqrt);
1265         PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
1266 }
1267
1268 /*
1269 =========
1270 VM_asin
1271
1272 float   asin(float)
1273 =========
1274 */
1275 void VM_asin (void)
1276 {
1277         VM_SAFEPARMCOUNT(1,VM_asin);
1278         PRVM_G_FLOAT(OFS_RETURN) = asin(PRVM_G_FLOAT(OFS_PARM0));
1279 }
1280
1281 /*
1282 =========
1283 VM_acos
1284 float   acos(float)
1285 =========
1286 */
1287 void VM_acos (void)
1288 {
1289         VM_SAFEPARMCOUNT(1,VM_acos);
1290         PRVM_G_FLOAT(OFS_RETURN) = acos(PRVM_G_FLOAT(OFS_PARM0));
1291 }
1292
1293 /*
1294 =========
1295 VM_atan
1296 float   atan(float)
1297 =========
1298 */
1299 void VM_atan (void)
1300 {
1301         VM_SAFEPARMCOUNT(1,VM_atan);
1302         PRVM_G_FLOAT(OFS_RETURN) = atan(PRVM_G_FLOAT(OFS_PARM0));
1303 }
1304
1305 /*
1306 =========
1307 VM_atan2
1308 float   atan2(float,float)
1309 =========
1310 */
1311 void VM_atan2 (void)
1312 {
1313         VM_SAFEPARMCOUNT(2,VM_atan2);
1314         PRVM_G_FLOAT(OFS_RETURN) = atan2(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1315 }
1316
1317 /*
1318 =========
1319 VM_tan
1320 float   tan(float)
1321 =========
1322 */
1323 void VM_tan (void)
1324 {
1325         VM_SAFEPARMCOUNT(1,VM_tan);
1326         PRVM_G_FLOAT(OFS_RETURN) = tan(PRVM_G_FLOAT(OFS_PARM0));
1327 }
1328
1329 /*
1330 =================
1331 VM_randomvec
1332
1333 Returns a vector of length < 1 and > 0
1334
1335 vector randomvec()
1336 =================
1337 */
1338 void VM_randomvec (void)
1339 {
1340         vec3_t          temp;
1341         //float         length;
1342
1343         VM_SAFEPARMCOUNT(0, VM_randomvec);
1344
1345         //// WTF ??
1346         do
1347         {
1348                 temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1349                 temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1350                 temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1351         }
1352         while (DotProduct(temp, temp) >= 1);
1353         VectorCopy (temp, PRVM_G_VECTOR(OFS_RETURN));
1354
1355         /*
1356         temp[0] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1357         temp[1] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1358         temp[2] = (rand()&32767) * (2.0 / 32767.0) - 1.0;
1359         // length returned always > 0
1360         length = (rand()&32766 + 1) * (1.0 / 32767.0) / VectorLength(temp);
1361         VectorScale(temp,length, temp);*/
1362         //VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN));
1363 }
1364
1365 //=============================================================================
1366
1367 /*
1368 =========
1369 VM_registercvar
1370
1371 float   registercvar (string name, string value, float flags)
1372 =========
1373 */
1374 void VM_registercvar (void)
1375 {
1376         const char *name, *value;
1377         int     flags;
1378
1379         VM_SAFEPARMCOUNT(3,VM_registercvar);
1380
1381         name = PRVM_G_STRING(OFS_PARM0);
1382         value = PRVM_G_STRING(OFS_PARM1);
1383         flags = (int)PRVM_G_FLOAT(OFS_PARM2);
1384         PRVM_G_FLOAT(OFS_RETURN) = 0;
1385
1386         if(flags > CVAR_MAXFLAGSVAL)
1387                 return;
1388
1389 // first check to see if it has already been defined
1390         if (Cvar_FindVar (name))
1391                 return;
1392
1393 // check for overlap with a command
1394         if (Cmd_Exists (name))
1395         {
1396                 VM_Warning("VM_registercvar: %s is a command\n", name);
1397                 return;
1398         }
1399
1400         Cvar_Get(name, value, flags);
1401
1402         PRVM_G_FLOAT(OFS_RETURN) = 1; // success
1403 }
1404
1405 /*
1406 =================
1407 VM_min
1408
1409 returns the minimum of two supplied floats
1410
1411 float min(float a, float b, ...[float])
1412 =================
1413 */
1414 void VM_min (void)
1415 {
1416         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1417         if (prog->argc == 2)
1418                 PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1419         else if (prog->argc >= 3)
1420         {
1421                 int i;
1422                 float f = PRVM_G_FLOAT(OFS_PARM0);
1423                 for (i = 1;i < prog->argc;i++)
1424                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) < f)
1425                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1426                 PRVM_G_FLOAT(OFS_RETURN) = f;
1427         }
1428         else
1429                 PRVM_ERROR("VM_min: %s must supply at least 2 floats", PRVM_NAME);
1430 }
1431
1432 /*
1433 =================
1434 VM_max
1435
1436 returns the maximum of two supplied floats
1437
1438 float   max(float a, float b, ...[float])
1439 =================
1440 */
1441 void VM_max (void)
1442 {
1443         // LordHavoc: 3+ argument enhancement suggested by FrikaC
1444         if (prog->argc == 2)
1445                 PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1446         else if (prog->argc >= 3)
1447         {
1448                 int i;
1449                 float f = PRVM_G_FLOAT(OFS_PARM0);
1450                 for (i = 1;i < prog->argc;i++)
1451                         if (PRVM_G_FLOAT((OFS_PARM0+i*3)) > f)
1452                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1453                 PRVM_G_FLOAT(OFS_RETURN) = f;
1454         }
1455         else
1456                 PRVM_ERROR("VM_max: %s must supply at least 2 floats", PRVM_NAME);
1457 }
1458
1459 /*
1460 =================
1461 VM_bound
1462
1463 returns number bounded by supplied range
1464
1465 float   bound(float min, float value, float max)
1466 =================
1467 */
1468 void VM_bound (void)
1469 {
1470         VM_SAFEPARMCOUNT(3,VM_bound);
1471         PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
1472 }
1473
1474 /*
1475 =================
1476 VM_pow
1477
1478 returns a raised to power b
1479
1480 float   pow(float a, float b)
1481 =================
1482 */
1483 void VM_pow (void)
1484 {
1485         VM_SAFEPARMCOUNT(2,VM_pow);
1486         PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1487 }
1488
1489 /*
1490 =================
1491 VM_copyentity
1492
1493 copies data from one entity to another
1494
1495 copyentity(entity src, entity dst)
1496 =================
1497 */
1498 void VM_copyentity (void)
1499 {
1500         prvm_edict_t *in, *out;
1501         VM_SAFEPARMCOUNT(2,VM_copyentity);
1502         in = PRVM_G_EDICT(OFS_PARM0);
1503         out = PRVM_G_EDICT(OFS_PARM1);
1504         memcpy(out->fields.vp, in->fields.vp, prog->progs->entityfields * 4);
1505 }
1506
1507 /*
1508 =================
1509 VM_setcolor
1510
1511 sets the color of a client and broadcasts the update to all connected clients
1512
1513 setcolor(clientent, value)
1514 =================
1515 */
1516 /*void PF_setcolor (void)
1517 {
1518         client_t *client;
1519         int entnum, i;
1520         prvm_eval_t *val;
1521
1522         entnum = PRVM_G_EDICTNUM(OFS_PARM0);
1523         i = PRVM_G_FLOAT(OFS_PARM1);
1524
1525         if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active)
1526         {
1527                 Con_Print("tried to setcolor a non-client\n");
1528                 return;
1529         }
1530
1531         client = svs.clients + entnum-1;
1532         if ((val = PRVM_GETEDICTFIELDVALUE(client->edict, eval_clientcolors)))
1533                 val->_float = i;
1534         client->colors = i;
1535         client->old_colors = i;
1536         client->edict->fields.server->team = (i & 15) + 1;
1537
1538         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1539         MSG_WriteByte (&sv.reliable_datagram, entnum - 1);
1540         MSG_WriteByte (&sv.reliable_datagram, i);
1541 }*/
1542
1543 void VM_Files_Init(void)
1544 {
1545         int i;
1546         for (i = 0;i < PRVM_MAX_OPENFILES;i++)
1547                 prog->openfiles[i] = NULL;
1548 }
1549
1550 void VM_Files_CloseAll(void)
1551 {
1552         int i;
1553         for (i = 0;i < PRVM_MAX_OPENFILES;i++)
1554         {
1555                 if (prog->openfiles[i])
1556                         FS_Close(prog->openfiles[i]);
1557                 prog->openfiles[i] = NULL;
1558         }
1559 }
1560
1561 qfile_t *VM_GetFileHandle( int index )
1562 {
1563         if (index < 0 || index >= PRVM_MAX_OPENFILES)
1564         {
1565                 Con_Printf("VM_GetFileHandle: invalid file handle %i used in %s\n", index, PRVM_NAME);
1566                 return NULL;
1567         }
1568         if (prog->openfiles[index] == NULL)
1569         {
1570                 Con_Printf("VM_GetFileHandle: no such file handle %i (or file has been closed) in %s\n", index, PRVM_NAME);
1571                 return NULL;
1572         }
1573         return prog->openfiles[index];
1574 }
1575
1576 /*
1577 =========
1578 VM_fopen
1579
1580 float   fopen(string filename, float mode)
1581 =========
1582 */
1583 // float(string filename, float mode) fopen = #110;
1584 // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE),
1585 // returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
1586 void VM_fopen(void)
1587 {
1588         int filenum, mode;
1589         const char *modestring, *filename;
1590
1591         VM_SAFEPARMCOUNT(2,VM_fopen);
1592
1593         for (filenum = 0;filenum < PRVM_MAX_OPENFILES;filenum++)
1594                 if (prog->openfiles[filenum] == NULL)
1595                         break;
1596         if (filenum >= PRVM_MAX_OPENFILES)
1597         {
1598                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1599                 VM_Warning("VM_fopen: %s ran out of file handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENFILES);
1600                 return;
1601         }
1602         mode = (int)PRVM_G_FLOAT(OFS_PARM1);
1603         switch(mode)
1604         {
1605         case 0: // FILE_READ
1606                 modestring = "rb";
1607                 break;
1608         case 1: // FILE_APPEND
1609                 modestring = "ab";
1610                 break;
1611         case 2: // FILE_WRITE
1612                 modestring = "wb";
1613                 break;
1614         default:
1615                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1616                 VM_Warning("VM_fopen: %s: no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", PRVM_NAME, mode);
1617                 return;
1618         }
1619         filename = PRVM_G_STRING(OFS_PARM0);
1620
1621         prog->openfiles[filenum] = FS_Open(va("data/%s", filename), modestring, false, false);
1622         if (prog->openfiles[filenum] == NULL && mode == 0)
1623                 prog->openfiles[filenum] = FS_Open(va("%s", filename), modestring, false, false);
1624
1625         if (prog->openfiles[filenum] == NULL)
1626         {
1627                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1628                 if (developer.integer >= 100)
1629                         VM_Warning("VM_fopen: %s: %s mode %s failed\n", PRVM_NAME, filename, modestring);
1630         }
1631         else
1632         {
1633                 PRVM_G_FLOAT(OFS_RETURN) = filenum;
1634                 if (developer.integer >= 100)
1635                         Con_Printf("VM_fopen: %s: %s mode %s opened as #%i\n", PRVM_NAME, filename, modestring, filenum);
1636         }
1637 }
1638
1639 /*
1640 =========
1641 VM_fclose
1642
1643 fclose(float fhandle)
1644 =========
1645 */
1646 //void(float fhandle) fclose = #111; // closes a file
1647 void VM_fclose(void)
1648 {
1649         int filenum;
1650
1651         VM_SAFEPARMCOUNT(1,VM_fclose);
1652
1653         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1654         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1655         {
1656                 VM_Warning("VM_fclose: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1657                 return;
1658         }
1659         if (prog->openfiles[filenum] == NULL)
1660         {
1661                 VM_Warning("VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1662                 return;
1663         }
1664         FS_Close(prog->openfiles[filenum]);
1665         prog->openfiles[filenum] = NULL;
1666         if (developer.integer >= 100)
1667                 Con_Printf("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum);
1668 }
1669
1670 /*
1671 =========
1672 VM_fgets
1673
1674 string  fgets(float fhandle)
1675 =========
1676 */
1677 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
1678 void VM_fgets(void)
1679 {
1680         int c, end;
1681         char string[VM_STRINGTEMP_LENGTH];
1682         int filenum;
1683
1684         VM_SAFEPARMCOUNT(1,VM_fgets);
1685
1686         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1687         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1688         {
1689                 VM_Warning("VM_fgets: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1690                 return;
1691         }
1692         if (prog->openfiles[filenum] == NULL)
1693         {
1694                 VM_Warning("VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1695                 return;
1696         }
1697         end = 0;
1698         for (;;)
1699         {
1700                 c = FS_Getc(prog->openfiles[filenum]);
1701                 if (c == '\r' || c == '\n' || c < 0)
1702                         break;
1703                 if (end < VM_STRINGTEMP_LENGTH - 1)
1704                         string[end++] = c;
1705         }
1706         string[end] = 0;
1707         // remove \n following \r
1708         if (c == '\r')
1709         {
1710                 c = FS_Getc(prog->openfiles[filenum]);
1711                 if (c != '\n')
1712                         FS_UnGetc(prog->openfiles[filenum], (unsigned char)c);
1713         }
1714         if (developer.integer >= 100)
1715                 Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
1716         if (c >= 0 || end)
1717                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
1718         else
1719                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1720 }
1721
1722 /*
1723 =========
1724 VM_fputs
1725
1726 fputs(float fhandle, string s)
1727 =========
1728 */
1729 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
1730 void VM_fputs(void)
1731 {
1732         int stringlength;
1733         char string[VM_STRINGTEMP_LENGTH];
1734         int filenum;
1735
1736         VM_SAFEPARMCOUNT(2,VM_fputs);
1737
1738         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1739         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1740         {
1741                 VM_Warning("VM_fputs: invalid file handle %i used in %s\n", filenum, PRVM_NAME);
1742                 return;
1743         }
1744         if (prog->openfiles[filenum] == NULL)
1745         {
1746                 VM_Warning("VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, PRVM_NAME);
1747                 return;
1748         }
1749         VM_VarString(1, string, sizeof(string));
1750         if ((stringlength = (int)strlen(string)))
1751                 FS_Write(prog->openfiles[filenum], string, stringlength);
1752         if (developer.integer >= 100)
1753                 Con_Printf("fputs: %s: %s\n", PRVM_NAME, string);
1754 }
1755
1756 /*
1757 =========
1758 VM_strlen
1759
1760 float   strlen(string s)
1761 =========
1762 */
1763 //float(string s) strlen = #114; // returns how many characters are in a string
1764 void VM_strlen(void)
1765 {
1766         VM_SAFEPARMCOUNT(1,VM_strlen);
1767
1768         PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
1769 }
1770
1771 // DRESK - Decolorized String
1772 /*
1773 =========
1774 VM_strdecolorize
1775
1776 string  strdecolorize(string s)
1777 =========
1778 */
1779 // string (string s) strdecolorize = #472; // returns the passed in string with color codes stripped
1780 void VM_strdecolorize(void)
1781 {
1782         char szNewString[VM_STRINGTEMP_LENGTH];
1783         const char *szString;
1784         size_t nCnt;
1785         int nPos;
1786         int nFillPos;
1787         int bFinished;
1788                 nPos = 0;
1789                 nFillPos = 0;
1790                 nCnt = 0;
1791                 bFinished = 0;
1792
1793         // Prepare Strings
1794         VM_SAFEPARMCOUNT(1,VM_strdecolorize);
1795         szString = PRVM_G_STRING(OFS_PARM0);
1796
1797         while(!bFinished)
1798         { // Traverse through String
1799                 if( szString[nPos] == '\n' || szString[nPos] == '\r' || szString[nPos] <= 0)
1800                 { // String End Found
1801                         szNewString[nFillPos++] = szString[nPos];
1802                         bFinished = 1;
1803                 }
1804                 else
1805                 if( szString[nPos] == STRING_COLOR_TAG)
1806                 { // Color Code Located
1807                         if( szString[nPos + 1] == STRING_COLOR_TAG)
1808                         { // Valid Characters to Include
1809                                 szNewString[nFillPos++] = szString[nPos];
1810                                 nPos = nPos + 1;
1811                                 szNewString[nFillPos++] = szString[nPos];
1812                         }
1813                         else
1814                         if( szString[nPos + 1] >= '0' && szString[nPos + 1] <= '9' )
1815                         { // Color Code Found; Increment Position
1816                                 nPos = nPos + 1;
1817                         }
1818                         else
1819                         { // Unknown Color Code; Include
1820                                 szNewString[nFillPos++] = szString[nPos];
1821                                 nPos = nPos + 1;
1822                         }
1823                 }
1824                 else
1825                         // Include Character
1826                         szNewString[nFillPos++] = szString[nPos];
1827
1828                         // Increment Position
1829                         nPos = nPos + 1;
1830         }
1831
1832         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(szNewString);
1833 }
1834
1835 // DRESK - String Length (not counting color codes)
1836 /*
1837 =========
1838 VM_strlennocol
1839
1840 float   strlennocol(string s)
1841 =========
1842 */
1843 // float(string s) strlennocol = #471; // returns how many characters are in a string not including color codes
1844 // For example, ^2Dresk returns a length of 5
1845 void VM_strlennocol(void)
1846 {
1847         const char *szString;
1848         size_t nCnt;
1849         int nPos;
1850         int bFinished;
1851                 nPos = 0;
1852                 nCnt = 0;
1853                 bFinished = 0;
1854
1855         VM_SAFEPARMCOUNT(1,VM_strlennocol);
1856
1857         szString = PRVM_G_STRING(OFS_PARM0);
1858
1859         while(!bFinished)
1860         { // Count Characters
1861                 // SV_BroadcastPrintf("Position '%d'; Character '%c'; Length '%d'\n", nPos, szString[nPos], nCnt);
1862
1863                 if( szString[nPos] == '\n' || szString[nPos] == '\r' || szString[nPos] <= 0)
1864                 { // String End Found
1865                         // SV_BroadcastPrintf("Found End of String at '%d'\n", nPos);
1866                         bFinished = 1;
1867                 }
1868                 else
1869                 if( szString[nPos] == STRING_COLOR_TAG)
1870                 { // Color Code Located
1871                         if( szString[nPos + 1] == STRING_COLOR_TAG)
1872                         { // Increment Length; Skip Color Code
1873                                 nCnt = nCnt + 1;
1874                                 nPos = nPos + 1;
1875                         }
1876                         else
1877                         if( szString[nPos + 1] >= '0' && szString[nPos + 1] <= '9' )
1878                         { // Color Code Found; Increment Position
1879                                 // SV_BroadcastPrintf("Found Color Codes at '%d'\n", nPos);
1880                                 nPos = nPos + 1;
1881                         }
1882                         else
1883                         { // Unknown Color Code; Increment Length!
1884                                 nPos = nPos + 1;
1885                                 nCnt = nCnt + 1;
1886                         }
1887                 }
1888                 else
1889                         // Increment String Length
1890                         nCnt = nCnt + 1;
1891
1892                 // Increment Position
1893                 nPos = nPos + 1;
1894         }
1895         PRVM_G_FLOAT(OFS_RETURN) = nCnt;
1896 }
1897
1898 /*
1899 =========
1900 VM_strcat
1901
1902 string strcat(string,string,...[string])
1903 =========
1904 */
1905 //string(string s1, string s2) strcat = #115;
1906 // concatenates two strings (for example "abc", "def" would return "abcdef")
1907 // and returns as a tempstring
1908 void VM_strcat(void)
1909 {
1910         char s[VM_STRINGTEMP_LENGTH];
1911
1912         if(prog->argc < 1)
1913                 PRVM_ERROR("VM_strcat wrong parameter count (min. 1 expected ) !");
1914
1915         VM_VarString(0, s, sizeof(s));
1916         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(s);
1917 }
1918
1919 /*
1920 =========
1921 VM_substring
1922
1923 string  substring(string s, float start, float length)
1924 =========
1925 */
1926 // string(string s, float start, float length) substring = #116;
1927 // returns a section of a string as a tempstring
1928 void VM_substring(void)
1929 {
1930         int i, start, length;
1931         const char *s;
1932         char string[VM_STRINGTEMP_LENGTH];
1933
1934         VM_SAFEPARMCOUNT(3,VM_substring);
1935
1936         s = PRVM_G_STRING(OFS_PARM0);
1937         start = (int)PRVM_G_FLOAT(OFS_PARM1);
1938         length = (int)PRVM_G_FLOAT(OFS_PARM2);
1939         for (i = 0;i < start && *s;i++, s++);
1940         for (i = 0;i < (int)sizeof(string) - 1 && *s && i < length;i++, s++)
1941                 string[i] = *s;
1942         string[i] = 0;
1943         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
1944 }
1945
1946 /*
1947 =========
1948 VM_stov
1949
1950 vector  stov(string s)
1951 =========
1952 */
1953 //vector(string s) stov = #117; // returns vector value from a string
1954 void VM_stov(void)
1955 {
1956         char string[VM_STRINGTEMP_LENGTH];
1957
1958         VM_SAFEPARMCOUNT(1,VM_stov);
1959
1960         VM_VarString(0, string, sizeof(string));
1961         Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
1962 }
1963
1964 /*
1965 =========
1966 VM_strzone
1967
1968 string  strzone(string s)
1969 =========
1970 */
1971 //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)
1972 void VM_strzone(void)
1973 {
1974         char *out;
1975         char string[VM_STRINGTEMP_LENGTH];
1976         size_t alloclen;
1977
1978         VM_SAFEPARMCOUNT(1,VM_strzone);
1979
1980         VM_VarString(0, string, sizeof(string));
1981         alloclen = strlen(string) + 1;
1982         PRVM_G_INT(OFS_RETURN) = PRVM_AllocString(alloclen, &out);
1983         memcpy(out, string, alloclen);
1984 }
1985
1986 /*
1987 =========
1988 VM_strunzone
1989
1990 strunzone(string s)
1991 =========
1992 */
1993 //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!!!)
1994 void VM_strunzone(void)
1995 {
1996         VM_SAFEPARMCOUNT(1,VM_strunzone);
1997         PRVM_FreeString(PRVM_G_INT(OFS_PARM0));
1998 }
1999
2000 /*
2001 =========
2002 VM_command (used by client and menu)
2003
2004 clientcommand(float client, string s) (for client and menu)
2005 =========
2006 */
2007 //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
2008 //this function originally written by KrimZon, made shorter by LordHavoc
2009 void VM_clcommand (void)
2010 {
2011         client_t *temp_client;
2012         int i;
2013
2014         VM_SAFEPARMCOUNT(2,VM_clcommand);
2015
2016         i = (int)PRVM_G_FLOAT(OFS_PARM0);
2017         if (!sv.active  || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
2018         {
2019                 VM_Warning("VM_clientcommand: %s: invalid client/server is not active !\n", PRVM_NAME);
2020                 return;
2021         }
2022
2023         temp_client = host_client;
2024         host_client = svs.clients + i;
2025         Cmd_ExecuteString (PRVM_G_STRING(OFS_PARM1), src_client);
2026         host_client = temp_client;
2027 }
2028
2029
2030 /*
2031 =========
2032 VM_tokenize
2033
2034 float tokenize(string s)
2035 =========
2036 */
2037 //float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many
2038 //this function originally written by KrimZon, made shorter by LordHavoc
2039 //20040203: rewritten by LordHavoc (no longer uses allocations)
2040 int num_tokens = 0;
2041 int tokens[256];
2042 void VM_tokenize (void)
2043 {
2044         const char *p;
2045 #if 0
2046         size_t pos = 0;
2047         char tokenbuf[MAX_INPUTLINE];
2048         size_t tokenlen;
2049 #endif
2050
2051         VM_SAFEPARMCOUNT(1,VM_tokenize);
2052
2053         p = PRVM_G_STRING(OFS_PARM0);
2054
2055         num_tokens = 0;
2056         while(COM_ParseToken(&p, false))
2057         {
2058                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2059                         break;
2060 #if 0
2061                 tokenlen = strlen(com_token) + 1;
2062                 if (pos + tokenlen > sizeof(tokenbuf))
2063                         break;
2064                 tokens[num_tokens++] = PRVM_SetEngineString(tokenbuf + pos);
2065                 memcpy(tokenbuf + pos, com_token, tokenlen);
2066                 pos += tokenlen;
2067 #else
2068                 tokens[num_tokens++] = PRVM_SetTempString(com_token);
2069 #endif
2070         }
2071
2072         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2073 }
2074
2075 //string(float n) argv = #442; // returns a word from the tokenized string (returns nothing for an invalid index)
2076 //this function originally written by KrimZon, made shorter by LordHavoc
2077 void VM_argv (void)
2078 {
2079         int token_num;
2080
2081         VM_SAFEPARMCOUNT(1,VM_argv);
2082
2083         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2084
2085         if (token_num >= 0 && token_num < num_tokens)
2086                 PRVM_G_INT(OFS_RETURN) = tokens[token_num];
2087         else
2088                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2089 }
2090
2091 /*
2092 //void(entity e, entity tagentity, string tagname) setattachment = #443; // attachs e to a tag on tagentity (note: use "" to attach to entity origin/angles instead of a tag)
2093 void PF_setattachment (void)
2094 {
2095         prvm_edict_t *e = PRVM_G_EDICT(OFS_PARM0);
2096         prvm_edict_t *tagentity = PRVM_G_EDICT(OFS_PARM1);
2097         char *tagname = PRVM_G_STRING(OFS_PARM2);
2098         prvm_eval_t *v;
2099         int i, modelindex;
2100         model_t *model;
2101
2102         if (tagentity == NULL)
2103                 tagentity = prog->edicts;
2104
2105         v = PRVM_GETEDICTFIELDVALUE(e, eval_tag_entity);
2106         if (v)
2107                 fields.server->edict = PRVM_EDICT_TO_PROG(tagentity);
2108
2109         v = PRVM_GETEDICTFIELDVALUE(e, eval_tag_index);
2110         if (v)
2111                 fields.server->_float = 0;
2112         if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
2113         {
2114                 modelindex = (int)tagentity->fields.server->modelindex;
2115                 if (modelindex >= 0 && modelindex < MAX_MODELS)
2116                 {
2117                         model = sv.models[modelindex];
2118                         if (model->data_overridetagnamesforskin && (unsigned int)tagentity->fields.server->skin < (unsigned int)model->numskins && model->data_overridetagnamesforskin[(unsigned int)tagentity->fields.server->skin].num_overridetagnames)
2119                                 for (i = 0;i < model->data_overridetagnamesforskin[(unsigned int)tagentity->fields.server->skin].num_overridetagnames;i++)
2120                                         if (!strcmp(tagname, model->data_overridetagnamesforskin[(unsigned int)tagentity->fields.server->skin].data_overridetagnames[i].name))
2121                                                 fields.server->_float = i + 1;
2122                         // FIXME: use a model function to get tag info (need to handle skeletal)
2123                         if (fields.server->_float == 0 && model->num_tags)
2124                                 for (i = 0;i < model->num_tags;i++)
2125                                         if (!strcmp(tagname, model->data_tags[i].name))
2126                                                 fields.server->_float = i + 1;
2127                         if (fields.server->_float == 0)
2128                                 Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity), model->name);
2129                 }
2130                 else
2131                         Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i but it has no model\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity));
2132         }
2133 }*/
2134
2135 /*
2136 =========
2137 VM_isserver
2138
2139 float   isserver()
2140 =========
2141 */
2142 void VM_isserver(void)
2143 {
2144         VM_SAFEPARMCOUNT(0,VM_serverstate);
2145
2146         PRVM_G_FLOAT(OFS_RETURN) = sv.active;
2147 }
2148
2149 /*
2150 =========
2151 VM_clientcount
2152
2153 float   clientcount()
2154 =========
2155 */
2156 void VM_clientcount(void)
2157 {
2158         VM_SAFEPARMCOUNT(0,VM_clientcount);
2159
2160         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
2161 }
2162
2163 /*
2164 =========
2165 VM_clientstate
2166
2167 float   clientstate()
2168 =========
2169 */
2170 void VM_clientstate(void)
2171 {
2172         VM_SAFEPARMCOUNT(0,VM_clientstate);
2173
2174         PRVM_G_FLOAT(OFS_RETURN) = cls.state;
2175 }
2176
2177 /*
2178 =========
2179 VM_getostype
2180
2181 float   getostype(void)
2182 =========
2183 */ // not used at the moment -> not included in the common list
2184 void VM_getostype(void)
2185 {
2186         VM_SAFEPARMCOUNT(0,VM_getostype);
2187
2188         /*
2189         OS_WINDOWS
2190         OS_LINUX
2191         OS_MAC - not supported
2192         */
2193
2194 #ifdef WIN32
2195         PRVM_G_FLOAT(OFS_RETURN) = 0;
2196 #elif defined(MACOSX)
2197         PRVM_G_FLOAT(OFS_RETURN) = 2;
2198 #else
2199         PRVM_G_FLOAT(OFS_RETURN) = 1;
2200 #endif
2201 }
2202
2203 /*
2204 =========
2205 VM_getmousepos
2206
2207 vector  getmousepos()
2208 =========
2209 */
2210 void VM_getmousepos(void)
2211 {
2212
2213         VM_SAFEPARMCOUNT(0,VM_getmousepos);
2214
2215         PRVM_G_VECTOR(OFS_RETURN)[0] = in_mouse_x * vid_conwidth.integer / vid.width;
2216         PRVM_G_VECTOR(OFS_RETURN)[1] = in_mouse_y * vid_conheight.integer / vid.height;
2217         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
2218 }
2219
2220 /*
2221 =========
2222 VM_gettime
2223
2224 float   gettime(void)
2225 =========
2226 */
2227 void VM_gettime(void)
2228 {
2229         VM_SAFEPARMCOUNT(0,VM_gettime);
2230
2231         PRVM_G_FLOAT(OFS_RETURN) = (float) *prog->time;
2232 }
2233
2234 /*
2235 =========
2236 VM_loadfromdata
2237
2238 loadfromdata(string data)
2239 =========
2240 */
2241 void VM_loadfromdata(void)
2242 {
2243         VM_SAFEPARMCOUNT(1,VM_loadentsfromfile);
2244
2245         PRVM_ED_LoadFromFile(PRVM_G_STRING(OFS_PARM0));
2246 }
2247
2248 /*
2249 ========================
2250 VM_parseentitydata
2251
2252 parseentitydata(entity ent, string data)
2253 ========================
2254 */
2255 void VM_parseentitydata(void)
2256 {
2257         prvm_edict_t *ent;
2258         const char *data;
2259
2260         VM_SAFEPARMCOUNT(2, VM_parseentitydata);
2261
2262     // get edict and test it
2263         ent = PRVM_G_EDICT(OFS_PARM0);
2264         if (ent->priv.required->free)
2265                 PRVM_ERROR ("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", PRVM_NAME, PRVM_NUM_FOR_EDICT(ent));
2266
2267         data = PRVM_G_STRING(OFS_PARM1);
2268
2269     // parse the opening brace
2270         if (!COM_ParseTokenConsole(&data) || com_token[0] != '{' )
2271                 PRVM_ERROR ("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", PRVM_NAME, data );
2272
2273         PRVM_ED_ParseEdict (data, ent);
2274 }
2275
2276 /*
2277 =========
2278 VM_loadfromfile
2279
2280 loadfromfile(string file)
2281 =========
2282 */
2283 void VM_loadfromfile(void)
2284 {
2285         const char *filename;
2286         char *data;
2287
2288         VM_SAFEPARMCOUNT(1,VM_loadfromfile);
2289
2290         filename = PRVM_G_STRING(OFS_PARM0);
2291         if (FS_CheckNastyPath(filename, false))
2292         {
2293                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2294                 VM_Warning("VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", PRVM_NAME, filename);
2295                 return;
2296         }
2297
2298         // not conform with VM_fopen
2299         data = (char *)FS_LoadFile(filename, tempmempool, false, NULL);
2300         if (data == NULL)
2301                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2302
2303         PRVM_ED_LoadFromFile(data);
2304
2305         if(data)
2306                 Mem_Free(data);
2307 }
2308
2309
2310 /*
2311 =========
2312 VM_modulo
2313
2314 float   mod(float val, float m)
2315 =========
2316 */
2317 void VM_modulo(void)
2318 {
2319         int val, m;
2320         VM_SAFEPARMCOUNT(2,VM_module);
2321
2322         val = (int) PRVM_G_FLOAT(OFS_PARM0);
2323         m       = (int) PRVM_G_FLOAT(OFS_PARM1);
2324
2325         PRVM_G_FLOAT(OFS_RETURN) = (float) (val % m);
2326 }
2327
2328 void VM_Search_Init(void)
2329 {
2330         int i;
2331         for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++)
2332                 prog->opensearches[i] = NULL;
2333 }
2334
2335 void VM_Search_Reset(void)
2336 {
2337         int i;
2338         // reset the fssearch list
2339         for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++)
2340         {
2341                 if(prog->opensearches[i])
2342                         FS_FreeSearch(prog->opensearches[i]);
2343                 prog->opensearches[i] = NULL;
2344         }
2345 }
2346
2347 /*
2348 =========
2349 VM_search_begin
2350
2351 float search_begin(string pattern, float caseinsensitive, float quiet)
2352 =========
2353 */
2354 void VM_search_begin(void)
2355 {
2356         int handle;
2357         const char *pattern;
2358         int caseinsens, quiet;
2359
2360         VM_SAFEPARMCOUNT(3, VM_search_begin);
2361
2362         pattern = PRVM_G_STRING(OFS_PARM0);
2363
2364         VM_CheckEmptyString(pattern);
2365
2366         caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1);
2367         quiet = (int)PRVM_G_FLOAT(OFS_PARM2);
2368
2369         for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++)
2370                 if(!prog->opensearches[handle])
2371                         break;
2372
2373         if(handle >= PRVM_MAX_OPENSEARCHES)
2374         {
2375                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2376                 VM_Warning("VM_search_begin: %s ran out of search handles (%i)\n", PRVM_NAME, PRVM_MAX_OPENSEARCHES);
2377                 return;
2378         }
2379
2380         if(!(prog->opensearches[handle] = FS_Search(pattern,caseinsens, quiet)))
2381                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2382         else
2383                 PRVM_G_FLOAT(OFS_RETURN) = handle;
2384 }
2385
2386 /*
2387 =========
2388 VM_search_end
2389
2390 void    search_end(float handle)
2391 =========
2392 */
2393 void VM_search_end(void)
2394 {
2395         int handle;
2396         VM_SAFEPARMCOUNT(1, VM_search_end);
2397
2398         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2399
2400         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2401         {
2402                 VM_Warning("VM_search_end: invalid handle %i used in %s\n", handle, PRVM_NAME);
2403                 return;
2404         }
2405         if(prog->opensearches[handle] == NULL)
2406         {
2407                 VM_Warning("VM_search_end: no such handle %i in %s\n", handle, PRVM_NAME);
2408                 return;
2409         }
2410
2411         FS_FreeSearch(prog->opensearches[handle]);
2412         prog->opensearches[handle] = NULL;
2413 }
2414
2415 /*
2416 =========
2417 VM_search_getsize
2418
2419 float   search_getsize(float handle)
2420 =========
2421 */
2422 void VM_search_getsize(void)
2423 {
2424         int handle;
2425         VM_SAFEPARMCOUNT(1, VM_M_search_getsize);
2426
2427         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2428
2429         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2430         {
2431                 VM_Warning("VM_search_getsize: invalid handle %i used in %s\n", handle, PRVM_NAME);
2432                 return;
2433         }
2434         if(prog->opensearches[handle] == NULL)
2435         {
2436                 VM_Warning("VM_search_getsize: no such handle %i in %s\n", handle, PRVM_NAME);
2437                 return;
2438         }
2439
2440         PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames;
2441 }
2442
2443 /*
2444 =========
2445 VM_search_getfilename
2446
2447 string  search_getfilename(float handle, float num)
2448 =========
2449 */
2450 void VM_search_getfilename(void)
2451 {
2452         int handle, filenum;
2453         VM_SAFEPARMCOUNT(2, VM_search_getfilename);
2454
2455         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
2456         filenum = (int)PRVM_G_FLOAT(OFS_PARM1);
2457
2458         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
2459         {
2460                 VM_Warning("VM_search_getfilename: invalid handle %i used in %s\n", handle, PRVM_NAME);
2461                 return;
2462         }
2463         if(prog->opensearches[handle] == NULL)
2464         {
2465                 VM_Warning("VM_search_getfilename: no such handle %i in %s\n", handle, PRVM_NAME);
2466                 return;
2467         }
2468         if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames)
2469         {
2470                 VM_Warning("VM_search_getfilename: invalid filenum %i in %s\n", filenum, PRVM_NAME);
2471                 return;
2472         }
2473
2474         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog->opensearches[handle]->filenames[filenum]);
2475 }
2476
2477 /*
2478 =========
2479 VM_chr
2480
2481 string  chr(float ascii)
2482 =========
2483 */
2484 void VM_chr(void)
2485 {
2486         char tmp[2];
2487         VM_SAFEPARMCOUNT(1, VM_chr);
2488
2489         tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
2490         tmp[1] = 0;
2491
2492         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
2493 }
2494
2495 //=============================================================================
2496 // Draw builtins (client & menu)
2497
2498 /*
2499 =========
2500 VM_iscachedpic
2501
2502 float   iscachedpic(string pic)
2503 =========
2504 */
2505 void VM_iscachedpic(void)
2506 {
2507         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
2508
2509         // drawq hasnt such a function, thus always return true
2510         PRVM_G_FLOAT(OFS_RETURN) = false;
2511 }
2512
2513 /*
2514 =========
2515 VM_precache_pic
2516
2517 string  precache_pic(string pic)
2518 =========
2519 */
2520 void VM_precache_pic(void)
2521 {
2522         const char      *s;
2523
2524         VM_SAFEPARMCOUNT(1, VM_precache_pic);
2525
2526         s = PRVM_G_STRING(OFS_PARM0);
2527         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
2528         VM_CheckEmptyString (s);
2529
2530         // AK Draw_CachePic is supposed to always return a valid pointer
2531         if( Draw_CachePic(s, false)->tex == r_texture_notexture )
2532                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2533 }
2534
2535 /*
2536 =========
2537 VM_freepic
2538
2539 freepic(string s)
2540 =========
2541 */
2542 void VM_freepic(void)
2543 {
2544         const char *s;
2545
2546         VM_SAFEPARMCOUNT(1,VM_freepic);
2547
2548         s = PRVM_G_STRING(OFS_PARM0);
2549         VM_CheckEmptyString (s);
2550
2551         Draw_FreePic(s);
2552 }
2553
2554 /*
2555 =========
2556 VM_drawcharacter
2557
2558 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
2559 =========
2560 */
2561 void VM_drawcharacter(void)
2562 {
2563         float *pos,*scale,*rgb;
2564         char   character;
2565         int flag;
2566         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
2567
2568         character = (char) PRVM_G_FLOAT(OFS_PARM1);
2569         if(character == 0)
2570         {
2571                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2572                 VM_Warning("VM_drawcharacter: %s passed null character !\n",PRVM_NAME);
2573                 return;
2574         }
2575
2576         pos = PRVM_G_VECTOR(OFS_PARM0);
2577         scale = PRVM_G_VECTOR(OFS_PARM2);
2578         rgb = PRVM_G_VECTOR(OFS_PARM3);
2579         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2580
2581         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2582         {
2583                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2584                 VM_Warning("VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2585                 return;
2586         }
2587
2588         if(pos[2] || scale[2])
2589                 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")));
2590
2591         if(!scale[0] || !scale[1])
2592         {
2593                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2594                 VM_Warning("VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2595                 return;
2596         }
2597
2598         DrawQ_String (pos[0], pos[1], &character, 1, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2599         PRVM_G_FLOAT(OFS_RETURN) = 1;
2600 }
2601
2602 /*
2603 =========
2604 VM_drawstring
2605
2606 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
2607 =========
2608 */
2609 void VM_drawstring(void)
2610 {
2611         float *pos,*scale,*rgb;
2612         const char  *string;
2613         int flag;
2614         VM_SAFEPARMCOUNT(6,VM_drawstring);
2615
2616         string = PRVM_G_STRING(OFS_PARM1);
2617         pos = PRVM_G_VECTOR(OFS_PARM0);
2618         scale = PRVM_G_VECTOR(OFS_PARM2);
2619         rgb = PRVM_G_VECTOR(OFS_PARM3);
2620         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
2621
2622         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2623         {
2624                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2625                 VM_Warning("VM_drawstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2626                 return;
2627         }
2628
2629         if(!scale[0] || !scale[1])
2630         {
2631                 PRVM_G_FLOAT(OFS_RETURN) = -3;
2632                 VM_Warning("VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
2633                 return;
2634         }
2635
2636         if(pos[2] || scale[2])
2637                 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")));
2638
2639         DrawQ_String (pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2640         PRVM_G_FLOAT(OFS_RETURN) = 1;
2641 }
2642 /*
2643 =========
2644 VM_drawpic
2645
2646 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
2647 =========
2648 */
2649 void VM_drawpic(void)
2650 {
2651         const char *picname;
2652         float *size, *pos, *rgb;
2653         int flag;
2654
2655         VM_SAFEPARMCOUNT(6,VM_drawpic);
2656
2657         picname = PRVM_G_STRING(OFS_PARM1);
2658         VM_CheckEmptyString (picname);
2659
2660         // is pic cached ? no function yet for that
2661         if(!1)
2662         {
2663                 PRVM_G_FLOAT(OFS_RETURN) = -4;
2664                 VM_Warning("VM_drawpic: %s: %s not cached !\n", PRVM_NAME, picname);
2665                 return;
2666         }
2667
2668         pos = PRVM_G_VECTOR(OFS_PARM0);
2669         size = PRVM_G_VECTOR(OFS_PARM2);
2670         rgb = PRVM_G_VECTOR(OFS_PARM3);
2671         flag = (int) PRVM_G_FLOAT(OFS_PARM5);
2672
2673         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2674         {
2675                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2676                 VM_Warning("VM_drawpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2677                 return;
2678         }
2679
2680         if(pos[2] || size[2])
2681                 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")));
2682
2683         DrawQ_Pic(pos[0], pos[1], Draw_CachePic(picname, true), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
2684         PRVM_G_FLOAT(OFS_RETURN) = 1;
2685 }
2686
2687 /*
2688 =========
2689 VM_drawfill
2690
2691 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
2692 =========
2693 */
2694 void VM_drawfill(void)
2695 {
2696         float *size, *pos, *rgb;
2697         int flag;
2698
2699         VM_SAFEPARMCOUNT(5,VM_drawfill);
2700
2701
2702         pos = PRVM_G_VECTOR(OFS_PARM0);
2703         size = PRVM_G_VECTOR(OFS_PARM1);
2704         rgb = PRVM_G_VECTOR(OFS_PARM2);
2705         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
2706
2707         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
2708         {
2709                 PRVM_G_FLOAT(OFS_RETURN) = -2;
2710                 VM_Warning("VM_drawfill: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
2711                 return;
2712         }
2713
2714         if(pos[2] || size[2])
2715                 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")));
2716
2717         DrawQ_Pic(pos[0], pos[1], NULL, size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
2718         PRVM_G_FLOAT(OFS_RETURN) = 1;
2719 }
2720
2721 /*
2722 =========
2723 VM_drawsetcliparea
2724
2725 drawsetcliparea(float x, float y, float width, float height)
2726 =========
2727 */
2728 void VM_drawsetcliparea(void)
2729 {
2730         float x,y,w,h;
2731         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
2732
2733         x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
2734         y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
2735         w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
2736         h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
2737
2738         DrawQ_SetClipArea(x, y, w, h);
2739 }
2740
2741 /*
2742 =========
2743 VM_drawresetcliparea
2744
2745 drawresetcliparea()
2746 =========
2747 */
2748 void VM_drawresetcliparea(void)
2749 {
2750         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
2751
2752         DrawQ_ResetClipArea();
2753 }
2754
2755 /*
2756 =========
2757 VM_getimagesize
2758
2759 vector  getimagesize(string pic)
2760 =========
2761 */
2762 void VM_getimagesize(void)
2763 {
2764         const char *p;
2765         cachepic_t *pic;
2766
2767         VM_SAFEPARMCOUNT(1,VM_getimagesize);
2768
2769         p = PRVM_G_STRING(OFS_PARM0);
2770         VM_CheckEmptyString (p);
2771
2772         pic = Draw_CachePic (p, false);
2773
2774         PRVM_G_VECTOR(OFS_RETURN)[0] = pic->width;
2775         PRVM_G_VECTOR(OFS_RETURN)[1] = pic->height;
2776         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
2777 }
2778
2779 /*
2780 =========
2781 VM_keynumtostring
2782
2783 string keynumtostring(float keynum)
2784 =========
2785 */
2786 void VM_keynumtostring (void)
2787 {
2788         VM_SAFEPARMCOUNT(1, VM_keynumtostring);
2789
2790         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0)));
2791 }
2792
2793 /*
2794 =========
2795 VM_stringtokeynum
2796
2797 float stringtokeynum(string key)
2798 =========
2799 */
2800 void VM_stringtokeynum (void)
2801 {
2802         VM_SAFEPARMCOUNT( 1, VM_keynumtostring );
2803
2804         PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
2805 }
2806
2807 // CL_Video interface functions
2808
2809 /*
2810 ========================
2811 VM_cin_open
2812
2813 float cin_open(string file, string name)
2814 ========================
2815 */
2816 void VM_cin_open( void )
2817 {
2818         const char *file;
2819         const char *name;
2820
2821         VM_SAFEPARMCOUNT( 2, VM_cin_open );
2822
2823         file = PRVM_G_STRING( OFS_PARM0 );
2824         name = PRVM_G_STRING( OFS_PARM1 );
2825
2826         VM_CheckEmptyString( file );
2827     VM_CheckEmptyString( name );
2828
2829         if( CL_OpenVideo( file, name, MENUOWNER ) )
2830                 PRVM_G_FLOAT( OFS_RETURN ) = 1;
2831         else
2832                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
2833 }
2834
2835 /*
2836 ========================
2837 VM_cin_close
2838
2839 void cin_close(string name)
2840 ========================
2841 */
2842 void VM_cin_close( void )
2843 {
2844         const char *name;
2845
2846         VM_SAFEPARMCOUNT( 1, VM_cin_close );
2847
2848         name = PRVM_G_STRING( OFS_PARM0 );
2849         VM_CheckEmptyString( name );
2850
2851         CL_CloseVideo( CL_GetVideoByName( name ) );
2852 }
2853
2854 /*
2855 ========================
2856 VM_cin_setstate
2857 void cin_setstate(string name, float type)
2858 ========================
2859 */
2860 void VM_cin_setstate( void )
2861 {
2862         const char *name;
2863         clvideostate_t  state;
2864         clvideo_t               *video;
2865
2866         VM_SAFEPARMCOUNT( 2, VM_cin_netstate );
2867
2868         name = PRVM_G_STRING( OFS_PARM0 );
2869         VM_CheckEmptyString( name );
2870
2871         state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
2872
2873         video = CL_GetVideoByName( name );
2874         if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
2875                 CL_SetVideoState( video, state );
2876 }
2877
2878 /*
2879 ========================
2880 VM_cin_getstate
2881
2882 float cin_getstate(string name)
2883 ========================
2884 */
2885 void VM_cin_getstate( void )
2886 {
2887         const char *name;
2888         clvideo_t               *video;
2889
2890         VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
2891
2892         name = PRVM_G_STRING( OFS_PARM0 );
2893         VM_CheckEmptyString( name );
2894
2895         video = CL_GetVideoByName( name );
2896         if( video )
2897                 PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
2898         else
2899                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
2900 }
2901
2902 /*
2903 ========================
2904 VM_cin_restart
2905
2906 void cin_restart(string name)
2907 ========================
2908 */
2909 void VM_cin_restart( void )
2910 {
2911         const char *name;
2912         clvideo_t               *video;
2913
2914         VM_SAFEPARMCOUNT( 1, VM_cin_restart );
2915
2916         name = PRVM_G_STRING( OFS_PARM0 );
2917         VM_CheckEmptyString( name );
2918
2919         video = CL_GetVideoByName( name );
2920         if( video )
2921                 CL_RestartVideo( video );
2922 }
2923
2924 /*
2925 ==============
2926 VM_vectorvectors
2927
2928 Writes new values for v_forward, v_up, and v_right based on the given forward vector
2929 vectorvectors(vector, vector)
2930 ==============
2931 */
2932 void VM_vectorvectors (void)
2933 {
2934         VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward);
2935         VectorVectors(prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up);
2936 }
2937
2938 /*
2939 ========================
2940 VM_drawline
2941
2942 void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
2943 ========================
2944 */
2945 void VM_drawline (void)
2946 {
2947         float   *c1, *c2, *rgb;
2948         float   alpha, width;
2949         unsigned char   flags;
2950
2951         VM_SAFEPARMCOUNT(6, VM_drawline);
2952         width   = PRVM_G_FLOAT(OFS_PARM0);
2953         c1              = PRVM_G_VECTOR(OFS_PARM1);
2954         c2              = PRVM_G_VECTOR(OFS_PARM2);
2955         rgb             = PRVM_G_VECTOR(OFS_PARM3);
2956         alpha   = PRVM_G_FLOAT(OFS_PARM4);
2957         flags   = (int)PRVM_G_FLOAT(OFS_PARM5);
2958         DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
2959 }
2960
2961 //====================
2962 //QC POLYGON functions
2963 //====================
2964
2965 typedef struct
2966 {
2967         rtexture_t              *tex;
2968         float                   data[36];       //[515]: enough for polygons
2969         unsigned char                   flags;  //[515]: + VM_POLYGON_2D and VM_POLYGON_FL4V flags
2970 }vm_polygon_t;
2971
2972 //static float                  vm_polygon_linewidth = 1;
2973 static mempool_t                *vm_polygons_pool = NULL;
2974 static unsigned char                    vm_current_vertices = 0;
2975 static qboolean                 vm_polygons_initialized = false;
2976 static vm_polygon_t             *vm_polygons = NULL;
2977 static unsigned long    vm_polygons_num = 0, vm_drawpolygons_num = 0;   //[515]: ok long on 64bit ?
2978 static qboolean                 vm_polygonbegin = false;        //[515]: for "no-crap-on-the-screen" check
2979 #define VM_DEFPOLYNUM 64        //[515]: enough for default ?
2980
2981 #define VM_POLYGON_FL3V         16      //more than 2 vertices (used only for lines)
2982 #define VM_POLYGON_FLLINES      32
2983 #define VM_POLYGON_FL2D         64
2984 #define VM_POLYGON_FL4V         128     //4 vertices
2985
2986 void VM_InitPolygons (void)
2987 {
2988         vm_polygons_pool = Mem_AllocPool("VMPOLY", 0, NULL);
2989         vm_polygons = (vm_polygon_t *)Mem_Alloc(vm_polygons_pool, VM_DEFPOLYNUM*sizeof(vm_polygon_t));
2990         memset(vm_polygons, 0, VM_DEFPOLYNUM*sizeof(vm_polygon_t));
2991         vm_polygons_num = VM_DEFPOLYNUM;
2992         vm_drawpolygons_num = 0;
2993         vm_polygonbegin = false;
2994         vm_polygons_initialized = true;
2995 }
2996
2997 void VM_DrawPolygonCallback (const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2998 {
2999         int surfacelistindex;
3000         // LordHavoc: FIXME: this is stupid code
3001         for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
3002         {
3003                 const vm_polygon_t      *p = &vm_polygons[surfacelist[surfacelistindex]];
3004                 int                                     flags = p->flags & 0x0f;
3005
3006                 if(flags == DRAWFLAG_ADDITIVE)
3007                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
3008                 else if(flags == DRAWFLAG_MODULATE)
3009                         GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
3010                 else if(flags == DRAWFLAG_2XMODULATE)
3011                         GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
3012                 else
3013                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3014
3015                 R_Mesh_TexBind(0, R_GetTexture(p->tex));
3016
3017                 CHECKGLERROR
3018                 //[515]: is speed is max ?
3019                 if(p->flags & VM_POLYGON_FLLINES)       //[515]: lines
3020                 {
3021                         qglLineWidth(p->data[13]);CHECKGLERROR
3022                         qglBegin(GL_LINE_LOOP);
3023                                 qglTexCoord1f   (p->data[12]);
3024                                 qglColor4f              (p->data[20], p->data[21], p->data[22], p->data[23]);
3025                                 qglVertex3f             (p->data[0] , p->data[1],  p->data[2]);
3026
3027                                 qglTexCoord1f   (p->data[14]);
3028                                 qglColor4f              (p->data[24], p->data[25], p->data[26], p->data[27]);
3029                                 qglVertex3f             (p->data[3] , p->data[4],  p->data[5]);
3030
3031                                 if(p->flags & VM_POLYGON_FL3V)
3032                                 {
3033                                         qglTexCoord1f   (p->data[16]);
3034                                         qglColor4f              (p->data[28], p->data[29], p->data[30], p->data[31]);
3035                                         qglVertex3f             (p->data[6] , p->data[7],  p->data[8]);
3036
3037                                         if(p->flags & VM_POLYGON_FL4V)
3038                                         {
3039                                                 qglTexCoord1f   (p->data[18]);
3040                                                 qglColor4f              (p->data[32], p->data[33], p->data[34], p->data[35]);
3041                                                 qglVertex3f             (p->data[9] , p->data[10],  p->data[11]);
3042                                         }
3043                                 }
3044                         qglEnd();
3045                         CHECKGLERROR
3046                 }
3047                 else
3048                 {
3049                         qglBegin(GL_POLYGON);
3050                                 qglTexCoord2f   (p->data[12], p->data[13]);
3051                                 qglColor4f              (p->data[20], p->data[21], p->data[22], p->data[23]);
3052                                 qglVertex3f             (p->data[0] , p->data[1],  p->data[2]);
3053
3054                                 qglTexCoord2f   (p->data[14], p->data[15]);
3055                                 qglColor4f              (p->data[24], p->data[25], p->data[26], p->data[27]);
3056                                 qglVertex3f             (p->data[3] , p->data[4],  p->data[5]);
3057
3058                                 qglTexCoord2f   (p->data[16], p->data[17]);
3059                                 qglColor4f              (p->data[28], p->data[29], p->data[30], p->data[31]);
3060                                 qglVertex3f             (p->data[6] , p->data[7],  p->data[8]);
3061
3062                                 if(p->flags & VM_POLYGON_FL4V)
3063                                 {
3064                                         qglTexCoord2f   (p->data[18], p->data[19]);
3065                                         qglColor4f              (p->data[32], p->data[33], p->data[34], p->data[35]);
3066                                         qglVertex3f             (p->data[9] , p->data[10],  p->data[11]);
3067                                 }
3068                         qglEnd();
3069                         CHECKGLERROR
3070                 }
3071         }
3072 }
3073
3074 void VM_AddPolygonTo2DScene (vm_polygon_t *p)
3075 {
3076         drawqueuemesh_t mesh;
3077         static int              picelements[6] = {0, 1, 2, 0, 2, 3};
3078
3079         mesh.texture = p->tex;
3080         mesh.data_element3i = picelements;
3081         mesh.data_vertex3f = p->data;
3082         mesh.data_texcoord2f = p->data + 12;
3083         mesh.data_color4f = p->data + 20;
3084         if(p->flags & VM_POLYGON_FL4V)
3085         {
3086                 mesh.num_vertices = 4;
3087                 mesh.num_triangles = 2;
3088         }
3089         else
3090         {
3091                 mesh.num_vertices = 3;
3092                 mesh.num_triangles = 1;
3093         }
3094         if(p->flags & VM_POLYGON_FLLINES)       //[515]: lines
3095                 DrawQ_LineLoop (&mesh, (p->flags&0x0f));
3096         else
3097                 DrawQ_Mesh (&mesh, (p->flags&0x0f));
3098 }
3099
3100 //void(string texturename, float flag, float 2d, float lines) R_BeginPolygon
3101 void VM_R_PolygonBegin (void)
3102 {
3103         vm_polygon_t    *p;
3104         const char              *picname;
3105         if(prog->argc < 2)
3106                 VM_SAFEPARMCOUNT(2, VM_R_PolygonBegin);
3107
3108         if(!vm_polygons_initialized)
3109                 VM_InitPolygons();
3110         if(vm_polygonbegin)
3111         {
3112                 VM_Warning("VM_R_PolygonBegin: called twice without VM_R_PolygonEnd after first\n");
3113                 return;
3114         }
3115         if(vm_drawpolygons_num >= vm_polygons_num)
3116         {
3117                 p = (vm_polygon_t *)Mem_Alloc(vm_polygons_pool, 2 * vm_polygons_num * sizeof(vm_polygon_t));
3118                 memset(p, 0, 2 * vm_polygons_num * sizeof(vm_polygon_t));
3119                 memcpy(p, vm_polygons, vm_polygons_num * sizeof(vm_polygon_t));
3120                 Mem_Free(vm_polygons);
3121                 vm_polygons = p;
3122                 vm_polygons_num *= 2;
3123         }
3124         p = &vm_polygons[vm_drawpolygons_num];
3125         picname = PRVM_G_STRING(OFS_PARM0);
3126         if(picname[0])
3127                 p->tex = Draw_CachePic(picname, true)->tex;
3128         else
3129                 p->tex = r_texture_white;
3130         p->flags = (unsigned char)PRVM_G_FLOAT(OFS_PARM1);
3131         vm_current_vertices = 0;
3132         vm_polygonbegin = true;
3133         if(prog->argc >= 3)
3134         {
3135                 if(PRVM_G_FLOAT(OFS_PARM2))
3136                         p->flags |= VM_POLYGON_FL2D;
3137                 if(prog->argc >= 4 && PRVM_G_FLOAT(OFS_PARM3))
3138                 {
3139                         p->data[13] = PRVM_G_FLOAT(OFS_PARM3);  //[515]: linewidth
3140                         p->flags |= VM_POLYGON_FLLINES;
3141                 }
3142         }
3143 }
3144
3145 //void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex
3146 void VM_R_PolygonVertex (void)
3147 {
3148         float                   *coords, *tx, *rgb, alpha;
3149         vm_polygon_t    *p;
3150         VM_SAFEPARMCOUNT(4, VM_R_PolygonVertex);
3151
3152         if(!vm_polygonbegin)
3153         {
3154                 VM_Warning("VM_R_PolygonVertex: VM_R_PolygonBegin wasn't called\n");
3155                 return;
3156         }
3157         coords  = PRVM_G_VECTOR(OFS_PARM0);
3158         tx              = PRVM_G_VECTOR(OFS_PARM1);
3159         rgb             = PRVM_G_VECTOR(OFS_PARM2);
3160         alpha = PRVM_G_FLOAT(OFS_PARM3);
3161
3162         p = &vm_polygons[vm_drawpolygons_num];
3163         if(vm_current_vertices > 4)
3164         {
3165                 VM_Warning("VM_R_PolygonVertex: may have 4 vertices max\n");
3166                 return;
3167         }
3168
3169         p->data[vm_current_vertices*3]          = coords[0];
3170         p->data[1+vm_current_vertices*3]        = coords[1];
3171         p->data[2+vm_current_vertices*3]        = coords[2];
3172
3173         p->data[12+vm_current_vertices*2]       = tx[0];
3174         if(!(p->flags & VM_POLYGON_FLLINES))
3175                 p->data[13+vm_current_vertices*2]       = tx[1];
3176
3177         p->data[20+vm_current_vertices*4]       = rgb[0];
3178         p->data[21+vm_current_vertices*4]       = rgb[1];
3179         p->data[22+vm_current_vertices*4]       = rgb[2];
3180         p->data[23+vm_current_vertices*4]       = alpha;
3181
3182         vm_current_vertices++;
3183         if(vm_current_vertices == 4)
3184                 p->flags |= VM_POLYGON_FL4V;
3185         else
3186                 if(vm_current_vertices == 3)
3187                         p->flags |= VM_POLYGON_FL3V;
3188 }
3189
3190 //void() R_EndPolygon
3191 void VM_R_PolygonEnd (void)
3192 {
3193         if(!vm_polygonbegin)
3194         {
3195                 VM_Warning("VM_R_PolygonEnd: VM_R_PolygonBegin wasn't called\n");
3196                 return;
3197         }
3198         vm_polygonbegin = false;
3199         if(vm_current_vertices > 2 || (vm_current_vertices >= 2 && vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FLLINES))
3200         {
3201                 if(vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FL2D)    //[515]: don't use qcpolygons memory if 2D
3202                         VM_AddPolygonTo2DScene(&vm_polygons[vm_drawpolygons_num]);
3203                 else
3204                         vm_drawpolygons_num++;
3205         }
3206         else
3207                 VM_Warning("VM_R_PolygonEnd: %i vertices isn't a good choice\n", vm_current_vertices);
3208 }
3209
3210 void VM_AddPolygonsToMeshQueue (void)
3211 {
3212         int i;
3213         if(!vm_drawpolygons_num)
3214                 return;
3215         R_Mesh_Matrix(&identitymatrix);
3216         GL_CullFace(GL_NONE);
3217         for(i = 0;i < (int)vm_drawpolygons_num;i++)
3218                 VM_DrawPolygonCallback(NULL, NULL, 1, &i);
3219         vm_drawpolygons_num = 0;
3220 }
3221
3222 void Debug_PolygonBegin(const char *picname, int flags, qboolean draw2d, float linewidth)
3223 {
3224         vm_polygon_t    *p;
3225
3226         if(!vm_polygons_initialized)
3227                 VM_InitPolygons();
3228         if(vm_polygonbegin)
3229         {
3230                 Con_Printf("Debug_PolygonBegin: called twice without Debug_PolygonEnd after first\n");
3231                 return;
3232         }
3233         // limit polygons to a vaguely sane amount, beyond this each one just
3234         // replaces the last one
3235         vm_drawpolygons_num = min(vm_drawpolygons_num, (1<<20)-1);
3236         if(vm_drawpolygons_num >= vm_polygons_num)
3237         {
3238                 p = (vm_polygon_t *)Mem_Alloc(vm_polygons_pool, 2 * vm_polygons_num * sizeof(vm_polygon_t));
3239                 memset(p, 0, 2 * vm_polygons_num * sizeof(vm_polygon_t));
3240                 memcpy(p, vm_polygons, vm_polygons_num * sizeof(vm_polygon_t));
3241                 Mem_Free(vm_polygons);
3242                 vm_polygons = p;
3243                 vm_polygons_num *= 2;
3244         }
3245         p = &vm_polygons[vm_drawpolygons_num];
3246         if(picname && picname[0])
3247                 p->tex = Draw_CachePic(picname, true)->tex;
3248         else
3249                 p->tex = r_texture_white;
3250         p->flags = flags;
3251         vm_current_vertices = 0;
3252         vm_polygonbegin = true;
3253         if(draw2d)
3254                 p->flags |= VM_POLYGON_FL2D;
3255         if(linewidth)
3256         {
3257                 p->data[13] = linewidth;        //[515]: linewidth
3258                 p->flags |= VM_POLYGON_FLLINES;
3259         }
3260 }
3261
3262 void Debug_PolygonVertex(float x, float y, float z, float s, float t, float r, float g, float b, float a)
3263 {
3264         vm_polygon_t    *p;
3265
3266         if(!vm_polygonbegin)
3267         {
3268                 Con_Printf("Debug_PolygonVertex: Debug_PolygonBegin wasn't called\n");
3269                 return;
3270         }
3271
3272         p = &vm_polygons[vm_drawpolygons_num];
3273         if(vm_current_vertices > 4)
3274         {
3275                 Con_Printf("Debug_PolygonVertex: may have 4 vertices max\n");
3276                 return;
3277         }
3278
3279         p->data[vm_current_vertices*3]          = x;
3280         p->data[1+vm_current_vertices*3]        = y;
3281         p->data[2+vm_current_vertices*3]        = z;
3282
3283         p->data[12+vm_current_vertices*2]       = s;
3284         if(!(p->flags & VM_POLYGON_FLLINES))
3285                 p->data[13+vm_current_vertices*2]       = t;
3286
3287         p->data[20+vm_current_vertices*4]       = r;
3288         p->data[21+vm_current_vertices*4]       = g;
3289         p->data[22+vm_current_vertices*4]       = b;
3290         p->data[23+vm_current_vertices*4]       = a;
3291
3292         vm_current_vertices++;
3293         if(vm_current_vertices == 4)
3294                 p->flags |= VM_POLYGON_FL4V;
3295         else
3296                 if(vm_current_vertices == 3)
3297                         p->flags |= VM_POLYGON_FL3V;
3298 }
3299
3300 void Debug_PolygonEnd(void)
3301 {
3302         if(!vm_polygonbegin)
3303         {
3304                 Con_Printf("Debug_PolygonEnd: Debug_PolygonBegin wasn't called\n");
3305                 return;
3306         }
3307         vm_polygonbegin = false;
3308         if(vm_current_vertices > 2 || (vm_current_vertices >= 2 && vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FLLINES))
3309         {
3310                 if(vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FL2D)    //[515]: don't use qcpolygons memory if 2D
3311                         VM_AddPolygonTo2DScene(&vm_polygons[vm_drawpolygons_num]);
3312                 else
3313                         vm_drawpolygons_num++;
3314         }
3315         else
3316                 Con_Printf("Debug_PolygonEnd: %i vertices isn't a good choice\n", vm_current_vertices);
3317 }
3318
3319
3320
3321
3322
3323 // float(float number, float quantity) bitshift (EXT_BITSHIFT)
3324 void VM_bitshift (void)
3325 {
3326         int n1, n2;
3327         VM_SAFEPARMCOUNT(2, VM_bitshift);
3328
3329         n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0));
3330         n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
3331         if(!n1)
3332                 PRVM_G_FLOAT(OFS_RETURN) = n1;
3333         else
3334         if(n2 < 0)
3335                 PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
3336         else
3337                 PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
3338 }
3339
3340 ////////////////////////////////////////
3341 // AltString functions
3342 ////////////////////////////////////////
3343
3344 /*
3345 ========================
3346 VM_altstr_count
3347
3348 float altstr_count(string)
3349 ========================
3350 */
3351 void VM_altstr_count( void )
3352 {
3353         const char *altstr, *pos;
3354         int     count;
3355
3356         VM_SAFEPARMCOUNT( 1, VM_altstr_count );
3357
3358         altstr = PRVM_G_STRING( OFS_PARM0 );
3359         //VM_CheckEmptyString( altstr );
3360
3361         for( count = 0, pos = altstr ; *pos ; pos++ ) {
3362                 if( *pos == '\\' ) {
3363                         if( !*++pos ) {
3364                                 break;
3365                         }
3366                 } else if( *pos == '\'' ) {
3367                         count++;
3368                 }
3369         }
3370
3371         PRVM_G_FLOAT( OFS_RETURN ) = (float) (count / 2);
3372 }
3373
3374 /*
3375 ========================
3376 VM_altstr_prepare
3377
3378 string altstr_prepare(string)
3379 ========================
3380 */
3381 void VM_altstr_prepare( void )
3382 {
3383         char *out;
3384         const char *instr, *in;
3385         int size;
3386         char outstr[VM_STRINGTEMP_LENGTH];
3387
3388         VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
3389
3390         instr = PRVM_G_STRING( OFS_PARM0 );
3391
3392         for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ )
3393                 if( *in == '\'' ) {
3394                         *out++ = '\\';
3395                         *out = '\'';
3396                         size--;
3397                 } else
3398                         *out = *in;
3399         *out = 0;
3400
3401         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3402 }
3403
3404 /*
3405 ========================
3406 VM_altstr_get
3407
3408 string altstr_get(string, float)
3409 ========================
3410 */
3411 void VM_altstr_get( void )
3412 {
3413         const char *altstr, *pos;
3414         char *out;
3415         int count, size;
3416         char outstr[VM_STRINGTEMP_LENGTH];
3417
3418         VM_SAFEPARMCOUNT( 2, VM_altstr_get );
3419
3420         altstr = PRVM_G_STRING( OFS_PARM0 );
3421
3422         count = (int)PRVM_G_FLOAT( OFS_PARM1 );
3423         count = count * 2 + 1;
3424
3425         for( pos = altstr ; *pos && count ; pos++ )
3426                 if( *pos == '\\' ) {
3427                         if( !*++pos )
3428                                 break;
3429                 } else if( *pos == '\'' )
3430                         count--;
3431
3432         if( !*pos ) {
3433                 PRVM_G_INT( OFS_RETURN ) = 0;
3434                 return;
3435         }
3436
3437         for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
3438                 if( *pos == '\\' ) {
3439                         if( !*++pos )
3440                                 break;
3441                         *out = *pos;
3442                         size--;
3443                 } else if( *pos == '\'' )
3444                         break;
3445                 else
3446                         *out = *pos;
3447
3448         *out = 0;
3449         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3450 }
3451
3452 /*
3453 ========================
3454 VM_altstr_set
3455
3456 string altstr_set(string altstr, float num, string set)
3457 ========================
3458 */
3459 void VM_altstr_set( void )
3460 {
3461     int num;
3462         const char *altstr, *str;
3463         const char *in;
3464         char *out;
3465         char outstr[VM_STRINGTEMP_LENGTH];
3466
3467         VM_SAFEPARMCOUNT( 3, VM_altstr_set );
3468
3469         altstr = PRVM_G_STRING( OFS_PARM0 );
3470
3471         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3472
3473         str = PRVM_G_STRING( OFS_PARM2 );
3474
3475         out = outstr;
3476         for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
3477                 if( *in == '\\' ) {
3478                         if( !*++in ) {
3479                                 break;
3480                         }
3481                 } else if( *in == '\'' ) {
3482                         num--;
3483                 }
3484
3485         // copy set in
3486         for( ; *str; *out++ = *str++ );
3487         // now jump over the old content
3488         for( ; *in ; in++ )
3489                 if( *in == '\'' || (*in == '\\' && !*++in) )
3490                         break;
3491
3492         strlcpy(out, in, outstr + sizeof(outstr) - out);
3493         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3494 }
3495
3496 /*
3497 ========================
3498 VM_altstr_ins
3499 insert after num
3500 string  altstr_ins(string altstr, float num, string set)
3501 ========================
3502 */
3503 void VM_altstr_ins(void)
3504 {
3505         int num;
3506         const char *setstr;
3507         const char *set;
3508         const char *instr;
3509         const char *in;
3510         char *out;
3511         char outstr[VM_STRINGTEMP_LENGTH];
3512
3513         in = instr = PRVM_G_STRING( OFS_PARM0 );
3514         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
3515         set = setstr = PRVM_G_STRING( OFS_PARM2 );
3516
3517         out = outstr;
3518         for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
3519                 if( *in == '\\' ) {
3520                         if( !*++in ) {
3521                                 break;
3522                         }
3523                 } else if( *in == '\'' ) {
3524                         num--;
3525                 }
3526
3527         *out++ = '\'';
3528         for( ; *set ; *out++ = *set++ );
3529         *out++ = '\'';
3530
3531         strlcpy(out, in, outstr + sizeof(outstr) - out);
3532         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString( outstr );
3533 }
3534
3535
3536 ////////////////////////////////////////
3537 // BufString functions
3538 ////////////////////////////////////////
3539 //[515]: string buffers support
3540 #define MAX_QCSTR_BUFFERS 128
3541 #define MAX_QCSTR_STRINGS 1024
3542
3543 typedef struct
3544 {
3545         int             num_strings;
3546         char    *strings[MAX_QCSTR_STRINGS];
3547 }qcstrbuffer_t;
3548
3549 static qcstrbuffer_t    *qcstringbuffers[MAX_QCSTR_BUFFERS];
3550 static int                              num_qcstringbuffers;
3551 static int                              buf_sortpower;
3552
3553 #define BUFSTR_BUFFER(a) (a>=MAX_QCSTR_BUFFERS) ? NULL : (qcstringbuffers[a])
3554 #define BUFSTR_ISFREE(a) (a<MAX_QCSTR_BUFFERS&&qcstringbuffers[a]&&qcstringbuffers[a]->num_strings<=0) ? 1 : 0
3555
3556 static int BufStr_FindFreeBuffer (void)
3557 {
3558         int     i;
3559         if(num_qcstringbuffers == MAX_QCSTR_BUFFERS)
3560                 return -1;
3561         for(i=0;i<MAX_QCSTR_BUFFERS;i++)
3562                 if(!qcstringbuffers[i])
3563                 {
3564                         qcstringbuffers[i] = (qcstrbuffer_t *)Z_Malloc(sizeof(qcstrbuffer_t));
3565                         memset(qcstringbuffers[i], 0, sizeof(qcstrbuffer_t));
3566                         return i;
3567                 }
3568         return -1;
3569 }
3570
3571 static void BufStr_ClearBuffer (int index)
3572 {
3573         qcstrbuffer_t   *b = qcstringbuffers[index];
3574         int                             i;
3575
3576         if(b)
3577         {
3578                 if(b->num_strings > 0)
3579                 {
3580                         for(i=0;i<b->num_strings;i++)
3581                                 if(b->strings[i])
3582                                         Z_Free(b->strings[i]);
3583                         num_qcstringbuffers--;
3584                 }
3585                 Z_Free(qcstringbuffers[index]);
3586                 qcstringbuffers[index] = NULL;
3587         }
3588 }
3589
3590 static int BufStr_FindFreeString (qcstrbuffer_t *b)
3591 {
3592         int                             i;
3593         for(i=0;i<b->num_strings;i++)
3594                 if(!b->strings[i] || !b->strings[i][0])
3595                         return i;
3596         if(i == MAX_QCSTR_STRINGS)      return -1;
3597         else                                            return i;
3598 }
3599
3600 static int BufStr_SortStringsUP (const void *in1, const void *in2)
3601 {
3602         const char *a, *b;
3603         a = *((const char **) in1);
3604         b = *((const char **) in2);
3605         if(!a[0])       return 1;
3606         if(!b[0])       return -1;
3607         return strncmp(a, b, buf_sortpower);
3608 }
3609
3610 static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
3611 {
3612         const char *a, *b;
3613         a = *((const char **) in1);
3614         b = *((const char **) in2);
3615         if(!a[0])       return 1;
3616         if(!b[0])       return -1;
3617         return strncmp(b, a, buf_sortpower);
3618 }
3619
3620 #ifdef REMOVETHIS
3621 static void VM_BufStr_Init (void)
3622 {
3623         memset(qcstringbuffers, 0, sizeof(qcstringbuffers));
3624         num_qcstringbuffers = 0;
3625 }
3626
3627 static void VM_BufStr_ShutDown (void)
3628 {
3629         int i;
3630         for(i=0;i<MAX_QCSTR_BUFFERS && num_qcstringbuffers;i++)
3631                 BufStr_ClearBuffer(i);
3632 }
3633 #endif
3634
3635 /*
3636 ========================
3637 VM_buf_create
3638 creates new buffer, and returns it's index, returns -1 if failed
3639 float buf_create(void) = #460;
3640 ========================
3641 */
3642 void VM_buf_create (void)
3643 {
3644         int i;
3645         VM_SAFEPARMCOUNT(0, VM_buf_create);
3646         i = BufStr_FindFreeBuffer();
3647         if(i >= 0)
3648                 num_qcstringbuffers++;
3649         //else
3650                 //Con_Printf("VM_buf_create: buffers overflow in %s\n", PRVM_NAME);
3651         PRVM_G_FLOAT(OFS_RETURN) = i;
3652 }
3653
3654 /*
3655 ========================
3656 VM_buf_del
3657 deletes buffer and all strings in it
3658 void buf_del(float bufhandle) = #461;
3659 ========================
3660 */
3661 void VM_buf_del (void)
3662 {
3663         VM_SAFEPARMCOUNT(1, VM_buf_del);
3664         if(BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)))
3665                 BufStr_ClearBuffer((int)PRVM_G_FLOAT(OFS_PARM0));
3666         else
3667         {
3668                 VM_Warning("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3669                 return;
3670         }
3671 }
3672
3673 /*
3674 ========================
3675 VM_buf_getsize
3676 how many strings are stored in buffer
3677 float buf_getsize(float bufhandle) = #462;
3678 ========================
3679 */
3680 void VM_buf_getsize (void)
3681 {
3682         qcstrbuffer_t   *b;
3683         VM_SAFEPARMCOUNT(1, VM_buf_getsize);
3684
3685         b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
3686         if(!b)
3687         {
3688                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3689                 VM_Warning("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3690                 return;
3691         }
3692         else
3693                 PRVM_G_FLOAT(OFS_RETURN) = b->num_strings;
3694 }
3695
3696 /*
3697 ========================
3698 VM_buf_copy
3699 copy all content from one buffer to another, make sure it exists
3700 void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
3701 ========================
3702 */
3703 void VM_buf_copy (void)
3704 {
3705         qcstrbuffer_t   *b1, *b2;
3706         int                             i;
3707         VM_SAFEPARMCOUNT(2, VM_buf_copy);
3708
3709         b1 = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
3710         if(!b1)
3711         {
3712                 VM_Warning("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3713                 return;
3714         }
3715         i = (int)PRVM_G_FLOAT(OFS_PARM1);
3716         if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
3717         {
3718                 VM_Warning("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME);
3719                 return;
3720         }
3721         b2 = BUFSTR_BUFFER(i);
3722         if(!b2)
3723         {
3724                 VM_Warning("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
3725                 return;
3726         }
3727
3728         BufStr_ClearBuffer(i);
3729         qcstringbuffers[i] = (qcstrbuffer_t *)Z_Malloc(sizeof(qcstrbuffer_t));
3730         memset(qcstringbuffers[i], 0, sizeof(qcstrbuffer_t));
3731         b2->num_strings = b1->num_strings;
3732
3733         for(i=0;i<b1->num_strings;i++)
3734                 if(b1->strings[i] && b1->strings[i][0])
3735                 {
3736                         size_t stringlen;
3737                         stringlen = strlen(b1->strings[i]) + 1;
3738                         b2->strings[i] = (char *)Z_Malloc(stringlen);
3739                         if(!b2->strings[i])
3740                         {
3741                                 VM_Warning("VM_buf_copy: not enough memory for buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME);
3742                                 break;
3743                         }
3744                         memcpy(b2->strings[i], b1->strings[i], stringlen);
3745                 }
3746 }
3747
3748 /*
3749 ========================
3750 VM_buf_sort
3751 sort buffer by beginnings of strings (sortpower defaults it's lenght)
3752 "backward == TRUE" means that sorting goes upside-down
3753 void buf_sort(float bufhandle, float sortpower, float backward) = #464;
3754 ========================
3755 */
3756 void VM_buf_sort (void)
3757 {
3758         qcstrbuffer_t   *b;
3759         int                             i;
3760         VM_SAFEPARMCOUNT(3, VM_buf_sort);
3761
3762         b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
3763         if(!b)
3764         {
3765                 VM_Warning("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3766                 return;
3767         }
3768         if(b->num_strings <= 0)
3769         {
3770                 VM_Warning("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3771                 return;
3772         }
3773         buf_sortpower = (int)PRVM_G_FLOAT(OFS_PARM1);
3774         if(buf_sortpower <= 0)
3775                 buf_sortpower = 99999999;
3776
3777         if(!PRVM_G_FLOAT(OFS_PARM2))
3778                 qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsUP);
3779         else
3780                 qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
3781
3782         for(i=b->num_strings-1;i>=0;i--)        //[515]: delete empty lines
3783                 if(b->strings)
3784                 {
3785                         if(b->strings[i][0])
3786                                 break;
3787                         else
3788                         {
3789                                 Z_Free(b->strings[i]);
3790                                 --b->num_strings;
3791                                 b->strings[i] = NULL;
3792                         }
3793                 }
3794                 else
3795                         --b->num_strings;
3796 }
3797
3798 /*
3799 ========================
3800 VM_buf_implode
3801 concantenates all buffer string into one with "glue" separator and returns it as tempstring
3802 string buf_implode(float bufhandle, string glue) = #465;
3803 ========================
3804 */
3805 void VM_buf_implode (void)
3806 {
3807         qcstrbuffer_t   *b;
3808         char                    k[VM_STRINGTEMP_LENGTH];
3809         const char              *sep;
3810         int                             i;
3811         size_t                  l;
3812         VM_SAFEPARMCOUNT(2, VM_buf_implode);
3813
3814         b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
3815         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
3816         if(!b)
3817         {
3818                 VM_Warning("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3819                 return;
3820         }
3821         if(!b->num_strings)
3822                 return;
3823         sep = PRVM_G_STRING(OFS_PARM1);
3824         k[0] = 0;
3825         for(l=i=0;i<b->num_strings;i++)
3826                 if(b->strings[i])
3827                 {
3828                         l += (i > 0 ? strlen(sep) : 0) + strlen(b->strings[i]);
3829                         if (l >= sizeof(k) - 1)
3830                                 break;
3831                         strlcat(k, sep, sizeof(k));
3832                         strlcat(k, b->strings[i], sizeof(k));
3833                 }
3834         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(k);
3835 }
3836
3837 /*
3838 ========================
3839 VM_bufstr_get
3840 get a string from buffer, returns tempstring, dont str_unzone it!
3841 string bufstr_get(float bufhandle, float string_index) = #465;
3842 ========================
3843 */
3844 void VM_bufstr_get (void)
3845 {
3846         qcstrbuffer_t   *b;
3847         int                             strindex;
3848         VM_SAFEPARMCOUNT(2, VM_bufstr_get);
3849
3850         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
3851         b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
3852         if(!b)
3853         {
3854                 VM_Warning("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3855                 return;
3856         }
3857         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
3858         if(strindex < 0 || strindex > MAX_QCSTR_STRINGS)
3859         {
3860                 VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME);
3861                 return;
3862         }
3863         if(b->num_strings <= strindex)
3864                 return;
3865         if(b->strings[strindex])
3866                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(b->strings[strindex]);
3867 }
3868
3869 /*
3870 ========================
3871 VM_bufstr_set
3872 copies a string into selected slot of buffer
3873 void bufstr_set(float bufhandle, float string_index, string str) = #466;
3874 ========================
3875 */
3876 void VM_bufstr_set (void)
3877 {
3878         int                             bufindex, strindex;
3879         qcstrbuffer_t   *b;
3880         const char              *news;
3881         size_t                  alloclen;
3882
3883         VM_SAFEPARMCOUNT(3, VM_bufstr_set);
3884
3885         bufindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3886         b = BUFSTR_BUFFER(bufindex);
3887         if(!b)
3888         {
3889                 VM_Warning("VM_bufstr_set: invalid buffer %i used in %s\n", bufindex, PRVM_NAME);
3890                 return;
3891         }
3892         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
3893         if(strindex < 0 || strindex > MAX_QCSTR_STRINGS)
3894         {
3895                 VM_Warning("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME);
3896                 return;
3897         }
3898         news = PRVM_G_STRING(OFS_PARM2);
3899         if(b->strings[strindex])
3900                 Z_Free(b->strings[strindex]);
3901         alloclen = strlen(news) + 1;
3902         b->strings[strindex] = (char *)Z_Malloc(alloclen);
3903         memcpy(b->strings[strindex], news, alloclen);
3904 }
3905
3906 /*
3907 ========================
3908 VM_bufstr_add
3909 adds string to buffer in nearest free slot and returns it
3910 "order == TRUE" means that string will be added after last "full" slot
3911 float bufstr_add(float bufhandle, string str, float order) = #467;
3912 ========================
3913 */
3914 void VM_bufstr_add (void)
3915 {
3916         int                             bufindex, order, strindex;
3917         qcstrbuffer_t   *b;
3918         const char              *string;
3919         size_t                  alloclen;
3920
3921         VM_SAFEPARMCOUNT(3, VM_bufstr_add);
3922
3923         bufindex = (int)PRVM_G_FLOAT(OFS_PARM0);
3924         b = BUFSTR_BUFFER(bufindex);
3925         PRVM_G_FLOAT(OFS_RETURN) = -1;
3926         if(!b)
3927         {
3928                 VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", bufindex, PRVM_NAME);
3929                 return;
3930         }
3931         string = PRVM_G_STRING(OFS_PARM1);
3932         order = (int)PRVM_G_FLOAT(OFS_PARM2);
3933         if(order)
3934                 strindex = b->num_strings;
3935         else
3936         {
3937                 strindex = BufStr_FindFreeString(b);
3938                 if(strindex < 0)
3939                 {
3940                         VM_Warning("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME);
3941                         return;
3942                 }
3943         }
3944
3945         while(b->num_strings <= strindex)
3946         {
3947                 if(b->num_strings == MAX_QCSTR_STRINGS)
3948                 {
3949                         VM_Warning("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME);
3950                         return;
3951                 }
3952                 b->strings[b->num_strings] = NULL;
3953                 b->num_strings++;
3954         }
3955         if(b->strings[strindex])
3956                 Z_Free(b->strings[strindex]);
3957         alloclen = strlen(string) + 1;
3958         b->strings[strindex] = (char *)Z_Malloc(alloclen);
3959         memcpy(b->strings[strindex], string, alloclen);
3960         PRVM_G_FLOAT(OFS_RETURN) = strindex;
3961 }
3962
3963 /*
3964 ========================
3965 VM_bufstr_free
3966 delete string from buffer
3967 void bufstr_free(float bufhandle, float string_index) = #468;
3968 ========================
3969 */
3970 void VM_bufstr_free (void)
3971 {
3972         int                             i;
3973         qcstrbuffer_t   *b;
3974         VM_SAFEPARMCOUNT(2, VM_bufstr_free);
3975
3976         b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0));
3977         if(!b)
3978         {
3979                 VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
3980                 return;
3981         }
3982         i = (int)PRVM_G_FLOAT(OFS_PARM1);
3983         if(i < 0 || i > MAX_QCSTR_STRINGS)
3984         {
3985                 VM_Warning("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME);
3986                 return;
3987         }
3988         if(b->strings[i])
3989                 Z_Free(b->strings[i]);
3990         b->strings[i] = NULL;
3991         if(i+1 == b->num_strings)
3992                 --b->num_strings;
3993 }
3994
3995 //=============
3996
3997 void VM_Cmd_Init(void)
3998 {
3999         // only init the stuff for the current prog
4000         VM_Files_Init();
4001         VM_Search_Init();
4002 //      VM_BufStr_Init();
4003         if(vm_polygons_initialized)
4004         {
4005                 Mem_FreePool(&vm_polygons_pool);
4006                 vm_polygons_initialized = false;
4007         }
4008 }
4009
4010 void VM_Cmd_Reset(void)
4011 {
4012         CL_PurgeOwner( MENUOWNER );
4013         VM_Search_Reset();
4014         VM_Files_CloseAll();
4015 //      VM_BufStr_ShutDown();
4016         if(vm_polygons_initialized)
4017         {
4018                 Mem_FreePool(&vm_polygons_pool);
4019                 vm_polygons_initialized = false;
4020         }
4021 }
4022