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