]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_cmds.c
Comply with ISO C11. Engine will now compile with -pedantic, w/o extra warnings
[xonotic/darkplaces.git] / prvm_cmds.c
1 // AK
2 // Basically every vm builtin cmd should be in here.
3 // All 3 builtin and extension lists can be found here
4 // cause large (I think they will) parts are from pr_cmds the same copyright like in pr_cmds
5 // also applies here
6
7 #include "quakedef.h"
8
9 #include "prvm_cmds.h"
10 #include "libcurl.h"
11 #include <time.h>
12
13 #include "cl_collision.h"
14 #include "clvm_cmds.h"
15 #include "csprogs.h"
16 #include "ft2.h"
17 #include "mdfour.h"
18
19 extern cvar_t prvm_backtraceforwarnings;
20 #ifdef USEODE
21 extern dllhandle_t ode_dll;
22 #endif
23
24 // LadyHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value
25 void VM_Warning(prvm_prog_t *prog, const char *fmt, ...)
26 {
27         va_list argptr;
28         char msg[MAX_INPUTLINE];
29         static double recursive = -1;
30
31         va_start(argptr,fmt);
32         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
33         va_end(argptr);
34
35         Con_Printf(CON_WARN "%s", msg);
36
37         // TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black]
38         if(prvm_backtraceforwarnings.integer && recursive != host.realtime) // NOTE: this compares to the time, just in case if PRVM_PrintState causes a Host_Error and keeps recursive set
39         {
40                 recursive = host.realtime;
41                 PRVM_PrintState(prog, 0);
42                 recursive = -1;
43         }
44 }
45
46
47 //============================================================================
48 // Common
49
50 // TODO DONE: move vm_files and vm_fssearchlist to prvm_prog_t struct
51 // TODO: move vm_files and vm_fssearchlist back [9/13/2006 Black]
52 // TODO: (move vm_files and vm_fssearchlist to prvm_prog_t struct again) [2007-01-23 LadyHavoc]
53 // TODO: will this war ever end? [2007-01-23 LadyHavoc]
54
55 void VM_CheckEmptyString(prvm_prog_t *prog, const char *s)
56 {
57         if (ISWHITESPACE(s[0]))
58                 prog->error_cmd("%s: Bad string", prog->name);
59 }
60
61 void VM_GenerateFrameGroupBlend(prvm_prog_t *prog, framegroupblend_t *framegroupblend, const prvm_edict_t *ed)
62 {
63         // self.frame is the interpolation target (new frame)
64         // self.frame1time is the animation base time for the interpolation target
65         // self.frame2 is the interpolation start (previous frame)
66         // self.frame2time is the animation base time for the interpolation start
67         // self.lerpfrac is the interpolation strength for self.frame2
68         // self.lerpfrac3 is the interpolation strength for self.frame3
69         // self.lerpfrac4 is the interpolation strength for self.frame4
70         // pitch angle on a player model where the animator set up 5 sets of
71         // animations and the csqc simply lerps between sets)
72         framegroupblend[0].frame = (int) PRVM_gameedictfloat(ed, frame     );
73         framegroupblend[1].frame = (int) PRVM_gameedictfloat(ed, frame2    );
74         framegroupblend[2].frame = (int) PRVM_gameedictfloat(ed, frame3    );
75         framegroupblend[3].frame = (int) PRVM_gameedictfloat(ed, frame4    );
76         framegroupblend[0].start =       PRVM_gameedictfloat(ed, frame1time);
77         framegroupblend[1].start =       PRVM_gameedictfloat(ed, frame2time);
78         framegroupblend[2].start =       PRVM_gameedictfloat(ed, frame3time);
79         framegroupblend[3].start =       PRVM_gameedictfloat(ed, frame4time);
80         framegroupblend[1].lerp  =       PRVM_gameedictfloat(ed, lerpfrac  );
81         framegroupblend[2].lerp  =       PRVM_gameedictfloat(ed, lerpfrac3 );
82         framegroupblend[3].lerp  =       PRVM_gameedictfloat(ed, lerpfrac4 );
83         // assume that the (missing) lerpfrac1 is whatever remains after lerpfrac2+lerpfrac3+lerpfrac4 are summed
84         framegroupblend[0].lerp = 1 - framegroupblend[1].lerp - framegroupblend[2].lerp - framegroupblend[3].lerp;
85 }
86
87 // LadyHavoc: quite tempting to break apart this function to reuse the
88 //            duplicated code, but I suspect it is better for performance
89 //            this way
90 void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroupblend_t *framegroupblend, const dp_model_t *model, double curtime)
91 {
92         int sub2, numframes, f, i, k;
93         int isfirstframegroup = true;
94         int nolerp;
95         double sublerp, lerp, d;
96         const animscene_t *scene;
97         const framegroupblend_t *g;
98         frameblend_t *blend = frameblend;
99
100         memset(blend, 0, MAX_FRAMEBLENDS * sizeof(*blend));
101
102         // rpolzer: Not testing isanimated here - a model might have
103         // "animations" that move no vertices (but only bones), thus rendering
104         // may assume it's not animated while processing can't.
105         if (!model)
106         {
107                 blend[0].lerp = 1;
108                 return;
109         }
110
111         nolerp = ((model->type == mod_sprite) ? !r_lerpsprites.integer : !r_lerpmodels.integer) || (model->nolerp == true);
112         numframes = model->numframes;
113         for (k = 0, g = framegroupblend;k < MAX_FRAMEGROUPBLENDS;k++, g++)
114         {
115                 f = g->frame;
116                 if ((unsigned int)f >= (unsigned int)numframes)
117                 {
118                         if (developer_extra.integer)
119                                 Con_DPrintf("VM_FrameBlendFromFrameGroupBlend: no such frame %d in model %s\n", f, model->name);
120                         f = 0;
121                 }
122                 d = lerp = g->lerp;
123                 if (lerp <= 0)
124                         continue;
125                 if (nolerp)
126                 {
127                         if (isfirstframegroup)
128                         {
129                                 d = lerp = 1;
130                                 isfirstframegroup = false;
131                         }
132                         else
133                                 continue;
134                 }
135                 if (model->animscenes)
136                 {
137                         scene = model->animscenes + f;
138                         f = scene->firstframe;
139                         if (scene->framecount > 1)
140                         {
141                                 // this code path is only used on .zym models and torches
142                                 sublerp = scene->framerate * (curtime - g->start);
143                                 f = (int) floor(sublerp);
144                                 sublerp -= f;
145                                 sub2 = f + 1;
146                                 if (sublerp < (1.0 / 65536.0f))
147                                         sublerp = 0;
148                                 if (sublerp > (65535.0f / 65536.0f))
149                                         sublerp = 1;
150                                 if (nolerp)
151                                         sublerp = 0;
152                                 if (scene->loop)
153                                 {
154                                         f = (f % scene->framecount);
155                                         sub2 = (sub2 % scene->framecount);
156                                 }
157                                 f = bound(0, f, (scene->framecount - 1)) + scene->firstframe;
158                                 sub2 = bound(0, sub2, (scene->framecount - 1)) + scene->firstframe;
159                                 d = sublerp * lerp;
160                                 // two framelerps produced from one animation
161                                 if (d > 0)
162                                 {
163                                         for (i = 0;i < MAX_FRAMEBLENDS;i++)
164                                         {
165                                                 if (blend[i].lerp <= 0 || blend[i].subframe == sub2)
166                                                 {
167                                                         blend[i].subframe = sub2;
168                                                         blend[i].lerp += d;
169                                                         break;
170                                                 }
171                                         }
172                                 }
173                                 d = (1 - sublerp) * lerp;
174                         }
175                 }
176                 if (d > 0)
177                 {
178                         for (i = 0;i < MAX_FRAMEBLENDS;i++)
179                         {
180                                 if (blend[i].lerp <= 0 || blend[i].subframe == f)
181                                 {
182                                         blend[i].subframe = f;
183                                         blend[i].lerp += d;
184                                         break;
185                                 }
186                         }
187                 }
188         }
189 }
190
191 void VM_UpdateEdictSkeleton(prvm_prog_t *prog, prvm_edict_t *ed, const dp_model_t *edmodel, const frameblend_t *frameblend)
192 {
193         if (ed->priv.server->skeleton.model != edmodel)
194         {
195                 VM_RemoveEdictSkeleton(prog, ed);
196                 ed->priv.server->skeleton.model = edmodel;
197         }
198         if (!ed->priv.server->skeleton.model || !ed->priv.server->skeleton.model->num_bones)
199         {
200                 if(ed->priv.server->skeleton.relativetransforms)
201                         Mem_Free(ed->priv.server->skeleton.relativetransforms);
202                 ed->priv.server->skeleton.relativetransforms = NULL;
203                 return;
204         }
205
206         {
207                 int skeletonindex = -1;
208                 skeleton_t *skeleton;
209                 skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 1;
210                 if (skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones)
211                 {
212                         // custom skeleton controlled by the game (FTE_CSQC_SKELETONOBJECTS)
213                         if (!ed->priv.server->skeleton.relativetransforms)
214                                 ed->priv.server->skeleton.relativetransforms = (matrix4x4_t *)Mem_Alloc(prog->progs_mempool, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
215                         memcpy(ed->priv.server->skeleton.relativetransforms, skeleton->relativetransforms, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
216                 }
217                 else
218                 {
219                         if(ed->priv.server->skeleton.relativetransforms)
220                                 Mem_Free(ed->priv.server->skeleton.relativetransforms);
221                         ed->priv.server->skeleton.relativetransforms = NULL;
222                 }
223         }
224 }
225
226 void VM_RemoveEdictSkeleton(prvm_prog_t *prog, prvm_edict_t *ed)
227 {
228         if (ed->priv.server->skeleton.relativetransforms)
229                 Mem_Free(ed->priv.server->skeleton.relativetransforms);
230         memset(&ed->priv.server->skeleton, 0, sizeof(ed->priv.server->skeleton));
231 }
232
233
234
235
236 //============================================================================
237 //BUILT-IN FUNCTIONS
238
239 void VM_VarString(prvm_prog_t *prog, int first, char *out, int outlength)
240 {
241         int i;
242         const char *s;
243         char *outend;
244
245         outend = out + outlength - 1;
246         for (i = first;i < prog->argc && out < outend;i++)
247         {
248                 s = PRVM_G_STRING((OFS_PARM0+i*3));
249                 while (out < outend && *s)
250                         *out++ = *s++;
251         }
252         *out++ = 0;
253 }
254
255 /*
256 =================
257 VM_checkextension
258
259 returns true if the extension is supported by the server
260
261 checkextension(extensionname)
262 =================
263 */
264
265 // kind of helper function
266 static qboolean checkextension(prvm_prog_t *prog, const char *name)
267 {
268         const char **e;
269
270         for (e = prog->extensionstring;*e;e++)
271         {
272                 if(!strcasecmp(*e, name))
273                 {
274 #ifdef USEODE
275                         // special sheck for ODE
276                         if (!strncasecmp("DP_PHYSICS_ODE", name, 14))
277                         {
278 #ifndef LINK_TO_LIBODE
279                                 return ode_dll ? true : false;
280 #else
281 #ifdef LINK_TO_LIBODE
282                                 return true;
283 #else
284                                 return false;
285 #endif
286 #endif
287                         }
288 #endif
289
290                         // special sheck for d0_blind_id
291                         if (!strcasecmp("DP_CRYPTO", name))
292                                 return Crypto_Available();
293                         if (!strcasecmp("DP_QC_DIGEST_SHA256", name))
294                                 return Crypto_Available();
295
296                         return true;
297                 }
298         }
299         return false;
300 }
301
302 void VM_checkextension(prvm_prog_t *prog)
303 {
304         VM_SAFEPARMCOUNT(1,VM_checkextension);
305
306         PRVM_G_FLOAT(OFS_RETURN) = checkextension(prog, PRVM_G_STRING(OFS_PARM0));
307 }
308
309 /*
310 =================
311 VM_error
312
313 This is a TERMINAL error, which will kill off the entire prog.
314 Dumps self.
315
316 error(value)
317 =================
318 */
319 void VM_error(prvm_prog_t *prog)
320 {
321         prvm_edict_t    *ed;
322         char string[VM_STRINGTEMP_LENGTH];
323
324         VM_VarString(prog, 0, string, sizeof(string));
325         Con_Printf(CON_ERROR "======%s ERROR in %s:\n%s\n", prog->name, PRVM_GetString(prog, prog->xfunction->s_name), string);
326         ed = PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self));
327         PRVM_ED_Print(prog, ed, NULL);
328
329         prog->error_cmd("%s: Program error in function %s:\n%s\nTip: read above for entity information\n", prog->name, PRVM_GetString(prog, prog->xfunction->s_name), string);
330 }
331
332 /*
333 =================
334 VM_objerror
335
336 Dumps out self, then an error message.  The program is aborted and self is
337 removed, but the level can continue.
338
339 objerror(value)
340 =================
341 */
342 void VM_objerror(prvm_prog_t *prog)
343 {
344         prvm_edict_t    *ed;
345         char string[VM_STRINGTEMP_LENGTH];
346
347         VM_VarString(prog, 0, string, sizeof(string));
348         Con_Printf(CON_ERROR "======OBJECT ERROR======\n"); // , prog->name, PRVM_GetString(prog->xfunction->s_name), string); // or include them? FIXME
349         ed = PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self));
350         PRVM_ED_Print(prog, ed, NULL);
351         PRVM_ED_Free (prog, ed);
352         Con_Printf(CON_ERROR "%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information\n", prog->name, PRVM_GetString(prog, prog->xfunction->s_name), string);
353 }
354
355 /*
356 =================
357 VM_print
358
359 print to console
360
361 print(...[string])
362 =================
363 */
364 void VM_print(prvm_prog_t *prog)
365 {
366         char string[VM_STRINGTEMP_LENGTH];
367
368         VM_VarString(prog, 0, string, sizeof(string));
369         Con_Print(string);
370 }
371
372 /*
373 =================
374 VM_bprint
375
376 broadcast print to everyone on server
377
378 bprint(...[string])
379 =================
380 */
381 void VM_bprint(prvm_prog_t *prog)
382 {
383         char string[VM_STRINGTEMP_LENGTH];
384
385         if(!sv.active)
386         {
387                 VM_Warning(prog, "VM_bprint: game is not server(%s) !\n", prog->name);
388                 return;
389         }
390
391         VM_VarString(prog, 0, string, sizeof(string));
392         SV_BroadcastPrint(string);
393 }
394
395 /*
396 =================
397 VM_sprint (menu & client but only if server.active == true)
398
399 single print to a specific client
400
401 sprint(float clientnum,...[string])
402 =================
403 */
404 void VM_sprint(prvm_prog_t *prog)
405 {
406         client_t        *client;
407         int                     clientnum;
408         char string[VM_STRINGTEMP_LENGTH];
409
410         VM_SAFEPARMCOUNTRANGE(1, 8, VM_sprint);
411
412         //find client for this entity
413         clientnum = (int)PRVM_G_FLOAT(OFS_PARM0);
414         if (!sv.active  || clientnum < 0 || clientnum >= svs.maxclients || !svs.clients[clientnum].active)
415         {
416                 VM_Warning(prog, "VM_sprint: %s: invalid client or server is not active !\n", prog->name);
417                 return;
418         }
419
420         client = svs.clients + clientnum;
421         if (!client->netconnection)
422                 return;
423
424         VM_VarString(prog, 1, string, sizeof(string));
425         MSG_WriteChar(&client->netconnection->message,svc_print);
426         MSG_WriteString(&client->netconnection->message, string);
427 }
428
429 /*
430 =================
431 VM_centerprint
432
433 single print to the screen
434
435 centerprint(value)
436 =================
437 */
438 void VM_centerprint(prvm_prog_t *prog)
439 {
440         char string[VM_STRINGTEMP_LENGTH];
441
442         VM_SAFEPARMCOUNTRANGE(1, 8, VM_centerprint);
443         VM_VarString(prog, 0, string, sizeof(string));
444         SCR_CenterPrint(string);
445 }
446
447 /*
448 =================
449 VM_normalize
450
451 vector normalize(vector)
452 =================
453 */
454 void VM_normalize(prvm_prog_t *prog)
455 {
456         prvm_vec_t      *value1;
457         vec3_t  newvalue;
458         double  f;
459
460         VM_SAFEPARMCOUNT(1,VM_normalize);
461
462         value1 = PRVM_G_VECTOR(OFS_PARM0);
463
464         f = VectorLength2(value1);
465         if (f)
466         {
467                 f = 1.0 / sqrt(f);
468                 VectorScale(value1, f, newvalue);
469         }
470         else
471                 VectorClear(newvalue);
472
473         VectorCopy (newvalue, PRVM_G_VECTOR(OFS_RETURN));
474 }
475
476 /*
477 =================
478 VM_vlen
479
480 scalar vlen(vector)
481 =================
482 */
483 void VM_vlen(prvm_prog_t *prog)
484 {
485         VM_SAFEPARMCOUNT(1,VM_vlen);
486         PRVM_G_FLOAT(OFS_RETURN) = VectorLength(PRVM_G_VECTOR(OFS_PARM0));
487 }
488
489 /*
490 =================
491 VM_vectoyaw
492
493 float vectoyaw(vector)
494 =================
495 */
496 void VM_vectoyaw(prvm_prog_t *prog)
497 {
498         prvm_vec_t      *value1;
499         prvm_vec_t      yaw;
500
501         VM_SAFEPARMCOUNT(1,VM_vectoyaw);
502
503         value1 = PRVM_G_VECTOR(OFS_PARM0);
504
505         if (value1[1] == 0 && value1[0] == 0)
506                 yaw = 0;
507         else
508         {
509                 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
510                 if (yaw < 0)
511                         yaw += 360;
512         }
513
514         PRVM_G_FLOAT(OFS_RETURN) = yaw;
515 }
516
517
518 /*
519 =================
520 VM_vectoangles
521
522 vector vectoangles(vector[, vector])
523 =================
524 */
525 void VM_vectoangles(prvm_prog_t *prog)
526 {
527         vec3_t result, forward, up;
528         VM_SAFEPARMCOUNTRANGE(1, 2,VM_vectoangles);
529
530         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), forward);
531         if (prog->argc >= 2)
532         {
533                 VectorCopy(PRVM_G_VECTOR(OFS_PARM1), up);
534                 AnglesFromVectors(result, forward, up, true);
535         }
536         else
537                 AnglesFromVectors(result, forward, NULL, true);
538         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
539 }
540
541 /*
542 =================
543 VM_random
544
545 Returns a number from 0<= num < 1
546
547 float random()
548 =================
549 */
550 void VM_random(prvm_prog_t *prog)
551 {
552         VM_SAFEPARMCOUNT(0,VM_random);
553
554         PRVM_G_FLOAT(OFS_RETURN) = lhrandom(0, 1);
555 }
556
557 /*
558 =========
559 VM_localsound
560
561 localsound(string sample)
562 =========
563 */
564 void VM_localsound(prvm_prog_t *prog)
565 {
566         const char *s;
567
568         VM_SAFEPARMCOUNT(1,VM_localsound);
569
570         s = PRVM_G_STRING(OFS_PARM0);
571
572         if(!S_LocalSound (s))
573         {
574                 PRVM_G_FLOAT(OFS_RETURN) = -4;
575                 VM_Warning(prog, "VM_localsound: Failed to play %s for %s !\n", s, prog->name);
576                 return;
577         }
578
579         PRVM_G_FLOAT(OFS_RETURN) = 1;
580 }
581
582 /*
583 =================
584 VM_break
585
586 break()
587 =================
588 */
589 void VM_break(prvm_prog_t *prog)
590 {
591         prog->error_cmd("%s: break statement", prog->name);
592 }
593
594 //============================================================================
595
596 /*
597 =================
598 VM_localcmd_client
599
600 Sends text over to the client's execution buffer
601
602 [localcmd (string, ...) or]
603 cmd (string, ...)
604 =================
605 */
606 void VM_localcmd_client(prvm_prog_t *prog)
607 {
608         char string[VM_STRINGTEMP_LENGTH];
609         VM_SAFEPARMCOUNTRANGE(1, 8, VM_localcmd_client);
610         VM_VarString(prog, 0, string, sizeof(string));
611         Cbuf_AddText(&cmd_client, string);
612 }
613
614 /*
615 =================
616 VM_localcmd_server
617
618 Sends text over to the server's execution buffer
619
620 [localcmd (string, ...) or]
621 cmd (string, ...)
622 =================
623 */
624 void VM_localcmd_server(prvm_prog_t *prog)
625 {
626         char string[VM_STRINGTEMP_LENGTH];
627         VM_SAFEPARMCOUNTRANGE(1, 8, VM_localcmd_server);
628         VM_VarString(prog, 0, string, sizeof(string));
629         Cbuf_AddText(&cmd_server, string);
630 }
631
632 static qboolean PRVM_Cvar_ReadOk(prvm_prog_t *prog, const char *string)
633 {
634         cvar_t *cvar;
635         cvar = Cvar_FindVar(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask);
636         return ((cvar) && ((cvar->flags & CVAR_PRIVATE) == 0));
637 }
638
639 /*
640 =================
641 VM_cvar
642
643 float cvar (string)
644 =================
645 */
646 void VM_cvar(prvm_prog_t *prog)
647 {
648         char string[VM_STRINGTEMP_LENGTH];
649         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar);
650         VM_VarString(prog, 0, string, sizeof(string));
651         VM_CheckEmptyString(prog, string);
652         PRVM_G_FLOAT(OFS_RETURN) = PRVM_Cvar_ReadOk(prog, string) ? Cvar_VariableValue(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask) : 0;
653 }
654
655 /*
656 =================
657 VM_cvar
658
659 float cvar_type (string)
660 float CVAR_TYPEFLAG_EXISTS = 1;
661 float CVAR_TYPEFLAG_SAVED = 2;
662 float CVAR_TYPEFLAG_PRIVATE = 4;
663 float CVAR_TYPEFLAG_ENGINE = 8;
664 float CVAR_TYPEFLAG_HASDESCRIPTION = 16;
665 float CVAR_TYPEFLAG_READONLY = 32;
666 =================
667 */
668 void VM_cvar_type(prvm_prog_t *prog)
669 {
670         char string[VM_STRINGTEMP_LENGTH];
671         cvar_t *cvar;
672         int ret;
673
674         VM_SAFEPARMCOUNTRANGE(1, 8, VM_cvar_type);
675         VM_VarString(prog, 0, string, sizeof(string));
676         VM_CheckEmptyString(prog, string);
677         cvar = Cvar_FindVar(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask);
678
679
680         if(!cvar)
681         {
682                 PRVM_G_FLOAT(OFS_RETURN) = 0;
683                 return; // CVAR_TYPE_NONE
684         }
685
686         ret = 1; // CVAR_EXISTS
687         if(cvar->flags & CVAR_SAVE)
688                 ret |= 2; // CVAR_TYPE_SAVED
689         if(cvar->flags & CVAR_PRIVATE)
690                 ret |= 4; // CVAR_TYPE_PRIVATE
691         if(!(cvar->flags & CVAR_ALLOCATED))
692                 ret |= 8; // CVAR_TYPE_ENGINE
693         if(cvar->description != cvar_dummy_description)
694                 ret |= 16; // CVAR_TYPE_HASDESCRIPTION
695         if(cvar->flags & CVAR_READONLY)
696                 ret |= 32; // CVAR_TYPE_READONLY
697         
698         PRVM_G_FLOAT(OFS_RETURN) = ret;
699 }
700
701 /*
702 =================
703 VM_cvar_string
704
705 const string    VM_cvar_string (string, ...)
706 =================
707 */
708 void VM_cvar_string(prvm_prog_t *prog)
709 {
710         char string[VM_STRINGTEMP_LENGTH];
711         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_string);
712         VM_VarString(prog, 0, string, sizeof(string));
713         VM_CheckEmptyString(prog, string);
714         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_Cvar_ReadOk(prog, string) ? Cvar_VariableString(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask) : "");
715 }
716
717
718 /*
719 ========================
720 VM_cvar_defstring
721
722 const string    VM_cvar_defstring (string, ...)
723 ========================
724 */
725 void VM_cvar_defstring(prvm_prog_t *prog)
726 {
727         char string[VM_STRINGTEMP_LENGTH];
728         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_defstring);
729         VM_VarString(prog, 0, string, sizeof(string));
730         VM_CheckEmptyString(prog, string);
731         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Cvar_VariableDefString(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask));
732 }
733
734 /*
735 ========================
736 VM_cvar_defstring
737
738 const string    VM_cvar_description (string, ...)
739 ========================
740 */
741 void VM_cvar_description(prvm_prog_t *prog)
742 {
743         char string[VM_STRINGTEMP_LENGTH];
744         VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_description);
745         VM_VarString(prog, 0, string, sizeof(string));
746         VM_CheckEmptyString(prog, string);
747         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Cvar_VariableDescription(prog->console_cmd->cvars, string, prog->console_cmd->cvars_flagsmask));
748 }
749 /*
750 =================
751 VM_cvar_set
752
753 void cvar_set (string,string, ...)
754 =================
755 */
756 void VM_cvar_set(prvm_prog_t *prog)
757 {
758         const char *name;
759         char string[VM_STRINGTEMP_LENGTH];
760         VM_SAFEPARMCOUNTRANGE(2,8,VM_cvar_set);
761         VM_VarString(prog, 1, string, sizeof(string));
762         name = PRVM_G_STRING(OFS_PARM0);
763         VM_CheckEmptyString(prog, name);
764         Cvar_Set(prog->console_cmd->cvars, name, string);
765 }
766
767 /*
768 =========
769 VM_dprint
770
771 dprint(...[string])
772 =========
773 */
774 void VM_dprint(prvm_prog_t *prog)
775 {
776         char string[VM_STRINGTEMP_LENGTH];
777         VM_SAFEPARMCOUNTRANGE(1, 8, VM_dprint);
778         VM_VarString(prog, 0, string, sizeof(string));
779 #if 1
780         Con_DPrintf("%s", string);
781 #else
782         Con_DPrintf("%s: %s", prog->name, string);
783 #endif
784 }
785
786 /*
787 =========
788 VM_ftos
789
790 string  ftos(float)
791 =========
792 */
793
794 void VM_ftos(prvm_prog_t *prog)
795 {
796         prvm_vec_t v;
797         char s[128];
798
799         VM_SAFEPARMCOUNT(1, VM_ftos);
800
801         v = PRVM_G_FLOAT(OFS_PARM0);
802
803         if ((prvm_vec_t)((prvm_int_t)v) == v)
804                 dpsnprintf(s, sizeof(s), "%.0f", v);
805         else
806                 dpsnprintf(s, sizeof(s), "%f", v);
807         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
808 }
809
810 /*
811 =========
812 VM_fabs
813
814 float   fabs(float)
815 =========
816 */
817
818 void VM_fabs(prvm_prog_t *prog)
819 {
820         prvm_vec_t v;
821
822         VM_SAFEPARMCOUNT(1,VM_fabs);
823
824         v = PRVM_G_FLOAT(OFS_PARM0);
825         PRVM_G_FLOAT(OFS_RETURN) = fabs(v);
826 }
827
828 /*
829 =========
830 VM_vtos
831
832 string  vtos(vector)
833 =========
834 */
835
836 void VM_vtos(prvm_prog_t *prog)
837 {
838         char s[512];
839
840         VM_SAFEPARMCOUNT(1,VM_vtos);
841
842         dpsnprintf (s, sizeof(s), "'%5.1f %5.1f %5.1f'", PRVM_G_VECTOR(OFS_PARM0)[0], PRVM_G_VECTOR(OFS_PARM0)[1], PRVM_G_VECTOR(OFS_PARM0)[2]);
843         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
844 }
845
846 /*
847 =========
848 VM_etos
849
850 string  etos(entity)
851 =========
852 */
853
854 void VM_etos(prvm_prog_t *prog)
855 {
856         char s[128];
857
858         VM_SAFEPARMCOUNT(1, VM_etos);
859
860         dpsnprintf (s, sizeof(s), "entity %i", PRVM_G_EDICTNUM(OFS_PARM0));
861         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
862 }
863
864 /*
865 =========
866 VM_stof
867
868 float stof(...[string])
869 =========
870 */
871 void VM_stof(prvm_prog_t *prog)
872 {
873         char string[VM_STRINGTEMP_LENGTH];
874         VM_SAFEPARMCOUNTRANGE(1, 8, VM_stof);
875         VM_VarString(prog, 0, string, sizeof(string));
876         PRVM_G_FLOAT(OFS_RETURN) = atof(string);
877 }
878
879 /*
880 ========================
881 VM_itof
882
883 float itof(int ent)
884 ========================
885 */
886 void VM_itof(prvm_prog_t *prog)
887 {
888         VM_SAFEPARMCOUNT(1, VM_itof);
889         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
890 }
891
892 /*
893 ========================
894 VM_ftoe
895
896 entity ftoe(float num)
897 ========================
898 */
899 void VM_ftoe(prvm_prog_t *prog)
900 {
901         prvm_int_t ent;
902         VM_SAFEPARMCOUNT(1, VM_ftoe);
903
904         ent = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM0);
905         if (ent < 0 || ent >= prog->max_edicts || PRVM_PROG_TO_EDICT(ent)->priv.required->free)
906                 ent = 0; // return world instead of a free or invalid entity
907
908         PRVM_G_INT(OFS_RETURN) = ent;
909 }
910
911 /*
912 ========================
913 VM_etof
914
915 float etof(entity ent)
916 ========================
917 */
918 void VM_etof(prvm_prog_t *prog)
919 {
920         VM_SAFEPARMCOUNT(1, VM_etof);
921         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICTNUM(OFS_PARM0);
922 }
923
924 /*
925 =========
926 VM_strftime
927
928 string strftime(float uselocaltime, string[, string ...])
929 =========
930 */
931 void VM_strftime(prvm_prog_t *prog)
932 {
933         time_t t;
934 #if _MSC_VER >= 1400
935         struct tm tm;
936         int tmresult;
937 #else
938         struct tm *tm;
939 #endif
940         char fmt[VM_STRINGTEMP_LENGTH];
941         char result[VM_STRINGTEMP_LENGTH];
942         VM_SAFEPARMCOUNTRANGE(2, 8, VM_strftime);
943         VM_VarString(prog, 1, fmt, sizeof(fmt));
944         t = time(NULL);
945 #if _MSC_VER >= 1400
946         if (PRVM_G_FLOAT(OFS_PARM0))
947                 tmresult = localtime_s(&tm, &t);
948         else
949                 tmresult = gmtime_s(&tm, &t);
950         if (!tmresult)
951 #else
952         if (PRVM_G_FLOAT(OFS_PARM0))
953                 tm = localtime(&t);
954         else
955                 tm = gmtime(&t);
956         if (!tm)
957 #endif
958         {
959                 PRVM_G_INT(OFS_RETURN) = 0;
960                 return;
961         }
962 #if _MSC_VER >= 1400
963         strftime(result, sizeof(result), fmt, &tm);
964 #else
965         strftime(result, sizeof(result), fmt, tm);
966 #endif
967         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, result);
968 }
969
970 /*
971 =========
972 VM_spawn
973
974 entity spawn()
975 =========
976 */
977
978 void VM_spawn(prvm_prog_t *prog)
979 {
980         prvm_edict_t    *ed;
981         VM_SAFEPARMCOUNT(0, VM_spawn);
982         prog->xfunction->builtinsprofile += 20;
983         ed = PRVM_ED_Alloc(prog);
984         VM_RETURN_EDICT(ed);
985 }
986
987 /*
988 =========
989 VM_remove
990
991 remove(entity e)
992 =========
993 */
994
995 void VM_remove(prvm_prog_t *prog)
996 {
997         prvm_edict_t    *ed;
998         prog->xfunction->builtinsprofile += 20;
999
1000         VM_SAFEPARMCOUNT(1, VM_remove);
1001
1002         ed = PRVM_G_EDICT(OFS_PARM0);
1003         if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
1004         {
1005                 if (developer.integer > 0)
1006                         VM_Warning(prog, "VM_remove: tried to remove the null entity or a reserved entity!\n" );
1007         }
1008         else if( ed->priv.required->free )
1009         {
1010                 if (developer.integer > 0)
1011                         VM_Warning(prog, "VM_remove: tried to remove an already freed entity!\n" );
1012         }
1013         else
1014                 PRVM_ED_Free (prog, ed);
1015 }
1016
1017 /*
1018 =========
1019 VM_find
1020
1021 entity  find(entity start, .string field, string match)
1022 =========
1023 */
1024
1025 void VM_find(prvm_prog_t *prog)
1026 {
1027         int             e;
1028         int             f;
1029         const char      *s, *t;
1030         prvm_edict_t    *ed;
1031
1032         VM_SAFEPARMCOUNT(3,VM_find);
1033
1034         e = PRVM_G_EDICTNUM(OFS_PARM0);
1035         f = PRVM_G_INT(OFS_PARM1);
1036         s = PRVM_G_STRING(OFS_PARM2);
1037
1038         // LadyHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
1039         // expects it to find all the monsters, so we must be careful to support
1040         // searching for ""
1041
1042         for (e++ ; e < prog->num_edicts ; e++)
1043         {
1044                 prog->xfunction->builtinsprofile++;
1045                 ed = PRVM_EDICT_NUM(e);
1046                 if (ed->priv.required->free)
1047                         continue;
1048                 t = PRVM_E_STRING(ed,f);
1049                 if (!t)
1050                         t = "";
1051                 if (!strcmp(t,s))
1052                 {
1053                         VM_RETURN_EDICT(ed);
1054                         return;
1055                 }
1056         }
1057
1058         VM_RETURN_EDICT(prog->edicts);
1059 }
1060
1061 /*
1062 =========
1063 VM_findfloat
1064
1065   entity        findfloat(entity start, .float field, float match)
1066   entity        findentity(entity start, .entity field, entity match)
1067 =========
1068 */
1069 // LadyHavoc: added this for searching float, int, and entity reference fields
1070 void VM_findfloat(prvm_prog_t *prog)
1071 {
1072         int             e;
1073         int             f;
1074         prvm_vec_t      s;
1075         prvm_edict_t    *ed;
1076
1077         VM_SAFEPARMCOUNT(3,VM_findfloat);
1078
1079         e = PRVM_G_EDICTNUM(OFS_PARM0);
1080         f = PRVM_G_INT(OFS_PARM1);
1081         s = PRVM_G_FLOAT(OFS_PARM2);
1082
1083         for (e++ ; e < prog->num_edicts ; e++)
1084         {
1085                 prog->xfunction->builtinsprofile++;
1086                 ed = PRVM_EDICT_NUM(e);
1087                 if (ed->priv.required->free)
1088                         continue;
1089                 if (PRVM_E_FLOAT(ed,f) == s)
1090                 {
1091                         VM_RETURN_EDICT(ed);
1092                         return;
1093                 }
1094         }
1095
1096         VM_RETURN_EDICT(prog->edicts);
1097 }
1098
1099 /*
1100 =========
1101 VM_findchain
1102
1103 entity  findchain(.string field, string match)
1104 =========
1105 */
1106 // chained search for strings in entity fields
1107 // entity(.string field, string match) findchain = #402;
1108 void VM_findchain(prvm_prog_t *prog)
1109 {
1110         int             i;
1111         int             f;
1112         const char      *s, *t;
1113         prvm_edict_t    *ent, *chain;
1114         int chainfield;
1115
1116         VM_SAFEPARMCOUNTRANGE(2,3,VM_findchain);
1117
1118         if(prog->argc == 3)
1119                 chainfield = PRVM_G_INT(OFS_PARM2);
1120         else
1121                 chainfield = prog->fieldoffsets.chain;
1122         if (chainfield < 0)
1123                 prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
1124
1125         chain = prog->edicts;
1126
1127         f = PRVM_G_INT(OFS_PARM0);
1128         s = PRVM_G_STRING(OFS_PARM1);
1129
1130         // LadyHavoc: apparently BloodMage does a find(world, weaponmodel, "") and
1131         // expects it to find all the monsters, so we must be careful to support
1132         // searching for ""
1133
1134         ent = PRVM_NEXT_EDICT(prog->edicts);
1135         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1136         {
1137                 prog->xfunction->builtinsprofile++;
1138                 if (ent->priv.required->free)
1139                         continue;
1140                 t = PRVM_E_STRING(ent,f);
1141                 if (!t)
1142                         t = "";
1143                 if (strcmp(t,s))
1144                         continue;
1145
1146                 PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_NUM_FOR_EDICT(chain);
1147                 chain = ent;
1148         }
1149
1150         VM_RETURN_EDICT(chain);
1151 }
1152
1153 /*
1154 =========
1155 VM_findchainfloat
1156
1157 entity  findchainfloat(.string field, float match)
1158 entity  findchainentity(.string field, entity match)
1159 =========
1160 */
1161 // LadyHavoc: chained search for float, int, and entity reference fields
1162 // entity(.string field, float match) findchainfloat = #403;
1163 void VM_findchainfloat(prvm_prog_t *prog)
1164 {
1165         int             i;
1166         int             f;
1167         prvm_vec_t      s;
1168         prvm_edict_t    *ent, *chain;
1169         int chainfield;
1170
1171         VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainfloat);
1172
1173         if(prog->argc == 3)
1174                 chainfield = PRVM_G_INT(OFS_PARM2);
1175         else
1176                 chainfield = prog->fieldoffsets.chain;
1177         if (chainfield < 0)
1178                 prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
1179
1180         chain = (prvm_edict_t *)prog->edicts;
1181
1182         f = PRVM_G_INT(OFS_PARM0);
1183         s = PRVM_G_FLOAT(OFS_PARM1);
1184
1185         ent = PRVM_NEXT_EDICT(prog->edicts);
1186         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1187         {
1188                 prog->xfunction->builtinsprofile++;
1189                 if (ent->priv.required->free)
1190                         continue;
1191                 if (PRVM_E_FLOAT(ent,f) != s)
1192                         continue;
1193
1194                 PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_EDICT_TO_PROG(chain);
1195                 chain = ent;
1196         }
1197
1198         VM_RETURN_EDICT(chain);
1199 }
1200
1201 /*
1202 ========================
1203 VM_findflags
1204
1205 entity  findflags(entity start, .float field, float match)
1206 ========================
1207 */
1208 // LadyHavoc: search for flags in float fields
1209 void VM_findflags(prvm_prog_t *prog)
1210 {
1211         prvm_int_t      e;
1212         prvm_int_t      f;
1213         prvm_int_t      s;
1214         prvm_edict_t    *ed;
1215
1216         VM_SAFEPARMCOUNT(3, VM_findflags);
1217
1218
1219         e = PRVM_G_EDICTNUM(OFS_PARM0);
1220         f = PRVM_G_INT(OFS_PARM1);
1221         s = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM2);
1222
1223         for (e++ ; e < prog->num_edicts ; e++)
1224         {
1225                 prog->xfunction->builtinsprofile++;
1226                 ed = PRVM_EDICT_NUM(e);
1227                 if (ed->priv.required->free)
1228                         continue;
1229                 if (!PRVM_E_FLOAT(ed,f))
1230                         continue;
1231                 if ((prvm_int_t)PRVM_E_FLOAT(ed,f) & s)
1232                 {
1233                         VM_RETURN_EDICT(ed);
1234                         return;
1235                 }
1236         }
1237
1238         VM_RETURN_EDICT(prog->edicts);
1239 }
1240
1241 /*
1242 ========================
1243 VM_findchainflags
1244
1245 entity  findchainflags(.float field, float match)
1246 ========================
1247 */
1248 // LadyHavoc: chained search for flags in float fields
1249 void VM_findchainflags(prvm_prog_t *prog)
1250 {
1251         prvm_int_t              i;
1252         prvm_int_t              f;
1253         prvm_int_t              s;
1254         prvm_edict_t    *ent, *chain;
1255         int chainfield;
1256
1257         VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainflags);
1258
1259         if(prog->argc == 3)
1260                 chainfield = PRVM_G_INT(OFS_PARM2);
1261         else
1262                 chainfield = prog->fieldoffsets.chain;
1263         if (chainfield < 0)
1264                 prog->error_cmd("VM_findchain: %s doesnt have the specified chain field !", prog->name);
1265
1266         chain = (prvm_edict_t *)prog->edicts;
1267
1268         f = PRVM_G_INT(OFS_PARM0);
1269         s = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM1);
1270
1271         ent = PRVM_NEXT_EDICT(prog->edicts);
1272         for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1273         {
1274                 prog->xfunction->builtinsprofile++;
1275                 if (ent->priv.required->free)
1276                         continue;
1277                 if (!PRVM_E_FLOAT(ent,f))
1278                         continue;
1279                 if (!((prvm_int_t)PRVM_E_FLOAT(ent,f) & s))
1280                         continue;
1281
1282                 PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_EDICT_TO_PROG(chain);
1283                 chain = ent;
1284         }
1285
1286         VM_RETURN_EDICT(chain);
1287 }
1288
1289 /*
1290 =========
1291 VM_precache_sound
1292
1293 string  precache_sound (string sample)
1294 =========
1295 */
1296 void VM_precache_sound(prvm_prog_t *prog)
1297 {
1298         const char *s;
1299
1300         VM_SAFEPARMCOUNT(1, VM_precache_sound);
1301
1302         s = PRVM_G_STRING(OFS_PARM0);
1303         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1304         //VM_CheckEmptyString(prog, s);
1305
1306         if(snd_initialized.integer && !S_PrecacheSound(s, true, true))
1307         {
1308                 VM_Warning(prog, "VM_precache_sound: Failed to load %s for %s\n", s, prog->name);
1309                 return;
1310         }
1311 }
1312
1313 /*
1314 =================
1315 VM_precache_file
1316
1317 returns the same string as output
1318
1319 does nothing, only used by qcc to build .pak archives
1320 =================
1321 */
1322 void VM_precache_file(prvm_prog_t *prog)
1323 {
1324         VM_SAFEPARMCOUNT(1,VM_precache_file);
1325         // precache_file is only used to copy files with qcc, it does nothing
1326         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
1327 }
1328
1329 /*
1330 =========
1331 VM_coredump
1332
1333 coredump()
1334 =========
1335 */
1336 void VM_coredump(prvm_prog_t *prog)
1337 {
1338         cmd_state_t *cmd = cls.state == ca_dedicated ? &cmd_server : &cmd_client;
1339         VM_SAFEPARMCOUNT(0,VM_coredump);
1340
1341         Cbuf_AddText(cmd, "prvm_edicts ");
1342         Cbuf_AddText(cmd, prog->name);
1343         Cbuf_AddText(cmd, "\n");
1344 }
1345
1346 /*
1347 =========
1348 VM_stackdump
1349
1350 stackdump()
1351 =========
1352 */
1353 void VM_stackdump(prvm_prog_t *prog)
1354 {
1355         VM_SAFEPARMCOUNT(0, VM_stackdump);
1356
1357         PRVM_StackTrace(prog);
1358 }
1359
1360 /*
1361 =========
1362 VM_crash
1363
1364 crash()
1365 =========
1366 */
1367
1368 void VM_crash(prvm_prog_t *prog)
1369 {
1370         VM_SAFEPARMCOUNT(0, VM_crash);
1371
1372         prog->error_cmd("Crash called by %s",prog->name);
1373 }
1374
1375 /*
1376 =========
1377 VM_traceon
1378
1379 traceon()
1380 =========
1381 */
1382 void VM_traceon(prvm_prog_t *prog)
1383 {
1384         VM_SAFEPARMCOUNT(0,VM_traceon);
1385
1386         prog->trace = true;
1387 }
1388
1389 /*
1390 =========
1391 VM_traceoff
1392
1393 traceoff()
1394 =========
1395 */
1396 void VM_traceoff(prvm_prog_t *prog)
1397 {
1398         VM_SAFEPARMCOUNT(0,VM_traceoff);
1399
1400         prog->trace = false;
1401 }
1402
1403 /*
1404 =========
1405 VM_eprint
1406
1407 eprint(entity e)
1408 =========
1409 */
1410 void VM_eprint(prvm_prog_t *prog)
1411 {
1412         VM_SAFEPARMCOUNT(1,VM_eprint);
1413
1414         PRVM_ED_PrintNum (prog, PRVM_G_EDICTNUM(OFS_PARM0), NULL);
1415 }
1416
1417 /*
1418 =========
1419 VM_rint
1420
1421 float   rint(float)
1422 =========
1423 */
1424 void VM_rint(prvm_prog_t *prog)
1425 {
1426         prvm_vec_t f;
1427         VM_SAFEPARMCOUNT(1,VM_rint);
1428
1429         f = PRVM_G_FLOAT(OFS_PARM0);
1430         if (f > 0)
1431                 PRVM_G_FLOAT(OFS_RETURN) = floor(f + 0.5);
1432         else
1433                 PRVM_G_FLOAT(OFS_RETURN) = ceil(f - 0.5);
1434 }
1435
1436 /*
1437 =========
1438 VM_floor
1439
1440 float   floor(float)
1441 =========
1442 */
1443 void VM_floor(prvm_prog_t *prog)
1444 {
1445         VM_SAFEPARMCOUNT(1,VM_floor);
1446
1447         PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
1448 }
1449
1450 /*
1451 =========
1452 VM_ceil
1453
1454 float   ceil(float)
1455 =========
1456 */
1457 void VM_ceil(prvm_prog_t *prog)
1458 {
1459         VM_SAFEPARMCOUNT(1,VM_ceil);
1460
1461         PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
1462 }
1463
1464
1465 /*
1466 =============
1467 VM_nextent
1468
1469 entity  nextent(entity)
1470 =============
1471 */
1472 void VM_nextent(prvm_prog_t *prog)
1473 {
1474         int             i;
1475         prvm_edict_t    *ent;
1476
1477         VM_SAFEPARMCOUNT(1, VM_nextent);
1478
1479         i = PRVM_G_EDICTNUM(OFS_PARM0);
1480         while (1)
1481         {
1482                 prog->xfunction->builtinsprofile++;
1483                 i++;
1484                 if (i == prog->num_edicts)
1485                 {
1486                         VM_RETURN_EDICT(prog->edicts);
1487                         return;
1488                 }
1489                 ent = PRVM_EDICT_NUM(i);
1490                 if (!ent->priv.required->free)
1491                 {
1492                         VM_RETURN_EDICT(ent);
1493                         return;
1494                 }
1495         }
1496 }
1497
1498 //=============================================================================
1499
1500 /*
1501 ==============
1502 VM_changelevel
1503 server and menu
1504
1505 changelevel(string map)
1506 ==============
1507 */
1508 void VM_changelevel(prvm_prog_t *prog)
1509 {
1510         char vabuf[1024];
1511         VM_SAFEPARMCOUNT(1, VM_changelevel);
1512
1513         if(!sv.active)
1514         {
1515                 VM_Warning(prog, "VM_changelevel: game is not server (%s)\n", prog->name);
1516                 return;
1517         }
1518
1519 // make sure we don't issue two changelevels
1520         if (svs.changelevel_issued)
1521                 return;
1522         svs.changelevel_issued = true;
1523
1524         Cbuf_AddText(&cmd_server, va(vabuf, sizeof(vabuf), "changelevel %s\n", PRVM_G_STRING(OFS_PARM0)));
1525 }
1526
1527 /*
1528 =========
1529 VM_sin
1530
1531 float   sin(float)
1532 =========
1533 */
1534 void VM_sin(prvm_prog_t *prog)
1535 {
1536         VM_SAFEPARMCOUNT(1,VM_sin);
1537         PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
1538 }
1539
1540 /*
1541 =========
1542 VM_cos
1543 float   cos(float)
1544 =========
1545 */
1546 void VM_cos(prvm_prog_t *prog)
1547 {
1548         VM_SAFEPARMCOUNT(1,VM_cos);
1549         PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
1550 }
1551
1552 /*
1553 =========
1554 VM_sqrt
1555
1556 float   sqrt(float)
1557 =========
1558 */
1559 void VM_sqrt(prvm_prog_t *prog)
1560 {
1561         VM_SAFEPARMCOUNT(1,VM_sqrt);
1562         PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
1563 }
1564
1565 /*
1566 =========
1567 VM_asin
1568
1569 float   asin(float)
1570 =========
1571 */
1572 void VM_asin(prvm_prog_t *prog)
1573 {
1574         VM_SAFEPARMCOUNT(1,VM_asin);
1575         PRVM_G_FLOAT(OFS_RETURN) = asin(PRVM_G_FLOAT(OFS_PARM0));
1576 }
1577
1578 /*
1579 =========
1580 VM_acos
1581 float   acos(float)
1582 =========
1583 */
1584 void VM_acos(prvm_prog_t *prog)
1585 {
1586         VM_SAFEPARMCOUNT(1,VM_acos);
1587         PRVM_G_FLOAT(OFS_RETURN) = acos(PRVM_G_FLOAT(OFS_PARM0));
1588 }
1589
1590 /*
1591 =========
1592 VM_atan
1593 float   atan(float)
1594 =========
1595 */
1596 void VM_atan(prvm_prog_t *prog)
1597 {
1598         VM_SAFEPARMCOUNT(1,VM_atan);
1599         PRVM_G_FLOAT(OFS_RETURN) = atan(PRVM_G_FLOAT(OFS_PARM0));
1600 }
1601
1602 /*
1603 =========
1604 VM_atan2
1605 float   atan2(float,float)
1606 =========
1607 */
1608 void VM_atan2(prvm_prog_t *prog)
1609 {
1610         VM_SAFEPARMCOUNT(2,VM_atan2);
1611         PRVM_G_FLOAT(OFS_RETURN) = atan2(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1612 }
1613
1614 /*
1615 =========
1616 VM_tan
1617 float   tan(float)
1618 =========
1619 */
1620 void VM_tan(prvm_prog_t *prog)
1621 {
1622         VM_SAFEPARMCOUNT(1,VM_tan);
1623         PRVM_G_FLOAT(OFS_RETURN) = tan(PRVM_G_FLOAT(OFS_PARM0));
1624 }
1625
1626 /*
1627 =================
1628 VM_randomvec
1629
1630 Returns a vector of length < 1 and > 0
1631
1632 vector randomvec()
1633 =================
1634 */
1635 void VM_randomvec(prvm_prog_t *prog)
1636 {
1637         vec3_t temp;
1638         VM_SAFEPARMCOUNT(0, VM_randomvec);
1639         VectorRandom(temp);
1640         VectorCopy(temp, PRVM_G_VECTOR(OFS_RETURN));
1641 }
1642
1643 //=============================================================================
1644
1645 /*
1646 =========
1647 VM_registercvar
1648
1649 float   registercvar (string name, string value[, float flags])
1650 =========
1651 */
1652 void VM_registercvar(prvm_prog_t *prog)
1653 {
1654         const char *name, *value;
1655         int     flags;
1656
1657         VM_SAFEPARMCOUNTRANGE(2, 3, VM_registercvar);
1658
1659         name = PRVM_G_STRING(OFS_PARM0);
1660         value = PRVM_G_STRING(OFS_PARM1);
1661         flags = prog->argc >= 3 ? (int)PRVM_G_FLOAT(OFS_PARM2) : 0;
1662         PRVM_G_FLOAT(OFS_RETURN) = 0;
1663
1664         if(flags > CVAR_MAXFLAGSVAL)
1665                 return;
1666
1667 // first check to see if it has already been defined
1668         if (Cvar_FindVar (prog->console_cmd->cvars, name, prog->console_cmd->cvars_flagsmask))
1669                 return;
1670
1671 // check for overlap with a command
1672         if (Cmd_Exists(&cmd_client, name) || Cmd_Exists(&cmd_server, name))
1673         {
1674                 VM_Warning(prog, "VM_registercvar: %s is a command\n", name);
1675                 return;
1676         }
1677
1678         Cvar_Get(prog->console_cmd->cvars, name, value, prog->console_cmd->cvars_flagsmask | flags, NULL);
1679
1680         PRVM_G_FLOAT(OFS_RETURN) = 1; // success
1681 }
1682
1683
1684 /*
1685 =================
1686 VM_min
1687
1688 returns the minimum of two supplied floats
1689
1690 float min(float a, float b, ...[float])
1691 =================
1692 */
1693 void VM_min(prvm_prog_t *prog)
1694 {
1695         VM_SAFEPARMCOUNTRANGE(2, 8, VM_min);
1696         // LadyHavoc: 3+ argument enhancement suggested by FrikaC
1697         if (prog->argc >= 3)
1698         {
1699                 int i;
1700                 float f = PRVM_G_FLOAT(OFS_PARM0);
1701                 for (i = 1;i < prog->argc;i++)
1702                         if (f > PRVM_G_FLOAT((OFS_PARM0+i*3)))
1703                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1704                 PRVM_G_FLOAT(OFS_RETURN) = f;
1705         }
1706         else
1707                 PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1708 }
1709
1710 /*
1711 =================
1712 VM_max
1713
1714 returns the maximum of two supplied floats
1715
1716 float   max(float a, float b, ...[float])
1717 =================
1718 */
1719 void VM_max(prvm_prog_t *prog)
1720 {
1721         VM_SAFEPARMCOUNTRANGE(2, 8, VM_max);
1722         // LadyHavoc: 3+ argument enhancement suggested by FrikaC
1723         if (prog->argc >= 3)
1724         {
1725                 int i;
1726                 float f = PRVM_G_FLOAT(OFS_PARM0);
1727                 for (i = 1;i < prog->argc;i++)
1728                         if (f < PRVM_G_FLOAT((OFS_PARM0+i*3)))
1729                                 f = PRVM_G_FLOAT((OFS_PARM0+i*3));
1730                 PRVM_G_FLOAT(OFS_RETURN) = f;
1731         }
1732         else
1733                 PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1734 }
1735
1736 /*
1737 =================
1738 VM_bound
1739
1740 returns number bounded by supplied range
1741
1742 float   bound(float min, float value, float max)
1743 =================
1744 */
1745 void VM_bound(prvm_prog_t *prog)
1746 {
1747         VM_SAFEPARMCOUNT(3,VM_bound);
1748         PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
1749 }
1750
1751 /*
1752 =================
1753 VM_pow
1754
1755 returns a raised to power b
1756
1757 float   pow(float a, float b)
1758 =================
1759 */
1760 void VM_pow(prvm_prog_t *prog)
1761 {
1762         VM_SAFEPARMCOUNT(2,VM_pow);
1763         PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
1764 }
1765
1766 void VM_log(prvm_prog_t *prog)
1767 {
1768         VM_SAFEPARMCOUNT(1,VM_log);
1769         PRVM_G_FLOAT(OFS_RETURN) = log(PRVM_G_FLOAT(OFS_PARM0));
1770 }
1771
1772 void VM_Files_Init(prvm_prog_t *prog)
1773 {
1774         int i;
1775         for (i = 0;i < PRVM_MAX_OPENFILES;i++)
1776                 prog->openfiles[i] = NULL;
1777 }
1778
1779 void VM_Files_CloseAll(prvm_prog_t *prog)
1780 {
1781         int i;
1782         for (i = 0;i < PRVM_MAX_OPENFILES;i++)
1783         {
1784                 if (prog->openfiles[i])
1785                         FS_Close(prog->openfiles[i]);
1786                 prog->openfiles[i] = NULL;
1787         }
1788 }
1789
1790 static qfile_t *VM_GetFileHandle(prvm_prog_t *prog, int index)
1791 {
1792         if (index < 0 || index >= PRVM_MAX_OPENFILES)
1793         {
1794                 Con_Printf("VM_GetFileHandle: invalid file handle %i used in %s\n", index, prog->name);
1795                 return NULL;
1796         }
1797         if (prog->openfiles[index] == NULL)
1798         {
1799                 Con_Printf("VM_GetFileHandle: no such file handle %i (or file has been closed) in %s\n", index, prog->name);
1800                 return NULL;
1801         }
1802         return prog->openfiles[index];
1803 }
1804
1805 /*
1806 =========
1807 VM_fopen
1808
1809 float   fopen(string filename, float mode)
1810 =========
1811 */
1812 // float(string filename, float mode) fopen = #110;
1813 // opens a file inside quake/gamedir/data/ (mode is FILE_READ, FILE_APPEND, or FILE_WRITE),
1814 // returns fhandle >= 0 if successful, or fhandle < 0 if unable to open file for any reason
1815 void VM_fopen(prvm_prog_t *prog)
1816 {
1817         int filenum, mode;
1818         const char *modestring, *filename;
1819         char vabuf[1024];
1820
1821         VM_SAFEPARMCOUNT(2,VM_fopen);
1822
1823         for (filenum = 0;filenum < PRVM_MAX_OPENFILES;filenum++)
1824                 if (prog->openfiles[filenum] == NULL)
1825                         break;
1826         if (filenum >= PRVM_MAX_OPENFILES)
1827         {
1828                 PRVM_G_FLOAT(OFS_RETURN) = -2;
1829                 VM_Warning(prog, "VM_fopen: %s ran out of file handles (%i)\n", prog->name, PRVM_MAX_OPENFILES);
1830                 return;
1831         }
1832         filename = PRVM_G_STRING(OFS_PARM0);
1833         mode = (int)PRVM_G_FLOAT(OFS_PARM1);
1834         switch(mode)
1835         {
1836         case 0: // FILE_READ
1837                 modestring = "rb";
1838                 prog->openfiles[filenum] = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", filename), false);
1839                 if (prog->openfiles[filenum] == NULL)
1840                         prog->openfiles[filenum] = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", filename), false);
1841                 break;
1842         case 1: // FILE_APPEND
1843                 modestring = "a";
1844                 prog->openfiles[filenum] = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "data/%s", filename), modestring, false);
1845                 break;
1846         case 2: // FILE_WRITE
1847                 modestring = "w";
1848                 prog->openfiles[filenum] = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "data/%s", filename), modestring, false);
1849                 break;
1850         default:
1851                 PRVM_G_FLOAT(OFS_RETURN) = -3;
1852                 VM_Warning(prog, "VM_fopen: %s: no such mode %i (valid: 0 = read, 1 = append, 2 = write)\n", prog->name, mode);
1853                 return;
1854         }
1855
1856         if (prog->openfiles[filenum] == NULL)
1857         {
1858                 PRVM_G_FLOAT(OFS_RETURN) = -1;
1859                 if (developer_extra.integer)
1860                         VM_Warning(prog, "VM_fopen: %s: %s mode %s failed\n", prog->name, filename, modestring);
1861         }
1862         else
1863         {
1864                 PRVM_G_FLOAT(OFS_RETURN) = filenum;
1865                 if (developer_extra.integer)
1866                         Con_DPrintf("VM_fopen: %s: %s mode %s opened as #%i\n", prog->name, filename, modestring, filenum);
1867                 prog->openfiles_origin[filenum] = PRVM_AllocationOrigin(prog);
1868         }
1869 }
1870
1871 /*
1872 =========
1873 VM_fclose
1874
1875 fclose(float fhandle)
1876 =========
1877 */
1878 //void(float fhandle) fclose = #111; // closes a file
1879 void VM_fclose(prvm_prog_t *prog)
1880 {
1881         int filenum;
1882
1883         VM_SAFEPARMCOUNT(1,VM_fclose);
1884
1885         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1886         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1887         {
1888                 VM_Warning(prog, "VM_fclose: invalid file handle %i used in %s\n", filenum, prog->name);
1889                 return;
1890         }
1891         if (prog->openfiles[filenum] == NULL)
1892         {
1893                 VM_Warning(prog, "VM_fclose: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
1894                 return;
1895         }
1896         FS_Close(prog->openfiles[filenum]);
1897         prog->openfiles[filenum] = NULL;
1898         if(prog->openfiles_origin[filenum])
1899                 PRVM_Free((char *)prog->openfiles_origin[filenum]);
1900         if (developer_extra.integer)
1901                 Con_DPrintf("VM_fclose: %s: #%i closed\n", prog->name, filenum);
1902 }
1903
1904 /*
1905 =========
1906 VM_fgets
1907
1908 string  fgets(float fhandle)
1909 =========
1910 */
1911 //string(float fhandle) fgets = #112; // reads a line of text from the file and returns as a tempstring
1912 void VM_fgets(prvm_prog_t *prog)
1913 {
1914         int c, end;
1915         char string[VM_STRINGTEMP_LENGTH];
1916         int filenum;
1917
1918         VM_SAFEPARMCOUNT(1,VM_fgets);
1919
1920         // set the return value regardless of any possible errors
1921         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
1922
1923         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1924         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1925         {
1926                 VM_Warning(prog, "VM_fgets: invalid file handle %i used in %s\n", filenum, prog->name);
1927                 return;
1928         }
1929         if (prog->openfiles[filenum] == NULL)
1930         {
1931                 VM_Warning(prog, "VM_fgets: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
1932                 return;
1933         }
1934         end = 0;
1935         for (;;)
1936         {
1937                 c = FS_Getc(prog->openfiles[filenum]);
1938                 if (c == '\r' || c == '\n' || c < 0)
1939                         break;
1940                 if (end < VM_STRINGTEMP_LENGTH - 1)
1941                         string[end++] = c;
1942         }
1943         string[end] = 0;
1944         // remove \n following \r
1945         if (c == '\r')
1946         {
1947                 c = FS_Getc(prog->openfiles[filenum]);
1948                 if (c != '\n')
1949                         FS_UnGetc(prog->openfiles[filenum], (unsigned char)c);
1950         }
1951         if (developer_extra.integer)
1952                 Con_DPrintf("fgets: %s: %s\n", prog->name, string);
1953         if (c >= 0 || end)
1954                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
1955 }
1956
1957 /*
1958 =========
1959 VM_fputs
1960
1961 fputs(float fhandle, string s)
1962 =========
1963 */
1964 //void(float fhandle, string s) fputs = #113; // writes a line of text to the end of the file
1965 void VM_fputs(prvm_prog_t *prog)
1966 {
1967         int stringlength;
1968         char string[VM_STRINGTEMP_LENGTH];
1969         int filenum;
1970
1971         VM_SAFEPARMCOUNT(2,VM_fputs);
1972
1973         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
1974         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
1975         {
1976                 VM_Warning(prog, "VM_fputs: invalid file handle %i used in %s\n", filenum, prog->name);
1977                 return;
1978         }
1979         if (prog->openfiles[filenum] == NULL)
1980         {
1981                 VM_Warning(prog, "VM_fputs: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
1982                 return;
1983         }
1984         VM_VarString(prog, 1, string, sizeof(string));
1985         if ((stringlength = (int)strlen(string)))
1986                 FS_Write(prog->openfiles[filenum], string, stringlength);
1987         if (developer_extra.integer)
1988                 Con_DPrintf("fputs: %s: %s\n", prog->name, string);
1989 }
1990
1991 /*
1992 =========
1993 VM_writetofile
1994
1995         writetofile(float fhandle, entity ent)
1996 =========
1997 */
1998 void VM_writetofile(prvm_prog_t *prog)
1999 {
2000         prvm_edict_t * ent;
2001         qfile_t *file;
2002
2003         VM_SAFEPARMCOUNT(2, VM_writetofile);
2004
2005         file = VM_GetFileHandle(prog, (int)PRVM_G_FLOAT(OFS_PARM0));
2006         if( !file )
2007         {
2008                 VM_Warning(prog, "VM_writetofile: invalid or closed file handle\n");
2009                 return;
2010         }
2011
2012         ent = PRVM_G_EDICT(OFS_PARM1);
2013         if(ent->priv.required->free)
2014         {
2015                 VM_Warning(prog, "VM_writetofile: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
2016                 return;
2017         }
2018
2019         PRVM_ED_Write (prog, file, ent);
2020 }
2021
2022 // KrimZon - DP_QC_ENTITYDATA
2023 /*
2024 =========
2025 VM_numentityfields
2026
2027 float() numentityfields
2028 Return the number of entity fields - NOT offsets
2029 =========
2030 */
2031 void VM_numentityfields(prvm_prog_t *prog)
2032 {
2033         PRVM_G_FLOAT(OFS_RETURN) = prog->numfielddefs;
2034 }
2035
2036 // KrimZon - DP_QC_ENTITYDATA
2037 /*
2038 =========
2039 VM_entityfieldname
2040
2041 string(float fieldnum) entityfieldname
2042 Return name of the specified field as a string, or empty if the field is invalid (warning)
2043 =========
2044 */
2045 void VM_entityfieldname(prvm_prog_t *prog)
2046 {
2047         ddef_t *d;
2048         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
2049
2050         if (i < 0 || i >= prog->numfielddefs)
2051         {
2052                 VM_Warning(prog, "VM_entityfieldname: %s: field index out of bounds\n", prog->name);
2053                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
2054                 return;
2055         }
2056
2057         d = &prog->fielddefs[i];
2058         PRVM_G_INT(OFS_RETURN) = d->s_name; // presuming that s_name points to a string already
2059 }
2060
2061 // KrimZon - DP_QC_ENTITYDATA
2062 /*
2063 =========
2064 VM_entityfieldtype
2065
2066 float(float fieldnum) entityfieldtype
2067 =========
2068 */
2069 void VM_entityfieldtype(prvm_prog_t *prog)
2070 {
2071         ddef_t *d;
2072         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
2073         
2074         if (i < 0 || i >= prog->numfielddefs)
2075         {
2076                 VM_Warning(prog, "VM_entityfieldtype: %s: field index out of bounds\n", prog->name);
2077                 PRVM_G_FLOAT(OFS_RETURN) = -1.0;
2078                 return;
2079         }
2080         
2081         d = &prog->fielddefs[i];
2082         PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t)d->type;
2083 }
2084
2085 // KrimZon - DP_QC_ENTITYDATA
2086 /*
2087 =========
2088 VM_getentityfieldstring
2089
2090 string(float fieldnum, entity ent) getentityfieldstring
2091 =========
2092 */
2093 void VM_getentityfieldstring(prvm_prog_t *prog)
2094 {
2095         // put the data into a string
2096         ddef_t *d;
2097         int type, j;
2098         prvm_eval_t *val;
2099         prvm_edict_t * ent;
2100         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
2101         char valuebuf[MAX_INPUTLINE];
2102         
2103         if (i < 0 || i >= prog->numfielddefs)
2104         {
2105         VM_Warning(prog, "VM_entityfielddata: %s: field index out of bounds\n", prog->name);
2106                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
2107                 return;
2108         }
2109         
2110         d = &prog->fielddefs[i];
2111         
2112         // get the entity
2113         ent = PRVM_G_EDICT(OFS_PARM1);
2114         if(ent->priv.required->free)
2115         {
2116                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
2117                 VM_Warning(prog, "VM_entityfielddata: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
2118                 return;
2119         }
2120         val = (prvm_eval_t *)(ent->fields.fp + d->ofs);
2121         
2122         // if it's 0 or blank, return an empty string
2123         type = d->type & ~DEF_SAVEGLOBAL;
2124         for (j=0 ; j<prvm_type_size[type] ; j++)
2125                 if (val->ivector[j])
2126                         break;
2127         if (j == prvm_type_size[type])
2128         {
2129                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
2130                 return;
2131         }
2132                 
2133         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
2134 }
2135
2136 // KrimZon - DP_QC_ENTITYDATA
2137 /*
2138 =========
2139 VM_putentityfieldstring
2140
2141 float(float fieldnum, entity ent, string s) putentityfieldstring
2142 =========
2143 */
2144 void VM_putentityfieldstring(prvm_prog_t *prog)
2145 {
2146         ddef_t *d;
2147         prvm_edict_t * ent;
2148         int i = (int)PRVM_G_FLOAT(OFS_PARM0);
2149
2150         if (i < 0 || i >= prog->numfielddefs)
2151         {
2152         VM_Warning(prog, "VM_entityfielddata: %s: field index out of bounds\n", prog->name);
2153                 PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
2154                 return;
2155         }
2156
2157         d = &prog->fielddefs[i];
2158
2159         // get the entity
2160         ent = PRVM_G_EDICT(OFS_PARM1);
2161         if(ent->priv.required->free)
2162         {
2163                 VM_Warning(prog, "VM_entityfielddata: %s: entity %i is free !\n", prog->name, PRVM_NUM_FOR_EDICT(ent));
2164                 PRVM_G_FLOAT(OFS_RETURN) = 0.0f;
2165                 return;
2166         }
2167
2168         // parse the string into the value
2169         PRVM_G_FLOAT(OFS_RETURN) = ( PRVM_ED_ParseEpair(prog, ent, d, PRVM_G_STRING(OFS_PARM2), false) ) ? 1.0f : 0.0f;
2170 }
2171
2172 /*
2173 =========
2174 VM_strlen
2175
2176 float   strlen(string s)
2177 =========
2178 */
2179 //float(string s) strlen = #114; // returns how many characters are in a string
2180 void VM_strlen(prvm_prog_t *prog)
2181 {
2182         VM_SAFEPARMCOUNT(1,VM_strlen);
2183
2184         //PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
2185         PRVM_G_FLOAT(OFS_RETURN) = u8_strlen(PRVM_G_STRING(OFS_PARM0));
2186 }
2187
2188 // DRESK - Decolorized String
2189 /*
2190 =========
2191 VM_strdecolorize
2192
2193 string  strdecolorize(string s)
2194 =========
2195 */
2196 // string (string s) strdecolorize = #472; // returns the passed in string with color codes stripped
2197 void VM_strdecolorize(prvm_prog_t *prog)
2198 {
2199         char szNewString[VM_STRINGTEMP_LENGTH];
2200         const char *szString;
2201
2202         // Prepare Strings
2203         VM_SAFEPARMCOUNT(1,VM_strdecolorize);
2204         szString = PRVM_G_STRING(OFS_PARM0);
2205         COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), true);
2206         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
2207 }
2208
2209 // DRESK - String Length (not counting color codes)
2210 /*
2211 =========
2212 VM_strlennocol
2213
2214 float   strlennocol(string s)
2215 =========
2216 */
2217 // float(string s) strlennocol = #471; // returns how many characters are in a string not including color codes
2218 // For example, ^2Dresk returns a length of 5
2219 void VM_strlennocol(prvm_prog_t *prog)
2220 {
2221         const char *szString;
2222         int nCnt;
2223
2224         VM_SAFEPARMCOUNT(1,VM_strlennocol);
2225
2226         szString = PRVM_G_STRING(OFS_PARM0);
2227
2228         //nCnt = (int)COM_StringLengthNoColors(szString, 0, NULL);
2229         nCnt = (int)u8_COM_StringLengthNoColors(szString, 0, NULL);
2230
2231         PRVM_G_FLOAT(OFS_RETURN) = nCnt;
2232 }
2233
2234 // DRESK - String to Uppercase and Lowercase
2235 /*
2236 =========
2237 VM_strtolower
2238
2239 string  strtolower(string s)
2240 =========
2241 */
2242 // string (string s) strtolower = #480; // returns passed in string in lowercase form
2243 void VM_strtolower(prvm_prog_t *prog)
2244 {
2245         char szNewString[VM_STRINGTEMP_LENGTH];
2246         const char *szString;
2247
2248         // Prepare Strings
2249         VM_SAFEPARMCOUNT(1,VM_strtolower);
2250         szString = PRVM_G_STRING(OFS_PARM0);
2251
2252         COM_ToLowerString(szString, szNewString, sizeof(szNewString) );
2253
2254         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
2255 }
2256
2257 /*
2258 =========
2259 VM_strtoupper
2260
2261 string  strtoupper(string s)
2262 =========
2263 */
2264 // string (string s) strtoupper = #481; // returns passed in string in uppercase form
2265 void VM_strtoupper(prvm_prog_t *prog)
2266 {
2267         char szNewString[VM_STRINGTEMP_LENGTH];
2268         const char *szString;
2269
2270         // Prepare Strings
2271         VM_SAFEPARMCOUNT(1,VM_strtoupper);
2272         szString = PRVM_G_STRING(OFS_PARM0);
2273
2274         COM_ToUpperString(szString, szNewString, sizeof(szNewString) );
2275
2276         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
2277 }
2278
2279 /*
2280 =========
2281 VM_strcat
2282
2283 string strcat(string,string,...[string])
2284 =========
2285 */
2286 //string(string s1, string s2) strcat = #115;
2287 // concatenates two strings (for example "abc", "def" would return "abcdef")
2288 // and returns as a tempstring
2289 void VM_strcat(prvm_prog_t *prog)
2290 {
2291         char s[VM_STRINGTEMP_LENGTH];
2292         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strcat);
2293
2294         VM_VarString(prog, 0, s, sizeof(s));
2295         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, s);
2296 }
2297
2298 /*
2299 =========
2300 VM_substring
2301
2302 string  substring(string s, float start, float length)
2303 =========
2304 */
2305 // string(string s, float start, float length) substring = #116;
2306 // returns a section of a string as a tempstring
2307 void VM_substring(prvm_prog_t *prog)
2308 {
2309         int start, length;
2310         int u_slength = 0, u_start;
2311         size_t u_length;
2312         const char *s;
2313         char string[VM_STRINGTEMP_LENGTH];
2314
2315         VM_SAFEPARMCOUNT(3,VM_substring);
2316
2317         /*
2318         s = PRVM_G_STRING(OFS_PARM0);
2319         start = (int)PRVM_G_FLOAT(OFS_PARM1);
2320         length = (int)PRVM_G_FLOAT(OFS_PARM2);
2321         slength = strlen(s);
2322
2323         if (start < 0) // FTE_STRINGS feature
2324                 start += slength;
2325         start = bound(0, start, slength);
2326
2327         if (length < 0) // FTE_STRINGS feature
2328                 length += slength - start + 1;
2329         maxlen = min((int)sizeof(string) - 1, slength - start);
2330         length = bound(0, length, maxlen);
2331
2332         memcpy(string, s + start, length);
2333         string[length] = 0;
2334         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
2335         */
2336         
2337         s = PRVM_G_STRING(OFS_PARM0);
2338         start = (int)PRVM_G_FLOAT(OFS_PARM1);
2339         length = (int)PRVM_G_FLOAT(OFS_PARM2);
2340
2341         if (start < 0) // FTE_STRINGS feature
2342         {
2343                 u_slength = (int)u8_strlen(s);
2344                 start += u_slength;
2345                 start = bound(0, start, u_slength);
2346         }
2347
2348         if (length < 0) // FTE_STRINGS feature
2349         {
2350                 if (!u_slength) // it's not calculated when it's not needed above
2351                         u_slength = (int)u8_strlen(s);
2352                 length += u_slength - start + 1;
2353         }
2354                 
2355         // positive start, positive length
2356         u_start = u8_byteofs(s, start, NULL);
2357         if (u_start < 0)
2358         {
2359                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
2360                 return;
2361         }
2362         u_length = u8_bytelen(s + u_start, length);
2363         if (u_length >= sizeof(string)-1)
2364                 u_length = sizeof(string)-1;
2365         
2366         memcpy(string, s + u_start, u_length);
2367         string[u_length] = 0;
2368         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
2369 }
2370
2371 /*
2372 =========
2373 VM_strreplace
2374
2375 string(string search, string replace, string subject) strreplace = #484;
2376 =========
2377 */
2378 // replaces all occurrences of search with replace in the string subject, and returns the result
2379 void VM_strreplace(prvm_prog_t *prog)
2380 {
2381         int i, j, si;
2382         const char *search, *replace, *subject;
2383         char string[VM_STRINGTEMP_LENGTH];
2384         int search_len, replace_len, subject_len;
2385
2386         VM_SAFEPARMCOUNT(3,VM_strreplace);
2387
2388         search = PRVM_G_STRING(OFS_PARM0);
2389         replace = PRVM_G_STRING(OFS_PARM1);
2390         subject = PRVM_G_STRING(OFS_PARM2);
2391
2392         search_len = (int)strlen(search);
2393         replace_len = (int)strlen(replace);
2394         subject_len = (int)strlen(subject);
2395
2396         si = 0;
2397         for (i = 0; i <= subject_len - search_len; i++)
2398         {
2399                 for (j = 0; j < search_len; j++) // thus, i+j < subject_len
2400                         if (subject[i+j] != search[j])
2401                                 break;
2402                 if (j == search_len)
2403                 {
2404                         // NOTE: if search_len == 0, we always hit THIS case, and never the other
2405                         // found it at offset 'i'
2406                         for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
2407                                 string[si++] = replace[j];
2408                         if(search_len > 0)
2409                         {
2410                                 i += search_len - 1;
2411                         }
2412                         else
2413                         {
2414                                 // the above would subtract 1 from i... so we
2415                                 // don't do that, but instead output the next
2416                                 // char
2417                                 if (si < (int)sizeof(string) - 1)
2418                                         string[si++] = subject[i];
2419                         }
2420                 }
2421                 else
2422                 {
2423                         // in THIS case, we know search_len > 0, thus i < subject_len
2424                         // not found
2425                         if (si < (int)sizeof(string) - 1)
2426                                 string[si++] = subject[i];
2427                 }
2428         }
2429         // remaining chars (these cannot match)
2430         for (; i < subject_len; i++)
2431                 if (si < (int)sizeof(string) - 1)
2432                         string[si++] = subject[i];
2433         string[si] = '\0';
2434
2435         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
2436 }
2437
2438 /*
2439 =========
2440 VM_strireplace
2441
2442 string(string search, string replace, string subject) strireplace = #485;
2443 =========
2444 */
2445 // case-insensitive version of strreplace
2446 void VM_strireplace(prvm_prog_t *prog)
2447 {
2448         int i, j, si;
2449         const char *search, *replace, *subject;
2450         char string[VM_STRINGTEMP_LENGTH];
2451         int search_len, replace_len, subject_len;
2452
2453         VM_SAFEPARMCOUNT(3, VM_strireplace);
2454
2455         search = PRVM_G_STRING(OFS_PARM0);
2456         replace = PRVM_G_STRING(OFS_PARM1);
2457         subject = PRVM_G_STRING(OFS_PARM2);
2458
2459         search_len = (int)strlen(search);
2460         replace_len = (int)strlen(replace);
2461         subject_len = (int)strlen(subject);
2462
2463         si = 0;
2464         for (i = 0; i <= subject_len - search_len; i++)
2465         {
2466                 for (j = 0; j < search_len; j++) // thus, i+j < subject_len
2467                         if (tolower(subject[i+j]) != tolower(search[j]))
2468                                 break;
2469                 if (j == search_len)
2470                 {
2471                         // NOTE: if search_len == 0, we always hit THIS case, and never the other
2472                         // found it at offset 'i'
2473                         for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++)
2474                                 string[si++] = replace[j];
2475                         if(search_len > 0)
2476                         {
2477                                 i += search_len - 1;
2478                         }
2479                         else
2480                         {
2481                                 // the above would subtract 1 from i... so we
2482                                 // don't do that, but instead output the next
2483                                 // char
2484                                 if (si < (int)sizeof(string) - 1)
2485                                         string[si++] = subject[i];
2486                         }
2487                 }
2488                 else
2489                 {
2490                         // in THIS case, we know search_len > 0, thus i < subject_len
2491                         // not found
2492                         if (si < (int)sizeof(string) - 1)
2493                                 string[si++] = subject[i];
2494                 }
2495         }
2496         // remaining chars (these cannot match)
2497         for (; i < subject_len; i++)
2498                 if (si < (int)sizeof(string) - 1)
2499                         string[si++] = subject[i];
2500         string[si] = '\0';
2501
2502         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, string);
2503 }
2504
2505 /*
2506 =========
2507 VM_stov
2508
2509 vector  stov(string s)
2510 =========
2511 */
2512 //vector(string s) stov = #117; // returns vector value from a string
2513 void VM_stov(prvm_prog_t *prog)
2514 {
2515         char string[VM_STRINGTEMP_LENGTH];
2516
2517         VM_SAFEPARMCOUNT(1,VM_stov);
2518
2519         VM_VarString(prog, 0, string, sizeof(string));
2520         Math_atov(string, PRVM_G_VECTOR(OFS_RETURN));
2521 }
2522
2523 /*
2524 =========
2525 VM_strzone
2526
2527 string  strzone(string s)
2528 =========
2529 */
2530 //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)
2531 void VM_strzone(prvm_prog_t *prog)
2532 {
2533         char *out;
2534         char string[VM_STRINGTEMP_LENGTH];
2535         size_t alloclen;
2536
2537         VM_SAFEPARMCOUNT(1,VM_strzone);
2538
2539         VM_VarString(prog, 0, string, sizeof(string));
2540         alloclen = strlen(string) + 1;
2541         PRVM_G_INT(OFS_RETURN) = PRVM_AllocString(prog, alloclen, &out);
2542         memcpy(out, string, alloclen);
2543 }
2544
2545 /*
2546 =========
2547 VM_strunzone
2548
2549 strunzone(string s)
2550 =========
2551 */
2552 //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!!!)
2553 void VM_strunzone(prvm_prog_t *prog)
2554 {
2555         VM_SAFEPARMCOUNT(1,VM_strunzone);
2556         PRVM_FreeString(prog, PRVM_G_INT(OFS_PARM0));
2557 }
2558
2559 /*
2560 =========
2561 VM_command (used by client and menu)
2562
2563 clientcommand(float client, string s) (for client and menu)
2564 =========
2565 */
2566 //void(entity e, string s) clientcommand = #440; // executes a command string as if it came from the specified client
2567 //this function originally written by KrimZon, made shorter by LadyHavoc
2568 void VM_clcommand (prvm_prog_t *prog)
2569 {
2570         client_t *temp_client;
2571         int i;
2572
2573         VM_SAFEPARMCOUNT(2,VM_clcommand);
2574
2575         i = (int)PRVM_G_FLOAT(OFS_PARM0);
2576         if (!sv.active  || i < 0 || i >= svs.maxclients || !svs.clients[i].active)
2577         {
2578                 VM_Warning(prog, "VM_clientcommand: %s: invalid client/server is not active !\n", prog->name);
2579                 return;
2580         }
2581
2582         temp_client = host_client;
2583         host_client = svs.clients + i;
2584         Cmd_ExecuteString (&cmd_serverfromclient, PRVM_G_STRING(OFS_PARM1), src_client, true);
2585         host_client = temp_client;
2586 }
2587
2588
2589 /*
2590 =========
2591 VM_tokenize
2592
2593 float tokenize(string s)
2594 =========
2595 */
2596 //float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many
2597 //this function originally written by KrimZon, made shorter by LadyHavoc
2598 //20040203: rewritten by LadyHavoc (no longer uses allocations)
2599 static int num_tokens = 0;
2600 static int tokens[VM_STRINGTEMP_LENGTH / 2];
2601 static int tokens_startpos[VM_STRINGTEMP_LENGTH / 2];
2602 static int tokens_endpos[VM_STRINGTEMP_LENGTH / 2];
2603 static char tokenize_string[VM_STRINGTEMP_LENGTH];
2604 void VM_tokenize (prvm_prog_t *prog)
2605 {
2606         const char *p;
2607
2608         VM_SAFEPARMCOUNT(1,VM_tokenize);
2609
2610         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2611         p = tokenize_string;
2612
2613         num_tokens = 0;
2614         for(;;)
2615         {
2616                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2617                         break;
2618
2619                 // skip whitespace here to find token start pos
2620                 while(*p && ISWHITESPACE(*p))
2621                         ++p;
2622
2623                 tokens_startpos[num_tokens] = p - tokenize_string;
2624                 if(!COM_ParseToken_VM_Tokenize(&p, false))
2625                         break;
2626                 tokens_endpos[num_tokens] = p - tokenize_string;
2627                 tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
2628                 ++num_tokens;
2629         }
2630
2631         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2632 }
2633
2634 //float(string s) tokenize = #514; // takes apart a string into individal words (access them with argv), returns how many
2635 void VM_tokenize_console (prvm_prog_t *prog)
2636 {
2637         const char *p;
2638
2639         VM_SAFEPARMCOUNT(1, VM_tokenize_console);
2640
2641         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2642         p = tokenize_string;
2643
2644         num_tokens = 0;
2645         for(;;)
2646         {
2647                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2648                         break;
2649
2650                 // skip whitespace here to find token start pos
2651                 while(*p && ISWHITESPACE(*p))
2652                         ++p;
2653
2654                 tokens_startpos[num_tokens] = p - tokenize_string;
2655                 if(!COM_ParseToken_Console(&p))
2656                         break;
2657                 tokens_endpos[num_tokens] = p - tokenize_string;
2658                 tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
2659                 ++num_tokens;
2660         }
2661
2662         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2663 }
2664
2665 /*
2666 =========
2667 VM_tokenizebyseparator
2668
2669 float tokenizebyseparator(string s, string separator1, ...)
2670 =========
2671 */
2672 //float(string s, string separator1, ...) tokenizebyseparator = #479; // takes apart a string into individal words (access them with argv), returns how many
2673 //this function returns the token preceding each instance of a separator (of
2674 //which there can be multiple), and the text following the last separator
2675 //useful for parsing certain kinds of data like IP addresses
2676 //example:
2677 //numnumbers = tokenizebyseparator("10.1.2.3", ".");
2678 //returns 4 and the tokens "10" "1" "2" "3".
2679 void VM_tokenizebyseparator (prvm_prog_t *prog)
2680 {
2681         int j, k;
2682         int numseparators;
2683         int separatorlen[7];
2684         const char *separators[7];
2685         const char *p, *p0;
2686         const char *token;
2687         char tokentext[MAX_INPUTLINE];
2688
2689         VM_SAFEPARMCOUNTRANGE(2, 8,VM_tokenizebyseparator);
2690
2691         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2692         p = tokenize_string;
2693
2694         numseparators = 0;
2695         for (j = 1;j < prog->argc;j++)
2696         {
2697                 // skip any blank separator strings
2698                 const char *s = PRVM_G_STRING(OFS_PARM0+j*3);
2699                 if (!s[0])
2700                         continue;
2701                 separators[numseparators] = s;
2702                 separatorlen[numseparators] = (int)strlen(s);
2703                 numseparators++;
2704         }
2705
2706         num_tokens = 0;
2707         j = 0;
2708
2709         while (num_tokens < (int)(sizeof(tokens)/sizeof(tokens[0])))
2710         {
2711                 token = tokentext + j;
2712                 tokens_startpos[num_tokens] = p - tokenize_string;
2713                 p0 = p;
2714                 while (*p)
2715                 {
2716                         for (k = 0;k < numseparators;k++)
2717                         {
2718                                 if (!strncmp(p, separators[k], separatorlen[k]))
2719                                 {
2720                                         p += separatorlen[k];
2721                                         break;
2722                                 }
2723                         }
2724                         if (k < numseparators)
2725                                 break;
2726                         if (j < (int)sizeof(tokentext)-1)
2727                                 tokentext[j++] = *p;
2728                         p++;
2729                         p0 = p;
2730                 }
2731                 tokens_endpos[num_tokens] = p0 - tokenize_string;
2732                 if (j >= (int)sizeof(tokentext))
2733                         break;
2734                 tokentext[j++] = 0;
2735                 tokens[num_tokens++] = PRVM_SetTempString(prog, token);
2736                 if (!*p)
2737                         break;
2738         }
2739
2740         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2741 }
2742
2743 //string(float n) argv = #442; // returns a word from the tokenized string (returns nothing for an invalid index)
2744 //this function originally written by KrimZon, made shorter by LadyHavoc
2745 void VM_argv (prvm_prog_t *prog)
2746 {
2747         int token_num;
2748
2749         VM_SAFEPARMCOUNT(1,VM_argv);
2750
2751         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2752
2753         if(token_num < 0)
2754                 token_num += num_tokens;
2755
2756         if (token_num >= 0 && token_num < num_tokens)
2757                 PRVM_G_INT(OFS_RETURN) = tokens[token_num];
2758         else
2759                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2760 }
2761
2762 //float(float n) argv_start_index = #515; // returns the start index of a token
2763 void VM_argv_start_index (prvm_prog_t *prog)
2764 {
2765         int token_num;
2766
2767         VM_SAFEPARMCOUNT(1,VM_argv);
2768
2769         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2770
2771         if(token_num < 0)
2772                 token_num += num_tokens;
2773
2774         if (token_num >= 0 && token_num < num_tokens)
2775                 PRVM_G_FLOAT(OFS_RETURN) = tokens_startpos[token_num];
2776         else
2777                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2778 }
2779
2780 //float(float n) argv_end_index = #516; // returns the end index of a token
2781 void VM_argv_end_index (prvm_prog_t *prog)
2782 {
2783         int token_num;
2784
2785         VM_SAFEPARMCOUNT(1,VM_argv);
2786
2787         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2788
2789         if(token_num < 0)
2790                 token_num += num_tokens;
2791
2792         if (token_num >= 0 && token_num < num_tokens)
2793                 PRVM_G_FLOAT(OFS_RETURN) = tokens_endpos[token_num];
2794         else
2795                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2796 }
2797
2798 /*
2799 =========
2800 VM_isserver
2801
2802 float   isserver()
2803 =========
2804 */
2805 void VM_isserver(prvm_prog_t *prog)
2806 {
2807         VM_SAFEPARMCOUNT(0, VM_isserver);
2808
2809         PRVM_G_FLOAT(OFS_RETURN) = sv.active;
2810 }
2811
2812 /*
2813 =========
2814 VM_clientcount
2815
2816 float   clientcount()
2817 =========
2818 */
2819 void VM_clientcount(prvm_prog_t *prog)
2820 {
2821         VM_SAFEPARMCOUNT(0,VM_clientcount);
2822
2823         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
2824 }
2825
2826 /*
2827 =========
2828 VM_clientstate
2829
2830 float   clientstate()
2831 =========
2832 */
2833 void VM_clientstate(prvm_prog_t *prog)
2834 {
2835         VM_SAFEPARMCOUNT(0,VM_clientstate);
2836
2837
2838         switch( cls.state ) {
2839                 case ca_uninitialized:
2840                 case ca_dedicated:
2841                         PRVM_G_FLOAT(OFS_RETURN) = 0;
2842                         break;
2843                 case ca_disconnected:
2844                         PRVM_G_FLOAT(OFS_RETURN) = 1;
2845                         break;
2846                 case ca_connected:
2847                         PRVM_G_FLOAT(OFS_RETURN) = 2;
2848                         break;
2849                 default:
2850                         // should never be reached!
2851                         break;
2852         }
2853 }
2854
2855 /*
2856 =========
2857 VM_getostype
2858
2859 float   getostype(prvm_prog_t *prog)
2860 =========
2861 */ // not used at the moment -> not included in the common list
2862 void VM_getostype(prvm_prog_t *prog)
2863 {
2864         VM_SAFEPARMCOUNT(0,VM_getostype);
2865
2866         /*
2867         OS_WINDOWS
2868         OS_LINUX
2869         OS_MAC - not supported
2870         */
2871
2872 #ifdef WIN32
2873         PRVM_G_FLOAT(OFS_RETURN) = 0;
2874 #elif defined(MACOSX)
2875         PRVM_G_FLOAT(OFS_RETURN) = 2;
2876 #else
2877         PRVM_G_FLOAT(OFS_RETURN) = 1;
2878 #endif
2879 }
2880
2881 /*
2882 =========
2883 VM_gettime
2884
2885 float   gettime(prvm_prog_t *prog)
2886 =========
2887 */
2888 float CDAudio_GetPosition(void);
2889 void VM_gettime(prvm_prog_t *prog)
2890 {
2891         int timer_index;
2892
2893         VM_SAFEPARMCOUNTRANGE(0,1,VM_gettime);
2894
2895         if(prog->argc == 0)
2896         {
2897                 PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) host.realtime;
2898         }
2899         else
2900         {
2901                 timer_index = (int) PRVM_G_FLOAT(OFS_PARM0);
2902                 switch(timer_index)
2903                 {
2904                         case 0: // GETTIME_FRAMESTART
2905                                 PRVM_G_FLOAT(OFS_RETURN) = host.realtime;
2906                                 break;
2907                         case 1: // GETTIME_REALTIME
2908                                 PRVM_G_FLOAT(OFS_RETURN) = Sys_DirtyTime();
2909                                 break;
2910                         case 2: // GETTIME_HIRES
2911                                 PRVM_G_FLOAT(OFS_RETURN) = (Sys_DirtyTime() - host.dirtytime);
2912                                 break;
2913                         case 3: // GETTIME_UPTIME
2914                                 PRVM_G_FLOAT(OFS_RETURN) = host.realtime;
2915                                 break;
2916                         case 4: // GETTIME_CDTRACK
2917                                 PRVM_G_FLOAT(OFS_RETURN) = CDAudio_GetPosition();
2918                                 break;
2919                         default:
2920                                 VM_Warning(prog, "VM_gettime: %s: unsupported timer specified, returning realtime\n", prog->name);
2921                                 PRVM_G_FLOAT(OFS_RETURN) = host.realtime;
2922                                 break;
2923                 }
2924         }
2925 }
2926
2927 /*
2928 =========
2929 VM_getsoundtime
2930
2931 float   getsoundtime(prvm_prog_t *prog)
2932 =========
2933 */
2934
2935 void VM_getsoundtime (prvm_prog_t *prog)
2936 {
2937         int entnum, entchannel;
2938         VM_SAFEPARMCOUNT(2,VM_getsoundtime);
2939
2940         if (prog == SVVM_prog)
2941                 entnum = PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0));
2942         else if (prog == CLVM_prog)
2943                 entnum = MAX_EDICTS + PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0));
2944         else
2945         {
2946                 VM_Warning(prog, "VM_getsoundtime: %s: not supported on this progs\n", prog->name);
2947                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2948                 return;
2949         }
2950         entchannel = (int)PRVM_G_FLOAT(OFS_PARM1);
2951         entchannel = CHAN_USER2ENGINE(entchannel);
2952         if (!IS_CHAN(entchannel))
2953                 VM_Warning(prog, "VM_getsoundtime: %s: bad channel %i\n", prog->name, entchannel);
2954         PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t)S_GetEntChannelPosition(entnum, entchannel);
2955 }
2956
2957 /*
2958 =========
2959 VM_GetSoundLen
2960
2961 string  soundlength (string sample)
2962 =========
2963 */
2964 void VM_soundlength (prvm_prog_t *prog)
2965 {
2966         const char *s;
2967
2968         VM_SAFEPARMCOUNT(1, VM_soundlength);
2969
2970         s = PRVM_G_STRING(OFS_PARM0);
2971         PRVM_G_FLOAT(OFS_RETURN) = S_SoundLength(s);
2972 }
2973
2974 /*
2975 =========
2976 VM_loadfromdata
2977
2978 loadfromdata(string data)
2979 =========
2980 */
2981 void VM_loadfromdata(prvm_prog_t *prog)
2982 {
2983         VM_SAFEPARMCOUNT(1, VM_loadfromdata);
2984
2985         PRVM_ED_LoadFromFile(prog, PRVM_G_STRING(OFS_PARM0));
2986 }
2987
2988 /*
2989 ========================
2990 VM_parseentitydata
2991
2992 parseentitydata(entity ent, string data)
2993 ========================
2994 */
2995 void VM_parseentitydata(prvm_prog_t *prog)
2996 {
2997         prvm_edict_t *ent;
2998         const char *data;
2999
3000         VM_SAFEPARMCOUNT(2, VM_parseentitydata);
3001
3002         // get edict and test it
3003         ent = PRVM_G_EDICT(OFS_PARM0);
3004         if (ent->priv.required->free)
3005                 prog->error_cmd("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", prog->name, PRVM_NUM_FOR_EDICT(ent));
3006
3007         data = PRVM_G_STRING(OFS_PARM1);
3008
3009         // parse the opening brace
3010         if (!COM_ParseToken_Simple(&data, false, false, true) || com_token[0] != '{' )
3011                 prog->error_cmd("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", prog->name, data );
3012
3013         PRVM_ED_ParseEdict (prog, data, ent);
3014 }
3015
3016 /*
3017 =========
3018 VM_loadfromfile
3019
3020 loadfromfile(string file)
3021 =========
3022 */
3023 void VM_loadfromfile(prvm_prog_t *prog)
3024 {
3025         const char *filename;
3026         char *data;
3027
3028         VM_SAFEPARMCOUNT(1,VM_loadfromfile);
3029
3030         filename = PRVM_G_STRING(OFS_PARM0);
3031         if (FS_CheckNastyPath(filename, false))
3032         {
3033                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3034                 VM_Warning(prog, "VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", prog->name, filename);
3035                 return;
3036         }
3037
3038         // not conform with VM_fopen
3039         data = (char *)FS_LoadFile(filename, tempmempool, false, NULL);
3040         if (data == NULL)
3041                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3042
3043         PRVM_ED_LoadFromFile(prog, data);
3044
3045         if(data)
3046                 Mem_Free(data);
3047 }
3048
3049
3050 /*
3051 =========
3052 VM_modulo
3053
3054 float   mod(float val, float m)
3055 =========
3056 */
3057 void VM_modulo(prvm_prog_t *prog)
3058 {
3059         prvm_int_t val, m;
3060         VM_SAFEPARMCOUNT(2, VM_modulo);
3061
3062         val = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM0);
3063         m       = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM1);
3064
3065         PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) (val % m);
3066 }
3067
3068 static void VM_Search_Init(prvm_prog_t *prog)
3069 {
3070         int i;
3071         for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++)
3072                 prog->opensearches[i] = NULL;
3073 }
3074
3075 static void VM_Search_Reset(prvm_prog_t *prog)
3076 {
3077         int i;
3078         // reset the fssearch list
3079         for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++)
3080         {
3081                 if(prog->opensearches[i])
3082                         FS_FreeSearch(prog->opensearches[i]);
3083                 prog->opensearches[i] = NULL;
3084         }
3085 }
3086
3087 /*
3088 =========
3089 VM_search_begin
3090
3091 float search_begin(string pattern, float caseinsensitive, float quiet)
3092 =========
3093 */
3094 void VM_search_begin(prvm_prog_t *prog)
3095 {
3096         int handle;
3097         const char *pattern;
3098         int caseinsens, quiet;
3099
3100         VM_SAFEPARMCOUNT(3, VM_search_begin);
3101
3102         pattern = PRVM_G_STRING(OFS_PARM0);
3103
3104         VM_CheckEmptyString(prog, pattern);
3105
3106         caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1);
3107         quiet = (int)PRVM_G_FLOAT(OFS_PARM2);
3108
3109         for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++)
3110                 if(!prog->opensearches[handle])
3111                         break;
3112
3113         if(handle >= PRVM_MAX_OPENSEARCHES)
3114         {
3115                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3116                 VM_Warning(prog, "VM_search_begin: %s ran out of search handles (%i)\n", prog->name, PRVM_MAX_OPENSEARCHES);
3117                 return;
3118         }
3119
3120         if(!(prog->opensearches[handle] = FS_Search(pattern,caseinsens, quiet)))
3121                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3122         else
3123         {
3124                 prog->opensearches_origin[handle] = PRVM_AllocationOrigin(prog);
3125                 PRVM_G_FLOAT(OFS_RETURN) = handle;
3126         }
3127 }
3128
3129 /*
3130 =========
3131 VM_search_end
3132
3133 void    search_end(float handle)
3134 =========
3135 */
3136 void VM_search_end(prvm_prog_t *prog)
3137 {
3138         int handle;
3139         VM_SAFEPARMCOUNT(1, VM_search_end);
3140
3141         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
3142
3143         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
3144         {
3145                 VM_Warning(prog, "VM_search_end: invalid handle %i used in %s\n", handle, prog->name);
3146                 return;
3147         }
3148         if(prog->opensearches[handle] == NULL)
3149         {
3150                 VM_Warning(prog, "VM_search_end: no such handle %i in %s\n", handle, prog->name);
3151                 return;
3152         }
3153
3154         FS_FreeSearch(prog->opensearches[handle]);
3155         prog->opensearches[handle] = NULL;
3156         if(prog->opensearches_origin[handle])
3157                 PRVM_Free((char *)prog->opensearches_origin[handle]);
3158 }
3159
3160 /*
3161 =========
3162 VM_search_getsize
3163
3164 float   search_getsize(float handle)
3165 =========
3166 */
3167 void VM_search_getsize(prvm_prog_t *prog)
3168 {
3169         int handle;
3170         VM_SAFEPARMCOUNT(1, VM_search_getsize);
3171
3172         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
3173
3174         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
3175         {
3176                 VM_Warning(prog, "VM_search_getsize: invalid handle %i used in %s\n", handle, prog->name);
3177                 return;
3178         }
3179         if(prog->opensearches[handle] == NULL)
3180         {
3181                 VM_Warning(prog, "VM_search_getsize: no such handle %i in %s\n", handle, prog->name);
3182                 return;
3183         }
3184
3185         PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames;
3186 }
3187
3188 /*
3189 =========
3190 VM_search_getfilename
3191
3192 string  search_getfilename(float handle, float num)
3193 =========
3194 */
3195 void VM_search_getfilename(prvm_prog_t *prog)
3196 {
3197         int handle, filenum;
3198         VM_SAFEPARMCOUNT(2, VM_search_getfilename);
3199
3200         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
3201         filenum = (int)PRVM_G_FLOAT(OFS_PARM1);
3202
3203         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
3204         {
3205                 VM_Warning(prog, "VM_search_getfilename: invalid handle %i used in %s\n", handle, prog->name);
3206                 return;
3207         }
3208         if(prog->opensearches[handle] == NULL)
3209         {
3210                 VM_Warning(prog, "VM_search_getfilename: no such handle %i in %s\n", handle, prog->name);
3211                 return;
3212         }
3213         if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames)
3214         {
3215                 VM_Warning(prog, "VM_search_getfilename: invalid filenum %i in %s\n", filenum, prog->name);
3216                 return;
3217         }
3218
3219         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, prog->opensearches[handle]->filenames[filenum]);
3220 }
3221
3222 /*
3223 =========
3224 VM_chr
3225
3226 string  chr(float ascii)
3227 =========
3228 */
3229 void VM_chr(prvm_prog_t *prog)
3230 {
3231         /*
3232         char tmp[2];
3233         VM_SAFEPARMCOUNT(1, VM_chr);
3234
3235         tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
3236         tmp[1] = 0;
3237
3238         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp);
3239         */
3240         
3241         char tmp[8];
3242         int len;
3243         VM_SAFEPARMCOUNT(1, VM_chr);
3244
3245         len = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0), tmp, sizeof(tmp));
3246         tmp[len] = 0;
3247         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp);
3248 }
3249
3250 //=============================================================================
3251 // Draw builtins (client & menu)
3252
3253 /*
3254 =========
3255 VM_iscachedpic
3256
3257 float   iscachedpic(string pic)
3258 =========
3259 */
3260 void VM_iscachedpic(prvm_prog_t *prog)
3261 {
3262         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
3263
3264         // drawq hasnt such a function, thus always return true
3265         PRVM_G_FLOAT(OFS_RETURN) = false;
3266 }
3267
3268 /*
3269 =========
3270 VM_precache_pic
3271
3272 string  precache_pic(string pic)
3273 =========
3274 */
3275 #define PRECACHE_PIC_FROMWAD 1 /* FTEQW, not supported here */
3276 #define PRECACHE_PIC_NOTPERSISTENT 2
3277 //#define PRECACHE_PIC_NOCLAMP 4
3278 #define PRECACHE_PIC_MIPMAP 8
3279 void VM_precache_pic(prvm_prog_t *prog)
3280 {
3281         const char      *s;
3282         int flags = CACHEPICFLAG_FAILONMISSING;
3283
3284         VM_SAFEPARMCOUNTRANGE(1, 2, VM_precache_pic);
3285
3286         s = PRVM_G_STRING(OFS_PARM0);
3287         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
3288         VM_CheckEmptyString(prog, s);
3289
3290         if(prog->argc >= 2)
3291         {
3292                 int f = PRVM_G_FLOAT(OFS_PARM1);
3293                 if(f & PRECACHE_PIC_NOTPERSISTENT)
3294                         flags |= CACHEPICFLAG_NOTPERSISTENT;
3295                 //if(f & PRECACHE_PIC_NOCLAMP)
3296                 //      flags |= CACHEPICFLAG_NOCLAMP;
3297                 if(f & PRECACHE_PIC_MIPMAP)
3298                         flags |= CACHEPICFLAG_MIPMAP;
3299         }
3300
3301         if( !Draw_IsPicLoaded(Draw_CachePic_Flags(s, flags | CACHEPICFLAG_QUIET)) )
3302                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
3303 }
3304
3305 /*
3306 =========
3307 VM_freepic
3308
3309 freepic(string s)
3310 =========
3311 */
3312 void VM_freepic(prvm_prog_t *prog)
3313 {
3314         const char *s;
3315
3316         VM_SAFEPARMCOUNT(1,VM_freepic);
3317
3318         s = PRVM_G_STRING(OFS_PARM0);
3319         VM_CheckEmptyString(prog, s);
3320
3321         Draw_FreePic(s);
3322 }
3323
3324 static void getdrawfontscale(prvm_prog_t *prog, float *sx, float *sy)
3325 {
3326         vec3_t v;
3327         *sx = *sy = 1;
3328         VectorCopy(PRVM_drawglobalvector(drawfontscale), v);
3329         if(VectorLength2(v) > 0)
3330         {
3331                 *sx = v[0];
3332                 *sy = v[1];
3333         }
3334 }
3335
3336 static dp_font_t *getdrawfont(prvm_prog_t *prog)
3337 {
3338         int f = (int) PRVM_drawglobalfloat(drawfont);
3339         if(f < 0 || f >= dp_fonts.maxsize)
3340                 return FONT_DEFAULT;
3341         return &dp_fonts.f[f];
3342 }
3343
3344 /*
3345 =========
3346 VM_drawcharacter
3347
3348 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
3349 =========
3350 */
3351 void VM_drawcharacter(prvm_prog_t *prog)
3352 {
3353         prvm_vec_t *pos,*scale,*rgb;
3354         char   character;
3355         int flag;
3356         float sx, sy;
3357         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
3358
3359         // polygonbegin without draw2d arg has to guess
3360         prog->polygonbegin_guess2d = true;
3361
3362         character = (char) PRVM_G_FLOAT(OFS_PARM1);
3363         if(character == 0)
3364         {
3365                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3366                 VM_Warning(prog, "VM_drawcharacter: %s passed null character !\n",prog->name);
3367                 return;
3368         }
3369
3370         pos = PRVM_G_VECTOR(OFS_PARM0);
3371         scale = PRVM_G_VECTOR(OFS_PARM2);
3372         rgb = PRVM_G_VECTOR(OFS_PARM3);
3373         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3374
3375         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3376         {
3377                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3378                 VM_Warning(prog, "VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3379                 return;
3380         }
3381
3382         if(pos[2] || scale[2])
3383                 VM_Warning(prog, "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")));
3384
3385         if(!scale[0] || !scale[1])
3386         {
3387                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3388                 VM_Warning(prog, "VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3389                 return;
3390         }
3391
3392         getdrawfontscale(prog, &sx, &sy);
3393         DrawQ_String_Scale(pos[0], pos[1], &character, 1, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont(prog));
3394         PRVM_G_FLOAT(OFS_RETURN) = 1;
3395 }
3396
3397 /*
3398 =========
3399 VM_drawstring
3400
3401 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha[, float flag])
3402 =========
3403 */
3404 void VM_drawstring(prvm_prog_t *prog)
3405 {
3406         prvm_vec_t *pos,*scale,*rgb;
3407         const char  *string;
3408         int flag = 0;
3409         float sx, sy;
3410         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawstring);
3411
3412         // polygonbegin without draw2d arg has to guess
3413         prog->polygonbegin_guess2d = true;
3414
3415         string = PRVM_G_STRING(OFS_PARM1);
3416         pos = PRVM_G_VECTOR(OFS_PARM0);
3417         scale = PRVM_G_VECTOR(OFS_PARM2);
3418         rgb = PRVM_G_VECTOR(OFS_PARM3);
3419         if (prog->argc >= 6)
3420                 flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3421
3422         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3423         {
3424                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3425                 VM_Warning(prog, "VM_drawstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3426                 return;
3427         }
3428
3429         if(!scale[0] || !scale[1])
3430         {
3431                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3432                 VM_Warning(prog, "VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3433                 return;
3434         }
3435
3436         if(pos[2] || scale[2])
3437                 VM_Warning(prog, "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")));
3438
3439         getdrawfontscale(prog, &sx, &sy);
3440         DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont(prog));
3441         //Font_DrawString(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true);
3442         PRVM_G_FLOAT(OFS_RETURN) = 1;
3443 }
3444
3445 /*
3446 =========
3447 VM_drawcolorcodedstring
3448
3449 float   drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag)
3450 /
3451 float   drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
3452 =========
3453 */
3454 void VM_drawcolorcodedstring(prvm_prog_t *prog)
3455 {
3456         prvm_vec_t *pos, *scale;
3457         const char  *string;
3458         int flag;
3459         vec3_t rgb;
3460         float sx, sy, alpha;
3461
3462         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawcolorcodedstring);
3463
3464         // polygonbegin without draw2d arg has to guess
3465         prog->polygonbegin_guess2d = true;
3466
3467         if (prog->argc == 6) // full 6 parms, like normal drawstring
3468         {
3469                 pos = PRVM_G_VECTOR(OFS_PARM0);
3470                 string = PRVM_G_STRING(OFS_PARM1);
3471                 scale = PRVM_G_VECTOR(OFS_PARM2);
3472                 VectorCopy(PRVM_G_VECTOR(OFS_PARM3), rgb); 
3473                 alpha = PRVM_G_FLOAT(OFS_PARM4);
3474                 flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3475         }
3476         else
3477         {
3478                 pos = PRVM_G_VECTOR(OFS_PARM0);
3479                 string = PRVM_G_STRING(OFS_PARM1);
3480                 scale = PRVM_G_VECTOR(OFS_PARM2);
3481                 rgb[0] = 1.0;
3482                 rgb[1] = 1.0;
3483                 rgb[2] = 1.0;
3484                 alpha = PRVM_G_FLOAT(OFS_PARM3);
3485                 flag = (int)PRVM_G_FLOAT(OFS_PARM4);
3486         }
3487
3488         if(flag < DRAWFLAG_NORMAL || flag >= DRAWFLAG_NUMFLAGS)
3489         {
3490                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3491                 VM_Warning(prog, "VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3492                 return;
3493         }
3494
3495         if(!scale[0] || !scale[1])
3496         {
3497                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3498                 VM_Warning(prog, "VM_drawcolorcodedstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3499                 return;
3500         }
3501
3502         if(pos[2] || scale[2])
3503                 VM_Warning(prog, "VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
3504
3505         getdrawfontscale(prog, &sx, &sy);
3506         DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], alpha, flag, NULL, false, getdrawfont(prog));
3507         if (prog->argc == 6) // also return vector of last color
3508                 VectorCopy(DrawQ_Color, PRVM_G_VECTOR(OFS_RETURN));
3509         else
3510                 PRVM_G_FLOAT(OFS_RETURN) = 1;
3511 }
3512 /*
3513 =========
3514 VM_stringwidth
3515
3516 float   stringwidth(string text, float allowColorCodes, float size)
3517 =========
3518 */
3519 void VM_stringwidth(prvm_prog_t *prog)
3520 {
3521         const char  *string;
3522         vec2_t szv;
3523         float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
3524         int colors;
3525         float sx, sy;
3526         size_t maxlen = 0;
3527         VM_SAFEPARMCOUNTRANGE(2, 3, VM_stringwidth);
3528
3529         getdrawfontscale(prog, &sx, &sy);
3530         if(prog->argc == 3)
3531         {
3532                 Vector2Copy(PRVM_G_VECTOR(OFS_PARM2), szv);
3533                 mult = 1;
3534         }
3535         else
3536         {
3537                 // we want the width for 8x8 font size, divided by 8
3538                 Vector2Set(szv, 8, 8);
3539                 mult = 0.125;
3540                 // to make sure snapping is turned off, ALWAYS use a nontrivial scale in this case
3541                 if(sx >= 0.9 && sx <= 1.1)
3542                 {
3543                         mult *= 2;
3544                         sx /= 2;
3545                         sy /= 2;
3546                 }
3547         }
3548
3549         string = PRVM_G_STRING(OFS_PARM0);
3550         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
3551
3552         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_UntilWidth_TrackColors_Scale(string, &maxlen, szv[0], szv[1], sx, sy, NULL, !colors, getdrawfont(prog), 1000000000) * mult;
3553 /*
3554         if(prog->argc == 3)
3555         {
3556                 mult = sz = PRVM_G_FLOAT(OFS_PARM2);
3557         }
3558         else
3559         {
3560                 sz = 8;
3561                 mult = 1;
3562         }
3563
3564         string = PRVM_G_STRING(OFS_PARM0);
3565         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
3566
3567         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw
3568 */
3569 }
3570
3571 /*
3572 =========
3573 VM_findfont
3574
3575 float findfont(string s)
3576 =========
3577 */
3578
3579 static float getdrawfontnum(const char *fontname)
3580 {
3581         int i;
3582
3583         for(i = 0; i < dp_fonts.maxsize; ++i)
3584                 if(!strcmp(dp_fonts.f[i].title, fontname))
3585                         return i;
3586         return -1;
3587 }
3588
3589 void VM_findfont(prvm_prog_t *prog)
3590 {
3591         VM_SAFEPARMCOUNT(1,VM_findfont);
3592         PRVM_G_FLOAT(OFS_RETURN) = getdrawfontnum(PRVM_G_STRING(OFS_PARM0));
3593 }
3594
3595 /*
3596 =========
3597 VM_loadfont
3598
3599 float loadfont(string fontname, string fontmaps, string sizes, float slot)
3600 =========
3601 */
3602
3603 void VM_loadfont(prvm_prog_t *prog)
3604 {
3605         const char *fontname, *filelist, *sizes, *c, *cm;
3606         char mainfont[MAX_QPATH];
3607         int i, numsizes;
3608         float sz, scale, voffset;
3609         dp_font_t *f;
3610
3611         VM_SAFEPARMCOUNTRANGE(3,6,VM_loadfont);
3612
3613         fontname = PRVM_G_STRING(OFS_PARM0);
3614         if (!fontname[0])
3615                 fontname = "default";
3616
3617         filelist = PRVM_G_STRING(OFS_PARM1);
3618         if (!filelist[0])
3619                 filelist = "gfx/conchars";
3620
3621         sizes = PRVM_G_STRING(OFS_PARM2);
3622         if (!sizes[0])
3623                 sizes = "10";
3624
3625         // find a font
3626         f = NULL;
3627         if (prog->argc >= 4)
3628         {
3629                 i = PRVM_G_FLOAT(OFS_PARM3);
3630                 if (i >= 0 && i < dp_fonts.maxsize)
3631                 {
3632                         f = &dp_fonts.f[i];
3633                         strlcpy(f->title, fontname, sizeof(f->title)); // replace name
3634                 }
3635         }
3636         if (!f)
3637                 f = FindFont(fontname, true);
3638         if (!f)
3639         {
3640                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3641                 return; // something go wrong
3642         }
3643
3644         memset(f->fallbacks, 0, sizeof(f->fallbacks));
3645         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
3646
3647         // first font is handled "normally"
3648         c = strchr(filelist, ':');
3649         cm = strchr(filelist, ',');
3650         if(c && (!cm || c < cm))
3651                 f->req_face = atoi(c+1);
3652         else
3653         {
3654                 f->req_face = 0;
3655                 c = cm;
3656         }
3657         if(!c || (c - filelist) > MAX_QPATH)
3658                 strlcpy(mainfont, filelist, sizeof(mainfont));
3659         else
3660         {
3661                 memcpy(mainfont, filelist, c - filelist);
3662                 mainfont[c - filelist] = 0;
3663         }
3664
3665         // handle fallbacks
3666         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
3667         {
3668                 c = strchr(filelist, ',');
3669                 if(!c)
3670                         break;
3671                 filelist = c + 1;
3672                 if(!*filelist)
3673                         break;
3674                 c = strchr(filelist, ':');
3675                 cm = strchr(filelist, ',');
3676                 if(c && (!cm || c < cm))
3677                         f->fallback_faces[i] = atoi(c+1);
3678                 else
3679                 {
3680                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
3681                         c = cm;
3682                 }
3683                 if(!c || (c-filelist) > MAX_QPATH)
3684                 {
3685                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
3686                 }
3687                 else
3688                 {
3689                         memcpy(f->fallbacks[i], filelist, c - filelist);
3690                         f->fallbacks[i][c - filelist] = 0;
3691                 }
3692         }
3693
3694         // handle sizes
3695         for(i = 0; i < MAX_FONT_SIZES; ++i)
3696                 f->req_sizes[i] = -1;
3697         for (numsizes = 0,c = sizes;;)
3698         {
3699                 if (!COM_ParseToken_VM_Tokenize(&c, 0))
3700                         break;
3701                 sz = atof(com_token);
3702                 // detect crap size
3703                 if (sz < 0.001f || sz > 1000.0f)
3704                 {
3705                         VM_Warning(prog, "VM_loadfont: crap size %s", com_token);
3706                         continue;
3707                 }
3708                 // check overflow
3709                 if (numsizes == MAX_FONT_SIZES)
3710                 {
3711                         VM_Warning(prog, "VM_loadfont: MAX_FONT_SIZES = %i exceeded", MAX_FONT_SIZES);
3712                         break;
3713                 }
3714                 f->req_sizes[numsizes] = sz;
3715                 numsizes++;
3716         }
3717
3718         // additional scale/hoffset parms
3719         scale = 1;
3720         voffset = 0;
3721         if (prog->argc >= 5)
3722         {
3723                 scale = PRVM_G_FLOAT(OFS_PARM4);
3724                 if (scale <= 0)
3725                         scale = 1;
3726         }
3727         if (prog->argc >= 6)
3728                 voffset = PRVM_G_FLOAT(OFS_PARM5);
3729
3730         // load
3731         LoadFont(true, mainfont, f, scale, voffset);
3732
3733         // return index of loaded font
3734         PRVM_G_FLOAT(OFS_RETURN) = (f - dp_fonts.f);
3735 }
3736
3737 /*
3738 =========
3739 VM_drawpic
3740
3741 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
3742 =========
3743 */
3744 void VM_drawpic(prvm_prog_t *prog)
3745 {
3746         const char *picname;
3747         prvm_vec_t *size, *pos, *rgb;
3748         int flag = 0;
3749
3750         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawpic);
3751
3752         // polygonbegin without draw2d arg has to guess
3753         prog->polygonbegin_guess2d = true;
3754
3755         picname = PRVM_G_STRING(OFS_PARM1);
3756         VM_CheckEmptyString(prog, picname);
3757
3758         // is pic cached ? no function yet for that
3759         if(!1)
3760         {
3761                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3762                 VM_Warning(prog, "VM_drawpic: %s: %s not cached !\n", prog->name, picname);
3763                 return;
3764         }
3765
3766         pos = PRVM_G_VECTOR(OFS_PARM0);
3767         size = PRVM_G_VECTOR(OFS_PARM2);
3768         rgb = PRVM_G_VECTOR(OFS_PARM3);
3769         if (prog->argc >= 6)
3770                 flag = (int) PRVM_G_FLOAT(OFS_PARM5);
3771
3772         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3773         {
3774                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3775                 VM_Warning(prog, "VM_drawpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3776                 return;
3777         }
3778
3779         if(pos[2] || size[2])
3780                 VM_Warning(prog, "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")));
3781
3782         DrawQ_Pic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag);
3783         PRVM_G_FLOAT(OFS_RETURN) = 1;
3784 }
3785 /*
3786 =========
3787 VM_drawrotpic
3788
3789 float   drawrotpic(vector position, string pic, vector size, vector org, float angle, vector rgb, float alpha, float flag)
3790 =========
3791 */
3792 void VM_drawrotpic(prvm_prog_t *prog)
3793 {
3794         const char *picname;
3795         prvm_vec_t *size, *pos, *org, *rgb;
3796         int flag;
3797
3798         VM_SAFEPARMCOUNT(8,VM_drawrotpic);
3799
3800         // polygonbegin without draw2d arg has to guess
3801         prog->polygonbegin_guess2d = true;
3802
3803         picname = PRVM_G_STRING(OFS_PARM1);
3804         VM_CheckEmptyString(prog, picname);
3805
3806         // is pic cached ? no function yet for that
3807         if(!1)
3808         {
3809                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3810                 VM_Warning(prog, "VM_drawrotpic: %s: %s not cached !\n", prog->name, picname);
3811                 return;
3812         }
3813
3814         pos = PRVM_G_VECTOR(OFS_PARM0);
3815         size = PRVM_G_VECTOR(OFS_PARM2);
3816         org = PRVM_G_VECTOR(OFS_PARM3);
3817         rgb = PRVM_G_VECTOR(OFS_PARM5);
3818         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
3819
3820         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3821         {
3822                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3823                 VM_Warning(prog, "VM_drawrotpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3824                 return;
3825         }
3826
3827         if(pos[2] || size[2] || org[2])
3828                 VM_Warning(prog, "VM_drawrotpic: z value from pos/size/org discarded\n");
3829
3830         DrawQ_RotPic(pos[0], pos[1], Draw_CachePic_Flags(picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], org[0], org[1], PRVM_G_FLOAT(OFS_PARM4), rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM6), flag);
3831         PRVM_G_FLOAT(OFS_RETURN) = 1;
3832 }
3833 /*
3834 =========
3835 VM_drawsubpic
3836
3837 float   drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
3838
3839 =========
3840 */
3841 void VM_drawsubpic(prvm_prog_t *prog)
3842 {
3843         const char *picname;
3844         prvm_vec_t *size, *pos, *rgb, *srcPos, *srcSize, alpha;
3845         int flag;
3846
3847         VM_SAFEPARMCOUNT(8,VM_drawsubpic);
3848
3849         // polygonbegin without draw2d arg has to guess
3850         prog->polygonbegin_guess2d = true;
3851
3852         picname = PRVM_G_STRING(OFS_PARM2);
3853         VM_CheckEmptyString(prog, picname);
3854
3855         // is pic cached ? no function yet for that
3856         if(!1)
3857         {
3858                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3859                 VM_Warning(prog, "VM_drawsubpic: %s: %s not cached !\n", prog->name, picname);
3860                 return;
3861         }
3862
3863         pos = PRVM_G_VECTOR(OFS_PARM0);
3864         size = PRVM_G_VECTOR(OFS_PARM1);
3865         srcPos = PRVM_G_VECTOR(OFS_PARM3);
3866         srcSize = PRVM_G_VECTOR(OFS_PARM4);
3867         rgb = PRVM_G_VECTOR(OFS_PARM5);
3868         alpha = PRVM_G_FLOAT(OFS_PARM6);
3869         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
3870
3871         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3872         {
3873                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3874                 VM_Warning(prog, "VM_drawsubpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3875                 return;
3876         }
3877
3878         if(pos[2] || size[2])
3879                 VM_Warning(prog, "VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size")));
3880
3881         DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT),
3882                 size[0], size[1],
3883                 srcPos[0],              srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3884                 srcPos[0] + srcSize[0], srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3885                 srcPos[0],              srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3886                 srcPos[0] + srcSize[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3887                 flag);
3888         PRVM_G_FLOAT(OFS_RETURN) = 1;
3889 }
3890
3891 /*
3892 =========
3893 VM_drawfill
3894
3895 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
3896 =========
3897 */
3898 void VM_drawfill(prvm_prog_t *prog)
3899 {
3900         prvm_vec_t *size, *pos, *rgb;
3901         int flag;
3902
3903         VM_SAFEPARMCOUNT(5,VM_drawfill);
3904
3905         // polygonbegin without draw2d arg has to guess
3906         prog->polygonbegin_guess2d = true;
3907
3908         pos = PRVM_G_VECTOR(OFS_PARM0);
3909         size = PRVM_G_VECTOR(OFS_PARM1);
3910         rgb = PRVM_G_VECTOR(OFS_PARM2);
3911         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
3912
3913         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3914         {
3915                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3916                 VM_Warning(prog, "VM_drawfill: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3917                 return;
3918         }
3919
3920         if(pos[2] || size[2])
3921                 VM_Warning(prog, "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")));
3922
3923         DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
3924         PRVM_G_FLOAT(OFS_RETURN) = 1;
3925 }
3926
3927 /*
3928 =========
3929 VM_drawsetcliparea
3930
3931 drawsetcliparea(float x, float y, float width, float height)
3932 =========
3933 */
3934 void VM_drawsetcliparea(prvm_prog_t *prog)
3935 {
3936         float x,y,w,h;
3937         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
3938
3939         // polygonbegin without draw2d arg has to guess
3940         prog->polygonbegin_guess2d = true;
3941
3942         x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
3943         y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
3944         w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
3945         h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
3946
3947         DrawQ_SetClipArea(x, y, w, h);
3948 }
3949
3950 /*
3951 =========
3952 VM_drawresetcliparea
3953
3954 drawresetcliparea()
3955 =========
3956 */
3957 void VM_drawresetcliparea(prvm_prog_t *prog)
3958 {
3959         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
3960
3961         // polygonbegin without draw2d arg has to guess
3962         prog->polygonbegin_guess2d = true;
3963
3964         DrawQ_ResetClipArea();
3965 }
3966
3967 /*
3968 =========
3969 VM_getimagesize
3970
3971 vector  getimagesize(string pic)
3972 =========
3973 */
3974 void VM_getimagesize(prvm_prog_t *prog)
3975 {
3976         const char *p;
3977         cachepic_t *pic;
3978
3979         VM_SAFEPARMCOUNT(1,VM_getimagesize);
3980
3981         p = PRVM_G_STRING(OFS_PARM0);
3982         VM_CheckEmptyString(prog, p);
3983
3984         pic = Draw_CachePic_Flags (p, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOTPERSISTENT);
3985         if (!Draw_IsPicLoaded(pic))
3986         {
3987                 PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
3988                 PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
3989         }
3990         else
3991         {
3992                 PRVM_G_VECTOR(OFS_RETURN)[0] = Draw_GetPicWidth(pic);
3993                 PRVM_G_VECTOR(OFS_RETURN)[1] = Draw_GetPicHeight(pic);
3994         }
3995         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3996 }
3997
3998 /*
3999 =========
4000 VM_keynumtostring
4001
4002 string keynumtostring(float keynum)
4003 =========
4004 */
4005 void VM_keynumtostring (prvm_prog_t *prog)
4006 {
4007         char tinystr[2];
4008         VM_SAFEPARMCOUNT(1, VM_keynumtostring);
4009
4010         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0), tinystr, sizeof(tinystr)));
4011 }
4012
4013 /*
4014 =========
4015 VM_findkeysforcommand
4016
4017 string  findkeysforcommand(string command, float bindmap)
4018
4019 the returned string is an altstring
4020 =========
4021 */
4022 #define FKFC_NUMKEYS 5
4023 void M_FindKeysForCommand(const char *command, int *keys);
4024 void VM_findkeysforcommand(prvm_prog_t *prog)
4025 {
4026         const char *cmd;
4027         char ret[VM_STRINGTEMP_LENGTH];
4028         int keys[FKFC_NUMKEYS];
4029         int i;
4030         int bindmap;
4031         char vabuf[1024];
4032
4033         VM_SAFEPARMCOUNTRANGE(1, 2, VM_findkeysforcommand);
4034
4035         cmd = PRVM_G_STRING(OFS_PARM0);
4036         if(prog->argc == 2)
4037                 bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
4038         else
4039                 bindmap = 0; // consistent to "bind"
4040
4041         VM_CheckEmptyString(prog, cmd);
4042
4043         Key_FindKeysForCommand(cmd, keys, FKFC_NUMKEYS, bindmap);
4044
4045         ret[0] = 0;
4046         for(i = 0; i < FKFC_NUMKEYS; i++)
4047                 strlcat(ret, va(vabuf, sizeof(vabuf), " \'%i\'", keys[i]), sizeof(ret));
4048
4049         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, ret);
4050 }
4051
4052 /*
4053 =========
4054 VM_stringtokeynum
4055
4056 float stringtokeynum(string key)
4057 =========
4058 */
4059 void VM_stringtokeynum (prvm_prog_t *prog)
4060 {
4061         VM_SAFEPARMCOUNT( 1, VM_stringtokeynum );
4062
4063         PRVM_G_FLOAT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
4064 }
4065
4066 /*
4067 =========
4068 VM_getkeybind
4069
4070 string getkeybind(float key, float bindmap)
4071 =========
4072 */
4073 void VM_getkeybind (prvm_prog_t *prog)
4074 {
4075         int bindmap;
4076         VM_SAFEPARMCOUNTRANGE(1, 2, VM_getkeybind);
4077         if(prog->argc == 2)
4078                 bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
4079         else
4080                 bindmap = 0; // consistent to "bind"
4081
4082         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap));
4083 }
4084
4085 /*
4086 =========
4087 VM_setkeybind
4088
4089 float setkeybind(float key, string cmd, float bindmap)
4090 =========
4091 */
4092 void VM_setkeybind (prvm_prog_t *prog)
4093 {
4094         int bindmap;
4095         VM_SAFEPARMCOUNTRANGE(2, 3, VM_setkeybind);
4096         if(prog->argc == 3)
4097                 bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM2), MAX_BINDMAPS-1);
4098         else
4099                 bindmap = 0; // consistent to "bind"
4100
4101         PRVM_G_FLOAT(OFS_RETURN) = 0;
4102         if(Key_SetBinding((int)PRVM_G_FLOAT(OFS_PARM0), bindmap, PRVM_G_STRING(OFS_PARM1)))
4103                 PRVM_G_FLOAT(OFS_RETURN) = 1;
4104 }
4105
4106 /*
4107 =========
4108 VM_getbindmap
4109
4110 vector getbindmaps()
4111 =========
4112 */
4113 void VM_getbindmaps (prvm_prog_t *prog)
4114 {
4115         int fg, bg;
4116         VM_SAFEPARMCOUNT(0, VM_getbindmaps);
4117         Key_GetBindMap(&fg, &bg);
4118         PRVM_G_VECTOR(OFS_RETURN)[0] = fg;
4119         PRVM_G_VECTOR(OFS_RETURN)[1] = bg;
4120         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
4121 }
4122
4123 /*
4124 =========
4125 VM_setbindmap
4126
4127 float setbindmaps(vector bindmap)
4128 =========
4129 */
4130 void VM_setbindmaps (prvm_prog_t *prog)
4131 {
4132         VM_SAFEPARMCOUNT(1, VM_setbindmaps);
4133         PRVM_G_FLOAT(OFS_RETURN) = 0;
4134         if(PRVM_G_VECTOR(OFS_PARM0)[2] == 0)
4135                 if(Key_SetBindMap((int)PRVM_G_VECTOR(OFS_PARM0)[0], (int)PRVM_G_VECTOR(OFS_PARM0)[1]))
4136                         PRVM_G_FLOAT(OFS_RETURN) = 1;
4137 }
4138
4139 // CL_Video interface functions
4140
4141 /*
4142 ========================
4143 VM_cin_open
4144
4145 float cin_open(string file, string name)
4146 ========================
4147 */
4148 void VM_cin_open(prvm_prog_t *prog)
4149 {
4150         const char *file;
4151         const char *name;
4152
4153         VM_SAFEPARMCOUNT( 2, VM_cin_open );
4154
4155         file = PRVM_G_STRING( OFS_PARM0 );
4156         name = PRVM_G_STRING( OFS_PARM1 );
4157
4158         VM_CheckEmptyString(prog,  file );
4159     VM_CheckEmptyString(prog,  name );
4160
4161         if( CL_OpenVideo( file, name, MENUOWNER, "" ) )
4162                 PRVM_G_FLOAT( OFS_RETURN ) = 1;
4163         else
4164                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
4165 }
4166
4167 /*
4168 ========================
4169 VM_cin_close
4170
4171 void cin_close(string name)
4172 ========================
4173 */
4174 void VM_cin_close(prvm_prog_t *prog)
4175 {
4176         const char *name;
4177
4178         VM_SAFEPARMCOUNT( 1, VM_cin_close );
4179
4180         name = PRVM_G_STRING( OFS_PARM0 );
4181         VM_CheckEmptyString(prog,  name );
4182
4183         CL_CloseVideo( CL_GetVideoByName( name ) );
4184 }
4185
4186 /*
4187 ========================
4188 VM_cin_setstate
4189 void cin_setstate(string name, float type)
4190 ========================
4191 */
4192 void VM_cin_setstate(prvm_prog_t *prog)
4193 {
4194         const char *name;
4195         clvideostate_t  state;
4196         clvideo_t               *video;
4197
4198         VM_SAFEPARMCOUNT( 2, VM_cin_setstate );
4199
4200         name = PRVM_G_STRING( OFS_PARM0 );
4201         VM_CheckEmptyString(prog,  name );
4202
4203         state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
4204
4205         video = CL_GetVideoByName( name );
4206         if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
4207                 CL_SetVideoState( video, state );
4208 }
4209
4210 /*
4211 ========================
4212 VM_cin_getstate
4213
4214 float cin_getstate(string name)
4215 ========================
4216 */
4217 void VM_cin_getstate(prvm_prog_t *prog)
4218 {
4219         const char *name;
4220         clvideo_t               *video;
4221
4222         VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
4223
4224         name = PRVM_G_STRING( OFS_PARM0 );
4225         VM_CheckEmptyString(prog,  name );
4226
4227         video = CL_GetVideoByName( name );
4228         if( video )
4229                 PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
4230         else
4231                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
4232 }
4233
4234 /*
4235 ========================
4236 VM_cin_restart
4237
4238 void cin_restart(string name)
4239 ========================
4240 */
4241 void VM_cin_restart(prvm_prog_t *prog)
4242 {
4243         const char *name;
4244         clvideo_t               *video;
4245
4246         VM_SAFEPARMCOUNT( 1, VM_cin_restart );
4247
4248         name = PRVM_G_STRING( OFS_PARM0 );
4249         VM_CheckEmptyString(prog,  name );
4250
4251         video = CL_GetVideoByName( name );
4252         if( video )
4253                 CL_RestartVideo( video );
4254 }
4255
4256 /*
4257 ========================
4258 VM_gecko_create
4259
4260 float[bool] gecko_create( string name )
4261 ========================
4262 */
4263 void VM_gecko_create(prvm_prog_t *prog) {
4264         // REMOVED
4265         PRVM_G_FLOAT( OFS_RETURN ) = 0;
4266 }
4267
4268 /*
4269 ========================
4270 VM_gecko_destroy
4271
4272 void gecko_destroy( string name )
4273 ========================
4274 */
4275 void VM_gecko_destroy(prvm_prog_t *prog) {
4276         // REMOVED
4277 }
4278
4279 /*
4280 ========================
4281 VM_gecko_navigate
4282
4283 void gecko_navigate( string name, string URI )
4284 ========================
4285 */
4286 void VM_gecko_navigate(prvm_prog_t *prog) {
4287         // REMOVED
4288 }
4289
4290 /*
4291 ========================
4292 VM_gecko_keyevent
4293
4294 float[bool] gecko_keyevent( string name, float key, float eventtype ) 
4295 ========================
4296 */
4297 void VM_gecko_keyevent(prvm_prog_t *prog) {
4298         // REMOVED
4299         PRVM_G_FLOAT( OFS_RETURN ) = 0;
4300 }
4301
4302 /*
4303 ========================
4304 VM_gecko_movemouse
4305
4306 void gecko_mousemove( string name, float x, float y )
4307 ========================
4308 */
4309 void VM_gecko_movemouse(prvm_prog_t *prog) {
4310         // REMOVED
4311 }
4312
4313
4314 /*
4315 ========================
4316 VM_gecko_resize
4317
4318 void gecko_resize( string name, float w, float h )
4319 ========================
4320 */
4321 void VM_gecko_resize(prvm_prog_t *prog) {
4322         // REMOVED
4323 }
4324
4325
4326 /*
4327 ========================
4328 VM_gecko_get_texture_extent
4329
4330 vector gecko_get_texture_extent( string name )
4331 ========================
4332 */
4333 void VM_gecko_get_texture_extent(prvm_prog_t *prog) {
4334         // REMOVED
4335         PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
4336         PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
4337 }
4338
4339
4340
4341 /*
4342 ==============
4343 VM_makevectors
4344
4345 Writes new values for v_forward, v_up, and v_right based on angles
4346 void makevectors(vector angle)
4347 ==============
4348 */
4349 void VM_makevectors (prvm_prog_t *prog)
4350 {
4351         vec3_t angles, forward, right, up;
4352         VM_SAFEPARMCOUNT(1, VM_makevectors);
4353         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), angles);
4354         AngleVectors(angles, forward, right, up);
4355         VectorCopy(forward, PRVM_gameglobalvector(v_forward));
4356         VectorCopy(right, PRVM_gameglobalvector(v_right));
4357         VectorCopy(up, PRVM_gameglobalvector(v_up));
4358 }
4359
4360 /*
4361 ==============
4362 VM_vectorvectors
4363
4364 Writes new values for v_forward, v_up, and v_right based on the given forward vector
4365 vectorvectors(vector)
4366 ==============
4367 */
4368 void VM_vectorvectors (prvm_prog_t *prog)
4369 {
4370         vec3_t forward, right, up;
4371         VM_SAFEPARMCOUNT(1, VM_vectorvectors);
4372         VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), forward);
4373         VectorVectors(forward, right, up);
4374         VectorCopy(forward, PRVM_gameglobalvector(v_forward));
4375         VectorCopy(right, PRVM_gameglobalvector(v_right));
4376         VectorCopy(up, PRVM_gameglobalvector(v_up));
4377 }
4378
4379 /*
4380 ========================
4381 VM_drawline
4382
4383 void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
4384 ========================
4385 */
4386 void VM_drawline (prvm_prog_t *prog)
4387 {
4388         prvm_vec_t      *c1, *c2, *rgb;
4389         float   alpha, width;
4390         unsigned char   flags;
4391
4392         VM_SAFEPARMCOUNT(6, VM_drawline);
4393
4394         // polygonbegin without draw2d arg has to guess
4395         prog->polygonbegin_guess2d = true;
4396
4397         width   = PRVM_G_FLOAT(OFS_PARM0);
4398         c1              = PRVM_G_VECTOR(OFS_PARM1);
4399         c2              = PRVM_G_VECTOR(OFS_PARM2);
4400         rgb             = PRVM_G_VECTOR(OFS_PARM3);
4401         alpha   = PRVM_G_FLOAT(OFS_PARM4);
4402         flags   = (int)PRVM_G_FLOAT(OFS_PARM5);
4403         DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
4404 }
4405
4406 // float(float number, float quantity) bitshift (EXT_BITSHIFT)
4407 void VM_bitshift (prvm_prog_t *prog)
4408 {
4409         prvm_int_t n1, n2;
4410         VM_SAFEPARMCOUNT(2, VM_bitshift);
4411
4412         n1 = (prvm_int_t)fabs((prvm_vec_t)((prvm_int_t)PRVM_G_FLOAT(OFS_PARM0)));
4413         n2 = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM1);
4414         if(!n1)
4415                 PRVM_G_FLOAT(OFS_RETURN) = n1;
4416         else
4417         if(n2 < 0)
4418                 PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
4419         else
4420                 PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
4421 }
4422
4423 ////////////////////////////////////////
4424 // AltString functions
4425 ////////////////////////////////////////
4426
4427 /*
4428 ========================
4429 VM_altstr_count
4430
4431 float altstr_count(string)
4432 ========================
4433 */
4434 void VM_altstr_count(prvm_prog_t *prog)
4435 {
4436         const char *altstr, *pos;
4437         int     count;
4438
4439         VM_SAFEPARMCOUNT( 1, VM_altstr_count );
4440
4441         altstr = PRVM_G_STRING( OFS_PARM0 );
4442         //VM_CheckEmptyString(prog,  altstr );
4443
4444         for( count = 0, pos = altstr ; *pos ; pos++ ) {
4445                 if( *pos == '\\' ) {
4446                         if( !*++pos ) {
4447                                 break;
4448                         }
4449                 } else if( *pos == '\'' ) {
4450                         count++;
4451                 }
4452         }
4453
4454         PRVM_G_FLOAT( OFS_RETURN ) = (prvm_vec_t) (count / 2);
4455 }
4456
4457 /*
4458 ========================
4459 VM_altstr_prepare
4460
4461 string altstr_prepare(string)
4462 ========================
4463 */
4464 void VM_altstr_prepare(prvm_prog_t *prog)
4465 {
4466         const char *instr, *in;
4467         char outstr[VM_STRINGTEMP_LENGTH];
4468         size_t outpos;
4469
4470         VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
4471
4472         instr = PRVM_G_STRING( OFS_PARM0 );
4473
4474         for (in = instr, outpos = 0; *in && outpos < sizeof(outstr) - 1; ++in)
4475         {
4476                 if (*in == '\'' && outpos < sizeof(outstr) - 2)
4477                 {
4478                         outstr[outpos++] = '\\';
4479                         outstr[outpos++] = '\'';
4480                 }
4481                 else
4482                         outstr[outpos++] = *in;
4483         }
4484         outstr[outpos] = 0;
4485
4486         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4487 }
4488
4489 /*
4490 ========================
4491 VM_altstr_get
4492
4493 string altstr_get(string, float)
4494 ========================
4495 */
4496 void VM_altstr_get(prvm_prog_t *prog)
4497 {
4498         const char *altstr, *pos;
4499         char *out;
4500         int count, size;
4501         char outstr[VM_STRINGTEMP_LENGTH];
4502
4503         VM_SAFEPARMCOUNT( 2, VM_altstr_get );
4504
4505         altstr = PRVM_G_STRING( OFS_PARM0 );
4506
4507         count = (int)PRVM_G_FLOAT( OFS_PARM1 );
4508         count = count * 2 + 1;
4509
4510         for( pos = altstr ; *pos && count ; pos++ )
4511                 if( *pos == '\\' ) {
4512                         if( !*++pos )
4513                                 break;
4514                 } else if( *pos == '\'' )
4515                         count--;
4516
4517         if( !*pos ) {
4518                 PRVM_G_INT( OFS_RETURN ) = 0;
4519                 return;
4520         }
4521
4522         for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
4523                 if( *pos == '\\' ) {
4524                         if( !*++pos )
4525                                 break;
4526                         *out = *pos;
4527                         size--;
4528                 } else if( *pos == '\'' )
4529                         break;
4530                 else
4531                         *out = *pos;
4532
4533         *out = 0;
4534         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4535 }
4536
4537 /*
4538 ========================
4539 VM_altstr_set
4540
4541 string altstr_set(string altstr, float num, string set)
4542 ========================
4543 */
4544 void VM_altstr_set(prvm_prog_t *prog)
4545 {
4546     int num;
4547         const char *altstr, *str;
4548         const char *in;
4549         char *out;
4550         char outstr[VM_STRINGTEMP_LENGTH];
4551
4552         VM_SAFEPARMCOUNT( 3, VM_altstr_set );
4553
4554         altstr = PRVM_G_STRING( OFS_PARM0 );
4555
4556         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
4557
4558         str = PRVM_G_STRING( OFS_PARM2 );
4559
4560         out = outstr;
4561         for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
4562                 if( *in == '\\' ) {
4563                         if( !*++in ) {
4564                                 break;
4565                         }
4566                 } else if( *in == '\'' ) {
4567                         num--;
4568                 }
4569
4570         // copy set in
4571         for( ; *str; *out++ = *str++ );
4572         // now jump over the old content
4573         for( ; *in ; in++ )
4574                 if( *in == '\'' || (*in == '\\' && !*++in) )
4575                         break;
4576
4577         strlcpy(out, in, outstr + sizeof(outstr) - out);
4578         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4579 }
4580
4581 /*
4582 ========================
4583 VM_altstr_ins
4584 insert after num
4585 string  altstr_ins(string altstr, float num, string set)
4586 ========================
4587 */
4588 void VM_altstr_ins(prvm_prog_t *prog)
4589 {
4590         int num;
4591         const char *set;
4592         const char *in;
4593         char *out;
4594         char outstr[VM_STRINGTEMP_LENGTH];
4595
4596         VM_SAFEPARMCOUNT(3, VM_altstr_ins);
4597
4598         in = PRVM_G_STRING( OFS_PARM0 );
4599         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
4600         set = PRVM_G_STRING( OFS_PARM2 );
4601
4602         out = outstr;
4603         for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
4604                 if( *in == '\\' ) {
4605                         if( !*++in ) {
4606                                 break;
4607                         }
4608                 } else if( *in == '\'' ) {
4609                         num--;
4610                 }
4611
4612         *out++ = '\'';
4613         for( ; *set ; *out++ = *set++ );
4614         *out++ = '\'';
4615
4616         strlcpy(out, in, outstr + sizeof(outstr) - out);
4617         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4618 }
4619
4620
4621 ////////////////////////////////////////
4622 // BufString functions
4623 ////////////////////////////////////////
4624 //[515]: string buffers support
4625
4626 static size_t stringbuffers_sortlength;
4627
4628 static void BufStr_Expand(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex)
4629 {
4630         if (stringbuffer->max_strings <= strindex)
4631         {
4632                 char **oldstrings = stringbuffer->strings;
4633                 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
4634                 while (stringbuffer->max_strings <= strindex)
4635                         stringbuffer->max_strings *= 2;
4636                 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
4637                 if (stringbuffer->num_strings > 0)
4638                         memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
4639                 if (oldstrings)
4640                         Mem_Free(oldstrings);
4641         }
4642 }
4643
4644 static void BufStr_Shrink(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer)
4645 {
4646         // reduce num_strings if there are empty string slots at the end
4647         while (stringbuffer->num_strings > 0 && stringbuffer->strings[stringbuffer->num_strings - 1] == NULL)
4648                 stringbuffer->num_strings--;
4649
4650         // if empty, free the string pointer array
4651         if (stringbuffer->num_strings == 0)
4652         {
4653                 stringbuffer->max_strings = 0;
4654                 if (stringbuffer->strings)
4655                         Mem_Free(stringbuffer->strings);
4656                 stringbuffer->strings = NULL;
4657         }
4658 }
4659
4660 static int BufStr_SortStringsUP (const void *in1, const void *in2)
4661 {
4662         const char *a, *b;
4663         a = *((const char **) in1);
4664         b = *((const char **) in2);
4665         if(!a || !a[0]) return 1;
4666         if(!b || !b[0]) return -1;
4667         return strncmp(a, b, stringbuffers_sortlength);
4668 }
4669
4670 static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
4671 {
4672         const char *a, *b;
4673         a = *((const char **) in1);
4674         b = *((const char **) in2);
4675         if(!a || !a[0]) return 1;
4676         if(!b || !b[0]) return -1;
4677         return strncmp(b, a, stringbuffers_sortlength);
4678 }
4679
4680 prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, int flags, const char *format)
4681 {
4682         prvm_stringbuffer_t *stringbuffer;
4683         int i;
4684
4685         if (bufindex < 0)
4686                 return NULL;
4687
4688         // find buffer with wanted index
4689         if (bufindex < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray))
4690         {
4691                 if ( (stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, bufindex)) )
4692                 {
4693                         if (stringbuffer->flags & STRINGBUFFER_TEMP)
4694                                 stringbuffer->flags = flags; // created but has not been used yet
4695                         return stringbuffer;
4696                 }
4697                 return NULL;
4698         }
4699
4700         // allocate new buffer with wanted index
4701         while(1)
4702         {
4703                 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
4704                 stringbuffer->flags = STRINGBUFFER_TEMP;
4705                 for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
4706                 if (i == bufindex)
4707                 {
4708                         stringbuffer->flags = flags; // mark as used
4709                         break;
4710                 }
4711         }
4712         return stringbuffer;
4713 }
4714
4715 void BufStr_Set(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex, const char *str)
4716 {
4717         size_t  alloclen;
4718
4719         if (!stringbuffer || strindex < 0)
4720                 return;
4721
4722         BufStr_Expand(prog, stringbuffer, strindex);
4723         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4724         if (stringbuffer->strings[strindex])
4725                 Mem_Free(stringbuffer->strings[strindex]);
4726         stringbuffer->strings[strindex] = NULL;
4727
4728         if (str)
4729         {
4730                 // not the NULL string!
4731                 alloclen = strlen(str) + 1;
4732                 stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4733                 memcpy(stringbuffer->strings[strindex], str, alloclen);
4734         }
4735
4736         BufStr_Shrink(prog, stringbuffer);
4737 }
4738
4739 void BufStr_Del(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer)
4740 {
4741         int i;
4742         
4743         if (!stringbuffer)
4744                 return;
4745
4746         for (i = 0;i < stringbuffer->num_strings;i++)
4747                 if (stringbuffer->strings[i])
4748                         Mem_Free(stringbuffer->strings[i]);
4749         if (stringbuffer->strings)
4750                 Mem_Free(stringbuffer->strings);
4751         if(stringbuffer->origin)
4752                 PRVM_Free((char *)stringbuffer->origin);
4753         Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
4754 }
4755
4756 void BufStr_Flush(prvm_prog_t *prog)
4757 {
4758         prvm_stringbuffer_t *stringbuffer;
4759         int i, numbuffers;
4760
4761         numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
4762         for (i = 0; i < numbuffers; i++)
4763                 if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
4764                         BufStr_Del(prog, stringbuffer);
4765         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
4766 }
4767
4768 /*
4769 ========================
4770 VM_buf_create
4771 creates new buffer, and returns it's index, returns -1 if failed
4772 float buf_create(prvm_prog_t *prog) = #460;
4773 float newbuf(string format, float flags) = #460;
4774 ========================
4775 */
4776
4777 void VM_buf_create (prvm_prog_t *prog)
4778 {
4779         prvm_stringbuffer_t *stringbuffer;
4780         int i;
4781
4782         VM_SAFEPARMCOUNTRANGE(0, 2, VM_buf_create);
4783
4784         // VorteX: optional parm1 (buffer format) is unfinished, to keep intact with future databuffers extension must be set to "string"
4785         if(prog->argc >= 1 && strcmp(PRVM_G_STRING(OFS_PARM0), "string"))
4786         {
4787                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4788                 return;
4789         }
4790         stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
4791         for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
4792         stringbuffer->origin = PRVM_AllocationOrigin(prog);
4793         // optional flags parm
4794         if (prog->argc >= 2)
4795                 stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & STRINGBUFFER_QCFLAGS;
4796         PRVM_G_FLOAT(OFS_RETURN) = i;
4797 }
4798
4799
4800
4801 /*
4802 ========================
4803 VM_buf_del
4804 deletes buffer and all strings in it
4805 void buf_del(float bufhandle) = #461;
4806 ========================
4807 */
4808 void VM_buf_del (prvm_prog_t *prog)
4809 {
4810         prvm_stringbuffer_t *stringbuffer;
4811         VM_SAFEPARMCOUNT(1, VM_buf_del);
4812         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4813         if (stringbuffer)
4814                 BufStr_Del(prog, stringbuffer);
4815         else
4816         {
4817                 VM_Warning(prog, "VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4818                 return;
4819         }
4820 }
4821
4822 /*
4823 ========================
4824 VM_buf_getsize
4825 how many strings are stored in buffer
4826 float buf_getsize(float bufhandle) = #462;
4827 ========================
4828 */
4829 void VM_buf_getsize (prvm_prog_t *prog)
4830 {
4831         prvm_stringbuffer_t *stringbuffer;
4832         VM_SAFEPARMCOUNT(1, VM_buf_getsize);
4833
4834         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4835         if(!stringbuffer)
4836         {
4837                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4838                 VM_Warning(prog, "VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4839                 return;
4840         }
4841         else
4842                 PRVM_G_FLOAT(OFS_RETURN) = stringbuffer->num_strings;
4843 }
4844
4845 /*
4846 ========================
4847 VM_buf_copy
4848 copy all content from one buffer to another, make sure it exists
4849 void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
4850 ========================
4851 */
4852 void VM_buf_copy (prvm_prog_t *prog)
4853 {
4854         prvm_stringbuffer_t *srcstringbuffer, *dststringbuffer;
4855         int i;
4856         VM_SAFEPARMCOUNT(2, VM_buf_copy);
4857
4858         srcstringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4859         if(!srcstringbuffer)
4860         {
4861                 VM_Warning(prog, "VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4862                 return;
4863         }
4864         i = (int)PRVM_G_FLOAT(OFS_PARM1);
4865         if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
4866         {
4867                 VM_Warning(prog, "VM_buf_copy: source == destination (%i) in %s\n", i, prog->name);
4868                 return;
4869         }
4870         dststringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4871         if(!dststringbuffer)
4872         {
4873                 VM_Warning(prog, "VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
4874                 return;
4875         }
4876
4877         for (i = 0;i < dststringbuffer->num_strings;i++)
4878                 if (dststringbuffer->strings[i])
4879                         Mem_Free(dststringbuffer->strings[i]);
4880         if (dststringbuffer->strings)
4881                 Mem_Free(dststringbuffer->strings);
4882         *dststringbuffer = *srcstringbuffer;
4883         if (dststringbuffer->max_strings)
4884                 dststringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(dststringbuffer->strings[0]) * dststringbuffer->max_strings);
4885
4886         for (i = 0;i < dststringbuffer->num_strings;i++)
4887         {
4888                 if (srcstringbuffer->strings[i])
4889                 {
4890                         size_t stringlen;
4891                         stringlen = strlen(srcstringbuffer->strings[i]) + 1;
4892                         dststringbuffer->strings[i] = (char *)Mem_Alloc(prog->progs_mempool, stringlen);
4893                         memcpy(dststringbuffer->strings[i], srcstringbuffer->strings[i], stringlen);
4894                 }
4895         }
4896 }
4897
4898 /*
4899 ========================
4900 VM_buf_sort
4901 sort buffer by beginnings of strings (cmplength defaults it's length)
4902 "backward == true" means that sorting goes upside-down
4903 void buf_sort(float bufhandle, float cmplength, float backward) = #464;
4904 ========================
4905 */
4906 void VM_buf_sort (prvm_prog_t *prog)
4907 {
4908         prvm_stringbuffer_t *stringbuffer;
4909         VM_SAFEPARMCOUNT(3, VM_buf_sort);
4910
4911         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4912         if(!stringbuffer)
4913         {
4914                 VM_Warning(prog, "VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4915                 return;
4916         }
4917         if(stringbuffer->num_strings <= 0)
4918         {
4919                 VM_Warning(prog, "VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4920                 return;
4921         }
4922         stringbuffers_sortlength = (int)PRVM_G_FLOAT(OFS_PARM1);
4923         if(stringbuffers_sortlength <= 0)
4924                 stringbuffers_sortlength = 0x7FFFFFFF;
4925
4926         if(!PRVM_G_FLOAT(OFS_PARM2))
4927                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsUP);
4928         else
4929                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
4930
4931         BufStr_Shrink(prog, stringbuffer);
4932 }
4933
4934 /*
4935 ========================
4936 VM_buf_implode
4937 concantenates all buffer string into one with "glue" separator and returns it as tempstring
4938 string buf_implode(float bufhandle, string glue) = #465;
4939 ========================
4940 */
4941 void VM_buf_implode (prvm_prog_t *prog)
4942 {
4943         prvm_stringbuffer_t *stringbuffer;
4944         char                    k[VM_STRINGTEMP_LENGTH];
4945         const char              *sep;
4946         int                             i;
4947         size_t                  l;
4948         VM_SAFEPARMCOUNT(2, VM_buf_implode);
4949
4950         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4951         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4952         if(!stringbuffer)
4953         {
4954                 VM_Warning(prog, "VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4955                 return;
4956         }
4957         if(!stringbuffer->num_strings)
4958                 return;
4959         sep = PRVM_G_STRING(OFS_PARM1);
4960         k[0] = 0;
4961         for(l = i = 0;i < stringbuffer->num_strings;i++)
4962         {
4963                 if(stringbuffer->strings[i])
4964                 {
4965                         l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
4966                         if (l >= sizeof(k) - 1)
4967                                 break;
4968                         strlcat(k, sep, sizeof(k));
4969                         strlcat(k, stringbuffer->strings[i], sizeof(k));
4970                 }
4971         }
4972         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, k);
4973 }
4974
4975 /*
4976 ========================
4977 VM_bufstr_get
4978 get a string from buffer, returns tempstring, dont str_unzone it!
4979 string bufstr_get(float bufhandle, float string_index) = #465;
4980 ========================
4981 */
4982 void VM_bufstr_get (prvm_prog_t *prog)
4983 {
4984         prvm_stringbuffer_t *stringbuffer;
4985         int                             strindex;
4986         VM_SAFEPARMCOUNT(2, VM_bufstr_get);
4987
4988         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4989         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4990         if(!stringbuffer)
4991         {
4992                 VM_Warning(prog, "VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4993                 return;
4994         }
4995         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4996         if (strindex < 0)
4997         {
4998                 // VM_Warning(prog, "VM_bufstr_get: invalid string index %i used in %s\n", strindex, prog->name);
4999                 return;
5000         }
5001         if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
5002                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, stringbuffer->strings[strindex]);
5003 }
5004
5005 /*
5006 ========================
5007 VM_bufstr_set
5008 copies a string into selected slot of buffer
5009 void bufstr_set(float bufhandle, float string_index, string str) = #466;
5010 ========================
5011 */
5012 void VM_bufstr_set (prvm_prog_t *prog)
5013 {
5014         int                             strindex;
5015         prvm_stringbuffer_t *stringbuffer;
5016         const char              *news;
5017
5018         VM_SAFEPARMCOUNT(3, VM_bufstr_set);
5019
5020         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5021         if(!stringbuffer)
5022         {
5023                 VM_Warning(prog, "VM_bufstr_set: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5024                 return;
5025         }
5026         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
5027         if(strindex < 0 || strindex >= 1000000) // huge number of strings
5028         {
5029                 VM_Warning(prog, "VM_bufstr_set: invalid string index %i used in %s\n", strindex, prog->name);
5030                 return;
5031         }
5032
5033         news = PRVM_G_STRING(OFS_PARM2);
5034         BufStr_Set(prog, stringbuffer, strindex, news);
5035 }
5036
5037 /*
5038 ========================
5039 VM_bufstr_add
5040 adds string to buffer in first free slot and returns its index
5041 "order == true" means that string will be added after last "full" slot
5042 float bufstr_add(float bufhandle, string str, float order) = #467;
5043 ========================
5044 */
5045 void VM_bufstr_add (prvm_prog_t *prog)
5046 {
5047         int                             order, strindex;
5048         prvm_stringbuffer_t *stringbuffer;
5049         const char              *string;
5050         size_t                  alloclen;
5051
5052         VM_SAFEPARMCOUNT(3, VM_bufstr_add);
5053
5054         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5055         PRVM_G_FLOAT(OFS_RETURN) = -1;
5056         if(!stringbuffer)
5057         {
5058                 VM_Warning(prog, "VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5059                 return;
5060         }
5061         if(!PRVM_G_INT(OFS_PARM1)) // NULL string
5062         {
5063                 VM_Warning(prog, "VM_bufstr_add: can not add an empty string to buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5064                 return;
5065         }
5066         string = PRVM_G_STRING(OFS_PARM1);
5067         order = (int)PRVM_G_FLOAT(OFS_PARM2);
5068         if(order)
5069                 strindex = stringbuffer->num_strings;
5070         else
5071                 for (strindex = 0;strindex < stringbuffer->num_strings;strindex++)
5072                         if (stringbuffer->strings[strindex] == NULL)
5073                                 break;
5074
5075         BufStr_Expand(prog, stringbuffer, strindex);
5076
5077         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
5078         alloclen = strlen(string) + 1;
5079         stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
5080         memcpy(stringbuffer->strings[strindex], string, alloclen);
5081
5082         PRVM_G_FLOAT(OFS_RETURN) = strindex;
5083 }
5084
5085 /*
5086 ========================
5087 VM_bufstr_free
5088 delete string from buffer
5089 void bufstr_free(float bufhandle, float string_index) = #468;
5090 ========================
5091 */
5092 void VM_bufstr_free (prvm_prog_t *prog)
5093 {
5094         int                             i;
5095         prvm_stringbuffer_t     *stringbuffer;
5096         VM_SAFEPARMCOUNT(2, VM_bufstr_free);
5097
5098         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5099         if(!stringbuffer)
5100         {
5101                 VM_Warning(prog, "VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5102                 return;
5103         }
5104         i = (int)PRVM_G_FLOAT(OFS_PARM1);
5105         if(i < 0)
5106         {
5107                 VM_Warning(prog, "VM_bufstr_free: invalid string index %i used in %s\n", i, prog->name);
5108                 return;
5109         }
5110
5111         if (i < stringbuffer->num_strings)
5112         {
5113                 if(stringbuffer->strings[i])
5114                         Mem_Free(stringbuffer->strings[i]);
5115                 stringbuffer->strings[i] = NULL;
5116         }
5117
5118         BufStr_Shrink(prog, stringbuffer);
5119 }
5120
5121 /*
5122 ========================
5123 VM_buf_loadfile
5124 load a file into string buffer, return 0 or 1
5125 float buf_loadfile(string filename, float bufhandle) = #535;
5126 ========================
5127 */
5128 void VM_buf_loadfile(prvm_prog_t *prog)
5129 {
5130         size_t alloclen;
5131         prvm_stringbuffer_t *stringbuffer;
5132         char string[VM_STRINGTEMP_LENGTH];
5133         int strindex, c, end;
5134         const char *filename;
5135         char vabuf[1024];
5136         qfile_t *file;
5137
5138         VM_SAFEPARMCOUNT(2, VM_buf_loadfile);
5139
5140         // get file
5141         filename = PRVM_G_STRING(OFS_PARM0);
5142         file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", filename), false);
5143         if (file == NULL)
5144                 file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", filename), false);
5145         if (file == NULL)
5146         {
5147                 if (developer_extra.integer)
5148                         VM_Warning(prog, "VM_buf_loadfile: failed to open file %s in %s\n", filename, prog->name);
5149                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5150                 return;
5151         }
5152
5153         // get string buffer
5154         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
5155         if(!stringbuffer)
5156         {
5157                 VM_Warning(prog, "VM_buf_loadfile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
5158                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5159                 return;
5160         }
5161
5162         // read file (append to the end of buffer)
5163         strindex = stringbuffer->num_strings;
5164         while(1)
5165         {
5166                 // read line
5167                 end = 0;
5168                 for (;;)
5169                 {
5170                         c = FS_Getc(file);
5171                         if (c == '\r' || c == '\n' || c < 0)
5172                                 break;
5173                         if (end < VM_STRINGTEMP_LENGTH - 1)
5174                                 string[end++] = c;
5175                 }
5176                 string[end] = 0;
5177                 // remove \n following \r
5178                 if (c == '\r')
5179                 {
5180                         c = FS_Getc(file);
5181                         if (c != '\n')
5182                                 FS_UnGetc(file, (unsigned char)c);
5183                 }
5184                 // add and continue
5185                 if (c >= 0 || end)
5186                 {
5187                         BufStr_Expand(prog, stringbuffer, strindex);
5188                         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
5189                         alloclen = strlen(string) + 1;
5190                         stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
5191                         memcpy(stringbuffer->strings[strindex], string, alloclen);
5192                         strindex = stringbuffer->num_strings;
5193                 }
5194                 else
5195                         break;
5196         }
5197
5198         // close file
5199         FS_Close(file);
5200         PRVM_G_FLOAT(OFS_RETURN) = 1;
5201 }
5202
5203 /*
5204 ========================
5205 VM_buf_writefile
5206 writes stringbuffer to a file, returns 0 or 1
5207 float buf_writefile(float filehandle, float bufhandle, [, float startpos, float numstrings]) = #468;
5208 ========================
5209 */
5210
5211 void VM_buf_writefile(prvm_prog_t *prog)
5212 {
5213         int filenum, strindex, strnum, strlength;
5214         prvm_stringbuffer_t *stringbuffer;
5215
5216         VM_SAFEPARMCOUNTRANGE(2, 4, VM_buf_writefile);
5217
5218         // get file
5219         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
5220         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
5221         {
5222                 VM_Warning(prog, "VM_buf_writefile: invalid file handle %i used in %s\n", filenum, prog->name);
5223                 return;
5224         }
5225         if (prog->openfiles[filenum] == NULL)
5226         {
5227                 VM_Warning(prog, "VM_buf_writefile: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
5228                 return;
5229         }
5230         
5231         // get string buffer
5232         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
5233         if(!stringbuffer)
5234         {
5235                 VM_Warning(prog, "VM_buf_writefile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
5236                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5237                 return;
5238         }
5239
5240         // get start and end parms
5241         if (prog->argc > 3)
5242         {
5243                 strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
5244                 strnum = (int)PRVM_G_FLOAT(OFS_PARM3);
5245         }
5246         else if (prog->argc > 2)
5247         {
5248                 strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
5249                 strnum = stringbuffer->num_strings - strindex;
5250         }
5251         else
5252         {
5253                 strindex = 0;
5254                 strnum = stringbuffer->num_strings;
5255         }
5256         if (strindex < 0 || strindex >= stringbuffer->num_strings)
5257         {
5258                 VM_Warning(prog, "VM_buf_writefile: wrong start string index %i used in %s\n", strindex, prog->name);
5259                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5260                 return;
5261         }
5262         if (strnum < 0)
5263         {
5264                 VM_Warning(prog, "VM_buf_writefile: wrong strings count %i used in %s\n", strnum, prog->name);
5265                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5266                 return;
5267         }
5268
5269         // write
5270         while(strindex < stringbuffer->num_strings && strnum)
5271         {
5272                 if (stringbuffer->strings[strindex])
5273                 {
5274                         if ((strlength = (int)strlen(stringbuffer->strings[strindex])))
5275                                 FS_Write(prog->openfiles[filenum], stringbuffer->strings[strindex], strlength);
5276                         FS_Write(prog->openfiles[filenum], "\n", 1);
5277                 }
5278                 strindex++;
5279                 strnum--;
5280         }
5281
5282         PRVM_G_FLOAT(OFS_RETURN) = 1;
5283 }
5284
5285 #define MATCH_AUTO     0
5286 #define MATCH_WHOLE    1
5287 #define MATCH_LEFT     2
5288 #define MATCH_RIGHT    3
5289 #define MATCH_MIDDLE   4
5290 #define MATCH_PATTERN  5
5291
5292 static const char *detect_match_rule(char *pattern, int *matchrule)
5293 {
5294         char *ppos, *qpos;
5295         int patternlength;
5296
5297         patternlength = (int)strlen(pattern);
5298         ppos = strchr(pattern, '*');
5299         qpos = strchr(pattern, '?');
5300         // has ? - pattern
5301         if (qpos) 
5302         {
5303                 *matchrule = MATCH_PATTERN;
5304                 return pattern;
5305         }
5306         // has * - left, mid, right or pattern
5307         if (ppos)
5308         {
5309                 // starts with * - may be right/mid or pattern
5310                 if ((ppos - pattern) == 0)
5311                 {
5312                         ppos = strchr(pattern+1, '*');
5313                         // *something 
5314                         if (!ppos) 
5315                         {
5316                                 *matchrule = MATCH_RIGHT;
5317                                 return pattern+1;
5318                         }
5319                         // *something*
5320                         if ((ppos - pattern) == patternlength)
5321                         {
5322                                 *matchrule = MATCH_MIDDLE;
5323                                 *ppos = 0;
5324                                 return pattern+1;
5325                         }
5326                         // *som*thing
5327                         *matchrule = MATCH_PATTERN;
5328                         return pattern;
5329                 }
5330                 // end with * - left
5331                 if ((ppos - pattern) == patternlength)
5332                 {
5333                         *matchrule = MATCH_LEFT;
5334                         *ppos = 0;
5335                         return pattern;
5336                 }
5337                 // som*thing
5338                 *matchrule = MATCH_PATTERN;
5339                 return pattern;
5340         }
5341         // have no wildcards - whole string
5342         *matchrule = MATCH_WHOLE;
5343         return pattern;
5344 }
5345
5346 // todo: support UTF8
5347 static qboolean match_rule(const char *string, int max_string, const char *pattern, int patternlength, int rule)
5348 {
5349         const char *mid;
5350
5351         if (rule == 1)
5352                 return !strncmp(string, pattern, max_string) ? true : false;
5353         if (rule == 2)
5354                 return !strncmp(string, pattern, patternlength) ? true : false;
5355         if (rule == 3)
5356         {
5357                 mid = strstr(string, pattern);
5358                 return mid && !*(mid+patternlength);
5359         }
5360         if (rule == 4)
5361                 return strstr(string, pattern) ? true : false;
5362         // pattern
5363         return matchpattern_with_separator(string, pattern, false, "", false) ? true : false;
5364 }
5365
5366 /*
5367 ========================
5368 VM_bufstr_find
5369 find an index of bufstring matching rule
5370 float bufstr_find(float bufhandle, string match, float matchrule, float startpos, float step) = #468;
5371 ========================
5372 */
5373
5374 void VM_bufstr_find(prvm_prog_t *prog)
5375 {
5376         prvm_stringbuffer_t *stringbuffer;
5377         char string[VM_STRINGTEMP_LENGTH];
5378         int matchrule, matchlen, i, step;
5379         const char *match;
5380
5381         VM_SAFEPARMCOUNTRANGE(3, 5, VM_bufstr_find);
5382
5383         PRVM_G_FLOAT(OFS_RETURN) = -1;
5384
5385         // get string buffer
5386         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5387         if(!stringbuffer)
5388         {
5389                 VM_Warning(prog, "VM_bufstr_find: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5390                 return;
5391         }
5392
5393         // get pattern/rule
5394         matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
5395         if (matchrule < 0 || matchrule > 5)
5396         {
5397                 VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
5398                 return;
5399         }
5400         if (matchrule)
5401                 match = PRVM_G_STRING(OFS_PARM1);
5402         else
5403         {
5404                 strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
5405                 match = detect_match_rule(string, &matchrule);
5406         }
5407         matchlen = (int)strlen(match);
5408
5409         // find
5410         i = (prog->argc > 3) ? (int)PRVM_G_FLOAT(OFS_PARM3) : 0;
5411         step = (prog->argc > 4) ? (int)PRVM_G_FLOAT(OFS_PARM4) : 1;
5412         while(i < stringbuffer->num_strings)
5413         {
5414                 if (stringbuffer->strings[i] && match_rule(stringbuffer->strings[i], VM_STRINGTEMP_LENGTH, match, matchlen, matchrule))
5415                 {
5416                         PRVM_G_FLOAT(OFS_RETURN) = i;
5417                         break;
5418                 }
5419                 i += step;
5420         }
5421 }
5422
5423 /*
5424 ========================
5425 VM_matchpattern
5426 float matchpattern(string s, string pattern, float matchrule, float startpos) = #468;
5427 ========================
5428 */
5429 void VM_matchpattern(prvm_prog_t *prog)
5430 {
5431         const char *s, *match;
5432         char string[VM_STRINGTEMP_LENGTH];
5433         int matchrule, l;
5434
5435         VM_SAFEPARMCOUNTRANGE(2, 4, VM_matchpattern);
5436
5437         s = PRVM_G_STRING(OFS_PARM0);
5438
5439         // get pattern/rule
5440         matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
5441         if (matchrule < 0 || matchrule > 5)
5442         {
5443                 VM_Warning(prog, "VM_matchpattern: invalid match rule %i in %s\n", matchrule, prog->name);
5444                 return;
5445         }
5446         if (matchrule)
5447                 match = PRVM_G_STRING(OFS_PARM1);
5448         else
5449         {
5450                 strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
5451                 match = detect_match_rule(string, &matchrule);
5452         }
5453
5454         // offset
5455         l = (int)strlen(match);
5456         if (prog->argc > 3)
5457                 s += max(0, min((unsigned int)PRVM_G_FLOAT(OFS_PARM3), strlen(s)-1));
5458
5459         // match
5460         PRVM_G_FLOAT(OFS_RETURN) = match_rule(s, VM_STRINGTEMP_LENGTH, match, l, matchrule);
5461 }
5462
5463 /*
5464 ========================
5465 VM_buf_cvarlist
5466 ========================
5467 */
5468
5469 void VM_buf_cvarlist(prvm_prog_t *prog)
5470 {
5471         cvar_t *cvar;
5472         const char *partial, *antipartial;
5473         size_t len, antilen;
5474         size_t alloclen;
5475         qboolean ispattern, antiispattern;
5476         int n;
5477         prvm_stringbuffer_t     *stringbuffer;
5478         VM_SAFEPARMCOUNTRANGE(2, 3, VM_buf_cvarlist);
5479
5480         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5481         if(!stringbuffer)
5482         {
5483                 VM_Warning(prog, "VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5484                 return;
5485         }
5486
5487         partial = PRVM_G_STRING(OFS_PARM1);
5488         if(!partial)
5489                 len = 0;
5490         else
5491                 len = strlen(partial);
5492
5493         if(prog->argc == 3)
5494                 antipartial = PRVM_G_STRING(OFS_PARM2);
5495         else
5496                 antipartial = NULL;
5497         if(!antipartial)
5498                 antilen = 0;
5499         else
5500                 antilen = strlen(antipartial);
5501         
5502         for (n = 0;n < stringbuffer->num_strings;n++)
5503                 if (stringbuffer->strings[n])
5504                         Mem_Free(stringbuffer->strings[n]);
5505         if (stringbuffer->strings)
5506                 Mem_Free(stringbuffer->strings);
5507         stringbuffer->strings = NULL;
5508
5509         ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
5510         antiispattern = antipartial && (strchr(antipartial, '*') || strchr(antipartial, '?'));
5511
5512         n = 0;
5513         for(cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
5514         {
5515                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
5516                         continue;
5517
5518                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
5519                         continue;
5520
5521                 ++n;
5522         }
5523
5524         stringbuffer->max_strings = stringbuffer->num_strings = n;
5525         if (stringbuffer->max_strings)
5526                 stringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(stringbuffer->strings[0]) * stringbuffer->max_strings);
5527         
5528         n = 0;
5529         for(cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
5530         {
5531                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
5532                         continue;
5533
5534                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
5535                         continue;
5536
5537                 alloclen = strlen(cvar->name) + 1;
5538                 stringbuffer->strings[n] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
5539                 memcpy(stringbuffer->strings[n], cvar->name, alloclen);
5540
5541                 ++n;
5542         }
5543 }
5544
5545
5546
5547
5548 //=============
5549
5550 /*
5551 ==============
5552 VM_changeyaw
5553
5554 This was a major timewaster in progs, so it was converted to C
5555 ==============
5556 */
5557 void VM_changeyaw (prvm_prog_t *prog)
5558 {
5559         prvm_edict_t            *ent;
5560         float           ideal, current, move, speed;
5561
5562         // this is called (VERY HACKISHLY) by VM_SV_MoveToGoal, so it can not use any
5563         // parameters because they are the parameters to VM_SV_MoveToGoal, not this
5564         //VM_SAFEPARMCOUNT(0, VM_changeyaw);
5565
5566         ent = PRVM_PROG_TO_EDICT(PRVM_gameglobaledict(self));
5567         if (ent == prog->edicts)
5568         {
5569                 VM_Warning(prog, "changeyaw: can not modify world entity\n");
5570                 return;
5571         }
5572         if (ent->priv.server->free)
5573         {
5574                 VM_Warning(prog, "changeyaw: can not modify free entity\n");
5575                 return;
5576         }
5577         current = PRVM_gameedictvector(ent, angles)[1];
5578         current = ANGLEMOD(current);
5579         ideal = PRVM_gameedictfloat(ent, ideal_yaw);
5580         speed = PRVM_gameedictfloat(ent, yaw_speed);
5581
5582         if (current == ideal)
5583                 return;
5584         move = ideal - current;
5585         if (ideal > current)
5586         {
5587                 if (move >= 180)
5588                         move = move - 360;
5589         }
5590         else
5591         {
5592                 if (move <= -180)
5593                         move = move + 360;
5594         }
5595         if (move > 0)
5596         {
5597                 if (move > speed)
5598                         move = speed;
5599         }
5600         else
5601         {
5602                 if (move < -speed)
5603                         move = -speed;
5604         }
5605
5606         current += move;
5607         PRVM_gameedictvector(ent, angles)[1] = ANGLEMOD(current);
5608 }
5609
5610 /*
5611 ==============
5612 VM_changepitch
5613 ==============
5614 */
5615 void VM_changepitch (prvm_prog_t *prog)
5616 {
5617         prvm_edict_t            *ent;
5618         float           ideal, current, move, speed;
5619
5620         VM_SAFEPARMCOUNT(1, VM_changepitch);
5621
5622         ent = PRVM_G_EDICT(OFS_PARM0);
5623         if (ent == prog->edicts)
5624         {
5625                 VM_Warning(prog, "changepitch: can not modify world entity\n");
5626                 return;
5627         }
5628         if (ent->priv.server->free)
5629         {
5630                 VM_Warning(prog, "changepitch: can not modify free entity\n");
5631                 return;
5632         }
5633         current = PRVM_gameedictvector(ent, angles)[0];
5634         current = ANGLEMOD(current);
5635         ideal = PRVM_gameedictfloat(ent, idealpitch);
5636         speed = PRVM_gameedictfloat(ent, pitch_speed);
5637
5638         if (current == ideal)
5639                 return;
5640         move = ideal - current;
5641         if (ideal > current)
5642         {
5643                 if (move >= 180)
5644                         move = move - 360;
5645         }
5646         else
5647         {
5648                 if (move <= -180)
5649                         move = move + 360;
5650         }
5651         if (move > 0)
5652         {
5653                 if (move > speed)
5654                         move = speed;
5655         }
5656         else
5657         {
5658                 if (move < -speed)
5659                         move = -speed;
5660         }
5661
5662         current += move;
5663         PRVM_gameedictvector(ent, angles)[0] = ANGLEMOD(current);
5664 }
5665
5666
5667 void VM_uncolorstring (prvm_prog_t *prog)
5668 {
5669         char szNewString[VM_STRINGTEMP_LENGTH];
5670         const char *szString;
5671
5672         // Prepare Strings
5673         VM_SAFEPARMCOUNT(1, VM_uncolorstring);
5674         szString = PRVM_G_STRING(OFS_PARM0);
5675         COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), true);
5676         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
5677         
5678 }
5679
5680 // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
5681 //strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.
5682 void VM_strstrofs (prvm_prog_t *prog)
5683 {
5684         const char *instr, *match;
5685         int firstofs;
5686         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
5687         instr = PRVM_G_STRING(OFS_PARM0);
5688         match = PRVM_G_STRING(OFS_PARM1);
5689         firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
5690         firstofs = (int)u8_bytelen(instr, firstofs);
5691
5692         if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
5693         {
5694                 PRVM_G_FLOAT(OFS_RETURN) = -1;
5695                 return;
5696         }
5697
5698         match = strstr(instr+firstofs, match);
5699         if (!match)
5700                 PRVM_G_FLOAT(OFS_RETURN) = -1;
5701         else
5702                 PRVM_G_FLOAT(OFS_RETURN) = u8_strnlen(instr, match-instr);
5703 }
5704
5705 //#222 string(string s, float index) str2chr (FTE_STRINGS)
5706 void VM_str2chr (prvm_prog_t *prog)
5707 {
5708         const char *s;
5709         Uchar ch;
5710         int index;
5711         VM_SAFEPARMCOUNT(2, VM_str2chr);
5712         s = PRVM_G_STRING(OFS_PARM0);
5713         index = (int)u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
5714
5715         if((unsigned)index < strlen(s))
5716         {
5717                 if (utf8_enable.integer)
5718                         ch = u8_getchar_noendptr(s + index);
5719                 else
5720                         ch = (unsigned char)s[index];
5721                 PRVM_G_FLOAT(OFS_RETURN) = ch;
5722         }
5723         else
5724                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5725 }
5726
5727 //#223 string(float c, ...) chr2str (FTE_STRINGS)
5728 void VM_chr2str (prvm_prog_t *prog)
5729 {
5730         /*
5731         char    t[9];
5732         int             i;
5733         VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
5734         for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
5735                 t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
5736         t[i] = 0;
5737         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
5738         */
5739         char t[9 * 4 + 1];
5740         int i;
5741         size_t len = 0;
5742         VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
5743         for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i)
5744                 len += u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1);
5745         t[len] = 0;
5746         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
5747 }
5748
5749 static int chrconv_number(int i, int base, int conv)
5750 {
5751         i -= base;
5752         switch (conv)
5753         {
5754         default:
5755         case 5:
5756         case 6:
5757         case 0:
5758                 break;
5759         case 1:
5760                 base = '0';
5761                 break;
5762         case 2:
5763                 base = '0'+128;
5764                 break;
5765         case 3:
5766                 base = '0'-30;
5767                 break;
5768         case 4:
5769                 base = '0'+128-30;
5770                 break;
5771         }
5772         return i + base;
5773 }
5774 static int chrconv_punct(int i, int base, int conv)
5775 {
5776         i -= base;
5777         switch (conv)
5778         {
5779         default:
5780         case 0:
5781                 break;
5782         case 1:
5783                 base = 0;
5784                 break;
5785         case 2:
5786                 base = 128;
5787                 break;
5788         }
5789         return i + base;
5790 }
5791
5792 static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
5793 {
5794         //convert case and colour seperatly...
5795
5796         i -= baset + basec;
5797         switch (convt)
5798         {
5799         default:
5800         case 0:
5801                 break;
5802         case 1:
5803                 baset = 0;
5804                 break;
5805         case 2:
5806                 baset = 128;
5807                 break;
5808
5809         case 5:
5810         case 6:
5811                 baset = 128*((charnum&1) == (convt-5));
5812                 break;
5813         }
5814
5815         switch (convc)
5816         {
5817         default:
5818         case 0:
5819                 break;
5820         case 1:
5821                 basec = 'a';
5822                 break;
5823         case 2:
5824                 basec = 'A';
5825                 break;
5826         }
5827         return i + basec + baset;
5828 }
5829 // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
5830 //bulk convert a string. change case or colouring.
5831 void VM_strconv (prvm_prog_t *prog)
5832 {
5833         int ccase, redalpha, rednum, len, i;
5834         unsigned char resbuf[VM_STRINGTEMP_LENGTH];
5835         unsigned char *result = resbuf;
5836
5837         VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
5838
5839         ccase = (int) PRVM_G_FLOAT(OFS_PARM0);  //0 same, 1 lower, 2 upper
5840         redalpha = (int) PRVM_G_FLOAT(OFS_PARM1);       //0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate
5841         rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
5842         VM_VarString(prog, 3, (char *) resbuf, sizeof(resbuf));
5843         len = (int)strlen((char *) resbuf);
5844
5845         for (i = 0; i < len; i++, result++)     //should this be done backwards?
5846         {
5847                 if (*result >= '0' && *result <= '9')   //normal numbers...
5848                         *result = chrconv_number(*result, '0', rednum);
5849                 else if (*result >= '0'+128 && *result <= '9'+128)
5850                         *result = chrconv_number(*result, '0'+128, rednum);
5851                 else if (*result >= '0'+128-30 && *result <= '9'+128-30)
5852                         *result = chrconv_number(*result, '0'+128-30, rednum);
5853                 else if (*result >= '0'-30 && *result <= '9'-30)
5854                         *result = chrconv_number(*result, '0'-30, rednum);
5855
5856                 else if (*result >= 'a' && *result <= 'z')      //normal numbers...
5857                         *result = chrchar_alpha(*result, 'a', 0, ccase, redalpha, i);
5858                 else if (*result >= 'A' && *result <= 'Z')      //normal numbers...
5859                         *result = chrchar_alpha(*result, 'A', 0, ccase, redalpha, i);
5860                 else if (*result >= 'a'+128 && *result <= 'z'+128)      //normal numbers...
5861                         *result = chrchar_alpha(*result, 'a', 128, ccase, redalpha, i);
5862                 else if (*result >= 'A'+128 && *result <= 'Z'+128)      //normal numbers...
5863                         *result = chrchar_alpha(*result, 'A', 128, ccase, redalpha, i);
5864
5865                 else if ((*result & 127) < 16 || !redalpha)     //special chars..
5866                         *result = *result;
5867                 else if (*result < 128)
5868                         *result = chrconv_punct(*result, 0, redalpha);
5869                 else
5870                         *result = chrconv_punct(*result, 128, redalpha);
5871         }
5872         *result = '\0';
5873
5874         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, (char *) resbuf);
5875 }
5876
5877 // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
5878 void VM_strpad (prvm_prog_t *prog)
5879 {
5880         char src[VM_STRINGTEMP_LENGTH];
5881         char destbuf[VM_STRINGTEMP_LENGTH];
5882         int pad;
5883         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
5884         pad = (int) PRVM_G_FLOAT(OFS_PARM0);
5885         VM_VarString(prog, 1, src, sizeof(src));
5886
5887         // note: < 0 = left padding, > 0 = right padding,
5888         // this is reverse logic of printf!
5889         dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
5890
5891         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, destbuf);
5892 }
5893
5894 // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
5895 //uses qw style \key\value strings
5896 void VM_infoadd (prvm_prog_t *prog)
5897 {
5898         const char *info, *key;
5899         char value[VM_STRINGTEMP_LENGTH];
5900         char temp[VM_STRINGTEMP_LENGTH];
5901
5902         VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
5903         info = PRVM_G_STRING(OFS_PARM0);
5904         key = PRVM_G_STRING(OFS_PARM1);
5905         VM_VarString(prog, 2, value, sizeof(value));
5906
5907         strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
5908
5909         InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
5910
5911         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, temp);
5912 }
5913
5914 // #227 string(string info, string key) infoget (FTE_STRINGS)
5915 //uses qw style \key\value strings
5916 void VM_infoget (prvm_prog_t *prog)
5917 {
5918         const char *info;
5919         const char *key;
5920         char value[VM_STRINGTEMP_LENGTH];
5921
5922         VM_SAFEPARMCOUNT(2, VM_infoget);
5923         info = PRVM_G_STRING(OFS_PARM0);
5924         key = PRVM_G_STRING(OFS_PARM1);
5925
5926         InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
5927
5928         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, value);
5929 }
5930
5931 //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
5932 // also float(string s1, string s2) strcmp (FRIK_FILE)
5933 void VM_strncmp (prvm_prog_t *prog)
5934 {
5935         const char *s1, *s2;
5936         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncmp);
5937         s1 = PRVM_G_STRING(OFS_PARM0);
5938         s2 = PRVM_G_STRING(OFS_PARM1);
5939         if (prog->argc > 2)
5940         {
5941                 PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
5942         }
5943         else
5944         {
5945                 PRVM_G_FLOAT(OFS_RETURN) = strcmp(s1, s2);
5946         }
5947 }
5948
5949 // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
5950 // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
5951 void VM_strncasecmp (prvm_prog_t *prog)
5952 {
5953         const char *s1, *s2;
5954         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncasecmp);
5955         s1 = PRVM_G_STRING(OFS_PARM0);
5956         s2 = PRVM_G_STRING(OFS_PARM1);
5957         if (prog->argc > 2)
5958         {
5959                 PRVM_G_FLOAT(OFS_RETURN) = strncasecmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
5960         }
5961         else
5962         {
5963                 PRVM_G_FLOAT(OFS_RETURN) = strcasecmp(s1, s2);
5964         }
5965 }
5966
5967 // #494 float(float caseinsensitive, string s, ...) crc16
5968 void VM_crc16(prvm_prog_t *prog)
5969 {
5970         float insensitive;
5971         char s[VM_STRINGTEMP_LENGTH];
5972         VM_SAFEPARMCOUNTRANGE(2, 8, VM_crc16);
5973         insensitive = PRVM_G_FLOAT(OFS_PARM0);
5974         VM_VarString(prog, 1, s, sizeof(s));
5975         PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
5976 }
5977
5978 // #639 float(string digest, string data, ...) digest_hex
5979 void VM_digest_hex(prvm_prog_t *prog)
5980 {
5981         const char *digest;
5982
5983         char out[32];
5984         char outhex[65];
5985         int outlen;
5986
5987         char s[VM_STRINGTEMP_LENGTH];
5988         int len;
5989
5990         VM_SAFEPARMCOUNTRANGE(2, 8, VM_digest_hex);
5991         digest = PRVM_G_STRING(OFS_PARM0);
5992         if(!digest)
5993                 digest = "";
5994         VM_VarString(prog, 1, s, sizeof(s));
5995         len = (int)strlen(s);
5996
5997         outlen = 0;
5998
5999         if(!strcmp(digest, "MD4"))
6000         {
6001                 outlen = 16;
6002                 mdfour((unsigned char *) out, (unsigned char *) s, len);
6003         }
6004         else if(!strcmp(digest, "SHA256") && Crypto_Available())
6005         {
6006                 outlen = 32;
6007                 sha256((unsigned char *) out, (unsigned char *) s, len);
6008         }
6009         // no warning needed on mismatch - we return string_null to QC
6010
6011         if(outlen)
6012         {
6013                 int i;
6014                 static const char *hexmap = "0123456789abcdef";
6015                 for(i = 0; i < outlen; ++i)
6016                 {
6017                         outhex[2*i]   = hexmap[(out[i] >> 4) & 15];
6018                         outhex[2*i+1] = hexmap[(out[i] >> 0) & 15];
6019                 }
6020                 outhex[2*i] = 0;
6021                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outhex);
6022         }
6023         else
6024                 PRVM_G_INT(OFS_RETURN) = 0;
6025 }
6026
6027 void VM_wasfreed (prvm_prog_t *prog)
6028 {
6029         VM_SAFEPARMCOUNT(1, VM_wasfreed);
6030         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICT(OFS_PARM0)->priv.required->free;
6031 }
6032
6033 void VM_SetTraceGlobals(prvm_prog_t *prog, const trace_t *trace)
6034 {
6035         PRVM_gameglobalfloat(trace_allsolid) = trace->allsolid;
6036         PRVM_gameglobalfloat(trace_startsolid) = trace->startsolid;
6037         PRVM_gameglobalfloat(trace_fraction) = trace->fraction;
6038         PRVM_gameglobalfloat(trace_inwater) = trace->inwater;
6039         PRVM_gameglobalfloat(trace_inopen) = trace->inopen;
6040         VectorCopy(trace->endpos, PRVM_gameglobalvector(trace_endpos));
6041         VectorCopy(trace->plane.normal, PRVM_gameglobalvector(trace_plane_normal));
6042         PRVM_gameglobalfloat(trace_plane_dist) = trace->plane.dist;
6043         PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
6044         PRVM_gameglobalfloat(trace_dpstartcontents) = trace->startsupercontents;
6045         PRVM_gameglobalfloat(trace_dphitcontents) = trace->hitsupercontents;
6046         PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = trace->hitq3surfaceflags;
6047         PRVM_gameglobalstring(trace_dphittexturename) = trace->hittexture ? PRVM_SetTempString(prog, trace->hittexture->name) : 0;
6048 }
6049
6050 void VM_ClearTraceGlobals(prvm_prog_t *prog)
6051 {
6052         // clean up all trace globals when leaving the VM (anti-triggerbot safeguard)
6053         PRVM_gameglobalfloat(trace_allsolid) = 0;
6054         PRVM_gameglobalfloat(trace_startsolid) = 0;
6055         PRVM_gameglobalfloat(trace_fraction) = 0;
6056         PRVM_gameglobalfloat(trace_inwater) = 0;
6057         PRVM_gameglobalfloat(trace_inopen) = 0;
6058         VectorClear(PRVM_gameglobalvector(trace_endpos));
6059         VectorClear(PRVM_gameglobalvector(trace_plane_normal));
6060         PRVM_gameglobalfloat(trace_plane_dist) = 0;
6061         PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(prog->edicts);
6062         PRVM_gameglobalfloat(trace_dpstartcontents) = 0;
6063         PRVM_gameglobalfloat(trace_dphitcontents) = 0;
6064         PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = 0;
6065         PRVM_gameglobalstring(trace_dphittexturename) = 0;
6066 }
6067
6068 //=============
6069
6070 void VM_Cmd_Init(prvm_prog_t *prog)
6071 {
6072         // only init the stuff for the current prog
6073         VM_Files_Init(prog);
6074         VM_Search_Init(prog);
6075 }
6076
6077 static void animatemodel_reset(prvm_prog_t *prog);
6078
6079 void VM_Cmd_Reset(prvm_prog_t *prog)
6080 {
6081         CL_PurgeOwner( MENUOWNER );
6082         VM_Search_Reset(prog);
6083         VM_Files_CloseAll(prog);
6084         animatemodel_reset(prog);
6085 }
6086
6087 // #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
6088 // does URI escaping on a string (replace evil stuff by %AB escapes)
6089 void VM_uri_escape (prvm_prog_t *prog)
6090 {
6091         char src[VM_STRINGTEMP_LENGTH];
6092         char dest[VM_STRINGTEMP_LENGTH];
6093         char *p, *q;
6094         static const char *hex = "0123456789ABCDEF";
6095
6096         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_escape);
6097         VM_VarString(prog, 0, src, sizeof(src));
6098
6099         for(p = src, q = dest; *p && q < dest + sizeof(dest) - 3; ++p)
6100         {
6101                 if((*p >= 'A' && *p <= 'Z')
6102                         || (*p >= 'a' && *p <= 'z')
6103                         || (*p >= '0' && *p <= '9')
6104                         || (*p == '-')  || (*p == '_') || (*p == '.')
6105                         || (*p == '!')  || (*p == '~')
6106                         || (*p == '\'') || (*p == '(') || (*p == ')'))
6107                         *q++ = *p;
6108                 else
6109                 {
6110                         *q++ = '%';
6111                         *q++ = hex[(*(unsigned char *)p >> 4) & 0xF];
6112                         *q++ = hex[ *(unsigned char *)p       & 0xF];
6113                 }
6114         }
6115         *q++ = 0;
6116
6117         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
6118 }
6119
6120 // #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
6121 // does URI unescaping on a string (get back the evil stuff)
6122 void VM_uri_unescape (prvm_prog_t *prog)
6123 {
6124         char src[VM_STRINGTEMP_LENGTH];
6125         char dest[VM_STRINGTEMP_LENGTH];
6126         char *p, *q;
6127         int hi, lo;
6128
6129         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_unescape);
6130         VM_VarString(prog, 0, src, sizeof(src));
6131
6132         for(p = src, q = dest; *p; ) // no need to check size, because unescape can't expand
6133         {
6134                 if(*p == '%')
6135                 {
6136                         if(p[1] >= '0' && p[1] <= '9')
6137                                 hi = p[1] - '0';
6138                         else if(p[1] >= 'a' && p[1] <= 'f')
6139                                 hi = p[1] - 'a' + 10;
6140                         else if(p[1] >= 'A' && p[1] <= 'F')
6141                                 hi = p[1] - 'A' + 10;
6142                         else
6143                                 goto nohex;
6144                         if(p[2] >= '0' && p[2] <= '9')
6145                                 lo = p[2] - '0';
6146                         else if(p[2] >= 'a' && p[2] <= 'f')
6147                                 lo = p[2] - 'a' + 10;
6148                         else if(p[2] >= 'A' && p[2] <= 'F')
6149                                 lo = p[2] - 'A' + 10;
6150                         else
6151                                 goto nohex;
6152                         if(hi != 0 || lo != 0) // don't unescape NUL bytes
6153                                 *q++ = (char) (hi * 0x10 + lo);
6154                         p += 3;
6155                         continue;
6156                 }
6157
6158 nohex:
6159                 // otherwise:
6160                 *q++ = *p++;
6161         }
6162         *q++ = 0;
6163
6164         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
6165 }
6166
6167 // #502 string(string filename) whichpack (DP_QC_WHICHPACK)
6168 // returns the name of the pack containing a file, or "" if it is not in any pack (but local or non-existant)
6169 void VM_whichpack (prvm_prog_t *prog)
6170 {
6171         const char *fn, *pack;
6172
6173         VM_SAFEPARMCOUNT(1, VM_whichpack);
6174         fn = PRVM_G_STRING(OFS_PARM0);
6175         pack = FS_WhichPack(fn);
6176
6177         PRVM_G_INT(OFS_RETURN) = pack ? PRVM_SetTempString(prog, pack) : 0;
6178 }
6179
6180 typedef struct
6181 {
6182         prvm_prog_t *prog;
6183         double starttime;
6184         float id;
6185         char buffer[MAX_INPUTLINE];
6186         char posttype[128];
6187         unsigned char *postdata; // free when uri_to_prog_t is freed
6188         size_t postlen;
6189         char *sigdata; // free when uri_to_prog_t is freed
6190         size_t siglen;
6191 }
6192 uri_to_prog_t;
6193
6194 static void uri_to_string_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
6195 {
6196         prvm_prog_t *prog;
6197         uri_to_prog_t *handle = (uri_to_prog_t *) cbdata;
6198
6199         prog = handle->prog;
6200         if(!prog->loaded)
6201         {
6202                 // curl reply came too late... so just drop it
6203                 if(handle->postdata)
6204                         Z_Free(handle->postdata);
6205                 if(handle->sigdata)
6206                         Z_Free(handle->sigdata);
6207                 Z_Free(handle);
6208                 return;
6209         }
6210
6211         if((prog->starttime == handle->starttime) && (PRVM_allfunction(URI_Get_Callback)))
6212         {
6213                 if(length_received >= sizeof(handle->buffer))
6214                         length_received = sizeof(handle->buffer) - 1;
6215                 handle->buffer[length_received] = 0;
6216
6217                 PRVM_G_FLOAT(OFS_PARM0) = handle->id;
6218                 PRVM_G_FLOAT(OFS_PARM1) = status;
6219                 PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, handle->buffer);
6220                 prog->ExecuteProgram(prog, PRVM_allfunction(URI_Get_Callback), "QC function URI_Get_Callback is missing");
6221         }
6222
6223         if(handle->postdata)
6224                 Z_Free(handle->postdata);
6225         if(handle->sigdata)
6226                 Z_Free(handle->sigdata);
6227         Z_Free(handle);
6228 }
6229
6230 // uri_get() gets content from an URL and calls a callback "uri_get_callback" with it set as string; an unique ID of the transfer is returned
6231 // returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string
6232 void VM_uri_get (prvm_prog_t *prog)
6233 {
6234         const char *url;
6235         float id;
6236         qboolean ret;
6237         uri_to_prog_t *handle;
6238         const char *posttype = NULL;
6239         const char *postseparator = NULL;
6240         int poststringbuffer = -1;
6241         int postkeyid = -1;
6242         const char *query_string = NULL;
6243         size_t lq;
6244
6245         if(!PRVM_allfunction(URI_Get_Callback))
6246                 prog->error_cmd("uri_get called by %s without URI_Get_Callback defined", prog->name);
6247
6248         VM_SAFEPARMCOUNTRANGE(2, 6, VM_uri_get);
6249
6250         url = PRVM_G_STRING(OFS_PARM0);
6251         id = PRVM_G_FLOAT(OFS_PARM1);
6252         if(prog->argc >= 3)
6253                 posttype = PRVM_G_STRING(OFS_PARM2);
6254         if(prog->argc >= 4)
6255                 postseparator = PRVM_G_STRING(OFS_PARM3);
6256         if(prog->argc >= 5)
6257                 poststringbuffer = PRVM_G_FLOAT(OFS_PARM4);
6258         if(prog->argc >= 6)
6259                 postkeyid = PRVM_G_FLOAT(OFS_PARM5);
6260         handle = (uri_to_prog_t *) Z_Malloc(sizeof(*handle)); // this can't be the prog's mem pool, as curl may call the callback later!
6261
6262         query_string = strchr(url, '?');
6263         if(query_string)
6264                 ++query_string;
6265         lq = query_string ? strlen(query_string) : 0;
6266
6267         handle->prog = prog;
6268         handle->starttime = prog->starttime;
6269         handle->id = id;
6270         if(postseparator && posttype && *posttype)
6271         {
6272                 size_t l = strlen(postseparator);
6273                 if(poststringbuffer >= 0)
6274                 {
6275                         size_t ltotal;
6276                         int i;
6277                         // "implode"
6278                         prvm_stringbuffer_t *stringbuffer;
6279                         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, poststringbuffer);
6280                         if(!stringbuffer)
6281                         {
6282                                 VM_Warning(prog, "uri_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
6283                                 return;
6284                         }
6285                         ltotal = 0;
6286                         for(i = 0;i < stringbuffer->num_strings;i++)
6287                         {
6288                                 if(i > 0)
6289                                         ltotal += l;
6290                                 if(stringbuffer->strings[i])
6291                                         ltotal += strlen(stringbuffer->strings[i]);
6292                         }
6293                         handle->postdata = (unsigned char *)Z_Malloc(ltotal + 1 + lq);
6294                         handle->postlen = ltotal;
6295                         ltotal = 0;
6296                         for(i = 0;i < stringbuffer->num_strings;i++)
6297                         {
6298                                 if(i > 0)
6299                                 {
6300                                         memcpy(handle->postdata + ltotal, postseparator, l);
6301                                         ltotal += l;
6302                                 }
6303                                 if(stringbuffer->strings[i])
6304                                 {
6305                                         memcpy(handle->postdata + ltotal, stringbuffer->strings[i], strlen(stringbuffer->strings[i]));
6306                                         ltotal += strlen(stringbuffer->strings[i]);
6307                                 }
6308                         }
6309                         if(ltotal != handle->postlen)
6310                                 prog->error_cmd("%s: string buffer content size mismatch, possible overrun", prog->name);
6311                 }
6312                 else
6313                 {
6314                         handle->postdata = (unsigned char *)Z_Malloc(l + 1 + lq);
6315                         handle->postlen = l;
6316                         memcpy(handle->postdata, postseparator, l);
6317                 }
6318                 handle->postdata[handle->postlen] = 0;
6319                 if(query_string)
6320                         memcpy(handle->postdata + handle->postlen + 1, query_string, lq);
6321                 if(postkeyid >= 0)
6322                 {
6323                         // POST: we sign postdata \0 query string
6324                         size_t ll;
6325                         handle->sigdata = (char *)Z_Malloc(8192);
6326                         strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
6327                         l = strlen(handle->sigdata);
6328                         handle->siglen = Crypto_SignDataDetached(handle->postdata, handle->postlen + 1 + lq, postkeyid, handle->sigdata + l, 8192 - l);
6329                         if(!handle->siglen)
6330                         {
6331                                 Z_Free(handle->sigdata);
6332                                 handle->sigdata = NULL;
6333                                 goto out1;
6334                         }
6335                         ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
6336                         if(!ll)
6337                         {
6338                                 Z_Free(handle->sigdata);
6339                                 handle->sigdata = NULL;
6340                                 goto out1;
6341                         }
6342                         handle->siglen = l + ll;
6343                         handle->sigdata[handle->siglen] = 0;
6344                 }
6345 out1:
6346                 strlcpy(handle->posttype, posttype, sizeof(handle->posttype));
6347                 ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, handle->posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
6348         }
6349         else
6350         {
6351                 if(postkeyid >= 0 && query_string)
6352                 {
6353                         // GET: we sign JUST the query string
6354                         size_t l, ll;
6355                         handle->sigdata = (char *)Z_Malloc(8192);
6356                         strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
6357                         l = strlen(handle->sigdata);
6358                         handle->siglen = Crypto_SignDataDetached(query_string, lq, postkeyid, handle->sigdata + l, 8192 - l);
6359                         if(!handle->siglen)
6360                         {
6361                                 Z_Free(handle->sigdata);
6362                                 handle->sigdata = NULL;
6363                                 goto out2;
6364                         }
6365                         ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
6366                         if(!ll)
6367                         {
6368                                 Z_Free(handle->sigdata);
6369                                 handle->sigdata = NULL;
6370                                 goto out2;
6371                         }
6372                         handle->siglen = l + ll;
6373                         handle->sigdata[handle->siglen] = 0;
6374                 }
6375 out2:
6376                 handle->postdata = NULL;
6377                 handle->postlen = 0;
6378                 ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, NULL, NULL, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
6379         }
6380         if(ret)
6381         {
6382                 PRVM_G_INT(OFS_RETURN) = 1;
6383         }
6384         else
6385         {
6386                 if(handle->postdata)
6387                         Z_Free(handle->postdata);
6388                 if(handle->sigdata)
6389                         Z_Free(handle->sigdata);
6390                 Z_Free(handle);
6391                 PRVM_G_INT(OFS_RETURN) = 0;
6392         }
6393 }
6394
6395 void VM_netaddress_resolve (prvm_prog_t *prog)
6396 {
6397         const char *ip;
6398         char normalized[128];
6399         int port;
6400         lhnetaddress_t addr;
6401
6402         VM_SAFEPARMCOUNTRANGE(1, 2, VM_netaddress_resolve);
6403
6404         ip = PRVM_G_STRING(OFS_PARM0);
6405         port = 0;
6406         if(prog->argc > 1)
6407                 port = (int) PRVM_G_FLOAT(OFS_PARM1);
6408
6409         if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
6410                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, normalized);
6411         else
6412                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
6413 }
6414
6415 //string(prvm_prog_t *prog) getextresponse = #624; // returns the next extResponse packet that was sent to this client
6416 void VM_CL_getextresponse (prvm_prog_t *prog)
6417 {
6418         VM_SAFEPARMCOUNT(0,VM_argv);
6419
6420         if (cl_net_extresponse_count <= 0)
6421                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
6422         else
6423         {
6424                 int first;
6425                 --cl_net_extresponse_count;
6426                 first = (cl_net_extresponse_last + NET_EXTRESPONSE_MAX - cl_net_extresponse_count) % NET_EXTRESPONSE_MAX;
6427                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cl_net_extresponse[first]);
6428         }
6429 }
6430
6431 void VM_SV_getextresponse (prvm_prog_t *prog)
6432 {
6433         VM_SAFEPARMCOUNT(0,VM_argv);
6434
6435         if (sv_net_extresponse_count <= 0)
6436                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
6437         else
6438         {
6439                 int first;
6440                 --sv_net_extresponse_count;
6441                 first = (sv_net_extresponse_last + NET_EXTRESPONSE_MAX - sv_net_extresponse_count) % NET_EXTRESPONSE_MAX;
6442                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, sv_net_extresponse[first]);
6443         }
6444 }
6445
6446 /*
6447 =========
6448 Common functions between menu.dat and clsprogs
6449 =========
6450 */
6451
6452 //#349 float() isdemo 
6453 void VM_CL_isdemo (prvm_prog_t *prog)
6454 {
6455         VM_SAFEPARMCOUNT(0, VM_CL_isdemo);
6456         PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
6457 }
6458
6459 //#355 float() videoplaying 
6460 void VM_CL_videoplaying (prvm_prog_t *prog)
6461 {
6462         VM_SAFEPARMCOUNT(0, VM_CL_videoplaying);
6463         PRVM_G_FLOAT(OFS_RETURN) = cl_videoplaying;
6464 }
6465
6466 /*
6467 =========
6468 VM_M_callfunction
6469
6470         callfunction(...,string function_name)
6471 Extension: pass
6472 =========
6473 */
6474 void VM_callfunction(prvm_prog_t *prog)
6475 {
6476         mfunction_t *func;
6477         const char *s;
6478
6479         VM_SAFEPARMCOUNTRANGE(1, 8, VM_callfunction);
6480
6481         s = PRVM_G_STRING(OFS_PARM0+(prog->argc - 1)*3);
6482
6483         VM_CheckEmptyString(prog, s);
6484
6485         func = PRVM_ED_FindFunction(prog, s);
6486
6487         if(!func)
6488                 prog->error_cmd("VM_callfunction: function %s not found !", s);
6489         else if (func->first_statement < 0)
6490         {
6491                 // negative statements are built in functions
6492                 int builtinnumber = -func->first_statement;
6493                 prog->xfunction->builtinsprofile++;
6494                 if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
6495                         prog->builtins[builtinnumber](prog);
6496                 else
6497                         prog->error_cmd("No such builtin #%i in %s; most likely cause: outdated engine build. Try updating!", builtinnumber, prog->name);
6498         }
6499         else if(func - prog->functions > 0)
6500         {
6501                 prog->argc--;
6502                 prog->ExecuteProgram(prog, func - prog->functions,"");
6503                 prog->argc++;
6504         }
6505 }
6506
6507 /*
6508 =========
6509 VM_isfunction
6510
6511 float   isfunction(string function_name)
6512 =========
6513 */
6514 void VM_isfunction(prvm_prog_t *prog)
6515 {
6516         mfunction_t *func;
6517         const char *s;
6518
6519         VM_SAFEPARMCOUNT(1, VM_isfunction);
6520
6521         s = PRVM_G_STRING(OFS_PARM0);
6522
6523         VM_CheckEmptyString(prog, s);
6524
6525         func = PRVM_ED_FindFunction(prog, s);
6526
6527         if(!func)
6528                 PRVM_G_FLOAT(OFS_RETURN) = false;
6529         else
6530                 PRVM_G_FLOAT(OFS_RETURN) = true;
6531 }
6532
6533 /*
6534 =========
6535 VM_sprintf
6536
6537 string sprintf(string format, ...)
6538 =========
6539 */
6540
6541 void VM_sprintf(prvm_prog_t *prog)
6542 {
6543         const char *s, *s0;
6544         char outbuf[MAX_INPUTLINE];
6545         char *o = outbuf, *end = outbuf + sizeof(outbuf), *err;
6546         const char *p;
6547         int argpos = 1;
6548         int width, precision, thisarg, flags;
6549         char formatbuf[16];
6550         char *f;
6551         int isfloat;
6552         static prvm_int_t dummyivec[3] = {0, 0, 0};
6553         static prvm_vec_t dummyvec[3] = {0, 0, 0};
6554         char vabuf[1024];
6555
6556 #define PRINTF_ALTERNATE 1
6557 #define PRINTF_ZEROPAD 2
6558 #define PRINTF_LEFT 4
6559 #define PRINTF_SPACEPOSITIVE 8
6560 #define PRINTF_SIGNPOSITIVE 16
6561
6562         formatbuf[0] = '%';
6563
6564         s = PRVM_G_STRING(OFS_PARM0);
6565
6566 #define GETARG_FLOAT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
6567 #define GETARG_VECTOR(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)
6568 #define GETARG_INT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0)
6569 #define GETARG_INTVECTOR(a) (((a)>=1 && (a)<prog->argc) ? ((prvm_int_t*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)
6570 #define GETARG_STRING(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_STRING(OFS_PARM0 + 3 * (a))) : "")
6571
6572         for(;;)
6573         {
6574                 s0 = s;
6575                 switch(*s)
6576                 {
6577                         case 0:
6578                                 goto finished;
6579                         case '%':
6580                                 ++s;
6581
6582                                 if(*s == '%')
6583                                         goto verbatim;
6584
6585                                 // complete directive format:
6586                                 // %3$*1$.*2$ld
6587                                 
6588                                 width = -1;
6589                                 precision = -1;
6590                                 thisarg = -1;
6591                                 flags = 0;
6592                                 isfloat = -1;
6593
6594                                 // is number following?
6595                                 if(*s >= '0' && *s <= '9')
6596                                 {
6597                                         width = strtol(s, &err, 10);
6598                                         if(!err)
6599                                         {
6600                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6601                                                 goto finished;
6602                                         }
6603                                         if(*err == '$')
6604                                         {
6605                                                 thisarg = width;
6606                                                 width = -1;
6607                                                 s = err + 1;
6608                                         }
6609                                         else
6610                                         {
6611                                                 if(*s == '0')
6612                                                 {
6613                                                         flags |= PRINTF_ZEROPAD;
6614                                                         if(width == 0)
6615                                                                 width = -1; // it was just a flag
6616                                                 }
6617                                                 s = err;
6618                                         }
6619                                 }
6620
6621                                 if(width < 0)
6622                                 {
6623                                         for(;;)
6624                                         {
6625                                                 switch(*s)
6626                                                 {
6627                                                         case '#': flags |= PRINTF_ALTERNATE; break;
6628                                                         case '0': flags |= PRINTF_ZEROPAD; break;
6629                                                         case '-': flags |= PRINTF_LEFT; break;
6630                                                         case ' ': flags |= PRINTF_SPACEPOSITIVE; break;
6631                                                         case '+': flags |= PRINTF_SIGNPOSITIVE; break;
6632                                                         default:
6633                                                                 goto noflags;
6634                                                 }
6635                                                 ++s;
6636                                         }
6637 noflags:
6638                                         if(*s == '*')
6639                                         {
6640                                                 ++s;
6641                                                 if(*s >= '0' && *s <= '9')
6642                                                 {
6643                                                         width = strtol(s, &err, 10);
6644                                                         if(!err || *err != '$')
6645                                                         {
6646                                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6647                                                                 goto finished;
6648                                                         }
6649                                                         s = err + 1;
6650                                                 }
6651                                                 else
6652                                                         width = argpos++;
6653                                                 width = GETARG_FLOAT(width);
6654                                                 if(width < 0)
6655                                                 {
6656                                                         flags |= PRINTF_LEFT;
6657                                                         width = -width;
6658                                                 }
6659                                         }
6660                                         else if(*s >= '0' && *s <= '9')
6661                                         {
6662                                                 width = strtol(s, &err, 10);
6663                                                 if(!err)
6664                                                 {
6665                                                         VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6666                                                         goto finished;
6667                                                 }
6668                                                 s = err;
6669                                                 if(width < 0)
6670                                                 {
6671                                                         flags |= PRINTF_LEFT;
6672                                                         width = -width;
6673                                                 }
6674                                         }
6675                                         // otherwise width stays -1
6676                                 }
6677
6678                                 if(*s == '.')
6679                                 {
6680                                         ++s;
6681                                         if(*s == '*')
6682                                         {
6683                                                 ++s;
6684                                                 if(*s >= '0' && *s <= '9')
6685                                                 {
6686                                                         precision = strtol(s, &err, 10);
6687                                                         if(!err || *err != '$')
6688                                                         {
6689                                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6690                                                                 goto finished;
6691                                                         }
6692                                                         s = err + 1;
6693                                                 }
6694                                                 else
6695                                                         precision = argpos++;
6696                                                 precision = GETARG_FLOAT(precision);
6697                                         }
6698                                         else if(*s >= '0' && *s <= '9')
6699                                         {
6700                                                 precision = strtol(s, &err, 10);
6701                                                 if(!err)
6702                                                 {
6703                                                         VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6704                                                         goto finished;
6705                                                 }
6706                                                 s = err;
6707                                         }
6708                                         else
6709                                         {
6710                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6711                                                 goto finished;
6712                                         }
6713                                 }
6714
6715                                 for(;;)
6716                                 {
6717                                         switch(*s)
6718                                         {
6719                                                 case 'h': isfloat = 1; break;
6720                                                 case 'l': isfloat = 0; break;
6721                                                 case 'L': isfloat = 0; break;
6722                                                 case 'j': break;
6723                                                 case 'z': break;
6724                                                 case 't': break;
6725                                                 default:
6726                                                         goto nolength;
6727                                         }
6728                                         ++s;
6729                                 }
6730 nolength:
6731
6732                                 // now s points to the final directive char and is no longer changed
6733                                 if(isfloat < 0)
6734                                 {
6735                                         if(*s == 'i')
6736                                                 isfloat = 0;
6737                                         else
6738                                                 isfloat = 1;
6739                                 }
6740
6741                                 if(thisarg < 0)
6742                                         thisarg = argpos++;
6743
6744                                 if(o < end - 1)
6745                                 {
6746                                         f = &formatbuf[1];
6747                                         if(*s != 's' && *s != 'c')
6748                                                 if(flags & PRINTF_ALTERNATE) *f++ = '#';
6749                                         if(flags & PRINTF_ZEROPAD) *f++ = '0';
6750                                         if(flags & PRINTF_LEFT) *f++ = '-';
6751                                         if(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';
6752                                         if(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';
6753                                         *f++ = '*';
6754                                         if(precision >= 0)
6755                                         {
6756                                                 *f++ = '.';
6757                                                 *f++ = '*';
6758                                         }
6759                                         if(*s == 'd' || *s == 'i' || *s == 'o' || *s == 'u' || *s == 'x' || *s == 'X')
6760                                         {
6761                                                 // make it use a good integer type
6762                                                 for(p = INT_LOSSLESS_FORMAT_SIZE; *p; )
6763                                                         *f++ = *p++;
6764                                         }
6765                                         *f++ = *s;
6766                                         *f++ = 0;
6767
6768                                         if(width < 0) // not set
6769                                                 width = 0;
6770
6771                                         switch(*s)
6772                                         {
6773                                                 case 'd': case 'i':
6774                                                         if(precision < 0) // not set
6775                                                                 o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_INT(thisarg))));
6776                                                         else
6777                                                                 o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_INT(thisarg))));
6778                                                         break;
6779                                                 case 'o': case 'u': case 'x': case 'X':
6780                                                         if(precision < 0) // not set
6781                                                                 o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_INT(thisarg))));
6782                                                         else
6783                                                                 o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_INT(thisarg))));
6784                                                         break;
6785                                                 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
6786                                                         if(precision < 0) // not set
6787                                                                 o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
6788                                                         else
6789                                                                 o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
6790                                                         break;
6791                                                 case 'v': case 'V':
6792                                                         f[-2] += 'g' - 'v';
6793                                                         if(precision < 0) // not set
6794                                                                 o += dpsnprintf(o, end - o, va(vabuf, sizeof(vabuf), "%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
6795                                                                         width, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
6796                                                                         width, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
6797                                                                         width, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
6798                                                                 );
6799                                                         else
6800                                                                 o += dpsnprintf(o, end - o, va(vabuf, sizeof(vabuf), "%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
6801                                                                         width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
6802                                                                         width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
6803                                                                         width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
6804                                                                 );
6805                                                         break;
6806                                                 case 'c':
6807                                                         if(flags & PRINTF_ALTERNATE)
6808                                                         {
6809                                                                 if(precision < 0) // not set
6810                                                                         o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
6811                                                                 else
6812                                                                         o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
6813                                                         }
6814                                                         else
6815                                                         {
6816                                                                 unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));
6817                                                                 char charbuf16[16];
6818                                                                 const char *buf = u8_encodech(c, NULL, charbuf16);
6819                                                                 if(!buf)
6820                                                                         buf = "";
6821                                                                 if(precision < 0) // not set
6822                                                                         precision = end - o - 1;
6823                                                                 o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision);
6824                                                         }
6825                                                         break;
6826                                                 case 's':
6827                                                         if(flags & PRINTF_ALTERNATE)
6828                                                         {
6829                                                                 if(precision < 0) // not set
6830                                                                         o += dpsnprintf(o, end - o, formatbuf, width, GETARG_STRING(thisarg));
6831                                                                 else
6832                                                                         o += dpsnprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));
6833                                                         }
6834                                                         else
6835                                                         {
6836                                                                 if(precision < 0) // not set
6837                                                                         precision = end - o - 1;
6838                                                                 if(flags & PRINTF_SIGNPOSITIVE)
6839                                                                         o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
6840                                                                 else
6841                                                                         o += u8_strpad_colorcodes(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
6842                                                         }
6843                                                         break;
6844                                                 default:
6845                                                         VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6846                                                         goto finished;
6847                                         }
6848                                 }
6849                                 ++s;
6850                                 break;
6851                         default:
6852 verbatim:
6853                                 if(o < end - 1)
6854                                         *o++ = *s;
6855                                 ++s;
6856                                 break;
6857                 }
6858         }
6859 finished:
6860         *o = 0;
6861         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outbuf);
6862 }
6863
6864
6865 // surface querying
6866
6867 static dp_model_t *getmodel(prvm_prog_t *prog, prvm_edict_t *ed)
6868 {
6869         if (prog == SVVM_prog)
6870                 return SV_GetModelFromEdict(ed);
6871         else if (prog == CLVM_prog)
6872                 return CL_GetModelFromEdict(ed);
6873         else
6874                 return NULL;
6875 }
6876
6877 struct animatemodel_cache
6878 {
6879         dp_model_t *model;
6880         frameblend_t frameblend[MAX_FRAMEBLENDS];
6881         skeleton_t *skeleton_p;
6882         skeleton_t skeleton;
6883         float *data_vertex3f;
6884         float *data_svector3f;
6885         float *data_tvector3f;
6886         float *data_normal3f;
6887         int max_vertices;
6888         float *buf_vertex3f;
6889         float *buf_svector3f;
6890         float *buf_tvector3f;
6891         float *buf_normal3f;
6892 };
6893
6894 static void animatemodel_reset(prvm_prog_t *prog)
6895 {
6896         if (!prog->animatemodel_cache)
6897                 return;
6898         if(prog->animatemodel_cache->buf_vertex3f) Mem_Free(prog->animatemodel_cache->buf_vertex3f);
6899         if(prog->animatemodel_cache->buf_svector3f) Mem_Free(prog->animatemodel_cache->buf_svector3f);
6900         if(prog->animatemodel_cache->buf_tvector3f) Mem_Free(prog->animatemodel_cache->buf_tvector3f);
6901         if(prog->animatemodel_cache->buf_normal3f) Mem_Free(prog->animatemodel_cache->buf_normal3f);
6902         Mem_Free(prog->animatemodel_cache);
6903 }
6904
6905 static void animatemodel(prvm_prog_t *prog, dp_model_t *model, prvm_edict_t *ed)
6906 {
6907         skeleton_t *skeleton;
6908         int skeletonindex = -1;
6909         qboolean need = false;
6910         struct animatemodel_cache *animatemodel_cache;
6911         if (!prog->animatemodel_cache)
6912         {
6913                 prog->animatemodel_cache = (struct animatemodel_cache *)Mem_Alloc(prog->progs_mempool, sizeof(struct animatemodel_cache));
6914                 memset(prog->animatemodel_cache, 0, sizeof(struct animatemodel_cache));
6915         }
6916         animatemodel_cache = prog->animatemodel_cache;
6917         if(!(model->surfmesh.isanimated && model->AnimateVertices))
6918         {
6919                 animatemodel_cache->data_vertex3f = model->surfmesh.data_vertex3f;
6920                 animatemodel_cache->data_svector3f = model->surfmesh.data_svector3f;
6921                 animatemodel_cache->data_tvector3f = model->surfmesh.data_tvector3f;
6922                 animatemodel_cache->data_normal3f = model->surfmesh.data_normal3f;
6923                 return;
6924         }
6925         need |= (animatemodel_cache->model != model);
6926         VM_GenerateFrameGroupBlend(prog, ed->priv.server->framegroupblend, ed);
6927         VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model, PRVM_serverglobalfloat(time));
6928         need |= (memcmp(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend))) != 0;
6929         skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 1;
6930         if (!(skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones))
6931                 skeleton = NULL;
6932         need |= (animatemodel_cache->skeleton_p != skeleton);
6933         if(skeleton)
6934                 need |= (memcmp(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton))) != 0;
6935         if(!need)
6936                 return;
6937         if(model->surfmesh.num_vertices > animatemodel_cache->max_vertices)
6938         {
6939                 animatemodel_cache->max_vertices = model->surfmesh.num_vertices * 2;
6940                 if(animatemodel_cache->buf_vertex3f) Mem_Free(animatemodel_cache->buf_vertex3f);
6941                 if(animatemodel_cache->buf_svector3f) Mem_Free(animatemodel_cache->buf_svector3f);
6942                 if(animatemodel_cache->buf_tvector3f) Mem_Free(animatemodel_cache->buf_tvector3f);
6943                 if(animatemodel_cache->buf_normal3f) Mem_Free(animatemodel_cache->buf_normal3f);
6944                 animatemodel_cache->buf_vertex3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6945                 animatemodel_cache->buf_svector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6946                 animatemodel_cache->buf_tvector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6947                 animatemodel_cache->buf_normal3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6948         }
6949         animatemodel_cache->data_vertex3f = animatemodel_cache->buf_vertex3f;
6950         animatemodel_cache->data_svector3f = animatemodel_cache->buf_svector3f;
6951         animatemodel_cache->data_tvector3f = animatemodel_cache->buf_tvector3f;
6952         animatemodel_cache->data_normal3f = animatemodel_cache->buf_normal3f;
6953         VM_UpdateEdictSkeleton(prog, ed, model, ed->priv.server->frameblend);
6954         model->AnimateVertices(model, ed->priv.server->frameblend, &ed->priv.server->skeleton, animatemodel_cache->data_vertex3f, animatemodel_cache->data_normal3f, animatemodel_cache->data_svector3f, animatemodel_cache->data_tvector3f);
6955         animatemodel_cache->model = model;
6956         memcpy(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
6957         animatemodel_cache->skeleton_p = skeleton;
6958         if(skeleton)
6959                 memcpy(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton));
6960 }
6961
6962 static void getmatrix(prvm_prog_t *prog, prvm_edict_t *ed, matrix4x4_t *out)
6963 {
6964         if (prog == SVVM_prog)
6965                 SV_GetEntityMatrix(prog, ed, out, false);
6966         else if (prog == CLVM_prog)
6967                 CL_GetEntityMatrix(prog, ed, out, false);
6968         else
6969                 *out = identitymatrix;
6970 }
6971
6972 static void applytransform_forward(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6973 {
6974         matrix4x4_t m;
6975         getmatrix(prog, ed, &m);
6976         Matrix4x4_Transform(&m, in, out);
6977 }
6978
6979 static void applytransform_forward_direction(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6980 {
6981         matrix4x4_t m;
6982         getmatrix(prog, ed, &m);
6983         Matrix4x4_Transform3x3(&m, in, out);
6984 }
6985
6986 static void applytransform_inverted(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6987 {
6988         matrix4x4_t m, n;
6989         getmatrix(prog, ed, &m);
6990         Matrix4x4_Invert_Full(&n, &m);
6991         Matrix4x4_Transform3x3(&n, in, out);
6992 }
6993
6994 static void applytransform_forward_normal(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6995 {
6996         matrix4x4_t m;
6997         float p[4];
6998         getmatrix(prog, ed, &m);
6999         Matrix4x4_TransformPositivePlane(&m, in[0], in[1], in[2], 0, p);
7000         VectorCopy(p, out);
7001 }
7002
7003 static void clippointtosurface(prvm_prog_t *prog, prvm_edict_t *ed, dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t out)
7004 {
7005         int i, j, k;
7006         float *v[3], facenormal[3], edgenormal[3], sidenormal[3], temp[3], offsetdist, dist, bestdist;
7007         const int *e;
7008         animatemodel(prog, model, ed);
7009         bestdist = 1000000000;
7010         VectorCopy(p, out);
7011         for (i = 0, e = (model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);i < surface->num_triangles;i++, e += 3)
7012         {
7013                 // clip original point to each triangle of the surface and find the
7014                 // triangle that is closest
7015                 v[0] = prog->animatemodel_cache->data_vertex3f + e[0] * 3;
7016                 v[1] = prog->animatemodel_cache->data_vertex3f + e[1] * 3;
7017                 v[2] = prog->animatemodel_cache->data_vertex3f + e[2] * 3;
7018                 TriangleNormal(v[0], v[1], v[2], facenormal);
7019                 VectorNormalize(facenormal);
7020                 offsetdist = DotProduct(v[0], facenormal) - DotProduct(p, facenormal);
7021                 VectorMA(p, offsetdist, facenormal, temp);
7022                 for (j = 0, k = 2;j < 3;k = j, j++)
7023                 {
7024                         VectorSubtract(v[k], v[j], edgenormal);
7025                         CrossProduct(edgenormal, facenormal, sidenormal);
7026                         VectorNormalize(sidenormal);
7027                         offsetdist = DotProduct(v[k], sidenormal) - DotProduct(temp, sidenormal);
7028                         if (offsetdist < 0)
7029                                 VectorMA(temp, offsetdist, sidenormal, temp);
7030                 }
7031                 dist = VectorDistance2(temp, p);
7032                 if (bestdist > dist)
7033                 {
7034                         bestdist = dist;
7035                         VectorCopy(temp, out);
7036                 }
7037         }
7038 }
7039
7040 static msurface_t *getsurface(dp_model_t *model, int surfacenum)
7041 {
7042         if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces)
7043                 return NULL;
7044         return model->data_surfaces + surfacenum + model->firstmodelsurface;
7045 }
7046
7047
7048 //PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
7049 void VM_getsurfacenumpoints(prvm_prog_t *prog)
7050 {
7051         dp_model_t *model;
7052         msurface_t *surface;
7053         VM_SAFEPARMCOUNT(2, VM_getsurfacenumpoints);
7054         // return 0 if no such surface
7055         if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7056         {
7057                 PRVM_G_FLOAT(OFS_RETURN) = 0;
7058                 return;
7059         }
7060
7061         // note: this (incorrectly) assumes it is a simple polygon
7062         PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices;
7063 }
7064 //PF_getsurfacepoint,     // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
7065 void VM_getsurfacepoint(prvm_prog_t *prog)
7066 {
7067         prvm_edict_t *ed;
7068         dp_model_t *model;
7069         msurface_t *surface;
7070         int pointnum;
7071         vec3_t result;
7072         VM_SAFEPARMCOUNT(3, VM_getsurfacepoint);
7073         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7074         ed = PRVM_G_EDICT(OFS_PARM0);
7075         if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7076                 return;
7077         // note: this (incorrectly) assumes it is a simple polygon
7078         pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
7079         if (pointnum < 0 || pointnum >= surface->num_vertices)
7080                 return;
7081         animatemodel(prog, model, ed);
7082         applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7083         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7084 }
7085 //PF_getsurfacepointattribute,     // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
7086 // float SPA_POSITION = 0;
7087 // float SPA_S_AXIS = 1;
7088 // float SPA_T_AXIS = 2;
7089 // float SPA_R_AXIS = 3; // same as SPA_NORMAL
7090 // float SPA_TEXCOORDS0 = 4;
7091 // float SPA_LIGHTMAP0_TEXCOORDS = 5;
7092 // float SPA_LIGHTMAP0_COLOR = 6;
7093 void VM_getsurfacepointattribute(prvm_prog_t *prog)
7094 {
7095         prvm_edict_t *ed;
7096         dp_model_t *model;
7097         msurface_t *surface;
7098         int pointnum;
7099         int attributetype;
7100         vec3_t result;
7101
7102         VM_SAFEPARMCOUNT(4, VM_getsurfacepointattribute);
7103         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7104         ed = PRVM_G_EDICT(OFS_PARM0);
7105         if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7106                 return;
7107         pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
7108         if (pointnum < 0 || pointnum >= surface->num_vertices)
7109                 return;
7110         attributetype = (int) PRVM_G_FLOAT(OFS_PARM3);
7111
7112         animatemodel(prog, model, ed);
7113
7114         switch( attributetype ) {
7115                 // float SPA_POSITION = 0;
7116                 case 0:
7117                         applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7118                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7119                         break;
7120                 // float SPA_S_AXIS = 1;
7121                 case 1:
7122                         applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7123                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7124                         break;
7125                 // float SPA_T_AXIS = 2;
7126                 case 2:
7127                         applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7128                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7129                         break;
7130                 // float SPA_R_AXIS = 3; // same as SPA_NORMAL
7131                 case 3:
7132                         applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7133                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7134                         break;
7135                 // float SPA_TEXCOORDS0 = 4;
7136                 case 4: {
7137                         float *texcoord = &(model->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[pointnum * 2];
7138                         result[0] = texcoord[0];
7139                         result[1] = texcoord[1];
7140                         result[2] = 0.0f;
7141                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7142                         break;
7143                 }
7144                 // float SPA_LIGHTMAP0_TEXCOORDS = 5;
7145                 case 5: {
7146                         float *texcoord = &(model->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[pointnum * 2];
7147                         result[0] = texcoord[0];
7148                         result[1] = texcoord[1];
7149                         result[2] = 0.0f;
7150                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7151                         break;
7152                 }
7153                 // float SPA_LIGHTMAP0_COLOR = 6;
7154                 case 6:
7155                         // ignore alpha for now..
7156                         VectorCopy( &(model->surfmesh.data_lightmapcolor4f + 4 * surface->num_firstvertex)[pointnum * 4], PRVM_G_VECTOR(OFS_RETURN));
7157                         break;
7158                 default:
7159                         VectorSet( PRVM_G_VECTOR(OFS_RETURN), 0.0f, 0.0f, 0.0f );
7160                         break;
7161         }
7162 }
7163 //PF_getsurfacenormal,    // #436 vector(entity e, float s) getsurfacenormal = #436;
7164 void VM_getsurfacenormal(prvm_prog_t *prog)
7165 {
7166         dp_model_t *model;
7167         msurface_t *surface;
7168         vec3_t normal;
7169         vec3_t result;
7170         VM_SAFEPARMCOUNT(2, VM_getsurfacenormal);
7171         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7172         if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7173                 return;
7174         // note: this only returns the first triangle, so it doesn't work very
7175         // well for curved surfaces or arbitrary meshes
7176         animatemodel(prog, model, PRVM_G_EDICT(OFS_PARM0));
7177         TriangleNormal((prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex), (prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex) + 3, (prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex) + 6, normal);
7178         applytransform_forward_normal(prog, normal, PRVM_G_EDICT(OFS_PARM0), result);
7179         VectorNormalize(result);
7180         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7181 }
7182 //PF_getsurfacetexture,   // #437 string(entity e, float s) getsurfacetexture = #437;
7183 void VM_getsurfacetexture(prvm_prog_t *prog)
7184 {
7185         dp_model_t *model;
7186         msurface_t *surface;
7187         VM_SAFEPARMCOUNT(2, VM_getsurfacetexture);
7188         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
7189         if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7190                 return;
7191         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, surface->texture->name);
7192 }
7193 //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
7194 void VM_getsurfacenearpoint(prvm_prog_t *prog)
7195 {
7196         int surfacenum, best;
7197         vec3_t clipped, p;
7198         vec_t dist, bestdist;
7199         prvm_edict_t *ed;
7200         dp_model_t *model;
7201         msurface_t *surface;
7202         vec3_t point;
7203         VM_SAFEPARMCOUNT(2, VM_getsurfacenearpoint);
7204         PRVM_G_FLOAT(OFS_RETURN) = -1;
7205         ed = PRVM_G_EDICT(OFS_PARM0);
7206         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), point);
7207
7208         if (!ed || ed->priv.server->free)
7209                 return;
7210         model = getmodel(prog, ed);
7211         if (!model || !model->num_surfaces)
7212                 return;
7213
7214         animatemodel(prog, model, ed);
7215
7216         applytransform_inverted(prog, point, ed, p);
7217         best = -1;
7218         bestdist = 1000000000;
7219         for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++)
7220         {
7221                 surface = model->data_surfaces + surfacenum + model->firstmodelsurface;
7222                 // first see if the nearest point on the surface's box is closer than the previous match
7223                 clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0];
7224                 clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1];
7225                 clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2];
7226                 dist = VectorLength2(clipped);
7227                 if (dist < bestdist)
7228                 {
7229                         // it is, check the nearest point on the actual geometry
7230                         clippointtosurface(prog, ed, model, surface, p, clipped);
7231                         VectorSubtract(clipped, p, clipped);
7232                         dist += VectorLength2(clipped);
7233                         if (dist < bestdist)
7234                         {
7235                                 // that's closer too, store it as the best match
7236                                 best = surfacenum;
7237                                 bestdist = dist;
7238                         }
7239                 }
7240         }
7241         PRVM_G_FLOAT(OFS_RETURN) = best;
7242 }
7243 //PF_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
7244 void VM_getsurfaceclippedpoint(prvm_prog_t *prog)
7245 {
7246         prvm_edict_t *ed;
7247         dp_model_t *model;
7248         msurface_t *surface;
7249         vec3_t p, out, inp;
7250         VM_SAFEPARMCOUNT(3, VM_getsurfaceclippedpoint);
7251         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7252         ed = PRVM_G_EDICT(OFS_PARM0);
7253         if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7254                 return;
7255         animatemodel(prog, model, ed);
7256         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), inp);
7257         applytransform_inverted(prog, inp, ed, p);
7258         clippointtosurface(prog, ed, model, surface, p, out);
7259         VectorAdd(out, PRVM_serveredictvector(ed, origin), PRVM_G_VECTOR(OFS_RETURN));
7260 }
7261
7262 //PF_getsurfacenumtriangles, // #??? float(entity e, float s) getsurfacenumtriangles = #???;
7263 void VM_getsurfacenumtriangles(prvm_prog_t *prog)
7264 {
7265        dp_model_t *model;
7266        msurface_t *surface;
7267        VM_SAFEPARMCOUNT(2, VM_getsurfacenumtriangles);
7268        // return 0 if no such surface
7269        if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7270        {
7271                PRVM_G_FLOAT(OFS_RETURN) = 0;
7272                return;
7273        }
7274
7275        PRVM_G_FLOAT(OFS_RETURN) = surface->num_triangles;
7276 }
7277 //PF_getsurfacetriangle,     // #??? vector(entity e, float s, float n) getsurfacetriangle = #???;
7278 void VM_getsurfacetriangle(prvm_prog_t *prog)
7279 {
7280        const vec3_t d = {-1, -1, -1};
7281        prvm_edict_t *ed;
7282        dp_model_t *model;
7283        msurface_t *surface;
7284        int trinum;
7285        VM_SAFEPARMCOUNT(3, VM_getsurfacetriangle);
7286        VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7287        ed = PRVM_G_EDICT(OFS_PARM0);
7288        if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7289                return;
7290        trinum = (int)PRVM_G_FLOAT(OFS_PARM2);
7291        if (trinum < 0 || trinum >= surface->num_triangles)
7292                return;
7293        // FIXME: implement rotation/scaling
7294        VectorMA(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], surface->num_firstvertex, d, PRVM_G_VECTOR(OFS_RETURN));
7295 }
7296
7297 //
7298 // physics builtins
7299 //
7300
7301 #define VM_physics_ApplyCmd(ed,f) if (!ed->priv.server->ode_body) VM_physics_newstackfunction(prog, ed, f); else World_Physics_ApplyCmd(ed, f)
7302
7303 static edict_odefunc_t *VM_physics_newstackfunction(prvm_prog_t *prog, prvm_edict_t *ed, edict_odefunc_t *f)
7304 {
7305         edict_odefunc_t *newfunc, *func;
7306
7307         newfunc = (edict_odefunc_t *)Mem_Alloc(prog->progs_mempool, sizeof(edict_odefunc_t));
7308         memcpy(newfunc, f, sizeof(edict_odefunc_t));
7309         newfunc->next = NULL;
7310         if (!ed->priv.server->ode_func)
7311                 ed->priv.server->ode_func = newfunc;
7312         else
7313         {
7314                 for (func = ed->priv.server->ode_func; func->next; func = func->next);
7315                 func->next = newfunc;
7316         }
7317         return newfunc;
7318 }
7319
7320 // void(entity e, float physics_enabled) physics_enable = #;
7321 void VM_physics_enable(prvm_prog_t *prog)
7322 {
7323         prvm_edict_t *ed;
7324         edict_odefunc_t f;
7325         
7326         VM_SAFEPARMCOUNT(2, VM_physics_enable);
7327         ed = PRVM_G_EDICT(OFS_PARM0);
7328         if (!ed)
7329         {
7330                 if (developer.integer > 0)
7331                         VM_Warning(prog, "VM_physics_enable: null entity!\n");
7332                 return;
7333         }
7334         // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
7335         if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
7336         {
7337                 VM_Warning(prog, "VM_physics_enable: entity is not MOVETYPE_PHYSICS!\n");
7338                 return;
7339         }
7340         f.type = PRVM_G_FLOAT(OFS_PARM1) == 0 ? ODEFUNC_DISABLE : ODEFUNC_ENABLE;
7341         VM_physics_ApplyCmd(ed, &f);
7342 }
7343
7344 // void(entity e, vector force, vector relative_ofs) physics_addforce = #;
7345 void VM_physics_addforce(prvm_prog_t *prog)
7346 {
7347         prvm_edict_t *ed;
7348         edict_odefunc_t f;
7349         
7350         VM_SAFEPARMCOUNT(3, VM_physics_addforce);
7351         ed = PRVM_G_EDICT(OFS_PARM0);
7352         if (!ed)
7353         {
7354                 if (developer.integer > 0)
7355                         VM_Warning(prog, "VM_physics_addforce: null entity!\n");
7356                 return;
7357         }
7358         // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
7359         if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
7360         {
7361                 VM_Warning(prog, "VM_physics_addforce: entity is not MOVETYPE_PHYSICS!\n");
7362                 return;
7363         }
7364         f.type = ODEFUNC_FORCE;
7365         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
7366         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), f.v2);
7367         VM_physics_ApplyCmd(ed, &f);
7368 }
7369
7370 // void(entity e, vector torque) physics_addtorque = #;
7371 void VM_physics_addtorque(prvm_prog_t *prog)
7372 {
7373         prvm_edict_t *ed;
7374         edict_odefunc_t f;
7375         
7376         VM_SAFEPARMCOUNT(2, VM_physics_addtorque);
7377         ed = PRVM_G_EDICT(OFS_PARM0);
7378         if (!ed)
7379         {
7380                 if (developer.integer > 0)
7381                         VM_Warning(prog, "VM_physics_addtorque: null entity!\n");
7382                 return;
7383         }
7384         // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
7385         if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
7386         {
7387                 VM_Warning(prog, "VM_physics_addtorque: entity is not MOVETYPE_PHYSICS!\n");
7388                 return;
7389         }
7390         f.type = ODEFUNC_TORQUE;
7391         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
7392         VM_physics_ApplyCmd(ed, &f);
7393 }
7394
7395 extern cvar_t prvm_coverage;
7396 void VM_coverage(prvm_prog_t *prog)
7397 {
7398         VM_SAFEPARMCOUNT(0, VM_coverage);
7399         if (prog->explicit_profile[prog->xstatement]++ == 0 && (prvm_coverage.integer & 2))
7400                 PRVM_ExplicitCoverageEvent(prog, prog->xfunction, prog->xstatement);
7401 }