]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_cmds.c
mvm_cmds: Remove duplicate clientcommand builtin invalidly used by the menu
[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 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 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 qbool 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 qbool 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 & CF_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 & CF_ARCHIVE)
688                 ret |= 2; // CVAR_TYPE_SAVED
689         if(cvar->flags & CF_PRIVATE)
690                 ret |= 4; // CVAR_TYPE_PRIVATE
691         if(!(cvar->flags & CF_ALLOCATED))
692                 ret |= 8; // CVAR_TYPE_ENGINE
693         if(cvar->description != cvar_dummy_description)
694                 ret |= 16; // CVAR_TYPE_HASDESCRIPTION
695         if(cvar->flags & CF_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 =      !host_isclient.integer ? &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 > CF_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         mdef_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         mdef_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         mdef_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         mdef_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_tokenize
2562
2563 float tokenize(string s)
2564 =========
2565 */
2566 //float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many
2567 //this function originally written by KrimZon, made shorter by LadyHavoc
2568 //20040203: rewritten by LadyHavoc (no longer uses allocations)
2569 static int num_tokens = 0;
2570 static int tokens[VM_STRINGTEMP_LENGTH / 2];
2571 static int tokens_startpos[VM_STRINGTEMP_LENGTH / 2];
2572 static int tokens_endpos[VM_STRINGTEMP_LENGTH / 2];
2573 static char tokenize_string[VM_STRINGTEMP_LENGTH];
2574 void VM_tokenize (prvm_prog_t *prog)
2575 {
2576         const char *p;
2577
2578         VM_SAFEPARMCOUNT(1,VM_tokenize);
2579
2580         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2581         p = tokenize_string;
2582
2583         num_tokens = 0;
2584         for(;;)
2585         {
2586                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2587                         break;
2588
2589                 // skip whitespace here to find token start pos
2590                 while(*p && ISWHITESPACE(*p))
2591                         ++p;
2592
2593                 tokens_startpos[num_tokens] = p - tokenize_string;
2594                 if(!COM_ParseToken_VM_Tokenize(&p, false))
2595                         break;
2596                 tokens_endpos[num_tokens] = p - tokenize_string;
2597                 tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
2598                 ++num_tokens;
2599         }
2600
2601         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2602 }
2603
2604 //float(string s) tokenize = #514; // takes apart a string into individal words (access them with argv), returns how many
2605 void VM_tokenize_console (prvm_prog_t *prog)
2606 {
2607         const char *p;
2608
2609         VM_SAFEPARMCOUNT(1, VM_tokenize_console);
2610
2611         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2612         p = tokenize_string;
2613
2614         num_tokens = 0;
2615         for(;;)
2616         {
2617                 if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
2618                         break;
2619
2620                 // skip whitespace here to find token start pos
2621                 while(*p && ISWHITESPACE(*p))
2622                         ++p;
2623
2624                 tokens_startpos[num_tokens] = p - tokenize_string;
2625                 if(!COM_ParseToken_Console(&p))
2626                         break;
2627                 tokens_endpos[num_tokens] = p - tokenize_string;
2628                 tokens[num_tokens] = PRVM_SetTempString(prog, com_token);
2629                 ++num_tokens;
2630         }
2631
2632         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2633 }
2634
2635 /*
2636 =========
2637 VM_tokenizebyseparator
2638
2639 float tokenizebyseparator(string s, string separator1, ...)
2640 =========
2641 */
2642 //float(string s, string separator1, ...) tokenizebyseparator = #479; // takes apart a string into individal words (access them with argv), returns how many
2643 //this function returns the token preceding each instance of a separator (of
2644 //which there can be multiple), and the text following the last separator
2645 //useful for parsing certain kinds of data like IP addresses
2646 //example:
2647 //numnumbers = tokenizebyseparator("10.1.2.3", ".");
2648 //returns 4 and the tokens "10" "1" "2" "3".
2649 void VM_tokenizebyseparator (prvm_prog_t *prog)
2650 {
2651         int j, k;
2652         int numseparators;
2653         int separatorlen[7];
2654         const char *separators[7];
2655         const char *p, *p0;
2656         const char *token;
2657         char tokentext[MAX_INPUTLINE];
2658
2659         VM_SAFEPARMCOUNTRANGE(2, 8,VM_tokenizebyseparator);
2660
2661         strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
2662         p = tokenize_string;
2663
2664         numseparators = 0;
2665         for (j = 1;j < prog->argc;j++)
2666         {
2667                 // skip any blank separator strings
2668                 const char *s = PRVM_G_STRING(OFS_PARM0+j*3);
2669                 if (!s[0])
2670                         continue;
2671                 separators[numseparators] = s;
2672                 separatorlen[numseparators] = (int)strlen(s);
2673                 numseparators++;
2674         }
2675
2676         num_tokens = 0;
2677         j = 0;
2678
2679         while (num_tokens < (int)(sizeof(tokens)/sizeof(tokens[0])))
2680         {
2681                 token = tokentext + j;
2682                 tokens_startpos[num_tokens] = p - tokenize_string;
2683                 p0 = p;
2684                 while (*p)
2685                 {
2686                         for (k = 0;k < numseparators;k++)
2687                         {
2688                                 if (!strncmp(p, separators[k], separatorlen[k]))
2689                                 {
2690                                         p += separatorlen[k];
2691                                         break;
2692                                 }
2693                         }
2694                         if (k < numseparators)
2695                                 break;
2696                         if (j < (int)sizeof(tokentext)-1)
2697                                 tokentext[j++] = *p;
2698                         p++;
2699                         p0 = p;
2700                 }
2701                 tokens_endpos[num_tokens] = p0 - tokenize_string;
2702                 if (j >= (int)sizeof(tokentext))
2703                         break;
2704                 tokentext[j++] = 0;
2705                 tokens[num_tokens++] = PRVM_SetTempString(prog, token);
2706                 if (!*p)
2707                         break;
2708         }
2709
2710         PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
2711 }
2712
2713 //string(float n) argv = #442; // returns a word from the tokenized string (returns nothing for an invalid index)
2714 //this function originally written by KrimZon, made shorter by LadyHavoc
2715 void VM_argv (prvm_prog_t *prog)
2716 {
2717         int token_num;
2718
2719         VM_SAFEPARMCOUNT(1,VM_argv);
2720
2721         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2722
2723         if(token_num < 0)
2724                 token_num += num_tokens;
2725
2726         if (token_num >= 0 && token_num < num_tokens)
2727                 PRVM_G_INT(OFS_RETURN) = tokens[token_num];
2728         else
2729                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
2730 }
2731
2732 //float(float n) argv_start_index = #515; // returns the start index of a token
2733 void VM_argv_start_index (prvm_prog_t *prog)
2734 {
2735         int token_num;
2736
2737         VM_SAFEPARMCOUNT(1,VM_argv);
2738
2739         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2740
2741         if(token_num < 0)
2742                 token_num += num_tokens;
2743
2744         if (token_num >= 0 && token_num < num_tokens)
2745                 PRVM_G_FLOAT(OFS_RETURN) = tokens_startpos[token_num];
2746         else
2747                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2748 }
2749
2750 //float(float n) argv_end_index = #516; // returns the end index of a token
2751 void VM_argv_end_index (prvm_prog_t *prog)
2752 {
2753         int token_num;
2754
2755         VM_SAFEPARMCOUNT(1,VM_argv);
2756
2757         token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
2758
2759         if(token_num < 0)
2760                 token_num += num_tokens;
2761
2762         if (token_num >= 0 && token_num < num_tokens)
2763                 PRVM_G_FLOAT(OFS_RETURN) = tokens_endpos[token_num];
2764         else
2765                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2766 }
2767
2768 /*
2769 =========
2770 VM_isserver
2771
2772 float   isserver()
2773 =========
2774 */
2775 void VM_isserver(prvm_prog_t *prog)
2776 {
2777         VM_SAFEPARMCOUNT(0, VM_isserver);
2778
2779         PRVM_G_FLOAT(OFS_RETURN) = sv.active;
2780 }
2781
2782 /*
2783 =========
2784 VM_clientcount
2785
2786 float   clientcount()
2787 =========
2788 */
2789 void VM_clientcount(prvm_prog_t *prog)
2790 {
2791         VM_SAFEPARMCOUNT(0,VM_clientcount);
2792
2793         PRVM_G_FLOAT(OFS_RETURN) = svs.maxclients;
2794 }
2795
2796 /*
2797 =========
2798 VM_clientstate
2799
2800 float   clientstate()
2801 =========
2802 */
2803 void VM_clientstate(prvm_prog_t *prog)
2804 {
2805         VM_SAFEPARMCOUNT(0,VM_clientstate);
2806
2807
2808         switch( cls.state ) {
2809                 case ca_uninitialized:
2810                 case ca_dedicated:
2811                         PRVM_G_FLOAT(OFS_RETURN) = 0;
2812                         break;
2813                 case ca_disconnected:
2814                         PRVM_G_FLOAT(OFS_RETURN) = 1;
2815                         break;
2816                 case ca_connected:
2817                         PRVM_G_FLOAT(OFS_RETURN) = 2;
2818                         break;
2819                 default:
2820                         // should never be reached!
2821                         break;
2822         }
2823 }
2824
2825 /*
2826 =========
2827 VM_getostype
2828
2829 float   getostype(prvm_prog_t *prog)
2830 =========
2831 */ // not used at the moment -> not included in the common list
2832 void VM_getostype(prvm_prog_t *prog)
2833 {
2834         VM_SAFEPARMCOUNT(0,VM_getostype);
2835
2836         /*
2837         OS_WINDOWS
2838         OS_LINUX
2839         OS_MAC - not supported
2840         */
2841
2842 #ifdef WIN32
2843         PRVM_G_FLOAT(OFS_RETURN) = 0;
2844 #elif defined(MACOSX)
2845         PRVM_G_FLOAT(OFS_RETURN) = 2;
2846 #else
2847         PRVM_G_FLOAT(OFS_RETURN) = 1;
2848 #endif
2849 }
2850
2851 /*
2852 =========
2853 VM_gettime
2854
2855 float   gettime(prvm_prog_t *prog)
2856 =========
2857 */
2858 float CDAudio_GetPosition(void);
2859 void VM_gettime(prvm_prog_t *prog)
2860 {
2861         int timer_index;
2862
2863         VM_SAFEPARMCOUNTRANGE(0,1,VM_gettime);
2864
2865         if(prog->argc == 0)
2866         {
2867                 PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) host.realtime;
2868         }
2869         else
2870         {
2871                 timer_index = (int) PRVM_G_FLOAT(OFS_PARM0);
2872                 switch(timer_index)
2873                 {
2874                         case 0: // GETTIME_FRAMESTART
2875                                 PRVM_G_FLOAT(OFS_RETURN) = host.realtime;
2876                                 break;
2877                         case 1: // GETTIME_REALTIME
2878                                 PRVM_G_FLOAT(OFS_RETURN) = Sys_DirtyTime();
2879                                 break;
2880                         case 2: // GETTIME_HIRES
2881                                 PRVM_G_FLOAT(OFS_RETURN) = (Sys_DirtyTime() - host.dirtytime);
2882                                 break;
2883                         case 3: // GETTIME_UPTIME
2884                                 PRVM_G_FLOAT(OFS_RETURN) = host.realtime;
2885                                 break;
2886                         case 4: // GETTIME_CDTRACK
2887                                 PRVM_G_FLOAT(OFS_RETURN) = CDAudio_GetPosition();
2888                                 break;
2889                         default:
2890                                 VM_Warning(prog, "VM_gettime: %s: unsupported timer specified, returning realtime\n", prog->name);
2891                                 PRVM_G_FLOAT(OFS_RETURN) = host.realtime;
2892                                 break;
2893                 }
2894         }
2895 }
2896
2897 /*
2898 =========
2899 VM_getsoundtime
2900
2901 float   getsoundtime(prvm_prog_t *prog)
2902 =========
2903 */
2904
2905 void VM_getsoundtime (prvm_prog_t *prog)
2906 {
2907         int entnum, entchannel;
2908         VM_SAFEPARMCOUNT(2,VM_getsoundtime);
2909
2910         if (prog == SVVM_prog)
2911                 entnum = PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0));
2912         else if (prog == CLVM_prog)
2913                 entnum = MAX_EDICTS + PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0));
2914         else
2915         {
2916                 VM_Warning(prog, "VM_getsoundtime: %s: not supported on this progs\n", prog->name);
2917                 PRVM_G_FLOAT(OFS_RETURN) = -1;
2918                 return;
2919         }
2920         entchannel = (int)PRVM_G_FLOAT(OFS_PARM1);
2921         entchannel = CHAN_USER2ENGINE(entchannel);
2922         if (!IS_CHAN(entchannel))
2923                 VM_Warning(prog, "VM_getsoundtime: %s: bad channel %i\n", prog->name, entchannel);
2924         PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t)S_GetEntChannelPosition(entnum, entchannel);
2925 }
2926
2927 /*
2928 =========
2929 VM_GetSoundLen
2930
2931 string  soundlength (string sample)
2932 =========
2933 */
2934 void VM_soundlength (prvm_prog_t *prog)
2935 {
2936         const char *s;
2937
2938         VM_SAFEPARMCOUNT(1, VM_soundlength);
2939
2940         s = PRVM_G_STRING(OFS_PARM0);
2941         PRVM_G_FLOAT(OFS_RETURN) = S_SoundLength(s);
2942 }
2943
2944 /*
2945 =========
2946 VM_loadfromdata
2947
2948 loadfromdata(string data)
2949 =========
2950 */
2951 void VM_loadfromdata(prvm_prog_t *prog)
2952 {
2953         VM_SAFEPARMCOUNT(1, VM_loadfromdata);
2954
2955         PRVM_ED_LoadFromFile(prog, PRVM_G_STRING(OFS_PARM0));
2956 }
2957
2958 /*
2959 ========================
2960 VM_parseentitydata
2961
2962 parseentitydata(entity ent, string data)
2963 ========================
2964 */
2965 void VM_parseentitydata(prvm_prog_t *prog)
2966 {
2967         prvm_edict_t *ent;
2968         const char *data;
2969
2970         VM_SAFEPARMCOUNT(2, VM_parseentitydata);
2971
2972         // get edict and test it
2973         ent = PRVM_G_EDICT(OFS_PARM0);
2974         if (ent->priv.required->free)
2975                 prog->error_cmd("VM_parseentitydata: %s: Can only set already spawned entities (entity %i is free)!", prog->name, PRVM_NUM_FOR_EDICT(ent));
2976
2977         data = PRVM_G_STRING(OFS_PARM1);
2978
2979         // parse the opening brace
2980         if (!COM_ParseToken_Simple(&data, false, false, true) || com_token[0] != '{' )
2981                 prog->error_cmd("VM_parseentitydata: %s: Couldn't parse entity data:\n%s", prog->name, data );
2982
2983         PRVM_ED_ParseEdict (prog, data, ent);
2984 }
2985
2986 /*
2987 =========
2988 VM_loadfromfile
2989
2990 loadfromfile(string file)
2991 =========
2992 */
2993 void VM_loadfromfile(prvm_prog_t *prog)
2994 {
2995         const char *filename;
2996         char *data;
2997
2998         VM_SAFEPARMCOUNT(1,VM_loadfromfile);
2999
3000         filename = PRVM_G_STRING(OFS_PARM0);
3001         if (FS_CheckNastyPath(filename, false))
3002         {
3003                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3004                 VM_Warning(prog, "VM_loadfromfile: %s dangerous or non-portable filename \"%s\" not allowed. (contains : or \\ or begins with .. or /)\n", prog->name, filename);
3005                 return;
3006         }
3007
3008         // not conform with VM_fopen
3009         data = (char *)FS_LoadFile(filename, tempmempool, false, NULL);
3010         if (data == NULL)
3011                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3012
3013         PRVM_ED_LoadFromFile(prog, data);
3014
3015         if(data)
3016                 Mem_Free(data);
3017 }
3018
3019
3020 /*
3021 =========
3022 VM_modulo
3023
3024 float   mod(float val, float m)
3025 =========
3026 */
3027 void VM_modulo(prvm_prog_t *prog)
3028 {
3029         prvm_int_t val, m;
3030         VM_SAFEPARMCOUNT(2, VM_modulo);
3031
3032         val = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM0);
3033         m       = (prvm_int_t) PRVM_G_FLOAT(OFS_PARM1);
3034
3035         PRVM_G_FLOAT(OFS_RETURN) = (prvm_vec_t) (val % m);
3036 }
3037
3038 static void VM_Search_Init(prvm_prog_t *prog)
3039 {
3040         int i;
3041         for (i = 0;i < PRVM_MAX_OPENSEARCHES;i++)
3042                 prog->opensearches[i] = NULL;
3043 }
3044
3045 static void VM_Search_Reset(prvm_prog_t *prog)
3046 {
3047         int i;
3048         // reset the fssearch list
3049         for(i = 0; i < PRVM_MAX_OPENSEARCHES; i++)
3050         {
3051                 if(prog->opensearches[i])
3052                         FS_FreeSearch(prog->opensearches[i]);
3053                 prog->opensearches[i] = NULL;
3054         }
3055 }
3056
3057 /*
3058 =========
3059 VM_search_begin
3060
3061 float search_begin(string pattern, float caseinsensitive, float quiet[, string packfile])
3062 =========
3063 */
3064 void VM_search_begin(prvm_prog_t *prog)
3065 {
3066         int handle;
3067         const char *packfile = NULL, *pattern;
3068         int caseinsens, quiet;
3069
3070         VM_SAFEPARMCOUNTRANGE(3, 4, VM_search_begin);
3071
3072         pattern = PRVM_G_STRING(OFS_PARM0);
3073
3074         VM_CheckEmptyString(prog, pattern);
3075
3076         caseinsens = (int)PRVM_G_FLOAT(OFS_PARM1);
3077         quiet = (int)PRVM_G_FLOAT(OFS_PARM2);
3078
3079         // optional packfile parameter (DP_QC_FS_SEARCH_PACKFILE)
3080         if(prog->argc >= 4)
3081                 packfile = PRVM_G_STRING(OFS_PARM3);
3082
3083         for(handle = 0; handle < PRVM_MAX_OPENSEARCHES; handle++)
3084                 if(!prog->opensearches[handle])
3085                         break;
3086
3087         if(handle >= PRVM_MAX_OPENSEARCHES)
3088         {
3089                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3090                 VM_Warning(prog, "VM_search_begin: %s ran out of search handles (%i)\n", prog->name, PRVM_MAX_OPENSEARCHES);
3091                 return;
3092         }
3093
3094         if(!(prog->opensearches[handle] = FS_Search(pattern,caseinsens, quiet, packfile)))
3095                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3096         else
3097         {
3098                 prog->opensearches_origin[handle] = PRVM_AllocationOrigin(prog);
3099                 PRVM_G_FLOAT(OFS_RETURN) = handle;
3100         }
3101 }
3102
3103 /*
3104 =========
3105 VM_search_end
3106
3107 void    search_end(float handle)
3108 =========
3109 */
3110 void VM_search_end(prvm_prog_t *prog)
3111 {
3112         int handle;
3113         VM_SAFEPARMCOUNT(1, VM_search_end);
3114
3115         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
3116
3117         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
3118         {
3119                 VM_Warning(prog, "VM_search_end: invalid handle %i used in %s\n", handle, prog->name);
3120                 return;
3121         }
3122         if(prog->opensearches[handle] == NULL)
3123         {
3124                 VM_Warning(prog, "VM_search_end: no such handle %i in %s\n", handle, prog->name);
3125                 return;
3126         }
3127
3128         FS_FreeSearch(prog->opensearches[handle]);
3129         prog->opensearches[handle] = NULL;
3130         if(prog->opensearches_origin[handle])
3131                 PRVM_Free((char *)prog->opensearches_origin[handle]);
3132 }
3133
3134 /*
3135 =========
3136 VM_search_getsize
3137
3138 float   search_getsize(float handle)
3139 =========
3140 */
3141 void VM_search_getsize(prvm_prog_t *prog)
3142 {
3143         int handle;
3144         VM_SAFEPARMCOUNT(1, VM_search_getsize);
3145
3146         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
3147
3148         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
3149         {
3150                 VM_Warning(prog, "VM_search_getsize: invalid handle %i used in %s\n", handle, prog->name);
3151                 return;
3152         }
3153         if(prog->opensearches[handle] == NULL)
3154         {
3155                 VM_Warning(prog, "VM_search_getsize: no such handle %i in %s\n", handle, prog->name);
3156                 return;
3157         }
3158
3159         PRVM_G_FLOAT(OFS_RETURN) = prog->opensearches[handle]->numfilenames;
3160 }
3161
3162 /*
3163 =========
3164 VM_search_getfilename
3165
3166 string  search_getfilename(float handle, float num)
3167 =========
3168 */
3169 void VM_search_getfilename(prvm_prog_t *prog)
3170 {
3171         int handle, filenum;
3172         VM_SAFEPARMCOUNT(2, VM_search_getfilename);
3173
3174         handle = (int)PRVM_G_FLOAT(OFS_PARM0);
3175         filenum = (int)PRVM_G_FLOAT(OFS_PARM1);
3176
3177         if(handle < 0 || handle >= PRVM_MAX_OPENSEARCHES)
3178         {
3179                 VM_Warning(prog, "VM_search_getfilename: invalid handle %i used in %s\n", handle, prog->name);
3180                 return;
3181         }
3182         if(prog->opensearches[handle] == NULL)
3183         {
3184                 VM_Warning(prog, "VM_search_getfilename: no such handle %i in %s\n", handle, prog->name);
3185                 return;
3186         }
3187         if(filenum < 0 || filenum >= prog->opensearches[handle]->numfilenames)
3188         {
3189                 VM_Warning(prog, "VM_search_getfilename: invalid filenum %i in %s\n", filenum, prog->name);
3190                 return;
3191         }
3192
3193         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, prog->opensearches[handle]->filenames[filenum]);
3194 }
3195
3196 /*
3197 =========
3198 VM_chr
3199
3200 string  chr(float ascii)
3201 =========
3202 */
3203 void VM_chr(prvm_prog_t *prog)
3204 {
3205         /*
3206         char tmp[2];
3207         VM_SAFEPARMCOUNT(1, VM_chr);
3208
3209         tmp[0] = (unsigned char) PRVM_G_FLOAT(OFS_PARM0);
3210         tmp[1] = 0;
3211
3212         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp);
3213         */
3214         
3215         char tmp[8];
3216         int len;
3217         VM_SAFEPARMCOUNT(1, VM_chr);
3218
3219         len = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0), tmp, sizeof(tmp));
3220         tmp[len] = 0;
3221         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, tmp);
3222 }
3223
3224 //=============================================================================
3225 // Draw builtins (client & menu)
3226
3227 /*
3228 =========
3229 VM_iscachedpic
3230
3231 float   iscachedpic(string pic)
3232 =========
3233 */
3234 void VM_iscachedpic(prvm_prog_t *prog)
3235 {
3236         VM_SAFEPARMCOUNT(1,VM_iscachedpic);
3237
3238         // drawq hasnt such a function, thus always return true
3239         PRVM_G_FLOAT(OFS_RETURN) = false;
3240 }
3241
3242 /*
3243 =========
3244 VM_precache_pic
3245
3246 string  precache_pic(string pic)
3247 =========
3248 */
3249 #define PRECACHE_PIC_FROMWAD 1 /* FTEQW, not supported here */
3250 #define PRECACHE_PIC_NOTPERSISTENT 2
3251 //#define PRECACHE_PIC_NOCLAMP 4
3252 #define PRECACHE_PIC_MIPMAP 8
3253 void VM_precache_pic(prvm_prog_t *prog)
3254 {
3255         const char      *s;
3256         int flags = CACHEPICFLAG_FAILONMISSING;
3257
3258         VM_SAFEPARMCOUNTRANGE(1, 2, VM_precache_pic);
3259
3260         s = PRVM_G_STRING(OFS_PARM0);
3261         PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
3262         VM_CheckEmptyString(prog, s);
3263
3264         if(prog->argc >= 2)
3265         {
3266                 int f = PRVM_G_FLOAT(OFS_PARM1);
3267                 if(f & PRECACHE_PIC_NOTPERSISTENT)
3268                         flags |= CACHEPICFLAG_NOTPERSISTENT;
3269                 //if(f & PRECACHE_PIC_NOCLAMP)
3270                 //      flags |= CACHEPICFLAG_NOCLAMP;
3271                 if(f & PRECACHE_PIC_MIPMAP)
3272                         flags |= CACHEPICFLAG_MIPMAP;
3273         }
3274
3275         if( !Draw_IsPicLoaded(Draw_CachePic_Flags(s, flags | CACHEPICFLAG_QUIET)) )
3276                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
3277 }
3278
3279 /*
3280 =========
3281 VM_freepic
3282
3283 freepic(string s)
3284 =========
3285 */
3286 void VM_freepic(prvm_prog_t *prog)
3287 {
3288         const char *s;
3289
3290         VM_SAFEPARMCOUNT(1,VM_freepic);
3291
3292         s = PRVM_G_STRING(OFS_PARM0);
3293         VM_CheckEmptyString(prog, s);
3294
3295         Draw_FreePic(s);
3296 }
3297
3298 static void getdrawfontscale(prvm_prog_t *prog, float *sx, float *sy)
3299 {
3300         vec3_t v;
3301         *sx = *sy = 1;
3302         VectorCopy(PRVM_drawglobalvector(drawfontscale), v);
3303         if(VectorLength2(v) > 0)
3304         {
3305                 *sx = v[0];
3306                 *sy = v[1];
3307         }
3308 }
3309
3310 static dp_font_t *getdrawfont(prvm_prog_t *prog)
3311 {
3312         int f = (int) PRVM_drawglobalfloat(drawfont);
3313         if(f < 0 || f >= dp_fonts.maxsize)
3314                 return FONT_DEFAULT;
3315         return &dp_fonts.f[f];
3316 }
3317
3318 /*
3319 =========
3320 VM_drawcharacter
3321
3322 float   drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag)
3323 =========
3324 */
3325 void VM_drawcharacter(prvm_prog_t *prog)
3326 {
3327         prvm_vec_t *pos,*scale,*rgb;
3328         char   character;
3329         int flag;
3330         float sx, sy;
3331         VM_SAFEPARMCOUNT(6,VM_drawcharacter);
3332
3333         // polygonbegin without draw2d arg has to guess
3334         prog->polygonbegin_guess2d = true;
3335
3336         character = (char) PRVM_G_FLOAT(OFS_PARM1);
3337         if(character == 0)
3338         {
3339                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3340                 VM_Warning(prog, "VM_drawcharacter: %s passed null character !\n",prog->name);
3341                 return;
3342         }
3343
3344         pos = PRVM_G_VECTOR(OFS_PARM0);
3345         scale = PRVM_G_VECTOR(OFS_PARM2);
3346         rgb = PRVM_G_VECTOR(OFS_PARM3);
3347         flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3348
3349         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3350         {
3351                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3352                 VM_Warning(prog, "VM_drawcharacter: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3353                 return;
3354         }
3355
3356         if(pos[2] || scale[2])
3357                 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")));
3358
3359         if(!scale[0] || !scale[1])
3360         {
3361                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3362                 VM_Warning(prog, "VM_drawcharacter: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3363                 return;
3364         }
3365
3366         getdrawfontscale(prog, &sx, &sy);
3367         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));
3368         PRVM_G_FLOAT(OFS_RETURN) = 1;
3369 }
3370
3371 /*
3372 =========
3373 VM_drawstring
3374
3375 float   drawstring(vector position, string text, vector scale, vector rgb, float alpha[, float flag])
3376 =========
3377 */
3378 void VM_drawstring(prvm_prog_t *prog)
3379 {
3380         prvm_vec_t *pos,*scale,*rgb;
3381         const char  *string;
3382         int flag = 0;
3383         float sx, sy;
3384         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawstring);
3385
3386         // polygonbegin without draw2d arg has to guess
3387         prog->polygonbegin_guess2d = true;
3388
3389         string = PRVM_G_STRING(OFS_PARM1);
3390         pos = PRVM_G_VECTOR(OFS_PARM0);
3391         scale = PRVM_G_VECTOR(OFS_PARM2);
3392         rgb = PRVM_G_VECTOR(OFS_PARM3);
3393         if (prog->argc >= 6)
3394                 flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3395
3396         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3397         {
3398                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3399                 VM_Warning(prog, "VM_drawstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3400                 return;
3401         }
3402
3403         if(!scale[0] || !scale[1])
3404         {
3405                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3406                 VM_Warning(prog, "VM_drawstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3407                 return;
3408         }
3409
3410         if(pos[2] || scale[2])
3411                 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")));
3412
3413         getdrawfontscale(prog, &sx, &sy);
3414         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));
3415         //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);
3416         PRVM_G_FLOAT(OFS_RETURN) = 1;
3417 }
3418
3419 /*
3420 =========
3421 VM_drawcolorcodedstring
3422
3423 float   drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag)
3424 /
3425 float   drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag)
3426 =========
3427 */
3428 void VM_drawcolorcodedstring(prvm_prog_t *prog)
3429 {
3430         prvm_vec_t *pos, *scale;
3431         const char  *string;
3432         int flag;
3433         vec3_t rgb;
3434         float sx, sy, alpha;
3435
3436         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawcolorcodedstring);
3437
3438         // polygonbegin without draw2d arg has to guess
3439         prog->polygonbegin_guess2d = true;
3440
3441         if (prog->argc == 6) // full 6 parms, like normal drawstring
3442         {
3443                 pos = PRVM_G_VECTOR(OFS_PARM0);
3444                 string = PRVM_G_STRING(OFS_PARM1);
3445                 scale = PRVM_G_VECTOR(OFS_PARM2);
3446                 VectorCopy(PRVM_G_VECTOR(OFS_PARM3), rgb); 
3447                 alpha = PRVM_G_FLOAT(OFS_PARM4);
3448                 flag = (int)PRVM_G_FLOAT(OFS_PARM5);
3449         }
3450         else
3451         {
3452                 pos = PRVM_G_VECTOR(OFS_PARM0);
3453                 string = PRVM_G_STRING(OFS_PARM1);
3454                 scale = PRVM_G_VECTOR(OFS_PARM2);
3455                 rgb[0] = 1.0;
3456                 rgb[1] = 1.0;
3457                 rgb[2] = 1.0;
3458                 alpha = PRVM_G_FLOAT(OFS_PARM3);
3459                 flag = (int)PRVM_G_FLOAT(OFS_PARM4);
3460         }
3461
3462         if(flag < DRAWFLAG_NORMAL || flag >= DRAWFLAG_NUMFLAGS)
3463         {
3464                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3465                 VM_Warning(prog, "VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3466                 return;
3467         }
3468
3469         if(!scale[0] || !scale[1])
3470         {
3471                 PRVM_G_FLOAT(OFS_RETURN) = -3;
3472                 VM_Warning(prog, "VM_drawcolorcodedstring: scale %s is null !\n", (scale[0] == 0) ? ((scale[1] == 0) ? "x and y" : "x") : "y");
3473                 return;
3474         }
3475
3476         if(pos[2] || scale[2])
3477                 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")));
3478
3479         getdrawfontscale(prog, &sx, &sy);
3480         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));
3481         if (prog->argc == 6) // also return vector of last color
3482                 VectorCopy(DrawQ_Color, PRVM_G_VECTOR(OFS_RETURN));
3483         else
3484                 PRVM_G_FLOAT(OFS_RETURN) = 1;
3485 }
3486 /*
3487 =========
3488 VM_stringwidth
3489
3490 float   stringwidth(string text, float allowColorCodes, float size)
3491 =========
3492 */
3493 void VM_stringwidth(prvm_prog_t *prog)
3494 {
3495         const char  *string;
3496         vec2_t szv;
3497         float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
3498         int colors;
3499         float sx, sy;
3500         size_t maxlen = 0;
3501         VM_SAFEPARMCOUNTRANGE(2, 3, VM_stringwidth);
3502
3503         getdrawfontscale(prog, &sx, &sy);
3504         if(prog->argc == 3)
3505         {
3506                 Vector2Copy(PRVM_G_VECTOR(OFS_PARM2), szv);
3507                 mult = 1;
3508         }
3509         else
3510         {
3511                 // we want the width for 8x8 font size, divided by 8
3512                 Vector2Set(szv, 8, 8);
3513                 mult = 0.125;
3514                 // to make sure snapping is turned off, ALWAYS use a nontrivial scale in this case
3515                 if(sx >= 0.9 && sx <= 1.1)
3516                 {
3517                         mult *= 2;
3518                         sx /= 2;
3519                         sy /= 2;
3520                 }
3521         }
3522
3523         string = PRVM_G_STRING(OFS_PARM0);
3524         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
3525
3526         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_UntilWidth_TrackColors_Scale(string, &maxlen, szv[0], szv[1], sx, sy, NULL, !colors, getdrawfont(prog), 1000000000) * mult;
3527 /*
3528         if(prog->argc == 3)
3529         {
3530                 mult = sz = PRVM_G_FLOAT(OFS_PARM2);
3531         }
3532         else
3533         {
3534                 sz = 8;
3535                 mult = 1;
3536         }
3537
3538         string = PRVM_G_STRING(OFS_PARM0);
3539         colors = (int)PRVM_G_FLOAT(OFS_PARM1);
3540
3541         PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw
3542 */
3543 }
3544
3545 /*
3546 =========
3547 VM_findfont
3548
3549 float findfont(string s)
3550 =========
3551 */
3552
3553 static float getdrawfontnum(const char *fontname)
3554 {
3555         int i;
3556
3557         for(i = 0; i < dp_fonts.maxsize; ++i)
3558                 if(!strcmp(dp_fonts.f[i].title, fontname))
3559                         return i;
3560         return -1;
3561 }
3562
3563 void VM_findfont(prvm_prog_t *prog)
3564 {
3565         VM_SAFEPARMCOUNT(1,VM_findfont);
3566         PRVM_G_FLOAT(OFS_RETURN) = getdrawfontnum(PRVM_G_STRING(OFS_PARM0));
3567 }
3568
3569 /*
3570 =========
3571 VM_loadfont
3572
3573 float loadfont(string fontname, string fontmaps, string sizes, float slot)
3574 =========
3575 */
3576
3577 void VM_loadfont(prvm_prog_t *prog)
3578 {
3579         const char *fontname, *filelist, *sizes, *c, *cm;
3580         char mainfont[MAX_QPATH];
3581         int i, numsizes;
3582         float sz, scale, voffset;
3583         dp_font_t *f;
3584
3585         VM_SAFEPARMCOUNTRANGE(3,6,VM_loadfont);
3586
3587         fontname = PRVM_G_STRING(OFS_PARM0);
3588         if (!fontname[0])
3589                 fontname = "default";
3590
3591         filelist = PRVM_G_STRING(OFS_PARM1);
3592         if (!filelist[0])
3593                 filelist = "gfx/conchars";
3594
3595         sizes = PRVM_G_STRING(OFS_PARM2);
3596         if (!sizes[0])
3597                 sizes = "10";
3598
3599         // find a font
3600         f = NULL;
3601         if (prog->argc >= 4)
3602         {
3603                 i = PRVM_G_FLOAT(OFS_PARM3);
3604                 if (i >= 0 && i < dp_fonts.maxsize)
3605                 {
3606                         f = &dp_fonts.f[i];
3607                         strlcpy(f->title, fontname, sizeof(f->title)); // replace name
3608                 }
3609         }
3610         if (!f)
3611                 f = FindFont(fontname, true);
3612         if (!f)
3613         {
3614                 PRVM_G_FLOAT(OFS_RETURN) = -1;
3615                 return; // something go wrong
3616         }
3617
3618         memset(f->fallbacks, 0, sizeof(f->fallbacks));
3619         memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
3620
3621         // first font is handled "normally"
3622         c = strchr(filelist, ':');
3623         cm = strchr(filelist, ',');
3624         if(c && (!cm || c < cm))
3625                 f->req_face = atoi(c+1);
3626         else
3627         {
3628                 f->req_face = 0;
3629                 c = cm;
3630         }
3631         if(!c || (c - filelist) > MAX_QPATH)
3632                 strlcpy(mainfont, filelist, sizeof(mainfont));
3633         else
3634         {
3635                 memcpy(mainfont, filelist, c - filelist);
3636                 mainfont[c - filelist] = 0;
3637         }
3638
3639         // handle fallbacks
3640         for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
3641         {
3642                 c = strchr(filelist, ',');
3643                 if(!c)
3644                         break;
3645                 filelist = c + 1;
3646                 if(!*filelist)
3647                         break;
3648                 c = strchr(filelist, ':');
3649                 cm = strchr(filelist, ',');
3650                 if(c && (!cm || c < cm))
3651                         f->fallback_faces[i] = atoi(c+1);
3652                 else
3653                 {
3654                         f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
3655                         c = cm;
3656                 }
3657                 if(!c || (c-filelist) > MAX_QPATH)
3658                 {
3659                         strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
3660                 }
3661                 else
3662                 {
3663                         memcpy(f->fallbacks[i], filelist, c - filelist);
3664                         f->fallbacks[i][c - filelist] = 0;
3665                 }
3666         }
3667
3668         // handle sizes
3669         for(i = 0; i < MAX_FONT_SIZES; ++i)
3670                 f->req_sizes[i] = -1;
3671         for (numsizes = 0,c = sizes;;)
3672         {
3673                 if (!COM_ParseToken_VM_Tokenize(&c, 0))
3674                         break;
3675                 sz = atof(com_token);
3676                 // detect crap size
3677                 if (sz < 0.001f || sz > 1000.0f)
3678                 {
3679                         VM_Warning(prog, "VM_loadfont: crap size %s", com_token);
3680                         continue;
3681                 }
3682                 // check overflow
3683                 if (numsizes == MAX_FONT_SIZES)
3684                 {
3685                         VM_Warning(prog, "VM_loadfont: MAX_FONT_SIZES = %i exceeded", MAX_FONT_SIZES);
3686                         break;
3687                 }
3688                 f->req_sizes[numsizes] = sz;
3689                 numsizes++;
3690         }
3691
3692         // additional scale/hoffset parms
3693         scale = 1;
3694         voffset = 0;
3695         if (prog->argc >= 5)
3696         {
3697                 scale = PRVM_G_FLOAT(OFS_PARM4);
3698                 if (scale <= 0)
3699                         scale = 1;
3700         }
3701         if (prog->argc >= 6)
3702                 voffset = PRVM_G_FLOAT(OFS_PARM5);
3703
3704         // load
3705         LoadFont(true, mainfont, f, scale, voffset);
3706
3707         // return index of loaded font
3708         PRVM_G_FLOAT(OFS_RETURN) = (f - dp_fonts.f);
3709 }
3710
3711 /*
3712 =========
3713 VM_drawpic
3714
3715 float   drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)
3716 =========
3717 */
3718 void VM_drawpic(prvm_prog_t *prog)
3719 {
3720         const char *picname;
3721         prvm_vec_t *size, *pos, *rgb;
3722         int flag = 0;
3723
3724         VM_SAFEPARMCOUNTRANGE(5,6,VM_drawpic);
3725
3726         // polygonbegin without draw2d arg has to guess
3727         prog->polygonbegin_guess2d = true;
3728
3729         picname = PRVM_G_STRING(OFS_PARM1);
3730         VM_CheckEmptyString(prog, picname);
3731
3732         // is pic cached ? no function yet for that
3733         if(!1)
3734         {
3735                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3736                 VM_Warning(prog, "VM_drawpic: %s: %s not cached !\n", prog->name, picname);
3737                 return;
3738         }
3739
3740         pos = PRVM_G_VECTOR(OFS_PARM0);
3741         size = PRVM_G_VECTOR(OFS_PARM2);
3742         rgb = PRVM_G_VECTOR(OFS_PARM3);
3743         if (prog->argc >= 6)
3744                 flag = (int) PRVM_G_FLOAT(OFS_PARM5);
3745
3746         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3747         {
3748                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3749                 VM_Warning(prog, "VM_drawpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3750                 return;
3751         }
3752
3753         if(pos[2] || size[2])
3754                 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")));
3755
3756         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);
3757         PRVM_G_FLOAT(OFS_RETURN) = 1;
3758 }
3759 /*
3760 =========
3761 VM_drawrotpic
3762
3763 float   drawrotpic(vector position, string pic, vector size, vector org, float angle, vector rgb, float alpha, float flag)
3764 =========
3765 */
3766 void VM_drawrotpic(prvm_prog_t *prog)
3767 {
3768         const char *picname;
3769         prvm_vec_t *size, *pos, *org, *rgb;
3770         int flag;
3771
3772         VM_SAFEPARMCOUNT(8,VM_drawrotpic);
3773
3774         // polygonbegin without draw2d arg has to guess
3775         prog->polygonbegin_guess2d = true;
3776
3777         picname = PRVM_G_STRING(OFS_PARM1);
3778         VM_CheckEmptyString(prog, picname);
3779
3780         // is pic cached ? no function yet for that
3781         if(!1)
3782         {
3783                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3784                 VM_Warning(prog, "VM_drawrotpic: %s: %s not cached !\n", prog->name, picname);
3785                 return;
3786         }
3787
3788         pos = PRVM_G_VECTOR(OFS_PARM0);
3789         size = PRVM_G_VECTOR(OFS_PARM2);
3790         org = PRVM_G_VECTOR(OFS_PARM3);
3791         rgb = PRVM_G_VECTOR(OFS_PARM5);
3792         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
3793
3794         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3795         {
3796                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3797                 VM_Warning(prog, "VM_drawrotpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3798                 return;
3799         }
3800
3801         if(pos[2] || size[2] || org[2])
3802                 VM_Warning(prog, "VM_drawrotpic: z value from pos/size/org discarded\n");
3803
3804         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);
3805         PRVM_G_FLOAT(OFS_RETURN) = 1;
3806 }
3807 /*
3808 =========
3809 VM_drawsubpic
3810
3811 float   drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
3812
3813 =========
3814 */
3815 void VM_drawsubpic(prvm_prog_t *prog)
3816 {
3817         const char *picname;
3818         prvm_vec_t *size, *pos, *rgb, *srcPos, *srcSize, alpha;
3819         int flag;
3820
3821         VM_SAFEPARMCOUNT(8,VM_drawsubpic);
3822
3823         // polygonbegin without draw2d arg has to guess
3824         prog->polygonbegin_guess2d = true;
3825
3826         picname = PRVM_G_STRING(OFS_PARM2);
3827         VM_CheckEmptyString(prog, picname);
3828
3829         // is pic cached ? no function yet for that
3830         if(!1)
3831         {
3832                 PRVM_G_FLOAT(OFS_RETURN) = -4;
3833                 VM_Warning(prog, "VM_drawsubpic: %s: %s not cached !\n", prog->name, picname);
3834                 return;
3835         }
3836
3837         pos = PRVM_G_VECTOR(OFS_PARM0);
3838         size = PRVM_G_VECTOR(OFS_PARM1);
3839         srcPos = PRVM_G_VECTOR(OFS_PARM3);
3840         srcSize = PRVM_G_VECTOR(OFS_PARM4);
3841         rgb = PRVM_G_VECTOR(OFS_PARM5);
3842         alpha = PRVM_G_FLOAT(OFS_PARM6);
3843         flag = (int) PRVM_G_FLOAT(OFS_PARM7);
3844
3845         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3846         {
3847                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3848                 VM_Warning(prog, "VM_drawsubpic: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3849                 return;
3850         }
3851
3852         if(pos[2] || size[2])
3853                 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")));
3854
3855         DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT),
3856                 size[0], size[1],
3857                 srcPos[0],              srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3858                 srcPos[0] + srcSize[0], srcPos[1],              rgb[0], rgb[1], rgb[2], alpha,
3859                 srcPos[0],              srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3860                 srcPos[0] + srcSize[0], srcPos[1] + srcSize[1], rgb[0], rgb[1], rgb[2], alpha,
3861                 flag);
3862         PRVM_G_FLOAT(OFS_RETURN) = 1;
3863 }
3864
3865 /*
3866 =========
3867 VM_drawfill
3868
3869 float drawfill(vector position, vector size, vector rgb, float alpha, float flag)
3870 =========
3871 */
3872 void VM_drawfill(prvm_prog_t *prog)
3873 {
3874         prvm_vec_t *size, *pos, *rgb;
3875         int flag;
3876
3877         VM_SAFEPARMCOUNT(5,VM_drawfill);
3878
3879         // polygonbegin without draw2d arg has to guess
3880         prog->polygonbegin_guess2d = true;
3881
3882         pos = PRVM_G_VECTOR(OFS_PARM0);
3883         size = PRVM_G_VECTOR(OFS_PARM1);
3884         rgb = PRVM_G_VECTOR(OFS_PARM2);
3885         flag = (int) PRVM_G_FLOAT(OFS_PARM4);
3886
3887         if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
3888         {
3889                 PRVM_G_FLOAT(OFS_RETURN) = -2;
3890                 VM_Warning(prog, "VM_drawfill: %s: wrong DRAWFLAG %i !\n",prog->name,flag);
3891                 return;
3892         }
3893
3894         if(pos[2] || size[2])
3895                 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")));
3896
3897         DrawQ_Fill(pos[0], pos[1], size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM3), flag);
3898         PRVM_G_FLOAT(OFS_RETURN) = 1;
3899 }
3900
3901 /*
3902 =========
3903 VM_drawsetcliparea
3904
3905 drawsetcliparea(float x, float y, float width, float height)
3906 =========
3907 */
3908 void VM_drawsetcliparea(prvm_prog_t *prog)
3909 {
3910         float x,y,w,h;
3911         VM_SAFEPARMCOUNT(4,VM_drawsetcliparea);
3912
3913         // polygonbegin without draw2d arg has to guess
3914         prog->polygonbegin_guess2d = true;
3915
3916         x = bound(0, PRVM_G_FLOAT(OFS_PARM0), vid_conwidth.integer);
3917         y = bound(0, PRVM_G_FLOAT(OFS_PARM1), vid_conheight.integer);
3918         w = bound(0, PRVM_G_FLOAT(OFS_PARM2) + PRVM_G_FLOAT(OFS_PARM0) - x, (vid_conwidth.integer  - x));
3919         h = bound(0, PRVM_G_FLOAT(OFS_PARM3) + PRVM_G_FLOAT(OFS_PARM1) - y, (vid_conheight.integer - y));
3920
3921         DrawQ_SetClipArea(x, y, w, h);
3922 }
3923
3924 /*
3925 =========
3926 VM_drawresetcliparea
3927
3928 drawresetcliparea()
3929 =========
3930 */
3931 void VM_drawresetcliparea(prvm_prog_t *prog)
3932 {
3933         VM_SAFEPARMCOUNT(0,VM_drawresetcliparea);
3934
3935         // polygonbegin without draw2d arg has to guess
3936         prog->polygonbegin_guess2d = true;
3937
3938         DrawQ_ResetClipArea();
3939 }
3940
3941 /*
3942 =========
3943 VM_getimagesize
3944
3945 vector  getimagesize(string pic)
3946 =========
3947 */
3948 void VM_getimagesize(prvm_prog_t *prog)
3949 {
3950         const char *p;
3951         cachepic_t *pic;
3952
3953         VM_SAFEPARMCOUNT(1,VM_getimagesize);
3954
3955         p = PRVM_G_STRING(OFS_PARM0);
3956         VM_CheckEmptyString(prog, p);
3957
3958         pic = Draw_CachePic_Flags (p, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOTPERSISTENT);
3959         if (!Draw_IsPicLoaded(pic))
3960         {
3961                 PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
3962                 PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
3963         }
3964         else
3965         {
3966                 PRVM_G_VECTOR(OFS_RETURN)[0] = Draw_GetPicWidth(pic);
3967                 PRVM_G_VECTOR(OFS_RETURN)[1] = Draw_GetPicHeight(pic);
3968         }
3969         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
3970 }
3971
3972 /*
3973 =========
3974 VM_keynumtostring
3975
3976 string keynumtostring(float keynum)
3977 =========
3978 */
3979 void VM_keynumtostring (prvm_prog_t *prog)
3980 {
3981         char tinystr[2];
3982         VM_SAFEPARMCOUNT(1, VM_keynumtostring);
3983
3984         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_KeynumToString((int)PRVM_G_FLOAT(OFS_PARM0), tinystr, sizeof(tinystr)));
3985 }
3986
3987 /*
3988 =========
3989 VM_findkeysforcommand
3990
3991 string  findkeysforcommand(string command, float bindmap)
3992
3993 the returned string is an altstring
3994 =========
3995 */
3996 #define FKFC_NUMKEYS 5
3997 void M_FindKeysForCommand(const char *command, int *keys);
3998 void VM_findkeysforcommand(prvm_prog_t *prog)
3999 {
4000         const char *cmd;
4001         char ret[VM_STRINGTEMP_LENGTH];
4002         int keys[FKFC_NUMKEYS];
4003         int i;
4004         int bindmap;
4005         char vabuf[1024];
4006
4007         VM_SAFEPARMCOUNTRANGE(1, 2, VM_findkeysforcommand);
4008
4009         cmd = PRVM_G_STRING(OFS_PARM0);
4010         if(prog->argc == 2)
4011                 bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
4012         else
4013                 bindmap = 0; // consistent to "bind"
4014
4015         VM_CheckEmptyString(prog, cmd);
4016
4017         Key_FindKeysForCommand(cmd, keys, FKFC_NUMKEYS, bindmap);
4018
4019         ret[0] = 0;
4020         for(i = 0; i < FKFC_NUMKEYS; i++)
4021                 strlcat(ret, va(vabuf, sizeof(vabuf), " \'%i\'", keys[i]), sizeof(ret));
4022
4023         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, ret);
4024 }
4025
4026 /*
4027 =========
4028 VM_stringtokeynum
4029
4030 float stringtokeynum(string key)
4031 =========
4032 */
4033 void VM_stringtokeynum (prvm_prog_t *prog)
4034 {
4035         VM_SAFEPARMCOUNT( 1, VM_stringtokeynum );
4036
4037         PRVM_G_FLOAT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0));
4038 }
4039
4040 /*
4041 =========
4042 VM_getkeybind
4043
4044 string getkeybind(float key, float bindmap)
4045 =========
4046 */
4047 void VM_getkeybind (prvm_prog_t *prog)
4048 {
4049         int bindmap;
4050         VM_SAFEPARMCOUNTRANGE(1, 2, VM_getkeybind);
4051         if(prog->argc == 2)
4052                 bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1);
4053         else
4054                 bindmap = 0; // consistent to "bind"
4055
4056         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap));
4057 }
4058
4059 /*
4060 =========
4061 VM_setkeybind
4062
4063 float setkeybind(float key, string cmd, float bindmap)
4064 =========
4065 */
4066 void VM_setkeybind (prvm_prog_t *prog)
4067 {
4068         int bindmap;
4069         VM_SAFEPARMCOUNTRANGE(2, 3, VM_setkeybind);
4070         if(prog->argc == 3)
4071                 bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM2), MAX_BINDMAPS-1);
4072         else
4073                 bindmap = 0; // consistent to "bind"
4074
4075         PRVM_G_FLOAT(OFS_RETURN) = 0;
4076         if(Key_SetBinding((int)PRVM_G_FLOAT(OFS_PARM0), bindmap, PRVM_G_STRING(OFS_PARM1)))
4077                 PRVM_G_FLOAT(OFS_RETURN) = 1;
4078 }
4079
4080 /*
4081 =========
4082 VM_getbindmap
4083
4084 vector getbindmaps()
4085 =========
4086 */
4087 void VM_getbindmaps (prvm_prog_t *prog)
4088 {
4089         int fg, bg;
4090         VM_SAFEPARMCOUNT(0, VM_getbindmaps);
4091         Key_GetBindMap(&fg, &bg);
4092         PRVM_G_VECTOR(OFS_RETURN)[0] = fg;
4093         PRVM_G_VECTOR(OFS_RETURN)[1] = bg;
4094         PRVM_G_VECTOR(OFS_RETURN)[2] = 0;
4095 }
4096
4097 /*
4098 =========
4099 VM_setbindmap
4100
4101 float setbindmaps(vector bindmap)
4102 =========
4103 */
4104 void VM_setbindmaps (prvm_prog_t *prog)
4105 {
4106         VM_SAFEPARMCOUNT(1, VM_setbindmaps);
4107         PRVM_G_FLOAT(OFS_RETURN) = 0;
4108         if(PRVM_G_VECTOR(OFS_PARM0)[2] == 0)
4109                 if(Key_SetBindMap((int)PRVM_G_VECTOR(OFS_PARM0)[0], (int)PRVM_G_VECTOR(OFS_PARM0)[1]))
4110                         PRVM_G_FLOAT(OFS_RETURN) = 1;
4111 }
4112
4113 // CL_Video interface functions
4114
4115 /*
4116 ========================
4117 VM_cin_open
4118
4119 float cin_open(string file, string name)
4120 ========================
4121 */
4122 void VM_cin_open(prvm_prog_t *prog)
4123 {
4124         const char *file;
4125         const char *name;
4126
4127         VM_SAFEPARMCOUNT( 2, VM_cin_open );
4128
4129         file = PRVM_G_STRING( OFS_PARM0 );
4130         name = PRVM_G_STRING( OFS_PARM1 );
4131
4132         VM_CheckEmptyString(prog,  file );
4133     VM_CheckEmptyString(prog,  name );
4134
4135         if( CL_OpenVideo( file, name, MENUOWNER, "" ) )
4136                 PRVM_G_FLOAT( OFS_RETURN ) = 1;
4137         else
4138                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
4139 }
4140
4141 /*
4142 ========================
4143 VM_cin_close
4144
4145 void cin_close(string name)
4146 ========================
4147 */
4148 void VM_cin_close(prvm_prog_t *prog)
4149 {
4150         const char *name;
4151
4152         VM_SAFEPARMCOUNT( 1, VM_cin_close );
4153
4154         name = PRVM_G_STRING( OFS_PARM0 );
4155         VM_CheckEmptyString(prog,  name );
4156
4157         CL_CloseVideo( CL_GetVideoByName( name ) );
4158 }
4159
4160 /*
4161 ========================
4162 VM_cin_setstate
4163 void cin_setstate(string name, float type)
4164 ========================
4165 */
4166 void VM_cin_setstate(prvm_prog_t *prog)
4167 {
4168         const char *name;
4169         clvideostate_t  state;
4170         clvideo_t               *video;
4171
4172         VM_SAFEPARMCOUNT( 2, VM_cin_setstate );
4173
4174         name = PRVM_G_STRING( OFS_PARM0 );
4175         VM_CheckEmptyString(prog,  name );
4176
4177         state = (clvideostate_t)((int)PRVM_G_FLOAT( OFS_PARM1 ));
4178
4179         video = CL_GetVideoByName( name );
4180         if( video && state > CLVIDEO_UNUSED && state < CLVIDEO_STATECOUNT )
4181                 CL_SetVideoState( video, state );
4182 }
4183
4184 /*
4185 ========================
4186 VM_cin_getstate
4187
4188 float cin_getstate(string name)
4189 ========================
4190 */
4191 void VM_cin_getstate(prvm_prog_t *prog)
4192 {
4193         const char *name;
4194         clvideo_t               *video;
4195
4196         VM_SAFEPARMCOUNT( 1, VM_cin_getstate );
4197
4198         name = PRVM_G_STRING( OFS_PARM0 );
4199         VM_CheckEmptyString(prog,  name );
4200
4201         video = CL_GetVideoByName( name );
4202         if( video )
4203                 PRVM_G_FLOAT( OFS_RETURN ) = (int)video->state;
4204         else
4205                 PRVM_G_FLOAT( OFS_RETURN ) = 0;
4206 }
4207
4208 /*
4209 ========================
4210 VM_cin_restart
4211
4212 void cin_restart(string name)
4213 ========================
4214 */
4215 void VM_cin_restart(prvm_prog_t *prog)
4216 {
4217         const char *name;
4218         clvideo_t               *video;
4219
4220         VM_SAFEPARMCOUNT( 1, VM_cin_restart );
4221
4222         name = PRVM_G_STRING( OFS_PARM0 );
4223         VM_CheckEmptyString(prog,  name );
4224
4225         video = CL_GetVideoByName( name );
4226         if( video )
4227                 CL_RestartVideo( video );
4228 }
4229
4230 /*
4231 ========================
4232 VM_gecko_create
4233
4234 float[bool] gecko_create( string name )
4235 ========================
4236 */
4237 void VM_gecko_create(prvm_prog_t *prog) {
4238         // REMOVED
4239         PRVM_G_FLOAT( OFS_RETURN ) = 0;
4240 }
4241
4242 /*
4243 ========================
4244 VM_gecko_destroy
4245
4246 void gecko_destroy( string name )
4247 ========================
4248 */
4249 void VM_gecko_destroy(prvm_prog_t *prog) {
4250         // REMOVED
4251 }
4252
4253 /*
4254 ========================
4255 VM_gecko_navigate
4256
4257 void gecko_navigate( string name, string URI )
4258 ========================
4259 */
4260 void VM_gecko_navigate(prvm_prog_t *prog) {
4261         // REMOVED
4262 }
4263
4264 /*
4265 ========================
4266 VM_gecko_keyevent
4267
4268 float[bool] gecko_keyevent( string name, float key, float eventtype ) 
4269 ========================
4270 */
4271 void VM_gecko_keyevent(prvm_prog_t *prog) {
4272         // REMOVED
4273         PRVM_G_FLOAT( OFS_RETURN ) = 0;
4274 }
4275
4276 /*
4277 ========================
4278 VM_gecko_movemouse
4279
4280 void gecko_mousemove( string name, float x, float y )
4281 ========================
4282 */
4283 void VM_gecko_movemouse(prvm_prog_t *prog) {
4284         // REMOVED
4285 }
4286
4287
4288 /*
4289 ========================
4290 VM_gecko_resize
4291
4292 void gecko_resize( string name, float w, float h )
4293 ========================
4294 */
4295 void VM_gecko_resize(prvm_prog_t *prog) {
4296         // REMOVED
4297 }
4298
4299
4300 /*
4301 ========================
4302 VM_gecko_get_texture_extent
4303
4304 vector gecko_get_texture_extent( string name )
4305 ========================
4306 */
4307 void VM_gecko_get_texture_extent(prvm_prog_t *prog) {
4308         // REMOVED
4309         PRVM_G_VECTOR(OFS_RETURN)[0] = 0;
4310         PRVM_G_VECTOR(OFS_RETURN)[1] = 0;
4311 }
4312
4313
4314
4315 /*
4316 ==============
4317 VM_makevectors
4318
4319 Writes new values for v_forward, v_up, and v_right based on angles
4320 void makevectors(vector angle)
4321 ==============
4322 */
4323 void VM_makevectors (prvm_prog_t *prog)
4324 {
4325         vec3_t angles, forward, right, up;
4326         VM_SAFEPARMCOUNT(1, VM_makevectors);
4327         VectorCopy(PRVM_G_VECTOR(OFS_PARM0), angles);
4328         AngleVectors(angles, forward, right, up);
4329         VectorCopy(forward, PRVM_gameglobalvector(v_forward));
4330         VectorCopy(right, PRVM_gameglobalvector(v_right));
4331         VectorCopy(up, PRVM_gameglobalvector(v_up));
4332 }
4333
4334 /*
4335 ==============
4336 VM_vectorvectors
4337
4338 Writes new values for v_forward, v_up, and v_right based on the given forward vector
4339 vectorvectors(vector)
4340 ==============
4341 */
4342 void VM_vectorvectors (prvm_prog_t *prog)
4343 {
4344         vec3_t forward, right, up;
4345         VM_SAFEPARMCOUNT(1, VM_vectorvectors);
4346         VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), forward);
4347         VectorVectors(forward, right, up);
4348         VectorCopy(forward, PRVM_gameglobalvector(v_forward));
4349         VectorCopy(right, PRVM_gameglobalvector(v_right));
4350         VectorCopy(up, PRVM_gameglobalvector(v_up));
4351 }
4352
4353 /*
4354 ========================
4355 VM_drawline
4356
4357 void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags)
4358 ========================
4359 */
4360 void VM_drawline (prvm_prog_t *prog)
4361 {
4362         prvm_vec_t      *c1, *c2, *rgb;
4363         float   alpha, width;
4364         unsigned char   flags;
4365
4366         VM_SAFEPARMCOUNT(6, VM_drawline);
4367
4368         // polygonbegin without draw2d arg has to guess
4369         prog->polygonbegin_guess2d = true;
4370
4371         width   = PRVM_G_FLOAT(OFS_PARM0);
4372         c1              = PRVM_G_VECTOR(OFS_PARM1);
4373         c2              = PRVM_G_VECTOR(OFS_PARM2);
4374         rgb             = PRVM_G_VECTOR(OFS_PARM3);
4375         alpha   = PRVM_G_FLOAT(OFS_PARM4);
4376         flags   = (int)PRVM_G_FLOAT(OFS_PARM5);
4377         DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags);
4378 }
4379
4380 // float(float number, float quantity) bitshift (EXT_BITSHIFT)
4381 void VM_bitshift (prvm_prog_t *prog)
4382 {
4383         prvm_int_t n1, n2;
4384         VM_SAFEPARMCOUNT(2, VM_bitshift);
4385
4386         n1 = (prvm_int_t)fabs((prvm_vec_t)((prvm_int_t)PRVM_G_FLOAT(OFS_PARM0)));
4387         n2 = (prvm_int_t)PRVM_G_FLOAT(OFS_PARM1);
4388         if(!n1)
4389                 PRVM_G_FLOAT(OFS_RETURN) = n1;
4390         else
4391         if(n2 < 0)
4392                 PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2);
4393         else
4394                 PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2);
4395 }
4396
4397 ////////////////////////////////////////
4398 // AltString functions
4399 ////////////////////////////////////////
4400
4401 /*
4402 ========================
4403 VM_altstr_count
4404
4405 float altstr_count(string)
4406 ========================
4407 */
4408 void VM_altstr_count(prvm_prog_t *prog)
4409 {
4410         const char *altstr, *pos;
4411         int     count;
4412
4413         VM_SAFEPARMCOUNT( 1, VM_altstr_count );
4414
4415         altstr = PRVM_G_STRING( OFS_PARM0 );
4416         //VM_CheckEmptyString(prog,  altstr );
4417
4418         for( count = 0, pos = altstr ; *pos ; pos++ ) {
4419                 if( *pos == '\\' ) {
4420                         if( !*++pos ) {
4421                                 break;
4422                         }
4423                 } else if( *pos == '\'' ) {
4424                         count++;
4425                 }
4426         }
4427
4428         PRVM_G_FLOAT( OFS_RETURN ) = (prvm_vec_t) (count / 2);
4429 }
4430
4431 /*
4432 ========================
4433 VM_altstr_prepare
4434
4435 string altstr_prepare(string)
4436 ========================
4437 */
4438 void VM_altstr_prepare(prvm_prog_t *prog)
4439 {
4440         const char *instr, *in;
4441         char outstr[VM_STRINGTEMP_LENGTH];
4442         size_t outpos;
4443
4444         VM_SAFEPARMCOUNT( 1, VM_altstr_prepare );
4445
4446         instr = PRVM_G_STRING( OFS_PARM0 );
4447
4448         for (in = instr, outpos = 0; *in && outpos < sizeof(outstr) - 1; ++in)
4449         {
4450                 if (*in == '\'' && outpos < sizeof(outstr) - 2)
4451                 {
4452                         outstr[outpos++] = '\\';
4453                         outstr[outpos++] = '\'';
4454                 }
4455                 else
4456                         outstr[outpos++] = *in;
4457         }
4458         outstr[outpos] = 0;
4459
4460         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4461 }
4462
4463 /*
4464 ========================
4465 VM_altstr_get
4466
4467 string altstr_get(string, float)
4468 ========================
4469 */
4470 void VM_altstr_get(prvm_prog_t *prog)
4471 {
4472         const char *altstr, *pos;
4473         char *out;
4474         int count, size;
4475         char outstr[VM_STRINGTEMP_LENGTH];
4476
4477         VM_SAFEPARMCOUNT( 2, VM_altstr_get );
4478
4479         altstr = PRVM_G_STRING( OFS_PARM0 );
4480
4481         count = (int)PRVM_G_FLOAT( OFS_PARM1 );
4482         count = count * 2 + 1;
4483
4484         for( pos = altstr ; *pos && count ; pos++ )
4485                 if( *pos == '\\' ) {
4486                         if( !*++pos )
4487                                 break;
4488                 } else if( *pos == '\'' )
4489                         count--;
4490
4491         if( !*pos ) {
4492                 PRVM_G_INT( OFS_RETURN ) = 0;
4493                 return;
4494         }
4495
4496         for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ )
4497                 if( *pos == '\\' ) {
4498                         if( !*++pos )
4499                                 break;
4500                         *out = *pos;
4501                         size--;
4502                 } else if( *pos == '\'' )
4503                         break;
4504                 else
4505                         *out = *pos;
4506
4507         *out = 0;
4508         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4509 }
4510
4511 /*
4512 ========================
4513 VM_altstr_set
4514
4515 string altstr_set(string altstr, float num, string set)
4516 ========================
4517 */
4518 void VM_altstr_set(prvm_prog_t *prog)
4519 {
4520     int num;
4521         const char *altstr, *str;
4522         const char *in;
4523         char *out;
4524         char outstr[VM_STRINGTEMP_LENGTH];
4525
4526         VM_SAFEPARMCOUNT( 3, VM_altstr_set );
4527
4528         altstr = PRVM_G_STRING( OFS_PARM0 );
4529
4530         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
4531
4532         str = PRVM_G_STRING( OFS_PARM2 );
4533
4534         out = outstr;
4535         for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ )
4536                 if( *in == '\\' ) {
4537                         if( !*++in ) {
4538                                 break;
4539                         }
4540                 } else if( *in == '\'' ) {
4541                         num--;
4542                 }
4543
4544         // copy set in
4545         for( ; *str; *out++ = *str++ );
4546         // now jump over the old content
4547         for( ; *in ; in++ )
4548                 if( *in == '\'' || (*in == '\\' && !*++in) )
4549                         break;
4550
4551         strlcpy(out, in, outstr + sizeof(outstr) - out);
4552         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4553 }
4554
4555 /*
4556 ========================
4557 VM_altstr_ins
4558 insert after num
4559 string  altstr_ins(string altstr, float num, string set)
4560 ========================
4561 */
4562 void VM_altstr_ins(prvm_prog_t *prog)
4563 {
4564         int num;
4565         const char *set;
4566         const char *in;
4567         char *out;
4568         char outstr[VM_STRINGTEMP_LENGTH];
4569
4570         VM_SAFEPARMCOUNT(3, VM_altstr_ins);
4571
4572         in = PRVM_G_STRING( OFS_PARM0 );
4573         num = (int)PRVM_G_FLOAT( OFS_PARM1 );
4574         set = PRVM_G_STRING( OFS_PARM2 );
4575
4576         out = outstr;
4577         for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
4578                 if( *in == '\\' ) {
4579                         if( !*++in ) {
4580                                 break;
4581                         }
4582                 } else if( *in == '\'' ) {
4583                         num--;
4584                 }
4585
4586         *out++ = '\'';
4587         for( ; *set ; *out++ = *set++ );
4588         *out++ = '\'';
4589
4590         strlcpy(out, in, outstr + sizeof(outstr) - out);
4591         PRVM_G_INT( OFS_RETURN ) = PRVM_SetTempString(prog,  outstr );
4592 }
4593
4594
4595 ////////////////////////////////////////
4596 // BufString functions
4597 ////////////////////////////////////////
4598 //[515]: string buffers support
4599
4600 static size_t stringbuffers_sortlength;
4601
4602 static void BufStr_Expand(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex)
4603 {
4604         if (stringbuffer->max_strings <= strindex)
4605         {
4606                 char **oldstrings = stringbuffer->strings;
4607                 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
4608                 while (stringbuffer->max_strings <= strindex)
4609                         stringbuffer->max_strings *= 2;
4610                 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
4611                 if (stringbuffer->num_strings > 0)
4612                         memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
4613                 if (oldstrings)
4614                         Mem_Free(oldstrings);
4615         }
4616 }
4617
4618 static void BufStr_Shrink(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer)
4619 {
4620         // reduce num_strings if there are empty string slots at the end
4621         while (stringbuffer->num_strings > 0 && stringbuffer->strings[stringbuffer->num_strings - 1] == NULL)
4622                 stringbuffer->num_strings--;
4623
4624         // if empty, free the string pointer array
4625         if (stringbuffer->num_strings == 0)
4626         {
4627                 stringbuffer->max_strings = 0;
4628                 if (stringbuffer->strings)
4629                         Mem_Free(stringbuffer->strings);
4630                 stringbuffer->strings = NULL;
4631         }
4632 }
4633
4634 static int BufStr_SortStringsUP (const void *in1, const void *in2)
4635 {
4636         const char *a, *b;
4637         a = *((const char **) in1);
4638         b = *((const char **) in2);
4639         if(!a || !a[0]) return 1;
4640         if(!b || !b[0]) return -1;
4641         return strncmp(a, b, stringbuffers_sortlength);
4642 }
4643
4644 static int BufStr_SortStringsDOWN (const void *in1, const void *in2)
4645 {
4646         const char *a, *b;
4647         a = *((const char **) in1);
4648         b = *((const char **) in2);
4649         if(!a || !a[0]) return 1;
4650         if(!b || !b[0]) return -1;
4651         return strncmp(b, a, stringbuffers_sortlength);
4652 }
4653
4654 prvm_stringbuffer_t *BufStr_FindCreateReplace (prvm_prog_t *prog, int bufindex, int flags, const char *format)
4655 {
4656         prvm_stringbuffer_t *stringbuffer;
4657         int i;
4658
4659         if (bufindex < 0)
4660                 return NULL;
4661
4662         // find buffer with wanted index
4663         if (bufindex < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray))
4664         {
4665                 if ( (stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, bufindex)) )
4666                 {
4667                         if (stringbuffer->flags & STRINGBUFFER_TEMP)
4668                                 stringbuffer->flags = flags; // created but has not been used yet
4669                         return stringbuffer;
4670                 }
4671                 return NULL;
4672         }
4673
4674         // allocate new buffer with wanted index
4675         while(1)
4676         {
4677                 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
4678                 stringbuffer->flags = STRINGBUFFER_TEMP;
4679                 for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
4680                 if (i == bufindex)
4681                 {
4682                         stringbuffer->flags = flags; // mark as used
4683                         break;
4684                 }
4685         }
4686         return stringbuffer;
4687 }
4688
4689 void BufStr_Set(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer, int strindex, const char *str)
4690 {
4691         size_t  alloclen;
4692
4693         if (!stringbuffer || strindex < 0)
4694                 return;
4695
4696         BufStr_Expand(prog, stringbuffer, strindex);
4697         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
4698         if (stringbuffer->strings[strindex])
4699                 Mem_Free(stringbuffer->strings[strindex]);
4700         stringbuffer->strings[strindex] = NULL;
4701
4702         if (str)
4703         {
4704                 // not the NULL string!
4705                 alloclen = strlen(str) + 1;
4706                 stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
4707                 memcpy(stringbuffer->strings[strindex], str, alloclen);
4708         }
4709
4710         BufStr_Shrink(prog, stringbuffer);
4711 }
4712
4713 void BufStr_Del(prvm_prog_t *prog, prvm_stringbuffer_t *stringbuffer)
4714 {
4715         int i;
4716         
4717         if (!stringbuffer)
4718                 return;
4719
4720         for (i = 0;i < stringbuffer->num_strings;i++)
4721                 if (stringbuffer->strings[i])
4722                         Mem_Free(stringbuffer->strings[i]);
4723         if (stringbuffer->strings)
4724                 Mem_Free(stringbuffer->strings);
4725         if(stringbuffer->origin)
4726                 PRVM_Free((char *)stringbuffer->origin);
4727         Mem_ExpandableArray_FreeRecord(&prog->stringbuffersarray, stringbuffer);
4728 }
4729
4730 void BufStr_Flush(prvm_prog_t *prog)
4731 {
4732         prvm_stringbuffer_t *stringbuffer;
4733         int i, numbuffers;
4734
4735         numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
4736         for (i = 0; i < numbuffers; i++)
4737                 if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
4738                         BufStr_Del(prog, stringbuffer);
4739         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
4740 }
4741
4742 /*
4743 ========================
4744 VM_buf_create
4745 creates new buffer, and returns it's index, returns -1 if failed
4746 float buf_create(prvm_prog_t *prog) = #460;
4747 float newbuf(string format, float flags) = #460;
4748 ========================
4749 */
4750
4751 void VM_buf_create (prvm_prog_t *prog)
4752 {
4753         prvm_stringbuffer_t *stringbuffer;
4754         int i;
4755
4756         VM_SAFEPARMCOUNTRANGE(0, 2, VM_buf_create);
4757
4758         // VorteX: optional parm1 (buffer format) is unfinished, to keep intact with future databuffers extension must be set to "string"
4759         if(prog->argc >= 1 && strcmp(PRVM_G_STRING(OFS_PARM0), "string"))
4760         {
4761                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4762                 return;
4763         }
4764         stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
4765         for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
4766         stringbuffer->origin = PRVM_AllocationOrigin(prog);
4767         // optional flags parm
4768         if (prog->argc >= 2)
4769                 stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & STRINGBUFFER_QCFLAGS;
4770         PRVM_G_FLOAT(OFS_RETURN) = i;
4771 }
4772
4773
4774
4775 /*
4776 ========================
4777 VM_buf_del
4778 deletes buffer and all strings in it
4779 void buf_del(float bufhandle) = #461;
4780 ========================
4781 */
4782 void VM_buf_del (prvm_prog_t *prog)
4783 {
4784         prvm_stringbuffer_t *stringbuffer;
4785         VM_SAFEPARMCOUNT(1, VM_buf_del);
4786         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4787         if (stringbuffer)
4788                 BufStr_Del(prog, stringbuffer);
4789         else
4790         {
4791                 VM_Warning(prog, "VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4792                 return;
4793         }
4794 }
4795
4796 /*
4797 ========================
4798 VM_buf_getsize
4799 how many strings are stored in buffer
4800 float buf_getsize(float bufhandle) = #462;
4801 ========================
4802 */
4803 void VM_buf_getsize (prvm_prog_t *prog)
4804 {
4805         prvm_stringbuffer_t *stringbuffer;
4806         VM_SAFEPARMCOUNT(1, VM_buf_getsize);
4807
4808         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4809         if(!stringbuffer)
4810         {
4811                 PRVM_G_FLOAT(OFS_RETURN) = -1;
4812                 VM_Warning(prog, "VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4813                 return;
4814         }
4815         else
4816                 PRVM_G_FLOAT(OFS_RETURN) = stringbuffer->num_strings;
4817 }
4818
4819 /*
4820 ========================
4821 VM_buf_copy
4822 copy all content from one buffer to another, make sure it exists
4823 void buf_copy(float bufhandle_from, float bufhandle_to) = #463;
4824 ========================
4825 */
4826 void VM_buf_copy (prvm_prog_t *prog)
4827 {
4828         prvm_stringbuffer_t *srcstringbuffer, *dststringbuffer;
4829         int i;
4830         VM_SAFEPARMCOUNT(2, VM_buf_copy);
4831
4832         srcstringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4833         if(!srcstringbuffer)
4834         {
4835                 VM_Warning(prog, "VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4836                 return;
4837         }
4838         i = (int)PRVM_G_FLOAT(OFS_PARM1);
4839         if(i == (int)PRVM_G_FLOAT(OFS_PARM0))
4840         {
4841                 VM_Warning(prog, "VM_buf_copy: source == destination (%i) in %s\n", i, prog->name);
4842                 return;
4843         }
4844         dststringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4845         if(!dststringbuffer)
4846         {
4847                 VM_Warning(prog, "VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
4848                 return;
4849         }
4850
4851         for (i = 0;i < dststringbuffer->num_strings;i++)
4852                 if (dststringbuffer->strings[i])
4853                         Mem_Free(dststringbuffer->strings[i]);
4854         if (dststringbuffer->strings)
4855                 Mem_Free(dststringbuffer->strings);
4856         *dststringbuffer = *srcstringbuffer;
4857         if (dststringbuffer->max_strings)
4858                 dststringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(dststringbuffer->strings[0]) * dststringbuffer->max_strings);
4859
4860         for (i = 0;i < dststringbuffer->num_strings;i++)
4861         {
4862                 if (srcstringbuffer->strings[i])
4863                 {
4864                         size_t stringlen;
4865                         stringlen = strlen(srcstringbuffer->strings[i]) + 1;
4866                         dststringbuffer->strings[i] = (char *)Mem_Alloc(prog->progs_mempool, stringlen);
4867                         memcpy(dststringbuffer->strings[i], srcstringbuffer->strings[i], stringlen);
4868                 }
4869         }
4870 }
4871
4872 /*
4873 ========================
4874 VM_buf_sort
4875 sort buffer by beginnings of strings (cmplength defaults it's length)
4876 "backward == true" means that sorting goes upside-down
4877 void buf_sort(float bufhandle, float cmplength, float backward) = #464;
4878 ========================
4879 */
4880 void VM_buf_sort (prvm_prog_t *prog)
4881 {
4882         prvm_stringbuffer_t *stringbuffer;
4883         VM_SAFEPARMCOUNT(3, VM_buf_sort);
4884
4885         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4886         if(!stringbuffer)
4887         {
4888                 VM_Warning(prog, "VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4889                 return;
4890         }
4891         if(stringbuffer->num_strings <= 0)
4892         {
4893                 VM_Warning(prog, "VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4894                 return;
4895         }
4896         stringbuffers_sortlength = (int)PRVM_G_FLOAT(OFS_PARM1);
4897         if(stringbuffers_sortlength <= 0)
4898                 stringbuffers_sortlength = 0x7FFFFFFF;
4899
4900         if(!PRVM_G_FLOAT(OFS_PARM2))
4901                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsUP);
4902         else
4903                 qsort(stringbuffer->strings, stringbuffer->num_strings, sizeof(char*), BufStr_SortStringsDOWN);
4904
4905         BufStr_Shrink(prog, stringbuffer);
4906 }
4907
4908 /*
4909 ========================
4910 VM_buf_implode
4911 concantenates all buffer string into one with "glue" separator and returns it as tempstring
4912 string buf_implode(float bufhandle, string glue) = #465;
4913 ========================
4914 */
4915 void VM_buf_implode (prvm_prog_t *prog)
4916 {
4917         prvm_stringbuffer_t *stringbuffer;
4918         char                    k[VM_STRINGTEMP_LENGTH];
4919         const char              *sep;
4920         int                             i;
4921         size_t                  l;
4922         VM_SAFEPARMCOUNT(2, VM_buf_implode);
4923
4924         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4925         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4926         if(!stringbuffer)
4927         {
4928                 VM_Warning(prog, "VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4929                 return;
4930         }
4931         if(!stringbuffer->num_strings)
4932                 return;
4933         sep = PRVM_G_STRING(OFS_PARM1);
4934         k[0] = 0;
4935         for(l = i = 0;i < stringbuffer->num_strings;i++)
4936         {
4937                 if(stringbuffer->strings[i])
4938                 {
4939                         l += (i > 0 ? strlen(sep) : 0) + strlen(stringbuffer->strings[i]);
4940                         if (l >= sizeof(k) - 1)
4941                                 break;
4942                         strlcat(k, sep, sizeof(k));
4943                         strlcat(k, stringbuffer->strings[i], sizeof(k));
4944                 }
4945         }
4946         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, k);
4947 }
4948
4949 /*
4950 ========================
4951 VM_bufstr_get
4952 get a string from buffer, returns tempstring, dont str_unzone it!
4953 string bufstr_get(float bufhandle, float string_index) = #465;
4954 ========================
4955 */
4956 void VM_bufstr_get (prvm_prog_t *prog)
4957 {
4958         prvm_stringbuffer_t *stringbuffer;
4959         int                             strindex;
4960         VM_SAFEPARMCOUNT(2, VM_bufstr_get);
4961
4962         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
4963         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4964         if(!stringbuffer)
4965         {
4966                 VM_Warning(prog, "VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4967                 return;
4968         }
4969         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
4970         if (strindex < 0)
4971         {
4972                 // VM_Warning(prog, "VM_bufstr_get: invalid string index %i used in %s\n", strindex, prog->name);
4973                 return;
4974         }
4975         if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex])
4976                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, stringbuffer->strings[strindex]);
4977 }
4978
4979 /*
4980 ========================
4981 VM_bufstr_set
4982 copies a string into selected slot of buffer
4983 void bufstr_set(float bufhandle, float string_index, string str) = #466;
4984 ========================
4985 */
4986 void VM_bufstr_set (prvm_prog_t *prog)
4987 {
4988         int                             strindex;
4989         prvm_stringbuffer_t *stringbuffer;
4990         const char              *news;
4991
4992         VM_SAFEPARMCOUNT(3, VM_bufstr_set);
4993
4994         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
4995         if(!stringbuffer)
4996         {
4997                 VM_Warning(prog, "VM_bufstr_set: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
4998                 return;
4999         }
5000         strindex = (int)PRVM_G_FLOAT(OFS_PARM1);
5001         if(strindex < 0 || strindex >= 1000000) // huge number of strings
5002         {
5003                 VM_Warning(prog, "VM_bufstr_set: invalid string index %i used in %s\n", strindex, prog->name);
5004                 return;
5005         }
5006
5007         news = PRVM_G_STRING(OFS_PARM2);
5008         BufStr_Set(prog, stringbuffer, strindex, news);
5009 }
5010
5011 /*
5012 ========================
5013 VM_bufstr_add
5014 adds string to buffer in first free slot and returns its index
5015 "order == true" means that string will be added after last "full" slot
5016 float bufstr_add(float bufhandle, string str, float order) = #467;
5017 ========================
5018 */
5019 void VM_bufstr_add (prvm_prog_t *prog)
5020 {
5021         int                             order, strindex;
5022         prvm_stringbuffer_t *stringbuffer;
5023         const char              *string;
5024         size_t                  alloclen;
5025
5026         VM_SAFEPARMCOUNT(3, VM_bufstr_add);
5027
5028         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5029         PRVM_G_FLOAT(OFS_RETURN) = -1;
5030         if(!stringbuffer)
5031         {
5032                 VM_Warning(prog, "VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5033                 return;
5034         }
5035         if(!PRVM_G_INT(OFS_PARM1)) // NULL string
5036         {
5037                 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);
5038                 return;
5039         }
5040         string = PRVM_G_STRING(OFS_PARM1);
5041         order = (int)PRVM_G_FLOAT(OFS_PARM2);
5042         if(order)
5043                 strindex = stringbuffer->num_strings;
5044         else
5045                 for (strindex = 0;strindex < stringbuffer->num_strings;strindex++)
5046                         if (stringbuffer->strings[strindex] == NULL)
5047                                 break;
5048
5049         BufStr_Expand(prog, stringbuffer, strindex);
5050
5051         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
5052         alloclen = strlen(string) + 1;
5053         stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
5054         memcpy(stringbuffer->strings[strindex], string, alloclen);
5055
5056         PRVM_G_FLOAT(OFS_RETURN) = strindex;
5057 }
5058
5059 /*
5060 ========================
5061 VM_bufstr_free
5062 delete string from buffer
5063 void bufstr_free(float bufhandle, float string_index) = #468;
5064 ========================
5065 */
5066 void VM_bufstr_free (prvm_prog_t *prog)
5067 {
5068         int                             i;
5069         prvm_stringbuffer_t     *stringbuffer;
5070         VM_SAFEPARMCOUNT(2, VM_bufstr_free);
5071
5072         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5073         if(!stringbuffer)
5074         {
5075                 VM_Warning(prog, "VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5076                 return;
5077         }
5078         i = (int)PRVM_G_FLOAT(OFS_PARM1);
5079         if(i < 0)
5080         {
5081                 VM_Warning(prog, "VM_bufstr_free: invalid string index %i used in %s\n", i, prog->name);
5082                 return;
5083         }
5084
5085         if (i < stringbuffer->num_strings)
5086         {
5087                 if(stringbuffer->strings[i])
5088                         Mem_Free(stringbuffer->strings[i]);
5089                 stringbuffer->strings[i] = NULL;
5090         }
5091
5092         BufStr_Shrink(prog, stringbuffer);
5093 }
5094
5095 /*
5096 ========================
5097 VM_buf_loadfile
5098 load a file into string buffer, return 0 or 1
5099 float buf_loadfile(string filename, float bufhandle) = #535;
5100 ========================
5101 */
5102 void VM_buf_loadfile(prvm_prog_t *prog)
5103 {
5104         size_t alloclen;
5105         prvm_stringbuffer_t *stringbuffer;
5106         char string[VM_STRINGTEMP_LENGTH];
5107         int strindex, c, end;
5108         const char *filename;
5109         char vabuf[1024];
5110         qfile_t *file;
5111
5112         VM_SAFEPARMCOUNT(2, VM_buf_loadfile);
5113
5114         // get file
5115         filename = PRVM_G_STRING(OFS_PARM0);
5116         file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "data/%s", filename), false);
5117         if (file == NULL)
5118                 file = FS_OpenVirtualFile(va(vabuf, sizeof(vabuf), "%s", filename), false);
5119         if (file == NULL)
5120         {
5121                 if (developer_extra.integer)
5122                         VM_Warning(prog, "VM_buf_loadfile: failed to open file %s in %s\n", filename, prog->name);
5123                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5124                 return;
5125         }
5126
5127         // get string buffer
5128         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
5129         if(!stringbuffer)
5130         {
5131                 VM_Warning(prog, "VM_buf_loadfile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
5132                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5133                 return;
5134         }
5135
5136         // read file (append to the end of buffer)
5137         strindex = stringbuffer->num_strings;
5138         while(1)
5139         {
5140                 // read line
5141                 end = 0;
5142                 for (;;)
5143                 {
5144                         c = FS_Getc(file);
5145                         if (c == '\r' || c == '\n' || c < 0)
5146                                 break;
5147                         if (end < VM_STRINGTEMP_LENGTH - 1)
5148                                 string[end++] = c;
5149                 }
5150                 string[end] = 0;
5151                 // remove \n following \r
5152                 if (c == '\r')
5153                 {
5154                         c = FS_Getc(file);
5155                         if (c != '\n')
5156                                 FS_UnGetc(file, (unsigned char)c);
5157                 }
5158                 // add and continue
5159                 if (c >= 0 || end)
5160                 {
5161                         BufStr_Expand(prog, stringbuffer, strindex);
5162                         stringbuffer->num_strings = max(stringbuffer->num_strings, strindex + 1);
5163                         alloclen = strlen(string) + 1;
5164                         stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
5165                         memcpy(stringbuffer->strings[strindex], string, alloclen);
5166                         strindex = stringbuffer->num_strings;
5167                 }
5168                 else
5169                         break;
5170         }
5171
5172         // close file
5173         FS_Close(file);
5174         PRVM_G_FLOAT(OFS_RETURN) = 1;
5175 }
5176
5177 /*
5178 ========================
5179 VM_buf_writefile
5180 writes stringbuffer to a file, returns 0 or 1
5181 float buf_writefile(float filehandle, float bufhandle, [, float startpos, float numstrings]) = #468;
5182 ========================
5183 */
5184
5185 void VM_buf_writefile(prvm_prog_t *prog)
5186 {
5187         int filenum, strindex, strnum, strlength;
5188         prvm_stringbuffer_t *stringbuffer;
5189
5190         VM_SAFEPARMCOUNTRANGE(2, 4, VM_buf_writefile);
5191
5192         // get file
5193         filenum = (int)PRVM_G_FLOAT(OFS_PARM0);
5194         if (filenum < 0 || filenum >= PRVM_MAX_OPENFILES)
5195         {
5196                 VM_Warning(prog, "VM_buf_writefile: invalid file handle %i used in %s\n", filenum, prog->name);
5197                 return;
5198         }
5199         if (prog->openfiles[filenum] == NULL)
5200         {
5201                 VM_Warning(prog, "VM_buf_writefile: no such file handle %i (or file has been closed) in %s\n", filenum, prog->name);
5202                 return;
5203         }
5204         
5205         // get string buffer
5206         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM1));
5207         if(!stringbuffer)
5208         {
5209                 VM_Warning(prog, "VM_buf_writefile: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), prog->name);
5210                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5211                 return;
5212         }
5213
5214         // get start and end parms
5215         if (prog->argc > 3)
5216         {
5217                 strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
5218                 strnum = (int)PRVM_G_FLOAT(OFS_PARM3);
5219         }
5220         else if (prog->argc > 2)
5221         {
5222                 strindex = (int)PRVM_G_FLOAT(OFS_PARM2);
5223                 strnum = stringbuffer->num_strings - strindex;
5224         }
5225         else
5226         {
5227                 strindex = 0;
5228                 strnum = stringbuffer->num_strings;
5229         }
5230         if (strindex < 0 || strindex >= stringbuffer->num_strings)
5231         {
5232                 VM_Warning(prog, "VM_buf_writefile: wrong start string index %i used in %s\n", strindex, prog->name);
5233                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5234                 return;
5235         }
5236         if (strnum < 0)
5237         {
5238                 VM_Warning(prog, "VM_buf_writefile: wrong strings count %i used in %s\n", strnum, prog->name);
5239                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5240                 return;
5241         }
5242
5243         // write
5244         while(strindex < stringbuffer->num_strings && strnum)
5245         {
5246                 if (stringbuffer->strings[strindex])
5247                 {
5248                         if ((strlength = (int)strlen(stringbuffer->strings[strindex])))
5249                                 FS_Write(prog->openfiles[filenum], stringbuffer->strings[strindex], strlength);
5250                         FS_Write(prog->openfiles[filenum], "\n", 1);
5251                 }
5252                 strindex++;
5253                 strnum--;
5254         }
5255
5256         PRVM_G_FLOAT(OFS_RETURN) = 1;
5257 }
5258
5259 #define MATCH_AUTO     0
5260 #define MATCH_WHOLE    1
5261 #define MATCH_LEFT     2
5262 #define MATCH_RIGHT    3
5263 #define MATCH_MIDDLE   4
5264 #define MATCH_PATTERN  5
5265
5266 static const char *detect_match_rule(char *pattern, int *matchrule)
5267 {
5268         char *ppos, *qpos;
5269         int patternlength;
5270
5271         patternlength = (int)strlen(pattern);
5272         ppos = strchr(pattern, '*');
5273         qpos = strchr(pattern, '?');
5274         // has ? - pattern
5275         if (qpos) 
5276         {
5277                 *matchrule = MATCH_PATTERN;
5278                 return pattern;
5279         }
5280         // has * - left, mid, right or pattern
5281         if (ppos)
5282         {
5283                 // starts with * - may be right/mid or pattern
5284                 if ((ppos - pattern) == 0)
5285                 {
5286                         ppos = strchr(pattern+1, '*');
5287                         // *something 
5288                         if (!ppos) 
5289                         {
5290                                 *matchrule = MATCH_RIGHT;
5291                                 return pattern+1;
5292                         }
5293                         // *something*
5294                         if ((ppos - pattern) == patternlength)
5295                         {
5296                                 *matchrule = MATCH_MIDDLE;
5297                                 *ppos = 0;
5298                                 return pattern+1;
5299                         }
5300                         // *som*thing
5301                         *matchrule = MATCH_PATTERN;
5302                         return pattern;
5303                 }
5304                 // end with * - left
5305                 if ((ppos - pattern) == patternlength)
5306                 {
5307                         *matchrule = MATCH_LEFT;
5308                         *ppos = 0;
5309                         return pattern;
5310                 }
5311                 // som*thing
5312                 *matchrule = MATCH_PATTERN;
5313                 return pattern;
5314         }
5315         // have no wildcards - whole string
5316         *matchrule = MATCH_WHOLE;
5317         return pattern;
5318 }
5319
5320 // todo: support UTF8
5321 static qbool match_rule(const char *string, int max_string, const char *pattern, int patternlength, int rule)
5322 {
5323         const char *mid;
5324
5325         if (rule == 1)
5326                 return !strncmp(string, pattern, max_string) ? true : false;
5327         if (rule == 2)
5328                 return !strncmp(string, pattern, patternlength) ? true : false;
5329         if (rule == 3)
5330         {
5331                 mid = strstr(string, pattern);
5332                 return mid && !*(mid+patternlength);
5333         }
5334         if (rule == 4)
5335                 return strstr(string, pattern) ? true : false;
5336         // pattern
5337         return matchpattern_with_separator(string, pattern, false, "", false) ? true : false;
5338 }
5339
5340 /*
5341 ========================
5342 VM_bufstr_find
5343 find an index of bufstring matching rule
5344 float bufstr_find(float bufhandle, string match, float matchrule, float startpos, float step) = #468;
5345 ========================
5346 */
5347
5348 void VM_bufstr_find(prvm_prog_t *prog)
5349 {
5350         prvm_stringbuffer_t *stringbuffer;
5351         char string[VM_STRINGTEMP_LENGTH];
5352         int matchrule, matchlen, i, step;
5353         const char *match;
5354
5355         VM_SAFEPARMCOUNTRANGE(3, 5, VM_bufstr_find);
5356
5357         PRVM_G_FLOAT(OFS_RETURN) = -1;
5358
5359         // get string buffer
5360         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5361         if(!stringbuffer)
5362         {
5363                 VM_Warning(prog, "VM_bufstr_find: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5364                 return;
5365         }
5366
5367         // get pattern/rule
5368         matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
5369         if (matchrule < 0 || matchrule > 5)
5370         {
5371                 VM_Warning(prog, "VM_bufstr_find: invalid match rule %i in %s\n", matchrule, prog->name);
5372                 return;
5373         }
5374         if (matchrule)
5375                 match = PRVM_G_STRING(OFS_PARM1);
5376         else
5377         {
5378                 strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
5379                 match = detect_match_rule(string, &matchrule);
5380         }
5381         matchlen = (int)strlen(match);
5382
5383         // find
5384         i = (prog->argc > 3) ? (int)PRVM_G_FLOAT(OFS_PARM3) : 0;
5385         step = (prog->argc > 4) ? (int)PRVM_G_FLOAT(OFS_PARM4) : 1;
5386         while(i < stringbuffer->num_strings)
5387         {
5388                 if (stringbuffer->strings[i] && match_rule(stringbuffer->strings[i], VM_STRINGTEMP_LENGTH, match, matchlen, matchrule))
5389                 {
5390                         PRVM_G_FLOAT(OFS_RETURN) = i;
5391                         break;
5392                 }
5393                 i += step;
5394         }
5395 }
5396
5397 /*
5398 ========================
5399 VM_matchpattern
5400 float matchpattern(string s, string pattern, float matchrule, float startpos) = #468;
5401 ========================
5402 */
5403 void VM_matchpattern(prvm_prog_t *prog)
5404 {
5405         const char *s, *match;
5406         char string[VM_STRINGTEMP_LENGTH];
5407         int matchrule, l;
5408
5409         VM_SAFEPARMCOUNTRANGE(2, 4, VM_matchpattern);
5410
5411         s = PRVM_G_STRING(OFS_PARM0);
5412
5413         // get pattern/rule
5414         matchrule = (int)PRVM_G_FLOAT(OFS_PARM2);
5415         if (matchrule < 0 || matchrule > 5)
5416         {
5417                 VM_Warning(prog, "VM_matchpattern: invalid match rule %i in %s\n", matchrule, prog->name);
5418                 return;
5419         }
5420         if (matchrule)
5421                 match = PRVM_G_STRING(OFS_PARM1);
5422         else
5423         {
5424                 strlcpy(string, PRVM_G_STRING(OFS_PARM1), sizeof(string));
5425                 match = detect_match_rule(string, &matchrule);
5426         }
5427
5428         // offset
5429         l = (int)strlen(match);
5430         if (prog->argc > 3)
5431                 s += max(0, min((unsigned int)PRVM_G_FLOAT(OFS_PARM3), strlen(s)-1));
5432
5433         // match
5434         PRVM_G_FLOAT(OFS_RETURN) = match_rule(s, VM_STRINGTEMP_LENGTH, match, l, matchrule);
5435 }
5436
5437 /*
5438 ========================
5439 VM_buf_cvarlist
5440 ========================
5441 */
5442
5443 void VM_buf_cvarlist(prvm_prog_t *prog)
5444 {
5445         cvar_t *cvar;
5446         const char *partial, *antipartial;
5447         size_t len, antilen;
5448         size_t alloclen;
5449         qbool ispattern, antiispattern;
5450         int n;
5451         prvm_stringbuffer_t     *stringbuffer;
5452         VM_SAFEPARMCOUNTRANGE(2, 3, VM_buf_cvarlist);
5453
5454         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
5455         if(!stringbuffer)
5456         {
5457                 VM_Warning(prog, "VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
5458                 return;
5459         }
5460
5461         partial = PRVM_G_STRING(OFS_PARM1);
5462         if(!partial)
5463                 len = 0;
5464         else
5465                 len = strlen(partial);
5466
5467         if(prog->argc == 3)
5468                 antipartial = PRVM_G_STRING(OFS_PARM2);
5469         else
5470                 antipartial = NULL;
5471         if(!antipartial)
5472                 antilen = 0;
5473         else
5474                 antilen = strlen(antipartial);
5475         
5476         for (n = 0;n < stringbuffer->num_strings;n++)
5477                 if (stringbuffer->strings[n])
5478                         Mem_Free(stringbuffer->strings[n]);
5479         if (stringbuffer->strings)
5480                 Mem_Free(stringbuffer->strings);
5481         stringbuffer->strings = NULL;
5482
5483         ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
5484         antiispattern = antipartial && (strchr(antipartial, '*') || strchr(antipartial, '?'));
5485
5486         n = 0;
5487         for(cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
5488         {
5489                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
5490                         continue;
5491
5492                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
5493                         continue;
5494
5495                 ++n;
5496         }
5497
5498         stringbuffer->max_strings = stringbuffer->num_strings = n;
5499         if (stringbuffer->max_strings)
5500                 stringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(stringbuffer->strings[0]) * stringbuffer->max_strings);
5501         
5502         n = 0;
5503         for(cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
5504         {
5505                 if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
5506                         continue;
5507
5508                 if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
5509                         continue;
5510
5511                 alloclen = strlen(cvar->name) + 1;
5512                 stringbuffer->strings[n] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
5513                 memcpy(stringbuffer->strings[n], cvar->name, alloclen);
5514
5515                 ++n;
5516         }
5517 }
5518
5519
5520
5521
5522 //=============
5523
5524 /*
5525 ==============
5526 VM_changeyaw
5527
5528 This was a major timewaster in progs, so it was converted to C
5529 ==============
5530 */
5531 void VM_changeyaw (prvm_prog_t *prog)
5532 {
5533         prvm_edict_t            *ent;
5534         float           ideal, current, move, speed;
5535
5536         // this is called (VERY HACKISHLY) by VM_SV_MoveToGoal, so it can not use any
5537         // parameters because they are the parameters to VM_SV_MoveToGoal, not this
5538         //VM_SAFEPARMCOUNT(0, VM_changeyaw);
5539
5540         ent = PRVM_PROG_TO_EDICT(PRVM_gameglobaledict(self));
5541         if (ent == prog->edicts)
5542         {
5543                 VM_Warning(prog, "changeyaw: can not modify world entity\n");
5544                 return;
5545         }
5546         if (ent->priv.server->free)
5547         {
5548                 VM_Warning(prog, "changeyaw: can not modify free entity\n");
5549                 return;
5550         }
5551         current = PRVM_gameedictvector(ent, angles)[1];
5552         current = ANGLEMOD(current);
5553         ideal = PRVM_gameedictfloat(ent, ideal_yaw);
5554         speed = PRVM_gameedictfloat(ent, yaw_speed);
5555
5556         if (current == ideal)
5557                 return;
5558         move = ideal - current;
5559         if (ideal > current)
5560         {
5561                 if (move >= 180)
5562                         move = move - 360;
5563         }
5564         else
5565         {
5566                 if (move <= -180)
5567                         move = move + 360;
5568         }
5569         if (move > 0)
5570         {
5571                 if (move > speed)
5572                         move = speed;
5573         }
5574         else
5575         {
5576                 if (move < -speed)
5577                         move = -speed;
5578         }
5579
5580         current += move;
5581         PRVM_gameedictvector(ent, angles)[1] = ANGLEMOD(current);
5582 }
5583
5584 /*
5585 ==============
5586 VM_changepitch
5587 ==============
5588 */
5589 void VM_changepitch (prvm_prog_t *prog)
5590 {
5591         prvm_edict_t            *ent;
5592         float           ideal, current, move, speed;
5593
5594         VM_SAFEPARMCOUNT(1, VM_changepitch);
5595
5596         ent = PRVM_G_EDICT(OFS_PARM0);
5597         if (ent == prog->edicts)
5598         {
5599                 VM_Warning(prog, "changepitch: can not modify world entity\n");
5600                 return;
5601         }
5602         if (ent->priv.server->free)
5603         {
5604                 VM_Warning(prog, "changepitch: can not modify free entity\n");
5605                 return;
5606         }
5607         current = PRVM_gameedictvector(ent, angles)[0];
5608         current = ANGLEMOD(current);
5609         ideal = PRVM_gameedictfloat(ent, idealpitch);
5610         speed = PRVM_gameedictfloat(ent, pitch_speed);
5611
5612         if (current == ideal)
5613                 return;
5614         move = ideal - current;
5615         if (ideal > current)
5616         {
5617                 if (move >= 180)
5618                         move = move - 360;
5619         }
5620         else
5621         {
5622                 if (move <= -180)
5623                         move = move + 360;
5624         }
5625         if (move > 0)
5626         {
5627                 if (move > speed)
5628                         move = speed;
5629         }
5630         else
5631         {
5632                 if (move < -speed)
5633                         move = -speed;
5634         }
5635
5636         current += move;
5637         PRVM_gameedictvector(ent, angles)[0] = ANGLEMOD(current);
5638 }
5639
5640
5641 void VM_uncolorstring (prvm_prog_t *prog)
5642 {
5643         char szNewString[VM_STRINGTEMP_LENGTH];
5644         const char *szString;
5645
5646         // Prepare Strings
5647         VM_SAFEPARMCOUNT(1, VM_uncolorstring);
5648         szString = PRVM_G_STRING(OFS_PARM0);
5649         COM_StringDecolorize(szString, 0, szNewString, sizeof(szNewString), true);
5650         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, szNewString);
5651         
5652 }
5653
5654 // #221 float(string str, string sub[, float startpos]) strstrofs (FTE_STRINGS)
5655 //strstr, without generating a new string. Use in conjunction with FRIK_FILE's substring for more similar strstr.
5656 void VM_strstrofs (prvm_prog_t *prog)
5657 {
5658         const char *instr, *match;
5659         int firstofs;
5660         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
5661         instr = PRVM_G_STRING(OFS_PARM0);
5662         match = PRVM_G_STRING(OFS_PARM1);
5663         firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
5664         firstofs = (int)u8_bytelen(instr, firstofs);
5665
5666         if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
5667         {
5668                 PRVM_G_FLOAT(OFS_RETURN) = -1;
5669                 return;
5670         }
5671
5672         match = strstr(instr+firstofs, match);
5673         if (!match)
5674                 PRVM_G_FLOAT(OFS_RETURN) = -1;
5675         else
5676                 PRVM_G_FLOAT(OFS_RETURN) = u8_strnlen(instr, match-instr);
5677 }
5678
5679 //#222 string(string s, float index) str2chr (FTE_STRINGS)
5680 void VM_str2chr (prvm_prog_t *prog)
5681 {
5682         const char *s;
5683         Uchar ch;
5684         int index;
5685         VM_SAFEPARMCOUNT(2, VM_str2chr);
5686         s = PRVM_G_STRING(OFS_PARM0);
5687         index = (int)u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
5688
5689         if((unsigned)index < strlen(s))
5690         {
5691                 if (utf8_enable.integer)
5692                         ch = u8_getchar_noendptr(s + index);
5693                 else
5694                         ch = (unsigned char)s[index];
5695                 PRVM_G_FLOAT(OFS_RETURN) = ch;
5696         }
5697         else
5698                 PRVM_G_FLOAT(OFS_RETURN) = 0;
5699 }
5700
5701 //#223 string(float c, ...) chr2str (FTE_STRINGS)
5702 void VM_chr2str (prvm_prog_t *prog)
5703 {
5704         /*
5705         char    t[9];
5706         int             i;
5707         VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
5708         for(i = 0;i < prog->argc && i < (int)sizeof(t) - 1;i++)
5709                 t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
5710         t[i] = 0;
5711         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
5712         */
5713         char t[9 * 4 + 1];
5714         int i;
5715         size_t len = 0;
5716         VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
5717         for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i)
5718                 len += u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1);
5719         t[len] = 0;
5720         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, t);
5721 }
5722
5723 static int chrconv_number(int i, int base, int conv)
5724 {
5725         i -= base;
5726         switch (conv)
5727         {
5728         default:
5729         case 5:
5730         case 6:
5731         case 0:
5732                 break;
5733         case 1:
5734                 base = '0';
5735                 break;
5736         case 2:
5737                 base = '0'+128;
5738                 break;
5739         case 3:
5740                 base = '0'-30;
5741                 break;
5742         case 4:
5743                 base = '0'+128-30;
5744                 break;
5745         }
5746         return i + base;
5747 }
5748 static int chrconv_punct(int i, int base, int conv)
5749 {
5750         i -= base;
5751         switch (conv)
5752         {
5753         default:
5754         case 0:
5755                 break;
5756         case 1:
5757                 base = 0;
5758                 break;
5759         case 2:
5760                 base = 128;
5761                 break;
5762         }
5763         return i + base;
5764 }
5765
5766 static int chrchar_alpha(int i, int basec, int baset, int convc, int convt, int charnum)
5767 {
5768         //convert case and colour seperatly...
5769
5770         i -= baset + basec;
5771         switch (convt)
5772         {
5773         default:
5774         case 0:
5775                 break;
5776         case 1:
5777                 baset = 0;
5778                 break;
5779         case 2:
5780                 baset = 128;
5781                 break;
5782
5783         case 5:
5784         case 6:
5785                 baset = 128*((charnum&1) == (convt-5));
5786                 break;
5787         }
5788
5789         switch (convc)
5790         {
5791         default:
5792         case 0:
5793                 break;
5794         case 1:
5795                 basec = 'a';
5796                 break;
5797         case 2:
5798                 basec = 'A';
5799                 break;
5800         }
5801         return i + basec + baset;
5802 }
5803 // #224 string(float ccase, float calpha, float cnum, string s, ...) strconv (FTE_STRINGS)
5804 //bulk convert a string. change case or colouring.
5805 void VM_strconv (prvm_prog_t *prog)
5806 {
5807         int ccase, redalpha, rednum, len, i;
5808         unsigned char resbuf[VM_STRINGTEMP_LENGTH];
5809         unsigned char *result = resbuf;
5810
5811         VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
5812
5813         ccase = (int) PRVM_G_FLOAT(OFS_PARM0);  //0 same, 1 lower, 2 upper
5814         redalpha = (int) PRVM_G_FLOAT(OFS_PARM1);       //0 same, 1 white, 2 red,  5 alternate, 6 alternate-alternate
5815         rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
5816         VM_VarString(prog, 3, (char *) resbuf, sizeof(resbuf));
5817         len = (int)strlen((char *) resbuf);
5818
5819         for (i = 0; i < len; i++, result++)     //should this be done backwards?
5820         {
5821                 if (*result >= '0' && *result <= '9')   //normal numbers...
5822                         *result = chrconv_number(*result, '0', rednum);
5823                 else if (*result >= '0'+128 && *result <= '9'+128)
5824                         *result = chrconv_number(*result, '0'+128, rednum);
5825                 else if (*result >= '0'+128-30 && *result <= '9'+128-30)
5826                         *result = chrconv_number(*result, '0'+128-30, rednum);
5827                 else if (*result >= '0'-30 && *result <= '9'-30)
5828                         *result = chrconv_number(*result, '0'-30, rednum);
5829
5830                 else if (*result >= 'a' && *result <= 'z')      //normal numbers...
5831                         *result = chrchar_alpha(*result, 'a', 0, ccase, redalpha, i);
5832                 else if (*result >= 'A' && *result <= 'Z')      //normal numbers...
5833                         *result = chrchar_alpha(*result, 'A', 0, ccase, redalpha, i);
5834                 else if (*result >= 'a'+128 && *result <= 'z'+128)      //normal numbers...
5835                         *result = chrchar_alpha(*result, 'a', 128, ccase, redalpha, i);
5836                 else if (*result >= 'A'+128 && *result <= 'Z'+128)      //normal numbers...
5837                         *result = chrchar_alpha(*result, 'A', 128, ccase, redalpha, i);
5838
5839                 else if ((*result & 127) < 16 || !redalpha)     //special chars..
5840                         *result = *result;
5841                 else if (*result < 128)
5842                         *result = chrconv_punct(*result, 0, redalpha);
5843                 else
5844                         *result = chrconv_punct(*result, 128, redalpha);
5845         }
5846         *result = '\0';
5847
5848         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, (char *) resbuf);
5849 }
5850
5851 // #225 string(float chars, string s, ...) strpad (FTE_STRINGS)
5852 void VM_strpad (prvm_prog_t *prog)
5853 {
5854         char src[VM_STRINGTEMP_LENGTH];
5855         char destbuf[VM_STRINGTEMP_LENGTH];
5856         int pad;
5857         VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
5858         pad = (int) PRVM_G_FLOAT(OFS_PARM0);
5859         VM_VarString(prog, 1, src, sizeof(src));
5860
5861         // note: < 0 = left padding, > 0 = right padding,
5862         // this is reverse logic of printf!
5863         dpsnprintf(destbuf, sizeof(destbuf), "%*s", -pad, src);
5864
5865         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, destbuf);
5866 }
5867
5868 // #226 string(string info, string key, string value, ...) infoadd (FTE_STRINGS)
5869 //uses qw style \key\value strings
5870 void VM_infoadd (prvm_prog_t *prog)
5871 {
5872         const char *info, *key;
5873         char value[VM_STRINGTEMP_LENGTH];
5874         char temp[VM_STRINGTEMP_LENGTH];
5875
5876         VM_SAFEPARMCOUNTRANGE(2, 8, VM_infoadd);
5877         info = PRVM_G_STRING(OFS_PARM0);
5878         key = PRVM_G_STRING(OFS_PARM1);
5879         VM_VarString(prog, 2, value, sizeof(value));
5880
5881         strlcpy(temp, info, VM_STRINGTEMP_LENGTH);
5882
5883         InfoString_SetValue(temp, VM_STRINGTEMP_LENGTH, key, value);
5884
5885         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, temp);
5886 }
5887
5888 // #227 string(string info, string key) infoget (FTE_STRINGS)
5889 //uses qw style \key\value strings
5890 void VM_infoget (prvm_prog_t *prog)
5891 {
5892         const char *info;
5893         const char *key;
5894         char value[VM_STRINGTEMP_LENGTH];
5895
5896         VM_SAFEPARMCOUNT(2, VM_infoget);
5897         info = PRVM_G_STRING(OFS_PARM0);
5898         key = PRVM_G_STRING(OFS_PARM1);
5899
5900         InfoString_GetValue(info, key, value, VM_STRINGTEMP_LENGTH);
5901
5902         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, value);
5903 }
5904
5905 //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS)
5906 // also float(string s1, string s2) strcmp (FRIK_FILE)
5907 void VM_strncmp (prvm_prog_t *prog)
5908 {
5909         const char *s1, *s2;
5910         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncmp);
5911         s1 = PRVM_G_STRING(OFS_PARM0);
5912         s2 = PRVM_G_STRING(OFS_PARM1);
5913         if (prog->argc > 2)
5914         {
5915                 PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
5916         }
5917         else
5918         {
5919                 PRVM_G_FLOAT(OFS_RETURN) = strcmp(s1, s2);
5920         }
5921 }
5922
5923 // #229 float(string s1, string s2) strcasecmp (FTE_STRINGS)
5924 // #230 float(string s1, string s2, float len) strncasecmp (FTE_STRINGS)
5925 void VM_strncasecmp (prvm_prog_t *prog)
5926 {
5927         const char *s1, *s2;
5928         VM_SAFEPARMCOUNTRANGE(2, 3, VM_strncasecmp);
5929         s1 = PRVM_G_STRING(OFS_PARM0);
5930         s2 = PRVM_G_STRING(OFS_PARM1);
5931         if (prog->argc > 2)
5932         {
5933                 PRVM_G_FLOAT(OFS_RETURN) = strncasecmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2));
5934         }
5935         else
5936         {
5937                 PRVM_G_FLOAT(OFS_RETURN) = strcasecmp(s1, s2);
5938         }
5939 }
5940
5941 // #494 float(float caseinsensitive, string s, ...) crc16
5942 void VM_crc16(prvm_prog_t *prog)
5943 {
5944         float insensitive;
5945         char s[VM_STRINGTEMP_LENGTH];
5946         VM_SAFEPARMCOUNTRANGE(2, 8, VM_crc16);
5947         insensitive = PRVM_G_FLOAT(OFS_PARM0);
5948         VM_VarString(prog, 1, s, sizeof(s));
5949         PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s)));
5950 }
5951
5952 // #639 float(string digest, string data, ...) digest_hex
5953 void VM_digest_hex(prvm_prog_t *prog)
5954 {
5955         const char *digest;
5956
5957         char out[32];
5958         char outhex[65];
5959         int outlen;
5960
5961         char s[VM_STRINGTEMP_LENGTH];
5962         int len;
5963
5964         VM_SAFEPARMCOUNTRANGE(2, 8, VM_digest_hex);
5965         digest = PRVM_G_STRING(OFS_PARM0);
5966         if(!digest)
5967                 digest = "";
5968         VM_VarString(prog, 1, s, sizeof(s));
5969         len = (int)strlen(s);
5970
5971         outlen = 0;
5972
5973         if(!strcmp(digest, "MD4"))
5974         {
5975                 outlen = 16;
5976                 mdfour((unsigned char *) out, (unsigned char *) s, len);
5977         }
5978         else if(!strcmp(digest, "SHA256") && Crypto_Available())
5979         {
5980                 outlen = 32;
5981                 sha256((unsigned char *) out, (unsigned char *) s, len);
5982         }
5983         // no warning needed on mismatch - we return string_null to QC
5984
5985         if(outlen)
5986         {
5987                 int i;
5988                 static const char *hexmap = "0123456789abcdef";
5989                 for(i = 0; i < outlen; ++i)
5990                 {
5991                         outhex[2*i]   = hexmap[(out[i] >> 4) & 15];
5992                         outhex[2*i+1] = hexmap[(out[i] >> 0) & 15];
5993                 }
5994                 outhex[2*i] = 0;
5995                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outhex);
5996         }
5997         else
5998                 PRVM_G_INT(OFS_RETURN) = 0;
5999 }
6000
6001 void VM_wasfreed (prvm_prog_t *prog)
6002 {
6003         VM_SAFEPARMCOUNT(1, VM_wasfreed);
6004         PRVM_G_FLOAT(OFS_RETURN) = PRVM_G_EDICT(OFS_PARM0)->priv.required->free;
6005 }
6006
6007 void VM_SetTraceGlobals(prvm_prog_t *prog, const trace_t *trace)
6008 {
6009         PRVM_gameglobalfloat(trace_allsolid) = trace->allsolid;
6010         PRVM_gameglobalfloat(trace_startsolid) = trace->startsolid;
6011         PRVM_gameglobalfloat(trace_fraction) = trace->fraction;
6012         PRVM_gameglobalfloat(trace_inwater) = trace->inwater;
6013         PRVM_gameglobalfloat(trace_inopen) = trace->inopen;
6014         VectorCopy(trace->endpos, PRVM_gameglobalvector(trace_endpos));
6015         VectorCopy(trace->plane.normal, PRVM_gameglobalvector(trace_plane_normal));
6016         PRVM_gameglobalfloat(trace_plane_dist) = trace->plane.dist;
6017         PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts);
6018         PRVM_gameglobalfloat(trace_dpstartcontents) = trace->startsupercontents;
6019         PRVM_gameglobalfloat(trace_dphitcontents) = trace->hitsupercontents;
6020         PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = trace->hitq3surfaceflags;
6021         PRVM_gameglobalstring(trace_dphittexturename) = trace->hittexture ? PRVM_SetTempString(prog, trace->hittexture->name) : 0;
6022 }
6023
6024 void VM_ClearTraceGlobals(prvm_prog_t *prog)
6025 {
6026         // clean up all trace globals when leaving the VM (anti-triggerbot safeguard)
6027         PRVM_gameglobalfloat(trace_allsolid) = 0;
6028         PRVM_gameglobalfloat(trace_startsolid) = 0;
6029         PRVM_gameglobalfloat(trace_fraction) = 0;
6030         PRVM_gameglobalfloat(trace_inwater) = 0;
6031         PRVM_gameglobalfloat(trace_inopen) = 0;
6032         VectorClear(PRVM_gameglobalvector(trace_endpos));
6033         VectorClear(PRVM_gameglobalvector(trace_plane_normal));
6034         PRVM_gameglobalfloat(trace_plane_dist) = 0;
6035         PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(prog->edicts);
6036         PRVM_gameglobalfloat(trace_dpstartcontents) = 0;
6037         PRVM_gameglobalfloat(trace_dphitcontents) = 0;
6038         PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = 0;
6039         PRVM_gameglobalstring(trace_dphittexturename) = 0;
6040 }
6041
6042 //=============
6043
6044 void VM_Cmd_Init(prvm_prog_t *prog)
6045 {
6046         // only init the stuff for the current prog
6047         VM_Files_Init(prog);
6048         VM_Search_Init(prog);
6049 }
6050
6051 static void animatemodel_reset(prvm_prog_t *prog);
6052
6053 void VM_Cmd_Reset(prvm_prog_t *prog)
6054 {
6055         CL_PurgeOwner( MENUOWNER );
6056         VM_Search_Reset(prog);
6057         VM_Files_CloseAll(prog);
6058         animatemodel_reset(prog);
6059 }
6060
6061 // #510 string(string input, ...) uri_escape (DP_QC_URI_ESCAPE)
6062 // does URI escaping on a string (replace evil stuff by %AB escapes)
6063 void VM_uri_escape (prvm_prog_t *prog)
6064 {
6065         char src[VM_STRINGTEMP_LENGTH];
6066         char dest[VM_STRINGTEMP_LENGTH];
6067         char *p, *q;
6068         static const char *hex = "0123456789ABCDEF";
6069
6070         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_escape);
6071         VM_VarString(prog, 0, src, sizeof(src));
6072
6073         for(p = src, q = dest; *p && q < dest + sizeof(dest) - 3; ++p)
6074         {
6075                 if((*p >= 'A' && *p <= 'Z')
6076                         || (*p >= 'a' && *p <= 'z')
6077                         || (*p >= '0' && *p <= '9')
6078                         || (*p == '-')  || (*p == '_') || (*p == '.')
6079                         || (*p == '!')  || (*p == '~')
6080                         || (*p == '\'') || (*p == '(') || (*p == ')'))
6081                         *q++ = *p;
6082                 else
6083                 {
6084                         *q++ = '%';
6085                         *q++ = hex[(*(unsigned char *)p >> 4) & 0xF];
6086                         *q++ = hex[ *(unsigned char *)p       & 0xF];
6087                 }
6088         }
6089         *q++ = 0;
6090
6091         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
6092 }
6093
6094 // #510 string(string input, ...) uri_unescape (DP_QC_URI_ESCAPE)
6095 // does URI unescaping on a string (get back the evil stuff)
6096 void VM_uri_unescape (prvm_prog_t *prog)
6097 {
6098         char src[VM_STRINGTEMP_LENGTH];
6099         char dest[VM_STRINGTEMP_LENGTH];
6100         char *p, *q;
6101         int hi, lo;
6102
6103         VM_SAFEPARMCOUNTRANGE(1, 8, VM_uri_unescape);
6104         VM_VarString(prog, 0, src, sizeof(src));
6105
6106         for(p = src, q = dest; *p; ) // no need to check size, because unescape can't expand
6107         {
6108                 if(*p == '%')
6109                 {
6110                         if(p[1] >= '0' && p[1] <= '9')
6111                                 hi = p[1] - '0';
6112                         else if(p[1] >= 'a' && p[1] <= 'f')
6113                                 hi = p[1] - 'a' + 10;
6114                         else if(p[1] >= 'A' && p[1] <= 'F')
6115                                 hi = p[1] - 'A' + 10;
6116                         else
6117                                 goto nohex;
6118                         if(p[2] >= '0' && p[2] <= '9')
6119                                 lo = p[2] - '0';
6120                         else if(p[2] >= 'a' && p[2] <= 'f')
6121                                 lo = p[2] - 'a' + 10;
6122                         else if(p[2] >= 'A' && p[2] <= 'F')
6123                                 lo = p[2] - 'A' + 10;
6124                         else
6125                                 goto nohex;
6126                         if(hi != 0 || lo != 0) // don't unescape NUL bytes
6127                                 *q++ = (char) (hi * 0x10 + lo);
6128                         p += 3;
6129                         continue;
6130                 }
6131
6132 nohex:
6133                 // otherwise:
6134                 *q++ = *p++;
6135         }
6136         *q++ = 0;
6137
6138         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, dest);
6139 }
6140
6141 // #502 string(string filename) whichpack (DP_QC_WHICHPACK)
6142 // returns the name of the pack containing a file, or "" if it is not in any pack (but local or non-existant)
6143 void VM_whichpack (prvm_prog_t *prog)
6144 {
6145         const char *fn, *pack;
6146
6147         VM_SAFEPARMCOUNT(1, VM_whichpack);
6148         fn = PRVM_G_STRING(OFS_PARM0);
6149         pack = FS_WhichPack(fn);
6150
6151         PRVM_G_INT(OFS_RETURN) = pack ? PRVM_SetTempString(prog, pack) : 0;
6152 }
6153
6154 typedef struct
6155 {
6156         prvm_prog_t *prog;
6157         double starttime;
6158         float id;
6159         char buffer[MAX_INPUTLINE];
6160         char posttype[128];
6161         unsigned char *postdata; // free when uri_to_prog_t is freed
6162         size_t postlen;
6163         char *sigdata; // free when uri_to_prog_t is freed
6164         size_t siglen;
6165 }
6166 uri_to_prog_t;
6167
6168 static void uri_to_string_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
6169 {
6170         prvm_prog_t *prog;
6171         uri_to_prog_t *handle = (uri_to_prog_t *) cbdata;
6172
6173         prog = handle->prog;
6174         if(!prog->loaded)
6175         {
6176                 // curl reply came too late... so just drop it
6177                 if(handle->postdata)
6178                         Z_Free(handle->postdata);
6179                 if(handle->sigdata)
6180                         Z_Free(handle->sigdata);
6181                 Z_Free(handle);
6182                 return;
6183         }
6184
6185         if((prog->starttime == handle->starttime) && (PRVM_allfunction(URI_Get_Callback)))
6186         {
6187                 if(length_received >= sizeof(handle->buffer))
6188                         length_received = sizeof(handle->buffer) - 1;
6189                 handle->buffer[length_received] = 0;
6190
6191                 PRVM_G_FLOAT(OFS_PARM0) = handle->id;
6192                 PRVM_G_FLOAT(OFS_PARM1) = status;
6193                 PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(prog, handle->buffer);
6194                 prog->ExecuteProgram(prog, PRVM_allfunction(URI_Get_Callback), "QC function URI_Get_Callback is missing");
6195         }
6196
6197         if(handle->postdata)
6198                 Z_Free(handle->postdata);
6199         if(handle->sigdata)
6200                 Z_Free(handle->sigdata);
6201         Z_Free(handle);
6202 }
6203
6204 // 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
6205 // 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
6206 void VM_uri_get (prvm_prog_t *prog)
6207 {
6208         const char *url;
6209         float id;
6210         qbool ret;
6211         uri_to_prog_t *handle;
6212         const char *posttype = NULL;
6213         const char *postseparator = NULL;
6214         int poststringbuffer = -1;
6215         int postkeyid = -1;
6216         const char *query_string = NULL;
6217         size_t lq;
6218
6219         if(!PRVM_allfunction(URI_Get_Callback))
6220                 prog->error_cmd("uri_get called by %s without URI_Get_Callback defined", prog->name);
6221
6222         VM_SAFEPARMCOUNTRANGE(2, 6, VM_uri_get);
6223
6224         url = PRVM_G_STRING(OFS_PARM0);
6225         id = PRVM_G_FLOAT(OFS_PARM1);
6226         if(prog->argc >= 3)
6227                 posttype = PRVM_G_STRING(OFS_PARM2);
6228         if(prog->argc >= 4)
6229                 postseparator = PRVM_G_STRING(OFS_PARM3);
6230         if(prog->argc >= 5)
6231                 poststringbuffer = PRVM_G_FLOAT(OFS_PARM4);
6232         if(prog->argc >= 6)
6233                 postkeyid = PRVM_G_FLOAT(OFS_PARM5);
6234         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!
6235
6236         query_string = strchr(url, '?');
6237         if(query_string)
6238                 ++query_string;
6239         lq = query_string ? strlen(query_string) : 0;
6240
6241         handle->prog = prog;
6242         handle->starttime = prog->starttime;
6243         handle->id = id;
6244         if(postseparator && posttype && *posttype)
6245         {
6246                 size_t l = strlen(postseparator);
6247                 if(poststringbuffer >= 0)
6248                 {
6249                         size_t ltotal;
6250                         int i;
6251                         // "implode"
6252                         prvm_stringbuffer_t *stringbuffer;
6253                         stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, poststringbuffer);
6254                         if(!stringbuffer)
6255                         {
6256                                 VM_Warning(prog, "uri_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), prog->name);
6257                                 return;
6258                         }
6259                         ltotal = 0;
6260                         for(i = 0;i < stringbuffer->num_strings;i++)
6261                         {
6262                                 if(i > 0)
6263                                         ltotal += l;
6264                                 if(stringbuffer->strings[i])
6265                                         ltotal += strlen(stringbuffer->strings[i]);
6266                         }
6267                         handle->postdata = (unsigned char *)Z_Malloc(ltotal + 1 + lq);
6268                         handle->postlen = ltotal;
6269                         ltotal = 0;
6270                         for(i = 0;i < stringbuffer->num_strings;i++)
6271                         {
6272                                 if(i > 0)
6273                                 {
6274                                         memcpy(handle->postdata + ltotal, postseparator, l);
6275                                         ltotal += l;
6276                                 }
6277                                 if(stringbuffer->strings[i])
6278                                 {
6279                                         memcpy(handle->postdata + ltotal, stringbuffer->strings[i], strlen(stringbuffer->strings[i]));
6280                                         ltotal += strlen(stringbuffer->strings[i]);
6281                                 }
6282                         }
6283                         if(ltotal != handle->postlen)
6284                                 prog->error_cmd("%s: string buffer content size mismatch, possible overrun", prog->name);
6285                 }
6286                 else
6287                 {
6288                         handle->postdata = (unsigned char *)Z_Malloc(l + 1 + lq);
6289                         handle->postlen = l;
6290                         memcpy(handle->postdata, postseparator, l);
6291                 }
6292                 handle->postdata[handle->postlen] = 0;
6293                 if(query_string)
6294                         memcpy(handle->postdata + handle->postlen + 1, query_string, lq);
6295                 if(postkeyid >= 0)
6296                 {
6297                         // POST: we sign postdata \0 query string
6298                         size_t ll;
6299                         handle->sigdata = (char *)Z_Malloc(8192);
6300                         strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
6301                         l = strlen(handle->sigdata);
6302                         handle->siglen = Crypto_SignDataDetached(handle->postdata, handle->postlen + 1 + lq, postkeyid, handle->sigdata + l, 8192 - l);
6303                         if(!handle->siglen)
6304                         {
6305                                 Z_Free(handle->sigdata);
6306                                 handle->sigdata = NULL;
6307                                 goto out1;
6308                         }
6309                         ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
6310                         if(!ll)
6311                         {
6312                                 Z_Free(handle->sigdata);
6313                                 handle->sigdata = NULL;
6314                                 goto out1;
6315                         }
6316                         handle->siglen = l + ll;
6317                         handle->sigdata[handle->siglen] = 0;
6318                 }
6319 out1:
6320                 strlcpy(handle->posttype, posttype, sizeof(handle->posttype));
6321                 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);
6322         }
6323         else
6324         {
6325                 if(postkeyid >= 0 && query_string)
6326                 {
6327                         // GET: we sign JUST the query string
6328                         size_t l, ll;
6329                         handle->sigdata = (char *)Z_Malloc(8192);
6330                         strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192);
6331                         l = strlen(handle->sigdata);
6332                         handle->siglen = Crypto_SignDataDetached(query_string, lq, postkeyid, handle->sigdata + l, 8192 - l);
6333                         if(!handle->siglen)
6334                         {
6335                                 Z_Free(handle->sigdata);
6336                                 handle->sigdata = NULL;
6337                                 goto out2;
6338                         }
6339                         ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1);
6340                         if(!ll)
6341                         {
6342                                 Z_Free(handle->sigdata);
6343                                 handle->sigdata = NULL;
6344                                 goto out2;
6345                         }
6346                         handle->siglen = l + ll;
6347                         handle->sigdata[handle->siglen] = 0;
6348                 }
6349 out2:
6350                 handle->postdata = NULL;
6351                 handle->postlen = 0;
6352                 ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, NULL, NULL, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle);
6353         }
6354         if(ret)
6355         {
6356                 PRVM_G_INT(OFS_RETURN) = 1;
6357         }
6358         else
6359         {
6360                 if(handle->postdata)
6361                         Z_Free(handle->postdata);
6362                 if(handle->sigdata)
6363                         Z_Free(handle->sigdata);
6364                 Z_Free(handle);
6365                 PRVM_G_INT(OFS_RETURN) = 0;
6366         }
6367 }
6368
6369 void VM_netaddress_resolve (prvm_prog_t *prog)
6370 {
6371         const char *ip;
6372         char normalized[128];
6373         int port;
6374         lhnetaddress_t addr;
6375
6376         VM_SAFEPARMCOUNTRANGE(1, 2, VM_netaddress_resolve);
6377
6378         ip = PRVM_G_STRING(OFS_PARM0);
6379         port = 0;
6380         if(prog->argc > 1)
6381                 port = (int) PRVM_G_FLOAT(OFS_PARM1);
6382
6383         if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
6384                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, normalized);
6385         else
6386                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, "");
6387 }
6388
6389 //string(prvm_prog_t *prog) getextresponse = #624; // returns the next extResponse packet that was sent to this client
6390 void VM_CL_getextresponse (prvm_prog_t *prog)
6391 {
6392         VM_SAFEPARMCOUNT(0,VM_argv);
6393
6394         if (cl_net_extresponse_count <= 0)
6395                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
6396         else
6397         {
6398                 int first;
6399                 --cl_net_extresponse_count;
6400                 first = (cl_net_extresponse_last + NET_EXTRESPONSE_MAX - cl_net_extresponse_count) % NET_EXTRESPONSE_MAX;
6401                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, cl_net_extresponse[first]);
6402         }
6403 }
6404
6405 void VM_SV_getextresponse (prvm_prog_t *prog)
6406 {
6407         VM_SAFEPARMCOUNT(0,VM_argv);
6408
6409         if (sv_net_extresponse_count <= 0)
6410                 PRVM_G_INT(OFS_RETURN) = OFS_NULL;
6411         else
6412         {
6413                 int first;
6414                 --sv_net_extresponse_count;
6415                 first = (sv_net_extresponse_last + NET_EXTRESPONSE_MAX - sv_net_extresponse_count) % NET_EXTRESPONSE_MAX;
6416                 PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, sv_net_extresponse[first]);
6417         }
6418 }
6419
6420 /*
6421 =========
6422 Common functions between menu.dat and clsprogs
6423 =========
6424 */
6425
6426 //#349 float() isdemo 
6427 void VM_CL_isdemo (prvm_prog_t *prog)
6428 {
6429         VM_SAFEPARMCOUNT(0, VM_CL_isdemo);
6430         PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback;
6431 }
6432
6433 //#355 float() videoplaying 
6434 void VM_CL_videoplaying (prvm_prog_t *prog)
6435 {
6436         VM_SAFEPARMCOUNT(0, VM_CL_videoplaying);
6437         PRVM_G_FLOAT(OFS_RETURN) = cl_videoplaying;
6438 }
6439
6440 /*
6441 =========
6442 VM_M_callfunction
6443
6444         callfunction(...,string function_name)
6445 Extension: pass
6446 =========
6447 */
6448 void VM_callfunction(prvm_prog_t *prog)
6449 {
6450         mfunction_t *func;
6451         const char *s;
6452
6453         VM_SAFEPARMCOUNTRANGE(1, 8, VM_callfunction);
6454
6455         s = PRVM_G_STRING(OFS_PARM0+(prog->argc - 1)*3);
6456
6457         VM_CheckEmptyString(prog, s);
6458
6459         func = PRVM_ED_FindFunction(prog, s);
6460
6461         if(!func)
6462                 prog->error_cmd("VM_callfunction: function %s not found !", s);
6463         else if (func->first_statement < 0)
6464         {
6465                 // negative statements are built in functions
6466                 int builtinnumber = -func->first_statement;
6467                 prog->xfunction->builtinsprofile++;
6468                 if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
6469                         prog->builtins[builtinnumber](prog);
6470                 else
6471                         prog->error_cmd("No such builtin #%i in %s; most likely cause: outdated engine build. Try updating!", builtinnumber, prog->name);
6472         }
6473         else if(func - prog->functions > 0)
6474         {
6475                 prog->argc--;
6476                 prog->ExecuteProgram(prog, func - prog->functions,"");
6477                 prog->argc++;
6478         }
6479 }
6480
6481 /*
6482 =========
6483 VM_isfunction
6484
6485 float   isfunction(string function_name)
6486 =========
6487 */
6488 void VM_isfunction(prvm_prog_t *prog)
6489 {
6490         mfunction_t *func;
6491         const char *s;
6492
6493         VM_SAFEPARMCOUNT(1, VM_isfunction);
6494
6495         s = PRVM_G_STRING(OFS_PARM0);
6496
6497         VM_CheckEmptyString(prog, s);
6498
6499         func = PRVM_ED_FindFunction(prog, s);
6500
6501         if(!func)
6502                 PRVM_G_FLOAT(OFS_RETURN) = false;
6503         else
6504                 PRVM_G_FLOAT(OFS_RETURN) = true;
6505 }
6506
6507 /*
6508 =========
6509 VM_sprintf
6510
6511 string sprintf(string format, ...)
6512 =========
6513 */
6514
6515 void VM_sprintf(prvm_prog_t *prog)
6516 {
6517         const char *s, *s0;
6518         char outbuf[MAX_INPUTLINE];
6519         char *o = outbuf, *end = outbuf + sizeof(outbuf), *err;
6520         const char *p;
6521         int argpos = 1;
6522         int width, precision, thisarg, flags;
6523         char formatbuf[16];
6524         char *f;
6525         int isfloat;
6526         static prvm_int_t dummyivec[3] = {0, 0, 0};
6527         static prvm_vec_t dummyvec[3] = {0, 0, 0};
6528         char vabuf[1024];
6529
6530 #define PRINTF_ALTERNATE 1
6531 #define PRINTF_ZEROPAD 2
6532 #define PRINTF_LEFT 4
6533 #define PRINTF_SPACEPOSITIVE 8
6534 #define PRINTF_SIGNPOSITIVE 16
6535
6536         formatbuf[0] = '%';
6537
6538         s = PRVM_G_STRING(OFS_PARM0);
6539
6540 #define GETARG_FLOAT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
6541 #define GETARG_VECTOR(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)
6542 #define GETARG_INT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0)
6543 #define GETARG_INTVECTOR(a) (((a)>=1 && (a)<prog->argc) ? ((prvm_int_t*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec)
6544 #define GETARG_STRING(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_STRING(OFS_PARM0 + 3 * (a))) : "")
6545
6546         for(;;)
6547         {
6548                 s0 = s;
6549                 switch(*s)
6550                 {
6551                         case 0:
6552                                 goto finished;
6553                         case '%':
6554                                 ++s;
6555
6556                                 if(*s == '%')
6557                                         goto verbatim;
6558
6559                                 // complete directive format:
6560                                 // %3$*1$.*2$ld
6561                                 
6562                                 width = -1;
6563                                 precision = -1;
6564                                 thisarg = -1;
6565                                 flags = 0;
6566                                 isfloat = -1;
6567
6568                                 // is number following?
6569                                 if(*s >= '0' && *s <= '9')
6570                                 {
6571                                         width = strtol(s, &err, 10);
6572                                         if(!err)
6573                                         {
6574                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6575                                                 goto finished;
6576                                         }
6577                                         if(*err == '$')
6578                                         {
6579                                                 thisarg = width;
6580                                                 width = -1;
6581                                                 s = err + 1;
6582                                         }
6583                                         else
6584                                         {
6585                                                 if(*s == '0')
6586                                                 {
6587                                                         flags |= PRINTF_ZEROPAD;
6588                                                         if(width == 0)
6589                                                                 width = -1; // it was just a flag
6590                                                 }
6591                                                 s = err;
6592                                         }
6593                                 }
6594
6595                                 if(width < 0)
6596                                 {
6597                                         for(;;)
6598                                         {
6599                                                 switch(*s)
6600                                                 {
6601                                                         case '#': flags |= PRINTF_ALTERNATE; break;
6602                                                         case '0': flags |= PRINTF_ZEROPAD; break;
6603                                                         case '-': flags |= PRINTF_LEFT; break;
6604                                                         case ' ': flags |= PRINTF_SPACEPOSITIVE; break;
6605                                                         case '+': flags |= PRINTF_SIGNPOSITIVE; break;
6606                                                         default:
6607                                                                 goto noflags;
6608                                                 }
6609                                                 ++s;
6610                                         }
6611 noflags:
6612                                         if(*s == '*')
6613                                         {
6614                                                 ++s;
6615                                                 if(*s >= '0' && *s <= '9')
6616                                                 {
6617                                                         width = strtol(s, &err, 10);
6618                                                         if(!err || *err != '$')
6619                                                         {
6620                                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6621                                                                 goto finished;
6622                                                         }
6623                                                         s = err + 1;
6624                                                 }
6625                                                 else
6626                                                         width = argpos++;
6627                                                 width = GETARG_FLOAT(width);
6628                                                 if(width < 0)
6629                                                 {
6630                                                         flags |= PRINTF_LEFT;
6631                                                         width = -width;
6632                                                 }
6633                                         }
6634                                         else if(*s >= '0' && *s <= '9')
6635                                         {
6636                                                 width = strtol(s, &err, 10);
6637                                                 if(!err)
6638                                                 {
6639                                                         VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6640                                                         goto finished;
6641                                                 }
6642                                                 s = err;
6643                                                 if(width < 0)
6644                                                 {
6645                                                         flags |= PRINTF_LEFT;
6646                                                         width = -width;
6647                                                 }
6648                                         }
6649                                         // otherwise width stays -1
6650                                 }
6651
6652                                 if(*s == '.')
6653                                 {
6654                                         ++s;
6655                                         if(*s == '*')
6656                                         {
6657                                                 ++s;
6658                                                 if(*s >= '0' && *s <= '9')
6659                                                 {
6660                                                         precision = strtol(s, &err, 10);
6661                                                         if(!err || *err != '$')
6662                                                         {
6663                                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6664                                                                 goto finished;
6665                                                         }
6666                                                         s = err + 1;
6667                                                 }
6668                                                 else
6669                                                         precision = argpos++;
6670                                                 precision = GETARG_FLOAT(precision);
6671                                         }
6672                                         else if(*s >= '0' && *s <= '9')
6673                                         {
6674                                                 precision = strtol(s, &err, 10);
6675                                                 if(!err)
6676                                                 {
6677                                                         VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6678                                                         goto finished;
6679                                                 }
6680                                                 s = err;
6681                                         }
6682                                         else
6683                                         {
6684                                                 VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6685                                                 goto finished;
6686                                         }
6687                                 }
6688
6689                                 for(;;)
6690                                 {
6691                                         switch(*s)
6692                                         {
6693                                                 case 'h': isfloat = 1; break;
6694                                                 case 'l': isfloat = 0; break;
6695                                                 case 'L': isfloat = 0; break;
6696                                                 case 'j': break;
6697                                                 case 'z': break;
6698                                                 case 't': break;
6699                                                 default:
6700                                                         goto nolength;
6701                                         }
6702                                         ++s;
6703                                 }
6704 nolength:
6705
6706                                 // now s points to the final directive char and is no longer changed
6707                                 if(isfloat < 0)
6708                                 {
6709                                         if(*s == 'i')
6710                                                 isfloat = 0;
6711                                         else
6712                                                 isfloat = 1;
6713                                 }
6714
6715                                 if(thisarg < 0)
6716                                         thisarg = argpos++;
6717
6718                                 if(o < end - 1)
6719                                 {
6720                                         f = &formatbuf[1];
6721                                         if(*s != 's' && *s != 'c')
6722                                                 if(flags & PRINTF_ALTERNATE) *f++ = '#';
6723                                         if(flags & PRINTF_ZEROPAD) *f++ = '0';
6724                                         if(flags & PRINTF_LEFT) *f++ = '-';
6725                                         if(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';
6726                                         if(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';
6727                                         *f++ = '*';
6728                                         if(precision >= 0)
6729                                         {
6730                                                 *f++ = '.';
6731                                                 *f++ = '*';
6732                                         }
6733                                         if(*s == 'd' || *s == 'i' || *s == 'o' || *s == 'u' || *s == 'x' || *s == 'X')
6734                                         {
6735                                                 // make it use a good integer type
6736                                                 for(p = INT_LOSSLESS_FORMAT_SIZE; *p; )
6737                                                         *f++ = *p++;
6738                                         }
6739                                         *f++ = *s;
6740                                         *f++ = 0;
6741
6742                                         if(width < 0) // not set
6743                                                 width = 0;
6744
6745                                         switch(*s)
6746                                         {
6747                                                 case 'd': case 'i':
6748                                                         if(precision < 0) // not set
6749                                                                 o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_S(GETARG_INT(thisarg))));
6750                                                         else
6751                                                                 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))));
6752                                                         break;
6753                                                 case 'o': case 'u': case 'x': case 'X':
6754                                                         if(precision < 0) // not set
6755                                                                 o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_FLOAT(thisarg)) : INT_LOSSLESS_FORMAT_CONVERT_U(GETARG_INT(thisarg))));
6756                                                         else
6757                                                                 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))));
6758                                                         break;
6759                                                 case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
6760                                                         if(precision < 0) // not set
6761                                                                 o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
6762                                                         else
6763                                                                 o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
6764                                                         break;
6765                                                 case 'v': case 'V':
6766                                                         f[-2] += 'g' - 'v';
6767                                                         if(precision < 0) // not set
6768                                                                 o += dpsnprintf(o, end - o, va(vabuf, sizeof(vabuf), "%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
6769                                                                         width, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
6770                                                                         width, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
6771                                                                         width, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
6772                                                                 );
6773                                                         else
6774                                                                 o += dpsnprintf(o, end - o, va(vabuf, sizeof(vabuf), "%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf),
6775                                                                         width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]),
6776                                                                         width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]),
6777                                                                         width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2])
6778                                                                 );
6779                                                         break;
6780                                                 case 'c':
6781                                                         if(flags & PRINTF_ALTERNATE)
6782                                                         {
6783                                                                 if(precision < 0) // not set
6784                                                                         o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
6785                                                                 else
6786                                                                         o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
6787                                                         }
6788                                                         else
6789                                                         {
6790                                                                 unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));
6791                                                                 char charbuf16[16];
6792                                                                 const char *buf = u8_encodech(c, NULL, charbuf16);
6793                                                                 if(!buf)
6794                                                                         buf = "";
6795                                                                 if(precision < 0) // not set
6796                                                                         precision = end - o - 1;
6797                                                                 o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision);
6798                                                         }
6799                                                         break;
6800                                                 //spike FIXME -- 'S' for quoted tokenize-safe-or-print escaping of strings so stuff can safely survive console commands.
6801                                                 case 's':
6802                                                         if(flags & PRINTF_ALTERNATE)
6803                                                         {
6804                                                                 if(precision < 0) // not set
6805                                                                         o += dpsnprintf(o, end - o, formatbuf, width, GETARG_STRING(thisarg));
6806                                                                 else
6807                                                                         o += dpsnprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));
6808                                                         }
6809                                                         else
6810                                                         {
6811                                                                 if(precision < 0) // not set
6812                                                                         precision = end - o - 1;
6813                                                                 if(flags & PRINTF_SIGNPOSITIVE)
6814                                                                         o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
6815                                                                 else
6816                                                                         o += u8_strpad_colorcodes(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
6817                                                         }
6818                                                         break;
6819                                                 default:
6820                                                         VM_Warning(prog, "VM_sprintf: invalid directive in %s: %s\n", prog->name, s0);
6821                                                         goto finished;
6822                                         }
6823                                 }
6824                                 ++s;
6825                                 break;
6826                         default:
6827 verbatim:
6828                                 if(o < end - 1)
6829                                         *o++ = *s;
6830                                 ++s;
6831                                 break;
6832                 }
6833         }
6834 finished:
6835         *o = 0;
6836         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, outbuf);
6837 }
6838
6839
6840 // surface querying
6841
6842 static model_t *getmodel(prvm_prog_t *prog, prvm_edict_t *ed)
6843 {
6844         if (prog == SVVM_prog)
6845                 return SV_GetModelFromEdict(ed);
6846         else if (prog == CLVM_prog)
6847                 return CL_GetModelFromEdict(ed);
6848         else
6849                 return NULL;
6850 }
6851
6852 struct animatemodel_cache
6853 {
6854         model_t *model;
6855         frameblend_t frameblend[MAX_FRAMEBLENDS];
6856         skeleton_t *skeleton_p;
6857         skeleton_t skeleton;
6858         float *data_vertex3f;
6859         float *data_svector3f;
6860         float *data_tvector3f;
6861         float *data_normal3f;
6862         int max_vertices;
6863         float *buf_vertex3f;
6864         float *buf_svector3f;
6865         float *buf_tvector3f;
6866         float *buf_normal3f;
6867 };
6868
6869 static void animatemodel_reset(prvm_prog_t *prog)
6870 {
6871         if (!prog->animatemodel_cache)
6872                 return;
6873         if(prog->animatemodel_cache->buf_vertex3f) Mem_Free(prog->animatemodel_cache->buf_vertex3f);
6874         if(prog->animatemodel_cache->buf_svector3f) Mem_Free(prog->animatemodel_cache->buf_svector3f);
6875         if(prog->animatemodel_cache->buf_tvector3f) Mem_Free(prog->animatemodel_cache->buf_tvector3f);
6876         if(prog->animatemodel_cache->buf_normal3f) Mem_Free(prog->animatemodel_cache->buf_normal3f);
6877         Mem_Free(prog->animatemodel_cache);
6878 }
6879
6880 static void animatemodel(prvm_prog_t *prog, model_t *model, prvm_edict_t *ed)
6881 {
6882         skeleton_t *skeleton;
6883         int skeletonindex = -1;
6884         qbool need = false;
6885         struct animatemodel_cache *animatemodel_cache;
6886         if (!prog->animatemodel_cache)
6887         {
6888                 prog->animatemodel_cache = (struct animatemodel_cache *)Mem_Alloc(prog->progs_mempool, sizeof(struct animatemodel_cache));
6889                 memset(prog->animatemodel_cache, 0, sizeof(struct animatemodel_cache));
6890         }
6891         animatemodel_cache = prog->animatemodel_cache;
6892         if(!(model->surfmesh.isanimated && model->AnimateVertices))
6893         {
6894                 animatemodel_cache->data_vertex3f = model->surfmesh.data_vertex3f;
6895                 animatemodel_cache->data_svector3f = model->surfmesh.data_svector3f;
6896                 animatemodel_cache->data_tvector3f = model->surfmesh.data_tvector3f;
6897                 animatemodel_cache->data_normal3f = model->surfmesh.data_normal3f;
6898                 return;
6899         }
6900         need |= (animatemodel_cache->model != model);
6901         VM_GenerateFrameGroupBlend(prog, ed->priv.server->framegroupblend, ed);
6902         VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model, PRVM_serverglobalfloat(time));
6903         need |= (memcmp(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend))) != 0;
6904         skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 1;
6905         if (!(skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones))
6906                 skeleton = NULL;
6907         need |= (animatemodel_cache->skeleton_p != skeleton);
6908         if(skeleton)
6909                 need |= (memcmp(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton))) != 0;
6910         if(!need)
6911                 return;
6912         if(model->surfmesh.num_vertices > animatemodel_cache->max_vertices)
6913         {
6914                 animatemodel_cache->max_vertices = model->surfmesh.num_vertices * 2;
6915                 if(animatemodel_cache->buf_vertex3f) Mem_Free(animatemodel_cache->buf_vertex3f);
6916                 if(animatemodel_cache->buf_svector3f) Mem_Free(animatemodel_cache->buf_svector3f);
6917                 if(animatemodel_cache->buf_tvector3f) Mem_Free(animatemodel_cache->buf_tvector3f);
6918                 if(animatemodel_cache->buf_normal3f) Mem_Free(animatemodel_cache->buf_normal3f);
6919                 animatemodel_cache->buf_vertex3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6920                 animatemodel_cache->buf_svector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6921                 animatemodel_cache->buf_tvector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6922                 animatemodel_cache->buf_normal3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache->max_vertices);
6923         }
6924         animatemodel_cache->data_vertex3f = animatemodel_cache->buf_vertex3f;
6925         animatemodel_cache->data_svector3f = animatemodel_cache->buf_svector3f;
6926         animatemodel_cache->data_tvector3f = animatemodel_cache->buf_tvector3f;
6927         animatemodel_cache->data_normal3f = animatemodel_cache->buf_normal3f;
6928         VM_UpdateEdictSkeleton(prog, ed, model, ed->priv.server->frameblend);
6929         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);
6930         animatemodel_cache->model = model;
6931         memcpy(&animatemodel_cache->frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
6932         animatemodel_cache->skeleton_p = skeleton;
6933         if(skeleton)
6934                 memcpy(&animatemodel_cache->skeleton, skeleton, sizeof(ed->priv.server->skeleton));
6935 }
6936
6937 static void getmatrix(prvm_prog_t *prog, prvm_edict_t *ed, matrix4x4_t *out)
6938 {
6939         if (prog == SVVM_prog)
6940                 SV_GetEntityMatrix(prog, ed, out, false);
6941         else if (prog == CLVM_prog)
6942                 CL_GetEntityMatrix(prog, ed, out, false);
6943         else
6944                 *out = identitymatrix;
6945 }
6946
6947 static void applytransform_forward(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6948 {
6949         matrix4x4_t m;
6950         getmatrix(prog, ed, &m);
6951         Matrix4x4_Transform(&m, in, out);
6952 }
6953
6954 static void applytransform_forward_direction(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6955 {
6956         matrix4x4_t m;
6957         getmatrix(prog, ed, &m);
6958         Matrix4x4_Transform3x3(&m, in, out);
6959 }
6960
6961 static void applytransform_inverted(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6962 {
6963         matrix4x4_t m, n;
6964         getmatrix(prog, ed, &m);
6965         Matrix4x4_Invert_Full(&n, &m);
6966         Matrix4x4_Transform3x3(&n, in, out);
6967 }
6968
6969 static void applytransform_forward_normal(prvm_prog_t *prog, const vec3_t in, prvm_edict_t *ed, vec3_t out)
6970 {
6971         matrix4x4_t m;
6972         float p[4];
6973         getmatrix(prog, ed, &m);
6974         Matrix4x4_TransformPositivePlane(&m, in[0], in[1], in[2], 0, p);
6975         VectorCopy(p, out);
6976 }
6977
6978 static void clippointtosurface(prvm_prog_t *prog, prvm_edict_t *ed, model_t *model, msurface_t *surface, vec3_t p, vec3_t out)
6979 {
6980         int i, j, k;
6981         float *v[3], facenormal[3], edgenormal[3], sidenormal[3], temp[3], offsetdist, dist, bestdist;
6982         const int *e;
6983         animatemodel(prog, model, ed);
6984         bestdist = 1000000000;
6985         VectorCopy(p, out);
6986         for (i = 0, e = (model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);i < surface->num_triangles;i++, e += 3)
6987         {
6988                 // clip original point to each triangle of the surface and find the
6989                 // triangle that is closest
6990                 v[0] = prog->animatemodel_cache->data_vertex3f + e[0] * 3;
6991                 v[1] = prog->animatemodel_cache->data_vertex3f + e[1] * 3;
6992                 v[2] = prog->animatemodel_cache->data_vertex3f + e[2] * 3;
6993                 TriangleNormal(v[0], v[1], v[2], facenormal);
6994                 VectorNormalize(facenormal);
6995                 offsetdist = DotProduct(v[0], facenormal) - DotProduct(p, facenormal);
6996                 VectorMA(p, offsetdist, facenormal, temp);
6997                 for (j = 0, k = 2;j < 3;k = j, j++)
6998                 {
6999                         VectorSubtract(v[k], v[j], edgenormal);
7000                         CrossProduct(edgenormal, facenormal, sidenormal);
7001                         VectorNormalize(sidenormal);
7002                         offsetdist = DotProduct(v[k], sidenormal) - DotProduct(temp, sidenormal);
7003                         if (offsetdist < 0)
7004                                 VectorMA(temp, offsetdist, sidenormal, temp);
7005                 }
7006                 dist = VectorDistance2(temp, p);
7007                 if (bestdist > dist)
7008                 {
7009                         bestdist = dist;
7010                         VectorCopy(temp, out);
7011                 }
7012         }
7013 }
7014
7015 static msurface_t *getsurface(model_t *model, int surfacenum)
7016 {
7017         if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces)
7018                 return NULL;
7019         return model->data_surfaces + surfacenum + model->firstmodelsurface;
7020 }
7021
7022
7023 //PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
7024 void VM_getsurfacenumpoints(prvm_prog_t *prog)
7025 {
7026         model_t *model;
7027         msurface_t *surface;
7028         VM_SAFEPARMCOUNT(2, VM_getsurfacenumpoints);
7029         // return 0 if no such surface
7030         if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7031         {
7032                 PRVM_G_FLOAT(OFS_RETURN) = 0;
7033                 return;
7034         }
7035
7036         // note: this (incorrectly) assumes it is a simple polygon
7037         PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices;
7038 }
7039 //PF_getsurfacepoint,     // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
7040 void VM_getsurfacepoint(prvm_prog_t *prog)
7041 {
7042         prvm_edict_t *ed;
7043         model_t *model;
7044         msurface_t *surface;
7045         int pointnum;
7046         vec3_t result;
7047         VM_SAFEPARMCOUNT(3, VM_getsurfacepoint);
7048         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7049         ed = PRVM_G_EDICT(OFS_PARM0);
7050         if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7051                 return;
7052         // note: this (incorrectly) assumes it is a simple polygon
7053         pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
7054         if (pointnum < 0 || pointnum >= surface->num_vertices)
7055                 return;
7056         animatemodel(prog, model, ed);
7057         applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7058         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7059 }
7060 //PF_getsurfacepointattribute,     // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
7061 // float SPA_POSITION = 0;
7062 // float SPA_S_AXIS = 1;
7063 // float SPA_T_AXIS = 2;
7064 // float SPA_R_AXIS = 3; // same as SPA_NORMAL
7065 // float SPA_TEXCOORDS0 = 4;
7066 // float SPA_LIGHTMAP0_TEXCOORDS = 5;
7067 // float SPA_LIGHTMAP0_COLOR = 6;
7068 void VM_getsurfacepointattribute(prvm_prog_t *prog)
7069 {
7070         prvm_edict_t *ed;
7071         model_t *model;
7072         msurface_t *surface;
7073         int pointnum;
7074         int attributetype;
7075         vec3_t result;
7076
7077         VM_SAFEPARMCOUNT(4, VM_getsurfacepointattribute);
7078         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7079         ed = PRVM_G_EDICT(OFS_PARM0);
7080         if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7081                 return;
7082         pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
7083         if (pointnum < 0 || pointnum >= surface->num_vertices)
7084                 return;
7085         attributetype = (int) PRVM_G_FLOAT(OFS_PARM3);
7086
7087         animatemodel(prog, model, ed);
7088
7089         switch( attributetype ) {
7090                 // float SPA_POSITION = 0;
7091                 case 0:
7092                         applytransform_forward(prog, &(prog->animatemodel_cache->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7093                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7094                         break;
7095                 // float SPA_S_AXIS = 1;
7096                 case 1:
7097                         applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7098                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7099                         break;
7100                 // float SPA_T_AXIS = 2;
7101                 case 2:
7102                         applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7103                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7104                         break;
7105                 // float SPA_R_AXIS = 3; // same as SPA_NORMAL
7106                 case 3:
7107                         applytransform_forward_direction(prog, &(prog->animatemodel_cache->data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, result);
7108                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7109                         break;
7110                 // float SPA_TEXCOORDS0 = 4;
7111                 case 4: {
7112                         float *texcoord = &(model->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[pointnum * 2];
7113                         result[0] = texcoord[0];
7114                         result[1] = texcoord[1];
7115                         result[2] = 0.0f;
7116                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7117                         break;
7118                 }
7119                 // float SPA_LIGHTMAP0_TEXCOORDS = 5;
7120                 case 5: {
7121                         float *texcoord = &(model->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[pointnum * 2];
7122                         result[0] = texcoord[0];
7123                         result[1] = texcoord[1];
7124                         result[2] = 0.0f;
7125                         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7126                         break;
7127                 }
7128                 // float SPA_LIGHTMAP0_COLOR = 6;
7129                 case 6:
7130                         // ignore alpha for now..
7131                         VectorCopy( &(model->surfmesh.data_lightmapcolor4f + 4 * surface->num_firstvertex)[pointnum * 4], PRVM_G_VECTOR(OFS_RETURN));
7132                         break;
7133                 default:
7134                         VectorSet( PRVM_G_VECTOR(OFS_RETURN), 0.0f, 0.0f, 0.0f );
7135                         break;
7136         }
7137 }
7138 //PF_getsurfacenormal,    // #436 vector(entity e, float s) getsurfacenormal = #436;
7139 void VM_getsurfacenormal(prvm_prog_t *prog)
7140 {
7141         model_t *model;
7142         msurface_t *surface;
7143         vec3_t normal;
7144         vec3_t result;
7145         VM_SAFEPARMCOUNT(2, VM_getsurfacenormal);
7146         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7147         if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7148                 return;
7149         // note: this only returns the first triangle, so it doesn't work very
7150         // well for curved surfaces or arbitrary meshes
7151         animatemodel(prog, model, PRVM_G_EDICT(OFS_PARM0));
7152         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);
7153         applytransform_forward_normal(prog, normal, PRVM_G_EDICT(OFS_PARM0), result);
7154         VectorNormalize(result);
7155         VectorCopy(result, PRVM_G_VECTOR(OFS_RETURN));
7156 }
7157 //PF_getsurfacetexture,   // #437 string(entity e, float s) getsurfacetexture = #437;
7158 void VM_getsurfacetexture(prvm_prog_t *prog)
7159 {
7160         model_t *model;
7161         msurface_t *surface;
7162         VM_SAFEPARMCOUNT(2, VM_getsurfacetexture);
7163         PRVM_G_INT(OFS_RETURN) = OFS_NULL;
7164         if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7165                 return;
7166         PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(prog, surface->texture->name);
7167 }
7168 //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
7169 void VM_getsurfacenearpoint(prvm_prog_t *prog)
7170 {
7171         int surfacenum, best;
7172         vec3_t clipped, p;
7173         vec_t dist, bestdist;
7174         prvm_edict_t *ed;
7175         model_t *model;
7176         msurface_t *surface;
7177         vec3_t point;
7178         VM_SAFEPARMCOUNT(2, VM_getsurfacenearpoint);
7179         PRVM_G_FLOAT(OFS_RETURN) = -1;
7180         ed = PRVM_G_EDICT(OFS_PARM0);
7181         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), point);
7182
7183         if (!ed || ed->priv.server->free)
7184                 return;
7185         model = getmodel(prog, ed);
7186         if (!model || !model->num_surfaces)
7187                 return;
7188
7189         animatemodel(prog, model, ed);
7190
7191         applytransform_inverted(prog, point, ed, p);
7192         best = -1;
7193         bestdist = 1000000000;
7194         for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++)
7195         {
7196                 surface = model->data_surfaces + surfacenum + model->firstmodelsurface;
7197                 // first see if the nearest point on the surface's box is closer than the previous match
7198                 clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0];
7199                 clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1];
7200                 clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2];
7201                 dist = VectorLength2(clipped);
7202                 if (dist < bestdist)
7203                 {
7204                         // it is, check the nearest point on the actual geometry
7205                         clippointtosurface(prog, ed, model, surface, p, clipped);
7206                         VectorSubtract(clipped, p, clipped);
7207                         dist += VectorLength2(clipped);
7208                         if (dist < bestdist)
7209                         {
7210                                 // that's closer too, store it as the best match
7211                                 best = surfacenum;
7212                                 bestdist = dist;
7213                         }
7214                 }
7215         }
7216         PRVM_G_FLOAT(OFS_RETURN) = best;
7217 }
7218 //PF_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
7219 void VM_getsurfaceclippedpoint(prvm_prog_t *prog)
7220 {
7221         prvm_edict_t *ed;
7222         model_t *model;
7223         msurface_t *surface;
7224         vec3_t p, out, inp;
7225         VM_SAFEPARMCOUNT(3, VM_getsurfaceclippedpoint);
7226         VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7227         ed = PRVM_G_EDICT(OFS_PARM0);
7228         if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7229                 return;
7230         animatemodel(prog, model, ed);
7231         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), inp);
7232         applytransform_inverted(prog, inp, ed, p);
7233         clippointtosurface(prog, ed, model, surface, p, out);
7234         VectorAdd(out, PRVM_serveredictvector(ed, origin), PRVM_G_VECTOR(OFS_RETURN));
7235 }
7236
7237 //PF_getsurfacenumtriangles, // #??? float(entity e, float s) getsurfacenumtriangles = #???;
7238 void VM_getsurfacenumtriangles(prvm_prog_t *prog)
7239 {
7240        model_t *model;
7241        msurface_t *surface;
7242        VM_SAFEPARMCOUNT(2, VM_getsurfacenumtriangles);
7243        // return 0 if no such surface
7244        if (!(model = getmodel(prog, PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7245        {
7246                PRVM_G_FLOAT(OFS_RETURN) = 0;
7247                return;
7248        }
7249
7250        PRVM_G_FLOAT(OFS_RETURN) = surface->num_triangles;
7251 }
7252 //PF_getsurfacetriangle,     // #??? vector(entity e, float s, float n) getsurfacetriangle = #???;
7253 void VM_getsurfacetriangle(prvm_prog_t *prog)
7254 {
7255        const vec3_t d = {-1, -1, -1};
7256        prvm_edict_t *ed;
7257        model_t *model;
7258        msurface_t *surface;
7259        int trinum;
7260        VM_SAFEPARMCOUNT(3, VM_getsurfacetriangle);
7261        VectorClear(PRVM_G_VECTOR(OFS_RETURN));
7262        ed = PRVM_G_EDICT(OFS_PARM0);
7263        if (!(model = getmodel(prog, ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
7264                return;
7265        trinum = (int)PRVM_G_FLOAT(OFS_PARM2);
7266        if (trinum < 0 || trinum >= surface->num_triangles)
7267                return;
7268        // FIXME: implement rotation/scaling
7269        VectorMA(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], surface->num_firstvertex, d, PRVM_G_VECTOR(OFS_RETURN));
7270 }
7271
7272 //
7273 // physics builtins
7274 //
7275
7276 #define VM_physics_ApplyCmd(ed,f) if (!ed->priv.server->ode_body) VM_physics_newstackfunction(prog, ed, f); else World_Physics_ApplyCmd(ed, f)
7277
7278 static edict_odefunc_t *VM_physics_newstackfunction(prvm_prog_t *prog, prvm_edict_t *ed, edict_odefunc_t *f)
7279 {
7280         edict_odefunc_t *newfunc, *func;
7281
7282         newfunc = (edict_odefunc_t *)Mem_Alloc(prog->progs_mempool, sizeof(edict_odefunc_t));
7283         memcpy(newfunc, f, sizeof(edict_odefunc_t));
7284         newfunc->next = NULL;
7285         if (!ed->priv.server->ode_func)
7286                 ed->priv.server->ode_func = newfunc;
7287         else
7288         {
7289                 for (func = ed->priv.server->ode_func; func->next; func = func->next);
7290                 func->next = newfunc;
7291         }
7292         return newfunc;
7293 }
7294
7295 // void(entity e, float physics_enabled) physics_enable = #;
7296 void VM_physics_enable(prvm_prog_t *prog)
7297 {
7298         prvm_edict_t *ed;
7299         edict_odefunc_t f;
7300         
7301         VM_SAFEPARMCOUNT(2, VM_physics_enable);
7302         ed = PRVM_G_EDICT(OFS_PARM0);
7303         if (!ed)
7304         {
7305                 if (developer.integer > 0)
7306                         VM_Warning(prog, "VM_physics_enable: null entity!\n");
7307                 return;
7308         }
7309         // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
7310         if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
7311         {
7312                 VM_Warning(prog, "VM_physics_enable: entity is not MOVETYPE_PHYSICS!\n");
7313                 return;
7314         }
7315         f.type = PRVM_G_FLOAT(OFS_PARM1) == 0 ? ODEFUNC_DISABLE : ODEFUNC_ENABLE;
7316         VM_physics_ApplyCmd(ed, &f);
7317 }
7318
7319 // void(entity e, vector force, vector relative_ofs) physics_addforce = #;
7320 void VM_physics_addforce(prvm_prog_t *prog)
7321 {
7322         prvm_edict_t *ed;
7323         edict_odefunc_t f;
7324         
7325         VM_SAFEPARMCOUNT(3, VM_physics_addforce);
7326         ed = PRVM_G_EDICT(OFS_PARM0);
7327         if (!ed)
7328         {
7329                 if (developer.integer > 0)
7330                         VM_Warning(prog, "VM_physics_addforce: null entity!\n");
7331                 return;
7332         }
7333         // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
7334         if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
7335         {
7336                 VM_Warning(prog, "VM_physics_addforce: entity is not MOVETYPE_PHYSICS!\n");
7337                 return;
7338         }
7339         f.type = ODEFUNC_FORCE;
7340         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
7341         VectorCopy(PRVM_G_VECTOR(OFS_PARM2), f.v2);
7342         VM_physics_ApplyCmd(ed, &f);
7343 }
7344
7345 // void(entity e, vector torque) physics_addtorque = #;
7346 void VM_physics_addtorque(prvm_prog_t *prog)
7347 {
7348         prvm_edict_t *ed;
7349         edict_odefunc_t f;
7350         
7351         VM_SAFEPARMCOUNT(2, VM_physics_addtorque);
7352         ed = PRVM_G_EDICT(OFS_PARM0);
7353         if (!ed)
7354         {
7355                 if (developer.integer > 0)
7356                         VM_Warning(prog, "VM_physics_addtorque: null entity!\n");
7357                 return;
7358         }
7359         // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer
7360         if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS)
7361         {
7362                 VM_Warning(prog, "VM_physics_addtorque: entity is not MOVETYPE_PHYSICS!\n");
7363                 return;
7364         }
7365         f.type = ODEFUNC_TORQUE;
7366         VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
7367         VM_physics_ApplyCmd(ed, &f);
7368 }
7369
7370 extern cvar_t prvm_coverage;
7371 void VM_coverage(prvm_prog_t *prog)
7372 {
7373         VM_SAFEPARMCOUNT(0, VM_coverage);
7374         if (prog->explicit_profile[prog->xstatement]++ == 0 && (prvm_coverage.integer & 2))
7375                 PRVM_ExplicitCoverageEvent(prog, prog->xfunction, prog->xstatement);
7376 }