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