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