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