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